diff --git a/Changelog.md b/Changelog.md
index da66cf97c..162cf51d1 100644
--- a/Changelog.md
+++ b/Changelog.md
@@ -16,6 +16,7 @@
## Features
* Deleted comments will be removed when loading more comments [#7045](https://github.com/diaspora/diaspora/pull/7045)
* The "subscribe" indicator on a post now gets toggled when you like or rehsare a post [#7040](https://github.com/diaspora/diaspora/pull/7040)
+* Add OpenGraph video support [#7043](https://github.com/diaspora/diaspora/pull/7043)
# 0.6.0.0
diff --git a/app/assets/javascripts/app/helpers/open_graph.js b/app/assets/javascripts/app/helpers/open_graph.js
deleted file mode 100644
index f106b681f..000000000
--- a/app/assets/javascripts/app/helpers/open_graph.js
+++ /dev/null
@@ -1,12 +0,0 @@
-// @license magnet:?xt=urn:btih:0b31508aeb0634b347b8270c7bee4d411b5d4109&dn=agpl-3.0.txt AGPL-v3-or-Later
-
-(function(){
- app.helpers.openGraph = {
- html : function (open_graph_cache) {
- if (!open_graph_cache) { return ""; }
- return '
';
- },
- };
-})();
-// @license-end
-
diff --git a/app/assets/javascripts/app/views/content_view.js b/app/assets/javascripts/app/views/content_view.js
index 76fb62739..485fca01c 100644
--- a/app/assets/javascripts/app/views/content_view.js
+++ b/app/assets/javascripts/app/views/content_view.js
@@ -151,6 +151,10 @@ app.views.OEmbed = app.views.Base.extend({
app.views.OpenGraph = app.views.Base.extend({
templateName : "opengraph",
+ events: {
+ "click .video-overlay": "loadVideo"
+ },
+
initialize: function() {
this.truncateDescription();
},
@@ -161,6 +165,12 @@ app.views.OpenGraph = app.views.Base.extend({
var ogdesc = this.model.get('open_graph_cache');
ogdesc.description = app.helpers.truncate(ogdesc.description, 250);
}
+ },
+
+ loadVideo: function() {
+ this.$(".opengraph-container").html(
+ ""
+ );
}
});
diff --git a/app/assets/stylesheets/opengraph.scss b/app/assets/stylesheets/opengraph.scss
index b0f877e4c..3e7bd5c02 100644
--- a/app/assets/stylesheets/opengraph.scss
+++ b/app/assets/stylesheets/opengraph.scss
@@ -10,20 +10,41 @@
overflow: hidden;
a {
color: #000;
- img {
- margin: 5px 5px 5px 0px;
- float: left;
- max-width: 150px;
- padding-right: 5px;
- }
.og-title {
- margin-bottom: 5px;
font-weight: bold;
}
}
a:hover {
color: $blue;
}
+
+ .thumb {
+ float: left;
+ margin: 5px;
+ margin-left: 0;
+ max-width: 150px;
+ padding-right: 5px;
+
+ .video-overlay {
+ cursor: pointer;
+ height: 145px;
+ width: 145px;
+ }
+
+ .overlay {
+ background-color: rgba($black, .2);
+ background-image: image-url('buttons/playbtn.png');
+ background-position: center;
+ background-repeat: no-repeat;
+ background-size: 60px 60px;
+ display: inline-block;
+ height: 145px;
+ position: relative;
+ top: -145px;
+ width: 145px;
+ }
+ }
+
.og-description {
color: $text-grey;
}
diff --git a/app/assets/templates/opengraph_tpl.jst.hbs b/app/assets/templates/opengraph_tpl.jst.hbs
index fd1a66853..57fe9fc76 100644
--- a/app/assets/templates/opengraph_tpl.jst.hbs
+++ b/app/assets/templates/opengraph_tpl.jst.hbs
@@ -1,12 +1,20 @@
{{#unless o_embed_cache}}
{{#if open_graph_cache}}
{{/if}}
{{/unless}}
-
diff --git a/app/models/open_graph_cache.rb b/app/models/open_graph_cache.rb
index 7bf381f01..ffae81c94 100644
--- a/app/models/open_graph_cache.rb
+++ b/app/models/open_graph_cache.rb
@@ -13,6 +13,7 @@ class OpenGraphCache < ActiveRecord::Base
t.add :image
t.add :description
t.add :url
+ t.add :video_url
end
def image
@@ -39,8 +40,15 @@ class OpenGraphCache < ActiveRecord::Base
self.image = object.og.image.url
self.url = object.og.url
self.description = object.og.description
+ if object.og.video.try(:secure_url) && secure_video_url?(object.og.video.secure_url)
+ self.video_url = object.og.video.secure_url
+ end
self.save
rescue OpenGraphReader::NoOpenGraphDataError, OpenGraphReader::InvalidObjectError
end
+
+ def secure_video_url?(url)
+ SECURE_OPENGRAPH_VIDEO_URLS.any? {|u| u =~ url }
+ end
end
diff --git a/config/initializers/open_graph_reader.rb b/config/initializers/open_graph_reader.rb
index f1ec34134..182dd4bdb 100644
--- a/config/initializers/open_graph_reader.rb
+++ b/config/initializers/open_graph_reader.rb
@@ -5,3 +5,18 @@ OpenGraphReader.configure do |config|
config.synthesize_image_url = true
config.guess_datetime_format = true
end
+
+og_video_urls = []
+og_providers = YAML.load_file(Rails.root.join("config", "open_graph_providers.yml"))
+og_providers.each do |_, provider|
+ provider["video_urls"].each do |video_url|
+ # taken from https://github.com/ruby-oembed/ruby-oembed/blob/fe2b63c/lib/oembed/provider.rb#L68
+ _, scheme, domain, path = *video_url.match(%r{([^:]*)://?([^/?]*)(.*)})
+ domain = Regexp.escape(domain).gsub("\\*", "(.*?)").gsub("(.*?)\\.", "([^\\.]+\\.)?")
+ path = Regexp.escape(path).gsub("\\*", "(.*?)")
+ url = Regexp.new("^#{Regexp.escape(scheme)}://#{domain}#{path}")
+ og_video_urls << url
+ end if provider["video_urls"]
+end
+
+SECURE_OPENGRAPH_VIDEO_URLS = og_video_urls
diff --git a/config/open_graph_providers.yml b/config/open_graph_providers.yml
new file mode 100644
index 000000000..00c94955d
--- /dev/null
+++ b/config/open_graph_providers.yml
@@ -0,0 +1,5 @@
+# SECURITY NOTICE! CROSS-SITE SCRIPTING!
+# these endpoints may inject html code into our page
+bandcamp:
+ video_urls:
+ - https://bandcamp.com/EmbeddedPlayer/*/*
diff --git a/db/migrate/20160901072443_add_video_url_to_open_graph_cache.rb b/db/migrate/20160901072443_add_video_url_to_open_graph_cache.rb
new file mode 100644
index 000000000..487248a26
--- /dev/null
+++ b/db/migrate/20160901072443_add_video_url_to_open_graph_cache.rb
@@ -0,0 +1,5 @@
+class AddVideoUrlToOpenGraphCache < ActiveRecord::Migration
+ def change
+ add_column :open_graph_caches, :video_url, :text
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index 593703cb4..f911b7660 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -11,7 +11,7 @@
#
# It's strongly recommended that you check this file into your version control system.
-ActiveRecord::Schema.define(version: 20160822212739) do
+ActiveRecord::Schema.define(version: 20160901072443) do
create_table "account_deletions", force: :cascade do |t|
t.string "diaspora_handle", limit: 255
@@ -300,6 +300,7 @@ ActiveRecord::Schema.define(version: 20160822212739) do
t.text "image", limit: 65535
t.text "url", limit: 65535
t.text "description", limit: 65535
+ t.text "video_url", limit: 65535
end
create_table "participations", force: :cascade do |t|
diff --git a/spec/factories.rb b/spec/factories.rb
index 4b5363f70..3d4fd827f 100644
--- a/spec/factories.rb
+++ b/spec/factories.rb
@@ -257,6 +257,7 @@ FactoryGirl.define do
title "Some article"
ob_type "article"
description "This is the article lead"
+ video_url "http://example.com/videos/123.html"
end
factory(:tag_following) do
diff --git a/spec/javascripts/app/views/open_graph_view_spec.js b/spec/javascripts/app/views/open_graph_view_spec.js
index 77bba41a1..e40c49ae9 100644
--- a/spec/javascripts/app/views/open_graph_view_spec.js
+++ b/spec/javascripts/app/views/open_graph_view_spec.js
@@ -1,30 +1,72 @@
describe("app.views.OpenGraph", function() {
- var open_graph_cache = {
- "url": "http://example.com/articles/123",
- "title": "Example title",
- "description": "Test description",
- "image": "http://example.com/thumb.jpg",
- "ob_type": "article"
- };
-
- beforeEach(function(){
- this.statusMessage = factory.statusMessage({
- "open_graph_cache": open_graph_cache
+ context("without a video_url", function() {
+ beforeEach(function() {
+ this.openGraphCache = {
+ "url": "http://example.com/articles/123",
+ "title": "Example title",
+ "description": "Test description",
+ "image": "http://example.com/thumb.jpg",
+ "ob_type": "article"
+ };
+ this.statusMessage = factory.statusMessage({
+ "open_graph_cache": this.openGraphCache
+ });
+ this.view = new app.views.OpenGraph({model: this.statusMessage});
});
- this.view = new app.views.OpenGraph({model : this.statusMessage});
- });
+ describe("rendering", function() {
+ it("shows the preview based on the opengraph data", function() {
+ this.view.render();
+ var html = this.view.$el.html();
- describe("rendering", function(){
- it("shows the preview based on the opengraph data", function(){
- this.view.render();
- var html = this.view.$el.html();
-
- expect(html).toContain(open_graph_cache.url);
- expect(html).toContain(open_graph_cache.title);
- expect(html).toContain(open_graph_cache.description);
- expect(html).toContain(open_graph_cache.image);
+ expect(html).toContain(this.openGraphCache.url);
+ expect(html).toContain(this.openGraphCache.title);
+ expect(html).toContain(this.openGraphCache.description);
+ expect(html).toContain(this.openGraphCache.image);
+ expect(html).not.toContain("video-overlay");
+ });
});
});
+ context("with a video_url", function() {
+ beforeEach(function() {
+ this.openGraphCache = {
+ "url": "http://example.com/articles/123",
+ "title": "Example title",
+ "description": "Test description",
+ "image": "http://example.com/thumb.jpg",
+ "ob_type": "article",
+ "video_url": "http://example.com"
+ };
+ this.statusMessage = factory.statusMessage({
+ "open_graph_cache": this.openGraphCache
+ });
+ this.view = new app.views.OpenGraph({model: this.statusMessage});
+ });
+
+ describe("rendering", function() {
+ it("shows the preview based on the opengraph data", function() {
+ this.view.render();
+ var html = this.view.$el.html();
+
+ expect(html).toContain(this.openGraphCache.url);
+ expect(html).toContain(this.openGraphCache.title);
+ expect(html).toContain(this.openGraphCache.description);
+ expect(html).toContain(this.openGraphCache.image);
+ expect(html).toContain(this.openGraphCache.video_url);
+ expect(html).toContain("video-overlay");
+ });
+ });
+
+ describe("loadVideo", function() {
+ it("adds an iframe with the video", function() {
+ this.view.render();
+ spec.content().html(this.view.$el);
+ expect($("iframe").length).toBe(0);
+ $(".video-overlay").click();
+ expect($("iframe").length).toBe(1);
+ expect($("iframe").attr("src")).toBe(this.openGraphCache.video_url);
+ });
+ });
+ });
});
diff --git a/spec/models/open_graph_cache_spec.rb b/spec/models/open_graph_cache_spec.rb
new file mode 100644
index 000000000..adbf98a03
--- /dev/null
+++ b/spec/models/open_graph_cache_spec.rb
@@ -0,0 +1,61 @@
+# Copyright (c) 2010-2011, Diaspora Inc. This file is
+# licensed under the Affero General Public License version 3 or later. See
+# the COPYRIGHT file.
+
+require "spec_helper"
+
+describe OpenGraphCache, type: :model do
+ describe "fetch_and_save_opengraph_data!" do
+ context "with an unsecure video url" do
+ it "doesn't save the video url" do
+ expect(OpenGraphReader).to receive(:fetch!).with("https://example.com/article/123").and_return(
+ double(
+ og: double(
+ description: "This is the article lead",
+ image: double(url: "https://example.com/image/123.jpg"),
+ title: "Some article",
+ type: "article",
+ url: "https://example.com/acticle/123-seo-foo",
+ video: double(secure_url: "https://example.com/videos/123.html")
+ )
+ )
+ )
+ ogc = OpenGraphCache.new(url: "https://example.com/article/123")
+ ogc.fetch_and_save_opengraph_data!
+
+ expect(ogc.description).to eq("This is the article lead")
+ expect(ogc.image).to eq("https://example.com/image/123.jpg")
+ expect(ogc.title).to eq("Some article")
+ expect(ogc.ob_type).to eq("article")
+ expect(ogc.url).to eq("https://example.com/acticle/123-seo-foo")
+ expect(ogc.video_url).to be_nil
+ end
+ end
+
+ context "with a secure video url" do
+ it "saves the video url" do
+ expect(OpenGraphReader).to receive(:fetch!).with("https://example.com/article/123").and_return(
+ double(
+ og: double(
+ description: "This is the article lead",
+ image: double(url: "https://example.com/image/123.jpg"),
+ title: "Some article",
+ type: "article",
+ url: "https://example.com/acticle/123-seo-foo",
+ video: double(secure_url: "https://bandcamp.com/EmbeddedPlayer/v=2/track=12/size=small")
+ )
+ )
+ )
+ ogc = OpenGraphCache.new(url: "https://example.com/article/123")
+ ogc.fetch_and_save_opengraph_data!
+
+ expect(ogc.description).to eq("This is the article lead")
+ expect(ogc.image).to eq("https://example.com/image/123.jpg")
+ expect(ogc.title).to eq("Some article")
+ expect(ogc.ob_type).to eq("article")
+ expect(ogc.url).to eq("https://example.com/acticle/123-seo-foo")
+ expect(ogc.video_url).to eq("https://bandcamp.com/EmbeddedPlayer/v=2/track=12/size=small")
+ end
+ end
+ end
+end
diff --git a/spec/presenters/post_presenter_spec.rb b/spec/presenters/post_presenter_spec.rb
index 5c9c52728..6bd2ca689 100644
--- a/spec/presenters/post_presenter_spec.rb
+++ b/spec/presenters/post_presenter_spec.rb
@@ -133,4 +133,25 @@ describe PostPresenter do
expect(PostPresenter.new(reshare).send(:description)).to eq(nil)
end
end
+
+ describe "#build_open_graph_cache" do
+ it "returns a dummy og cache if the og cache is missing" do
+ expect(@presenter.build_open_graph_cache.image).to be_nil
+ end
+
+ context "with an open graph cache" do
+ it "delegates to as_api_response" do
+ og_cache = double("open_graph_cache")
+ expect(og_cache).to receive(:as_api_response).with(:backbone)
+ @presenter.post = double(open_graph_cache: og_cache)
+ @presenter.send(:build_open_graph_cache)
+ end
+
+ it "returns the open graph cache data" do
+ open_graph_cache = FactoryGirl.create(:open_graph_cache)
+ post = FactoryGirl.create(:status_message, public: true, open_graph_cache: open_graph_cache)
+ expect(PostPresenter.new(post).send(:build_open_graph_cache)).to eq(open_graph_cache.as_api_response(:backbone))
+ end
+ end
+ end
end