From 4f55cf1de2b1c8223c378a53e3f0878d7d8f822c Mon Sep 17 00:00:00 2001 From: Dusty Reagan Date: Wed, 22 Sep 2021 11:43:56 -0500 Subject: [PATCH] [not verified] Refactor post list thumbnail preview to function server side. All javascript removed. (#21126) * Reworking post-list thumbnails to be server side. * Added changelog file. * Added CSS and placeholder image for empty thumbnails. * CSS fix placeholder disappearing. * Updated unit tests. Added alt tag to thumbnail image. * Updated comments. * Added rtl.css. * Don't add thumbnail column if 'title' is missing for some reason. * Add thumbnails to "Pages" admin table. Removed unused class name from thumbnail img. * Created add_thumbnail_filters_and_actions and moved column filter and action out of enqueue_scripts and into current_screen. * Removed unused in_admin_footer action. --- .../post-list/changelog/refactor-server-side | 3 + .../post-list/src/class-post-list.php | 93 ++++++++-------- .../post-list/src/class-post-thumbnail.php | 9 +- projects/packages/post-list/src/index.js | 36 ------- projects/packages/post-list/src/rtl.css | 9 ++ projects/packages/post-list/src/style.css | 48 +++++++-- .../post-list/tests/php/bootstrap.php | 2 +- .../post-list/tests/php/test-post-list.php | 100 ++++++++++++++++-- .../tests/php/test-post-thumbnail.php | 3 + 9 files changed, 205 insertions(+), 98 deletions(-) create mode 100644 projects/packages/post-list/changelog/refactor-server-side delete mode 100644 projects/packages/post-list/src/index.js create mode 100644 projects/packages/post-list/src/rtl.css diff --git a/projects/packages/post-list/changelog/refactor-server-side b/projects/packages/post-list/changelog/refactor-server-side new file mode 100644 index 0000000000000..a150a6d0ea9a1 --- /dev/null +++ b/projects/packages/post-list/changelog/refactor-server-side @@ -0,0 +1,3 @@ +Significance: patch +Type: changed +Comment: Refactored to function server side. All javascript removed. diff --git a/projects/packages/post-list/src/class-post-list.php b/projects/packages/post-list/src/class-post-list.php index a78ac66571e5a..292da62b15c38 100644 --- a/projects/packages/post-list/src/class-post-list.php +++ b/projects/packages/post-list/src/class-post-list.php @@ -7,8 +7,6 @@ namespace Automattic\Jetpack\Post_List; -use Automattic\Jetpack\Assets; - /** * The Post_List Admin Area */ @@ -34,13 +32,12 @@ public static function get_instance() { } /** - * Sets up Post List action callbacks if needed. + * Sets up Post List action callbacks. */ public function register() { if ( ! did_action( 'jetpack_on_posts_list_init' ) ) { add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_scripts' ) ); - add_action( 'in_admin_footer', array( $this, 'create_app_root_element' ) ); - + add_action( 'current_screen', array( $this, 'add_thumbnail_filters_and_actions' ) ); add_filter( 'default_hidden_columns', array( $this, 'adjust_default_columns' ), 10, 2 ); /** @@ -53,72 +50,80 @@ public function register() { } /** - * Enqueue scripts depending on the post-list query var. + * Enqueue scripts. * * @param string $hook Page hook. */ public function enqueue_scripts( $hook ) { if ( 'edit.php' === $hook ) { - $plugin_path = Assets::get_file_url_for_environment( './index.js', './index.js', __FILE__ ); - - wp_enqueue_script( - 'jetpack_posts_list_ui_script', - $plugin_path, - array(), - self::PACKAGE_VERSION, - true - ); - wp_enqueue_style( 'jetpack_posts_list_ui_style', plugin_dir_url( __DIR__ ) . './src/style.css', array(), self::PACKAGE_VERSION ); - wp_style_add_data( 'jetpack_posts_list_ui_style', 'rtl', - plugin_dir_url( __DIR__ ) . 'build/style.rtl.css' + plugin_dir_url( __DIR__ ) . './src/rtl.css' ); - - wp_set_script_translations( 'jetpack_posts_list_ui_script', 'jetpack' ); - - add_action( 'admin_footer', array( $this, 'print_post_data' ) ); } } /** - * Outputs a JSON blob to the global `wp_admin_posts` variable, for use - * by the JS application + * If the current_screen has 'edit' as the base, add filters and actions to add the thumbnail column to the Posts + * and Pages admin tables. + * + * @param object $current_screen The current screen. */ - public function print_post_data() { - global $wp_query; + public function add_thumbnail_filters_and_actions( $current_screen ) { + if ( 'edit' === $current_screen->base ) { + // Add the thumbnail column to the "Posts" admin table. + add_filter( 'manage_posts_columns', array( $this, 'add_thumbnail_column' ) ); + add_action( 'manage_posts_custom_column', array( $this, 'populate_thumbnail_rows' ), 10, 2 ); + + // Add the thumbnail column to the "Pages" admin table. + add_filter( 'manage_pages_columns', array( $this, 'add_thumbnail_column' ) ); + add_action( 'manage_pages_custom_column', array( $this, 'populate_thumbnail_rows' ), 10, 2 ); + } + } - if ( ! post_type_supports( $wp_query->query['post_type'], 'thumbnail' ) ) { - return; + /** + * Adds a new column header for displaying the thumbnail of a post. + * + * @param array $columns An array of column names. + * @return array An array of column names. + */ + public function add_thumbnail_column( $columns ) { + $new_column = array( 'thumbnail' => '' . __( 'Thumbnail', 'jetpack' ) . '' ); + $keys = array_keys( $columns ); + $position = array_search( 'title', $keys, true ); + + // If 'title' not found, don't insert the thumbnail column. + if ( false !== $position ) { + $columns = array_merge( array_slice( $columns, 0, $position ), $new_column, array_slice( $columns, $position ) ); } - $post_data = array_map( - function ( $post ) { - $thumbnail = Post_Thumbnail::get_post_thumbnail( $post ); - return array( - 'id' => $post->ID, - 'type' => $post->post_type, - 'featured_image' => $thumbnail, - ); - }, - $wp_query->posts - ); - wp_add_inline_script( 'jetpack_posts_list_ui_script', 'var wpAdminPosts = ' . wp_json_encode( $post_data ) ); + return $columns; } /** - * Add a placeholder element for the - * to mount the client app (root element). + * Displays the thumbnail content. + * + * @param string $column The name of the column to display. + * @param int $post_id The current post ID. */ - public function create_app_root_element() { - echo ''; + public function populate_thumbnail_rows( $column, $post_id ) { + if ( 'thumbnail' !== $column ) { + return; + } + + $thumbnail = Post_Thumbnail::get_post_thumbnail( get_post( $post_id ) ); + if ( $thumbnail ) { + echo '' . esc_attr( $thumbnail['alt'] ) . ''; + } else { + echo ''; + } } /** diff --git a/projects/packages/post-list/src/class-post-thumbnail.php b/projects/packages/post-list/src/class-post-thumbnail.php index eca99241ba589..fdb7cf7460dde 100644 --- a/projects/packages/post-list/src/class-post-thumbnail.php +++ b/projects/packages/post-list/src/class-post-thumbnail.php @@ -16,7 +16,7 @@ class Post_Thumbnail { * neither exists returns the image array with null values. * * @param object $post The current post. - * @return array The thumbnail image id and URLs + * @return array|null The thumbnail image id and URLs */ public static function get_post_thumbnail( $post ) { $image_id = null; @@ -31,7 +31,7 @@ public static function get_post_thumbnail( $post ) { $image_id = get_post_thumbnail_id( $post_id ); $image_url = get_the_post_thumbnail_url( $post_id ); $image_thumb = get_the_post_thumbnail_url( $post_id, array( 50, 50 ) ); - $image_alt = get_post_meta( $post_id, '_wp_attachment_image_alt', true ); + $image_alt = get_post_meta( $image_id, '_wp_attachment_image_alt', true ); } else { // If a featured image does not exist look for the first "media library" hosted image on the post. $attachment_id = self::get_first_image_id_from_post_content( $post->post_content ); @@ -44,6 +44,11 @@ public static function get_post_thumbnail( $post ) { } } + // If no thumbnail is found return null. + if ( null === $image_thumb ) { + return null; + } + // Escape values just in case. return array( 'id' => esc_attr( $image_id ), diff --git a/projects/packages/post-list/src/index.js b/projects/packages/post-list/src/index.js deleted file mode 100644 index cc8259a444df8..0000000000000 --- a/projects/packages/post-list/src/index.js +++ /dev/null @@ -1,36 +0,0 @@ -window.document.addEventListener( 'DOMContentLoaded', () => { - // Data global containers. - const posts = [ ...window.wpAdminPosts ]; - - posts.forEach( post => { - const postRow = document.getElementById( 'post-' + post.id ); - if ( ! postRow ) { - return; - } - - const postTitleElementWrapper = postRow.querySelector( '.column-title' ); - - // Inject post-featured-image__container container just before post title. - const postFeaturedImageElement = document.createElement( 'span' ); - postFeaturedImageElement.classList.add( 'post-featured-image__container' ); - postFeaturedImageElement.classList.add( - post.featured_image.id ? 'has-featured-image' : 'no-featured-image' - ); - - postTitleElementWrapper.insertBefore( - postFeaturedImageElement, - postTitleElementWrapper.firstChild - ); - - if ( post.featured_image.url ) { - const thumbnailImage = document.createElement( 'img' ); - thumbnailImage.setAttribute( 'src', post.featured_image.thumb ); - thumbnailImage.setAttribute( 'alt', post.featured_image.alt ); - thumbnailImage.setAttribute( 'width', 50 ); - thumbnailImage.setAttribute( 'height', 50 ); - - thumbnailImage.classList.add( 'post-featured-image__image' ); - postFeaturedImageElement.appendChild( thumbnailImage ); - } - } ); -} ); diff --git a/projects/packages/post-list/src/rtl.css b/projects/packages/post-list/src/rtl.css new file mode 100644 index 0000000000000..b50199552e70c --- /dev/null +++ b/projects/packages/post-list/src/rtl.css @@ -0,0 +1,9 @@ +/* Pull "Title" header name over in "Thumbnail"s place. */ +th.column-thumbnail + th a { + margin-right:-60px; +} + +/* If thumbnail column has .hidden class. The entire column is toggled off. Don't pull "Title" header name over. */ +th.column-thumbnail.hidden + th a { + margin-right:0; +} diff --git a/projects/packages/post-list/src/style.css b/projects/packages/post-list/src/style.css index 37ea538bbe055..1da12965cdfc4 100644 --- a/projects/packages/post-list/src/style.css +++ b/projects/packages/post-list/src/style.css @@ -1,16 +1,46 @@ +/* Set the width of the thumbnail column */ +.column-thumbnail { + width: 50px; +} -.post-featured-image__container { - float: left; - margin-right: 5px; +/* Style the thumbnail image. */ +.column-thumbnail img { + width: 50px; + height: 50px; + border-radius: 4px; } -.post-featured-image__container .post-list__post-featured-image { - width: 48px; - height: 48px; - fill: #ccc; +/* Style the Dashicon placeholder for empty thumbnails. */ +.column-thumbnail .dashicons { + font-size: 50px; + width: 50px; + height: 50px; + background: #c3c4c7; border-radius: 4px; + color: #f6f7f7; +} + +/* Keep data-colname from appearing on mobile view. */ +.column-thumbnail::before { + content: "" !important; +} + +/* Move the "Thumbnail" and "Title" columns closer together. */ +th.column-thumbnail { + padding-right:0; +} + +/* Don't display the "Thumbnail" column name in the header or footer. */ +th.column-thumbnail span { + display:none; +} + +/* Pull "Title" header name over in "Thumbnail"s place. */ +th.column-thumbnail + th a { + margin-left:-60px; } -.post-list__post-featured-image .components-popover__content { - padding: 5px; +/* If thumbnail column has .hidden class. The entire column is toggled off. Don't pull "Title" header name over. */ +th.column-thumbnail.hidden + th a { + margin-left:0; } diff --git a/projects/packages/post-list/tests/php/bootstrap.php b/projects/packages/post-list/tests/php/bootstrap.php index 9af8fa53ed24b..eb6af51de7fda 100644 --- a/projects/packages/post-list/tests/php/bootstrap.php +++ b/projects/packages/post-list/tests/php/bootstrap.php @@ -6,7 +6,7 @@ */ /** - * Composer's autoloader is all we need. + * Composer's autoloader. */ require_once __DIR__ . '/../../vendor/autoload.php'; diff --git a/projects/packages/post-list/tests/php/test-post-list.php b/projects/packages/post-list/tests/php/test-post-list.php index 1bfeab2fb964e..904f5d8042bb4 100644 --- a/projects/packages/post-list/tests/php/test-post-list.php +++ b/projects/packages/post-list/tests/php/test-post-list.php @@ -1,6 +1,9 @@ assertFalse( has_action( 'admin_enqueue_scripts', array( $post_list, 'enqueue_scripts' ) ) ); + $this->assertFalse( has_action( 'current_screen', array( $post_list, 'add_thumbnail_filters_and_actions' ) ) ); // Set up our action callbacks using the register() method. $post_list->register(); // Assert the action was added. $this->assertNotFalse( has_action( 'admin_enqueue_scripts', array( $post_list, 'enqueue_scripts' ) ) ); + $this->assertNotFalse( has_action( 'current_screen', array( $post_list, 'add_thumbnail_filters_and_actions' ) ) ); // Confirm it was only fired once even though we call it twice. $post_list->register(); @@ -53,16 +58,99 @@ public function test_register() { public function test_enqueue_scripts() { $post_list = Post_List::get_instance(); - // Confirm that our script, style, and action have not been added before the enqueue_scripts() method call. - $this->assertFalse( wp_script_is( 'jetpack_posts_list_ui_script' ) ); + // Confirm that our style, filter, and action have not been added before the enqueue_scripts() method call. $this->assertFalse( wp_style_is( 'jetpack_posts_list_ui_style' ) ); - $this->assertFalse( has_action( 'admin_footer' ) ); $post_list->enqueue_scripts( 'edit.php' ); - // Assert that our script, style, and action have been added. - $this->assertTrue( wp_script_is( 'jetpack_posts_list_ui_script' ) ); + // Assert that our style, filter, and action has been added. $this->assertTrue( wp_style_is( 'jetpack_posts_list_ui_style' ) ); - $this->assertTrue( has_action( 'admin_footer' ) ); + } + + /** + * Test the add_thumbnail_filters_and_actions() method. + */ + public function test_add_thumbnail_filters_and_actions() { + $post_list = Post_List::get_instance(); + + // Confirm that our style, filter, and action have not been added before the enqueue_scripts() method call. + $this->assertFalse( has_filter( 'manage_posts_columns' ) ); + $this->assertFalse( has_action( 'manage_posts_custom_column' ) ); + $this->assertFalse( has_filter( 'manage_pages_columns' ) ); + $this->assertFalse( has_action( 'manage_pages_custom_column' ) ); + + $current_screen = (object) array( 'base' => 'edit' ); + $post_list->add_thumbnail_filters_and_actions( $current_screen ); + + // Assert that our style, filter, and action has been added. + $this->assertTrue( has_filter( 'manage_posts_columns' ) ); + $this->assertTrue( has_action( 'manage_posts_custom_column' ) ); + $this->assertTrue( has_filter( 'manage_pages_columns' ) ); + $this->assertTrue( has_action( 'manage_pages_custom_column' ) ); + } + + /** + * Test the add_thumbnail_filters_and_actions() method doesn't add if screen not 'edit' base. + */ + public function test_add_thumbnail_filters_and_actions_wrong_screen() { + $post_list = Post_List::get_instance(); + $current_screen = (object) array( 'base' => 'edit-tags' ); + $post_list->add_thumbnail_filters_and_actions( $current_screen ); + + // Confirm that our style, filter, and action have not been added before the enqueue_scripts() method call. + $this->assertFalse( has_filter( 'manage_posts_columns' ) ); + $this->assertFalse( has_action( 'manage_posts_custom_column' ) ); + $this->assertFalse( has_filter( 'manage_pages_columns' ) ); + $this->assertFalse( has_action( 'manage_pages_custom_column' ) ); + } + + /** + * Test the add_thumbnail_column() method. + */ + public function test_add_thumbnail_column() { + $columns = array( + 'cb' => '', + 'title' => 'Title', + 'author' => 'Author', + 'categories' => 'Categories', + 'tags' => 'Tags', + 'comments' => 'Comments', + 'date' => 'Date', + ); + + $columns_expected = array( + 'cb' => '', + 'thumbnail' => 'Thumbnail', + 'title' => 'Title', + 'author' => 'Author', + 'categories' => 'Categories', + 'tags' => 'Tags', + 'comments' => 'Comments', + 'date' => 'Date', + ); + + $post_list = Post_List::get_instance(); + $columns_results = $post_list->add_thumbnail_column( $columns ); + + $this->assertSame( $columns_results, $columns_expected ); + } + + /** + * Test the add_thumbnail_column() method with 'title' missing. + */ + public function test_add_thumbnail_column_no_title() { + $columns = array( + 'cb' => '', + 'author' => 'Author', + 'categories' => 'Categories', + 'tags' => 'Tags', + 'comments' => 'Comments', + 'date' => 'Date', + ); + + $post_list = Post_List::get_instance(); + $columns_results = $post_list->add_thumbnail_column( $columns ); + + $this->assertSame( $columns_results, $columns ); } } diff --git a/projects/packages/post-list/tests/php/test-post-thumbnail.php b/projects/packages/post-list/tests/php/test-post-thumbnail.php index a7a7ca8202cf8..a0611c2a95a7a 100644 --- a/projects/packages/post-list/tests/php/test-post-thumbnail.php +++ b/projects/packages/post-list/tests/php/test-post-thumbnail.php @@ -1,6 +1,9 @@