@wordpress/core-data 7.6.0 → 7.7.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.
Files changed (54) hide show
  1. package/CHANGELOG.md +2 -0
  2. package/README.md +2 -2
  3. package/build/actions.js +39 -16
  4. package/build/actions.js.map +1 -1
  5. package/build/footnotes/index.js +5 -1
  6. package/build/footnotes/index.js.map +1 -1
  7. package/build/hooks/use-entity-prop.js +4 -4
  8. package/build/hooks/use-entity-prop.js.map +1 -1
  9. package/build/reducer.js +5 -0
  10. package/build/reducer.js.map +1 -1
  11. package/build/resolvers.js +54 -20
  12. package/build/resolvers.js.map +1 -1
  13. package/build/utils/index.js +2 -2
  14. package/build/utils/index.js.map +1 -1
  15. package/build/utils/user-permissions.js +5 -7
  16. package/build/utils/user-permissions.js.map +1 -1
  17. package/build-module/actions.js +38 -16
  18. package/build-module/actions.js.map +1 -1
  19. package/build-module/footnotes/index.js +5 -1
  20. package/build-module/footnotes/index.js.map +1 -1
  21. package/build-module/hooks/use-entity-prop.js +4 -4
  22. package/build-module/hooks/use-entity-prop.js.map +1 -1
  23. package/build-module/reducer.js +5 -0
  24. package/build-module/reducer.js.map +1 -1
  25. package/build-module/resolvers.js +55 -21
  26. package/build-module/resolvers.js.map +1 -1
  27. package/build-module/utils/index.js +1 -1
  28. package/build-module/utils/index.js.map +1 -1
  29. package/build-module/utils/user-permissions.js +4 -6
  30. package/build-module/utils/user-permissions.js.map +1 -1
  31. package/build-types/actions.d.ts +20 -2
  32. package/build-types/actions.d.ts.map +1 -1
  33. package/build-types/footnotes/index.d.ts.map +1 -1
  34. package/build-types/hooks/use-entity-prop.d.ts +5 -5
  35. package/build-types/hooks/use-entity-prop.d.ts.map +1 -1
  36. package/build-types/index.d.ts +5 -2
  37. package/build-types/index.d.ts.map +1 -1
  38. package/build-types/reducer.d.ts.map +1 -1
  39. package/build-types/resolvers.d.ts +1 -1
  40. package/build-types/resolvers.d.ts.map +1 -1
  41. package/build-types/utils/index.d.ts +1 -1
  42. package/build-types/utils/user-permissions.d.ts +1 -1
  43. package/build-types/utils/user-permissions.d.ts.map +1 -1
  44. package/package.json +18 -18
  45. package/src/actions.js +38 -16
  46. package/src/footnotes/index.js +9 -4
  47. package/src/hooks/test/use-entity-records.js +2 -2
  48. package/src/hooks/use-entity-prop.js +4 -4
  49. package/src/reducer.js +5 -0
  50. package/src/resolvers.js +79 -24
  51. package/src/test/resolvers.js +4 -3
  52. package/src/utils/index.js +1 -1
  53. package/src/utils/user-permissions.js +4 -6
  54. package/tsconfig.tsbuildinfo +1 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wordpress/core-data",
3
- "version": "7.6.0",
3
+ "version": "7.7.0",
4
4
  "description": "Access to and manipulation of core WordPress entities.",
5
5
  "author": "The WordPress Contributors",
6
6
  "license": "GPL-2.0-or-later",
@@ -32,22 +32,22 @@
32
32
  ],
