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

Dataviews: All templates: Add: Sorting to template author and add author_text to the rest API. #56333

Merged
merged 3 commits into from
Nov 27, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
121 changes: 121 additions & 0 deletions lib/compat/wordpress-6.5/rest-api.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,124 @@ function gutenberg_register_global_styles_revisions_endpoints() {
}

add_action( 'rest_api_init', 'gutenberg_register_global_styles_revisions_endpoints' );

/**
* Registers additional fields for wp_template rest api.
*
* @access private
* @internal
*
* @param array $template_object Template object.
* @return string Original source of the template one of theme, plugin, site, or user.
*/
function _gutenberg_get_wp_templates_original_source_field( $template_object ) {
if ( 'wp_template' === $template_object['type'] || 'wp_template_part' === $template_object['type'] ) {
// Added by theme.
// Template originally provided by a theme, but customized by a user.
// Templates originally didn't have the 'origin' field so identify
// older customized templates by checking for no origin and a 'theme'
// or 'custom' source.
if ( $template_object['has_theme_file'] &&
( 'theme' === $template_object['origin'] || (
empty( $template_object['origin'] ) && in_array(
$template_object['source'],
array(
'theme',
'custom',
),
true
) )
)
) {
return 'theme';
}

// Added by plugin.
if ( $template_object['has_theme_file'] && 'plugin' === $template_object['origin'] ) {
return 'plugin';
}

// Added by site.
// Template was created from scratch, but has no author. Author support
// was only added to templates in WordPress 5.9. Fallback to showing the
// site logo and title.
if ( empty( $template_object['has_theme_file'] ) && 'custom' === $template_object['source'] && empty( $template_object['author'] ) ) {
return 'site';
}
}

// Added by user.
return 'user';
}

/**
* Registers additional fields for wp_template rest api.
*
* @access private
* @internal
*
* @param array $template_object Template object.
* @return string Human readable text for the author.
*/
function _gutenberg_get_wp_templates_author_text_field( $template_object ) {
$original_source = _gutenberg_get_wp_templates_original_source_field( $template_object );
switch ( $original_source ) {
case 'theme':
$theme_name = wp_get_theme( $template_object['theme'] )->get( 'Name' );
return empty( $theme_name ) ? $template_object['theme'] : $theme_name;
case 'plugin':
$plugins = get_plugins();
$plugin = $plugins[ plugin_basename( sanitize_text_field( $template_object['theme'] . '.php' ) ) ];
return empty( $plugin['Name'] ) ? $template_object['theme'] : $plugin['Name'];
case 'site':
return get_bloginfo( 'name' );
case 'user':
return get_user_by( 'id', $template_object['author'] )->get( 'display_name' );
}
}

/**
* Registers additional fields for wp_template rest api.
*
* @access private
* @internal
*/
function _gutenberg_register_wp_templates_additional_fields() {
register_rest_field(
'wp_template',
'author_text',
array(
'get_callback' => '_gutenberg_get_wp_templates_author_text_field',
'update_callback' => null,
'schema' => array(
'type' => 'string',
'description' => __( 'Human readable text for the author.', 'gutenberg' ),
'readonly' => true,
'context' => array( 'view', 'edit', 'embed' ),
),
)
);

register_rest_field(
'wp_template',
'original_source',
array(
'get_callback' => '_gutenberg_get_wp_templates_original_source_field',
'update_callback' => null,
'schema' => array(
'description' => __( 'Where the template originally comes from e.g. \'theme\'', 'gutenberg' ),
'type' => 'string',
'readonly' => true,
'context' => array( 'view', 'edit', 'embed' ),
'enum' => array(
'theme',
'plugin',
'site',
'user',
),
),
)
);
}

