FIX: Display Instagram Oneboxes in an iframe (#14789)

We are no longer able to display the image returned by Instagram directly within a Discourse site (either in the composer, or within a cooked post within a topic), so:

- Display an image placeholder in the composer preview
- A cooked post should use an iframe to display the Instagram 'embed' content
This commit is contained in:
jbrw 2021-11-02 14:34:51 -04:00 committed by GitHub
parent 8d73730c44
commit aec125b617
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 66 additions and 44 deletions

View File

@ -866,6 +866,14 @@ aside.onebox.xkcd .onebox-body img {
justify-content: center;
align-items: center;
&.image {
&:before {
opacity: 0.8;
content: svg-uri(
'<svg width="128px" height="128px" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="grey" d="M464 448H48c-26.51 0-48-21.49-48-48V112c0-26.51 21.49-48 48-48h416c26.51 0 48 21.49 48 48v288c0 26.51-21.49 48-48 48zM112 120c-30.928 0-56 25.072-56 56s25.072 56 56 56 56-25.072 56-56-25.072-56-56-56zM64 384h384V272l-87.515-87.515c-4.686-4.686-12.284-4.686-16.971 0L208 320l-55.515-55.515c-4.686-4.686-12.284-4.686-16.971 0L64 336v48z"></path></svg>'
);
}
}
&.video {
&:before {
opacity: 0.8;

View File

@ -1633,7 +1633,7 @@ security:
allow_any: false
choices: "['*'] + Onebox::Engine.all_iframe_origins"
allowed_iframes:
default: "https://www.google.com/maps/embed?|https://www.openstreetmap.org/export/embed.html?|https://calendar.google.com/calendar/embed?|https://codepen.io/"
default: "https://www.google.com/maps/embed?|https://www.openstreetmap.org/export/embed.html?|https://calendar.google.com/calendar/embed?|https://codepen.io/|https://www.instagram.com"
type: list
list_type: simple
client: true

View File

@ -9,22 +9,41 @@ module Onebox
matches_regexp(/^https?:\/\/(?:www\.)?(?:instagram\.com|instagr\.am)\/?(?:.*)\/(?:p|tv)\/[a-zA-Z\d_-]+/)
always_https
requires_iframe_origins "https://www.instagram.com"
def clean_url
url.scan(/^https?:\/\/(?:www\.)?(?:instagram\.com|instagr\.am)\/?(?:.*)\/(?:p|tv)\/[a-zA-Z\d_-]+/).flatten.first
end
def data
oembed = get_oembed
raise "No oEmbed data found. Ensure 'facebook_app_access_token' is valid" if oembed.data.empty?
@data ||= begin
oembed = get_oembed
raise "No oEmbed data found. Ensure 'facebook_app_access_token' is valid" if oembed.data.empty?
{
link: clean_url.gsub("/#{oembed.author_name}/", "/"),
title: "@#{oembed.author_name}",
image: oembed.thumbnail_url,
description: Onebox::Helpers.truncate(oembed.title, 250),
}
{
link: clean_url.gsub("/#{oembed.author_name}/", "/") + '/embed',
title: "@#{oembed.author_name}",
image: oembed.thumbnail_url,
image_width: oembed.data[:thumbnail_width],
image_height: oembed.data[:thumbnail_height],
description: Onebox::Helpers.truncate(oembed.title, 250),
}
end
end
def placeholder_html
::Onebox::Helpers.image_placeholder_html
end
def to_html
<<-HTML
<iframe
src="#{data[:link]}"
width="#{data[:image_width]}"
height="#{data[:image_height].to_i + 98}"
frameborder="0"
></iframe>
HTML
end
protected

View File

@ -234,6 +234,10 @@ module Onebox
Addressable::URI.unencode(url)
end
def self.image_placeholder_html
"<div class='onebox-placeholder-container'><span class='placeholder-icon image'></span></div>"
end
def self.video_placeholder_html
"<div class='onebox-placeholder-container'><span class='placeholder-icon video'></span></div>"
end

View File

@ -308,7 +308,7 @@ describe Oneboxer do
end
end
context 'facebook_app_access_token' do
context 'instagram' do
it 'providing a token should attempt to use new endpoint' do
url = "https://www.instagram.com/p/CHLkBERAiLa"
access_token = 'abc123'
@ -318,7 +318,7 @@ describe Oneboxer do
stub_request(:head, url)
stub_request(:get, "https://graph.facebook.com/v9.0/instagram_oembed?url=#{url}&access_token=#{access_token}").to_return(body: response("instagram_new"))
expect(Oneboxer.preview(url, invalidate_oneboxes: true)).not_to include('instagram-description')
expect(Oneboxer.preview(url, invalidate_oneboxes: true)).to include('placeholder-icon image')
end
it 'unconfigured token should attempt to use old endpoint' do
@ -326,7 +326,15 @@ describe Oneboxer do
stub_request(:head, url)
stub_request(:get, "https://api.instagram.com/oembed/?url=#{url}").to_return(body: response("instagram_old"))
expect(Oneboxer.preview(url, invalidate_oneboxes: true)).to include('instagram-description')
expect(Oneboxer.preview(url, invalidate_oneboxes: true)).to include('placeholder-icon image')
end
it 'renders result using an iframe' do
url = "https://www.instagram.com/p/CHLkBERAiLa"
stub_request(:head, url)
stub_request(:get, "https://api.instagram.com/oembed/?url=#{url}").to_return(body: response("instagram_old"))
expect(Oneboxer.onebox(url, invalidate_oneboxes: true)).to include('iframe')
end
end

File diff suppressed because one or more lines are too long

View File

@ -5,22 +5,23 @@ require "rails_helper"
describe Onebox::Engine::InstagramOnebox do
let(:access_token) { 'abc123' }
let(:link) { "https://www.instagram.com/p/CARbvuYDm3Q" }
let(:onebox_options) { { allowed_iframe_regexes: Onebox::Engine.origins_to_regexes(["https://www.instagram.com"]) } }
it 'oneboxes links that include the username' do
link_with_profile = 'https://www.instagram.com/bennyblood24/p/CARbvuYDm3Q/'
onebox_klass = Onebox::Matcher.new(link_with_profile).oneboxed
onebox_klass = Onebox::Matcher.new(link_with_profile, onebox_options).oneboxed
expect(onebox_klass.name).to eq(described_class.name)
end
it 'oneboxes photo links' do
photo_link = 'https://www.instagram.com/p/CARbvuYDm3Q/'
onebox_klass = Onebox::Matcher.new(photo_link).oneboxed
onebox_klass = Onebox::Matcher.new(photo_link, onebox_options).oneboxed
expect(onebox_klass.name).to eq(described_class.name)
end
it 'oneboxes tv links' do
tv_link = "https://www.instagram.com/tv/CIlM7UzMgXO/?hl=en"
onebox_klass = Onebox::Matcher.new(tv_link).oneboxed
onebox_klass = Onebox::Matcher.new(tv_link, onebox_options).oneboxed
expect(onebox_klass.name).to eq(described_class.name)
end
@ -28,6 +29,7 @@ describe Onebox::Engine::InstagramOnebox do
let(:api_link) { "https://graph.facebook.com/v9.0/instagram_oembed?url=#{link}&access_token=#{access_token}" }
before do
stub_request(:head, link)
stub_request(:get, api_link).to_return(status: 200, body: onebox_response("instagram"))
stub_request(:get, "https://api.instagram.com/oembed/?url=https://www.instagram.com/p/CARbvuYDm3Q")
.to_return(status: 200, body: onebox_response("instagram"))
@ -39,18 +41,15 @@ describe Onebox::Engine::InstagramOnebox do
Onebox.options = @previous_options
end
it "includes title" do
onebox = described_class.new(link)
html = onebox.to_html
expect(html).to include('<a href="https://www.instagram.com/p/CARbvuYDm3Q" target="_blank" rel="noopener">@natgeo</a>')
it "renders preview with a placeholder" do
expect(Oneboxer.preview(link, invalidate_oneboxes: true)).to include('placeholder-icon image')
end
it "includes image" do
it "renders html using an iframe" do
onebox = described_class.new(link)
html = onebox.to_html
expect(html).to include("https://scontent.cdninstagram.com/v/t51.2885-15/sh0.08/e35/s640x640/97565241_163250548553285_9172168193050746487_n.jpg")
expect(html).to include('<iframe')
end
end
@ -59,7 +58,8 @@ describe Onebox::Engine::InstagramOnebox do
let(:html) { described_class.new(link).to_html }
before do
stub_request(:get, api_link).to_return(status: 200, body: onebox_response("instagram_old_onebox"))
stub_request(:head, link)
stub_request(:get, api_link).to_return(status: 200, body: onebox_response("instagram_old"))
@previous_options = Onebox.options.to_h
Onebox.options = {}
end
@ -68,12 +68,12 @@ describe Onebox::Engine::InstagramOnebox do
Onebox.options = @previous_options
end
it "includes title" do
expect(html).to include('<a href="https://www.instagram.com/p/CARbvuYDm3Q" target="_blank" rel="noopener">@natgeo</a>')
it "renders preview with a placeholder" do
expect(Oneboxer.preview(link, invalidate_oneboxes: true)).to include('placeholder-icon image')
end
it "includes image" do
expect(html).to include("https://scontent-yyz1-1.cdninstagram.com/v/t51.2885-15/sh0.08/e35/s640x640/97565241_163250548553285_9172168193050746487_n.jpg")
it "renders html using an iframe" do
expect(html).to include('<iframe')
end
end
end