-
Notifications
You must be signed in to change notification settings - Fork 4.2k
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
Enhance the embed block to support all WP_oEmbed embeds #816
Conversation
Because this supports so many different embeds (currently 53 supported by WP_oEmbed) we should think about how to present this to the user. Perhaps we have help on this block, or other blocks that extend it somehow... not sure! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great job on this PR. I know it's still WIP but I left some notes that I hope can help.
blocks/api/serializer.js
Outdated
* @param {String} saveContent Content to save into the post | ||
* @return {String} The post content | ||
*/ | ||
export function serializeBlock( blockType, blockAttributes, blockSettings, saveContent ) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
instead of taking all these arguments, should we replace those with a block object instead?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I had this on a list on clean-ups to do, glad you think it should be done too 👍
blocks/library/embed/index.js
Outdated
</figure> | ||
); | ||
render() { | ||
const { oembed_html, error, fetching } = this.state; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should the oembed_html
be camelCased?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ugh, my Python is showing! Will fix :)
blocks/library/embed/index.js
Outdated
{ wp.i18n.__( 'Embed' ) } | ||
</Button> | ||
</Placeholder> | ||
doServerSideRender( event ) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could we extract this ServerSideRender to a HoC? Maybe something like:
<ServerSideBlock blockType={ blockType } arguments={ arguments }>
{ ( html, error ) => (
<Sandbox html={ html } />
) }
</ServerSideBlock>
Any blocker to achieve this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The only blocker is I need to read more React docs to figure out how to do it. There were async issues with having Sandbox as the containing block, this seems like a much better way, I've just never done it before.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe this post could help https://medium.com/merrickchristensen/function-as-child-components-5f3920a9ace9
index.php
Outdated
return $oembed->get_html( $content['url'] ); | ||
} | ||
|
||
register_block( 'core/oembed', 'gutenberg_block_core_oembed' ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Aside: This index.php is getting bigger and bigger, we may want to split it soon.
Starting to look at this a bit, and I'm having some dumb questions, please forgive me. Can the "Generic Embed" block convert itself to being a different block once a URL is entered? Take for example this tweet: https://twitter.com/sydneysuks/status/757616209814654976 — if you embed that it gets cropped, presumably because we do that responsive video trick to resize youtube videos. It'd be nice if it could convert from We might even separate out into two groups. One group which is videos, these all get the "prevent click overlay" and the responsive size stuff, and the other group are simply freeform "unknown size" embeds, for which the block resizes itself into. |
On the cropping issue, this is one of the things I wanted you to look at :)
The sandbox iframe resizes, but there's something going on with the
iframe-overlay (or maybe the blocks-embed figure) that keeps the containing
elements a fixed size. But if I remove the iframe-overlay class, the
caption editable overlaps the embedded content... so it needs your expert
eye!
…On Wed, May 17, 2017 at 10:35 AM, Joen Asmussen ***@***.***> wrote:
Starting to look at this a bit, and I'm having some dumb questions, please
forgive me.
Can the "Generic Embed" block *convert itself* to being a different block
once a URL is entered? Take for example this tweet: https://twitter.com/
sydneysuks/status/757616209814654976 — if you embed that it gets cropped,
presumably because we do that responsive video trick to resize youtube
videos. It'd be nice if it could convert from core/oembed to either
core/oembed/twitter or core/twitter or something like that, so we could
provide separate styles for each embed.
We might even separate out into two groups. One group which is videos,
these all get the "prevent click overlay" and the responsive size stuff,
and the other group are simply freeform "unknown size" embeds, for which
the block resizes itself into.
—
You are receiving this because you authored the thread.
Reply to this email directly, view it on GitHub
<#816 (comment)>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/ADKSsj2cgwJlcnaWq1pRGfHsU8tmN7XGks5r6r9RgaJpZM4NdjZi>
.
|
Ah I understand now! Yes, will take a look. I suspect that we are doing the responsive video stuff (which does this cropping) right now to all embeds, and we should be doing that only to video embeds. |
So I noticed that the embedded html that comes back for youtube has stuff
in it to set size. We surround that iframe with a resizable iframe so that
it should work seamlessly, as long as the surrounding elements aren't
enforcing size too.
(At least, that's as much as I can figure out, possibly wrong...)
…On Wed, May 17, 2017 at 10:42 AM, Joen Asmussen ***@***.***> wrote:
Ah I understand now! Yes, will take a look. I suspect that we are doing
the responsive video stuff (which does this cropping) right now to *all*
embeds, and we should be doing that only to *video* embeds.
—
You are receiving this because you authored the thread.
Reply to this email directly, view it on GitHub
<#816 (comment)>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/ADKSsjZB1vUQLAQb7fWMK2aSlB5Ca9i1ks5r6sERgaJpZM4NdjZi>
.
|
Okay I've had a look, nice work! It seems like what we are doing with the video overlay (to prevent accidental play clicks) as well as the responsive video trick may be a bit too clever for what is essentially embeds. It's still what we should do, but it's probably a little more work. The thing is — right now the markup we output is the same whether we're embedding a youtube video or a tweet:
The above is kind of perfect for videos. It means we can decide on a 16:9 aspect ration using CSS, and then allow the video inside to scale to 100% width and height of the container, so it's a responsive video. It also allows us the "click preventer" suggested in #483. All good. The problem is, there's no way to scope the necessary CSS to apply only to video embeds, because the embeds inside are more or less unknown to us. If we were able to have the following markup, I could write CSS that would work for both videos and anything else. For video embeds:
Note the For anything else:
I don't know if this is possible or not. Perhaps @iseulde might know more? |
So what we could do is have a list of video domains where we apply the
click preventer? We already have to check the domain the url is on to
display the "Sorry, can't preview this" for facebook stuff.
…On Wed, May 17, 2017 at 11:50 AM, Joen Asmussen ***@***.***> wrote:
Okay I've had a look, nice work!
It seems like what we are doing with the video overlay (to prevent
accidental play clicks) as well as the responsive video trick may be a bit
too clever for what is essentially embeds. It's still what we should do,
but it's probably a little more work.
The thing is — right now the markup we output is the same whether we're
embedding a youtube video or a tweet:
<div class="editor-visual-editor__block" data-type="core/embed" tabindex="0">
<div>
<figure class="blocks-embed">
<div class="iframe-overlay">
<iframe title="" seamless="" scrolling="no" sandbox="allow-same-origin allow-scripts" width="525" height="508"></iframe>
</div>
</figure>
</div>
</div>
The above is kind of perfect for videos. It means we can decide on a 16:9
aspect ration using CSS, and then allow the video inside to scale to 100%
width and height of the container, so it's a responsive video. It also
allows us the "click preventer" suggested in #483
<#483>. All good.
The problem is, there's no way to scope the necessary CSS to apply only to
video embeds, because the embeds inside are more or less unknown to us.
If we were able to have the following markup, I could write CSS that would
work for both videos and *anything else*.
For video embeds:
<div class="editor-visual-editor__block" data-type="core/embed/video" tabindex="0">
<div>
<figure class="blocks-embed">
<iframe title="" seamless="" scrolling="no" sandbox="allow-same-origin allow-scripts" width="525" height="508"></iframe>
</figure>
</div>
</div>
*Note the core/embed/video.*
For anything else:
<div class="editor-visual-editor__block" data-type="core/embed" tabindex="0">
<div>
<figure class="blocks-embed">
<iframe title="" seamless="" scrolling="no" sandbox="allow-same-origin allow-scripts" width="525" height="508"></iframe>
</figure>
</div>
</div>
I don't know if this is possible or not. Perhaps @iseulde
<https://github.com/iseulde> might know more?
—
You are receiving this because you authored the thread.
Reply to this email directly, view it on GitHub
<#816 (comment)>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/ADKSsvdzOCpYeDj45yOI3AE_DrPGT14Gks5r6tD0gaJpZM4NdjZi>
.
|
That seems like it could work. A whitelist of domains that get the responsive treatment and click preventer, anything else gets the fallback style. Can it result in a CSS class being applied to the block, or a data attribute? I just need to be able to distinguish in CSS whether it's one or the other. Also, if you can get me this list of 54 supported, I volunteer to narrow down which ones are video, and get them back to you in an array format of your choosing. |
Once we determine if it's video or not, we can change the markup in any way
we like!
The full list of embeds is at
https://github.com/WordPress/WordPress/blob/master/wp-includes/class-oembed.php#L139
in a nice readable table :)
…On Wed, May 17, 2017 at 11:55 AM, Joen Asmussen ***@***.***> wrote:
So what we could do is have a list of video domains where we apply the
click preventer? We already have to check the domain the url is on to
display the "Sorry, can't preview this" for facebook stuff.
That seems like it could work. A whitelist of domains that get the
responsive treatment and click preventer, anything else gets the fallback
style.
Can it result in a CSS class being applied to the block, or a data
attribute? I just need to be able to distinguish in CSS whether it's one or
the other.
Also, if you can get me this list of 54 supported, I volunteer to narrow
down which ones are video, and get them back to you in an array format of
your choosing.
—
You are receiving this because you authored the thread.
Reply to this email directly, view it on GitHub
<#816 (comment)>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/ADKSsqlOYHD6xzx8xmducnOpZfcIhu3hks5r6tI7gaJpZM4NdjZi>
.
|
Here's a tentative list of embeds I think we should treat as "Video" embeds:
Let me know if you need it in another format. |
Just tested the tumblr post in the other branch (the one that doesn't use and just has an enclosing diff) and it does weird things, I think because there are height limitations on the block? Tumblr tries to resize for the height of the div, and progressively gets thinner and thinner until you can't see anything... in this branch, it's the correct width, but clipped heightwise.Anyway, I guess this is why it's WIP :) |
blocks/library/embed/index.js
Outdated
this.noPreview = [ | ||
'facebook.com', | ||
]; | ||
this.videoEmbeds = [ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The oEmbed response will indicate the type
that the media is, e.g. video
. So maybe this isn't necessary?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, it'd be good to not have to handle this here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's fantastic, thank you for letting me know about this! I'll work on changing this over.
blocks/library/embed/index.js
Outdated
} ); | ||
form.append( 'content', oembed_block ); | ||
this.setState( { error: false, fetching: true } ); | ||
fetch( api_url, { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is an oEmbed Proxy endpoint now in core which should be used, I believe. See https://core.trac.wordpress.org/ticket/40450
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For me it fails to embed because this is not the proper path. Needs path to the WP site. https://developer.wordpress.org/reference/functions/rest_url/
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great! I hardcoded the api url here because I didn't know how to discover it. I see that function is how to do it in PHP, is there a JS version too?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You'll need the URL and the nonce alike. They can be exported from PHP to JS as was done for Media: https://github.com/WordPress/wordpress-develop/blob/255bd917f2bc3c0d2b324ebbc979ab4147631c06/src/wp-includes/media.php#L3421-L3431
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure how to access that from JS... do you have an example of the JS side you could link me to?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You don't have to get a new one. In WordPress, a nonce is not strictly a number-used-once. It is more like a CSRF token that will expire after a day. You can presume that the WP-API should be responsible for keeping wpApiSettings.nonce
updated, though I don't think it is currently.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@notnownikki Alternatively, if you need other PHP settings, you can use https://developer.wordpress.org/reference/functions/wp_localize_script/ in WordPress.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was thinking we could use the proxy endpoint as well but I like the idea of having a more generic server side rendering endpoint (like the one implemented in the PR)
Edit: I guess we're interested in the video
flag returned by the proxy endpoint.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm having a few problems implementing this...
If I fetch the URL resulting from
wpApiSettings.root + 'oembed/1.0/proxy?url=' + encodeUriComponent( url ) + '&_wpnonce=' + wpApiSettings.nonce
...then I get back "Cookie nonce is invalid". (I've tried to set the none in a header too, but same error.)
However, if I visit the URL in a browser, I don't get a nonce error, but I do get a 404, the route isn't found.
Can you point me in the right direction? I've been through the REST API Handbook, and it all looks like it should work...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are you running WordPress 4.8-beta2? This is a new endpoint in 4.8.
components/resizable-iframe/index.js
Outdated
<iframe | ||
ref={ ( node ) => this.iframe = node } | ||
title={ this.props.title } | ||
seamless="seamless" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there currently any support for it? If not, is it worth adding here?
After #849 which added a bunch of tests for block parsing and serialization, this PR should probably be rebased against |
70a67f8
to
bb2f738
Compare
I rebased this against master, and simplified things. We had a lot of rendering issues with the resizing iframe, so after some discussions we decided to just use a div to hold the embedded html, and tackle the iframe rendering issues in the future. The rendering has been simplified, to use the oembed proxy API included in WP4.8. |
Also, previewing facebook embeds is disabled, because fb scripts want to change global things about the document they're running in (which the sandboxing iframe highlighted and wouldn't allow). |
I've just seen this doesn't render properly loading from the saved post. Off to fix that now. |
....aaaaand fixed. |
Tweets work really well! Wow! Pushed some tweaks to the placeholder, and I'll be looking at videos in a minute. |
I pushed a little change so the videos are now responsive again. I know there are some sandboxing challenges worth exploring in a separate PR, but right now, in this branch, embeds are working 🌟🌟🌟🌟🌟! In other words, 👍 on user experience. |
<form onSubmit={ this.doServerSideRender }> | ||
<input | ||
type="url" | ||
className="components-placeholder__input" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why are we using components-placeholder
instead of placeholder
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This was an @aduth request, if I recall correctly.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, we are using components as prefix for the whole folder. This is so specific to blocks, though. Mmm.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we are to adhere to the proposed guideline, this should be blocks-
prefix, not components-
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If it's truly generic to be considered a component, it should be moved to the components
directory to reflect as such.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, I agree this is not generic enough and should be blocks-
Added "html-embed" component which embeds html from the ombed proxy inside a div and makes sure any script elements get run. This initial version uses a div instead of a sandbox iframe due to some tricky rendering issues with reactive embdedded content from sites like tumblr.
This makes videos responsive again.
- Use spinner component - Adjust some parenthesis syntax
901ffc2
to
ea6ff87
Compare
blocks/library/embed/index.js
Outdated
</Button> | ||
</Placeholder> | ||
componentWillUnmount() { | ||
// can't about the fetch promise, so let it know we will unmount |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Typo abort
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Awesome Job. Let's get this merged 🚢
const { url } = this.props.attributes; | ||
const api_url = wpApiSettings.root + 'oembed/1.0/proxy?url=' + encodeURIComponent( url ) + '&_wpnonce=' + wpApiSettings.nonce; | ||
|
||
this.setState( { error: false, fetching: true } ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When loading the page on master
now, I see the following warning from this line:
Warning: setState(...): Can only update a mounted or mounting component. This usually means you called setState() on an unmounted component. This is a no-op
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, so this needs to be moved into componentWillMount
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe. I'd have to look again at where this function is called originally, and if by componentDidMount
, why the component would not be mounted at this point.
To a more general point, I think this is a handy reference for when side effects and state changes should occur during component lifecycle:
https://gist.github.com/bvaughn/923dffb2cd9504ee440791fade8db5f9
); | ||
} | ||
|
||
const domain = url.split( '/' )[ 2 ].replace( /^www\./, '' ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This feels a bit fragile, since it could easily throw errors if an unexpected url
reaches it (one without at least two slashes). We've already started using Node's url
module elsewhere, which can perform much more reliable parsing, specifically via url.parse
. Do we need the host
here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeah, the host without www. because we want to blacklist certain embeds from previewing in the editor (facebook specifically). I'll look at the url module :)
} | ||
|
||
const domain = url.split( '/' )[ 2 ].replace( /^www\./, '' ); | ||
const cannotPreview = this.noPreview.includes( domain ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Array#includes
is not polyfilled (yet) and this will error in IE11.
It should help to refactor the code to make use of withAPIData. Though embed endpoint uses transients to cache, |
Implements server side rendering for the Embed block so that it supports all urls processed by WP_oEmbedAdds a REST API endpoint so we can post a serialized block and get back the rendered HTML.Adds a server side block, 'core/oembed', that exists to render URLs with WP_oEmbed.Extracts individual block serialization in the serializer, so that we can serialize individual blocks for server side rendering.Adds awareness of domains we cannot preview content for in the editor, due to javascript issues.
Adds a
SandboxHtmlEmbed component that will put rendered HTML intoan iframea div, and make sure all script blocks get run.It uses the ResizableIframe from Calypso to make sure all rendered content is shown.URLs for testing:
https://twitter.com/mrbiffo/status/864555311918714880 : short tweet, iframe should resize.
http://doctorwho.tumblr.com/post/160705586507/onaperduamedee-to-really-feel-it-you-need-the : tall tumblr post, iframe should resize to show it all.
https://www.facebook.com/DoctorWho/videos/1854859861194699/ : facebook video, should show the url as a hyperlink with a message saying we're sorry that we cannot preview that content in the editor.
http://example.com/ : not supported by oEmbed, should display an error saying that we cannot embed that content.