@wordpress/editor 14.1.0 → 14.2.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 (128) hide show
  1. package/CHANGELOG.md +14 -12
  2. package/README.md +28 -7
  3. package/build/components/create-template-part-modal/index.js +2 -0
  4. package/build/components/create-template-part-modal/index.js.map +1 -1
  5. package/build/components/document-bar/index.js +11 -4
  6. package/build/components/document-bar/index.js.map +1 -1
  7. package/build/components/editor-interface/index.js +5 -3
  8. package/build/components/editor-interface/index.js.map +1 -1
  9. package/build/components/entities-saved-states/entity-record-item.js +4 -12
  10. package/build/components/entities-saved-states/entity-record-item.js.map +1 -1
  11. package/build/components/global-keyboard-shortcuts/index.js +1 -1
  12. package/build/components/global-keyboard-shortcuts/index.js.map +1 -1
  13. package/build/components/global-styles-provider/index.js +6 -65
  14. package/build/components/global-styles-provider/index.js.map +1 -1
  15. package/build/components/header/index.js +6 -2
  16. package/build/components/header/index.js.map +1 -1
  17. package/build/components/index.js +10 -2
  18. package/build/components/index.js.map +1 -1
  19. package/build/components/post-actions/actions.js +321 -254
  20. package/build/components/post-actions/actions.js.map +1 -1
  21. package/build/components/post-actions/index.js +6 -1
  22. package/build/components/post-actions/index.js.map +1 -1
  23. package/build/components/post-excerpt/index.js +2 -1
  24. package/build/components/post-excerpt/index.js.map +1 -1
  25. package/build/components/post-excerpt/panel.js +2 -1
  26. package/build/components/post-excerpt/panel.js.map +1 -1
  27. package/build/components/post-slug/check.js +8 -1
  28. package/build/components/post-slug/check.js.map +1 -1
  29. package/build/components/post-slug/index.js +6 -0
  30. package/build/components/post-slug/index.js.map +1 -1
  31. package/build/components/post-status/index.js +2 -1
  32. package/build/components/post-status/index.js.map +1 -1
  33. package/build/components/post-sticky/index.js +9 -13
  34. package/build/components/post-sticky/index.js.map +1 -1
  35. package/build/components/post-template/create-new-template-modal.js +2 -0
  36. package/build/components/post-template/create-new-template-modal.js.map +1 -1
  37. package/build/components/sidebar/index.js +2 -1
  38. package/build/components/sidebar/index.js.map +1 -1
  39. package/build/components/sidebar/post-summary.js +1 -2
  40. package/build/components/sidebar/post-summary.js.map +1 -1
  41. package/build/components/template-part-content-panel/index.js +70 -0
  42. package/build/components/template-part-content-panel/index.js.map +1 -0
  43. package/build/hooks/pattern-overrides.js +1 -1
  44. package/build/hooks/pattern-overrides.js.map +1 -1
  45. package/build/lock-unlock.js +1 -1
  46. package/build/lock-unlock.js.map +1 -1
  47. package/build/store/private-actions.js +9 -5
  48. package/build/store/private-actions.js.map +1 -1
  49. package/build-module/components/create-template-part-modal/index.js +2 -0
  50. package/build-module/components/create-template-part-modal/index.js.map +1 -1
  51. package/build-module/components/document-bar/index.js +11 -4
  52. package/build-module/components/document-bar/index.js.map +1 -1
  53. package/build-module/components/editor-interface/index.js +5 -3
  54. package/build-module/components/editor-interface/index.js.map +1 -1
  55. package/build-module/components/entities-saved-states/entity-record-item.js +6 -14
  56. package/build-module/components/entities-saved-states/entity-record-item.js.map +1 -1
  57. package/build-module/components/global-keyboard-shortcuts/index.js +1 -1
  58. package/build-module/components/global-keyboard-shortcuts/index.js.map +1 -1
  59. package/build-module/components/global-styles-provider/index.js +7 -66
  60. package/build-module/components/global-styles-provider/index.js.map +1 -1
  61. package/build-module/components/header/index.js +6 -2
  62. package/build-module/components/header/index.js.map +1 -1
  63. package/build-module/components/index.js +11 -2
  64. package/build-module/components/index.js.map +1 -1
  65. package/build-module/components/post-actions/actions.js +322 -255
  66. package/build-module/components/post-actions/actions.js.map +1 -1
  67. package/build-module/components/post-actions/index.js +6 -1
  68. package/build-module/components/post-actions/index.js.map +1 -1
  69. package/build-module/components/post-excerpt/index.js +2 -1
  70. package/build-module/components/post-excerpt/index.js.map +1 -1
  71. package/build-module/components/post-excerpt/panel.js +2 -1
  72. package/build-module/components/post-excerpt/panel.js.map +1 -1
  73. package/build-module/components/post-slug/check.js +9 -0
  74. package/build-module/components/post-slug/check.js.map +1 -1
  75. package/build-module/components/post-slug/index.js +6 -0
  76. package/build-module/components/post-slug/index.js.map +1 -1
  77. package/build-module/components/post-status/index.js +2 -1
  78. package/build-module/components/post-status/index.js.map +1 -1
  79. package/build-module/components/post-sticky/index.js +10 -14
  80. package/build-module/components/post-sticky/index.js.map +1 -1
  81. package/build-module/components/post-template/create-new-template-modal.js +2 -0
  82. package/build-module/components/post-template/create-new-template-modal.js.map +1 -1
  83. package/build-module/components/sidebar/index.js +2 -1
  84. package/build-module/components/sidebar/index.js.map +1 -1
  85. package/build-module/components/sidebar/post-summary.js +1 -2
  86. package/build-module/components/sidebar/post-summary.js.map +1 -1
  87. package/build-module/components/template-part-content-panel/index.js +63 -0
  88. package/build-module/components/template-part-content-panel/index.js.map +1 -0
  89. package/build-module/hooks/pattern-overrides.js +1 -1
  90. package/build-module/hooks/pattern-overrides.js.map +1 -1
  91. package/build-module/lock-unlock.js +1 -1
  92. package/build-module/lock-unlock.js.map +1 -1
  93. package/build-module/store/private-actions.js +9 -5
  94. package/build-module/store/private-actions.js.map +1 -1
  95. package/build-style/style-rtl.css +11 -23
  96. package/build-style/style.css +11 -23
  97. package/package.json +35 -35
  98. package/src/components/create-template-part-modal/index.js +2 -0
  99. package/src/components/create-template-part-modal/style.scss +0 -6
  100. package/src/components/document-bar/index.js +11 -4
  101. package/src/components/editor-interface/index.js +14 -8
  102. package/src/components/entities-saved-states/entity-record-item.js +4 -14
  103. package/src/components/entities-saved-states/style.scss +0 -13
  104. package/src/components/global-keyboard-shortcuts/index.js +1 -1
  105. package/src/components/global-styles-provider/index.js +7 -90
  106. package/src/components/header/index.js +2 -1
  107. package/src/components/index.js +11 -2
  108. package/src/components/post-actions/actions.js +443 -374
  109. package/src/components/post-actions/index.js +3 -1
  110. package/src/components/post-card-panel/style.scss +1 -0
  111. package/src/components/post-excerpt/index.js +4 -1
  112. package/src/components/post-excerpt/panel.js +2 -1
  113. package/src/components/post-publish-panel/style.scss +1 -1
  114. package/src/components/post-slug/check.js +8 -0
  115. package/src/components/post-slug/index.js +5 -0
  116. package/src/components/post-status/index.js +2 -0
  117. package/src/components/post-sticky/index.js +10 -13
  118. package/src/components/post-sticky/style.scss +3 -2
  119. package/src/components/post-template/create-new-template-modal.js +2 -0
  120. package/src/components/post-url/style.scss +4 -0
  121. package/src/components/sidebar/index.js +2 -0
  122. package/src/components/sidebar/post-summary.js +0 -2
  123. package/src/components/template-part-content-panel/index.js +62 -0
  124. package/src/components/visual-editor/style.scss +2 -0
  125. package/src/hooks/pattern-overrides.js +2 -3
  126. package/src/lock-unlock.js +1 -1
  127. package/src/store/private-actions.js +54 -21
  128. package/tsconfig.tsbuildinfo +1 -1
