Commit 4d51c024 authored by Steffen van Bergerem's avatar Steffen van Bergerem Committed by Benjamin Neff

Add OpenGraph video support

closes #7043
parent 0514545f
......@@ -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
......
// @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 '<img src="' + open_graph_cache.image + '" />';
},
};
})();
// @license-end
......@@ -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(
"<iframe src='" + this.$(".video-overlay").attr("data-video-url") + "' frameBorder=0 width='100%'></iframe>"
);
}
});
......
......@@ -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;
}
......
{{#unless o_embed_cache}}
{{#if open_graph_cache}}
<div class="opengraph-container">
<div class="thumb">
{{#if open_graph_cache.video_url }}
<div class="video-overlay" data-video-url="{{open_graph_cache.video_url}}">
<img src="{{open_graph_cache.image}}" />
<div class="overlay"></div>
</div>
{{else}}
<img src="{{open_graph_cache.image}}" />
{{/if}}
</div>
<a href="{{open_graph_cache.url}}" target="_blank">
<img src="{{open_graph_cache.image}}" />
<p class="og-title">{{open_graph_cache.title}}</p>
</a>
<p class="og-description">{{open_graph_cache.description}}</p>
</div>
{{/if}}
{{/unless}}
......@@ -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
......@@ -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
# SECURITY NOTICE! CROSS-SITE SCRIPTING!
# these endpoints may inject html code into our page
bandcamp:
video_urls:
- https://bandcamp.com/EmbeddedPlayer/*/*
class AddVideoUrlToOpenGraphCache < ActiveRecord::Migration
def change
add_column :open_graph_caches, :video_url, :text
end
end
......@@ -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|
......
......@@ -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
......
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();
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");
});
});
});
describe("rendering", function(){
it("shows the preview based on the opengraph data", function(){
this.view.render();
var html = this.view.$el.html();
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(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).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);
});
});
});
});
# 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
......@@ -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
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment