diff options
Diffstat (limited to 'plugins/jetpack/extensions')
7 files changed, 587 insertions, 10 deletions
diff --git a/plugins/jetpack/extensions/blocks/amazon/amazon.php b/plugins/jetpack/extensions/blocks/amazon/amazon.php new file mode 100644 index 00000000..a368ab79 --- /dev/null +++ b/plugins/jetpack/extensions/blocks/amazon/amazon.php @@ -0,0 +1,26 @@ +<?php +/** + * Amazon Block. + * + * @since 8.x + * + * @package Jetpack + */ + +jetpack_register_block( + 'jetpack/amazon', + array( 'render_callback' => 'jetpack_amazon_block_load_assets' ) +); + +/** + * Amazon block registration/dependency declaration. + * + * @param array $attr Array containing the Amazon block attributes. + * @param string $content String containing the Amazon block content. + * + * @return string + */ +function jetpack_amazon_block_load_assets( $attr, $content ) { + Jetpack_Gutenberg::load_assets_as_required( 'amazon' ); + return $content; +} diff --git a/plugins/jetpack/extensions/blocks/calendly/calendly.php b/plugins/jetpack/extensions/blocks/calendly/calendly.php new file mode 100644 index 00000000..0875a04e --- /dev/null +++ b/plugins/jetpack/extensions/blocks/calendly/calendly.php @@ -0,0 +1,185 @@ +<?php +/** + * Calendly Block. + * + * @since 8.2.0 + * + * @package Jetpack + */ + +namespace Jetpack\Calendly_Block; + +const FEATURE_NAME = 'calendly'; +const BLOCK_NAME = 'jetpack/' . FEATURE_NAME; + +/** + * Check if the block should be available on the site. + * + * @return bool + */ +function is_available() { + if ( + defined( 'IS_WPCOM' ) + && IS_WPCOM + && function_exists( 'has_any_blog_stickers' ) + ) { + if ( has_any_blog_stickers( + array( 'premium-plan', 'business-plan', 'ecommerce-plan' ), + get_current_blog_id() + ) ) { + return true; + } + return false; + } + + return true; +} + +/** + * Registers the block for use in Gutenberg + * This is done via an action so that we can disable + * registration if we need to. + */ +function register_block() { + if ( is_available() ) { + jetpack_register_block( + BLOCK_NAME, + array( 'render_callback' => 'Jetpack\Calendly_Block\load_assets' ) + ); + } +} +add_action( 'init', 'Jetpack\Calendly_Block\register_block' ); + +/** + * Set the availability of the block as the editor + * is loaded + */ +function set_availability() { + if ( is_available() ) { + \Jetpack_Gutenberg::set_extension_available( BLOCK_NAME ); + } else { + \Jetpack_Gutenberg::set_extension_unavailable( + BLOCK_NAME, + 'missing_plan', + array( + 'required_feature' => 'calendly', + 'required_plan' => 'value_bundle', + ) + ); + } +} +add_action( 'init', 'Jetpack\Calendly_Block\set_availability' ); + +/** + * Calendly block registration/dependency declaration. + * + * @param array $attr Array containing the Calendly block attributes. + * @param string $content String containing the Calendly block content. + * + * @return string + */ +function load_assets( $attr, $content ) { + $url = get_attribute( $attr, 'url' ); + if ( empty( $url ) ) { + return; + } + + /* + * Enqueue necessary scripts and styles. + */ + \Jetpack_Gutenberg::load_assets_as_required( 'calendly' ); + wp_enqueue_script( + 'jetpack-calendly-external-js', + 'https://assets.calendly.com/assets/external/widget.js', + null, + JETPACK__VERSION, + false + ); + + $style = get_attribute( $attr, 'style' ); + $hide_event_type_details = get_attribute( $attr, 'hideEventTypeDetails' ); + $background_color = get_attribute( $attr, 'backgroundColor' ); + $text_color = get_attribute( $attr, 'textColor' ); + $primary_color = get_attribute( $attr, 'primaryColor' ); + $submit_button_text = get_attribute( $attr, 'submitButtonText' ); + $submit_button_text_color = get_attribute( $attr, 'customTextButtonColor' ); + $submit_button_background_color = get_attribute( $attr, 'customBackgroundButtonColor' ); + $classes = \Jetpack_Gutenberg::block_classes( 'calendly', $attr ); + $block_id = wp_unique_id( 'calendly-block-' ); + + $url = add_query_arg( + array( + 'hide_event_type_details' => (int) $hide_event_type_details, + 'background_color' => sanitize_hex_color_no_hash( $background_color ), + 'text_color' => sanitize_hex_color_no_hash( $text_color ), + 'primary_color' => sanitize_hex_color_no_hash( $primary_color ), + ), + $url + ); + + if ( 'link' === $style ) { + wp_enqueue_style( 'jetpack-calendly-external-css', 'https://assets.calendly.com/assets/external/widget.css', null, JETPACK__VERSION ); + + /* + * If we have some additional styles from the editor + * (a custom text color, custom bg color, or both ) + * Let's add that CSS inline. + */ + if ( ! empty( $submit_button_text_color ) || ! empty( $submit_button_background_color ) ) { + $inline_styles = sprintf( + '#%1$s .wp-block-button__link{%2$s%3$s}', + esc_attr( $block_id ), + ! empty( $submit_button_text_color ) + ? 'color:#' . sanitize_hex_color_no_hash( $submit_button_text_color ) . ';' + : '', + ! empty( $submit_button_background_color ) + ? 'background-color:#' . sanitize_hex_color_no_hash( $submit_button_background_color ) . ';' + : '' + ); + wp_add_inline_style( 'jetpack-calendly-external-css', $inline_styles ); + } + + $content = sprintf( + '<div class="%1$s" id="%2$s"><a class="wp-block-button__link" role="button" onclick="Calendly.initPopupWidget({url:\'%3$s\'});return false;">%4$s</a></div>', + esc_attr( $classes ), + esc_attr( $block_id ), + esc_js( $url ), + wp_kses_post( $submit_button_text ) + ); + } else { // Inline style. + $content = sprintf( + '<div class="calendly-inline-widget %1$s" data-url="%2$s" style="min-width:320px;height:630px;"></div>', + esc_attr( $classes ), + esc_url( $url ) + ); + } + + return $content; +} + +/** + * Get filtered attributes. + * + * @param array $attributes Array containing the Calendly block attributes. + * @param string $attribute_name String containing the attribute name to get. + * + * @return string + */ +function get_attribute( $attributes, $attribute_name ) { + if ( isset( $attributes[ $attribute_name ] ) ) { + return $attributes[ $attribute_name ]; + } + + $default_attributes = array( + 'style' => 'inline', + 'submitButtonText' => esc_html__( 'Schedule time with me', 'jetpack' ), + 'backgroundColor' => 'ffffff', + 'textColor' => '4D5055', + 'primaryColor' => '00A2FF', + 'hideEventTypeDetails' => false, + ); + + if ( isset( $default_attributes[ $attribute_name ] ) ) { + return $default_attributes[ $attribute_name ]; + } +} diff --git a/plugins/jetpack/extensions/blocks/contact-info/class-jetpack-contact-info-block.php b/plugins/jetpack/extensions/blocks/contact-info/class-jetpack-contact-info-block.php index 7a34cbbb..cecf6949 100644 --- a/plugins/jetpack/extensions/blocks/contact-info/class-jetpack-contact-info-block.php +++ b/plugins/jetpack/extensions/blocks/contact-info/class-jetpack-contact-info-block.php @@ -98,7 +98,8 @@ class Jetpack_Contact_Info_Block { } /** - * Adds phone schema attributes. + * Adds phone schema attributes. Also wraps the tel link in a span so that + * it's recognized as a telephone number in Google's Structured Data. * * @param array $attr Array containing the phone block attributes. * @param string $content String containing the phone block content. @@ -106,9 +107,14 @@ class Jetpack_Contact_Info_Block { * @return string */ public static function render_phone( $attr, $content ) { - $content = self::has_attributes( $attr, array( 'className' ) ) ? - str_replace( 'href="tel:', 'itemprop="telephone" href="tel:', $content ) : - ''; - return $content; + if ( self::has_attributes( $attr, array( 'className' ) ) ) { + return str_replace( + array( '<a href="tel:', '</a>' ), + array( '<span itemprop="telephone"><a href="tel:', '</a></span>' ), + $content + ); + } + + return ''; } } diff --git a/plugins/jetpack/extensions/blocks/eventbrite/eventbrite.php b/plugins/jetpack/extensions/blocks/eventbrite/eventbrite.php new file mode 100644 index 00000000..0135837e --- /dev/null +++ b/plugins/jetpack/extensions/blocks/eventbrite/eventbrite.php @@ -0,0 +1,101 @@ +<?php +/** + * Eventbrite Block. + * + * @since 8.2.0 + * + * @package Jetpack + */ + +jetpack_register_block( + 'jetpack/eventbrite', + array( + 'render_callback' => 'jetpack_render_eventbrite_block', + ) +); + +/** + * Eventbrite block registration/dependency delclaration. + * + * @param array $attr Eventbrite block attributes. + * @param string $content Rendered embed element (without scripts) from the block editor. + * + * @return string + */ +function jetpack_render_eventbrite_block( $attr, $content ) { + if ( is_admin() || empty( $attr['eventId'] ) || empty( $attr['url'] ) ) { + return ''; + } + + $widget_id = wp_unique_id( 'eventbrite-widget-' ); + + wp_enqueue_script( 'eventbrite-widget', 'https://www.eventbrite.com/static/widgets/eb_widgets.js', array(), JETPACK__VERSION, true ); + + // Add CSS to hide direct link. + Jetpack_Gutenberg::load_assets_as_required( 'eventbrite' ); + + // Show the embedded version. + if ( empty( $attr['useModal'] ) ) { + wp_add_inline_script( + 'eventbrite-widget', + "window.EBWidgets.createWidget( { + widgetType: 'checkout', + eventId: " . absint( $attr['eventId'] ) . ", + iframeContainerId: '" . esc_js( $widget_id ) . "', + } );" + ); + + // $content contains a fallback link to the event that's saved in the post_content. + // Append a div that will hold the iframe embed created by the Eventbrite widget.js. + $content .= sprintf( + '<div id="%s" class="eventbrite__in-page-checkout"></div>', + esc_attr( $widget_id ) + ); + + return sprintf( + '%s<noscript><a href="%s" rel="noopener noreferrer" target="_blank">%s</a></noscript>', + $content, + esc_url( $attr['url'] ), + esc_html__( 'Register on Eventbrite', 'jetpack' ) + ); + } + + // Show the modal version. + wp_add_inline_script( + 'eventbrite-widget', + "window.EBWidgets.createWidget( { + widgetType: 'checkout', + eventId: " . absint( $attr['eventId'] ) . ", + modal: true, + modalTriggerElementId: '" . esc_js( $widget_id ) . "', + } );" + ); + + // Modal button is saved as an `<a>` element with `role="button"` because `<button>` is not allowed + // by WordPress.com wp_kses. This javascript adds the necessary event handling for button-like behavior. + // @link https://www.w3.org/TR/wai-aria-practices/examples/button/button.html. + wp_add_inline_script( + 'eventbrite-widget', + "( function() { + var widget = document.getElementById( '" . esc_js( $widget_id ) . "' ); + if ( widget ) { + widget.addEventListener( 'click', function( event ) { + event.preventDefault(); + } ); + + widget.addEventListener( 'keydown', function( event ) { + // Enter and space keys. + if ( event.keyCode === 13 || event.keyCode === 32 ) { + event.preventDefault(); + event.target && event.target.click(); + } + } ); + } + } )();" + ); + + // Replace the placeholder id saved in the post_content with a unique id used by widget.js. + $content = preg_replace( '/eventbrite-widget-\d+/', $widget_id, $content ); + + return $content; +} diff --git a/plugins/jetpack/extensions/blocks/map/map.php b/plugins/jetpack/extensions/blocks/map/map.php index d0c82a0d..d3b881be 100644 --- a/plugins/jetpack/extensions/blocks/map/map.php +++ b/plugins/jetpack/extensions/blocks/map/map.php @@ -15,6 +15,32 @@ jetpack_register_block( ); /** + * Return the site's own Mapbox API key if set, or the WordPress.com's one otherwise. + * + * @return string + */ +function jetpack_get_mapbox_api_key() { + if ( defined( 'IS_WPCOM' ) && IS_WPCOM ) { + $endpoint = sprintf( + 'https://public-api.wordpress.com/wpcom/v2/sites/%d/service-api-keys/mapbox', + get_current_blog_id() + ); + } else { + $endpoint = rest_url( 'wpcom/v2/service-api-keys/mapbox' ); + } + + $response = wp_remote_get( esc_url_raw( $endpoint ) ); + $response_code = wp_remote_retrieve_response_code( $response ); + + if ( 200 === $response_code ) { + $response_body = json_decode( wp_remote_retrieve_body( $response ) ); + return $response_body->service_api_key; + } + + return Jetpack_Options::get_option( 'mapbox_api_key' ); +} + +/** * Map block registration/dependency declaration. * * @param array $attr Array containing the map block attributes. @@ -23,7 +49,7 @@ jetpack_register_block( * @return string */ function jetpack_map_block_load_assets( $attr, $content ) { - $api_key = Jetpack_Options::get_option( 'mapbox_api_key' ); + $api_key = jetpack_get_mapbox_api_key(); if ( class_exists( 'Jetpack_AMP_Support' ) && Jetpack_AMP_Support::is_amp_request() ) { static $map_block_counter = array(); @@ -110,7 +136,7 @@ function jetpack_map_block_render_single_block_page() { /* Put together a new complete document containing only the requested block markup and the scripts/styles needed to render it */ $block_markup = $post_html->saveHTML( $container ); - $api_key = Jetpack_Options::get_option( 'mapbox_api_key' ); + $api_key = jetpack_get_mapbox_api_key(); $page_html = sprintf( '<!DOCTYPE html><head><style>html, body { margin: 0; padding: 0; }</style>%s</head><body>%s</body>', $head_content, diff --git a/plugins/jetpack/extensions/blocks/opentable/opentable.php b/plugins/jetpack/extensions/blocks/opentable/opentable.php new file mode 100644 index 00000000..1ea9cef1 --- /dev/null +++ b/plugins/jetpack/extensions/blocks/opentable/opentable.php @@ -0,0 +1,231 @@ +<?php +/** + * OpenTable Block. + * + * @since 8.2 + * + * @package Jetpack + */ + +namespace Jetpack\OpenTable_Block; + +const FEATURE_NAME = 'opentable'; +const BLOCK_NAME = 'jetpack/' . FEATURE_NAME; + +/** + * Check if the block should be available on the site. + * + * @return bool + */ +function is_available() { + if ( + defined( 'IS_WPCOM' ) + && IS_WPCOM + && function_exists( 'has_any_blog_stickers' ) + ) { + if ( has_any_blog_stickers( + array( 'premium-plan', 'business-plan', 'ecommerce-plan' ), + get_current_blog_id() + ) ) { + return true; + } + return false; + } + + return true; +} + +/** + * Registers the block for use in Gutenberg + * This is done via an action so that we can disable + * registration if we need to. + */ +function register_block() { + if ( is_available() ) { + jetpack_register_block( + BLOCK_NAME, + array( 'render_callback' => 'Jetpack\OpenTable_Block\load_assets' ) + ); + } +} +add_action( 'init', 'Jetpack\OpenTable_Block\register_block' ); + +/** + * Set the availability of the block as the editor + * is loaded. + */ +function set_availability() { + if ( is_available() ) { + \Jetpack_Gutenberg::set_extension_available( BLOCK_NAME ); + } else { + \Jetpack_Gutenberg::set_extension_unavailable( + BLOCK_NAME, + 'missing_plan', + array( + 'required_feature' => 'opentable', + 'required_plan' => 'premium-plan', + ) + ); + } +} +add_action( 'jetpack_register_gutenberg_extensions', 'Jetpack\OpenTable_Block\set_availability' ); + +/** + * Adds an inline script which updates the block editor settings to + * add the site locale. This feels sligktly better than calling back + * to the API before registering the block. It also seemed better than + * creating a global + */ +function add_language_setting() { + wp_add_inline_script( 'jetpack-blocks-editor', sprintf( "wp.data.dispatch( 'core/block-editor' ).updateSettings( { siteLocale: '%s' } )", str_replace( '_', '-', get_locale() ) ), 'before' ); +} +add_action( 'enqueue_block_assets', 'Jetpack\OpenTable_Block\add_language_setting' ); + +/** + * OpenTable block registration/dependency declaration. + * + * @param array $attributes Array containing the OpenTable block attributes. + * + * @return string + */ +function load_assets( $attributes ) { + \Jetpack_Gutenberg::load_assets_as_required( FEATURE_NAME ); + + $classes = array( sprintf( 'wp-block-jetpack-%s-theme-%s', FEATURE_NAME, get_attribute( $attributes, 'style' ) ) ); + if ( count( $attributes['rid'] ) > 1 ) { + $classes[] = 'is-multi'; + } + $classes = \Jetpack_Gutenberg::block_classes( + FEATURE_NAME, + $attributes, + $classes + ); + $content = '<div class="' . esc_attr( $classes ) . '">'; + // The OpenTable script uses multiple `rid` paramters, + // so we can't use WordPress to output it, as WordPress attempts to validate it and removes them. + // phpcs:ignore WordPress.WP.EnqueuedResources.NonEnqueuedScript + $content .= '<script type="text/javascript" src="' . esc_url( build_embed_url( $attributes ) ) . '"></script>'; + $content .= '</div>'; + return $content; +} + +/** + * Get the a block attribute + * + * @param array $attributes Array of block attributes. + * @param string $attribute_name The attribute to get. + * + * @return string The filtered attribute + */ +function get_attribute( $attributes, $attribute_name ) { + if ( isset( $attributes[ $attribute_name ] ) ) { + if ( in_array( $attribute_name, array( 'iframe', 'newtab' ), true ) ) { + return $attributes[ $attribute_name ] ? 'true' : 'false'; + } + return $attributes[ $attribute_name ]; + } + + $default_attributes = array( + 'style' => 'standard', + 'iframe' => 'true', + 'domain' => 'com', + 'lang' => 'en-US', + 'newtab' => 'false', + ); + + return isset( $default_attributes[ $attribute_name ] ) ? $default_attributes[ $attribute_name ] : null; +} + +/** + * Get the block type attribute + * + * @param array $attributes Array of block attributes. + * + * @return string The filtered attribute + */ +function get_type_attribute( $attributes ) { + if ( ! empty( $attributes['rid'] ) && count( $attributes['rid'] ) > 1 ) { + return 'multi'; + } + + if ( empty( $attributes['style'] ) || 'button' !== $attributes['style'] ) { + return 'standard'; + } + + return 'button'; +} + +/** + * Get the block theme attribute + * + * OpenTable has a confusing mix of themes and types for the widget. A type + * can have a theme, but the button style can not have a theme. The other two + * types (multi and standard) can have one of the three themes. + * + * We have combined these into a `style` attribute as really there are 4 styles + * standard, wide, tall, and button. Multi can be determined by the number of + * restaurant IDs we have. + * + * This function along with `jetpack_opentable_block_get_type_attribute`, translates + * the style attribute to a type and theme. + * + * Type Theme Style + * ==========|==========|========== + * Multi | | + * Standard | Standard | Standard + * | Wide | Wide + * | Tall | Tall + * Button | Standard | Button + * + * @param array $attributes Array of block attributes. + * + * @return string The filtered attribute + */ +function get_theme_attribute( $attributes ) { + $valid_themes = array( 'standard', 'wide', 'tall' ); + + if ( empty( $attributes['style'] ) + || ! in_array( $attributes['style'], $valid_themes, true ) + || 'button' === $attributes['style'] ) { + return 'standard'; + } + + return $attributes['style']; +} + +/** + * Build an embed URL from an array of block attributes. + * + * @param array $attributes Array of block attributess. + * + * @return string Embed URL + */ +function build_embed_url( $attributes ) { + $url = add_query_arg( + array( + 'type' => get_type_attribute( $attributes ), + 'theme' => get_theme_attribute( $attributes ), + 'iframe' => get_attribute( $attributes, 'iframe' ), + 'domain' => get_attribute( $attributes, 'domain' ), + 'lang' => get_attribute( $attributes, 'lang' ), + 'newtab' => get_attribute( $attributes, 'newtab' ), + ), + '//www.opentable.com/widget/reservation/loader' + ); + + if ( ! empty( $attributes['rid'] ) ) { + foreach ( $attributes['rid'] as $rid ) { + $url .= '&rid=' . $rid; + } + } + + /** + * Filter the OpenTable URL used to embed a widget. + * + * @since 8.2.0 + * + * @param string $url OpenTable embed URL. + * @param array $attributes Array of block attributes. + */ + return apply_filters( 'jetpack_opentable_block_url', $url, $attributes ); +} diff --git a/plugins/jetpack/extensions/blocks/tiled-gallery/tiled-gallery.php b/plugins/jetpack/extensions/blocks/tiled-gallery/tiled-gallery.php index d53feae4..7900e9b6 100644 --- a/plugins/jetpack/extensions/blocks/tiled-gallery/tiled-gallery.php +++ b/plugins/jetpack/extensions/blocks/tiled-gallery/tiled-gallery.php @@ -44,9 +44,11 @@ class Jetpack_Tiled_Gallery_Block { $is_squareish_layout = self::is_squareish_layout( $attr ); - $jetpack_plan = Jetpack_Plan::get(); - - wp_localize_script( 'jetpack-gallery-settings', 'jetpack_plan', array( 'data' => $jetpack_plan['product_slug'] ) ); + // Jetpack_Plan does not exist on WordPress.com. + if ( class_exists( 'Jetpack_Plan' ) ) { + $jetpack_plan = Jetpack_Plan::get(); + wp_localize_script( 'jetpack-gallery-settings', 'jetpack_plan', array( 'data' => $jetpack_plan['product_slug'] ) ); + } if ( preg_match_all( '/<img [^>]+>/', $content, $images ) ) { /** |