Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enable AMP stories to be embedded in posts #1944

Merged
merged 30 commits into from
Mar 13, 2019

Conversation

kienstra
Copy link
Contributor

@kienstra kienstra commented Mar 11, 2019

  • When adding a WordPress (embed) block and entering an AMP story URL, this will display a styled card
  • Uses single card from the Latest Stories block carousel (AMP Latest Stories dynamic block #1916)

Closes #1931

This will display when entering the URL
of the AMP story in the WordPress (embed) block.
This still needs to enqueue, and alter,
the styling from the Latest Stories block.
It should ideally make this styling
usable for both the embed and the block.
…y embed

These style rules maybe be reusable,
so enqueue this stylesheet also.
There's more work needed,
like possibly removing the .latest-stories-carousel
from the selector
Ensure that the new embed template is used.
Also, add some styling specific to that template.
@kienstra
Copy link
Contributor Author

Current Display

amp-story-embed

@kienstra
Copy link
Contributor Author

Styling And Layout

This could probably use styling and layout improvements. Also, it should probably have the sharing link that a post embed has:
amp-story-share

@westonruter
Copy link
Member

I don't think the sharing link is really that necessary. A user can just copy the URL of the link for the same effect, no? Normally a user would actually open the story and then share it from that point, I imagine.

The conflict was trivial,
it was the same edit made in is_amp_endpoint()
in both branches.
@kienstra
Copy link
Contributor Author

kienstra commented Mar 11, 2019

I don't think the sharing link is really that necessary.

Sure, sounds good. Like you mentioned, someone could just copy the link.

Change the way this simulates being
on an amp_story embed endpoint.
As Pascal mentioned,
this can replace the need to set 3 properties
of $wp_query.
*/
public static function enqueue_embed_styling() {
if ( is_embed() && is_singular( self::POST_TYPE_SLUG ) ) {
wp_enqueue_style(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This style is being registered in two places. Can it be registered in one and then enqueued in two?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good idea.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

a9b5fdb registers the CSS file on the wp_loaded hook. Embed endpoints don't seem to fire wp_enqueue_scripts.

It's not ideal, but wp_loaded was the best hook I could find that works for the embed and the Latest Stories block.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@kienstra you can use the wp_default_scripts action instead, perhaps including the CSS registration in this function:

/**
* Register default scripts for AMP components.
*
* @param WP_Scripts $wp_scripts Scripts.
*/
function amp_register_default_scripts( $wp_scripts ) {
/*
* Polyfill dependencies that are registered in Gutenberg and WordPress 5.0.
* Note that Gutenberg will override these at wp_enqueue_scripts if it is active.
*/
$handles = array( 'wp-i18n', 'wp-dom-ready' );
foreach ( $handles as $handle ) {
if ( ! isset( $wp_scripts->registered[ $handle ] ) ) {
$wp_scripts->add( $handle, amp_get_asset_url( sprintf( 'js/%s-compiled.js', $handle ) ) );
}
}
// AMP Runtime.
$handle = 'amp-runtime';
$wp_scripts->add(
$handle,
'https://cdn.ampproject.org/v0.js',
array(),
null
);
$wp_scripts->add_data(
$handle,
'amp_script_attributes',
array(
'async' => true,
)
);
// Shadow AMP API.
$handle = 'amp-shadow';
$wp_scripts->add(
$handle,
'https://cdn.ampproject.org/shadow-v0.js',
array(),
null
);
$wp_scripts->add_data(
$handle,
'amp_script_attributes',
array(
'async' => true,
)
);
// Get all AMP components as defined in the spec.
$extensions = array();
foreach ( AMP_Allowed_Tags_Generated::get_allowed_tag( 'script' ) as $script_spec ) {
if ( isset( $script_spec[ AMP_Rule_Spec::TAG_SPEC ]['extension_spec']['name'], $script_spec[ AMP_Rule_Spec::TAG_SPEC ]['extension_spec']['version'] ) ) {
$versions = $script_spec[ AMP_Rule_Spec::TAG_SPEC ]['extension_spec']['version'];
array_pop( $versions );
$extensions[ $script_spec[ AMP_Rule_Spec::TAG_SPEC ]['extension_spec']['name'] ] = array_pop( $versions );
}
}
foreach ( $extensions as $extension => $version ) {
$src = sprintf(
'https://cdn.ampproject.org/v0/%s-%s.js',
$extension,
$version
);
$wp_scripts->add(
$extension,
$src,
array( 'amp-runtime' ),
null
);
}
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, @westonruter! 2101161 moves the wp_register_style() call into amp_register_default_scripts().

A unit test failed in PHP 5.4 and 5.6 with WP 4.9,,
but passed in the other versions.
@westonruter
Copy link
Member

westonruter commented Mar 11, 2019

There are AMP validation errors related to the embed which I believe are out of scope of the changes here, but the link is showing up unexpectedly in the post editor:

image

The post embed looks good in the non-AMP page:

image

But it doesn't look right in AMP:

image

Again, this is largely due to poor post embed handling in WordPress: #809.

One idea to help deal with this is to add a render_block filter (or override/supply the render_callback) to then check if the URL being embedded is (1) on this site and (2) an AMP story post. If that is the case, then you could override the output to be the_single_story_card for that story post and avoid the iframe altogether. This would fix rendering of the post embed in AMP as well as eliminate the (known) validation errors.

Manually set $GLOBALS['wp_query']->is_singular
to true.
Still, this isn't an ideal solution.
As Weston mentioned, this will be tree-shaken
on Latest Stories blocks,
so it's not a problem including it in the stylesheet.
Thanks to Weston for the snippet.
As he mentioned, the file will exist.
So there's no need for a file_exists() check.
Before, it was fully enqueued in 2 places.
But as Weston mentioned,
it's better to register it,
then enqueue it by only passing its slug.
Use the 'wp_loaded' hook to register
This isn't ideal, but on the embed endpoint,
wp_enqueue_scripts doesn't run.
It looks like AMP_Story_Post_Type::register()
exits if register_block_type() does not exist
and that doesn't look to exist in 4.9.
So mark the test skipped.
As Weston mentioned,
the CSS registration seems to go there.
It's registered outside of the class that
handles AMP stories,
so describe what the stylesheet is.
Apply Weston's suggestion to override the render_callbck()
for the embed block.
It returns the card with at <div> wrapper,
not an entire <iframe>.
@kienstra
Copy link
Contributor Author

Embed Display In AMP

Hi @westonruter,
Thanks for your suggestion for overriding the embed block's render_callback for AMP stories.

6a81d3c does that, and an AMP story embed now looks better in AMP:

amp-story-embed-amp

And there aren't any more validation errors in the block editor.

If it wasn't registered,
it's not possible to create test posts
of that post type.
@kienstra
Copy link
Contributor Author

Ah, I still need to ensure that the embed URL is from the same site.

As Weston mentioned,
this is a necessary part of overriding the embed callback.
This is mainly taken from url_to_postid().
@kienstra
Copy link
Contributor Author

Embedding Doesn't Look To Work With Plain Permalinks

Embedding an AMP Story URL with the Wordpress (embed) block doesn't look to work when permalinks are set to 'Plain.'

But this is the case even with another custom post type.

Steps To Reproduce

  1. Add this to a plugin (mainly taken from this document):
/**
 * Registers a 'testimonials' post type.
 */
add_action( 'init', function() {
	register_post_type(
		'testimonials',
		array(
			'public' => true,
			'label'  => 'Testimonials',
		)
	 );
} );
  1. Go to Settings > Permalinks, and select 'Plain':

permalinks-plain

  1. Create a new 'Testimonial' post, with any title and content. Publish it, and copy its permalink:

new-testimonial-post

  1. Create a new post (with the post type of 'post')

  2. Add a WordPress (embed) block

  3. Enter the URL from the 'Testimonial' post in step 3.

  4. Expected: ideally, it should embed

  5. Actual: it displays 'Sorry, we could not embed that content'

embedding-did-not-work

Before, this function simply ran on its own.
But it really need to be in an assertion.
override_story_embed_callback() probably needed
more explanation, so add to the DocBlock.
@kienstra
Copy link
Contributor Author

kienstra commented Mar 12, 2019

Steps To Test

  1. Create a new post (of the post type 'post')
  2. Add a WordPress (embed) block:

wordpress-embed-block

  1. Enter the permalink of an AMP Story post that has a featured image, and click 'Embed'

  2. Expected: the AMP Story is embedded:

embedded-amp-story

  1. Expected: the front-end display:

amp-story-embed-front-end

@swissspidy
Copy link
Collaborator

swissspidy commented Mar 12, 2019

Should/Does this also support embedding from another site in the same network?

@westonruter
Copy link
Member

Should/Does this also support embedding from another site in the same network?

That would be nice but it wouldn't be supported presently as currently implemented via:

$post = get_post( get_page_by_path( $path, OBJECT, self::POST_TYPE_SLUG ) );

Thinking about this some more, I wonder if overriding the render_block filter is actually the best approach. Perhaps actually what is needed is to override the actual HTML that is returned for oEmbed response (the embed_html filter and/or oembed_response_data filter)? If we did this, perhaps we could eliminate the need for the oEmbed iframe altogether? If the oEmbed response html just contained the contents of the_single_story_card() then that would be very cool. I am dubious, however, due to the styling required.

@westonruter
Copy link
Member

I am dubious, however, due to the styling required.

Yeah, getting rid of the iframe entirely won't work due to wp_filter_oembed_result() and how it does:

	$allowed_html = array(
		'a'          => array(
			'href' => true,
		),
		'blockquote' => array(),
		'iframe'     => array(
			'src'          => true,
			'width'        => true,
			'height'       => true,
			'frameborder'  => true,
			'marginwidth'  => true,
			'marginheight' => true,
			'scrolling'    => true,
			'title'        => true,
		),
	);

	$html = wp_kses( $result, $allowed_html );

@westonruter
Copy link
Member

I added a few @todo comments.

@kienstra
Copy link
Contributor Author

Thanks, @westonruter! I'll carry out the @todo comments if you'd like.

@westonruter
Copy link
Member

Yep, go for it.

Something I'm surprised by actually with the WordPress embed is that there is no auto-complete for the story URL. If I create a link, I can just start typing the title of the post to get it suggested. But there is no similar facility for the WordPress Embed. This is out of scope for this PR, however. It's really something that should be supported by WordPress itself. So I've opened WordPress/gutenberg#14393

As Weston mentioned in code review,
these make more sense there
than in AMP_Editor_Blocks.
There's already a check with:
if ( $url_host !== $home_url_host ) {
So remove the extra check that didn't
take account of HTTP and HTTPS.
It looks like the style wasn't registered
in WP 4.9, because the register() method exited.
So call register_styles().
As Weston mentioned,
this breaks the method into separate methods.
Also, it keeps the STORY_CARD_CSS_SLUG registeration
on a 'wp_default_styles' callback.

It would have been good to run register_scripts()
on the 'wp_default_scripts' hook.
But get_current_screen() is null,
and that's needed to verify that it's
an AMP Story post type.
@kienstra
Copy link
Contributor Author

@todos applied

Hi @westonruter,
Thanks for reviewing this again, and good idea to open WordPress/gutenberg#14393.

The @todos that you added here are now applied.

kienstra and others added 2 commits March 12, 2019 14:05
The main register() file will exit
in WP 4.9,
so there's little need to test the render_callback.
@@ -222,6 +240,7 @@ public static function enqueue_block_editor_assets() {
return;
}

// @todo Move the following styles to register_styles method, and scripts to a new register_scripts method which runs at wp_default_scripts?
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What I meant here was that potentially there could be methods that run at wp_default_scripts and wp_default_styles which then do $scripts->add() and $styles->add() respectively. That would then leave the existing enqueue_block_editor_assets method to just wp_enqueue_script($handle) and wp_enqueue_style($handle).

But this is not really necessary for any scripts and styles that are enqueued in multiple places.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, that makes sense. Sorry for missing that.

It looks like you renamed some methods in 80b3a7e. Let me know if you'd like me to apply your suggestion of calling $scripts->add() and $styles->add at wp_default_scripts and wp_default_styles

return $pre_render;
}

$path = str_replace( home_url( self::REWRITE_SLUG . '/' ), '', $url );
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This still doesn't seem very robust. What if the $url is an HTTP one, but the site is in HTTPS? Since you've already verified above that the host is the same, it seems this should rather do something like:

Suggested change
$path = str_replace( home_url( self::REWRITE_SLUG . '/' ), '', $url );
$embed_url_path = wp_parse_url( $url, PHP_URL_PATH );
$base_url_path = wp_parse_url( trailingslashit( home_url( self::REWRITE_SLUG ) ), PHP_URL_PATH );
if ( 0 !== strpos( $embed_url_path, $base_url_path ) ) {
return $pre_render;
}
$path = substr( $embed_url_path, strlen( $base_url_path ) );

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, that's a good point. I committed your code verbatim.

westonruter and others added 3 commits March 12, 2019 15:16
As Weston mentioned, this is possible,
and this would need to override the embed output.
It looks like there's another test that failed,
and it may have been from $_SERVER['HTTPS'] being 'on'.
@westonruter westonruter merged commit 427c3d7 into amp-stories-redux Mar 13, 2019
@westonruter westonruter deleted the add/amp-stories-embed branch March 13, 2019 01:05
@westonruter westonruter added this to the v1.2 milestone May 21, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
cla: yes Signed the Google CLA
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants