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

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
diff --git a/app/assets/stylesheets/common/base/onebox.scss b/app/assets/stylesheets/common/base/onebox.scss
index 368c7cf..76a0576 100644
--- a/app/assets/stylesheets/common/base/onebox.scss
+++ b/app/assets/stylesheets/common/base/onebox.scss
@@ -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;
diff --git a/config/site_settings.yml b/config/site_settings.yml
index 399815b..5cfbd0e 100644
--- a/config/site_settings.yml
+++ b/config/site_settings.yml
@@ -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
diff --git a/lib/onebox/engine/instagram_onebox.rb b/lib/onebox/engine/instagram_onebox.rb
index 21a8ae6..7cc96ad 100644
--- a/lib/onebox/engine/instagram_onebox.rb
+++ b/lib/onebox/engine/instagram_onebox.rb
@@ -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
diff --git a/lib/onebox/helpers.rb b/lib/onebox/helpers.rb
index 94138de..4d54ac8 100644
--- a/lib/onebox/helpers.rb
+++ b/lib/onebox/helpers.rb
@@ -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
diff --git a/spec/components/oneboxer_spec.rb b/spec/components/oneboxer_spec.rb
index 527f054..21afb3a 100644
--- a/spec/components/oneboxer_spec.rb
+++ b/spec/components/oneboxer_spec.rb
@@ -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
 
diff --git a/spec/fixtures/onebox/instagram_old_onebox.response b/spec/fixtures/onebox/instagram_old_onebox.response
deleted file mode 100644
index b0c0a9a..0000000
--- a/spec/fixtures/onebox/instagram_old_onebox.response
+++ /dev/null
@@ -1,17 +0,0 @@
-
-{
-"version": "1.0",
-"title": "Photo by Pete McBride @pedromcbride | For the first time in three decades, inhabitants of northern India are able to see the Himalaya\u2014thanks to reduced air pollution over the last few weeks. Considering that India experiences some of the worst pollution in the world, this is a literal breath of fresh air. When I was there, the air was so thick you could taste the smoke and fumes.\n\nThe coronavirus pandemic that has led to India's temporary reduction in pollutants has also put the country on the world's largest lockdown, and it's too soon to tell what impact that has had on curbing the disease\u2014as well as what the long-term effects will be on attitudes toward fresh air once the population returns to business as usual. For more on India and the environment, follow @pedromcbride. #india #himalaya #covid19 #pollution",
-"author_name": "natgeo",
-"author_url": "https://www.instagram.com/natgeo",
-"author_id": 787132, "media_id": "2310750110684704208_787132",
-"provider_name": "Instagram",
-"provider_url": "https://www.instagram.com",
-"type": "rich",
-"width": 658,
-"height": null,

[... diff too long, it was truncated ...]

GitHub sha: aec125b6170c8c4b84515e26586d0dfb3247bf2d

This commit appears in #14789 which was approved by eviltrout. It was merged by jbrw.