Guidelines: Add actions for Import, Export and Revisions of guidelines#76155
Conversation
|
👋 Thanks for your first Pull Request and for helping build the future of Gutenberg and WordPress, @iamchughmayank! In case you missed it, we'd love to have you join us in our Slack community. If you want to learn more about WordPress development in general, check out the Core Handbook full of helpful information. |
ff441e7 to
6524a77
Compare
|
The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs. You can also manually ask me to refresh this list by adding the If you're merging code through a pull request on GitHub, copy and paste the following into the bottom of the merge commit message. To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook. |
0c948c2 to
c1e016e
Compare
|
Nice work on this @iamchughmayank. This is a great start! Here's what I found: Revision messaging When I first worked on this, I imagined that the rest of the history would get erased. Seeing it now, I understand we just add a new revision and you can in fact undo this. Can we replace the this can't be undone messaging to "A new revision will be saved so you can always come back."
General alignment and clipping
If we add 8px of left/right padding to the content (dataview plus description under the back), things line up nicely and we don't get that clipping:
Table alignment
Any chance we can get that cleaned up?
Column widths Any chance we can have the first column fill the table width and have the last two columns packed closer together? This is what I had in my design:
Revision preview I didn't have this in my design but I see now how the table doesn't offer much to go on other than the date and person who made the change. Is there any way we can introduce a preview of what changed? Due to the modular nature of the screen, I can see the updates being something like "Updated Site guidlines" or "Added paragraph block guidelines". Ideally we place that as the first column and then pack the date, user, and actions on the far right of the table. Refreshing When I visit the revision screen and refresh, it goes back to the main content guidelines page. Any chance we can keep it there? Screen.Recording.2026-03-06.at.1.47.26.PM.mov |
917d8fe to
31c30ce
Compare
|
@fditrapani - Thanks for the design review. I have implemented the suggestions and tried to add fixes as well. I think it can use another quick glance :) |
|
nice work @iamchughmayank. Looking good! Two minor things: Import modal Copy tweak Can we update "A new version will be saved so you can always come back." to "You can undo this anytime from revision history."
|
Re
Sorry, I did not realise that was the intention. Are you suggesting that after the file is picked, we show the modal. If the user hits "Continue" then only we carry on with the guidelines import. "Cancel" should drop the selected file. Is that correct? |
No problem. We didn't make it explicit one way or the other. The way you described it is how I expected it to work. |
Thanks for the confirmation. I have updated both points in 01d03c3 |
fditrapani
left a comment
There was a problem hiding this comment.
Nice work @iamchughmayank and thanks for the updates. Looks good on my end!
@fditrapani Thanks :) I was able to scan all the occurrences. Finding the ones wrapped in |
Note that the WP Admin URL will still have |
aagam-shah
left a comment
There was a problem hiding this comment.
Thanks for working on this and addressing all feedback 🚢
|
Thanks for the updates @iamchughmayank! The general Content Guidelines > Guidelines update looks good. Noting @saroshaga's point that the URL needs to be updated too. I also noticed the following When you upload an invalid file, we say "We ran into a problem importing your guidelines: Check that your file contains valid JSON markup and try again.". Can we update this to a proper sentence. It should read The generalized snack copy feels robotic and generalized (for a lack of a better word). If we can't say the specific categories (ie: Site guidlines saved., Copy guidelines saved.), can we just say "Guidelines saved."? I could swear the date header column was aligned to the left at one point. Right now it's misaligned with the content in the column. Revision table dataview options
|
|
@fditrapani
I hope each snap is self-explanatory. What is your recommendation for these? |
…oving revision history columns feature
@iamchughmayank : This is incorrect IMO. I agree with @fditrapani that we need some alignment on the toast notifications. How about we keep it simple with just "Guideline Saved" and "Guideline Removed" ? |
Thanks @saroshaga. This works for me! One minor detail is we should use sentence case so it would be "Guideline saved." and "Guideline removed." |
@saroshaga @fditrapani , I have pushed the changes in dfd8932 whilst maintaining plural condition -
|
saroshaga
left a comment
There was a problem hiding this comment.
Thank you for diligently addressing the feedback @iamchughmayank !! There are a few non-blocking items that we have already planned for.
I ran a functional test after the latest changes, and it looks good!
fditrapani
left a comment
There was a problem hiding this comment.
Looking good on my end too! thanks for all the updates @iamchughmayank.
andrewserong
left a comment
There was a problem hiding this comment.
Nice continued progress here folks! I've left a bunch of comments, but nothing blocking while this is still behind an experiment. A couple of general notes:
- There's lots of places where
as unknownis used, orunknownis used as a type on function parameters. In general this means there's an opportunity to cover missing types / additional error states that might have been missed. - The
api.tsfile now includes functions that could very well be actions on the store instead (i.e. take a look at thecore-datapackage as inspiration) - If refactoring to that approach, there's an opportunity to split the store file up into separate files, and to add test coverage
Usability-wise, this is all testing well for me:
✅ Exporting data worked well
✅ Importing a (valid) file worked as expected
✅ Basic error handling works okay (i.e. invalid JSON format, or malformed JSON)
A couple of other observations:
- Now that importing and exporting has been added, tests should be included to be explicit about what's a supported format. If / when the structure of the JSON changes, it'll be important to ensure formats can be migrated or handled gracefully.
- While renaming from Content Guidelines to Guidelines makes for simpler language, I'm wondering if it's too simple? As a word "guidelines" could be interpreted as meaning guidelines for user behaviour on a platform (e.g. code of conduct), so I'd favour being specific in naming so that it's clearer what this feature is for. In any case, I'm sure y'all will continue iterating on this 😄
This LGTM for now, so I'll merge this in. No need to reply to all my comments, I mostly wanted to capture the notes as you continue iterating on the feature, as now that's growing in feature scope, it could be a good time to polish some of the architecture in follow-ups.
Nice work again! 🚀
| // Add embeddable author link to get author name in revision history screen | ||
| if ( ! empty( $item->post_author ) ) { | ||
| $response->add_link( | ||
| 'author', | ||
| rest_url( 'wp/v2/users/' . $item->post_author ), | ||
| array( 'embeddable' => true ) | ||
| ); | ||
| } | ||
|
|
There was a problem hiding this comment.
If you want to include the author in the response, I think you can also do that via registering 'author' as one of the supports somewhere around here:
Though if you do that, you'd also want to make sure 'delete_with_user' is set to false so that if the user who created the guidelines is removed, the guidelines themselves are not.
(Not a blocker, just something that could be looked at in a follow-up if need be)
Edit: Oh wait, I don't think that approach will work since this isn't inheriting from the Post controller. Ignore me! 😄
| */ | ||
| import { importContentGuidelines, exportContentGuidelines } from '../api'; | ||
|
|
||
| function getErrorMessage( error: unknown ): string { |
There was a problem hiding this comment.
Why is the error type unknown here? If we're throwing errors from within functions we've written, shouldn't we know what type it is by the time we get to this function?
(Not a blocker)
| try { | ||
| const result = await fetchContentGuidelinesRevisions( { | ||
| guidelinesId: guidelinesId!, | ||
| perPage: 100, |
There was a problem hiding this comment.
Yes, in a follow-up, it would be good to using the selected page size here instead of hard-coding 👍
| <ul role="list" className="content-guidelines__actions-list"> | ||
| { ACTIONS.map( ( action ) => { | ||
| const descriptionId = `content-guidelines-action-${ action.slug }-description`; | ||
| return ( |
There was a problem hiding this comment.
The return statement here is fairly long, so there's possibly an opportunity here to decompose into a sub-component for the action items, which could help readability. It would also offer a way to avoid the buttonProps object which stood out to me as slightly odd.
I.e. instead of an ACTIONS array, create an Action component and pass it what it needs. You then wouldn't need a map either?
Not a blocker for now, the code you've got here works, just stuff to look at in follow-ups as the way it's written looks a little tangled to me.
| select( STORE_NAME ) as unknown as { | ||
| getId: () => number | null; | ||
| } |
There was a problem hiding this comment.
I'll just comment on this once but this comes up a few times in this PR — instead of using the STORE_NAME you can import the exported store, then I think you can avoid using as unknown. Since this route is written in TypeScript, we can hopefully avoid any and unknown so that we can try to keep things typed where we can.
| const FLAT_CATEGORIES = [ 'site', 'copy', 'images', 'additional' ] as const; | ||
|
|
||
| function isValidGuidelinesImport( | ||
| data: unknown |
There was a problem hiding this comment.
Why is data unknown at this stage? Can it be given a proper type?
| const { setGuideline, setBlockGuideline } = dispatch( STORE_NAME ); | ||
| const { createSuccessNotice } = dispatch( noticesStore ); | ||
|
|
||
| const parsed: unknown = JSON.parse( await file.text() ); |
There was a problem hiding this comment.
Should JSON syntax errors be handled explicitly somewhere? I.e. to have a friendlier version of the following error:
(This might slightly depend on the intended audience for this feature. If you're a developer, the granular error here might be okay, but to a general user uploading malformed JSON, it might be awkward)
| }; | ||
|
|
||
| downloadBlob( | ||
| 'guidelines.json', |
There was a problem hiding this comment.
Just an idea for a follow-up, but would it be worth including the date in the filename? I.e. how are folks going to be using the exported format?
| const response = ( await apiFetch( { | ||
| path: `/wp/v2/content-guidelines/${ guidelinesId }/revisions?${ params }`, | ||
| parse: false, | ||
| } ) ) as Response; |
There was a problem hiding this comment.
is the as Response here necessary?
| .content-guidelines__actions-notice { | ||
| border: none; | ||
| border-left: 4px solid $alert-yellow; | ||
| background-color: color.adjust($alert-yellow, $lightness: +35%); |
There was a problem hiding this comment.
Looks like this is just applying the background color of the is-warning state of the Notice component. Is this necessary?
#76155) * Add block guidelines management * Fix spacing of the dataviews * Remove dataviews sort and filter And omit dataviews search too when there are less than 6 items * Exclude blocks that already have guidelines from add-guideline list Also, use textcontrol instead of combobox in edit mode * Show confirmation modal when deleting block guideline from table * Improve save/delete logic * Use base-style variable * Fix bootstrapBlockRegistry() placement * Add types * Use hstack wrapper for add-block-guideline btn * Do not remove block-key when removing block guideline This is because the backend expects empty string in order to delete a block * Fix issue with empty page after deleting rows * Fix mistake with wrapping up action-text in double quote * Add . after success notice text * Remove undefined keys from block entries * Add clarifying comment for setting empty string to delete a block guideline * Content Guidelines: Add actions section UI * Adds simple import and export function * Content Guidelines: Add manage access and revision screens * Content Guidelines: Search in revision history screen * Content guidelines: Use filterSortAndPaginate from DataViews to implement search in guidelines revision history screen * Content guidelines: Hide Manage Access action button for MVP * Improve spacing * Content guidelines: Use DataViews inbuilt filtering * Refetch content-guidelines after restoring a revision * Improve revision-history page styles * Content guidelines: remove manage-access from MVP * Content Guidelines: Use Notice as error message component instead of Snackbar * Content Guidelines: Addresses PR feedback: 1. Uses downloadBlob from wordpress/blob 2. Adds isRTL support to the chevron 3. Uses dateI18n to format date on revision screen 4. Adds efficient catching of error during revision restoration * Content Guidelines: Align author to the right of revision history table * Content guidelines: Rebase with #76187 * Content guideliens: Adds confirmation modal to import guidelines * Content guidelines: Updates modal sequence and copy * Content guidelines: Addresses PR feedback to:1. Fix unnecessary try/catch 2. Remov unnecessary lint escapes 3. Use memoisation to avoid rerenders * Content guidelines: Remove unnecessary overrides in CSS, where possible * Content guidelines: Use memoisation in filter and pagination to avoid rerenders * Content guidelines: Revert changes done by incorrect rebase and merge * Content guidelines: Revert unnecessary changes brought in incorrect rebase * Content guidelines: Handle guidelines import error correctly by restoring previous version * Content guidelines: Pre-emptively add override for wordpress/ui Notice component * Content guidelines: Update copy for content guidelines import error * Guidelines: Rename "Content Guidelines" to "Guidelines" in all user-facing strings Rename the feature from "Content Guidelines" to "Guidelines" across all user-facing strings (page title, menu item, experiments toggle, notices, modals, aria labels, error messages) while keeping variable and file names unchanged. This aligns with the decision to use the shorter name while the feature remains experimental. Co-Authored-By: Claude Opus 4.6 <[email protected]> * Content guidelines: Change the text 'content guidelines' to 'guidelines' in user facing interfaces * Content Guidelines: Remove lint override which is not live yet * Content guidelines: Updates copy of import error notice and removes moving revision history columns feature * Content guidelines: Updates snackbar/toast message copy --------- Co-authored-by: Ahmed Sayeed Wasif <[email protected]> Co-authored-by: Aagam Shah <[email protected]> Co-authored-by: Claude Opus 4.6 <[email protected]> Co-authored-by: aswasif007 <[email protected]> Co-authored-by: iamchughmayank <[email protected]> Co-authored-by: aagam-shah <[email protected]> Co-authored-by: fditrapani <[email protected]> Co-authored-by: saroshaga <[email protected]> Co-authored-by: mirka <[email protected]> Co-authored-by: Jameswlepage <[email protected]>
|
I just cherry-picked this PR to the release/22.7 branch to get it included in the next release: 10a8796 |



















What?
Closes
Extends the Content Guidelines admin page (introduced in #75420) with four operational actions and two new sub-screens:
Why?
Beyond the PR ( #75420 ), this brings feature to maintain revision history and the feature to use custom JSON as content guidelines.
How?
Actions section
A new component renders four action rows (Import, Export, Manage Access, View history).
.jsonfiles to be uploadedguidelines.jsonRevision History sub-screen
A
/revision-historyNavigator screen built onDataViews(table layout):Mar 4, 2026, 7:06 pm) and User (author display name)GET /wp/v2/content-guidelines/{id}/revisions?_embed=author, readingX-WP-Total/X-WP-TotalPagesresponse headersincludesandis none of.POST /wp/v2/content-guidelines/{id}/revisions/{rev_id}/restorePHP: author embed fix
WP_REST_Revisions_Controller(core) has noprepare_links(), so its responses never include anauthorlink and?_embed=authorsilently returns nothing. Added$response->add_link('author', rest_url('wp/v2/users/' . $item->post_author), ['embeddable' => true])inGutenberg_Content_Guidelines_Revisions_Controller::prepare_item_for_response()— the same pattern used byWP_REST_Posts_Controller.Testing Instructions
.jsonfile downloads with the current guidelines.