@@ -3,7 +3,7 @@
3
3
  */
4
4
  import { external, trash, backup } from '@wordpress/icons';
5
5
  import { addQueryArgs } from '@wordpress/url';
6
- import { useDispatch, useSelect } from '@wordpress/data';
6
+ import { useDispatch, useSelect, useRegistry } from '@wordpress/data';
7
7
  import { decodeEntities } from '@wordpress/html-entities';
8
8
  import { store as coreStore } from '@wordpress/core-data';
9
9
  import { __, _n, sprintf, _x } from '@wordpress/i18n';
@@ -78,7 +78,6 @@ const deletePostAction = {
78
78
  RenderModal: ({
79
79
  items,
80
80
  closeModal,
81
- onActionStart,
82
81
  onActionPerformed
83
82
  }) => {
84
83
  const [isBusy, setIsBusy] = useState(false);
@@ -105,9 +104,6 @@ const deletePostAction = {
105
104
  variant: "primary",
106
105
  onClick: async () => {
107
106
  setIsBusy(true);
108
- if (onActionStart) {
109
- onActionStart(items);
110
- }
111
107
  await removeTemplates(items, {
112
108
  allowUndo: false
113
109
  });
@@ -137,7 +133,6 @@ const trashPostAction = {
137
133
  RenderModal: ({
138
134
  items,
139
135
  closeModal,
140
- onActionStart,
141
136
  onActionPerformed
142
137
  }) => {
143
138
  const [isBusy, setIsBusy] = useState(false);
@@ -168,9 +163,6 @@ const trashPostAction = {
168
163
  variant: "primary",
169
164
  onClick: async () => {
170
165
  setIsBusy(true);
171
- if (onActionStart) {
172
- onActionStart(items);
173
- }
174
166
  const promiseResult = await Promise.allSettled(items.map(item => deleteEntityRecord('postType', item.type, item.id, {}, {
175
167
  throwOnError: true
176
168
  })));
@@ -243,176 +235,192 @@ const trashPostAction = {
243
235
  });
244
236
  }
245
237
  };
246
- function usePermanentlyDeletePostAction() {
247
- const {
248
- createSuccessNotice,
249
- createErrorNotice
250
- } = useDispatch(noticesStore);
251
- const {
252
- deleteEntityRecord
253
- } = useDispatch(coreStore);
238
+ function useCanUserEligibilityCheckPostType(capability, resource, action) {
239
+ const registry = useRegistry();
254
240
  return useMemo(() => ({
255
- id: 'permanently-delete',
256
- label: __('Permanently delete'),
257
- supportsBulk: true,
258
- isEligible({
241
+ ...action,
242
+ isEligible(item) {
243
+ return action.isEligible(item) && registry.select(coreStore).canUser(capability, resource, item.id);
244
+ }
245
+ }), [action, registry, capability, resource]);
246
+ }
247
+ function useTrashPostAction(resource) {
248
+ return useCanUserEligibilityCheckPostType('delete', resource, trashPostAction);
249
+ }
250
+ const permanentlyDeletePostAction = {
251
+ id: 'permanently-delete',
252
+ label: __('Permanently delete'),
253
+ supportsBulk: true,
254
+ isEligible({
255
+ status
256
+ }) {
257
+ return status === 'trash';
258
+ },
259
+ async callback(posts, {
260
+ registry
261
+ }) {
262
+ const {
263
+ createSuccessNotice,
264
+ createErrorNotice
265
+ } = registry.dispatch(noticesStore);
266
+ const {
267
+ deleteEntityRecord
268
+ } = registry.dispatch(coreStore);
269
+ const promiseResult = await Promise.allSettled(posts.map(post => {
270
+ return deleteEntityRecord('postType', post.type, post.id, {
271
+ force: true
272
+ }, {
273
+ throwOnError: true
274
+ });
275
+ }));
276
+ // If all the promises were fulfilled with success.
277
+ if (promiseResult.every(({
259
278
  status
260
- }) {
261
- return status === 'trash';
262
- },
263
- async callback(posts, onActionPerformed) {
264
- const promiseResult = await Promise.allSettled(posts.map(post => {
265
- return deleteEntityRecord('postType', post.type, post.id, {
266
- force: true
267
- }, {
268
- throwOnError: true
269
- });
270
- }));
271
- // If all the promises were fulfilled with success.
272
- if (promiseResult.every(({
273
- status
274
- }) => status === 'fulfilled')) {
275
- let successMessage;
276
- if (promiseResult.length === 1) {
277
- successMessage = sprintf( /* translators: The posts's title. */
278
- __('"%s" permanently deleted.'), getItemTitle(posts[0]));
279
+ }) => status === 'fulfilled')) {
280
+ let successMessage;
281
+ if (promiseResult.length === 1) {
282
+ successMessage = sprintf( /* translators: The posts's title. */
283
+ __('"%s" permanently deleted.'), getItemTitle(posts[0]));
284
+ } else {
285
+ successMessage = __('The posts were permanently deleted.');
286
+ }
287
+ createSuccessNotice(successMessage, {
288
+ type: 'snackbar',
289
+ id: 'permanently-delete-post-action'
290
+ });
291
+ } else {
292
+ // If there was at lease one failure.
293
+ let errorMessage;
294
+ // If we were trying to permanently delete a single post.
295
+ if (promiseResult.length === 1) {
296
+ if (promiseResult[0].reason?.message) {
297
+ errorMessage = promiseResult[0].reason.message;
279
298
  } else {
280
- successMessage = __('The posts were permanently deleted.');
281
- }
282
- createSuccessNotice(successMessage, {
283
- type: 'snackbar',
284
- id: 'permanently-delete-post-action'
285
- });
286
- if (onActionPerformed) {
287
- onActionPerformed(posts);
299
+ errorMessage = __('An error occurred while permanently deleting the post.');
288
300
  }
301
+ // If we were trying to permanently delete multiple posts
289
302
  } else {
290
- // If there was at lease one failure.
291
- let errorMessage;
292
- // If we were trying to permanently delete a single post.
293
- if (promiseResult.length === 1) {
294
- if (promiseResult[0].reason?.message) {
295
- errorMessage = promiseResult[0].reason.message;
296
- } else {
297
- errorMessage = __('An error occurred while permanently deleting the post.');
303
+ const errorMessages = new Set();
304
+ const failedPromises = promiseResult.filter(({
305
+ status
306
+ }) => status === 'rejected');
307
+ for (const failedPromise of failedPromises) {
308
+ if (failedPromise.reason?.message) {
309
+ errorMessages.add(failedPromise.reason.message);
298
310
  }
299
- // If we were trying to permanently delete multiple posts
311
+ }
312
+ if (errorMessages.size === 0) {
313
+ errorMessage = __('An error occurred while permanently deleting the posts.');
314
+ } else if (errorMessages.size === 1) {
315
+ errorMessage = sprintf( /* translators: %s: an error message */
316
+ __('An error occurred while permanently deleting the posts: %s'), [...errorMessages][0]);
300
317
  } else {
301
- const errorMessages = new Set();
302
- const failedPromises = promiseResult.filter(({
303
- status
304
- }) => status === 'rejected');
305
- for (const failedPromise of failedPromises) {
306
- if (failedPromise.reason?.message) {
307
- errorMessages.add(failedPromise.reason.message);
308
- }
309
- }
310
- if (errorMessages.size === 0) {
311
- errorMessage = __('An error occurred while permanently deleting the posts.');
312
- } else if (errorMessages.size === 1) {
313
- errorMessage = sprintf( /* translators: %s: an error message */
314
- __('An error occurred while permanently deleting the posts: %s'), [...errorMessages][0]);
315
- } else {
316
- errorMessage = sprintf( /* translators: %s: a list of comma separated error messages */
317
- __('Some errors occurred while permanently deleting the posts: %s'), [...errorMessages].join(','));
318
- }
318
+ errorMessage = sprintf( /* translators: %s: a list of comma separated error messages */
319
+ __('Some errors occurred while permanently deleting the posts: %s'), [...errorMessages].join(','));
319
320
  }
320
- createErrorNotice(errorMessage, {
321
- type: 'snackbar'
322
- });
323
321
  }
322
+ createErrorNotice(errorMessage, {
323
+ type: 'snackbar'
324
+ });
324
325
  }
325
- }), [createSuccessNotice, createErrorNotice, deleteEntityRecord]);
326
+ }
327
+ };
328
+ function usePermanentlyDeletePostAction(resource) {
329
+ return useCanUserEligibilityCheckPostType('delete', resource, permanentlyDeletePostAction);
326
330
  }
327
- function useRestorePostAction() {
328
- const {
329
- createSuccessNotice,
330
- createErrorNotice
331
- } = useDispatch(noticesStore);
332
- const {
333
- editEntityRecord,
334
- saveEditedEntityRecord
335
- } = useDispatch(coreStore);
336
- return useMemo(() => ({
337
- id: 'restore',
338
- label: __('Restore'),
339
- isPrimary: true,
340
- icon: backup,
341
- supportsBulk: true,
342
- isEligible({
331
+ const restorePostAction = {
332
+ id: 'restore',
333
+ label: __('Restore'),
334
+ isPrimary: true,
335
+ icon: backup,
336
+ supportsBulk: true,
337
+ isEligible({
338
+ status
339
+ }) {
340
+ return status === 'trash';
341
+ },
342
+ async callback(posts, {
343
+ registry,
344
+ onActionPerformed
345
+ }) {
346
+ const {
347
+ createSuccessNotice,
348
+ createErrorNotice
349
+ } = registry.dispatch(noticesStore);
350
+ const {
351
+ editEntityRecord,
352
+ saveEditedEntityRecord
353
+ } = registry.dispatch(coreStore);
354
+ await Promise.allSettled(posts.map(post => {
355
+ return editEntityRecord('postType', post.type, post.id, {
356
+ status: 'draft'
357
+ });
358
+ }));
359
+ const promiseResult = await Promise.allSettled(posts.map(post => {
360
+ return saveEditedEntityRecord('postType', post.type, post.id, {
361
+ throwOnError: true
362
+ });
363
+ }));
364
+ if (promiseResult.every(({
343
365
  status
344
- }) {
345
- return status === 'trash';
346
- },
347
- async callback(posts, onActionPerformed) {
348
- await Promise.allSettled(posts.map(post => {
349
- return editEntityRecord('postType', post.type, post.id, {
350
- status: 'draft'
351
- });
352
- }));
353
- const promiseResult = await Promise.allSettled(posts.map(post => {
354
- return saveEditedEntityRecord('postType', post.type, post.id, {
355
- throwOnError: true
356
- });
357
- }));
358
- if (promiseResult.every(({
359
- status
360
- }) => status === 'fulfilled')) {
361
- let successMessage;
362
- if (posts.length === 1) {
363
- successMessage = sprintf( /* translators: The number of posts. */
364
- __('"%s" has been restored.'), getItemTitle(posts[0]));
365
- } else if (posts[0].type === 'page') {
366
- successMessage = sprintf( /* translators: The number of posts. */
367
- __('%d pages have been restored.'), posts.length);
366
+ }) => status === 'fulfilled')) {
367
+ let successMessage;
368
+ if (posts.length === 1) {
369
+ successMessage = sprintf( /* translators: The number of posts. */
370
+ __('"%s" has been restored.'), getItemTitle(posts[0]));
371
+ } else if (posts[0].type === 'page') {
372
+ successMessage = sprintf( /* translators: The number of posts. */
373
+ __('%d pages have been restored.'), posts.length);
374
+ } else {
375
+ successMessage = sprintf( /* translators: The number of posts. */
376
+ __('%d posts have been restored.'), posts.length);
377
+ }
378
+ createSuccessNotice(successMessage, {
379
+ type: 'snackbar',
380
+ id: 'restore-post-action'
381
+ });
382
+ if (onActionPerformed) {
383
+ onActionPerformed(posts);
384
+ }
385
+ } else {
386
+ // If there was at lease one failure.
387
+ let errorMessage;
388
+ // If we were trying to move a single post to the trash.
389
+ if (promiseResult.length === 1) {
390
+ if (promiseResult[0].reason?.message) {
391
+ errorMessage = promiseResult[0].reason.message;
368
392
  } else {
369
- successMessage = sprintf( /* translators: The number of posts. */
370
- __('%d posts have been restored.'), posts.length);
371
- }
372
- createSuccessNotice(successMessage, {
373
- type: 'snackbar',
374
- id: 'restore-post-action'
375
- });
376
- if (onActionPerformed) {
377
- onActionPerformed(posts);
393
+ errorMessage = __('An error occurred while restoring the post.');
378
394
  }
395
+ // If we were trying to move multiple posts to the trash
379
396
  } else {
380
- // If there was at lease one failure.
381
- let errorMessage;
382
- // If we were trying to move a single post to the trash.
383
- if (promiseResult.length === 1) {
384
- if (promiseResult[0].reason?.message) {
385
- errorMessage = promiseResult[0].reason.message;
386
- } else {
387
- errorMessage = __('An error occurred while restoring the post.');
397
+ const errorMessages = new Set();
398
+ const failedPromises = promiseResult.filter(({
399
+ status
400
+ }) => status === 'rejected');
401
+ for (const failedPromise of failedPromises) {
402
+ if (failedPromise.reason?.message) {
403
+ errorMessages.add(failedPromise.reason.message);
388
404
  }
389
- // If we were trying to move multiple posts to the trash
405
+ }
406
+ if (errorMessages.size === 0) {
407
+ errorMessage = __('An error occurred while restoring the posts.');
408
+ } else if (errorMessages.size === 1) {
409
+ errorMessage = sprintf( /* translators: %s: an error message */
410
+ __('An error occurred while restoring the posts: %s'), [...errorMessages][0]);
390
411
  } else {
391
- const errorMessages = new Set();
392
- const failedPromises = promiseResult.filter(({
393
- status
394
- }) => status === 'rejected');
395
- for (const failedPromise of failedPromises) {
396
- if (failedPromise.reason?.message) {
397
- errorMessages.add(failedPromise.reason.message);
398
- }
399
- }
400
- if (errorMessages.size === 0) {
401
- errorMessage = __('An error occurred while restoring the posts.');
402
- } else if (errorMessages.size === 1) {
403
- errorMessage = sprintf( /* translators: %s: an error message */
404
- __('An error occurred while restoring the posts: %s'), [...errorMessages][0]);
405
- } else {
406
- errorMessage = sprintf( /* translators: %s: a list of comma separated error messages */
407
- __('Some errors occurred while restoring the posts: %s'), [...errorMessages].join(','));
408
- }
412
+ errorMessage = sprintf( /* translators: %s: a list of comma separated error messages */
413
+ __('Some errors occurred while restoring the posts: %s'), [...errorMessages].join(','));
409
414
  }
410
- createErrorNotice(errorMessage, {
411
- type: 'snackbar'
412
- });
413
415
  }
416
+ createErrorNotice(errorMessage, {
417
+ type: 'snackbar'
418
+ });
414
419
  }
415
- }), [createSuccessNotice, createErrorNotice, editEntityRecord, saveEditedEntityRecord]);
420
+ }
421
+ };
422
+ function useRestorePostAction(resource) {
423
+ return useCanUserEligibilityCheckPostType('update', resource, restorePostAction);
416
424
  }
417
425
  const viewPostAction = {
418
426
  id: 'view-post',
@@ -422,7 +430,9 @@ const viewPostAction = {
422
430
  isEligible(post) {
423
431
  return post.status !== 'trash';
424
432
  },
425
- callback(posts, onActionPerformed) {
433
+ callback(posts, {
434
+ onActionPerformed
435
+ }) {
426
436
  const post = posts[0];
427
437
  window.open(post.link, '_blank');
428
438
  if (onActionPerformed) {
@@ -432,6 +442,7 @@ const viewPostAction = {
432
442
  };
433
443
  const postRevisionsAction = {
434
444
  id: 'view-post-revisions',
445
+ context: 'list',
435
446
  label(items) {
436
447
  var _items$0$_links$versi;
437
448
  const revisionsCount = (_items$0$_links$versi = items[0]._links?.['version-history']?.[0]?.count) !== null && _items$0$_links$versi !== void 0 ? _items$0$_links$versi : 0;
@@ -447,7 +458,9 @@ const postRevisionsAction = {
447
458
  const revisionsCount = (_post$_links$version = post?._links?.['version-history']?.[0]?.count) !== null && _post$_links$version !== void 0 ? _post$_links$version : 0;
448
459
  return lastRevisionId && revisionsCount > 1;
449
460
  },
450
- callback(posts, onActionPerformed) {
461
+ callback(posts, {
462
+ onActionPerformed
463
+ }) {
451
464
  const post = posts[0];
452
465
  const href = addQueryArgs('revision.php', {
453
466
  revision: post?._links?.['predecessor-version']?.[0]?.id
@@ -554,42 +567,55 @@ const renamePostAction = {
554
567
  });
555
568
  }
556
569
  };
557
- const duplicatePostAction = {
558
- id: 'duplicate-post',
559
- label: _x('Duplicate', 'action label'),
560
- isEligible({
561
- status
562
- }) {
563
- return status !== 'trash';
564
- },
565
- RenderModal: ({
566
- items,
567
- closeModal,
568
- onActionPerformed
569
- }) => {
570
- const [item] = items;
571
- const [isCreatingPage, setIsCreatingPage] = useState(false);
572
- const [title, setTitle] = useState(sprintf( /* translators: %s: Existing item title */
573
- __('%s (Copy)'), getItemTitle(item)));
574
- const {
575
- saveEntityRecord
576
- } = useDispatch(coreStore);
570
+ function useRenamePostAction(resource) {
571
+ return useCanUserEligibilityCheckPostType('update', resource, renamePostAction);
572
+ }
573
+ const useDuplicatePostAction = postType => {
574
+ const {
575
+ userCanCreatePost
576
+ } = useSelect(select => {
577
577
  const {
578
- createSuccessNotice,
579
- createErrorNotice
580
- } = useDispatch(noticesStore);
581
- async function createPage(event) {
582
- event.preventDefault();
583
- if (isCreatingPage) {
584
- return;
585
- }
586
- setIsCreatingPage(true);
587
- try {
588
- const newItem = await saveEntityRecord('postType', item.type, {
578
+ getPostType,
579
+ canUser
580
+ } = select(coreStore);
581
+ const resource = getPostType(postType)?.rest_base || '';
582
+ return {
583
+ userCanCreatePost: canUser('create', resource)
584
+ };
585
+ }, [postType]);
586
+ return useMemo(() => userCanCreatePost && {
587
+ id: 'duplicate-post',
588
+ label: _x('Duplicate', 'action label'),
589
+ isEligible({
590
+ status
591
+ }) {
592
+ return status !== 'trash';
593
+ },
594
+ RenderModal: ({
595
+ items,
596
+ closeModal,
597
+ onActionPerformed
598
+ }) => {
599
+ const [item] = items;
600
+ const [isCreatingPage, setIsCreatingPage] = useState(false);
601
+ const [title, setTitle] = useState(sprintf( /* translators: %s: Existing item title */
602
+ __('%s (Copy)'), getItemTitle(item)));
603
+ const {
604
+ saveEntityRecord
605
+ } = useDispatch(coreStore);
606
+ const {
607
+ createSuccessNotice,
608
+ createErrorNotice
609
+ } = useDispatch(noticesStore);
610
+ async function createPage(event) {
611
+ event.preventDefault();
612
+ if (isCreatingPage) {
613
+ return;
614
+ }
615
+ const newItemOject = {
589
616
  status: 'draft',
590
617
  title,
591
618
  slug: title || __('No title'),
592
- author: item.author,
593
619
  comment_status: item.comment_status,
594
620
  content: typeof item.content === 'string' ? item.content : item.content.raw,
595
621
  excerpt: item.excerpt.raw,
@@ -600,58 +626,69 @@ const duplicatePostAction = {
600
626
  format: item.format,
601
627
  featured_media: item.featured_media,
602
628
  menu_order: item.menu_order,
603
- ping_status: item.ping_status,
604
- categories: item.categories,
605
- tags: item.tags
606
- }, {
607
- throwOnError: true
608
- });
609
- createSuccessNotice(sprintf(
610
- // translators: %s: Title of the created template e.g: "Category".
611
- __('"%s" successfully created.'), decodeEntities(newItem.title?.rendered || title)), {
612
- id: 'duplicate-post-action',
613
- type: 'snackbar'
629
+ ping_status: item.ping_status
630
+ };
631
+ const assignablePropertiesPrefix = 'wp:action-assign-';
632
+ // Get all the properties that the current user is able to assign normally author, categories, tags,
633
+ // and custom taxonomies.
634
+ const assignableProperties = Object.keys(item?._links || {}).filter(property => property.startsWith(assignablePropertiesPrefix)).map(property => property.slice(assignablePropertiesPrefix.length));
635
+ assignableProperties.forEach(property => {
636
+ if (item[property]) {
637
+ newItemOject[property] = item[property];
638
+ }
614
639
  });
615
- if (onActionPerformed) {
616
- onActionPerformed([newItem]);
640
+ setIsCreatingPage(true);
641
+ try {
642
+ const newItem = await saveEntityRecord('postType', item.type, newItemOject, {
643
+ throwOnError: true
644
+ });
645
+ createSuccessNotice(sprintf(
646
+ // translators: %s: Title of the created template e.g: "Category".
647
+ __('"%s" successfully created.'), decodeEntities(newItem.title?.rendered || title)), {
648
+ id: 'duplicate-post-action',
649
+ type: 'snackbar'
650
+ });
651
+ if (onActionPerformed) {
652
+ onActionPerformed([newItem]);
653
+ }
654
+ } catch (error) {
655
+ const errorMessage = error.message && error.code !== 'unknown_error' ? error.message : __('An error occurred while duplicating the page.');
656
+ createErrorNotice(errorMessage, {
657
+ type: 'snackbar'
658
+ });
659
+ } finally {
660
+ setIsCreatingPage(false);
661
+ closeModal();
617
662
  }
618
- } catch (error) {
619
- const errorMessage = error.message && error.code !== 'unknown_error' ? error.message : __('An error occurred while duplicating the page.');
620
- createErrorNotice(errorMessage, {
621
- type: 'snackbar'
622
- });
623
- } finally {
624
- setIsCreatingPage(false);
625
- closeModal();
626
663
  }
627
- }
628
- return /*#__PURE__*/_jsx("form", {
629
- onSubmit: createPage,
630
- children: /*#__PURE__*/_jsxs(VStack, {
631
- spacing: 3,
632
- children: [/*#__PURE__*/_jsx(TextControl, {
633
- label: __('Title'),
634
- onChange: setTitle,
635
- placeholder: __('No title'),
636
- value: title
637
- }), /*#__PURE__*/_jsxs(HStack, {
638
- spacing: 2,
639
- justify: "end",
640
- children: [/*#__PURE__*/_jsx(Button, {
641
- variant: "tertiary",
642
- onClick: closeModal,
643
- children: __('Cancel')
644
- }), /*#__PURE__*/_jsx(Button, {
645
- variant: "primary",
646
- type: "submit",
647
- isBusy: isCreatingPage,
648
- "aria-disabled": isCreatingPage,
649
- children: _x('Duplicate', 'action label')
664
+ return /*#__PURE__*/_jsx("form", {
665
+ onSubmit: createPage,
666
+ children: /*#__PURE__*/_jsxs(VStack, {
667
+ spacing: 3,
668
+ children: [/*#__PURE__*/_jsx(TextControl, {
669
+ label: __('Title'),
670
+ onChange: setTitle,
671
+ placeholder: __('No title'),
672
+ value: title
673
+ }), /*#__PURE__*/_jsxs(HStack, {
674
+ spacing: 2,
675
+ justify: "end",
676
+ children: [/*#__PURE__*/_jsx(Button, {
677
+ variant: "tertiary",
678
+ onClick: closeModal,
679
+ children: __('Cancel')
680
+ }), /*#__PURE__*/_jsx(Button, {
681
+ variant: "primary",
682
+ type: "submit",
683
+ isBusy: isCreatingPage,
684
+ "aria-disabled": isCreatingPage,
685
+ children: _x('Duplicate', 'action label')
686
+ })]
650
687
  })]
651
- })]
652
- })
653
- });
654
- }
688
+ })
689
+ });
690
+ }
691
+ }, [userCanCreatePost]);
655
692
  };
656
693
  const isTemplatePartRevertable = item => {
657
694
  if (!item) {
@@ -672,7 +709,6 @@ const resetTemplateAction = {
672
709
  RenderModal: ({
673
710
  items,
674
711
  closeModal,
675
- onActionStart,
676
712
  onActionPerformed
677
713
  }) => {
678
714
  const [isBusy, setIsBusy] = useState(false);
@@ -736,9 +772,6 @@ const resetTemplateAction = {
736
772
  variant: "primary",
737
773
  onClick: async () => {
738
774
  setIsBusy(true);
739
- if (onActionStart) {
740
- onActionStart(items);
741
- }
742
775
  await onConfirm(items);
743
776
  onActionPerformed?.(items);
744
777
  setIsBusy(false);
@@ -808,24 +841,41 @@ export const duplicateTemplatePartAction = {
808
841
  });
809
842
  }
810
843
  };
811
- export function usePostActions(postType, onActionPerformed) {
844
+ export function usePostActions({
845
+ postType,
846
+ onActionPerformed,
847
+ context
848
+ }) {
812
849
  const {
813
850
  defaultActions,
814
- postTypeObject
851
+ postTypeObject,
852
+ userCanCreatePostType,
853
+ resource,
854
+ cachedCanUserResolvers
815
855
  } = useSelect(select => {
816
856
  const {
817
- getPostType
857
+ getPostType,
858
+ canUser,
859
+ getCachedResolvers
818
860
  } = select(coreStore);
819
861
  const {
820
862
  getEntityActions
821
863
  } = unlock(select(editorStore));
864
+ const _postTypeObject = getPostType(postType);
865
+ const _resource = _postTypeObject?.rest_base || '';
822
866
  return {
823
- postTypeObject: getPostType(postType),
824
- defaultActions: getEntityActions('postType', postType)
867
+ postTypeObject: _postTypeObject,
868
+ defaultActions: getEntityActions('postType', postType),
869
+ userCanCreatePostType: canUser('create', _resource),
870
+ resource: _resource,
871
+ cachedCanUserResolvers: getCachedResolvers()?.canUser
825
872
  };
826
873
  }, [postType]);
827
- const permanentlyDeletePostAction = usePermanentlyDeletePostAction();
828
- const restorePostAction = useRestorePostAction();
874
+ const duplicatePostAction = useDuplicatePostAction(postType);
875
+ const trashPostActionForPostType = useTrashPostAction(resource);
876
+ const permanentlyDeletePostActionForPostType = usePermanentlyDeletePostAction(resource);
877
+ const renamePostActionForPostType = useRenamePostAction(resource);
878
+ const restorePostActionForPostType = useRestorePostAction(resource);
829
879
  const isTemplateOrTemplatePart = [TEMPLATE_POST_TYPE, TEMPLATE_PART_POST_TYPE].includes(postType);
830
880
  const isPattern = postType === PATTERN_POST_TYPE;
831
881
  const isLoaded = !!postTypeObject;
@@ -835,14 +885,28 @@ export function usePostActions(postType, onActionPerformed) {
835
885
  if (!isLoaded) {
836
886
  return [];
837
887
  }
838
- const actions = [postTypeObject?.viewable && viewPostAction, supportsRevisions && postRevisionsAction, globalThis.IS_GUTENBERG_PLUGIN ? !isTemplateOrTemplatePart && !isPattern && duplicatePostAction : false, isTemplateOrTemplatePart && duplicateTemplatePartAction, isPattern && duplicatePatternAction, supportsTitle && renamePostAction, isPattern && exportPatternAsJSONAction, isTemplateOrTemplatePart ? resetTemplateAction : restorePostAction, isTemplateOrTemplatePart || isPattern ? deletePostAction : trashPostAction, !isTemplateOrTemplatePart && permanentlyDeletePostAction, ...defaultActions].filter(Boolean);
888
+ let actions = [postTypeObject?.viewable && viewPostAction, supportsRevisions && postRevisionsAction, globalThis.IS_GUTENBERG_PLUGIN ? !isTemplateOrTemplatePart && !isPattern && duplicatePostAction : false, isTemplateOrTemplatePart && userCanCreatePostType && duplicateTemplatePartAction, isPattern && userCanCreatePostType && duplicatePatternAction, supportsTitle && renamePostActionForPostType, isPattern && exportPatternAsJSONAction, isTemplateOrTemplatePart ? resetTemplateAction : restorePostActionForPostType, isTemplateOrTemplatePart || isPattern ? deletePostAction : trashPostActionForPostType, !isTemplateOrTemplatePart && permanentlyDeletePostActionForPostType, ...defaultActions].filter(Boolean);
889
+ // Filter actions based on provided context. If not provided
890
+ // all actions are returned. We'll have a single entry for getting the actions
891
+ // and the consumer should provide the context to filter the actions, if needed.
892
+ // Actions should also provide the `context` they support, if it's specific, to
893
+ // compare with the provided context to get all the actions.
894
+ // Right now the only supported context is `list`.
895
+ actions = actions.filter(action => {
896
+ if (!action.context) {
897
+ return true;
898
+ }
899
+ return action.context === context;
900
+ });
839
901
  if (onActionPerformed) {
840
902
  for (let i = 0; i < actions.length; ++i) {
841
903
  if (actions[i].callback) {
842
904
  const existingCallback = actions[i].callback;
843
905
  actions[i] = {
844
906
  ...actions[i],
845
- callback: (items, _onActionPerformed) => {
907
+ callback: (items, {
908
+ _onActionPerformed
909
+ }) => {
846
910
  existingCallback(items, _items => {
847
911
  if (_onActionPerformed) {
848
912
  _onActionPerformed(_items);
@@ -872,6 +936,9 @@ export function usePostActions(postType, onActionPerformed) {
872
936
  }
873
937
  }
874
938
  return actions;
875
- }, [defaultActions, isTemplateOrTemplatePart, isPattern, postTypeObject?.viewable, permanentlyDeletePostAction, restorePostAction, onActionPerformed, isLoaded, supportsRevisions, supportsTitle]);
939
+ // We are making this use memo depend on cachedCanUserResolvers as a way to make the component using this hook re-render
940
+ // when user capabilities are resolved. This makes sure the isEligible functions of actions dependent on capabilities are re-evaluated.
941
+ // eslint-disable-next-line react-hooks/exhaustive-deps
942
+ }, [defaultActions, userCanCreatePostType, isTemplateOrTemplatePart, isPattern, postTypeObject?.viewable, duplicatePostAction, trashPostActionForPostType, restorePostActionForPostType, renamePostActionForPostType, permanentlyDeletePostActionForPostType, onActionPerformed, isLoaded, supportsRevisions, supportsTitle, context, cachedCanUserResolvers]);
876
943
  }
877
944
  //# sourceMappingURL=actions.js.map