craftcms / ckeditor

Edit rich text content in Craft CMS using CKEditor.
https://plugins.craftcms.com/ckeditor
GNU General Public License v3.0
47 stars 25 forks source link

Using previewsInData config property for media embed gets removed by HTML Purifier #80

Closed sm9 closed 1 year ago

sm9 commented 1 year ago

We’re trying to make use of CKEditor's media embed option for YouTube and Vimeo embeds only. For that reason we’re not looking to subscribe to a premium service such as Iframely, as suggested in the CKEditor docs.

Instead, we’ve enabled the previewsInData config option for the editor, which works, but we can’t seem to workaround HTML Purifier removing the code that this option renders instead.

When set to true, previewsInData renders an embed as the following code:

<figure class="media">
    <div data-oembed-url="https://url/">
        <iframe src="https://preview/"></iframe>
    </div>
</figure>

Instead of:

<figure class="media">
    <oembed url="https://url/"></oembed>
</figure>

For info, this is our CKEditor config:

{
  "mediaEmbed": {
    "previewsInData": true
  }
}

And this is our HTML Purifier config:

{
  "Attr.AllowedFrameTargets": [
    "_blank"
  ],
  "Attr.EnableID": true,
  "HTML.AllowedComments": [
    "pagebreak"
  ],
  "HTML.SafeIframe": true,
  "URI.SafeIframeRegexp": "%^(https?:)?//(www.youtube.com/|player.vimeo.com/|vimeo.com/)%"
}

We're using Craft CMS Pro 4.4.7.1 and CKEditor 3.2.0.

We suspect the data-oembed-url is clashing with HTML Purifier, but can’t find any way to enable this attribute. Any advice would be appreciated, thanks.

brandonkelly commented 1 year ago

This has been fixed for the next Craft release (craftcms/cms#13108).

brandonkelly commented 1 year ago

Craft 4.4.8 has been released with that HTML Purifier change.

sm9 commented 1 year ago

Hi Brandon, that's great, thanks for this.

richardegil commented 1 year ago

I am running the latest version of both Craft and the CKEditor plugin and I am still experiencing this issue. All of my settings resemble the above. Any ideas?

i-just commented 1 year ago

@richardegil I’d first double-check that the URLs of the media you’re trying to embed are whitelisted by your URI.SafeIframeRegexp HTML Purifier config. For example, a YouTube video can have (at least) 3 different URLs: https://www.youtube.com/embed/<videoId>, https://www.youtube.com/watch?v=<videoId>, https://youtu.be/<videoId>

richardegil commented 1 year ago

@i-just THANK YOU! That fixed my issue.

kevinmu17 commented 1 year ago

So people can copy paste this

  "HTML.SafeIframe": true,
  "URI.SafeIframeRegexp": "%^(https?:)?//(www\\.youtube\\.com/embed/|www\\.youtube\\.com/watch\\?v=.+|youtu\\.be/.+|www\\.youtube(?:-nocookie)?\\.com/embed/|player\\.vimeo\\.com/video/)%"
jeffaglenn commented 1 year ago

@i-just & @kevinmu17 Thanks for this. I didn't have to escape the periods and I added vimeo.com root.

"URI.SafeIframeRegexp": "%^(https?:)?//(www.youtube.com|www.youtube.com/embed/|www.youtube.com/watch?v=.+|youtu.be/.+|www.youtube(?:-nocookie)?.com/embed/|player.vimeo.com/video/|vimeo.com)%"
wmdhosting commented 8 months ago

I added this to htmlpurfier config. but still video not showing on frontend, do i need to add anythign in twig or any other place.

{ "Attr.AllowedFrameTargets": [ "_blank" ], "Attr.EnableID": true, "HTML.AllowedComments": [ "pagebreak" ], "HTML.SafeIframe": true, "URI.SafeIframeRegexp": "%^(https?:)?//(www.youtube.com|www.youtube.com/embed/|www.youtube.com/watch?v=.+|youtu.be/.+|www.youtube(?:-nocookie)?.com/embed/|player.vimeo.com/video/|vimeo.com)%" }

SETU-WEB commented 8 months ago

Yea I am the same but looking at the Ckeditor docs you need to setup iframely or embedly to output on the front end https://ckeditor.com/docs/ckeditor5/latest/features/media-embed.html#displaying-embedded-media-on-your-website

wmdhosting commented 8 months ago

Did you make it work.. Can anyone confirm this. or there is way to do without this paying external services.. Seems in frist post says it posiblle without premium services..

wmdhosting commented 8 months ago

seems you can.. i tryed with JS::

<script>
document.addEventListener('DOMContentLoaded', function() {
    var oEmbeds = document.querySelectorAll('figure.media oembed');
    oEmbeds.forEach(function(oEmbed) {
        var url = oEmbed.getAttribute('url');
        var videoId = url.split('/').pop().split('?')[0]; // Extract the video ID
        var embedUrl = `https://www.youtube.com/embed/${videoId}`;

        var iframe = document.createElement('iframe');
        iframe.setAttribute('src', embedUrl);
        iframe.setAttribute('frameborder', '0');
        iframe.setAttribute('allow', 'accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture');
        iframe.setAttribute('allowfullscreen', 'true');
        iframe.style.width = '560px'; // Set width as needed
        iframe.style.height = '315px'; // Set height as needed

        var figure = oEmbed.parentElement;
        figure.innerHTML = ''; // Clear the original content
        figure.appendChild(iframe);
    });
});
</script>
wmdhosting commented 8 months ago

Here is updated code..

<script>
document.addEventListener('DOMContentLoaded', function() {
    var oEmbeds = document.querySelectorAll('figure.media oembed');
    oEmbeds.forEach(function(oEmbed) {
        var url = oEmbed.getAttribute('url');
        var videoId = url.split('/').pop().split('?')[0]; // Extract the video ID
        var embedUrl = `https://www.youtube.com/embed/${videoId}`;

        // Create iframe
        var iframe = document.createElement('iframe');
        iframe.setAttribute('src', embedUrl);
        iframe.setAttribute('frameborder', '0');
        iframe.setAttribute('allow', 'accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture');
        iframe.setAttribute('allowfullscreen', 'true');
        iframe.style.width = '100%';  // Ensure iframe fills the container
        iframe.style.height = '100%'; // Ensure iframe fills the container
        iframe.style.position = 'absolute';
        iframe.style.top = '0';
        iframe.style.left = '0';

        // Create a responsive container
        var container = document.createElement('div');
        container.style.position = 'relative';
        container.style.width = '100%'; // Container takes full width of its parent
        container.style.paddingTop = '56.25%'; // Aspect ratio of 16:9
        container.style.height = '0'; // Collapse the container's height, let padding-top give it height

        // Append iframe to container
        container.appendChild(iframe);

        var figure = oEmbed.parentElement;
        figure.innerHTML = ''; // Clear the original content
        figure.appendChild(container); // Add the responsive container with the iframe
    });
});
</script>