diff --git a/.eslintrc.json b/.eslintrc.json index 19f53ee97f58d..7864b2732645c 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -18,7 +18,8 @@ } }, "globals": { - "wp": true + "wp": true, + "wpApiSettings": true }, "plugins": [ "react", diff --git a/blocks/library/embed/index.js b/blocks/library/embed/index.js index 53f1a49c5de1b..25659caaecf83 100644 --- a/blocks/library/embed/index.js +++ b/blocks/library/embed/index.js @@ -1,7 +1,7 @@ /** * WordPress dependencies */ -import { Button, Placeholder } from 'components'; +import { Button, Placeholder, HtmlEmbed, Spinner } from 'components'; /** * Internal dependencies @@ -34,7 +34,6 @@ registerBlockType( 'core/embed', { category: 'embed', attributes: { - url: attr( 'iframe', 'src' ), title: attr( 'iframe', 'title' ), caption: children( 'figcaption' ), }, @@ -73,52 +72,130 @@ registerBlockType( 'core/embed', { } }, - edit( { attributes, setAttributes, focus, setFocus } ) { - const { url, title, caption } = attributes; + edit: class extends wp.element.Component { + constructor() { + super( ...arguments ); + this.doServerSideRender = this.doServerSideRender.bind( this ); + this.state = { + html: '', + type: '', + error: false, + fetching: false, + }; + this.noPreview = [ + 'facebook.com', + ]; + if ( this.props.attributes.url ) { + // if the url is already there, we're loading a saved block, so we need to render + this.doServerSideRender(); + } + } - if ( ! url ) { - return ( - - - - { wp.i18n.__( 'Embed' ) } - - + componentWillUnmount() { + // can't abort the fetch promise, so let it know we will unmount + this.unmounting = true; + } + + doServerSideRender( event ) { + if ( event ) { + event.preventDefault(); + } + 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 } ); + fetch( api_url, { + credentials: 'include', + } ).then( + ( response ) => { + if ( this.unmounting ) { + return; + } + response.json().then( ( obj ) => { + const { html, type } = obj; + if ( html ) { + this.setState( { html, type } ); + } else { + this.setState( { error: true } ); + } + this.setState( { fetching: false } ); + } ); + } ); } - return ( - - - - - { ( caption && caption.length > 0 ) || !! focus ? ( - setAttributes( { caption: value } ) } - inline - inlineToolbar - /> - ) : null } - - ); + render() { + const { html, type, error, fetching } = this.state; + const { url, caption } = this.props.attributes; + const { setAttributes, focus, setFocus } = this.props; + + if ( ! html ) { + return ( + + + setAttributes( { url: event.target.value } ) } /> + { ! fetching + ? + { wp.i18n.__( 'Embed' ) } + + : + } + { error && { wp.i18n.__( 'Sorry, we could not embed that content.' ) } } + + + ); + } + + const domain = url.split( '/' )[ 2 ].replace( /^www\./, '' ); + const cannotPreview = this.noPreview.includes( domain ); + let typeClassName = 'blocks-embed'; + + if ( 'video' === type ) { + typeClassName = 'blocks-embed-video'; + } + + return ( + + { ( cannotPreview ) ? ( + + { url } + { wp.i18n.__( 'Previews for this are unavailable in the editor, sorry!' ) } + + ) : ( + + ) } + { ( caption && caption.length > 0 ) || !! focus ? ( + setAttributes( { caption: value } ) } + inline + inlineToolbar + /> + ) : null } + + ); + } }, save( { attributes } ) { - const { url, title, caption } = attributes; - const iframe = ; - + const { url, caption } = attributes; if ( ! caption || ! caption.length ) { - return iframe; + return url; } return ( - { iframe } + { url } { caption } ); diff --git a/blocks/library/embed/style.scss b/blocks/library/embed/style.scss index 3b4a11df46927..7abbf2de16590 100644 --- a/blocks/library/embed/style.scss +++ b/blocks/library/embed/style.scss @@ -1,15 +1,16 @@ -.blocks-embed { +.blocks-embed, +.blocks-embed-video { margin: 0; } -.blocks-embed .iframe-overlay { +.blocks-embed-video > div:first-child { position: relative; width: 100%; height: 0; padding-bottom: 56.25%; /* 16:9 */ } -.blocks-embed iframe { +.blocks-embed-video > div > iframe { position: absolute; top: 0; left: 0; @@ -55,6 +56,5 @@ div[data-type="core/embed"] { margin-left: auto; margin-right: auto; } - } } diff --git a/blocks/test/fixtures/core-embed-youtube-caption.html b/blocks/test/fixtures/core-embed-youtube-caption.html index 3e6f600b254d6..784220eab0a20 100644 --- a/blocks/test/fixtures/core-embed-youtube-caption.html +++ b/blocks/test/fixtures/core-embed-youtube-caption.html @@ -1,3 +1,3 @@ -State of the Word 2016 - +https://www.youtube.com/embed/Nl6U7UotA-M"State of the Word 2016 + \ No newline at end of file diff --git a/blocks/test/fixtures/core-embed-youtube-caption.json b/blocks/test/fixtures/core-embed-youtube-caption.json index 81689a7432fd0..104b854351cf9 100644 --- a/blocks/test/fixtures/core-embed-youtube-caption.json +++ b/blocks/test/fixtures/core-embed-youtube-caption.json @@ -3,7 +3,7 @@ "uid": "_uid_0", "name": "core/embed", "attributes": { - "url": "//www.youtube.com/embed/Nl6U7UotA-M", + "url": "https://www.youtube.com/watch?v=Nl6U7UotA-M", "caption": [ "State of the Word 2016" ] diff --git a/blocks/test/fixtures/core-embed-youtube-caption.serialized.html b/blocks/test/fixtures/core-embed-youtube-caption.serialized.html index bde2a8c300104..f75c6cc292c51 100644 --- a/blocks/test/fixtures/core-embed-youtube-caption.serialized.html +++ b/blocks/test/fixtures/core-embed-youtube-caption.serialized.html @@ -1,5 +1,5 @@ - - + +https://www.youtube.com/watch?v=Nl6U7UotA-M State of the Word 2016 diff --git a/components/html-embed/index.js b/components/html-embed/index.js new file mode 100644 index 0000000000000..c4e113f13e54d --- /dev/null +++ b/components/html-embed/index.js @@ -0,0 +1,33 @@ +// When embedding HTML from the WP oEmbed proxy, we need to insert it +// into a div and make sure any scripts get run. This component takes +// HTML and puts it into a div element, and creates and adds new script +// elements so all scripts get run as expected. + +export default class HtmlEmbed extends wp.element.Component { + + componentDidMount() { + const body = this.node; + const { html = '' } = this.props; + + body.innerHTML = html; + + const scripts = body.getElementsByTagName( 'script' ); + const newScripts = Array.from( scripts ).map( ( script ) => { + const newScript = document.createElement( 'script' ); + if ( script.src ) { + newScript.src = script.src; + } else { + newScript.innerHTML = script.innerHTML; + } + return newScript; + } ); + + newScripts.forEach( ( script ) => body.appendChild( script ) ); + } + + render() { + return ( + this.node = node } /> + ); + } +} diff --git a/components/index.js b/components/index.js index 50ca974a39f10..1582340f6b380 100644 --- a/components/index.js +++ b/components/index.js @@ -1,6 +1,7 @@ export { default as Button } from './button'; export { default as Dashicon } from './dashicon'; export { default as FormToggle } from './form-toggle'; +export { default as HtmlEmbed } from './html-embed'; export { default as IconButton } from './icon-button'; export { default as Panel } from './panel'; export { default as PanelHeader } from './panel/header'; diff --git a/components/placeholder/style.scss b/components/placeholder/style.scss index d28fea95f215c..cb1f1aee0fcb8 100644 --- a/components/placeholder/style.scss +++ b/components/placeholder/style.scss @@ -22,7 +22,8 @@ } } -.components-placeholder__fieldset { +.components-placeholder__fieldset, +.components-placeholder__fieldset form { display: flex; flex-direction: row; justify-content: center; diff --git a/post-content.js b/post-content.js index 7313647c435d3..185280888e2c5 100644 --- a/post-content.js +++ b/post-content.js @@ -41,7 +41,7 @@ window._wpGutenbergPost = { '', '', - 'State of the Word 2016', + 'https://www.youtube.com/watch?v=Nl6U7UotA-MState of the Word 2016', '', '',
{ wp.i18n.__( 'Sorry, we could not embed that content.' ) }
{ url }
{ wp.i18n.__( 'Previews for this are unavailable in the editor, sorry!' ) }