Skip to content

Commit 81f66f9

Browse files
authored
Feat (Video Popup): add video schema (#3465)
* add video schema * add note * update note * added timezone, escaped attrs, add external thumbnail * move datetime picker styles
1 parent d0c6b96 commit 81f66f9

File tree

7 files changed

+217
-31
lines changed

7 files changed

+217
-31
lines changed

src/block/countdown/editor.scss

Lines changed: 0 additions & 25 deletions
This file was deleted.

src/block/countdown/index.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ import metadata from './block.json'
1717
import deprecated from './deprecated'
1818
import substitute from './substitute'
1919

20+
export { timezones } from './timezones'
21+
2022
export const settings = {
2123
...metadata,
2224
icon: CountdownIcon,

src/block/video-popup/edit.js

Lines changed: 86 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ import {
1717
InspectorBottomTip,
1818
AdvancedToggleControl,
1919
useBlockCssGenerator,
20+
ControlSeparator,
21+
AdvancedSelectControl,
2022
} from '~stackable/components'
2123
import {
2224
BlockDiv,
@@ -38,6 +40,8 @@ import {
3840
withQueryLoopContext,
3941
} from '~stackable/higher-order'
4042

43+
import { timezones as TIMEZONE_OPTIONS } from '../countdown'
44+
4145
/**
4246
* WordPress dependencies
4347
*/
@@ -46,6 +50,8 @@ import { InnerBlocks } from '@wordpress/block-editor'
4650
import { __ } from '@wordpress/i18n'
4751
import { addFilter } from '@wordpress/hooks'
4852
import { memo } from '@wordpress/element'
53+
import { DateTimePicker } from '@wordpress/components'
54+
import { getSettings as getDateSettings } from '@wordpress/date'
4955

5056
export const defaultIcon = '<svg data-prefix="fas" data-icon="play" class="svg-inline--fa fa-play fa-w-14" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512" aria-hidden="true"><path fill="currentColor" d="M424.4 214.7L72.4 6.6C43.8-10.3 0 6.1 0 47.9V464c0 37.5 40.7 60.1 72.4 41.3l352-208c31.4-18.5 31.5-64.1 0-82.6z"></path></svg>'
5157

@@ -104,7 +110,10 @@ const Edit = props => {
104110
setAttributes={ setAttributes }
105111
videoLink={ attributes.videoLink }
106112
videoId={ attributes.videoId }
107-
113+
videoName={ attributes.videoName }
114+
videoUploadDate={ attributes.videoUploadDate }
115+
videoDescription={ attributes.videoDescription }
116+
videoUploadDateTimezone={ attributes.videoUploadDateTimezone }
108117
/>
109118

110119
{ blockCss && <style key="block-css">{ blockCss }</style> }
@@ -129,14 +138,36 @@ const Edit = props => {
129138
}
130139

131140
const InspectorControls = memo( props => {
141+
const getUploadDate = ( uploadDate, timezone ) => {
142+
// If it uses local timezone, get offset from WordPress settings
143+
if ( ! timezone ) {
144+
const { timezone: localTimezone } = getDateSettings()
145+
const offset = Number( localTimezone.offset )
146+
const hours = Math.floor( Math.abs( offset ) )
147+
const minutes = Math.round( ( Math.abs( offset ) % 1 ) * 60 )
148+
149+
return uploadDate + ( offset >= 0 ? '+' : '-' ) +
150+
String( hours ).padStart( 2, '0' ) + ':' +
151+
String( minutes ).padStart( 2, '0' )
152+
}
153+
154+
const date = new Date( uploadDate )
155+
const offset = new Intl.DateTimeFormat( 'en-US', {
156+
timeZone: timezone,
157+
timeZoneName: 'longOffset',
158+
} ).format( date ).slice( -6 )
159+
160+
return uploadDate + offset
161+
}
162+
132163
return (
133164
<>
134165
<InspectorTabs hasLayoutPanel={ false } />
135166

136167
<InspectorStyleControls>
137168
<PanelAdvancedSettings
138169
title={ __( 'General', i18n ) }
139-
id="general"
170+
id="video-popup"
140171
initialOpen={ true }
141172
>
142173
<ImageControl2
@@ -146,11 +177,17 @@ const InspectorControls = memo( props => {
146177
onRemove={ () => props.setAttributes( {
147178
videoLink: '',
148179
videoId: '',
180+
videoName: '',
181+
videoDescription: '',
182+
videoUploadDate: '',
149183
} ) }
150184
onChange={ media => {
151185
props.setAttributes( {
152186
videoLink: media.url,
153187
videoId: media.url,
188+
videoName: media.title, // Use title, description and date from media library for video schema
189+
videoDescription: media.description,
190+
videoUploadDate: media.date.toISOString(),
154191
} )
155192
} }
156193
imageId={ urlIsVideo( props.videoLink ) ? props.videoId : '' }
@@ -164,10 +201,16 @@ const InspectorControls = memo( props => {
164201
isFormatType={ false }
165202
placeholder="https://"
166203
value={ ! urlIsVideo( props.videoLink ) ? props.videoLink : '' }
167-
onChange={ videoLink => props.setAttributes( {
168-
videoLink,
169-
videoId: getVideoProviderFromURL( videoLink ).id,
170-
} ) }
204+
onChange={ videoLink => {
205+
const videoProvider = getVideoProviderFromURL( videoLink )
206+
props.setAttributes( {
207+
videoLink,
208+
videoId: videoProvider.id,
209+
videoName: '',
210+
videoDescription: '',
211+
videoUploadDate: '',
212+
} )
213+
} }
171214
/>
172215
{ isVideoFile( props.videoLink ) && <>
173216
<AdvancedToggleControl
@@ -186,6 +229,43 @@ const InspectorControls = memo( props => {
186229
defaultValue={ false }
187230
/>
188231
</> }
232+
{ props.videoLink && <>
233+
<ControlSeparator />
234+
<p>{ __( 'Note: The following attributes are used to create the video schema.', i18n ) }</p>
235+
<AdvancedTextControl
236+
label={ __( 'Video name', i18n ) }
237+
value={ props.videoName }
238+
onChange={ videoName => props.setAttributes( { videoName } ) }
239+
/>
240+
<AdvancedTextControl
241+
label={ __( 'Video description', i18n ) }
242+
value={ props.videoDescription }
243+
onChange={ videoDescription => props.setAttributes( { videoDescription } ) }
244+
isMultiline={ true }
245+
/>
246+
<AdvancedTextControl
247+
// The date picker below always highlights a date even if there is no `videoUploadDate` attribute
248+
// This text control allows users to see if a date has been set/removed
249+
className="stk-components-datetime__date-input"
250+
label={ __( 'Video upload date', i18n ) }
251+
value={ props.videoUploadDate ? getUploadDate( props.videoUploadDate, props.videoUploadDateTimezone ) : '' }
252+
onChange={ videoUploadDate => props.setAttributes( { videoUploadDate } ) }
253+
inputType="date"
254+
readOnly={ true }
255+
/>
256+
<DateTimePicker
257+
currentDate={ props.videoUploadDate }
258+
is12Hour={ true }
259+
onChange={ videoUploadDate => props.setAttributes( { videoUploadDate } ) }
260+
/>
261+
<AdvancedSelectControl
262+
label={ __( 'Timezone', i18n ) }
263+
options={ TIMEZONE_OPTIONS }
264+
attribute="videoUploadDateTimezone"
265+
allowReset={ false }
266+
/>
267+
</> }
268+
189269
</PanelAdvancedSettings>
190270

191271
</InspectorStyleControls>

src/block/video-popup/editor.scss

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,3 +28,7 @@
2828
.editor-styles-wrapper .stk-block-video-popup [data-block] {
2929
max-width: none;
3030
}
31+
32+
.stk-components-datetime__date-input .components-text-control__input {
33+
background-color: #fff;
34+
}

src/block/video-popup/index.php

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,83 @@ function stackable_load_videopopup_frontend_script() {
1919
}
2020
add_action( 'stackable/video-popup/enqueue_scripts', 'stackable_load_videopopup_frontend_script' );
2121
}
22+
23+
if ( ! class_exists( 'Stackable_Video_Popup_Schema' ) ) {
24+
class Stackable_Video_Popup_Schema {
25+
public $video_entities = [];
26+
27+
function __construct() {
28+
add_filter( 'render_block_stackable/video-popup', array( $this, 'render_block_video_popup_schema' ), 10, 2 );
29+
add_filter( 'wp_footer', array( $this, 'print_video_popup_schema' ) );
30+
}
31+
32+
public function print_video_popup_schema() {
33+
if ( count( $this->video_entities ) ) {
34+
// Compile all video schema entities into a single script
35+
echo '<script type="application/ld+json"> [ ' . implode( ', ', $this->video_entities ) . ' ] </script>';
36+
}
37+
}
38+
39+
public function get_upload_date_timezone( $timezone_name ) {
40+
// If it uses local timezone, get offset from WordPress settings
41+
if ( ! $timezone_name ) {
42+
$offset = (float) get_option( 'gmt_offset' );
43+
$hours = (int) $offset;
44+
$minutes = ( $offset - $hours );
45+
$sign = ( $offset < 0 ) ? '-' : '+';
46+
$abs_hour = abs( $hours );
47+
$abs_mins = abs( $minutes * 60 );
48+
49+
return sprintf( '%s%02d:%02d', $sign, $abs_hour, $abs_mins );
50+
}
51+
52+
$timezone = new DateTimeZone( $timezone_name );
53+
$datetime = new DateTime('now', $timezone);
54+
return $datetime->format('P');
55+
}
56+
57+
public function render_block_video_popup_schema( $block_content, $block ) {
58+
// Initialize video schema
59+
$video_schema = array(
60+
'@context' => 'https://schema.org',
61+
'@type' => 'VideoObject'
62+
);
63+
64+
// Get video schema properties from block attributes
65+
$attributes = $block[ 'attrs' ];
66+
67+
// Get video name from the title of the post if not set
68+
$name = isset( $attributes[ 'videoName' ] ) ? $attributes[ 'videoName' ] : ( get_the_title() ?? '');
69+
// Get video upload date from the date of the post if not set
70+
$upload_date_timezone = $this->get_upload_date_timezone( isset( $attributes[ 'videoUploadDateTimezone' ] ) ? $attributes[ 'videoUploadDateTimezone' ] : false );
71+
$upload_date = isset( $attributes[ 'videoUploadDate' ] ) ? $attributes[ 'videoUploadDate' ] . $upload_date_timezone : ( get_the_date( 'c' ) ?? '');
72+
$description = isset( $attributes[ 'videoDescription' ] ) ? $attributes[ 'videoDescription' ] : '';
73+
$content_url = isset( $attributes[ 'videoLink' ] ) ? $attributes[ 'videoLink' ] : '';
74+
75+
error_log( $upload_date );
76+
77+
$video_schema[ 'name' ] = esc_attr( $name );
78+
$video_schema[ 'description' ] = esc_attr( $description );
79+
$video_schema[ 'uploadDate' ] = esc_attr( $upload_date );
80+
$video_schema[ 'contentUrl' ] = esc_url( $content_url );
81+
82+
// Get thumbnail URL from the image block if it exists
83+
if ( isset( $block[ 'innerBlocks' ] )
84+
&& count( $block[ 'innerBlocks' ] ) === 2
85+
&& $block[ 'innerBlocks' ][ 1 ][ 'blockName' ] === 'stackable/image'
86+
) {
87+
$image_attributes = $block[ 'innerBlocks' ][ 1 ][ 'attrs' ];
88+
$thumbnail_url = isset( $image_attributes[ 'imageUrl' ] ) ? $image_attributes[ 'imageUrl' ]
89+
: ( isset( $image_attributes[ 'imageExternalUrl' ] ) ? $image_attributes[ 'imageExternalUrl' ] : '' );
90+
$video_schema[ 'thumbnailUrl' ] = esc_url( $thumbnail_url );
91+
}
92+
93+
$video_schema_json = wp_json_encode( $video_schema, JSON_UNESCAPED_SLASHES );
94+
$this->video_entities[] = $video_schema_json;
95+
96+
return $block_content;
97+
}
98+
}
99+
100+
new Stackable_Video_Popup_Schema();
101+
}

src/block/video-popup/schema.js

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,22 @@ export const attributes = ( version = VERSION ) => {
6262
type: 'boolean',
6363
default: false,
6464
},
65+
videoName: {
66+
type: 'string',
67+
default: '',
68+
},
69+
videoDescription: {
70+
type: 'string',
71+
default: '',
72+
},
73+
videoUploadDate: {
74+
type: 'string',
75+
default: '',
76+
},
77+
videoUploadDateTimezone: {
78+
type: 'string',
79+
default: '',
80+
},
6581
},
6682
versionAdded: '3.0.0',
6783
versionDeprecated: '',

src/editor.scss

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,3 +178,32 @@ $block-joined: #{ join($blocks, (), $separator: comma) };
178178
padding: 2px;
179179
}
180180
}
181+
182+
// Adjust the styles of †he datetime picker.
183+
.ugb-toggle-panel-body {
184+
.components-datetime__time-wrapper {
185+
.components-base-control {
186+
margin-bottom: 0px !important;
187+
}
188+
189+
.components-datetime__timezone {
190+
display: none;
191+
}
192+
}
193+
194+
.components-datetime__date {
195+
.components-datetime__date__day[aria-label*="Selected" i] {
196+
color: #fff;
197+
&:hover {
198+
color: #fff !important;
199+
}
200+
}
201+
:last-child {
202+
row-gap: 8px;
203+
column-gap: 4px;
204+
div:nth-of-type(7) {
205+
justify-self: auto;
206+
}
207+
}
208+
}
209+
}

0 commit comments

Comments
 (0)