add_action( 'rest_api_init', '_gutenberg_register_wp_templates_additional_fields' );
Original file line number Diff line number Diff line change
Expand Up @@ -142,57 +142,7 @@ export default function DataviewsTemplates() {
useEntityRecords( 'postType', TEMPLATE_POST_TYPE, {
per_page: -1,
} );
const { shownTemplates, paginationInfo } = useMemo( () => {
if ( ! allTemplates ) {
return {
shownTemplates: EMPTY_ARRAY,
paginationInfo: { totalItems: 0, totalPages: 0 },
};
}
let filteredTemplates = [ ...allTemplates ];
// Handle global search.
if ( view.search ) {
const normalizedSearch = normalizeSearchInput( view.search );
filteredTemplates = filteredTemplates.filter( ( item ) => {
const title = item.title?.rendered || item.slug;
return (
normalizeSearchInput( title ).includes(
normalizedSearch
) ||
normalizeSearchInput( item.description ).includes(
normalizedSearch
)
);
} );
}
// Handle sorting.
// TODO: Explore how this can be more dynamic..
if ( view.sort ) {
if ( view.sort.field === 'title' ) {
filteredTemplates.sort( ( a, b ) => {
const titleA = a.title?.rendered || a.slug;
const titleB = b.title?.rendered || b.slug;
return view.sort.direction === 'asc'
? titleA.localeCompare( titleB )
: titleB.localeCompare( titleA );
} );
}
}
// Handle pagination.
const start = ( view.page - 1 ) * view.perPage;
const totalItems = filteredTemplates?.length || 0;
filteredTemplates = filteredTemplates?.slice(
start,
start + view.perPage
);
return {
shownTemplates: filteredTemplates,
paginationInfo: {
totalItems,
totalPages: Math.ceil( totalItems / view.perPage ),
},
};
}, [ allTemplates, view ] );

const fields = useMemo(
() => [
{
Expand Down Expand Up @@ -237,13 +187,74 @@ export default function DataviewsTemplates() {
{
header: __( 'Author' ),
id: 'author',
render: ( { item } ) => <AuthorField item={ item } />,
getValue: ( { item } ) => item.author_text,
render: ( { item } ) => {
return <AuthorField item={ item } />;
},
enableHiding: false,
enableSorting: false,
},
],
[]
);

const { shownTemplates, paginationInfo } = useMemo( () => {
if ( ! allTemplates ) {
return {
shownTemplates: EMPTY_ARRAY,
paginationInfo: { totalItems: 0, totalPages: 0 },
};
}
let filteredTemplates = [ ...allTemplates ];
// Handle global search.
if ( view.search ) {
const normalizedSearch = normalizeSearchInput( view.search );
filteredTemplates = filteredTemplates.filter( ( item ) => {
const title = item.title?.rendered || item.slug;
return (
normalizeSearchInput( title ).includes(
normalizedSearch
) ||
normalizeSearchInput( item.description ).includes(
normalizedSearch
)
);
} );
}

// Handle sorting.
if ( view.sort ) {
const stringSortingFields = [ 'title', 'author' ];
const fieldId = view.sort.field;
if ( stringSortingFields.includes( fieldId ) ) {
const fieldToSort = fields.find( ( field ) => {
return field.id === fieldId;
} );
filteredTemplates.sort( ( a, b ) => {
const valueA = fieldToSort.getValue( { item: a } ) ?? '';
const valueB = fieldToSort.getValue( { item: b } ) ?? '';
return view.sort.direction === 'asc'
? valueA.localeCompare( valueB )
: valueB.localeCompare( valueA );
} );
}
}

// Handle pagination.
const start = ( view.page - 1 ) * view.perPage;
const totalItems = filteredTemplates?.length || 0;
filteredTemplates = filteredTemplates?.slice(
start,
start + view.perPage
);
return {
shownTemplates: filteredTemplates,
paginationInfo: {
totalItems,
totalPages: Math.ceil( totalItems / view.perPage ),
},
};
}, [ allTemplates, view, fields ] );

const resetTemplateAction = useResetTemplateAction();
const actions = useMemo(
() => [
Expand Down
94 changes: 51 additions & 43 deletions phpunit/class-gutenberg-rest-templates-controller-test.php
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ public function test_get_item_schema() {
$response = rest_get_server()->dispatch( $request );
$data = $response->get_data();
$properties = $data['schema']['properties'];
$this->assertCount( 15, $properties );
$this->assertCount( 17, $properties );
$this->assertArrayHasKey( 'id', $properties );
$this->assertArrayHasKey( 'description', $properties );
$this->assertArrayHasKey( 'slug', $properties );
Expand Down Expand Up @@ -131,23 +131,25 @@ public function test_get_item() {

$this->assertSame(
array(
'id' => 'emptytheme//my_template',
'theme' => 'emptytheme',
'slug' => 'my_template',
'source' => 'custom',
'origin' => null,
'type' => 'wp_template',
'description' => 'Description of my template.',
'title' => array(
'id' => 'emptytheme//my_template',
'theme' => 'emptytheme',
'slug' => 'my_template',
'source' => 'custom',
'origin' => null,
'type' => 'wp_template',
'description' => 'Description of my template.',
'title' => array(
Comment on lines -134 to +141
Copy link
Contributor

Choose a reason for hiding this comment

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

Confirmed that these changes are not backported to Core version of this test file.

'raw' => 'My Template',
'rendered' => 'My Template',
),
'status' => 'publish',
'wp_id' => self::$post->ID,
'has_theme_file' => false,
'is_custom' => true,
'author' => 0,
'modified' => mysql_to_rfc3339( self::$post->post_modified ),
'status' => 'publish',
'wp_id' => self::$post->ID,
'has_theme_file' => false,
'is_custom' => true,
'author' => 0,
'modified' => mysql_to_rfc3339( self::$post->post_modified ),
'author_text' => 'Test Blog',
'original_source' => 'site',
),
$data
);
Expand All @@ -164,23 +166,25 @@ public function test_get_items() {

$this->assertSame(
array(
'id' => 'emptytheme//my_template',
'theme' => 'emptytheme',
'slug' => 'my_template',
'source' => 'custom',
'origin' => null,
'type' => 'wp_template',
'description' => 'Description of my template.',
'title' => array(
'id' => 'emptytheme//my_template',
'theme' => 'emptytheme',
'slug' => 'my_template',
'source' => 'custom',
'origin' => null,
'type' => 'wp_template',
'description' => 'Description of my template.',
'title' => array(
'raw' => 'My Template',
'rendered' => 'My Template',
),
'status' => 'publish',
'wp_id' => self::$post->ID,
'has_theme_file' => false,
'is_custom' => true,
'author' => 0,
'modified' => mysql_to_rfc3339( self::$post->post_modified ),
'status' => 'publish',
'wp_id' => self::$post->ID,
'has_theme_file' => false,
'is_custom' => true,
'author' => 0,
'modified' => mysql_to_rfc3339( self::$post->post_modified ),
'author_text' => 'Test Blog',
'original_source' => 'site',
),
$this->find_and_normalize_template_by_id( $data, 'emptytheme//my_template' )
);
Expand Down Expand Up @@ -225,27 +229,31 @@ public function test_create_item() {
unset( $data['_links'] );
unset( $data['wp_id'] );

$author_name = get_user_by( 'id', self::$admin_id )->get( 'display_name' );

$this->assertSame(
array(
'id' => 'emptytheme//my_custom_template',
'theme' => 'emptytheme',
'content' => array(
'id' => 'emptytheme//my_custom_template',
'theme' => 'emptytheme',
'content' => array(
'raw' => 'Content',
),
'slug' => 'my_custom_template',
'source' => 'custom',
'origin' => null,
'type' => 'wp_template',
'description' => 'Just a description',
'title' => array(
'slug' => 'my_custom_template',
'source' => 'custom',
'origin' => null,
'type' => 'wp_template',
'description' => 'Just a description',
'title' => array(
'raw' => 'My Template',
'rendered' => 'My Template',
),
'status' => 'publish',
'has_theme_file' => false,
'is_custom' => true,
'author' => self::$admin_id,
'modified' => $data['modified'],
'status' => 'publish',
'has_theme_file' => false,
'is_custom' => true,
'author' => self::$admin_id,
'modified' => $data['modified'],
'author_text' => $author_name,
'original_source' => 'user',
),
$data
);
Expand Down
Loading