Skip to content

Commit

Permalink
Blocks: Add a new "Avatar" block to use in Query Loop (#695)
Browse files Browse the repository at this point in the history
* Post Types: Add featured image support to organizers and speakers.

* Blocks: Add "Avatar" block to output speaker & organizer gravatars

* Add extra avatar size options

* Enable duotone feature on avatars

* Use the featured image for avatar if one exists

* Update the logic to always return an image, falling back to the default gravatar

* Display the featured image in place of the avatar

* Hide featured images on speaker & organizer posts to avoid changing behavior

* Use new avatar_or_image function in speaker & organizer blocks

* Update escaping

* Update hook comment to explain reasoning
  • Loading branch information
ryelle authored Sep 13, 2021
1 parent bf802a6 commit bc1e644
Show file tree
Hide file tree
Showing 12 changed files with 374 additions and 15 deletions.
1 change: 1 addition & 0 deletions public_html/wp-content/mu-plugins/blocks/blocks.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ function load_includes() {
require_once $components_dir . 'post-list/controller.php';

// Blocks.
require_once $blocks_dir . 'avatar/controller.php';
require_once $blocks_dir . 'organizers/controller.php';
require_once $blocks_dir . 'schedule/controller.php';
require_once $blocks_dir . 'sessions/controller.php';
Expand Down
2 changes: 2 additions & 0 deletions public_html/wp-content/mu-plugins/blocks/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,14 @@
"@wordpress/blocks": "11.0.1",
"@wordpress/components": "16.0.0",
"@wordpress/compose": "5.0.1",
"@wordpress/core-data": "4.0.1",
"@wordpress/data": "6.0.1",
"@wordpress/date": "4.2.1",
"@wordpress/element": "4.0.0",
"@wordpress/eslint-plugin": "9.1.1",
"@wordpress/html-entities": "3.2.1",
"@wordpress/i18n": "4.2.1",
"@wordpress/icons": "5.0.1",
"@wordpress/scripts": "18.0.0",
"@wordpress/server-side-render": "^3.0.1",
"@wordpress/url": "3.2.1",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
{
"apiVersion": 2,
"name": "wordcamp/avatar",
"title": "Avatar",
"category": "wordcamp",
"description": "Display the current person's avatar.",
"textdomain": "wordcamporg",
"usesContext": [ "postId", "postType", "queryId" ],
"attributes": {
"isLink": {
"type": "boolean",
"default": false
},
"size": {
"type": "number"
}
},
"supports": {
"align": [ "left", "right", "center" ],
"color": {
"__experimentalDuotone": "img",
"text": false,
"background": false
},
"html": false
},
"editor_script": "wordcamp-blocks",
"editor_style": "wordcamp-blocks"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
<?php
namespace WordCamp\Blocks\Avatar;
use function WordCamp\Post_Types\Utilities\get_avatar_or_image;

defined( 'WPINC' ) || die();

/**
* Register block types and enqueue scripts.
*
* @return void
*/
function init() {
register_block_type_from_metadata(
__DIR__,
array(
'attributes' => get_attributes_schema(),
'render_callback' => __NAMESPACE__ . '\render',
)
);
}
add_action( 'init', __NAMESPACE__ . '\init' );


/**
* Renders the block on the server.
*
* @param array $attributes Block attributes.
* @param string $content Block default content.
* @param WP_Block $block Block instance.
* @return string Returns the avatar for the current post.
*/
function render( $attributes, $content, $block ) {
if ( ! isset( $block->context['postId'] ) ) {
return '';
}
$post_ID = $block->context['postId'];

$defaults = wp_list_pluck( get_attributes_schema(), 'default' );
$attributes = wp_parse_args( $attributes, $defaults );
$email = get_post_meta( $post_ID, '_wcb_speaker_email', true );
$user_id = get_post_meta( $post_ID, '_wcpt_user_id', true );

$size = intval( $attributes['size'] );
$wrapper_attributes = get_block_wrapper_attributes( array(
'style' => "width:{$size}px;height:{$size}px;",
) );

$avatar = get_avatar_or_image( $post_ID, $size );

if ( isset( $attributes['isLink'] ) && $attributes['isLink'] ) {
$avatar = sprintf( '<a href="%1s">%2s</a>', get_the_permalink( $post_ID ), $avatar );
}

return "<figure $wrapper_attributes>$avatar</figure>";
}

/**
* Add data to be used by the JS scripts in the block editor.
*
* @param array $data
*
* @return array
*/
function add_script_data( array $data ) {
$data['avatar'] = array(
'schema' => get_attributes_schema(),
'options' => get_options(),
);

return $data;
}
add_filter( 'wordcamp_blocks_script_data', __NAMESPACE__ . '\add_script_data' );

/**
* Get the schema for the block's attributes.
*
* @return array
*/
function get_attributes_schema() {
$schema = array(
'isLink' => array(
'type' => 'boolean',
'default' => false,
),
'size' => array(
'type' => 'number',
'enum' => get_options( 'size' ),
'default' => 96,
),
);

return $schema;
}

/**
* Get the label/value pairs for all options or a specific type.
*
* @param string $type
*
* @return array
*/
function get_options( $type = '' ) {
$options = array(
'size' => rest_get_avatar_sizes(),
);

if ( $type ) {
return empty( $options[ $type ] ) ? array() : $options[ $type ];
}

return $options;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
/**
* WordPress dependencies
*/
import { __, sprintf } from '@wordpress/i18n';
import { store as coreStore, useEntityProp } from '@wordpress/core-data';
import { InspectorControls, useBlockProps } from '@wordpress/block-editor';
import { PanelBody, SelectControl, ToggleControl } from '@wordpress/components';
import { useSelect } from '@wordpress/data';

const placeholderChip = (
<div className="post-featured-image_placeholder">
<p> { __( 'Avatar', 'wordcamporg' ) }</p>
</div>
);

function getPostLabel( postType ) {
switch ( postType ) {
case 'wcb_speaker':
return __( 'Speaker', 'wordcamporg' );
case 'wcb_organizer':
return __( 'Organizer', 'wordcamporg' );
default:
return __( 'post', 'wordcamporg' );
}
}

const blockData = window.WordCampBlocks.avatar || {};

export default function PostAvatarEdit( { attributes, setAttributes, context: { postId, postType } } ) {
const { isLink, size = blockData.schema.size.default } = attributes;
const [ urls ] = useEntityProp( 'postType', postType, 'avatar_urls', postId );
const blockProps = useBlockProps( { style: { width: size, height: size } } );
const [ featuredImage ] = useEntityProp( 'postType', postType, 'featured_media', postId );
const url = useSelect(
( select ) => {
if ( ! featuredImage ) {
return urls ? urls[ size ] : '';
}
const image = select( coreStore ).getMedia( featuredImage, { context: 'view' } );
return image?.source_url || '';
},
[ featuredImage ]
);

if ( ! url ) {
return <div { ...blockProps }>{ placeholderChip }</div>;
}

const sizeOptions = blockData.options.size.map( ( value ) => ( { label: value + 'px', value: value } ) );

return (
<>
<InspectorControls>
<PanelBody title={ __( 'Settings', 'wordcamporg' ) }>
<SelectControl
label={ __( 'Avatar size', 'wordcamporg' ) }
value={ size }
options={ sizeOptions }
onChange={ ( newSize ) => setAttributes( { size: Number( newSize ) } ) }
/>
<ToggleControl
label={ sprintf(
// translators: %s: Name of the post type e.g: "post".
__( 'Link to %s', 'wordcamporg' ),
getPostLabel( postType )
) }
onChange={ () => setAttributes( { isLink: ! isLink } ) }
checked={ isLink }
/>
</PanelBody>
</InspectorControls>
<figure { ...blockProps }>
<img src={ url } alt={ __( 'Avatar', 'wordcamporg' ) } />
</figure>
</>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/**
* WordPress dependencies
*/
import { people as icon } from '@wordpress/icons';

/**
* Internal dependencies
*/
import edit from './edit';
import metadata from './block.json';

export const NAME = metadata.name;

export const SETTINGS = {
...metadata,
icon,
edit,
};
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/**
* Internal dependencies
*/
import * as avatar from './avatar';
import * as liveSchedule from './live-schedule';
import * as organizers from './organizers';
import * as schedule from './schedule';
Expand All @@ -9,6 +10,7 @@ import * as speakers from './speakers';
import * as sponsors from './sponsors';

export const BLOCKS = [
avatar,
liveSchedule,
organizers,
schedule,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
use WP_Post;
use function WordCamp\Blocks\Components\{ render_item_title, render_item_content };
use function WordCamp\Blocks\Utilities\{ get_all_the_content, get_trimmed_content };
use function WordCamp\Post_Types\Utilities\get_avatar_or_image;

defined( 'WPINC' ) || die();

Expand All @@ -28,12 +29,10 @@

<?php if ( true === $attributes['show_avatars'] ) : ?>
<div class="wordcamp-image__avatar-container align-<?php echo esc_attr( $attributes['avatar_align'] ); ?>">
<?php echo get_avatar(
$organizer->_wcpt_user_id,
<?php echo get_avatar_or_image( // phpcs:ignore -- escaped in function.
$organizer->ID,
$attributes['avatar_size'],
'',
sprintf( __( 'Avatar of %s', 'wordcamporg'), get_the_title( $organizer ) ),
array( 'force_display' => true )
sprintf( __( 'Avatar of %s', 'wordcamporg'), get_the_title( $organizer ) )
); ?>
</div>
<?php endif; ?>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
use WP_Post;
use function WordCamp\Blocks\Components\{ render_item_title, render_item_content, render_item_permalink };
use function WordCamp\Blocks\Utilities\{ get_all_the_content, get_trimmed_content };
use function WordCamp\Post_Types\Utilities\get_avatar_or_image;

defined( 'WPINC' ) || die();

Expand All @@ -28,12 +29,10 @@
<?php if ( true === $attributes['show_avatars'] ) : ?>
<div class="wordcamp-image__avatar-container align-<?php echo esc_attr( $attributes['avatar_align'] ); ?>">
<a href="<?php echo esc_url( get_permalink( $speaker ) ); ?>" class="wordcamp-image__avatar-link">
<?php echo get_avatar(
$speaker->_wcb_speaker_email,
<?php echo get_avatar_or_image( // phpcs:ignore -- escaped in function.
$speaker->ID,
$attributes['avatar_size'],
'',
sprintf( __( 'Avatar of %s', 'wordcamporg'), get_the_title( $speaker ) ),
array( 'force_display' => true )
sprintf( __( 'Avatar of %s', 'wordcamporg'), get_the_title( $speaker ) )
); ?>
</a>
</div>
Expand Down
16 changes: 16 additions & 0 deletions public_html/wp-content/plugins/wc-post-types/inc/rest-api.php
Original file line number Diff line number Diff line change
Expand Up @@ -646,3 +646,19 @@ function link_session_to_speakers( $response, $post ) {
}

add_filter( 'rest_prepare_wcb_session', __NAMESPACE__ . '\link_session_to_speakers', 10, 2 );

/**
* Add larger avatar sizes to the API response.
*
* @param int[] $sizes An array of int values that are the pixel sizes for avatars.
*
* @return array
*/
function add_larger_avatar_sizes( $sizes ) {
$sizes[] = 128;
$sizes[] = 256;
$sizes[] = 512;

return $sizes;
}
add_filter( 'rest_avatar_sizes', __NAMESPACE__ . '\add_larger_avatar_sizes' );
Loading

0 comments on commit bc1e644

Please sign in to comment.