-
Notifications
You must be signed in to change notification settings - Fork 798
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Implement SmartFrame embedding (#21101)
- Loading branch information
Showing
7 changed files
with
350 additions
and
0 deletions.
There are no files selected for viewing
4 changes: 4 additions & 0 deletions
4
projects/plugins/jetpack/changelog/add-smartframe-embed-support
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
Significance: minor | ||
Type: enhancement | ||
|
||
SmartFrame Embeds: add support SmartFrame embed using URLs, embed code, and shortcodes |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,3 +4,4 @@ | |
import './facebook'; | ||
import './instagram'; | ||
import './loom'; | ||
import './smartframe'; |
24 changes: 24 additions & 0 deletions
24
projects/plugins/jetpack/extensions/extended-blocks/core-embed/smartframe.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
/** | ||
* WordPress dependencies | ||
*/ | ||
import { registerBlockVariation } from '@wordpress/blocks'; | ||
import { __ } from '@wordpress/i18n'; | ||
|
||
/** | ||
* Internal dependencies | ||
*/ | ||
import { SmartFrameIcon } from '../../shared/icons'; | ||
/* | ||
* New `core/embed` block variation. | ||
*/ | ||
|
||
const coreEmbedVariationGetty = { | ||
name: 'smartframe', | ||
title: 'SmartFrame', | ||
icon: SmartFrameIcon, | ||
keywords: [ __( 'smartframe', 'jetpack' ) ], | ||
description: __( 'Embed a SmartFrame Image.', 'jetpack' ), | ||
patterns: [ /^https?:\/\/(.*?).smartframe.(io|net)\/.*/i ], | ||
attributes: { providerNameSlug: 'smartframe', responsive: true }, | ||
}; | ||
registerBlockVariation( 'core/embed', coreEmbedVariationGetty ); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
114 changes: 114 additions & 0 deletions
114
projects/plugins/jetpack/modules/shortcodes/smartframe.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,114 @@ | ||
<?php | ||
/** | ||
* Smartframe.io embed | ||
* | ||
* Example URL: https://mikael-korpela.smartframe.io/p/mantymetsa_1630927773870/7673dc41a775fb845cc26acf24f1fe4?t=rql1c6dbpv2 | ||
* Example embed code: <script src="https://embed.smartframe.io/6ae67829d1264ee0ea6071a788940eae.js" data-image-id="mantymetsa_1630927773870" data-width="100%" data-max-width="1412px"></script> | ||
* | ||
* @package automattic/jetpack | ||
*/ | ||
|
||
if ( defined( 'IS_WPCOM' ) && IS_WPCOM ) { | ||
add_action( 'init', 'jetpack_smartframe_enable_embeds' ); | ||
} else { | ||
jetpack_smartframe_enable_embeds(); | ||
} | ||
|
||
/** | ||
* Register smartframe as oembed provider. Add filter to reverse iframes to shortcode. Register [smartframe] shortcode. | ||
* | ||
* @since 10.2.0 | ||
*/ | ||
function jetpack_smartframe_enable_embeds() { | ||
// Support their oEmbed Endpoint. | ||
wp_oembed_add_provider( '#https?://(.*?)\.smartframe\.(io|net)/.*#i', 'https://oembed.smartframe.io/', true ); | ||
|
||
// Allow script to be filtered to short code (so direct copy+paste can be done). | ||
add_filter( 'pre_kses', 'jetpack_shortcodereverse_smartframe' ); | ||
|
||
// Actually display the smartframe Embed. | ||
add_shortcode( 'smartframe', 'jetpack_smartframe_shortcode' ); | ||
} | ||
|
||
/** | ||
* Compose shortcode based on smartframe iframes. | ||
* | ||
* @since 10.2.0 | ||
* | ||
* @param string $content Post content. | ||
* | ||
* @return mixed | ||
*/ | ||
function jetpack_shortcodereverse_smartframe( $content ) { | ||
if ( ! is_string( $content ) || false === stripos( $content, 'embed.smartframe' ) ) { | ||
return $content; | ||
} | ||
|
||
// phpcs:ignore WordPress.WP.EnqueuedResources.NonEnqueuedScript | ||
$regexp = '!<script\ssrc="https://embed\.smartframe\.(?:io|net)/(\w+)\.js"\sdata-image-id="(.*?)"(?:\sdata-width="(?:\d+(?:%|px))"\s)?(?:data-max-width="(\d+(%|px)))?"></script>!i'; | ||
$regexp_ent = str_replace( '&#0*58;', '&#0*58;|�*58;', htmlspecialchars( $regexp, ENT_NOQUOTES ) ); | ||
|
||
foreach ( compact( 'regexp', 'regexp_ent' ) as $regexp ) { | ||
if ( ! preg_match_all( $regexp, $content, $matches, PREG_SET_ORDER ) ) { | ||
continue; | ||
} | ||
|
||
foreach ( $matches as $match ) { | ||
// We need at least a script ID and an image ID. | ||
if ( ! isset( $match[1], $match[2] ) ) { | ||
continue; | ||
} | ||
$shortcode = sprintf( | ||
'[smartframe script-id="%1$s" image-id="%2$s"%3$s]', | ||
esc_attr( $match[1] ), | ||
esc_attr( $match[2] ), | ||
! empty( $match[3] ) ? ' max-width="' . esc_attr( $match[3] ) . '"' : '' | ||
); | ||
$content = str_replace( $match[0], $shortcode, $content ); | ||
} | ||
} | ||
/** This action is documented in modules/widgets/social-media-icons.php */ | ||
do_action( 'jetpack_bump_stats_extras', 'html_to_shortcode', 'smartframe' ); | ||
|
||
return $content; | ||
} | ||
|
||
/** | ||
* Parse shortcode arguments and render its output. | ||
* | ||
* @since 10.2.0 | ||
* | ||
* @param array $atts Shortcode parameters. | ||
* | ||
* @return string | ||
*/ | ||
function jetpack_smartframe_shortcode( $atts ) { | ||
if ( ! empty( $atts['image-id'] ) ) { | ||
$image_id = $atts['image-id']; | ||
} else { | ||
return '<!-- Missing smartframe image-id -->'; | ||
} | ||
if ( ! empty( $atts['script-id'] ) ) { | ||
$script_id = $atts['script-id']; | ||
} else { | ||
return '<!-- Missing smartframe script-id -->'; | ||
} | ||
|
||
$params = array( | ||
// ignore width for now, smartframe embed code has it "100%". % isn't allowed in oembed, making it 100px. | ||
// 'width' => isset( $atts['width'] ) ? (int) $atts['width'] : null,. | ||
'max-width' => isset( $atts['max-width'] ) ? (int) $atts['max-width'] : null, | ||
); | ||
|
||
$embed_url = sprintf( | ||
'https://imagecards.smartframe.io/%1$s/%2$s', | ||
esc_attr( $script_id ), | ||
esc_attr( $image_id ) | ||
); | ||
|
||
// wrap the embed with wp-block-embed__wrapper, otherwise it would be aligned to the very left of the viewport. | ||
return sprintf( | ||
'<div class="wp-block-embed__wrapper">%1$s</div>', | ||
wp_oembed_get( $embed_url, array_filter( $params ) ) | ||
); | ||
} |
77 changes: 77 additions & 0 deletions
77
.../plugins/jetpack/tests/php/modules/shortcodes/test-class.smartframe-HttpRequestCache.json
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
{ | ||
"https://oembed.smartframe.io/?maxwidth=500&maxheight=750&url=https%3A%2F%2Fimagecards.smartframe.io%2F6ae67829d1264ee0ea6071a788940eae%2Fmantymetsa_1630927773870&dnt=1&caller=example.org&format=json": [ | ||
{ | ||
"args": { | ||
"body": null, | ||
"method": "GET" | ||
}, | ||
"response": { | ||
"body": "{\"type\":\"rich\",\"version\":\"1.0\",\"html\":\"<script data-image-id='mantymetsa_1630927773870' src='https://embed.smartframe.io/6ae67829d1264ee0ea6071a788940eae.js' data-width='500' data-height='354\"></script>\",\"width\":500,\"height\":354,\"thumbnail_url\":\"https://thumbs.smartframe.io/6ae67829d1264ee0ea6071a788940eae/d9cb5f3e6787557531b960586f59388d/mantymetsa.jpg\",\"thumbnail_width\":400,\"thumbnail_height\":283,\"author_name\":\"\"}", | ||
"response": { | ||
"code": 200, | ||
"message": "OK" | ||
} | ||
} | ||
} | ||
], | ||
"https://oembed.smartframe.io/?maxwidth=640&maxheight=960&url=https%3A%2F%2Fimagecards.smartframe.io%2F6ae67829d1264ee0ea6071a788940eae%2Fmantymetsa_1630927773870&dnt=1&caller=example.org": [ | ||
{ | ||
"args": { | ||
"body": null, | ||
"method": "GET" | ||
}, | ||
"response": { | ||
"body": "{\"type\":\"rich\",\"version\":\"1.0\",\"html\":\"<script data-image-id='mantymetsa_1630927773870' src='https://embed.smartframe.io/6ae67829d1264ee0ea6071a788940eae.js' data-width='640' data-height='453'></script>\",\"width\":640,\"height\":453,\"thumbnail_url\":\"https://thumbs.smartframe.io/6ae67829d1264ee0ea6071a788940eae/d9cb5f3e6787557531b960586f59388d/mantymetsa.jpg\",\"thumbnail_width\":400,\"thumbnail_height\":283,\"author_name\":\"\"}", | ||
"response": { | ||
"code": 200, | ||
"message": "OK" | ||
} | ||
} | ||
} | ||
], | ||
"https://oembed.smartframe.io/?maxwidth=640&maxheight=960&url=https%3A%2F%2Fimagecards.smartframe.io%2F6ae67829d1264ee0ea6071a788940eae%2Fmantymetsa_1630927773870&dnt=1&caller=example.org&format=json": [ | ||
{ | ||
"args": { | ||
"body": null, | ||
"method": "GET" | ||
}, | ||
"response": { | ||
"body": "{\"type\":\"rich\",\"version\":\"1.0\",\"html\":\"<script data-image-id='mantymetsa_1630927773870' src='https://embed.smartframe.io/6ae67829d1264ee0ea6071a788940eae.js' data-width='640' data-height='453'></script>\",\"width\":640,\"height\":453,\"thumbnail_url\":\"https://thumbs.smartframe.io/6ae67829d1264ee0ea6071a788940eae/d9cb5f3e6787557531b960586f59388d/mantymetsa.jpg\",\"thumbnail_width\":400,\"thumbnail_height\":283,\"author_name\":\"\"}", | ||
"response": { | ||
"code": 200, | ||
"message": "OK" | ||
} | ||
} | ||
} | ||
], | ||
"https://oembed.smartframe.io/?maxwidth=640&maxheight=960&url=https%3A%2F%2Fimagecards.smartframe.io%2F6ae67829d1264ee0ea6071a788940eae%2Fmantymetsa_1630927773870&dnt=1&format=json": [ | ||
{ | ||
"args": { | ||
"body": null, | ||
"method": "GET" | ||
}, | ||
"response": { | ||
"body": "{\"type\":\"rich\",\"version\":\"1.0\",\"html\":\"<script data-image-id='mantymetsa_1630927773870' src='https://embed.smartframe.io/6ae67829d1264ee0ea6071a788940eae.js' data-width='640' data-height='453'></script>\",\"width\":640,\"height\":453,\"thumbnail_url\":\"https://thumbs.smartframe.io/6ae67829d1264ee0ea6071a788940eae/d9cb5f3e6787557531b960586f59388d/mantymetsa.jpg\",\"thumbnail_width\":400,\"thumbnail_height\":283,\"author_name\":\"\"}", | ||
"response": { | ||
"code": 200, | ||
"message": "OK" | ||
} | ||
} | ||
} | ||
], | ||
"https://oembed.smartframe.io/?maxwidth=640&maxheight=960&url=https%3A%2F%2Fimagecards.smartframe.io%2F6ae67829d1264ee0ea6071a788940eae%2Fmantymetsa_1630927773870&dnt=1F 2 / 2 (100%)omarhttps://oembed.smartframe.io/?maxwidth=640&maxheight=960&url=https%3A%2F%2Fimagecards.smartframe.io%2F6ae67829d1264ee0ea6071a788940eae%2Fmantymetsa_1630927773870&dnt=1": [ | ||
{ | ||
"args": { | ||
"body": null, | ||
"method": "GET" | ||
}, | ||
"response": { | ||
"body": "{\"type\":\"rich\",\"version\":\"1.0\",\"html\":\"<script data-image-id='mantymetsa_1630927773870' src='https://embed.smartframe.io/6ae67829d1264ee0ea6071a788940eae.js' data-width='640' data-height='453'></script>\",\"width\":640,\"height\":453,\"thumbnail_url\":\"https://thumbs.smartframe.io/6ae67829d1264ee0ea6071a788940eae/d9cb5f3e6787557531b960586f59388d/mantymetsa.jpg\",\"thumbnail_width\":400,\"thumbnail_height\":283,\"author_name\":\"\"}", | ||
"response": { | ||
"code": 200, | ||
"message": "OK" | ||
} | ||
} | ||
} | ||
] | ||
} |
119 changes: 119 additions & 0 deletions
119
projects/plugins/jetpack/tests/php/modules/shortcodes/test-class.smartframe.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,119 @@ | ||
<?php | ||
/** | ||
* Unit tests for smartframe embedding | ||
* | ||
* Tests smartframe shortcodes and embed code | ||
* | ||
* @package automattic/jetpack | ||
*/ | ||
|
||
/** | ||
* Shortcodes need external HTML requests to be converted to valid embed code (using smartframe's oembed endpoint) | ||
*/ | ||
require_once __DIR__ . '/trait.http-request-cache.php'; | ||
|
||
/** | ||
* Implements unit tests for smartframe embedding | ||
* | ||
* @covers ::shortcode_smartframe | ||
*/ | ||
class WP_Test_Jetpack_Shortcodes_SmartFrame extends WP_UnitTestCase { | ||
use Automattic\Jetpack\Tests\HttpRequestCacheTrait; | ||
|
||
const SMARTFRAME_IDENTIFIER = 'mantymetsa_1630927773870'; | ||
const SMARTFRAME_SCRIPT_ID = '6ae67829d1264ee0ea6071a788940eae'; | ||
|
||
const SMARTFRAME_SHORTCODE = '[smartframe script-id="6ae67829d1264ee0ea6071a788940eae" image-id="mantymetsa_1630927773870" max-width="1412px"]'; | ||
// phpcs:ignore WordPress.WP.EnqueuedResources.NonEnqueuedScript | ||
const SMARTFRAME_EMBED = '<script src="https://embed.smartframe.io/6ae67829d1264ee0ea6071a788940eae.js" data-image-id="mantymetsa_1630927773870" data-width="100%" data-max-width="1412px"></script>'; | ||
|
||
/** | ||
* Check for external HTTP requests and register filter | ||
*/ | ||
public function setUp() { | ||
parent::setUp(); | ||
|
||
if ( in_array( 'external-http', $this->getGroups(), true ) ) { | ||
// Used by WordPress.com - does nothing in Jetpack. | ||
add_filter( 'tests_allow_http_request', '__return_true' ); | ||
} else { | ||
/* | ||
* We normally make an HTTP request to SmartFrame's oEmbed endpoint to generate | ||
* the shortcode output. | ||
* This filter bypasses that HTTP request for these tests | ||
*/ | ||
add_filter( 'pre_oembed_result', array( $this, 'smartframe_oembed_response' ), 10, 3 ); | ||
} | ||
} | ||
|
||
/** | ||
* Mocks matching HTML for an embedded smartframe item | ||
* | ||
* @param string $html Post content. | ||
* @param string $url found URL. | ||
* | ||
* @since 10.2.0 | ||
*/ | ||
public function smartframe_oembed_response( $html, $url ) { | ||
if ( 0 !== strpos( $url, 'smartframe.io' ) ) { | ||
return $html; | ||
} | ||
return self::SMARTFRAME_EMBED; | ||
} | ||
|
||
/** | ||
* Verify that [smartframe] exists. | ||
* | ||
* @since 10.2.0 | ||
*/ | ||
public function test_shortcodes_smartframe_exists() { | ||
$this->assertEquals( shortcode_exists( 'smartframe' ), true ); | ||
} | ||
|
||
/** | ||
* See if the shortcode is converted to valid embedding code | ||
* | ||
* @group external-http | ||
* | ||
* @since 10.2.0 | ||
*/ | ||
public function test_smartframe_shortcode() { | ||
$parsed = do_shortcode( self::SMARTFRAME_SHORTCODE ); | ||
|
||
$doc = new DOMDocument(); | ||
$doc->loadHTML( $parsed ); | ||
$links = $doc->getElementsByTagName( 'script' ); | ||
|
||
foreach ( $links as $link ) { | ||
$this->assertTrue( $link->hasAttribute( 'data-image-id' ) ); | ||
$this->assertContains( self::SMARTFRAME_IDENTIFIER, $link->getAttribute( 'data-image-id' ) ); | ||
} | ||
} | ||
|
||
/** | ||
* Verify that embedding code is reversed into a valid shortcode | ||
* | ||
* @since 10.2.0 | ||
*/ | ||
public function test_smartframe_reverse_shortcode() { | ||
$shortcode = jetpack_shortcodereverse_smartframe( self::SMARTFRAME_EMBED ); | ||
$this->assertEquals( self::SMARTFRAME_SHORTCODE, $shortcode ); | ||
} | ||
|
||
/** | ||
* Uses a real HTTP request to SmartFrame's oEmbed endpoint to | ||
* verify that rendering the shortcode returns a SmartFrame image. | ||
* | ||
* @group external-http | ||
* | ||
* @since 10.2.0 | ||
*/ | ||
public function test_shortcodes_smartframe_image_via_oembed_http_request() { | ||
$image_id = self::SMARTFRAME_IDENTIFIER; | ||
$script_id = self::SMARTFRAME_SCRIPT_ID; | ||
$content = "[smartframe script-id='$script_id' image-id='$image_id']"; | ||
$shortcode_content = do_shortcode( $content ); | ||
|
||
$this->assertContains( $image_id, $shortcode_content ); | ||
} | ||
} |