33
33
  "dependencies": {
34
34
  "@babel/runtime": "^7.16.0",
35
- "@wordpress/api-fetch": "^7.6.0",
36
- "@wordpress/block-editor": "^14.1.0",
37
- "@wordpress/blocks": "^13.6.0",
38
- "@wordpress/compose": "^7.6.0",
39
- "@wordpress/data": "^10.6.0",
40
- "@wordpress/deprecated": "^4.6.0",
41
- "@wordpress/element": "^6.6.0",
42
- "@wordpress/html-entities": "^4.6.0",
43
- "@wordpress/i18n": "^5.6.0",
44
- "@wordpress/is-shallow-equal": "^5.6.0",
45
- "@wordpress/private-apis": "^1.6.0",
46
- "@wordpress/rich-text": "^7.6.0",
47
- "@wordpress/sync": "^1.6.0",
48
- "@wordpress/undo-manager": "^1.6.0",
49
- "@wordpress/url": "^4.6.0",
50
- "@wordpress/warning": "^3.6.0",
35
+ "@wordpress/api-fetch": "^7.7.0",
36
+ "@wordpress/block-editor": "^14.2.0",
37
+ "@wordpress/blocks": "^13.7.0",
38
+ "@wordpress/compose": "^7.7.0",
39
+ "@wordpress/data": "^10.7.0",
40
+ "@wordpress/deprecated": "^4.7.0",
41
+ "@wordpress/element": "^6.7.0",
42
+ "@wordpress/html-entities": "^4.7.0",
43
+ "@wordpress/i18n": "^5.7.0",
44
+ "@wordpress/is-shallow-equal": "^5.7.0",
45
+ "@wordpress/private-apis": "^1.7.0",
46
+ "@wordpress/rich-text": "^7.7.0",
47
+ "@wordpress/sync": "^1.7.0",
48
+ "@wordpress/undo-manager": "^1.7.0",
49
+ "@wordpress/url": "^4.7.0",
50
+ "@wordpress/warning": "^3.7.0",
51
51
  "change-case": "^4.1.2",
52
52
  "equivalent-key-map": "^0.2.2",
53
53
  "fast-deep-equal": "^3.1.3",
@@ -61,5 +61,5 @@
61
61
  "publishConfig": {
62
62
  "access": "public"
63
63
  },
64
- "gitHead": "ab9564947967bb3f00343130954b9efacba6cdd7"
64
+ "gitHead": "c90d920de07c53dff82c5914635b56fafa503b7f"
65
65
  }
package/src/actions.js CHANGED
@@ -265,17 +265,17 @@ export function receiveEmbedPreview( url, preview ) {
265
265
  /**
266
266
  * Action triggered to delete an entity record.
267
267
  *
268
- * @param {string} kind Kind of the deleted entity.
269
- * @param {string} name Name of the deleted entity.
270
- * @param {string} recordId Record ID of the deleted entity.
271
- * @param {?Object} query Special query parameters for the
272
- * DELETE API call.
273
- * @param {Object} [options] Delete options.
274
- * @param {Function} [options.__unstableFetch] Internal use only. Function to
275
- * call instead of `apiFetch()`.
276
- * Must return a promise.
277
- * @param {boolean} [options.throwOnError=false] If false, this action suppresses all
278
- * the exceptions. Defaults to false.
268
+ * @param {string} kind Kind of the deleted entity.
269
+ * @param {string} name Name of the deleted entity.
270
+ * @param {number|string} recordId Record ID of the deleted entity.
271
+ * @param {?Object} query Special query parameters for the
272
+ * DELETE API call.
273
+ * @param {Object} [options] Delete options.
274
+ * @param {Function} [options.__unstableFetch] Internal use only. Function to
275
+ * call instead of `apiFetch()`.
276
+ * Must return a promise.
277
+ * @param {boolean} [options.throwOnError=false] If false, this action suppresses all
278
+ * the exceptions. Defaults to false.
279
279
  */
280
280
  export const deleteEntityRecord =
281
281
  (
@@ -805,11 +805,11 @@ export const saveEditedEntityRecord =
805
805
  /**
806
806
  * Action triggered to save only specified properties for the entity.
807
807
  *
808
- * @param {string} kind Kind of the entity.
809
- * @param {string} name Name of the entity.
810
- * @param {Object} recordId ID of the record.
811
- * @param {Array} itemsToSave List of entity properties or property paths to save.
812
- * @param {Object} options Saving options.
808
+ * @param {string} kind Kind of the entity.
809
+ * @param {string} name Name of the entity.
810
+ * @param {number|string} recordId ID of the record.
811
+ * @param {Array} itemsToSave List of entity properties or property paths to save.
812
+ * @param {Object} options Saving options.
813
813
  */
814
814
  export const __experimentalSaveSpecifiedEntityEdits =
815
815
  ( kind, name, recordId, itemsToSave, options ) =>
@@ -888,6 +888,28 @@ export function receiveUserPermission( key, isAllowed ) {
888
888
  };
889
889
  }
890
890
 
891
+ /**
892
+ * Returns an action object used in signalling that the current user has
893
+ * permission to perform an action on a REST resource. Ignored from
894
+ * documentation as it's internal to the data store.
895
+ *
896
+ * @ignore
897
+ *
898
+ * @param {Object<string, boolean>} permissions An object where keys represent
899
+ * actions and REST resources, and
900
+ * values indicate whether the user
901
+ * is allowed to perform the
902
+ * action.
903
+ *
904
+ * @return {Object} Action object.
905
+ */
906
+ export function receiveUserPermissions( permissions ) {
907
+ return {
908
+ type: 'RECEIVE_USER_PERMISSIONS',
909
+ permissions,
910
+ };
911
+ }
912
+
891
913
  /**
892
914
  * Returns an action object used in signalling that the autosaves for a
893
915
  * post have been received.
@@ -72,6 +72,8 @@ export function updateFootnotesFromMeta( blocks, meta ) {
72
72
  ? RichTextData.fromHTMLString( value )
73
73
  : new RichTextData( value );
74
74
 
75
+ let hasFootnotes = false;
76
+
75
77
  richTextValue.replacements.forEach( ( replacement ) => {
76
78
  if ( replacement.type === 'core/footnote' ) {
77
79
  const id = replacement.attributes[ 'data-fn' ];
@@ -92,13 +94,16 @@ export function updateFootnotesFromMeta( blocks, meta ) {
92
94
  replacement.innerHTML = toHTMLString( {
93
95
  value: countValue,
94
96
  } );
97
+ hasFootnotes = true;
95
98
  }
96
99
  } );
97
100
 
98
- attributes[ key ] =
99
- typeof value === 'string'
100
- ? richTextValue.toHTMLString()
101
- : richTextValue;
101
+ if ( hasFootnotes ) {
102
+ attributes[ key ] =
103
+ typeof value === 'string'
104
+ ? richTextValue.toHTMLString()
105
+ : richTextValue;
106
+ }
102
107
  }
103
108
 
104
109
  return attributes;
@@ -69,8 +69,8 @@ describe( 'useEntityRecords', () => {
69
69
  hasStarted: true,
70
70
  isResolving: false,
71
71
  status: 'SUCCESS',
72
- totalItems: null,
73
- totalPages: null,
72
+ totalItems: 3,
73
+ totalPages: 1,
74
74
  } );
75
75
  } );
76
76
  } );
@@ -15,10 +15,10 @@ import useEntityId from './use-entity-id';
15
15
  * specified property of the nearest provided
16
16
  * entity of the specified type.
17
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.
18
+ * @param {string} kind The entity kind.
19
+ * @param {string} name The entity name.
20
+ * @param {string} prop The property name.
21
+ * @param {number|string} [_id] An entity ID to use instead of the context-provided one.
22
22
  *
23
23
  * @return {[*, Function, *]} An array where the first item is the
24
24
  * property value, the second is the
package/src/reducer.js CHANGED
@@ -521,6 +521,11 @@ export function userPermissions( state = {}, action ) {
521
521
  ...state,
522
522
  [ action.key ]: action.isAllowed,
523
523
  };
524
+ case 'RECEIVE_USER_PERMISSIONS':
525
+ return {
526
+ ...state,
527
+ ...action.permissions,
528
+ };
524
529
  }
525
530
 
526
531
  return state;
package/src/resolvers.js CHANGED
@@ -19,7 +19,7 @@ import {
19
19
  forwardResolver,
20
20
  getNormalizedCommaSeparable,
21
21
  getUserPermissionCacheKey,
22
- getUserPermissionsFromResponse,
22
+ getUserPermissionsFromAllowHeader,
23
23
  ALLOWED_RESOURCE_ACTIONS,
24
24
  } from './utils';
25
25
  import { getSyncProvider } from './sync';
@@ -173,26 +173,36 @@ export const getEntityRecord =
173
173
 
174
174
  const response = await apiFetch( { path, parse: false } );
175
175
  const record = await response.json();
176
- const permissions = getUserPermissionsFromResponse( response );
176
+ const permissions = getUserPermissionsFromAllowHeader(
177
+ response.headers?.get( 'allow' )
178
+ );
179
+
180
+ const canUserResolutionsArgs = [];
181
+ const receiveUserPermissionArgs = {};
182
+ for ( const action of ALLOWED_RESOURCE_ACTIONS ) {
183
+ receiveUserPermissionArgs[
184
+ getUserPermissionCacheKey( action, {
185
+ kind,
186
+ name,
187
+ id: key,
188
+ } )
189
+ ] = permissions[ action ];
190
+
191
+ canUserResolutionsArgs.push( [
192
+ action,
193
+ { kind, name, id: key },
194
+ ] );
195
+ }
177
196
 
178
197
  registry.batch( () => {
179
198
  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
- }
199
+ dispatch.receiveUserPermissions(
200
+ receiveUserPermissionArgs
201
+ );
202
+ dispatch.finishResolutions(
203
+ 'canUser',
204
+ canUserResolutionsArgs
205
+ );
196
206
  } );
197
207
  }
198
208
  } finally {
@@ -271,6 +281,10 @@ export const getEntityRecords =
271
281
  };
272
282
  } else {
273
283
  records = Object.values( await apiFetch( { path } ) );
284
+ meta = {
285
+ totalItems: records.length,
286
+ totalPages: 1,
287
+ };
274
288
  }
275
289
 
276
290
  // If we request fields but the result doesn't contain the fields,
@@ -299,19 +313,55 @@ export const getEntityRecords =
299
313
  meta
300
314
  );
301
315
 
302
- // When requesting all fields, the list of results can be used to
303
- // resolve the `getEntityRecord` selector in addition to `getEntityRecords`.
316
+ // When requesting all fields, the list of results can be used to resolve
317
+ // the `getEntityRecord` and `canUser` selectors in addition to `getEntityRecords`.
304
318
  // See https://github.com/WordPress/gutenberg/pull/26575
319
+ // See https://github.com/WordPress/gutenberg/pull/64504
305
320
  if ( ! query?._fields && ! query.context ) {
306
321
  const key = entityConfig.key || DEFAULT_ENTITY_KEY;
307
322
  const resolutionsArgs = records
308
323
  .filter( ( record ) => record?.[ key ] )
309
324
  .map( ( record ) => [ kind, name, record[ key ] ] );
310
325
 
326
+ const targetHints = records
327
+ .filter( ( record ) => record?.[ key ] )
328
+ .map( ( record ) => ( {
329
+ id: record[ key ],
330
+ permissions: getUserPermissionsFromAllowHeader(
331
+ record?._links?.self?.[ 0 ].targetHints.allow
332
+ ),
333
+ } ) );
334
+
335
+ const canUserResolutionsArgs = [];
336
+ const receiveUserPermissionArgs = {};
337
+ for ( const targetHint of targetHints ) {
338
+ for ( const action of ALLOWED_RESOURCE_ACTIONS ) {
339
+ canUserResolutionsArgs.push( [
340
+ action,
341
+ { kind, name, id: targetHint.id },
342
+ ] );
343
+
344
+ receiveUserPermissionArgs[
345
+ getUserPermissionCacheKey( action, {
346
+ kind,
347
+ name,
348
+ id: targetHint.id,
349
+ } )
350
+ ] = targetHint.permissions[ action ];
351
+ }
352
+ }
353
+
354
+ dispatch.receiveUserPermissions(
355
+ receiveUserPermissionArgs
356
+ );
311
357
  dispatch.finishResolutions(
312
358
  'getEntityRecord',
313
359
  resolutionsArgs
314
360
  );
361
+ dispatch.finishResolutions(
362
+ 'canUser',
363
+ canUserResolutionsArgs
364
+ );
315
365
  }
316
366
 
317
367
  dispatch.__unstableReleaseStoreLock( lock );
@@ -440,7 +490,12 @@ export const canUser =
440
490
  return;
441
491
  }
442
492
 
443
- const permissions = getUserPermissionsFromResponse( response );
493
+ // Optional chaining operator is used here because the API requests don't
494
+ // return the expected result in the React native version. Instead, API requests
495
+ // only return the result, without including response properties like the headers.
496
+ const permissions = getUserPermissionsFromAllowHeader(
497
+ response.headers?.get( 'allow' )
498
+ );
444
499
  registry.batch( () => {
445
500
  for ( const action of ALLOWED_RESOURCE_ACTIONS ) {
446
501
  const key = getUserPermissionCacheKey( action, resource, id );
@@ -463,9 +518,9 @@ export const canUser =
463
518
  * Checks whether the current user can perform the given action on the given
464
519
  * REST resource.
465
520
  *
466
- * @param {string} kind Entity kind.
467
- * @param {string} name Entity name.
468
- * @param {string} recordId Record's id.
521
+ * @param {string} kind Entity kind.
522
+ * @param {string} name Entity name.
523
+ * @param {number|string} recordId Record's id.
469
524
  */
470
525
  export const canUserEditEntityRecord =
471
526
  ( kind, name, recordId ) =>
@@ -36,8 +36,8 @@ describe( 'getEntityRecord', () => {
36
36
  receiveEntityRecords: jest.fn(),
37
37
  __unstableAcquireStoreLock: jest.fn(),
38
38
  __unstableReleaseStoreLock: jest.fn(),
39
- receiveUserPermission: jest.fn(),
40
- finishResolution: jest.fn(),
39
+ receiveUserPermissions: jest.fn(),
40
+ finishResolutions: jest.fn(),
41
41
  } );
42
42
  triggerFetch.mockReset();
43
43
  } );
@@ -181,7 +181,7 @@ describe( 'getEntityRecords', () => {
181
181
  {},
182
182
  false,
183
183
  undefined,
184
- undefined
184
+ { totalItems: 2, totalPages: 1 }
185
185
  );
186
186
  } );
187
187
 
@@ -219,6 +219,7 @@ describe( 'getEntityRecords', () => {
219
219
  const finishResolutions = jest.fn();
220
220
  const dispatch = Object.assign( jest.fn(), {
221
221
  receiveEntityRecords: jest.fn(),
222
+ receiveUserPermissions: jest.fn(),
222
223
  __unstableAcquireStoreLock: jest.fn(),
223
224
  __unstableReleaseStoreLock: jest.fn(),
224
225
  finishResolutions,
@@ -11,6 +11,6 @@ export { default as getNestedValue } from './get-nested-value';
11
11
  export { default as isNumericID } from './is-numeric-id';
12
12
  export {
13
13
  getUserPermissionCacheKey,
14
- getUserPermissionsFromResponse,
14
+ getUserPermissionsFromAllowHeader,
15
15
  ALLOWED_RESOURCE_ACTIONS,
16
16
  } from './user-permissions';
@@ -5,13 +5,11 @@ export const ALLOWED_RESOURCE_ACTIONS = [
5
5
  'delete',
6
6
  ];
7
7
 
8
- export function getUserPermissionsFromResponse( response ) {
8
+ export function getUserPermissionsFromAllowHeader( allowedMethods ) {
9
9
  const permissions = {};
10
-
11
- // Optional chaining operator is used here because the API requests don't
12
- // return the expected result in the React native version. Instead, API requests
13
- // only return the result, without including response properties like the headers.
14
- const allowedMethods = response.headers?.get( 'allow' ) || '';
10
+ if ( ! allowedMethods ) {
11
+ return permissions;
12
+ }
15
13
 
16
14
  const methods = {
17
15
  create: 'POST',