diff --git a/package-lock.json b/package-lock.json index dc95bcdb05e2f6..f0e5ba3b04e462 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18769,6 +18769,7 @@ "@wordpress/date": "file:packages/date", "@wordpress/deprecated": "file:packages/deprecated", "@wordpress/dom": "file:packages/dom", + "@wordpress/editor": "file:packages/editor", "@wordpress/element": "file:packages/element", "@wordpress/escape-html": "file:packages/escape-html", "@wordpress/hooks": "file:packages/hooks", diff --git a/packages/block-library/package.json b/packages/block-library/package.json index ef95510c01421f..16b2afa6d99203 100644 --- a/packages/block-library/package.json +++ b/packages/block-library/package.json @@ -46,6 +46,7 @@ "@wordpress/date": "file:../date", "@wordpress/deprecated": "file:../deprecated", "@wordpress/dom": "file:../dom", + "@wordpress/editor": "file:../editor", "@wordpress/element": "file:../element", "@wordpress/escape-html": "file:../escape-html", "@wordpress/hooks": "file:../hooks", diff --git a/packages/block-library/src/page-list/block.json b/packages/block-library/src/page-list/block.json index 146f236fca84f8..f654922dcb4afb 100644 --- a/packages/block-library/src/page-list/block.json +++ b/packages/block-library/src/page-list/block.json @@ -3,10 +3,13 @@ "name": "core/page-list", "title": "Page List", "category": "widgets", - "description": "Display a list of all pages.", + "description": "Display a list of pages.", "keywords": [ "menu", "navigation" ], "textdomain": "default", "attributes": { + "showOnlyChildPages": { + "type": "boolean" + } }, "usesContext": [ "textColor", diff --git a/packages/block-library/src/page-list/edit.js b/packages/block-library/src/page-list/edit.js index 7d44a376ce0429..49ea5580e64c54 100644 --- a/packages/block-library/src/page-list/edit.js +++ b/packages/block-library/src/page-list/edit.js @@ -11,10 +11,19 @@ import { BlockControls, useBlockProps, getColorClassName, + InspectorControls, } from '@wordpress/block-editor'; -import { ToolbarButton, Placeholder, Spinner } from '@wordpress/components'; +import { + PanelBody, + Placeholder, + Spinner, + ToggleControl, + ToolbarButton, +} from '@wordpress/components'; +import { useSelect } from '@wordpress/data'; +import { store as editorStore } from '@wordpress/editor'; import { __ } from '@wordpress/i18n'; -import { useEffect, useState, memo } from '@wordpress/element'; +import { memo, useEffect, useState } from '@wordpress/element'; import apiFetch from '@wordpress/api-fetch'; import { addQueryArgs } from '@wordpress/url'; @@ -28,7 +37,12 @@ import { ItemSubmenuIcon } from '../navigation-link/icons'; // Performance of Navigation Links is not good past this value. const MAX_PAGE_COUNT = 100; -export default function PageListEdit( { context, clientId } ) { +export default function PageListEdit( { + attributes, + clientId, + context, + setAttributes, +} ) { const { pagesByParentId, totalPages } = usePagesByParentId(); const isNavigationChild = 'showSubmenuIcon' in context; @@ -39,6 +53,13 @@ export default function PageListEdit( { context, clientId } ) { const openModal = () => setOpen( true ); const closeModal = () => setOpen( false ); + const showChildPageToggle = useSelect( ( select ) => { + const { getCurrentPostType } = select( editorStore ); + const currentPostType = getCurrentPostType(); + const hideToggleFrom = [ 'post' ]; + return ! hideToggleFrom.includes( currentPostType ); + } ); + const blockProps = useBlockProps( { className: classnames( 'wp-block-page-list', { 'has-text-color': !! context.textColor, @@ -57,6 +78,24 @@ export default function PageListEdit( { context, clientId } ) { return ( <> + + { showChildPageToggle && ( + + + setAttributes( { + showOnlyChildPages: ! attributes.showOnlyChildPages, + } ) + } + help={ __( + 'When enabled, the block lists only child pages of the current page.' + ) } + /> + + ) } + { allowConvertToLinks && ( diff --git a/packages/block-library/src/page-list/index.php b/packages/block-library/src/page-list/index.php index 733d2e0e4354ee..3b39e48e19aa8d 100644 --- a/packages/block-library/src/page-list/index.php +++ b/packages/block-library/src/page-list/index.php @@ -226,6 +226,31 @@ function block_core_page_list_nest_pages( $current_level, $children ) { return $current_level; } +/** + * Determines if page should be classified as top-level or child page. Broken + * out to this function to explain instead of a complex if statement. + * When only_child_pages is true, force child pages to be considered top level. + * Thy are top level since the parent is not shown (only child pages). + * + * @param int $page_parent_id Parent id of the page being tested. + * @param boolean $only_child_pages Only showing child pages. + * @param int $parent_id Top level parent id for child pages, needed so we can nest grand children. + * @return boolean True if page should be considered a child page. + */ +function block_core_page_list_treat_as_child( $page_parent_id, $only_child_pages, $parent_id ) { + if ( ! $page_parent_id ) { + return false; + } + + // Check only child pages, and id matches parent, then it is top level and + // should not be treated like a child page. + if ( $only_child_pages && ( $page_parent_id === $parent_id ) ) { + return false; + } + + return true; +} + /** * Renders the `core/page-list` block on server. * @@ -236,16 +261,30 @@ function block_core_page_list_nest_pages( $current_level, $children ) { * @return string Returns the page list markup. */ function render_block_core_page_list( $attributes, $content, $block ) { + global $post; static $block_id = 0; $block_id++; - $all_pages = get_pages( - array( - 'sort_column' => 'menu_order,post_title', - 'order' => 'asc', - ) + $only_child_pages = isset( $attributes['showOnlyChildPages'] ) && $attributes['showOnlyChildPages']; + // The pages will be siblings (same parent) or set parent id equal to self if no children. + $parent_id = ( $post->post_parent ) ? $post->post_parent : $post->ID; + + // TODO: When https://core.trac.wordpress.org/ticket/39037 REST API support for multiple orderby values is resolved, + // update 'sort_column' to 'menu_order, post_title'. Sorting by both menu_order and post_title ensures a stable sort. + // Otherwise with pages that have the same menu_order value, we can see different ordering depending on how DB + // queries are constructed internally. For example we might see a different order when a limit is set to <499 + // versus >= 500. + $query_args = array( + 'sort_column' => 'menu_order', + 'order' => 'asc', ); + if ( $only_child_pages && $parent_id ) { + $query_args['child_of'] = $parent_id; + } + + $all_pages = get_pages( $query_args ); + // If thare are no pages, there is nothing to show. if ( empty( $all_pages ) ) { return; @@ -264,7 +303,8 @@ function render_block_core_page_list( $attributes, $content, $block ) { $active_page_ancestor_ids = get_post_ancestors( $page->ID ); } - if ( $page->post_parent ) { + // See function for logic when pages are treated like child pages. + if ( block_core_page_list_treat_as_child( $page->post_parent, $only_child_pages, $parent_id ) ) { $pages_with_children[ $page->post_parent ][ $page->ID ] = array( 'page_id' => $page->ID, 'title' => $page->post_title,