Skip to content

Commit b31be21

Browse files
committed
feat: add featured image caption block
1 parent 2b3efad commit b31be21

File tree

7 files changed

+245
-0
lines changed

7 files changed

+245
-0
lines changed

includes/class-blocks.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ public static function init() {
3333
if ( wp_is_block_theme() ) {
3434
require_once NEWSPACK_ABSPATH . 'src/blocks/avatar/class-avatar-block.php';
3535
require_once NEWSPACK_ABSPATH . 'src/blocks/byline/class-byline-block.php';
36+
require_once NEWSPACK_ABSPATH . 'src/blocks/featured-image-caption/class-featured-image-caption-block.php';
3637
require_once NEWSPACK_ABSPATH . 'src/blocks/author-profile-social/class-author-profile-social-block.php';
3738
require_once NEWSPACK_ABSPATH . 'src/blocks/author-social-link/class-author-social-link-block.php';
3839
require_once NEWSPACK_ABSPATH . 'src/blocks/copyright-date/class-copyright-date-block.php';
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
{
2+
"$schema": "https://schemas.wp.org/trunk/block.json",
3+
"apiVersion": 3,
4+
"name": "newspack/featured-image-caption",
5+
"title": "Featured Image Caption",
6+
"category": "newspack",
7+
"textdomain": "newspack-plugin",
8+
"attributes": {
9+
"customCaption": {
10+
"type": "string",
11+
"default": ""
12+
}
13+
},
14+
"usesContext": [ "postId", "postType" ],
15+
"supports": {
16+
"html": false,
17+
"color": {
18+
"gradients": true,
19+
"__experimentalDefaultControls": {
20+
"background": true,
21+
"text": true,
22+
"link": true
23+
}
24+
},
25+
"spacing": {
26+
"margin": true,
27+
"padding": true
28+
},
29+
"typography": {
30+
"fontSize": true,
31+
"lineHeight": true,
32+
"textAlign": true,
33+
"__experimentalFontFamily": true,
34+
"__experimentalFontWeight": true,
35+
"__experimentalFontStyle": true,
36+
"__experimentalTextTransform": true,
37+
"__experimentalTextDecoration": true,
38+
"__experimentalLetterSpacing": true,
39+
"__experimentalDefaultControls": {
40+
"fontSize": true,
41+
"fontAppearance": true
42+
}
43+
}
44+
}
45+
}
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
<?php
2+
/**
3+
* Featured Image Caption Block.
4+
*
5+
* @package Newspack
6+
*/
7+
8+
namespace Newspack\Blocks\FeaturedImageCaption;
9+
10+
defined( 'ABSPATH' ) || exit;
11+
12+
/**
13+
* Featured Image Caption Block class.
14+
*/
15+
final class Featured_Image_Caption_Block {
16+
17+
/**
18+
* Initializer.
19+
*/
20+
public static function init() {
21+
\add_action( 'init', [ __CLASS__, 'register_block' ] );
22+
}
23+
24+
/**
25+
* Register the block.
26+
*/
27+
public static function register_block() {
28+
register_block_type_from_metadata(
29+
__DIR__ . '/block.json',
30+
[
31+
'render_callback' => [ __CLASS__, 'render_block' ],
32+
]
33+
);
34+
}
35+
36+
/**
37+
* Block render callback.
38+
*
39+
* @param array $attributes Block attributes.
40+
* @param string $content Block content.
41+
* @param \WP_Block $block Block instance.
42+
* @return string Rendered block.
43+
*/
44+
public static function render_block( $attributes, $content, $block ) {
45+
$post_id = ! empty( $block->context['postId'] ) ? $block->context['postId'] : get_the_ID();
46+
if ( ! $post_id ) {
47+
return '';
48+
}
49+
50+
$featured_image_id = get_post_thumbnail_id( $post_id );
51+
if ( ! $featured_image_id ) {
52+
return '';
53+
}
54+
55+
$custom_caption = $attributes['customCaption'] ?? '';
56+
57+
if ( $custom_caption ) {
58+
$output = wp_kses_post( $custom_caption );
59+
} else {
60+
$caption = wp_kses_post( wp_get_attachment_caption( $featured_image_id ) );
61+
$credit = '';
62+
63+
if ( class_exists( '\Newspack\Newspack_Image_Credits' ) ) {
64+
$credit = \Newspack\Newspack_Image_Credits::get_media_credit_string( $featured_image_id );
65+
}
66+
67+
$output = trim( $caption );
68+
if ( $output && $credit ) {
69+
$output .= ' ' . $credit;
70+
} elseif ( $credit ) {
71+
$output = $credit;
72+
}
73+
}
74+
75+
if ( ! $output ) {
76+
return '';
77+
}
78+
79+
$wrapper_attributes = get_block_wrapper_attributes();
80+
return sprintf( '<figcaption %1$s>%2$s</figcaption>', $wrapper_attributes, $output );
81+
}
82+
}
83+
Featured_Image_Caption_Block::init();
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
import { useBlockProps, RichText } from '@wordpress/block-editor';
2+
import { useEntityProp } from '@wordpress/core-data';
3+
import { useSelect } from '@wordpress/data';
4+
import { __, sprintf } from '@wordpress/i18n';
5+
6+
/**
7+
* Generates the credit text for the media credit and organization.
8+
*
9+
* @param {string} mediaCredit The media credit.
10+
* @param {string} organization The organization associated with the media credit. Optional.
11+
*
12+
* @return {string} The formatted credit text.
13+
*/
14+
const generateCreditText = ( mediaCredit, organization ) => {
15+
if ( mediaCredit && organization ) {
16+
return sprintf(
17+
/* translators: 1: media credit, 2: organization */
18+
__( 'Credit: %1$s / %2$s', 'newspack-plugin' ),
19+
mediaCredit,
20+
organization
21+
);
22+
}
23+
24+
return sprintf(
25+
/* translators: %s: media credit */
26+
__( 'Credit: %s', 'newspack-plugin' ),
27+
mediaCredit
28+
);
29+
};
30+
31+
export const Edit = ( { attributes, setAttributes, context: { postType, postId } } ) => {
32+
const blockProps = useBlockProps();
33+
34+
const [ featuredImage ] = useEntityProp( 'postType', postType, 'featured_media', postId );
35+
36+
const { caption, credit } = useSelect(
37+
select => {
38+
if ( ! featuredImage ) {
39+
return {};
40+
}
41+
const media = select( 'core' ).getMedia( featuredImage );
42+
if ( ! media ) {
43+
return {};
44+
}
45+
return {
46+
caption: media.caption?.raw || '',
47+
credit: media.meta?._media_credit ? generateCreditText( media.meta._media_credit, media.meta?._navis_media_credit_org ) : '',
48+
};
49+
},
50+
[ featuredImage ]
51+
);
52+
53+
const defaultText = [ caption, credit ].filter( Boolean ).join( ' ' );
54+
55+
if ( ! featuredImage ) {
56+
return (
57+
<figcaption { ...blockProps }>
58+
<span className="featured-image-caption-placeholder">
59+
{ __( 'Select a featured image or add a featured image block to add a caption.', 'newspack-plugin' ) }
60+
</span>
61+
</figcaption>
62+
);
63+
}
64+
65+
return (
66+
<RichText
67+
{ ...blockProps }
68+
tagName="figcaption"
69+
value={ attributes.customCaption }
70+
onChange={ val => setAttributes( { customCaption: val } ) }
71+
placeholder={ defaultText || __( 'Write caption…', 'newspack-plugin' ) }
72+
allowedFormats={ [ 'core/bold', 'core/italic', 'core/link' ] }
73+
/>
74+
);
75+
};
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
/**
2+
* WordPress dependencies
3+
*/
4+
import { __ } from '@wordpress/i18n';
5+
import { caption as icon } from '@wordpress/icons';
6+
7+
/**
8+
* Internal dependencies
9+
*/
10+
import metadata from './block.json';
11+
import { Edit } from './edit';
12+
import colors from '../../../packages/colors/colors.module.scss';
13+
import './style.scss';
14+
15+
export const title = __( 'Featured Image Caption', 'newspack-plugin' );
16+
17+
const { name } = metadata;
18+
19+
export { metadata, name };
20+
21+
export const settings = {
22+
title,
23+
icon: {
24+
src: icon,
25+
foreground: colors[ 'primary-400' ],
26+
},
27+
keywords: [
28+
__( 'caption', 'newspack-plugin' ),
29+
__( 'featured image', 'newspack-plugin' ),
30+
__( 'credit', 'newspack-plugin' ),
31+
__( 'newspack', 'newspack-plugin' ),
32+
],
33+
description: __( 'Display the featured image caption and credit.', 'newspack-plugin' ),
34+
edit: Edit,
35+
};
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
.featured-image-caption-placeholder {
2+
opacity: 0.6;
3+
}

src/blocks/index.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import * as correctionBox from './correction-box';
1414
import * as correctionItem from './correction-item';
1515
import * as avatar from './avatar';
1616
import * as byline from './byline';
17+
import * as featuredImageCaption from './featured-image-caption';
1718
import * as authorProfileSocial from './author-profile-social';
1819
import * as authorSocialLink from './author-social-link';
1920
import * as collections from './collections';
@@ -33,6 +34,7 @@ export const blocks = [
3334
correctionItem,
3435
avatar,
3536
byline,
37+
featuredImageCaption,
3638
authorProfileSocial,
3739
authorSocialLink,
3840
collections,
@@ -51,6 +53,7 @@ const blockThemeBlocks = [
5153
'newspack/avatar',
5254
'newspack/byline',
5355
'newspack/copyright-date',
56+
'newspack/featured-image-caption',
5457
'newspack/my-account-button',
5558
];
5659

0 commit comments

Comments
 (0)