From e94c30ddd0b05e05c6bd62a41e25115081db04e4 Mon Sep 17 00:00:00 2001 From: Rastislav Lamos Date: Thu, 8 Jun 2017 11:33:04 +0200 Subject: [PATCH] Add "Latest Posts" Block (#870) * Add initial scaffolding for REST API Latest Posts block * Load 5 latest posts into unordered list * Refactor to `latest-posts` instead of `rest-api-latest-posts` * Move data fetchers to data.js file Props @mtias for the suggestion * Refactor edit() to render a React component Fixes the issue of having to use `attributes` to store the latest posts and is also nicer. * Add poststoshow attribute with a default value 5 * Add server-side rendering I also had to rename the block from core/latest-posts to core/latestposts since I think there's a bug in the server-side matcher -- it ignores block comments with hyphens: #882 * Use defaultProps to initialize postsToShow * Add various fixes according to the review * Fix linting errors * Rebase and fix eslint/WP coding standards * Add fn which loads server-side rendering of blocks This was lost in last rebase * Avoid small render* functions and put the whole rendering in render * Add full post content fixture * Abort the latest posts query if component is unmounted before response * Import __ from i18n instead of using wp.i18n.__ * Use class prop instead of React state to store posts request * Add basic posts to show attribute validation --- blocks/api/categories.js | 1 + blocks/library/index.js | 1 + blocks/library/latest-posts/data.js | 19 +++++ blocks/library/latest-posts/index.js | 74 +++++++++++++++++++ blocks/library/latest-posts/index.php | 60 +++++++++++++++ blocks/test/fixtures/core-latestposts.html | 2 + blocks/test/fixtures/core-latestposts.json | 9 +++ .../fixtures/core-latestposts.serialized.html | 2 + gutenberg.php | 2 + lib/blocks.php | 11 +++ 10 files changed, 181 insertions(+) create mode 100644 blocks/library/latest-posts/data.js create mode 100644 blocks/library/latest-posts/index.js create mode 100644 blocks/library/latest-posts/index.php create mode 100644 blocks/test/fixtures/core-latestposts.html create mode 100644 blocks/test/fixtures/core-latestposts.json create mode 100644 blocks/test/fixtures/core-latestposts.serialized.html diff --git a/blocks/api/categories.js b/blocks/api/categories.js index 05361f55ff39b..493070492d0ea 100644 --- a/blocks/api/categories.js +++ b/blocks/api/categories.js @@ -16,6 +16,7 @@ const categories = [ { slug: 'formatting', title: __( 'Formatting' ) }, { slug: 'embed', title: __( 'Embed' ) }, { slug: 'layout', title: __( 'Layout Blocks' ) }, + { slug: 'rest-api', title: __( 'REST API Blocks' ) }, ]; /** diff --git a/blocks/library/index.js b/blocks/library/index.js index 59f625113cf56..5f1b28bcf894d 100644 --- a/blocks/library/index.js +++ b/blocks/library/index.js @@ -11,3 +11,4 @@ import './pullquote'; import './table'; import './preformatted'; import './code'; +import './latest-posts'; diff --git a/blocks/library/latest-posts/data.js b/blocks/library/latest-posts/data.js new file mode 100644 index 0000000000000..e48dbd2544d48 --- /dev/null +++ b/blocks/library/latest-posts/data.js @@ -0,0 +1,19 @@ +/** + * Returns a Promise with the latest posts or an error on failure. + * + * @param {Number} postsToShow Number of posts to display. + * + * @returns {wp.api.collections.Posts} Returns a Promise with the latest posts. + */ +export function getLatestPosts( postsToShow = 5 ) { + const postsCollection = new wp.api.collections.Posts(); + + const posts = postsCollection.fetch( { + data: { + per_page: postsToShow, + }, + } ); + + return posts; +} + diff --git a/blocks/library/latest-posts/index.js b/blocks/library/latest-posts/index.js new file mode 100644 index 0000000000000..af82a1bad7988 --- /dev/null +++ b/blocks/library/latest-posts/index.js @@ -0,0 +1,74 @@ +/** + * WordPress dependencies + */ +import { Placeholder } from 'components'; +import { __ } from 'i18n'; + +/** + * Internal dependencies + */ +import { registerBlockType } from '../../api'; +import { getLatestPosts } from './data.js'; + +registerBlockType( 'core/latestposts', { + title: __( 'Latest Posts' ), + + icon: 'list-view', + + category: 'rest-api', + + defaultAttributes: { + poststoshow: 5, + }, + + edit: class extends wp.element.Component { + constructor() { + super( ...arguments ); + + const { poststoshow } = this.props.attributes; + + this.state = { + latestPosts: [], + }; + + this.latestPostsRequest = getLatestPosts( poststoshow ); + + this.latestPostsRequest + .then( latestPosts => this.setState( { latestPosts } ) ); + } + + render() { + const { latestPosts } = this.state; + + if ( ! latestPosts.length ) { + return ( + + + ); + } + + return ( +
+ +
+ ); + } + }, + + componentWillUnmount() { + if ( this.latestPostsRequest.state() === 'pending' ) { + this.latestPostsRequest.abort(); + } + }, + + save() { + return null; + }, +} ); diff --git a/blocks/library/latest-posts/index.php b/blocks/library/latest-posts/index.php new file mode 100644 index 0000000000000..e947a54f8aaa2 --- /dev/null +++ b/blocks/library/latest-posts/index.php @@ -0,0 +1,60 @@ + 0 && + $posts_to_show_attr < 100 + ) { + $posts_to_show = $attributes['poststoshow']; + } + } + + $recent_posts = wp_get_recent_posts( array( + 'numberposts' => $posts_to_show, + 'post_status' => 'publish', + ) ); + + $posts_content = ''; + + foreach ( $recent_posts as $post ) { + $post_id = $post['ID']; + $post_permalink = get_permalink( $post_id ); + $post_title = get_the_title( $post_id ); + + $posts_content .= "
  • {$post_title}
  • \n"; + } + + $block_content = << + + + +CONTENT; + + return $block_content; +} + +register_block_type( 'core/latestposts', array( + 'render' => 'gutenberg_block_core_latest_posts', +) ); diff --git a/blocks/test/fixtures/core-latestposts.html b/blocks/test/fixtures/core-latestposts.html new file mode 100644 index 0000000000000..cc09cb936f430 --- /dev/null +++ b/blocks/test/fixtures/core-latestposts.html @@ -0,0 +1,2 @@ + + diff --git a/blocks/test/fixtures/core-latestposts.json b/blocks/test/fixtures/core-latestposts.json new file mode 100644 index 0000000000000..619edde2c7b2c --- /dev/null +++ b/blocks/test/fixtures/core-latestposts.json @@ -0,0 +1,9 @@ +[ + { + "uid": "_uid_0", + "name": "core/latestposts", + "attributes": { + "poststoshow": 5 + } + } +] diff --git a/blocks/test/fixtures/core-latestposts.serialized.html b/blocks/test/fixtures/core-latestposts.serialized.html new file mode 100644 index 0000000000000..cc09cb936f430 --- /dev/null +++ b/blocks/test/fixtures/core-latestposts.serialized.html @@ -0,0 +1,2 @@ + + diff --git a/gutenberg.php b/gutenberg.php index 6dae588b0c734..98fa84bc9c051 100644 --- a/gutenberg.php +++ b/gutenberg.php @@ -9,6 +9,8 @@ * @package gutenberg */ +define( 'GUTENBERG__PLUGIN_DIR', plugin_dir_path( __FILE__ ) ); + require_once dirname( __FILE__ ) . '/lib/blocks.php'; require_once dirname( __FILE__ ) . '/lib/client-assets.php'; require_once dirname( __FILE__ ) . '/lib/i18n.php'; diff --git a/lib/blocks.php b/lib/blocks.php index 58d23c7eb8a9d..7864b5f9b31f7 100644 --- a/lib/blocks.php +++ b/lib/blocks.php @@ -9,6 +9,8 @@ die( 'Silence is golden.' ); } +define( 'GUTENBERG__BLOCKS_LIBRARY_DIR', GUTENBERG__PLUGIN_DIR . 'blocks/library' ); + $wp_registered_blocks = array(); /** @@ -128,3 +130,12 @@ function do_blocks( $content ) { return $new_content; } add_filter( 'the_content', 'do_blocks', 10 ); // BEFORE do_shortcode(). + +/** + * Loads the server-side rendering of blocks. If your block supports + * server-side rendering, add it here. + */ +function gutenberg_load_blocks_server_side_rendering() { + require_once GUTENBERG__BLOCKS_LIBRARY_DIR . '/latest-posts/index.php'; +} +add_action( 'init', 'gutenberg_load_blocks_server_side_rendering' );