Skip to content

Commit

Permalink
Add an image thumbnail to wp-admin post list. (#20720)
Browse files Browse the repository at this point in the history
* [not verified] First approach

* [not verified] posts-page: start to rendering posts list with components

* [not verified] add package to renovate github file

* [not verified] wp-admin-plus: add composer compiling scripts

* [not verified] doc

* [not verified] rendering date button in date column

* [not verified] eslint: fix override eslint path

* [not verified] set post date optimistically

* [not verified] update github files

* [not verified] wp-admin-plus: changelog

* [not verified] remove codeclimate file

* [not verified] remove editorconfig file

* [not verified] cleaning...

* [not verified] wp-admin-plus: set selected day color

* [not verified] twaeking post date label

* [not verified] wp-admin-plus: refresh post status once saves

* [not verified] move post-date cmp to its own folder

* [not verified] add PostStatusLabel component

* [not verified] refact -> add usePost hook

* [not verified] usePost: refactoring hook

* [not verified] add @wordpress/eslint-plugin dev dep

* [not verified] post status label: pass status to hook

* [not verified] Populate post status with espcial cases. Tidy.

* [not verified] refactor pick and organize client data

* [not verified] update dependencies

* [not verified] add eslint config file

* [not verified] requier `any` post status

* [not verified] add "@babel/runtime": "7.14.0" dependency

* [not verified] rename package to wp-admin-posts-list-page

* [not verified] rename (again clap !) package name

* [not verified] communicate: init component

* [not verified] [not verified]

* [not verified] improve jsdoc

* [not verified] Copied thumbnail code from add/wpcom-posts-package to display a post's featured image in the posts list in wp-admin.

* [not verified] rename package name

* [not verified] rename changelog filename

* [not verified] remove post-date stuff

* [not verified] rename to Notice component. doc.

* [not verified] inject post-feature-image container

* [not verified] Add basic FeatureImage component

* [not verified] remove thumbnail class

* [not verified] First attempt to get the first image found in a post if a featured image is not set. Needs more work.

* [not verified] check and get post image

* [not verified] pick images inspecting post content

* [not verified] If no featured image is set, get the first image on the post hosted by the media library. We use DOMDocument and DomXPath to parse the post HTML to find the first image with a class name attribute containing 'wp-image-'.

* [not verified] Updated comments to pass linter.

* [not verified] Apply suggestions from code review

Co-authored-by: Jeremy Herve <[email protected]>

* [not verified] Added composer.lock & .jshintrc to .gitignore. Replaced spaces with tabs in style.scss.

* [not verified] Fixed infinite "jetpack build packages/post-list" build by changing "build-development" from "pnpm run start" fo "pnpm run build".

* [not verified] Rebase

* [not verified] Added content to changelog file. Alphabetized renovate.json.

* [not verified] Upgrade @wordpress/scripts to v18, and adjust build scripts

* [not verified] add no-featured-image image

* [not verified] change style by image when no-featured

* [not verified] add a super basic Tooltip when no featured image

* [not verified] use a div to wrap no-featured-image

* [not verified] Add Assets package as dependency

* [not verified] tweak tooltip style

* [not verified] clean CSS code

* [not verified] add basic eslint config file

* [not verified] Updated get_featured_or_first_post_image to find the class 'wp-image-ID' in the case of multiple class names.

* [not verified] update eslint-plugin version

* [not verified] Added unit test stub files.

* [not verified] Add unit test support

* [not verified] Updated our deprecated XML configuration scheme using the command './vendor/phpunit/phpunit/phpunit --migrate-configuration'

* [not verified] Refactor to use global data object, and remove unused code.

* [not verified] Remove unused eslintrc file

* [not verified] update building scripts

* [not verified] minor doc improvement

* [not verified] removing post-list dependency

* [not verified] Moved image get function to its own class and broke it up for more granular testing. Added additional unit test file for getting the image.

* [not verified] Refactor feature flag query var.

* [not verified] add @wordpress/icons dependency

* [not verified] replace bitmap image by icon when no thumb

* [not verified] build for dev without watching changes

* [not verified] Added new tests for Post_Image.

* [not verified] change image icon by a custom one

* [not verified] Renamed Post_Image class to Post_Thumbnail. Added phpcs:ignore on unit test filenames.

* [not verified] Ensure we only activate when thumbnails are supported

* [not verified] Renamed Admin class to Post_List.

* [not verified] Adding project structure requirments.

* [not verified] Update package version number

* [not verified] Correct .gitkeep location

* [not verified] add alt image property

* [not verified] remove post-list dependency from jetpack

* [not verified] remove code about post date

* [not verified] change package description

* [not verified] improve getting image alt value

* [not verified] Refactor how the package is initialized and remove actions.php

* [not verified] Update projects/packages/post-list/.eslintrc.js

Co-authored-by: Jeremy Herve <[email protected]>

* [not verified] Update projects/packages/post-list/.eslintrc.js

Co-authored-by: Jeremy Herve <[email protected]>

* [not verified] Update projects/packages/post-list/.eslintrc.js

Co-authored-by: Jeremy Herve <[email protected]>

* [not verified] Update projects/packages/post-list/src/components/featured-image/index.js

Co-authored-by: Jeremy Herve <[email protected]>

* [not verified] Update projects/packages/post-list/composer.json

Co-authored-by: Jeremy Herve <[email protected]>

* [not verified] Update projects/packages/post-list/src/class-post-list.php

Co-authored-by: Jeremy Herve <[email protected]>

* [not verified] Remove is_wp_admin_posts_list_page check

* [not verified] remove PACKAGE_VERSION class constant

* [not verified] remove unused packages

* [not verified] Removed actions.php reference.

* [not verified] Added wp_set_script_translations.

* [not verified] Implemented @dataProvider in test-post-thumbnail.php. Added WordBless for test-post-list.php unit tests. Updated get_first_image_id_from_post_content() regex and added a check for is_numeric.

* [not verified] Defined $image_alt = null. Updated comment.

* [not verified] Added /wordpress to .gitignore.

* [not verified] Rebase, updated lock files, update noded version in package.json.

* [not verified] Updating unit test configuration.

* [not verified] Updated yoast/phpunit-polyfills version number. Edited error messages in unit tests.

* [not verified] rebase

* [not verified] create root element for the client app

* [not verified] re-implement the app using portal
The idea here is creating an only one APP compopnent to improve the client performance in terms of rendering.

* [not verified] replace React by vanilla JS

* [not verified] clean compiling JS stuff

* [not verified] Updated unit tests, comments, and refactored namespace.

* [not verified] Removed .eslintrc.js.

* [not verified] Removed post-list dependency from Jetpack.

* [not verified] Attempt to remove projects/plugins/jetpack/composer.lock from PR.

* [not verified] Removed changelog since we removed the dependecy.

Since we removed the post-list dependency from the plugins composer file, I don't think we need this changelog file.

* [not verified] Updated Readme.md

Updated initialization hook code. Capitalized words in headers.

* [not verified] Added package version number.

* [not verified] Escape return values just in case.

* [not verified] Check for empty $post_content first and return null if true.

* [not verified] Updated version number.

* Apply suggestions from code review

Co-authored-by: Damián Suárez <[email protected]>

Co-authored-by: retrofox <[email protected]>
Co-authored-by: Dusty Reagan <[email protected]>
Co-authored-by: Jeremy Herve <[email protected]>
Co-authored-by: Paul Bunkham <[email protected]>
Co-authored-by: Allison Levine <[email protected]>
Co-authored-by: Brandon Kraft <[email protected]>
  • Loading branch information
7 people authored Sep 10, 2021
1 parent edbb7f3 commit e2c19e8
Show file tree
Hide file tree
Showing 16 changed files with 605 additions and 0 deletions.
1 change: 1 addition & 0 deletions .github/renovate.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
"automattic/jetpack-options",
"automattic/jetpack-partner",
"automattic/jetpack-password-checker",
"automattic/jetpack-post-list",
"automattic/jetpack-redirect",
"automattic/jetpack-roles",
"automattic/jetpack-status",
Expand Down
16 changes: 16 additions & 0 deletions projects/packages/post-list/.gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# Files not needed to be distributed in the package.
.gitattributes export-ignore
.github/ export-ignore
package.json export-ignore
webpack.config.js export-ignore
gulpfile.babel.js export-ignore
.babelrc export-ignore

# Files to include in the mirror repo
/build/** production-include

# Files to exclude from the mirror repo
/changelog/** production-exclude
/.eslintrc.js production-exclude
.gitignore production-exclude
_inc/** production-exclude
6 changes: 6 additions & 0 deletions projects/packages/post-list/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
/build
/node_modules
/.cache
/wordpress
composer.lock
.jshintrc
6 changes: 6 additions & 0 deletions projects/packages/post-list/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# Changelog

All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
35 changes: 35 additions & 0 deletions projects/packages/post-list/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# Jetpack Post List Package

Enhance the classic view of the Admin section of your WordPress site.

## How to Use

Package is published in [Packagist](https://packagist.org/packages/automattic/jetpack-post-list).

Use composer to add the package to your project:
```bash
composer add automattic/jetpack-post-list
```

Then you need to initialize it on the `admin_init` hook:

```php
add_action( 'admin_init', array( '\Automattic\Jetpack\Post_List\Post_List', 'configure' ) );
```

## Development

### Production
```bash
jetpack build -p packages/post-list
```

### Development
```bash
jetpack build packages/post-list
```

### Development Watching Mode 👀
```bash
jetpack watch packages/post-list
```
Empty file.
4 changes: 4 additions & 0 deletions projects/packages/post-list/changelog/add-post-list-package
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Significance: patch
Type: added

Add the new Post List package to Jetpack project
57 changes: 57 additions & 0 deletions projects/packages/post-list/composer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
{
"name": "automattic/jetpack-post-list",
"description": "Enhance the classic view of the Admin section of your WordPress site",
"type": "library",
"license": "GPL-2.0-or-later",
"require": {
"automattic/jetpack-assets": "^1.11"
},
"require-dev": {
"automattic/wordbless": "@dev",
"automattic/jetpack-changelogger": "^1.2",
"yoast/phpunit-polyfills": "1.0.1"
},
"autoload": {
"classmap": [
"src/"
]
},
"repositories": [
{
"type": "path",
"url": "../*",
"options": {
"monorepo": true
}
}
],
"scripts": {
"phpunit": [
"./vendor/phpunit/phpunit/phpunit --colors=always"
],
"test-coverage": [
"@composer update",
"phpdbg -d memory_limit=2048M -d max_execution_time=900 -qrr ./vendor/bin/phpunit --coverage-clover \"$COVERAGE_DIR/clover.xml\""
],
"test-php": [
"@composer update",
"@composer phpunit"
],
"post-update-cmd": "php -r \"copy('vendor/automattic/wordbless/src/dbless-wpdb.php', 'wordpress/wp-content/db.php');\""
},
"minimum-stability": "dev",
"prefer-stable": true,
"extra": {
"autotagger": true,
"mirror-repo": "automattic/jetpack-post-list",
"version-constants": {
"::PACKAGE_VERSION": "src/class-post-list.php"
},
"changelogger": {
"link-template": "https://github.com/automattic/jetpack-post-list/compare/v${old}...v${new}"
},
"branch-alias": {
"dev-master": "0.1.x-dev"
}
}
}
17 changes: 17 additions & 0 deletions projects/packages/post-list/phpunit.xml.dist
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<phpunit bootstrap="tests/php/bootstrap.php" backupGlobals="false" colors="true">
<testsuites>
<testsuite name="main">
<directory prefix="test" suffix=".php">tests/php</directory>
</testsuite>
</testsuites>
<filter>
<whitelist>
<directory suffix=".php">.</directory>
<exclude>
<directory suffix=".php">tests</directory>
<directory suffix=".php">vendor</directory>
<directory suffix=".php">wordpress</directory>
</exclude>
</whitelist>
</filter>
</phpunit>
122 changes: 122 additions & 0 deletions projects/packages/post-list/src/class-post-list.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
<?php
/**
* The Post List Admin Area.
*
* @package automattic/jetpack-post-list
*/

namespace Automattic\Jetpack\Post_List;

use Automattic\Jetpack\Assets;

/**
* The Post_List Admin Area
*/
class Post_List {

const PACKAGE_VERSION = '0.1.0-alpha';

/**
* The configuration method that is called from the jetpack-config package.
*/
public static function configure() {
$post_list = self::get_instance();
$post_list->register();
}

/**
* Initialize the Post List UI.
*
* @return Post_List Post_List instance.
*/
public static function get_instance() {
return new Post_List();
}

/**
* Sets up Post List action callbacks if needed.
*/
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' ) );

/**
* Action called after initializing Post_List Admin resources.
*
* @since $$next-version$$
*/
do_action( 'jetpack_on_posts_list_init' );
}
}

