Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Data Views: Add action for pages to set site homepage #65426

Merged
merged 96 commits into from
Nov 28, 2024
Merged
Show file tree
Hide file tree
Changes from 74 commits
Commits
Show all changes
96 commits
Select commit Hold shift + click to select a range
c7101cf
Adds basic action and modal to set page as homepage
creativecoder Sep 16, 2024
585ef01
Adds permissions and settings checks to set as homepage action
creativecoder Sep 16, 2024
41474da
Adds proper description and handles unpublished pages
creativecoder Sep 17, 2024
921d0f4
Adds action to set homepage to show latest posts
creativecoder Sep 18, 2024
23cefbd
Doesn't show action if there's a front-page template
creativecoder Sep 19, 2024
fb35be0
Creates page for posts, when specified
creativecoder Sep 19, 2024
5de983c
Refactors modal component
creativecoder Sep 30, 2024
0f3a986
Fixes issues from rebase
creativecoder Oct 1, 2024
1bdf2e0
Only show option on published pages
mikachan Oct 7, 2024
937ad00
Update snackbar wording
mikachan Oct 7, 2024
7df97a8
Check item exists before running getItemTitle logic
mikachan Oct 7, 2024
91a8275
Merge branch 'trunk' into try/site-editor-homepage-settings
mikachan Oct 7, 2024
44feed5
Make key optional on GetEntityRecord
mikachan Oct 7, 2024
768f9cb
Remove some ts-ignore comments
mikachan Oct 7, 2024
a04fb70
Add support for page_for_posts to Settings
mikachan Oct 7, 2024
7462ce7
Remove some more ts-ignores
mikachan Oct 7, 2024
2d2c856
Allow recordId to be optional
mikachan Oct 7, 2024
8bbd86d
Increase size of action modal
mikachan Oct 8, 2024
6fc3490
Implement choose existing page option
mikachan Oct 8, 2024
341a068
Fix number/string comparison
mikachan Oct 8, 2024
b27305f
Add initial e2e test
mikachan Oct 9, 2024
b387c34
Set post actions modal to medium
mikachan Oct 9, 2024
2429d77
Tweak ToggleGroupControl help text
mikachan Oct 9, 2024
57eceb1
Fix initial test
mikachan Oct 9, 2024
49be2ab
Merge branch 'trunk' into try/site-editor-homepage-settings
mikachan Oct 23, 2024
40568dc
Remove extra useSiteSettings hook
mikachan Oct 23, 2024
489f4d6
Allow setting draft pages to homepage
mikachan Oct 23, 2024
c36e3ac
Merge branch 'trunk' into try/site-editor-homepage-settings
mikachan Oct 29, 2024
3cb07e9
Fix merge conflict
mikachan Oct 29, 2024
9a5f4d7
Remove item check from getItemTitle
mikachan Oct 31, 2024
c4bd9ae
Remove posts page options
mikachan Nov 8, 2024
46071d7
Don't show homepage option if selected page is the page for posts
mikachan Nov 8, 2024
42e0791
Reload actions list when site settings change
mikachan Nov 8, 2024
3434c3f
Update tests
mikachan Nov 8, 2024
e84a33e
Merge branch 'trunk' into try/site-editor-homepage-settings
mikachan Nov 8, 2024
baea572
Remove call to __experimentalGetTemplateForLink
mikachan Nov 8, 2024
c413a17
Update tests
mikachan Nov 8, 2024
ba8becb
Remove item check in getItemTitle
mikachan Nov 12, 2024
7687e3c
Use useSelect instead of select
mikachan Nov 12, 2024
81b76f8
Remove PAGE_POST_TYPE constant
mikachan Nov 12, 2024
0ca96b0
Use saveEntityRecord instead of editEntityRecord
mikachan Nov 12, 2024
17754bd
Remove onSetLatestPostsHomepage option
mikachan Nov 12, 2024
e722ceb
Remove select for site settings from isEligible
mikachan Nov 12, 2024
91de6c0
Update post actions with site settings info
mikachan Nov 12, 2024
3c71533
Remove select for templates from isEligible
mikachan Nov 12, 2024
6b2f6b9
Skip last test for now
mikachan Nov 12, 2024
6a1f468
Restore whitespace
mikachan Nov 12, 2024
b81eb1c
Rename _select
mikachan Nov 13, 2024
6d36c1b
Remove sub-objects from additionalContext selectors
mikachan Nov 13, 2024
8b40105
Merge branch 'trunk' into try/site-editor-homepage-settings
mikachan Nov 15, 2024
3caa096
Remove duplicate page_for_posts definition
mikachan Nov 15, 2024
d20b87b
Fix page/post type error
mikachan Nov 15, 2024
29a1f72
Remove additional groups within additionalContext
mikachan Nov 15, 2024
0c5883a
Fix siteSettings in TitleView
mikachan Nov 15, 2024
24dcf8f
Move hasFrontPageTemplate check to private-actions
mikachan Nov 15, 2024
095bbe3
Add JSDoc to setAsHomepage
mikachan Nov 15, 2024
991ac5c
Refactor siteSettings in post-list
mikachan Nov 15, 2024
f2acc94
Move homepage action to edit-site
mikachan Nov 15, 2024
969ed4a
Revert unnecessary changes
mikachan Nov 15, 2024
259a476
Move getItemTitle to edit-site utils
mikachan Nov 15, 2024
efbf96c
Allow undefined on GetEntityRecord key
mikachan Nov 15, 2024
9de4488
Make it more clear that draft page will be published
mikachan Nov 15, 2024
5cbe7b4
Update draft page wording
mikachan Nov 18, 2024
7a2960a
Add set homepage action to post editor
mikachan Nov 18, 2024
4d45eaf
Merge branch 'trunk' into try/site-editor-homepage-settings
mikachan Nov 18, 2024
fb2ea4c
Attempt to fix build error
mikachan Nov 18, 2024
1a1f682
Remove homepage action from edit-site
mikachan Nov 18, 2024
c10846b
Remove extra line
mikachan Nov 18, 2024
5e5cf0c
Fix getting current homepage title
mikachan Nov 18, 2024
2b37368
Make key in getEntityRecord optional
mikachan Nov 18, 2024
43dd85f
Use getHomePage selector
mikachan Nov 18, 2024
4cd6988
Move canManageOptions and hasFrontPageTemplate to actions.js
mikachan Nov 18, 2024
b2e88eb
Make key optional in EntityRecordKey
mikachan Nov 19, 2024
953e999
Remove undefined from getEntityRecord calls
mikachan Nov 19, 2024
f01d347
Update packages/editor/src/components/post-actions/actions.js
mikachan Nov 20, 2024
d96e90e
Update getEntityRecord key docs
mikachan Nov 20, 2024
9685c3a
Refactor fetching currentHomePage
mikachan Nov 20, 2024
b029794
Disable modal buttons if saving
mikachan Nov 20, 2024
a9ea268
Store isPageDraft in useRef
mikachan Nov 20, 2024
e438496
Fix lint error
mikachan Nov 20, 2024
04a7ba4
Remove onActionPerformed
mikachan Nov 20, 2024
c97a17d
Fix current homepage test
mikachan Nov 20, 2024
0b8c27e
Remove duplicate getItemTitle function
mikachan Nov 21, 2024
f8df3b9
Merge branch 'trunk' into try/site-editor-homepage-settings
mikachan Nov 21, 2024
a175adc
Update logic for shouldShowSetAsHomepageAction
mikachan Nov 22, 2024
86af09a
Swap order of list of actions
mikachan Nov 22, 2024
10d97b7
Add comment about manual saveEntityRecord call
mikachan Nov 22, 2024
c23c35b
Remove unnecessary space
mikachan Nov 22, 2024
b1707e4
Remove temporary modalButtonLabel variable
mikachan Nov 22, 2024
d6a7059
Combine draft and publish status tests
mikachan Nov 22, 2024
a696688
Merge branch 'trunk' into try/site-editor-homepage-settings
mikachan Nov 22, 2024
6d01e34
Only allow action on pages with draft or publish status
mikachan Nov 25, 2024
18aeaf6
Remove handling of draft pages
mikachan Nov 25, 2024
d486bac
Merge branch 'trunk' into try/site-editor-homepage-settings
mikachan Nov 25, 2024
8bacdbb
Move closeModal into finally block
mikachan Nov 27, 2024
e1a7aa9
Refactor and remove renderModalBody
mikachan Nov 27, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion packages/core-data/src/private-selectors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,6 @@ export const getHomePage = createRegistrySelector( ( select ) =>
return { postType: 'wp_template', postId: frontPageTemplateId };
},
( state ) => [
// @ts-expect-error
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When I made key in GetEntityRecord optional, this line errored as an "Unused '@ts-expect-error' directive.", so I removed it and everything seems good when building 🤞

getEntityRecord( state, 'root', 'site' ),
getDefaultTemplateId( state, {
slug: 'front-page',
Expand Down
6 changes: 3 additions & 3 deletions packages/core-data/src/selectors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -310,7 +310,7 @@ export interface GetEntityRecord {
state: State,
kind: string,
name: string,
key: EntityRecordKey,
key?: EntityRecordKey,
getdave marked this conversation as resolved.
Show resolved Hide resolved
query?: GetRecordsHttpQuery
): EntityRecord | undefined;

Expand All @@ -321,7 +321,7 @@ export interface GetEntityRecord {
>(
kind: string,
name: string,
key: EntityRecordKey,
key?: EntityRecordKey,
query?: GetRecordsHttpQuery
) => EntityRecord | undefined;
__unstableNormalizeArgs?: ( args: EntityRecordArgs ) => EntityRecordArgs;
Expand Down Expand Up @@ -350,7 +350,7 @@ export const getEntityRecord = createSelector(
state: State,
kind: string,
name: string,
key: EntityRecordKey,
key?: EntityRecordKey,
query?: GetRecordsHttpQuery
): EntityRecord | undefined => {
const queriedState =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ export function ActionModal< Item >( {
__experimentalHideHeader={ !! action.hideModalHeader }
onRequestClose={ closeModal ?? ( () => {} ) }
focusOnMount="firstContentElement"
size="small"
size="medium"
overlayClassName={ `dataviews-action-modal dataviews-action-modal__${ kebabCase(
action.id
) }` }
Expand Down
40 changes: 38 additions & 2 deletions packages/editor/src/components/post-actions/actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,14 @@
*/
import { useDispatch, useSelect } from '@wordpress/data';
import { useMemo, useEffect } from '@wordpress/element';
import { store as coreStore } from '@wordpress/core-data';

/**
* Internal dependencies
*/
import { store as editorStore } from '../../store';
import { unlock } from '../../lock-unlock';
import { useSetAsHomepageAction } from './set-as-homepage';

export function usePostActions( { postType, onActionPerformed, context } ) {
const { defaultActions } = useSelect(
Expand All @@ -21,19 +23,47 @@ export function usePostActions( { postType, onActionPerformed, context } ) {
[ postType ]
);

const { canManageOptions, hasFrontPageTemplate } = useSelect(
( select ) => {
const { getEntityRecords } = select( coreStore );
const templates = getEntityRecords( 'postType', 'wp_template', {
per_page: -1,
} );

return {
canManageOptions: select( coreStore ).canUser( 'update', {
kind: 'root',
name: 'site',
} ),
hasFrontPageTemplate: !! templates?.find(
( template ) =>
'slug' in template && template.slug === 'front-page'
mikachan marked this conversation as resolved.
Show resolved Hide resolved
),
};
}
);

const setAsHomepageAction = useSetAsHomepageAction();
const shouldShowSetAsHomepageAction =
canManageOptions || ! hasFrontPageTemplate;
mikachan marked this conversation as resolved.
Show resolved Hide resolved

const { registerPostTypeActions } = unlock( useDispatch( editorStore ) );
useEffect( () => {
registerPostTypeActions( postType );
}, [ registerPostTypeActions, postType ] );

return useMemo( () => {
let actions = [
shouldShowSetAsHomepageAction ? setAsHomepageAction : [],
...defaultActions,
];
mikachan marked this conversation as resolved.
Show resolved Hide resolved
// Filter actions based on provided context. If not provided
// all actions are returned. We'll have a single entry for getting the actions
// and the consumer should provide the context to filter the actions, if needed.
// Actions should also provide the `context` they support, if it's specific, to
// compare with the provided context to get all the actions.
// Right now the only supported context is `list`.
const actions = defaultActions.filter( ( action ) => {
actions = actions.filter( ( action ) => {
if ( ! action.context ) {
return true;
}
Expand Down Expand Up @@ -88,5 +118,11 @@ export function usePostActions( { postType, onActionPerformed, context } ) {
}

return actions;
}, [ defaultActions, onActionPerformed, context ] );
}, [
context,
defaultActions,
onActionPerformed,
setAsHomepageAction,
shouldShowSetAsHomepageAction,
] );
}
2 changes: 1 addition & 1 deletion packages/editor/src/components/post-actions/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ function ActionWithModal( { action, item, ActionTrigger, onClose } ) {
action.id
) }` }
focusOnMount="firstContentElement"
size="small"
size="medium"
>
<RenderModal
items={ [ item ] }
Expand Down
217 changes: 217 additions & 0 deletions packages/editor/src/components/post-actions/set-as-homepage.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,217 @@
/**
* WordPress dependencies
*/
import { __, sprintf } from '@wordpress/i18n';
import { useEffect, useMemo, useState } from '@wordpress/element';
import {
Button,
__experimentalText as Text,
__experimentalHStack as HStack,
__experimentalVStack as VStack,
} from '@wordpress/components';
import { useDispatch, useSelect, resolveSelect } from '@wordpress/data';
import { store as coreStore } from '@wordpress/core-data';
import { store as noticesStore } from '@wordpress/notices';

/**
* Internal dependencies
*/
import { getItemTitle } from '../../utils/get-item-title';
import { unlock } from '../../lock-unlock';

const SetAsHomepageModal = ( { items, closeModal, onActionPerformed } ) => {
const [ item ] = items;
const pageTitle = getItemTitle( item );
const { showOnFront, currentHomePage } = useSelect( ( select ) => {
const { getEntityRecord } = select( coreStore );
const { getHomePage } = unlock( select( coreStore ) );
const siteSettings = getEntityRecord( 'root', 'site' );
const homePage = getHomePage();
return {
showOnFront: siteSettings?.show_on_front,
currentHomePage: homePage.postId,
};
} );
const [ currentHomePageTitle, setCurrentHomePageTitle ] = useState( '' );
const isPageDraft = item.status === 'draft';

const { getEntityRecord } = resolveSelect( coreStore );
const { saveEditedEntityRecord, saveEntityRecord } =
useDispatch( coreStore );
const { createSuccessNotice, createErrorNotice } =
useDispatch( noticesStore );

useEffect( () => {
const handleGetCurrentHomepage = async () => {
const currentHomePageItem = await getEntityRecord(
'postType',
'page',
currentHomePage
);
setCurrentHomePageTitle( getItemTitle( currentHomePageItem ) );
};
handleGetCurrentHomepage();
}, [ currentHomePage, getEntityRecord ] );
mikachan marked this conversation as resolved.
Show resolved Hide resolved

async function onSetPageAsHomepage( event ) {
event.preventDefault();

try {
// If selected page is set to draft, publish the page.
if ( isPageDraft ) {
await saveEntityRecord( 'postType', 'page', {
...item,
status: 'publish',
} );
}

// Save new home page settings.
await saveEntityRecord( 'root', 'site', {
page_on_front: item.id,
show_on_front: 'page',
} );

closeModal?.();

await saveEditedEntityRecord( 'root', 'site', undefined, {
page_on_front: item.id,
show_on_front: 'page',
} );
mikachan marked this conversation as resolved.
Show resolved Hide resolved
mikachan marked this conversation as resolved.
Show resolved Hide resolved

createSuccessNotice( __( 'Homepage updated' ), {
type: 'snackbar',
} );

onActionPerformed?.( items );
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should stick a debugger here and follow the code route. I suspect we don't need to call this in this scenario.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@oandregal Is there any documentation about onActionPerformed? I looked and couldn't find any.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've removed onActionPerformed for now: 04a7ba4.

} catch ( error ) {
const typedError = error;
const errorMessage =
typedError.message && typedError.code !== 'unknown_error'
? typedError.message
: __( 'An error occurred while setting the homepage' );
createErrorNotice( errorMessage, { type: 'snackbar' } );
}
}

const renderModalBody = () => {
if ( 'posts' === showOnFront ) {
return (
<>
<Text>
{ sprintf(
// translators: %s: title of the page to be set as the homepage.
__(
'Set "%s" as the site homepage? This will replace the current homepage which is set to display latest posts.'
),
pageTitle
) }
</Text>
</>
getdave marked this conversation as resolved.
Show resolved Hide resolved
);
}

if ( isPageDraft ) {
return (
<>
<Text>
{ sprintf(
// translators: %s: title of the page to be set as the homepage.
__(
'"%s" is a draft and will be published automatically if set as the homepage. Publish page and set as the site homepage?'
),
pageTitle
) }
</Text>
</>
);
}

const modalTranslatedString =
// translators: %1$s: title of page to be set as the home page. %2$s: title of the current home page.
__(
'Set "%1$s" as the site homepage? This will replace the current homepage: "%2$s"'
);

return (
<Text>
{ sprintf(
modalTranslatedString,
pageTitle,
currentHomePageTitle
) }{ ' ' }
mikachan marked this conversation as resolved.
Show resolved Hide resolved
</Text>
);
};

return (
<form onSubmit={ onSetPageAsHomepage }>
getdave marked this conversation as resolved.
Show resolved Hide resolved
<VStack spacing="5">
{ renderModalBody() }
<HStack justify="right">
<Button
__next40pxDefaultSize
variant="tertiary"
onClick={ () => {
closeModal?.();
} }
>
{ __( 'Cancel' ) }
</Button>
<Button
__next40pxDefaultSize
variant="primary"
type="submit"
>
{
// translators: Button to confirm setting the specified page as the homepage.
isPageDraft
? __( 'Publish and set homepage' )
: __( 'Set homepage' )
mikachan marked this conversation as resolved.
Show resolved Hide resolved
}
</Button>
</HStack>
</VStack>
</form>
);
};

export const useSetAsHomepageAction = () => {
const { pageOnFront, pageForPosts } = useSelect( ( select ) => {
const { getEntityRecord } = select( coreStore );
const siteSettings = getEntityRecord( 'root', 'site' );
return {
pageOnFront: siteSettings?.page_on_front,
pageForPosts: siteSettings?.page_for_posts,
};
} );

return useMemo(
() => ( {
id: 'set-as-homepage',
label: __( 'Set as homepage' ),
isEligible( post ) {
if ( post.status === 'trash' ) {
mikachan marked this conversation as resolved.
Show resolved Hide resolved
return false;
}

if ( post.type !== 'page' ) {
return false;
}

// Don't show the action if the page is already set as the homepage.
if ( pageOnFront === post.id ) {
return false;
}

// Don't show the action if the page is already set as the page for posts.
if ( pageForPosts === post.id ) {
return false;
}

return true;
},
RenderModal: SetAsHomepageModal,
} ),
[ pageForPosts, pageOnFront ]
);
};
2 changes: 1 addition & 1 deletion packages/editor/src/dataviews/types.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
type PostStatus =
| 'published'
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just realized we had this in the editor that we also have in the wordpress/fields package (see). It sounds to me the fields package should be the central package for that kind of information about fields and posts. Not for this PR, just sharing a potential follow-up to clean things up later.

| 'publish'
| 'draft'
| 'pending'
| 'private'
Expand Down
19 changes: 19 additions & 0 deletions packages/editor/src/utils/get-item-title.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/**
* WordPress dependencies
*/
import { decodeEntities } from '@wordpress/html-entities';

// This function is copied from packages/editor/src/dataviews/actions/utils.ts.

export function getItemTitle( item ) {
if ( typeof item.title === 'string' ) {
return decodeEntities( item.title );
}
if ( item.title && 'rendered' in item.title ) {
return decodeEntities( item.title.rendered );
}
if ( item.title && 'raw' in item.title ) {
return decodeEntities( item.title.raw );
}
return '';
}
2 changes: 1 addition & 1 deletion packages/fields/src/actions/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ export function isTemplateOrTemplatePart(
return p.type === TEMPLATE_POST_TYPE || p.type === TEMPLATE_PART_POST_TYPE;
}

export function getItemTitle( item: Post ) {
export function getItemTitle( item: Post ): string {
if ( typeof item.title === 'string' ) {
return decodeEntities( item.title );
}
Expand Down
7 changes: 3 additions & 4 deletions packages/fields/src/fields/title/title-view.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,10 @@ import { getItemTitle } from '../../actions/utils';
const TitleView = ( { item }: { item: BasePost } ) => {
const { frontPageId, postsPageId } = useSelect( ( select ) => {
const { getEntityRecord } = select( coreStore );
const siteSettings: Settings | undefined = getEntityRecord(
const siteSettings = getEntityRecord(
'root',
'site',
''
mikachan marked this conversation as resolved.
Show resolved Hide resolved
);
'site'
) as Partial< Settings >;
return {
frontPageId: siteSettings?.page_on_front,
postsPageId: siteSettings?.page_for_posts,
Expand Down
Loading
Loading