summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/jetpack/extensions')
-rw-r--r--plugins/jetpack/extensions/README.md40
-rw-r--r--plugins/jetpack/extensions/blocks/business-hours/business-hours.php132
-rw-r--r--plugins/jetpack/extensions/blocks/contact-info/class-jetpack-contact-info-block.php114
-rw-r--r--plugins/jetpack/extensions/blocks/contact-info/contact-info.php40
-rw-r--r--plugins/jetpack/extensions/blocks/gif/gif.php67
-rw-r--r--plugins/jetpack/extensions/blocks/mailchimp/mailchimp.php136
-rw-r--r--plugins/jetpack/extensions/blocks/map/map.php37
-rw-r--r--plugins/jetpack/extensions/blocks/markdown/markdown.php20
-rw-r--r--plugins/jetpack/extensions/blocks/repeat-visitor/repeat-visitor.php41
-rw-r--r--plugins/jetpack/extensions/blocks/slideshow/slideshow.php35
-rw-r--r--plugins/jetpack/extensions/blocks/tiled-gallery/tiled-gallery.php46
-rw-r--r--plugins/jetpack/extensions/blocks/vr/vr.php10
-rw-r--r--plugins/jetpack/extensions/blocks/wordads/wordads.php121
13 files changed, 839 insertions, 0 deletions
diff --git a/plugins/jetpack/extensions/README.md b/plugins/jetpack/extensions/README.md
new file mode 100644
index 00000000..b284863d
--- /dev/null
+++ b/plugins/jetpack/extensions/README.md
@@ -0,0 +1,40 @@
+# Jetpack Block Editor Extensions
+
+This directory lists extensions for the Block Editor, also known as Gutenberg, [that was introduced in WordPress 5.0](https://wordpress.org/news/2018/12/bebo/).
+
+## Extension Type
+
+We define different types of block editor extensions:
+
+- Blocks are available in the editor itself, and live in the `blocks` directory.
+- Plugins are available in the Jetpack sidebar that appears on the right side of the block editor. Those live in the `plugins` directory.
+
+When adding a new extension, add a new directory for your extension the matching directory.
+
+## Extension Structure
+
+Your extension should follow this structure:
+
+```
+.
+└── blockname/
+ └── blockname.php ← PHP file where the block and its assets are registered.
+```
+
+If your block depends on another block, place them all in extensions folder:
+
+```
+.
+├── blockname/
+└── sub-blockname/
+```
+
+**Note that this directory is still being populated. For now, you can find the blocks [here](https://github.com/Automattic/wp-calypso/tree/master/client/gutenberg/extensions).
+
+## Develop new blocks
+
+You can follow [the instructions here](../docs/guides/gutenberg-blocks.md) to add your own block to Jetpack.
+
+## Block naming conventions
+
+Blocks should use the `jetpack/` prefix, e.g. `jetpack/markdown`.
diff --git a/plugins/jetpack/extensions/blocks/business-hours/business-hours.php b/plugins/jetpack/extensions/blocks/business-hours/business-hours.php
new file mode 100644
index 00000000..2e1af067
--- /dev/null
+++ b/plugins/jetpack/extensions/blocks/business-hours/business-hours.php
@@ -0,0 +1,132 @@
+<?php
+/**
+ * Business Hours Block.
+ *
+ * @since 7.1.0
+ *
+ * @package Jetpack
+ */
+
+jetpack_register_block(
+ 'jetpack/business-hours',
+ array( 'render_callback' => 'jetpack_business_hours_render' )
+);
+
+/**
+ * Get's default days / hours to render a business hour block with no data provided.
+ *
+ * @return array
+ */
+function jetpack_business_hours_get_default_days() {
+ return array(
+ array(
+ 'name' => 'Sun',
+ 'hours' => array(),
+ ),
+ array(
+ 'name' => 'Mon',
+ 'hours' => array(
+ array( 'opening' => '09:00', 'closing' => '17:00' )
+ ),
+ ),
+ array(
+ 'name' => 'Tue',
+ 'hours' => array(
+ array( 'opening' => '09:00', 'closing' => '17:00' )
+ ),
+ ),
+ array(
+ 'name' => 'Wed',
+ 'hours' => array(
+ array( 'opening' => '09:00', 'closing' => '17:00' )
+ ),
+ ),
+ array(
+ 'name' => 'Thu',
+ 'hours' => array(
+ array( 'opening' => '09:00', 'closing' => '17:00' )
+ ),
+ ),
+ array(
+ 'name' => 'Fri',
+ 'hours' => array(
+ array( 'opening' => '09:00', 'closing' => '17:00' )
+ ),
+ ),
+ array(
+ 'name' => 'Sat',
+ 'hours' => array(),
+ ),
+ );
+}
+
+/**
+ * Dynamic rendering of the block.
+ *
+ * @param array $attributes Array containing the business hours block attributes.
+ *
+ * @return string
+ */
+function jetpack_business_hours_render( $attributes ) {
+ global $wp_locale;
+
+ if ( empty( $attributes['days'] ) || ! is_array( $attributes['days'] ) ) {
+ $attributes['days'] = jetpack_business_hours_get_default_days();
+ }
+
+ $start_of_week = (int) get_option( 'start_of_week', 0 );
+ $time_format = get_option( 'time_format' );
+ $content = sprintf(
+ '<dl class="jetpack-business-hours %s">',
+ ! empty( $attributes['className'] ) ? esc_attr( $attributes['className'] ) : ''
+ );
+
+ $days = array( 'Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat' );
+
+ if ( $start_of_week ) {
+ $chunk1 = array_slice( $attributes['days'], 0, $start_of_week );
+ $chunk2 = array_slice( $attributes['days'], $start_of_week );
+ $attributes['days'] = array_merge( $chunk2, $chunk1 );
+ }
+
+ foreach ( $attributes['days'] as $day ) {
+ $content .= '<dt class="' . esc_attr( $day['name'] ) . '">' .
+ ucfirst( $wp_locale->get_weekday( array_search( $day['name'], $days, true ) ) ) .
+ '</dt>';
+ $content .= '<dd class="' . esc_attr( $day['name'] ) . '">';
+ $days_hours = '';
+
+ foreach ( $day['hours'] as $hour ) {
+ $opening = strtotime( $hour['opening'] );
+ $closing = strtotime( $hour['closing'] );
+ if ( ! $opening || ! $closing ) {
+ continue;
+ }
+ $days_hours .= sprintf(
+ /* Translators: Business opening hours info. */
+ _x( 'From %1$s to %2$s', 'from business opening hour to closing hour', 'jetpack' ),
+ date( $time_format, $opening ),
+ date( $time_format, $closing )
+ );
+ $days_hours .= '<br />';
+ }
+
+ if ( empty( $days_hours ) ) {
+ $days_hours = esc_html__( 'Closed', 'jetpack' );
+ }
+ $content .= $days_hours;
+ $content .= '</dd>';
+ }
+
+ $content .= '</dl>';
+
+ /**
+ * Allows folks to filter the HTML content for the Business Hours block
+ *
+ * @since 7.1.0
+ *
+ * @param string $content The default HTML content set by `jetpack_business_hours_render`
+ * @param array $attributes Attributes generated in the block editor for the Business Hours block
+ */
+ return apply_filters( 'jetpack_business_hours_content', $content, $attributes );
+}
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
new file mode 100644
index 00000000..7a34cbbb
--- /dev/null
+++ b/plugins/jetpack/extensions/blocks/contact-info/class-jetpack-contact-info-block.php
@@ -0,0 +1,114 @@
+<?php
+/**
+ * Class Jetpack_Contact_Info_Block
+ *
+ * @package Jetpack
+ */
+
+/**
+ * Helper class that lets us add schema attributes dynamically because they are not something that is store with the content.
+ * Due to the limitations of wp_kses.
+ *
+ * @since 7.1.0
+ */
+class Jetpack_Contact_Info_Block {
+
+ /**
+ * Adds contact info schema attributes.
+ *
+ * @param array $attr Array containing the contact info block attributes.
+ * @param string $content String containing the contact info block content.
+ *
+ * @return string
+ */
+ public static function render( $attr, $content ) {
+ Jetpack_Gutenberg::load_styles_as_required( 'contact-info' );
+ return str_replace(
+ 'class="wp-block-jetpack-contact-info', // Closing " intentionally ommited to that the user can also add the className as expected.
+ 'itemprop="location" itemscope itemtype="http://schema.org/Organization" class="wp-block-jetpack-contact-info',
+ $content
+ );
+ }
+
+ /**
+ * Adds address schema attributes.
+ *
+ * @param array $attr Array containing the address block attributes.
+ * @param string $content String containing the address block content.
+ *
+ * @return string
+ */
+ public static function render_address( $attr, $content ) {
+ // Returns empty content if the only attribute set is linkToGoogleMaps.
+ if ( ! self::has_attributes( $attr, array( 'linkToGoogleMaps', 'className' ) ) ) {
+ return '';
+ }
+ $find = array(
+ 'class="wp-block-jetpack-address"',
+ 'class="jetpack-address__address',
+ // Closing " left out on purpose - there are multiple address fields and they all need to be updated with the same itemprop.
+ 'class="jetpack-address__region"',
+ 'class="jetpack-address__city"',
+ 'class="jetpack-address__postal"',
+ 'class="jetpack-address__country"',
+ );
+ $replace = array(
+ 'itemprop="address" itemscope itemtype="http://schema.org/PostalAddress" class="wp-block-jetpack-address" ',
+ 'itemprop="streetAddress" class="jetpack-address__address', // Closing " left out on purpose.
+ 'itemprop="addressRegion" class="jetpack-address__region"',
+ 'itemprop="addressLocality" class="jetpack-address__city"',
+ 'itemprop="postalCode" class="jetpack-address__postal"',
+ 'itemprop="addressCountry" class="jetpack-address__country"',
+ );
+
+ return str_replace( $find, $replace, $content );
+ }
+
+ /**
+ * Helper function that lets us determine if a block has any valid attributes.
+ *
+ * @param array $attr Array containing the block attributes.
+ * @param array $omit Array containing the block attributes that we ignore.
+ *
+ * @return string
+ */
+ public static function has_attributes( $attr, $omit = array() ) {
+ foreach ( $attr as $attribute => $value ) {
+ if ( ! in_array( $attribute, $omit, true ) && ! empty( $value ) ) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Adds email schema attributes.
+ *
+ * @param array $attr Array containing the email block attributes.
+ * @param string $content String containing the email block content.
+ *
+ * @return string
+ */
+ public static function render_email( $attr, $content ) {
+ $content = self::has_attributes( $attr, array( 'className' ) ) ?
+ str_replace( 'href="mailto:', 'itemprop="email" href="mailto:', $content ) :
+ '';
+ return $content;
+ }
+
+ /**
+ * Adds phone schema attributes.
+ *
+ * @param array $attr Array containing the phone block attributes.
+ * @param string $content String containing the phone block content.
+ *
+ * @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;
+ }
+}
diff --git a/plugins/jetpack/extensions/blocks/contact-info/contact-info.php b/plugins/jetpack/extensions/blocks/contact-info/contact-info.php
new file mode 100644
index 00000000..c7414517
--- /dev/null
+++ b/plugins/jetpack/extensions/blocks/contact-info/contact-info.php
@@ -0,0 +1,40 @@
+<?php
+/**
+ * Contact Info block and its child blocks.
+ *
+ * @since 7.1.0
+ *
+ * @package Jetpack
+ */
+
+jetpack_register_block(
+ 'jetpack/contact-info',
+ array(
+ 'render_callback' => array( 'Jetpack_Contact_Info_Block', 'render' ),
+ )
+);
+
+jetpack_register_block(
+ 'jetpack/address',
+ array(
+ 'parent' => array( 'jetpack/contact-info' ),
+ 'render_callback' => array( 'Jetpack_Contact_Info_Block', 'render_address' ),
+ )
+);
+
+jetpack_register_block(
+ 'jetpack/email',
+ array(
+ 'parent' => array( 'jetpack/contact-info' ),
+ 'render_callback' => array( 'Jetpack_Contact_Info_Block', 'render_email' ),
+ )
+);
+
+jetpack_register_block(
+ 'jetpack/phone',
+ array(
+ 'parent' => array( 'jetpack/contact-info' ),
+ 'render_callback' => array( 'Jetpack_Contact_Info_Block', 'render_phone' ),
+ )
+);
+require_once dirname( __FILE__ ) . '/class-jetpack-contact-info-block.php';
diff --git a/plugins/jetpack/extensions/blocks/gif/gif.php b/plugins/jetpack/extensions/blocks/gif/gif.php
new file mode 100644
index 00000000..cb35f3da
--- /dev/null
+++ b/plugins/jetpack/extensions/blocks/gif/gif.php
@@ -0,0 +1,67 @@
+<?php
+/**
+ * GIF Block.
+ *
+ * @since 7.0.0
+ *
+ * @package Jetpack
+ */
+
+jetpack_register_block(
+ 'jetpack/gif',
+ array(
+ 'render_callback' => 'jetpack_gif_block_render',
+ )
+);
+
+/**
+ * Gif block registration/dependency declaration.
+ *
+ * @param array $attr - Array containing the gif block attributes.
+ *
+ * @return string
+ */
+function jetpack_gif_block_render( $attr ) {
+ $padding_top = isset( $attr['paddingTop'] ) ? $attr['paddingTop'] : 0;
+ $style = 'padding-top:' . $padding_top;
+ $giphy_url = isset( $attr['giphyUrl'] ) ? $attr['giphyUrl'] : null;
+ $search_text = isset( $attr['searchText'] ) ? $attr['searchText'] : '';
+ $caption = isset( $attr['caption'] ) ? $attr['caption'] : null;
+
+ if ( ! $giphy_url ) {
+ return null;
+ }
+
+ /* TODO: replace with centralized block_class function */
+ $align = isset( $attr['align'] ) ? $attr['align'] : 'center';
+ $type = 'gif';
+ $classes = array(
+ 'wp-block-jetpack-' . $type,
+ 'align' . $align,
+ );
+ if ( isset( $attr['className'] ) ) {
+ array_push( $classes, $attr['className'] );
+ }
+ $classes = implode( $classes, ' ' );
+
+ ob_start();
+ ?>
+ <div class="<?php echo esc_attr( $classes ); ?>">
+ <figure>
+ <div class="wp-block-jetpack-gif-wrapper" style="<?php echo esc_attr( $style ); ?>">
+ <iframe src="<?php echo esc_url( $giphy_url ); ?>"
+ title="<?php echo esc_attr( $search_text ); ?>"></iframe>
+ </div>
+ <?php if ( $caption ) : ?>
+ <figcaption
+ class="wp-block-jetpack-gif-caption gallery-caption"><?php echo wp_kses_post( $caption ); ?></figcaption>
+ <?php endif; ?>
+ </figure>
+ </div>
+ <?php
+ $html = ob_get_clean();
+
+ Jetpack_Gutenberg::load_assets_as_required( 'gif' );
+
+ return $html;
+}
diff --git a/plugins/jetpack/extensions/blocks/mailchimp/mailchimp.php b/plugins/jetpack/extensions/blocks/mailchimp/mailchimp.php
new file mode 100644
index 00000000..be93f2f3
--- /dev/null
+++ b/plugins/jetpack/extensions/blocks/mailchimp/mailchimp.php
@@ -0,0 +1,136 @@
+<?php
+/**
+ * Mailchimp Block.
+ *
+ * @since 7.1.0
+ *
+ * @package Jetpack
+ */
+
+if ( ( defined( 'IS_WPCOM' ) && IS_WPCOM ) || Jetpack::is_active() ) {
+ jetpack_register_block(
+ 'jetpack/mailchimp',
+ array(
+ 'render_callback' => 'jetpack_mailchimp_block_load_assets',
+ )
+ );
+}
+
+/**
+ * Mailchimp block registration/dependency declaration.
+ *
+ * @param array $attr - Array containing the map block attributes.
+ *
+ * @return string
+ */
+function jetpack_mailchimp_block_load_assets( $attr ) {
+
+ if ( ! jetpack_mailchimp_verify_connection() ) {
+ return null;
+ }
+ $values = array();
+ $blog_id = ( defined( 'IS_WPCOM' ) && IS_WPCOM ) ?
+ get_current_blog_id() : Jetpack_Options::get_option( 'id' );
+ Jetpack_Gutenberg::load_assets_as_required( 'mailchimp', array( 'wp-polyfill' ) );
+ $defaults = array(
+ 'emailPlaceholder' => esc_html__( 'Enter your email', 'jetpack' ),
+ 'submitButtonText' => esc_html__( 'Join my email list', 'jetpack' ),
+ 'consentText' => esc_html__( 'By clicking submit, you agree to share your email address with the site owner and Mailchimp to receive marketing, updates, and other emails from the site owner. Use the unsubscribe link in those emails to opt out at any time.', 'jetpack' ),
+ 'processingLabel' => esc_html__( 'Processing…', 'jetpack' ),
+ 'successLabel' => esc_html__( 'Success! You\'re on the list.', 'jetpack' ),
+ 'errorLabel' => esc_html__( 'Whoops! There was an error and we couldn\'t process your subscription. Please reload the page and try again.', 'jetpack' ),
+ );
+ foreach ( $defaults as $id => $default ) {
+ $values[ $id ] = isset( $attr[ $id ] ) ? $attr[ $id ] : $default;
+ }
+
+ $values['submitButtonText'] = empty( $values['submitButtonText'] ) ? $defaults['submitButtonText'] : $values['submitButtonText'];
+
+ /* TODO: replace with centralized block_class function */
+ $align = isset( $attr['align'] ) ? $attr['align'] : 'center';
+ $type = 'mailchimp';
+ $classes = array(
+ 'wp-block-jetpack-' . $type,
+ 'align' . $align,
+ );
+ if ( isset( $attr['className'] ) ) {
+ array_push( $classes, $attr['className'] );
+ }
+ $classes = implode( $classes, ' ' );
+
+ $button_styles = array();
+ if ( ! empty( $attr['customBackgroundButtonColor'] ) ) {
+ array_push(
+ $button_styles,
+ sprintf(
+ 'background-color: %s',
+ sanitize_hex_color( $attr['customBackgroundButtonColor'] )
+ )
+ );
+ }
+ if ( ! empty( $attr['customTextButtonColor'] ) ) {
+ array_push(
+ $button_styles,
+ sprintf(
+ 'color: %s',
+ sanitize_hex_color( $attr['customTextButtonColor'] )
+ )
+ );
+ }
+ $button_styles = implode( $button_styles, ';' );
+
+ ob_start();
+ ?>
+ <div class="<?php echo esc_attr( $classes ); ?>" data-blog-id="<?php echo esc_attr( $blog_id ); ?>">
+ <div class="components-placeholder">
+ <form aria-describedby="wp-block-jetpack-mailchimp_consent-text">
+ <p>
+ <input
+ aria-label="<?php echo esc_attr( $values['emailPlaceholder'] ); ?>"
+ placeholder="<?php echo esc_attr( $values['emailPlaceholder'] ); ?>"
+ required
+ title="<?php echo esc_attr( $values['emailPlaceholder'] ); ?>"
+ type="email"
+ />
+ </p>
+ <p>
+ <button type="submit" class="components-button is-button is-primary" style="<?php echo esc_attr( $button_styles ); ?>">
+ <?php echo wp_kses_post( $values['submitButtonText'] ); ?>
+ </button>
+ </p>
+ <p id="wp-block-jetpack-mailchimp_consent-text" name="wp-block-jetpack-mailchimp_consent-text">
+ <?php echo wp_kses_post( $values['consentText'] ); ?>
+ </p>
+ </form>
+ <div class="wp-block-jetpack-mailchimp_notification wp-block-jetpack-mailchimp_processing" role="status">
+ <?php echo esc_html( $values['processingLabel'] ); ?>
+ </div>
+ <div class="wp-block-jetpack-mailchimp_notification wp-block-jetpack-mailchimp_success" role="status">
+ <?php echo esc_html( $values['successLabel'] ); ?>
+ </div>
+ <div class="wp-block-jetpack-mailchimp_notification wp-block-jetpack-mailchimp_error" role="alert">
+ <?php echo esc_html( $values['errorLabel'] ); ?>
+ </div>
+ </div>
+ </div>
+ <?php
+ $html = ob_get_clean();
+ return $html;
+}
+
+/**
+ * Mailchimp connection/list selection verification.
+ *
+ * @return boolean
+ */
+function jetpack_mailchimp_verify_connection() {
+ $option = get_option( 'jetpack_mailchimp' );
+ if ( ! $option ) {
+ return false;
+ }
+ $data = json_decode( $option, true );
+ if ( ! $data ) {
+ return false;
+ }
+ return isset( $data['follower_list_id'], $data['keyring_id'] );
+}
diff --git a/plugins/jetpack/extensions/blocks/map/map.php b/plugins/jetpack/extensions/blocks/map/map.php
new file mode 100644
index 00000000..6e8c9d2a
--- /dev/null
+++ b/plugins/jetpack/extensions/blocks/map/map.php
@@ -0,0 +1,37 @@
+<?php
+/**
+ * Map block.
+ *
+ * @since 6.8.0
+ *
+ * @package Jetpack
+ */
+
+jetpack_register_block(
+ 'jetpack/map',
+ array(
+ 'render_callback' => 'jetpack_map_block_load_assets',
+ )
+);
+
+/**
+ * Map block registration/dependency declaration.
+ *
+ * @param array $attr Array containing the map block attributes.
+ * @param string $content String containing the map block content.
+ *
+ * @return string
+ */
+function jetpack_map_block_load_assets( $attr, $content ) {
+ $dependencies = array(
+ 'lodash',
+ 'wp-element',
+ 'wp-i18n',
+ );
+
+ $api_key = Jetpack_Options::get_option( 'mapbox_api_key' );
+
+ Jetpack_Gutenberg::load_assets_as_required( 'map', $dependencies );
+
+ return preg_replace( '/<div /', '<div data-api-key="' . esc_attr( $api_key ) . '" ', $content, 1 );
+}
diff --git a/plugins/jetpack/extensions/blocks/markdown/markdown.php b/plugins/jetpack/extensions/blocks/markdown/markdown.php
new file mode 100644
index 00000000..7490b9d2
--- /dev/null
+++ b/plugins/jetpack/extensions/blocks/markdown/markdown.php
@@ -0,0 +1,20 @@
+<?php
+/**
+ * Markdown Block.
+ *
+ * @since 6.8.0
+ *
+ * @package Jetpack
+ */
+
+/**
+ * The block depends on the Markdown module to be active for now.
+ * Related discussion: https://github.com/Automattic/jetpack/issues/10294
+ */
+if (
+ ( defined( 'IS_WPCOM' ) && IS_WPCOM )
+ || ( method_exists( 'Jetpack', 'is_module_active' ) && Jetpack::is_module_active( 'markdown' ) )
+) {
+ jetpack_register_block( 'jetpack/markdown' );
+}
+
diff --git a/plugins/jetpack/extensions/blocks/repeat-visitor/repeat-visitor.php b/plugins/jetpack/extensions/blocks/repeat-visitor/repeat-visitor.php
new file mode 100644
index 00000000..11a1a10a
--- /dev/null
+++ b/plugins/jetpack/extensions/blocks/repeat-visitor/repeat-visitor.php
@@ -0,0 +1,41 @@
+<?php
+/**
+ * Repeat Visitor Block
+ *
+ * @since 7.2.0
+ *
+ * @package Jetpack
+ */
+
+jetpack_register_block(
+ 'jetpack/repeat-visitor',
+ array(
+ 'render_callback' => 'jetpack_repeat_visitor_block_render',
+ )
+);
+
+/**
+ * Repeat Visitor block dependency declaration.
+ *
+ * @param array $attributes Array containing the block attributes.
+ * @param string $content String containing the block content.
+ *
+ * @return string
+ */
+function jetpack_repeat_visitor_block_render( $attributes, $content ) {
+ Jetpack_Gutenberg::load_assets_as_required( 'repeat-visitor', array( 'wp-polyfill' ) );
+
+ $count = isset( $_COOKIE['jp-visit-counter'] ) ? intval( $_COOKIE['jp-visit-counter'] ) : 0;
+ $criteria = isset( $attributes['criteria'] ) ? $attributes['criteria'] : 'after-visits';
+ $threshold = isset( $attributes['threshold'] ) ? intval( $attributes['threshold'] ) : 3;
+
+ if (
+ ( 'after-visits' === $criteria && $count >= $threshold ) ||
+ ( 'before-visits' === $criteria && $count < $threshold )
+ ) {
+ return $content;
+ }
+
+ // return an empty div so that view script increments the visit counter in the cookie.
+ return '<div class="wp-block-jetpack-repeat-visitor"></div>';
+}
diff --git a/plugins/jetpack/extensions/blocks/slideshow/slideshow.php b/plugins/jetpack/extensions/blocks/slideshow/slideshow.php
new file mode 100644
index 00000000..4f53ef8f
--- /dev/null
+++ b/plugins/jetpack/extensions/blocks/slideshow/slideshow.php
@@ -0,0 +1,35 @@
+<?php
+/**
+ * Slideshow Block.
+ *
+ * @since 7.1.0
+ *
+ * @package Jetpack
+ */
+
+jetpack_register_block(
+ 'jetpack/slideshow',
+ array(
+ 'render_callback' => 'jetpack_slideshow_block_load_assets',
+ )
+);
+
+/**
+ * Slideshow block registration/dependency declaration.
+ *
+ * @param array $attr Array containing the slideshow block attributes.
+ * @param string $content String containing the slideshow block content.
+ *
+ * @return string
+ */
+function jetpack_slideshow_block_load_assets( $attr, $content ) {
+ $dependencies = array(
+ 'lodash',
+ 'wp-escape-html',
+ 'wp-polyfill',
+ );
+
+ Jetpack_Gutenberg::load_assets_as_required( 'slideshow', $dependencies );
+
+ return $content;
+}
diff --git a/plugins/jetpack/extensions/blocks/tiled-gallery/tiled-gallery.php b/plugins/jetpack/extensions/blocks/tiled-gallery/tiled-gallery.php
new file mode 100644
index 00000000..f8e69ee8
--- /dev/null
+++ b/plugins/jetpack/extensions/blocks/tiled-gallery/tiled-gallery.php
@@ -0,0 +1,46 @@
+<?php
+/**
+ * Tiled Gallery block. Depends on the Photon module.
+ *
+ * @since 6.9.0
+ *
+ * @package Jetpack
+ */
+
+if (
+ ( defined( 'IS_WPCOM' ) && IS_WPCOM ) ||
+ class_exists( 'Jetpack_Photon' ) && Jetpack::is_module_active( 'photon' )
+) {
+ jetpack_register_block(
+ 'jetpack/tiled-gallery',
+ array(
+ 'render_callback' => 'jetpack_tiled_gallery_load_block_assets',
+ )
+ );
+
+ /**
+ * Tiled gallery block registration/dependency declaration.
+ *
+ * @param array $attr Array containing the block attributes.
+ * @param string $content String containing the block content.
+ *
+ * @return string
+ */
+ function jetpack_tiled_gallery_load_block_assets( $attr, $content ) {
+ Jetpack_Gutenberg::load_assets_as_required(
+ 'tiled-gallery',
+ array( 'wp-polyfill' )
+ );
+
+ /**
+ * Filter the output of the Tiled Galleries content.
+ *
+ * @module tiled-gallery
+ *
+ * @since 6.9.0
+ *
+ * @param string $content Tiled Gallery block content.
+ */
+ return apply_filters( 'jetpack_tiled_galleries_block_content', $content );
+ }
+}
diff --git a/plugins/jetpack/extensions/blocks/vr/vr.php b/plugins/jetpack/extensions/blocks/vr/vr.php
new file mode 100644
index 00000000..f6ef70a6
--- /dev/null
+++ b/plugins/jetpack/extensions/blocks/vr/vr.php
@@ -0,0 +1,10 @@
+<?php
+/**
+ * VR Block.
+ *
+ * @since 7.1.0
+ *
+ * @package Jetpack
+ */
+
+jetpack_register_block( 'jetpack/vr' );
diff --git a/plugins/jetpack/extensions/blocks/wordads/wordads.php b/plugins/jetpack/extensions/blocks/wordads/wordads.php
new file mode 100644
index 00000000..6fd39e1b
--- /dev/null
+++ b/plugins/jetpack/extensions/blocks/wordads/wordads.php
@@ -0,0 +1,121 @@
+<?php //phpcs:ignore WordPress.Files.FileName.InvalidClassFileName
+/**
+ * Ads Block.
+ *
+ * @since 7.1.0
+ *
+ * @package Jetpack
+ */
+class Jetpack_WordAds_Gutenblock {
+ const BLOCK_NAME = 'jetpack/wordads';
+
+ /**
+ * Check if site is on WP.com Simple.
+ *
+ * @return bool
+ */
+ private static function is_wpcom() {
+ return defined( 'IS_WPCOM' ) && IS_WPCOM;
+ }
+ /**
+ * Check if the WordAds module is active.
+ *
+ * @return bool
+ */
+ private static function is_jetpack_module_active() {
+ return method_exists( 'Jetpack', 'is_module_active' ) && Jetpack::is_module_active( 'wordads' );
+ }
+
+ /**
+ * Check if the site is approved for ads for WP.com Simple sites.
+ *
+ * @return bool
+ */
+ private static function is_available() {
+ if ( self::is_wpcom() ) {
+ return has_any_blog_stickers( array( 'wordads', 'wordads-approved', 'wordads-approved-misfits' ), get_current_blog_id() );
+ }
+
+ return self::is_jetpack_module_active();
+ }
+
+ /**
+ * Register the WordAds block.
+ */
+ public static function register() {
+ if ( self::is_available() ) {
+ jetpack_register_block(
+ self::BLOCK_NAME,
+ array(
+ 'render_callback' => array( 'Jetpack_WordAds_Gutenblock', 'gutenblock_render' ),
+ )
+ );
+ }
+ }
+
+ /**
+ * Set if the WordAds block is available.
+ */
+ public static function set_availability() {
+ if ( ! self::is_available() ) {
+ Jetpack_Gutenberg::set_extension_unavailable( self::BLOCK_NAME, 'WordAds unavailable' );
+ return;
+ }
+ // Make the block available. Just in case it wasn't registed before.
+ Jetpack_Gutenberg::set_extension_available( self::BLOCK_NAME );
+ }
+
+ /**
+ * Renders the WordAds block.
+ *
+ * @param array $attr Block attributes.
+ *
+ * @return string Block HTML.
+ */
+ public static function gutenblock_render( $attr ) {
+ global $wordads;
+
+ /** This filter is already documented in modules/wordads/wordads.php `insert_ad()` */
+ if ( empty( $wordads ) || is_feed() || apply_filters( 'wordads_inpost_disable', false ) ) {
+ return '';
+ }
+
+ if ( ! empty( $attr['hideMobile'] ) && $wordads->params->is_mobile() ) {
+ return '';
+ }
+
+ if ( ! self::is_wpcom() && $wordads->option( 'wordads_house' ) ) {
+ return $wordads->get_ad( 'inline', 'house' );
+ }
+
+ // section_id is mostly depricated at this point, but it helps us (devs) keep track of which ads end up where
+ // 6 is to keep track of gutenblock ads.
+ $section_id = $wordads->params->blog_id . '6';
+ $align = 'center';
+ if ( isset( $attr['align'] ) && in_array( $attr['align'], array( 'left', 'center', 'right' ), true ) ) {
+ $align = $attr['align'];
+ }
+ $align = 'align' . $align;
+
+ $ad_tag_ids = $wordads->get_ad_tags();
+ $format = 'mrec';
+ if ( isset( $attr['format'] ) && in_array( $attr['format'], array_keys( $ad_tag_ids ), true ) ) {
+ $format = $attr['format'];
+ }
+
+ $height = $ad_tag_ids[ $format ]['height'];
+ $width = $ad_tag_ids[ $format ]['width'];
+ $snippet = $wordads->get_ad_snippet( $section_id, $height, $width, 'inline', $wordads->get_solo_unit_css() );
+ return $wordads->get_ad_div( 'inline', $snippet, array( $align ) );
+ }
+}
+
+add_action(
+ 'init',
+ array( 'Jetpack_WordAds_Gutenblock', 'register' )
+);
+
+add_action(
+ 'jetpack_register_gutenberg_extensions',
+ array( 'Jetpack_WordAds_Gutenblock', 'set_availability' )
+);