Skip to content

Commit

Permalink
Gutenberg: Add Goodreads Block (#33395)
Browse files Browse the repository at this point in the history
* Add slug

* First stab at Goodreads block

* Load block front-end

* Handle author profiles

* Endpoint for getting user ID

* Remove unneeded parameter

* Add tests

* Address feedback

* Update class-wpcom-rest-api-v2-endpoint-goodreads.php

* Update goodreads.php

* Rewrite to functional component

* Address feedback

* Update edit.js

* Fix block registration

* Fix block registration

* Refactor block

* Remove legacy widget

* Indentation fix

* Fix tests

* Manually update files

* Manually update file

* Fix bookNumber not saving

---------

Co-authored-by: Jeremy Herve <[email protected]>
  • Loading branch information
Aurorum and jeherve authored Feb 19, 2024
1 parent 63668e3 commit c5fa5a1
Show file tree
Hide file tree
Showing 22 changed files with 1,168 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
<?php
/**
* Get the User ID of a Goodreads account using its Author ID.
*
* @package automattic/jetpack
*/

/**
* Goodreads block endpoint.
*/
class WPCOM_REST_API_V2_Endpoint_Goodreads extends WP_REST_Controller {
/**
* Constructor.
*/
public function __construct() {
add_action( 'rest_api_init', array( $this, 'register_routes' ) );
}

/**
* Register endpoint route.
*/
public function register_routes() {
register_rest_route(
'wpcom/v2',
'/goodreads/user-id',
array(
array(
'methods' => WP_REST_Server::READABLE,
'callback' => array( $this, 'get_goodreads_user_id' ),
'permission_callback' => function () {
return current_user_can( 'edit_posts' );
},
'args' => array(
'id' => array(
'description' => __( 'Goodreads user ID', 'jetpack' ),
'type' => 'integer',
'required' => true,
'minimum' => 1,
'validate_callback' => function ( $param ) {
return is_numeric( $param ) && (int) $param > 0;
},
),
),
),
)
);
}

/**
* Get the user ID from the author ID.
*
* @param \WP_REST_Request $request request object.
*
* @return int Goodreads user ID (or 404 if not found).
*/
public function get_goodreads_user_id( $request ) {
$profile_id = $request->get_param( 'id' );
$url = 'https://www.goodreads.com/author/show/' . $profile_id;
$response = wp_remote_get( esc_url_raw( $url ) );
$not_found = new WP_Error( 'not_found', 'Goodreads user not found.', array( 'status' => 404 ) );

if ( is_wp_error( $response ) ) {
return $not_found;
}

$body = wp_remote_retrieve_body( $response );
$pattern = '/goodreads\.com\/user\/updates_rss\/(\d+)/';

if ( preg_match( $pattern, $body, $matches ) ) {
$user_id = intval( $matches[1] );
return $user_id;
}

return $not_found;
}
}

wpcom_rest_api_v2_load_plugin( 'WPCOM_REST_API_V2_Endpoint_Goodreads' );
4 changes: 4 additions & 0 deletions projects/plugins/jetpack/changelog/add-goodreads-block
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Significance: minor
Type: enhancement

Comment: Add Goodreads embed block in Gutenberg.
89 changes: 89 additions & 0 deletions projects/plugins/jetpack/extensions/blocks/goodreads/block.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
{
"$schema": "https://schemas.wp.org/trunk/block.json",
"apiVersion": 1,
"name": "jetpack/goodreads",
"title": "Goodreads",
"description": "Features books from the shelves of your Goodreads account.",
"keywords": [ "book", "read", "author" ],
"version": "1.0",
"textdomain": "jetpack",
"category": "embed",
"icon": "<svg height='24' viewBox='0 0 24 24' width='24' xmlns='http://www.w3.org/2000/svg' aria-hidden='true' focusable='false'><path d='M19.525 15.977V.49h-2.059v2.906h-.064c-.211-.455-.481-.891-.842-1.307-.36-.412-.767-.777-1.232-1.094-.466-.314-.962-.561-1.519-.736C13.256.09 12.669 0 12.038 0c-1.21 0-2.3.225-3.246.67-.947.447-1.743 1.057-2.385 1.83-.642.773-1.133 1.676-1.47 2.711-.336 1.037-.506 2.129-.506 3.283 0 1.199.141 2.326.425 3.382.286 1.057.737 1.976 1.368 2.762.631.78 1.412 1.397 2.375 1.833.961.436 2.119.661 3.471.661 1.248 0 2.33-.315 3.262-.946s1.638-1.473 2.119-2.525h.061v2.284c0 2.044-.421 3.607-1.264 4.705-.84 1.081-2.224 1.638-4.146 1.638-.572 0-1.128-.061-1.669-.181-.542-.12-1.036-.315-1.487-.57-.437-.271-.827-.601-1.143-1.038-.316-.435-.526-.961-.632-1.593H5.064c.067.887.315 1.654.737 2.3.424.646.961 1.172 1.602 1.593.641.406 1.367.706 2.172.902.811.194 1.639.3 2.494.3 1.383 0 2.541-.195 3.486-.555.947-.376 1.714-.902 2.301-1.608.601-.708 1.021-1.549 1.293-2.556.27-1.007.42-2.134.42-3.367l-.044.062zm-7.484-.557c-.955 0-1.784-.189-2.479-.571-.697-.38-1.277-.882-1.732-1.503-.467-.621-.797-1.332-1.022-2.139s-.332-1.633-.332-2.484c0-.871.105-1.725.301-2.563.21-.84.54-1.587.992-2.24.451-.652 1.037-1.182 1.728-1.584s1.533-.605 2.51-.605 1.803.209 2.495.621c.676.415 1.247.959 1.683 1.634.436.677.751 1.429.947 2.255.195.826.285 1.656.285 2.482 0 .852-.12 1.678-.345 2.484-.226.807-.572 1.518-1.038 2.139-.465.621-1.021 1.123-1.698 1.503-.676.382-1.458.571-2.359.571h.064z'></path></svg>",
"supports": {
"html": false,
"align": true
},
"attributes": {
"bookNumber": {
"type": "string",
"default": "5"
},
"class": {
"type": "string"
},
"customTitle": {
"type": "string",
"default": "My Bookshelf"
},
"goodreadsId": {
"type": "string"
},
"id": {
"type": "string"
},
"link": {
"type": "string"
},
"orderOption": {
"type": "string",
"default": "a"
},
"shelfOption": {
"type": "string",
"default": "read"
},
"showAuthor": {
"type": "boolean",
"default": true
},
"showCover": {
"type": "boolean",
"default": true
},
"showRating": {
"type": "boolean",
"default": true
},
"showReview": {
"type": "boolean",
"default": false
},
"showTags": {
"type": "boolean",
"default": false
},
"showTitle": {
"type": "boolean",
"default": true
},
"sortOption": {
"type": "string",
"default": "date_added"
},
"style": {
"type": "string",
"default": "default"
},
"userInput": {
"type": "string"
},
"widgetId": {
"type": "number"
}
},
"example": {
"attributes": {
"goodreadsId": 1176283
}
}
}
131 changes: 131 additions & 0 deletions projects/plugins/jetpack/extensions/blocks/goodreads/controls.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
import {
PanelBody,
SelectControl,
TextControl,
ToggleControl,
ToolbarButton,
ToolbarGroup,
} from '@wordpress/components';
import { __ } from '@wordpress/i18n';
import { GOODREADS_SHELF_OPTIONS, GOODREADS_ORDER_OPTIONS, GOODREADS_SORT_OPTIONS } from './utils';

const renderGoodreadsDisplaySettings = ( { attributes, setAttributes } ) => {
const { showCover, showAuthor, showTitle, showRating, showReview, showTags } = attributes;

return (
<PanelBody PanelBody title={ __( 'Display Settings', 'jetpack' ) } initialOpen>
<ToggleControl
label={ __( 'Show cover', 'jetpack' ) }
checked={ showCover }
onChange={ () => setAttributes( { showCover: ! showCover } ) }
/>

<ToggleControl
label={ __( 'Show author', 'jetpack' ) }
checked={ showAuthor }
onChange={ () => setAttributes( { showAuthor: ! showAuthor } ) }
/>

<ToggleControl
label={ __( 'Show title', 'jetpack' ) }
checked={ showTitle }
onChange={ () => setAttributes( { showTitle: ! showTitle } ) }
/>

<ToggleControl
label={ __( 'Show rating', 'jetpack' ) }
checked={ showRating }
onChange={ () => setAttributes( { showRating: ! showRating } ) }
/>

<ToggleControl
label={ __( 'Show review', 'jetpack' ) }
checked={ showReview }
onChange={ () => setAttributes( { showReview: ! showReview } ) }
/>

<ToggleControl
label={ __( 'Show tags', 'jetpack' ) }
checked={ showTags }
onChange={ () => setAttributes( { showTags: ! showTags } ) }
/>
</PanelBody>
);
};

export function GoodreadsInspectorControls( { attributes, setAttributes } ) {
const { style, shelfOption, bookNumber, orderOption, customTitle, sortOption } = attributes;

return (
<>
<PanelBody PanelBody title={ __( 'Block Settings', 'jetpack' ) } initialOpen>
<SelectControl
label={ __( 'Shelf', 'jetpack' ) }
value={ shelfOption }
onChange={ value => setAttributes( { shelfOption: value } ) }
options={ GOODREADS_SHELF_OPTIONS }
/>

<TextControl
label={ __( 'Title', 'jetpack' ) }
value={ customTitle || __( 'My Bookshelf', 'jetpack' ) }
onChange={ value => setAttributes( { customTitle: value } ) }
/>

<SelectControl
label={ __( 'Sort by', 'jetpack' ) }
value={ sortOption }
onChange={ value => setAttributes( { sortOption: value } ) }
options={ GOODREADS_SORT_OPTIONS }
/>

<SelectControl
label={ __( 'Order', 'jetpack' ) }
value={ orderOption }
onChange={ value => setAttributes( { orderOption: value } ) }
options={ GOODREADS_ORDER_OPTIONS }
/>

<TextControl
label={ __( 'Number of books', 'jetpack' ) }
type="number"
inputMode="numeric"
min="1"
max={ style === 'grid' ? 200 : 100 }
value={ bookNumber || 5 }
onChange={ value => setAttributes( { bookNumber: value } ) }
/>
</PanelBody>
{ style === 'default' && renderGoodreadsDisplaySettings( { attributes, setAttributes } ) }
</>
);
}

export function GoodreadsBlockControls( { attributes, setAttributes, setDisplayPreview } ) {
const { style } = attributes;
const layoutControls = [
{
icon: 'list-view',
title: __( 'Default view', 'jetpack' ),
onClick: () => setAttributes( { style: 'default' } ),
isActive: style === 'default',
},
{
icon: 'grid-view',
title: __( 'Grid view', 'jetpack' ),
onClick: () => setAttributes( { style: 'grid' } ),
isActive: style === 'grid',
},
];

return (
<>
<ToolbarButton
label={ __( 'Edit URL', 'jetpack' ) }
icon="edit"
onClick={ () => setDisplayPreview( false ) }
/>
<ToolbarGroup controls={ layoutControls } />
</>
);
}
Loading

0 comments on commit c5fa5a1

Please sign in to comment.