/**
* Enqueue scripts depending on the post-list query var.
*
* @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'
);

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
*/
public function print_post_data() {
global $wp_query;

if ( ! post_type_supports( $wp_query->query['post_type'], 'thumbnail' ) ) {
return;
}

$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 ) );
}

/**
* Add a placeholder element for the
* to mount the client app (root element).
*/
public function create_app_root_element() {
echo '<div id="wp-post-list-app" style="display: none;"></div>';
}
}

107 changes: 107 additions & 0 deletions projects/packages/post-list/src/class-post-thumbnail.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
<?php
/**
* This file contains the Post_Thumbnail class used for finding a suitable thumbnail for a post.
*
* @package automattic/jetpack-post-list
*/

namespace Automattic\Jetpack\Post_List;

/**
* The Post_Thumbnail class contains methods to find and return a suitable thumbnail for a post.
*/
class Post_Thumbnail {
/**
* Returns the featured image thumbnail or if no featured image is set, return the first image in the post. If
* neither exists returns the image array with null values.
*
* @param object $post The current post.
* @return array The thumbnail image id and URLs
*/
public static function get_post_thumbnail( $post ) {
$image_id = null;
$image_url = null;
$image_thumb = null;
$image_alt = null;

$post_id = $post->ID;

// If a featured image exists for the post, use that thumbnail.
if ( has_post_thumbnail( $post_id ) ) {
$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 );
} 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 );

if ( null !== $attachment_id ) {
$image_id = $attachment_id;
$image_url = wp_get_attachment_image_url( $attachment_id, 'full-size' );
$image_thumb = wp_get_attachment_image_url( $attachment_id, array( 50, 50 ) );
$image_alt = get_post_meta( $attachment_id, '_wp_attachment_image_alt', true );
}
}

// Escape values just in case.
return array(
'id' => esc_attr( $image_id ),
'url' => esc_url( $image_url ),
'thumb' => esc_url( $image_thumb ),
'alt' => esc_attr( $image_alt ),
);
}

/**
* Looks for the first image in the post content containing a class value of 'wp-image-{$attachment_id}' and
* returns the $attachment_id from that class value.
*
* @param string $post_content The current post's HTML content.
* @return int The image attachment id.
*/
public static function get_first_image_id_from_post_content( $post_content ) {
// If $post_content does not contain a value of substance, return null right away and avoid trying to parse it.
if ( empty( $post_content ) ) {
return null;
}

$attachment_id = null;
$dom = new \DOMDocument();

// libxml_use_internal_errors(true) silences PHP warnings and errors from malformed HTML in loadHTML().
// you can consult libxml_get_last_error() or libxml_get_errors() to check for errors if needed.
libxml_use_internal_errors( true );
$dom->loadHTML( $post_content );

// Media library images have a class attribute value containing 'wp-image-{$attachment_id}'.
// Use DomXPath to parse the post content and get the first img tag containing 'wp-image-' as a class value.
$class_name = 'wp-image-';
$dom_x_path = new \DomXPath( $dom );
$nodes = $dom_x_path->query( "//img[contains(@class, '$class_name')]/@class" );

if ( $nodes->length > 0 ) {
// Get the class attribute value of the 1st image node (aka index 0).
$class_value = $nodes[0]->value;

// Ignore all class attribute values except 'wp-image{$attachment_id}'.
// Regex english translation: Look for a word \b, that does not start or end with a hyphen (?!-), that
// starts with 'wp-image-', and ends with a number of any length \d+.
$class_name_found = preg_match( '/\b(?!-)wp-image-\d+(?!-)\b/', $class_value, $class_value );

if ( $class_name_found ) {
// Get the $attachment_id from the end of the class name value.
$attachment_id = str_replace( $class_name, '', $class_value[0] );

// If the ID we found is numeric, cast it as an int. Else, make it null.
if ( is_numeric( $attachment_id ) ) {
$attachment_id = (int) $attachment_id;
} else {
$attachment_id = null;
}
}
}

return $attachment_id;
}
}
Loading

0 comments on commit e2c19e8

Please sign in to comment.