Merge branch 'manuels-oembed'
This commit is contained in:
commit
cd2647c029
26 changed files with 551 additions and 12 deletions
1
Gemfile
1
Gemfile
|
|
@ -64,6 +64,7 @@ gem 'rails-i18n'
|
||||||
gem 'nokogiri'
|
gem 'nokogiri'
|
||||||
gem 'redcarpet', "2.0.0b5"
|
gem 'redcarpet', "2.0.0b5"
|
||||||
gem 'roxml', :git => 'git://github.com/Empact/roxml.git', :ref => '7ea9a9ffd2338aaef5b0'
|
gem 'roxml', :git => 'git://github.com/Empact/roxml.git', :ref => '7ea9a9ffd2338aaef5b0'
|
||||||
|
gem 'ruby-oembed'
|
||||||
|
|
||||||
# queue
|
# queue
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -406,6 +406,7 @@ GEM
|
||||||
linecache19 (>= 0.5.11)
|
linecache19 (>= 0.5.11)
|
||||||
ruby-debug-base19 (>= 0.11.19)
|
ruby-debug-base19 (>= 0.11.19)
|
||||||
ruby-hmac (0.4.0)
|
ruby-hmac (0.4.0)
|
||||||
|
ruby-oembed (0.8.3)
|
||||||
ruby-openid (2.1.8)
|
ruby-openid (2.1.8)
|
||||||
ruby-openid-apps-discovery (1.2.0)
|
ruby-openid-apps-discovery (1.2.0)
|
||||||
ruby-openid (>= 2.1.7)
|
ruby-openid (>= 2.1.7)
|
||||||
|
|
@ -532,6 +533,7 @@ DEPENDENCIES
|
||||||
rspec-rails (>= 2.0.0)
|
rspec-rails (>= 2.0.0)
|
||||||
ruby-debug
|
ruby-debug
|
||||||
ruby-debug19
|
ruby-debug19
|
||||||
|
ruby-oembed
|
||||||
sass (= 3.1.7)
|
sass (= 3.1.7)
|
||||||
selenium-webdriver (~> 2.7.0)
|
selenium-webdriver (~> 2.7.0)
|
||||||
settingslogic (= 2.0.6)
|
settingslogic (= 2.0.6)
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,6 @@ module MarkdownifyHelper
|
||||||
render_options[:filter_html] = true
|
render_options[:filter_html] = true
|
||||||
render_options[:hard_wrap] ||= true
|
render_options[:hard_wrap] ||= true
|
||||||
|
|
||||||
|
|
||||||
# This ugly little hack basically means
|
# This ugly little hack basically means
|
||||||
# "Give me the rawest contents of target available"
|
# "Give me the rawest contents of target available"
|
||||||
if target.respond_to?(:raw_message)
|
if target.respond_to?(:raw_message)
|
||||||
|
|
@ -32,7 +31,6 @@ module MarkdownifyHelper
|
||||||
|
|
||||||
return '' if message.blank?
|
return '' if message.blank?
|
||||||
|
|
||||||
#renderer = Redcarpet::Render::HTML.new(render_options)
|
|
||||||
renderer = Diaspora::Markdownify::HTML.new(render_options)
|
renderer = Diaspora::Markdownify::HTML.new(render_options)
|
||||||
markdown = Redcarpet::Markdown.new(renderer, markdown_options)
|
markdown = Redcarpet::Markdown.new(renderer, markdown_options)
|
||||||
|
|
||||||
|
|
|
||||||
33
app/helpers/o_embed_helper.rb
Normal file
33
app/helpers/o_embed_helper.rb
Normal file
|
|
@ -0,0 +1,33 @@
|
||||||
|
module OEmbedHelper
|
||||||
|
def o_embed_html(cache)
|
||||||
|
data = cache.data
|
||||||
|
data = {} if data.blank?
|
||||||
|
title = data.fetch('title', cache.url)
|
||||||
|
html = link_to(title, cache.url, :target => '_blank')
|
||||||
|
return html unless data.has_key?('type')
|
||||||
|
case data['type']
|
||||||
|
when 'video', 'rich'
|
||||||
|
if cache.is_trusted_and_has_html?
|
||||||
|
html = data['html']
|
||||||
|
elsif data.has_key?('thumbnail_url')
|
||||||
|
html = link_to_oembed_image(cache)
|
||||||
|
end
|
||||||
|
when 'photo'
|
||||||
|
if data.has_key?('url')
|
||||||
|
img_options = cache.options_hash('')
|
||||||
|
html = link_to_oembed_image(cache, '')
|
||||||
|
end
|
||||||
|
else
|
||||||
|
end
|
||||||
|
|
||||||
|
return html.html_safe
|
||||||
|
end
|
||||||
|
|
||||||
|
def link_to_oembed_image(cache, prefix = 'thumbnail_')
|
||||||
|
link_to(oembed_image_tag(cache, prefix), cache.url, :target => '_blank')
|
||||||
|
end
|
||||||
|
|
||||||
|
def oembed_image_tag(cache, prefix)
|
||||||
|
image_tag(cache.data[prefix + 'url'], cache.options_hash(prefix))
|
||||||
|
end
|
||||||
|
end
|
||||||
16
app/models/jobs/gather_o_embed_data.rb
Normal file
16
app/models/jobs/gather_o_embed_data.rb
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
# Copyright (c) 2010-2011, Diaspora Inc. This file is
|
||||||
|
# licensed under the Affero General Public License version 3 or later. See
|
||||||
|
# the COPYRIGHT file.
|
||||||
|
#
|
||||||
|
|
||||||
|
module Jobs
|
||||||
|
class GatherOEmbedData < Base
|
||||||
|
@queue = :http_service
|
||||||
|
|
||||||
|
def self.perform(post_id, url)
|
||||||
|
post = Post.find(post_id)
|
||||||
|
post.o_embed_cache = OEmbedCache.find_or_create_by_url(url)
|
||||||
|
post.save
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
42
app/models/o_embed_cache.rb
Normal file
42
app/models/o_embed_cache.rb
Normal file
|
|
@ -0,0 +1,42 @@
|
||||||
|
class OEmbedCache < ActiveRecord::Base
|
||||||
|
serialize :data
|
||||||
|
attr_accessible :url
|
||||||
|
|
||||||
|
has_many :posts
|
||||||
|
|
||||||
|
def self.find_or_create_by_url(url)
|
||||||
|
cache = OEmbedCache.find_or_initialize_by_url(url)
|
||||||
|
return cache if cache.persisted?
|
||||||
|
cache.fetch_and_save_oembed_data!
|
||||||
|
cache
|
||||||
|
end
|
||||||
|
|
||||||
|
def fetch_and_save_oembed_data!
|
||||||
|
begin
|
||||||
|
response = OEmbed::Providers.get(self.url, {:maxwidth => 420, :maxheight => 420, :frame => 1, :iframe => 1})
|
||||||
|
rescue Exception => e
|
||||||
|
# noop
|
||||||
|
else
|
||||||
|
self.data = response.fields
|
||||||
|
self.data['trusted_endpoint_url'] = response.provider.endpoint
|
||||||
|
self.save
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def is_trusted_and_has_html?
|
||||||
|
self.from_trusted? and self.data.has_key?('html')
|
||||||
|
end
|
||||||
|
|
||||||
|
def from_trusted?
|
||||||
|
SECURE_ENDPOINTS.include?(self.data['trusted_endpoint_url'])
|
||||||
|
end
|
||||||
|
|
||||||
|
def options_hash(prefix = 'thumbnail_')
|
||||||
|
return nil unless self.data.has_key?(prefix + 'url')
|
||||||
|
{
|
||||||
|
:height => self.data.fetch(prefix + 'height', ''),
|
||||||
|
:width => self.data.fetch(prefix + 'width', ''),
|
||||||
|
:alt => self.data.fetch('title', ''),
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
@ -28,6 +28,8 @@ class Post < ActiveRecord::Base
|
||||||
has_many :reshares, :class_name => "Reshare", :foreign_key => :root_guid, :primary_key => :guid
|
has_many :reshares, :class_name => "Reshare", :foreign_key => :root_guid, :primary_key => :guid
|
||||||
has_many :resharers, :class_name => 'Person', :through => :reshares, :source => :author
|
has_many :resharers, :class_name => 'Person', :through => :reshares, :source => :author
|
||||||
|
|
||||||
|
belongs_to :o_embed_cache
|
||||||
|
|
||||||
belongs_to :author, :class_name => 'Person'
|
belongs_to :author, :class_name => 'Person'
|
||||||
|
|
||||||
validates :guid, :uniqueness => true
|
validates :guid, :uniqueness => true
|
||||||
|
|
@ -36,7 +38,7 @@ class Post < ActiveRecord::Base
|
||||||
|
|
||||||
#scopes
|
#scopes
|
||||||
scope :all_public, where(:public => true, :pending => false)
|
scope :all_public, where(:public => true, :pending => false)
|
||||||
scope :includes_for_a_stream, includes({:author => :profile}, :mentions => {:person => :profile}) #note should include root and photos, but i think those are both on status_message
|
scope :includes_for_a_stream, includes(:o_embed_cache, {:author => :profile}, :mentions => {:person => :profile}) #note should include root and photos, but i think those are both on status_message
|
||||||
|
|
||||||
def self.for_a_stream(max_time, order)
|
def self.for_a_stream(max_time, order)
|
||||||
by_max_time(max_time, order).
|
by_max_time(max_time, order).
|
||||||
|
|
|
||||||
|
|
@ -22,10 +22,13 @@ class StatusMessage < Post
|
||||||
validate :presence_of_content
|
validate :presence_of_content
|
||||||
|
|
||||||
attr_accessible :text, :provider_display_name
|
attr_accessible :text, :provider_display_name
|
||||||
|
attr_accessor :oembed_url
|
||||||
serialize :youtube_titles, Hash
|
serialize :youtube_titles, Hash
|
||||||
|
|
||||||
after_create :create_mentions
|
after_create :create_mentions
|
||||||
|
|
||||||
|
after_create :queue_gather_oembed_data, :if => :contains_oembed_url_in_text?
|
||||||
|
|
||||||
#scopes
|
#scopes
|
||||||
scope :where_person_is_mentioned, lambda{|person| joins(:mentions).where(:mentions => {:person_id => person.id})}
|
scope :where_person_is_mentioned, lambda{|person| joins(:mentions).where(:mentions => {:person_id => person.id})}
|
||||||
|
|
||||||
|
|
@ -153,6 +156,16 @@ class StatusMessage < Post
|
||||||
self.text.blank? && self.photos.blank?
|
self.text.blank? && self.photos.blank?
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def queue_gather_oembed_data
|
||||||
|
Resque.enqueue(Jobs::GatherOEmbedData, self.id, self.oembed_url)
|
||||||
|
end
|
||||||
|
|
||||||
|
def contains_oembed_url_in_text?
|
||||||
|
require 'uri'
|
||||||
|
urls = URI.extract(self.raw_message, ['http', 'https'])
|
||||||
|
self.oembed_url = urls.find{|url| ENDPOINT_HOSTS_STRING.match(URI.parse(url).host)}
|
||||||
|
end
|
||||||
|
|
||||||
protected
|
protected
|
||||||
|
|
||||||
def presence_of_content
|
def presence_of_content
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@
|
||||||
= person_link(comment.author, :class => "hovercardable")
|
= person_link(comment.author, :class => "hovercardable")
|
||||||
|
|
||||||
%span{:class => direction_for(comment.text)}
|
%span{:class => direction_for(comment.text)}
|
||||||
= markdownify(comment, :youtube_maps => comment.youtube_titles)
|
= markdownify(comment, :oembed => true, :youtube_maps => comment.youtube_titles)
|
||||||
|
|
||||||
.comment_info
|
.comment_info
|
||||||
%time.timeago{:datetime => comment.created_at}
|
%time.timeago{:datetime => comment.created_at}
|
||||||
|
|
|
||||||
|
|
@ -12,5 +12,5 @@
|
||||||
= how_long_ago(message)
|
= how_long_ago(message)
|
||||||
|
|
||||||
%div{ :class => direction_for(message.text) }
|
%div{ :class => direction_for(message.text) }
|
||||||
= markdownify(message)
|
= markdownify(message, :oembed => true)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -22,13 +22,13 @@
|
||||||
%h4
|
%h4
|
||||||
=t('.bio')
|
=t('.bio')
|
||||||
%div{ :class => direction_for(person.profile.bio) }
|
%div{ :class => direction_for(person.profile.bio) }
|
||||||
= markdownify(person.profile.bio, :newlines => true)
|
= markdownify(person.profile.bio, :oembed => true, :newlines => true)
|
||||||
- unless person.profile.location.blank?
|
- unless person.profile.location.blank?
|
||||||
%li
|
%li
|
||||||
%h4
|
%h4
|
||||||
=t('.location')
|
=t('.location')
|
||||||
%div{ :class => direction_for(person.profile.location) }
|
%div{ :class => direction_for(person.profile.location) }
|
||||||
= markdownify(person.profile.location, :newlines => true)
|
= markdownify(person.profile.location, :oembed => true, :newlines => true)
|
||||||
|
|
||||||
%li
|
%li
|
||||||
- unless person.profile.gender.blank?
|
- unless person.profile.gender.blank?
|
||||||
|
|
|
||||||
|
|
@ -40,7 +40,7 @@
|
||||||
|
|
||||||
.span-8.last
|
.span-8.last
|
||||||
%p
|
%p
|
||||||
= markdownify(photo.status_message)
|
= markdownify(photo.status_message, :oembed => true)
|
||||||
%span{:style=>'font-size:smaller'}
|
%span{:style=>'font-size:smaller'}
|
||||||
=link_to t('.collection_permalink'), post_path(photo.status_message)
|
=link_to t('.collection_permalink'), post_path(photo.status_message)
|
||||||
%br
|
%br
|
||||||
|
|
|
||||||
|
|
@ -17,3 +17,5 @@
|
||||||
|
|
||||||
%div{:class => direction_for(post.text)}
|
%div{:class => direction_for(post.text)}
|
||||||
!= markdownify(post, :youtube_maps => post[:youtube_titles])
|
!= markdownify(post, :youtube_maps => post[:youtube_titles])
|
||||||
|
- if post.o_embed_cache_id.present?
|
||||||
|
= o_embed_html(post.o_embed_cache)
|
||||||
|
|
|
||||||
20
config/initializers/oembed.rb
Normal file
20
config/initializers/oembed.rb
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
require 'oembed'
|
||||||
|
require 'uri'
|
||||||
|
|
||||||
|
OEmbed::Providers.register_all
|
||||||
|
OEmbed::Providers.register_fallback(OEmbed::ProviderDiscovery)
|
||||||
|
#
|
||||||
|
# SECURITY NOTICE! CROSS-SITE SCRIPTING!
|
||||||
|
# these endpoints may inject html code into our page
|
||||||
|
# note that 'trusted_endpoint_url' is the only information
|
||||||
|
# in OEmbed that we can trust. anything else may be spoofed!
|
||||||
|
SECURE_ENDPOINTS = [::OEmbed::Providers::Youtube.endpoint,
|
||||||
|
::OEmbed::Providers::Viddler.endpoint,
|
||||||
|
::OEmbed::Providers::Qik.endpoint,
|
||||||
|
::OEmbed::Providers::Revision3.endpoint,
|
||||||
|
::OEmbed::Providers::Hulu.endpoint,
|
||||||
|
::OEmbed::Providers::Vimeo.endpoint,
|
||||||
|
'http://soundcloud.com/oembed',
|
||||||
|
'http://cubbi.es/oembed'
|
||||||
|
]
|
||||||
|
ENDPOINT_HOSTS_STRING = SECURE_ENDPOINTS.map{|e| URI.parse(e.split('{')[0]).host}.to_s
|
||||||
14
db/migrate/20110924112840_create_o_embed_caches.rb
Normal file
14
db/migrate/20110924112840_create_o_embed_caches.rb
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
class CreateOEmbedCaches < ActiveRecord::Migration
|
||||||
|
def self.up
|
||||||
|
create_table :o_embed_caches do |t|
|
||||||
|
t.string :url, :limit => 1024, :null => false, :unique => true
|
||||||
|
t.text :data, :null => false
|
||||||
|
end
|
||||||
|
add_index :o_embed_caches, :url
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.down
|
||||||
|
remove_index :o_embed_caches, :column => :url
|
||||||
|
drop_table :o_embed_caches
|
||||||
|
end
|
||||||
|
end
|
||||||
9
db/migrate/20111011193702_add_oembed_cache_to_posts.rb
Normal file
9
db/migrate/20111011193702_add_oembed_cache_to_posts.rb
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
class AddOembedCacheToPosts < ActiveRecord::Migration
|
||||||
|
def self.up
|
||||||
|
add_column :posts, :o_embed_cache_id, :integer
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.down
|
||||||
|
remove_column :posts, :o_embed_cache_id
|
||||||
|
end
|
||||||
|
end
|
||||||
10
db/schema.rb
10
db/schema.rb
|
|
@ -10,7 +10,7 @@
|
||||||
#
|
#
|
||||||
# It's strongly recommended to check this file into your version control system.
|
# It's strongly recommended to check this file into your version control system.
|
||||||
|
|
||||||
ActiveRecord::Schema.define(:version => 20111003232053) do
|
ActiveRecord::Schema.define(:version => 20111011193702) do
|
||||||
|
|
||||||
create_table "aspect_memberships", :force => true do |t|
|
create_table "aspect_memberships", :force => true do |t|
|
||||||
t.integer "aspect_id", :null => false
|
t.integer "aspect_id", :null => false
|
||||||
|
|
@ -178,6 +178,13 @@ ActiveRecord::Schema.define(:version => 20111003232053) do
|
||||||
add_index "notifications", ["target_id"], :name => "index_notifications_on_target_id"
|
add_index "notifications", ["target_id"], :name => "index_notifications_on_target_id"
|
||||||
add_index "notifications", ["target_type", "target_id"], :name => "index_notifications_on_target_type_and_target_id"
|
add_index "notifications", ["target_type", "target_id"], :name => "index_notifications_on_target_type_and_target_id"
|
||||||
|
|
||||||
|
create_table "o_embed_caches", :force => true do |t|
|
||||||
|
t.string "url", :limit => 1024, :null => false
|
||||||
|
t.text "data", :null => false
|
||||||
|
end
|
||||||
|
|
||||||
|
add_index "o_embed_caches", ["url"], :name => "index_o_embed_caches_on_url", :length => {"url"=>255}
|
||||||
|
|
||||||
create_table "oauth_access_tokens", :force => true do |t|
|
create_table "oauth_access_tokens", :force => true do |t|
|
||||||
t.integer "authorization_id", :null => false
|
t.integer "authorization_id", :null => false
|
||||||
t.string "access_token", :limit => 32, :null => false
|
t.string "access_token", :limit => 32, :null => false
|
||||||
|
|
@ -283,6 +290,7 @@ ActiveRecord::Schema.define(:version => 20111003232053) do
|
||||||
t.string "status_message_guid"
|
t.string "status_message_guid"
|
||||||
t.integer "likes_count", :default => 0
|
t.integer "likes_count", :default => 0
|
||||||
t.integer "comments_count", :default => 0
|
t.integer "comments_count", :default => 0
|
||||||
|
t.integer "o_embed_cache_id"
|
||||||
end
|
end
|
||||||
|
|
||||||
add_index "posts", ["author_id", "root_guid"], :name => "index_posts_on_author_id_and_root_guid", :unique => true
|
add_index "posts", ["author_id", "root_guid"], :name => "index_posts_on_author_id_and_root_guid", :unique => true
|
||||||
|
|
|
||||||
55
features/oembed.feature
Normal file
55
features/oembed.feature
Normal file
|
|
@ -0,0 +1,55 @@
|
||||||
|
@javascript
|
||||||
|
Feature: oembed
|
||||||
|
In order to make videos easy accessible
|
||||||
|
As a user
|
||||||
|
I want the links in my posts be replaced by their oEmbed representation
|
||||||
|
|
||||||
|
Background:
|
||||||
|
Given a user named "Alice Smith" with email "alice@alice.alice"
|
||||||
|
And I have several oEmbed data in cache
|
||||||
|
When I sign in as "alice@alice.alice"
|
||||||
|
And I am on the home page
|
||||||
|
|
||||||
|
Scenario: Post a secure video link
|
||||||
|
Given I expand the publisher
|
||||||
|
When I fill in "status_message_fake_text" with "http://youtube.com/watch?v=M3r2XDceM6A&format=json"
|
||||||
|
And I press "Share"
|
||||||
|
|
||||||
|
And I follow "Your Aspects"
|
||||||
|
Then I should see a video player
|
||||||
|
|
||||||
|
Scenario: Post an unsecure video link
|
||||||
|
Given I expand the publisher
|
||||||
|
When I fill in "status_message_fake_text" with "http://mytube.com/watch?v=M3r2XDceM6A&format=json"
|
||||||
|
And I press "Share"
|
||||||
|
|
||||||
|
And I follow "Your Aspects"
|
||||||
|
Then I should not see a video player
|
||||||
|
And I should see "http://mytube.com/watch?v=M3r2XDceM6A&format=json"
|
||||||
|
|
||||||
|
Scenario: Post an unsecure rich-typed link
|
||||||
|
Given I expand the publisher
|
||||||
|
When I fill in "status_message_fake_text" with "http://myrichtube.com/watch?v=M3r2XDceM6A&format=json"
|
||||||
|
And I press "Share"
|
||||||
|
|
||||||
|
And I follow "Your Aspects"
|
||||||
|
Then I should not see a video player
|
||||||
|
And I should see "http://myrichtube.com/watch?v=M3r2XDceM6A&format=json"
|
||||||
|
|
||||||
|
Scenario: Post a photo link
|
||||||
|
Given I expand the publisher
|
||||||
|
When I fill in "status_message_fake_text" with "http://farm4.static.flickr.com/3123/2341623661_7c99f48bbf_m.jpg"
|
||||||
|
And I press "Share"
|
||||||
|
|
||||||
|
And I follow "Your Aspects"
|
||||||
|
Then I should see a "img" within ".stream_element"
|
||||||
|
|
||||||
|
Scenario: Post an unsupported text link
|
||||||
|
Given I expand the publisher
|
||||||
|
When I fill in "status_message_fake_text" with "http://www.we-do-not-support-oembed.com/index.html"
|
||||||
|
And I press "Share"
|
||||||
|
|
||||||
|
And I follow "Your Aspects"
|
||||||
|
Then I should see "http://www.we-do-not-support-oembed.com/index.html" within ".stream_element"
|
||||||
|
|
||||||
|
|
||||||
118
features/step_definitions/oembed.rb
Normal file
118
features/step_definitions/oembed.rb
Normal file
|
|
@ -0,0 +1,118 @@
|
||||||
|
Given /^I have several oEmbed data in cache$/ do
|
||||||
|
scenarios = {
|
||||||
|
"photo" => {
|
||||||
|
"oembed_data" => {
|
||||||
|
"trusted_endpoint_url" => "__!SPOOFED!__",
|
||||||
|
"version" => "1.0",
|
||||||
|
"type" => "photo",
|
||||||
|
"title" => "ZB8T0193",
|
||||||
|
"width" => "240",
|
||||||
|
"height" => "160",
|
||||||
|
"url" => "http://farm4.static.flickr.com/3123/2341623661_7c99f48bbf_m.jpg"
|
||||||
|
},
|
||||||
|
"link_url" => 'http://www.flickr.com/photos/bees/2341623661',
|
||||||
|
"oembed_get_request" => "http://www.flickr.com/services/oembed/?format=json&frame=1&iframe=1&maxheight=420&maxwidth=420&url=http://www.flickr.com/photos/bees/2341623661",
|
||||||
|
},
|
||||||
|
|
||||||
|
"unsupported" => {
|
||||||
|
"oembed_data" => {},
|
||||||
|
"oembed_get_request" => 'http://www.we-do-not-support-oembed.com/index.html',
|
||||||
|
"link_url" => 'http://www.we-do-not-support-oembed.com/index.html',
|
||||||
|
"discovery_data" => 'no LINK tag!',
|
||||||
|
},
|
||||||
|
|
||||||
|
"secure_video" => {
|
||||||
|
"oembed_data" => {
|
||||||
|
"version" => "1.0",
|
||||||
|
"type" => "video",
|
||||||
|
"width" => 425,
|
||||||
|
"height" => 344,
|
||||||
|
"title" => "Amazing Nintendo Facts",
|
||||||
|
"html" => "<object width=\"425\" height=\"344\">
|
||||||
|
<param name=\"movie\" value=\"http://www.youtube.com/v/M3r2XDceM6A&fs=1\"></param>
|
||||||
|
<param name=\"allowFullScreen\" value=\"true\"></param>
|
||||||
|
<param name=\"allowscriptaccess\" value=\"always\"></param>
|
||||||
|
<embed src=\"http://www.youtube.com/v/M3r2XDceM6A&fs=1\"
|
||||||
|
type=\"application/x-shockwave-flash\" width=\"425\" height=\"344\"
|
||||||
|
allowscriptaccess=\"always\" allowfullscreen=\"true\"></embed>
|
||||||
|
</object>",
|
||||||
|
},
|
||||||
|
"link_url" => "http://youtube.com/watch?v=M3r2XDceM6A&format=json",
|
||||||
|
"oembed_get_request" => "http://www.youtube.com/oembed?format=json&frame=1&iframe=1&maxheight=420&maxwidth=420&url=http://youtube.com/watch?v=M3r2XDceM6A",
|
||||||
|
},
|
||||||
|
|
||||||
|
"unsecure_video" => {
|
||||||
|
"oembed_data" => {
|
||||||
|
"version" => "1.0",
|
||||||
|
"type" => "video",
|
||||||
|
"title" => "This is a video from an unsecure source",
|
||||||
|
"html" => "<object width=\"425\" height=\"344\">
|
||||||
|
<param name=\"movie\" value=\"http://www.youtube.com/v/M3r2XDceM6A&fs=1\"></param>
|
||||||
|
<param name=\"allowFullScreen\" value=\"true\"></param>
|
||||||
|
<param name=\"allowscriptaccess\" value=\"always\"></param>
|
||||||
|
<embed src=\"http://www.youtube.com/v/M3r2XDceM6A&fs=1\"
|
||||||
|
type=\"application/x-shockwave-flash\" width=\"425\" height=\"344\"
|
||||||
|
allowscriptaccess=\"always\" allowfullscreen=\"true\"></embed>
|
||||||
|
</object>",
|
||||||
|
},
|
||||||
|
"link_url" => "http://myrichtube.com/watch?v=M3r2XDceM6A&format=json",
|
||||||
|
"discovery_data" => '<link rel="alternate" type="application/json+oembed" href="http://www.mytube.com/oembed?format=json&frame=1&iframe=1&maxheight=420&maxwidth=420&url=http://mytube.com/watch?v=M3r2XDceM6A" />',
|
||||||
|
"oembed_get_request" => "http://www.mytube.com/oembed?format=json&frame=1&iframe=1&maxheight=420&maxwidth=420&url=http://mytube.com/watch?v=M3r2XDceM6A",
|
||||||
|
},
|
||||||
|
|
||||||
|
"secure_rich" => {
|
||||||
|
"oembed_data" => {
|
||||||
|
"version" => "1.0",
|
||||||
|
"type" => "rich",
|
||||||
|
"width" => 425,
|
||||||
|
"height" => 344,
|
||||||
|
"title" => "Amazing Nintendo Facts",
|
||||||
|
"html" => "<object width=\"425\" height=\"344\">
|
||||||
|
<param name=\"movie\" value=\"http://www.youtube.com/v/M3r2XDceM6A&fs=1\"></param>
|
||||||
|
<param name=\"allowFullScreen\" value=\"true\"></param>
|
||||||
|
<param name=\"allowscriptaccess\" value=\"always\"></param>
|
||||||
|
<embed src=\"http://www.youtube.com/v/M3r2XDceM6A&fs=1\"
|
||||||
|
type=\"application/x-shockwave-flash\" width=\"425\" height=\"344\"
|
||||||
|
allowscriptaccess=\"always\" allowfullscreen=\"true\"></embed>
|
||||||
|
</object>",
|
||||||
|
},
|
||||||
|
"link_url" => "http://yourichtube.com/watch?v=M3r2XDceM6A&format=json",
|
||||||
|
"oembed_get_request" => "http://www.youtube.com/oembed?format=json&frame=1&iframe=1&maxheight=420&maxwidth=420&url=http://youtube.com/watch?v=M3r2XDceM6A",
|
||||||
|
},
|
||||||
|
|
||||||
|
"unsecure_rich" => {
|
||||||
|
"oembed_data" => {
|
||||||
|
"version" => "1.0",
|
||||||
|
"type" => "rich",
|
||||||
|
"title" => "This is a video from an unsecure source",
|
||||||
|
"html" => "<object width=\"425\" height=\"344\">
|
||||||
|
<param name=\"movie\" value=\"http://www.youtube.com/v/M3r2XDceM6A&fs=1\"></param>
|
||||||
|
<param name=\"allowFullScreen\" value=\"true\"></param>
|
||||||
|
<param name=\"allowscriptaccess\" value=\"always\"></param>
|
||||||
|
<embed src=\"http://www.youtube.com/v/M3r2XDceM6A&fs=1\"
|
||||||
|
type=\"application/x-shockwave-flash\" width=\"425\" height=\"344\"
|
||||||
|
allowscriptaccess=\"always\" allowfullscreen=\"true\"></embed>
|
||||||
|
</object>",
|
||||||
|
},
|
||||||
|
"link_url" => "http://mytube.com/watch?v=M3r2XDceM6A&format=json",
|
||||||
|
"discovery_data" => '<link rel="alternate" type="application/json+oembed" href="http://www.mytube.com/oembed?format=json&frame=1&iframe=1&maxheight=420&maxwidth=420&url=http://mytube.com/watch?v=M3r2XDceM6A" />',
|
||||||
|
"oembed_get_request" => "http://www.mytube.com/oembed?format=json&frame=1&iframe=1&maxheight=420&maxwidth=420&url=http://mytube.com/watch?v=M3r2XDceM6A",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
scenarios.each do |type, data|
|
||||||
|
unless type=='unsupported'
|
||||||
|
url = data['oembed_get_request'].split('?')[0]
|
||||||
|
store_data = data['oembed_data'].merge('trusted_endpoint_url' => url)
|
||||||
|
OEmbedCache.new(:url => data['link_url'], :data => store_data.to_json);
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
Then /^I should see a video player$/ do
|
||||||
|
page.has_css?('object')
|
||||||
|
end
|
||||||
|
|
||||||
|
Then /^I should not see a video player$/ do
|
||||||
|
page.has_no_css?('object')
|
||||||
|
end
|
||||||
|
|
||||||
|
|
@ -3,7 +3,6 @@ require 'erb'
|
||||||
module Diaspora
|
module Diaspora
|
||||||
module Markdownify
|
module Markdownify
|
||||||
class HTML < Redcarpet::Render::HTML
|
class HTML < Redcarpet::Render::HTML
|
||||||
|
|
||||||
include ActionView::Helpers::TextHelper
|
include ActionView::Helpers::TextHelper
|
||||||
include ActionView::Helpers::TagHelper
|
include ActionView::Helpers::TagHelper
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,6 @@ describe TagsController do
|
||||||
it 'responds with json' do
|
it 'responds with json' do
|
||||||
get :index, :q => "ra", :format => 'json'
|
get :index, :q => "ra", :format => 'json'
|
||||||
#parse json
|
#parse json
|
||||||
response.body.should include("#diaspora")
|
|
||||||
response.body.should include("#rad")
|
response.body.should include("#rad")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
||||||
128
spec/helpers/o_embed_helper_spec.rb
Normal file
128
spec/helpers/o_embed_helper_spec.rb
Normal file
|
|
@ -0,0 +1,128 @@
|
||||||
|
require 'spec_helper'
|
||||||
|
|
||||||
|
describe OEmbedHelper do
|
||||||
|
describe 'o_embed_html' do
|
||||||
|
scenarios = {
|
||||||
|
"photo" => {
|
||||||
|
"oembed_data" => {
|
||||||
|
"trusted_endpoint_url" => "__!SPOOFED!__",
|
||||||
|
"version" => "1.0",
|
||||||
|
"type" => "photo",
|
||||||
|
"title" => "ZB8T0193",
|
||||||
|
"width" => "240",
|
||||||
|
"height" => "160",
|
||||||
|
"url" => "http://farm4.static.flickr.com/3123/2341623661_7c99f48bbf_m.jpg"
|
||||||
|
},
|
||||||
|
"link_url" => 'http://www.flickr.com/photos/bees/2341623661',
|
||||||
|
"oembed_get_request" => "http://www.flickr.com/services/oembed/?format=json&frame=1&iframe=1&maxheight=420&maxwidth=420&url=http://www.flickr.com/photos/bees/2341623661",
|
||||||
|
},
|
||||||
|
|
||||||
|
"unsupported" => {
|
||||||
|
"oembed_data" => "",
|
||||||
|
"oembed_get_request" => 'http://www.we-do-not-support-oembed.com/index.html',
|
||||||
|
"link_url" => 'http://www.we-do-not-support-oembed.com/index.html'
|
||||||
|
},
|
||||||
|
|
||||||
|
"secure_video" => {
|
||||||
|
"oembed_data" => {
|
||||||
|
"version" => "1.0",
|
||||||
|
"type" => "video",
|
||||||
|
"width" => 425,
|
||||||
|
"height" => 344,
|
||||||
|
'trusted_endpoint_url' => ::OEmbed::Providers::Youtube.endpoint,
|
||||||
|
"title" => "Amazing Nintendo Facts",
|
||||||
|
"html" => "<object width=\"425\" height=\"344\">
|
||||||
|
<param name=\"movie\" value=\"http://www.youtube.com/v/M3r2XDceM6A&fs=1\"></param>
|
||||||
|
<param name=\"allowFullScreen\" value=\"true\"></param>
|
||||||
|
<param name=\"allowscriptaccess\" value=\"always\"></param>
|
||||||
|
<embed src=\"http://www.youtube.com/v/M3r2XDceM6A&fs=1\"
|
||||||
|
type=\"application/x-shockwave-flash\" width=\"425\" height=\"344\"
|
||||||
|
allowscriptaccess=\"always\" allowfullscreen=\"true\"></embed>
|
||||||
|
</object>",
|
||||||
|
},
|
||||||
|
"link_url" => "http://youtube.com/watch?v=M3r2XDceM6A&format=json",
|
||||||
|
"oembed_get_request" => "http://www.youtube.com/oembed?format=json&frame=1&iframe=1&maxheight=420&maxwidth=420&url=http://youtube.com/watch?v=M3r2XDceM6A",
|
||||||
|
},
|
||||||
|
|
||||||
|
"unsecure_video" => {
|
||||||
|
"oembed_data" => {
|
||||||
|
"version" => "1.0",
|
||||||
|
"type" => "video",
|
||||||
|
"title" => "This is a video from an unsecure source",
|
||||||
|
"html" => "<object width=\"425\" height=\"344\">
|
||||||
|
<param name=\"movie\" value=\"http://www.youtube.com/v/M3r2XDceM6A&fs=1\"></param>
|
||||||
|
<param name=\"allowFullScreen\" value=\"true\"></param>
|
||||||
|
<param name=\"allowscriptaccess\" value=\"always\"></param>
|
||||||
|
<embed src=\"http://www.youtube.com/v/M3r2XDceM6A&fs=1\"
|
||||||
|
type=\"application/x-shockwave-flash\" width=\"425\" height=\"344\"
|
||||||
|
allowscriptaccess=\"always\" allowfullscreen=\"true\"></embed>
|
||||||
|
</object>",
|
||||||
|
},
|
||||||
|
"link_url" => "http://mytube.com/watch?v=M3r2XDceM6A&format=json",
|
||||||
|
"discovery_data" => '<link rel="alternate" type="application/json+oembed" href="http://www.mytube.com/oembed?format=json&frame=1&iframe=1&maxheight=420&maxwidth=420&url=http://mytube.com/watch?v=M3r2XDceM6A" />',
|
||||||
|
"oembed_get_request" => "http://www.mytube.com/oembed?format=json&frame=1&iframe=1&maxheight=420&maxwidth=420&url=http://mytube.com/watch?v=M3r2XDceM6A",
|
||||||
|
},
|
||||||
|
|
||||||
|
"secure_rich" => {
|
||||||
|
"oembed_data" => {
|
||||||
|
"version" => "1.0",
|
||||||
|
"type" => "rich",
|
||||||
|
"width" => 425,
|
||||||
|
"height" => 344,
|
||||||
|
'trusted_endpoint_url' => ::OEmbed::Providers::Youtube.endpoint,
|
||||||
|
"title" => "Amazing Nintendo Facts",
|
||||||
|
"html" => "<object width=\"425\" height=\"344\">
|
||||||
|
<param name=\"movie\" value=\"http://www.youtube.com/v/M3r2XDceM6A&fs=1\"></param>
|
||||||
|
<param name=\"allowFullScreen\" value=\"true\"></param>
|
||||||
|
<param name=\"allowscriptaccess\" value=\"always\"></param>
|
||||||
|
<embed src=\"http://www.youtube.com/v/M3r2XDceM6A&fs=1\"
|
||||||
|
type=\"application/x-shockwave-flash\" width=\"425\" height=\"344\"
|
||||||
|
allowscriptaccess=\"always\" allowfullscreen=\"true\"></embed>
|
||||||
|
</object>",
|
||||||
|
},
|
||||||
|
"link_url" => "http://youtube.com/watch?v=M3r2XDceM6A&format=json",
|
||||||
|
"oembed_get_request" => "http://www.youtube.com/oembed?format=json&frame=1&iframe=1&maxheight=420&maxwidth=420&url=http://youtube.com/watch?v=M3r2XDceM6A",
|
||||||
|
},
|
||||||
|
|
||||||
|
"unsecure_rich" => {
|
||||||
|
"oembed_data" => {
|
||||||
|
"version" => "1.0",
|
||||||
|
"type" => "rich",
|
||||||
|
"title" => "This is a video from an unsecure source",
|
||||||
|
"html" => "<object width=\"425\" height=\"344\">
|
||||||
|
<param name=\"movie\" value=\"http://www.youtube.com/v/M3r2XDceM6A&fs=1\"></param>
|
||||||
|
<param name=\"allowFullScreen\" value=\"true\"></param>
|
||||||
|
<param name=\"allowscriptaccess\" value=\"always\"></param>
|
||||||
|
<embed src=\"http://www.youtube.com/v/M3r2XDceM6A&fs=1\"
|
||||||
|
type=\"application/x-shockwave-flash\" width=\"425\" height=\"344\"
|
||||||
|
allowscriptaccess=\"always\" allowfullscreen=\"true\"></embed>
|
||||||
|
</object>",
|
||||||
|
},
|
||||||
|
"link_url" => "http://mytube.com/watch?v=M3r2XDceM6A&format=json",
|
||||||
|
"discovery_data" => '<link rel="alternate" type="application/json+oembed" href="http://www.mytube.com/oembed?format=json&frame=1&iframe=1&maxheight=420&maxwidth=420&url=http://mytube.com/watch?v=M3r2XDceM6A" />',
|
||||||
|
"oembed_get_request" => "http://www.mytube.com/oembed?format=json&frame=1&iframe=1&maxheight=420&maxwidth=420&url=http://mytube.com/watch?v=M3r2XDceM6A",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
scenarios.each do |type, data|
|
||||||
|
specify 'for type "'+type+'"' do
|
||||||
|
real_data = data['oembed_data']
|
||||||
|
cache = OEmbedCache.new(:url => data['link_url'])
|
||||||
|
cache.data = real_data
|
||||||
|
formatted = o_embed_html(cache)
|
||||||
|
case type
|
||||||
|
when 'photo'
|
||||||
|
formatted.should =~ /#{data['oembed_data']['url']}/
|
||||||
|
when 'unsupported'
|
||||||
|
formatted.should =~ /#{data['link_url']}/
|
||||||
|
when 'secure_video', 'secure_rich'
|
||||||
|
formatted.should =~ /#{data['oembed_data']['html']}/
|
||||||
|
when 'unsecure_video', 'unsecure_rich'
|
||||||
|
formatted.should_not =~ /#{data['oembed_data']['html']}/
|
||||||
|
formatted.should =~ /#{data['oembed_data']['title']}/
|
||||||
|
formatted.should =~ /#{data['oembed_data']['url']}/
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
59
spec/models/jobs/gather_o_embed_data.rb
Normal file
59
spec/models/jobs/gather_o_embed_data.rb
Normal file
|
|
@ -0,0 +1,59 @@
|
||||||
|
require 'spec_helper'
|
||||||
|
describe Jobs::GatherOEmbedData do
|
||||||
|
before do
|
||||||
|
@flickr_oembed_data = {
|
||||||
|
"trusted_endpoint_url" => "__!SPOOFED!__",
|
||||||
|
"version" => "1.0",
|
||||||
|
"type" => "photo",
|
||||||
|
"author_url" => "http://www.flickr.com/photos/bees/",
|
||||||
|
"cache_age" => 3600,
|
||||||
|
"provider_name" => "Flickr",
|
||||||
|
"provider_url" => "http://www.flickr.com/",
|
||||||
|
"title" => "ZB8T0193",
|
||||||
|
"author_name" => "Bees",
|
||||||
|
"width" => "240",
|
||||||
|
"height" => "160",
|
||||||
|
"url" => "https://farm4.static.flickr.com/3123/2341623661_7c99f48bbf_m.jpg"
|
||||||
|
}
|
||||||
|
|
||||||
|
@flickr_oembed_url = 'http://www.flickr.com/services/oembed/'
|
||||||
|
@flickr_photo_url = 'http://www.flickr.com/photos/bees/2341623661'
|
||||||
|
@flickr_oembed_get_request = @flickr_oembed_url+"?format=json&frame=1&iframe=1&maxheight=420&maxwidth=420&url="+@flickr_photo_url
|
||||||
|
|
||||||
|
@no_oembed_url = 'http://www.we-do-not-support-oembed.com/index.html'
|
||||||
|
|
||||||
|
stub_request(:get, @flickr_oembed_get_request).to_return(:status => 200, :body => @flickr_oembed_data.to_json)
|
||||||
|
stub_request(:get, @no_oembed_url).to_return(:status => 200, :body => '<html><body>hello there</body></html>')
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '.perform' do
|
||||||
|
it 'requests not data from the internet' do
|
||||||
|
Jobs::GatherOEmbedData.perform("Look at this! "+@flickr_photo_url)
|
||||||
|
|
||||||
|
a_request(:get, @flickr_oembed_get_request).should have_been_made
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'requests not data from the internet only once' do
|
||||||
|
Jobs::GatherOEmbedData.perform("Look at this! "+@flickr_photo_url)
|
||||||
|
Jobs::GatherOEmbedData.perform("Look at this! "+@flickr_photo_url)
|
||||||
|
|
||||||
|
a_request(:get, @flickr_oembed_get_request).should have_been_made.times(1)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'creates one cache entry' do
|
||||||
|
Jobs::GatherOEmbedData.perform("Look at this! "+@flickr_photo_url)
|
||||||
|
|
||||||
|
expected_data = @flickr_oembed_data
|
||||||
|
expected_data['trusted_endpoint_url'] = @flickr_oembed_url
|
||||||
|
OEmbedCache.find_by_url(@flickr_photo_url).data.should == expected_data
|
||||||
|
|
||||||
|
Jobs::GatherOEmbedData.perform("Look at this! "+@flickr_photo_url)
|
||||||
|
OEmbedCache.count(:conditions => {:url => @flickr_photo_url}).should == 1
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'creates no cache entry for unsupported pages' do
|
||||||
|
Jobs::GatherOEmbedData.perform("This page is lame! It does not support oEmbed: "+@no_oembed_url)
|
||||||
|
OEmbedCache.find_by_url(@no_oembed_url).should be_nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
5
spec/models/o_embed_cache_spec.rb
Normal file
5
spec/models/o_embed_cache_spec.rb
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
require 'spec_helper'
|
||||||
|
|
||||||
|
describe OEmbedCache do
|
||||||
|
pending "add some examples to (or delete) #{__FILE__}"
|
||||||
|
end
|
||||||
|
|
@ -312,4 +312,20 @@ STR
|
||||||
@status_message.after_dispatch(alice)
|
@status_message.after_dispatch(alice)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe '#contains_url_in_text?' do
|
||||||
|
it 'returns an array of all urls found in the raw message' do
|
||||||
|
sm = Factory(:status_message, :text => 'http://youtube.com is so cool. so is https://joindiaspora.com')
|
||||||
|
sm.contains_oembed_url_in_text?.should_not be_nil
|
||||||
|
sm.oembed_url.should == 'http://youtube.com'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'oembed' do
|
||||||
|
it 'should queue a GatherOembedData if it includes a link' do
|
||||||
|
sm = Factory.build(:status_message, :text => 'http://youtube.com is so cool. so is https://joindiaspora.com')
|
||||||
|
Resque.should_receive(:enqueue).with(Jobs::GatherOEmbedData, instance_of(Fixnum), instance_of(String))
|
||||||
|
sm.save
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue