diff --git a/Gemfile b/Gemfile
index 59087dbd4..fe0e640d8 100644
--- a/Gemfile
+++ b/Gemfile
@@ -64,6 +64,7 @@ gem 'rails_autolink', '1.1.0'
gem 'redcarpet', '3.0.0'
gem 'roxml', '3.1.6'
gem 'ruby-oembed', '0.8.8'
+gem 'opengraph', '0.0.4'
# Please remove when migrating to Rails 4
diff --git a/Gemfile.lock b/Gemfile.lock
index 2f89ad7a8..0f95c9437 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -249,6 +249,10 @@ GEM
omniauth-twitter (1.0.0)
multi_json (~> 1.3)
omniauth-oauth (~> 1.0)
+ opengraph (0.0.4)
+ hashie
+ nokogiri (~> 1.5.0)
+ rest-client (~> 1.6.0)
orm_adapter (0.4.0)
polyglot (0.3.3)
pry (0.9.12.2)
@@ -322,6 +326,8 @@ GEM
redis-namespace (1.3.0)
redis (~> 3.0.0)
remotipart (1.2.1)
+ rest-client (1.6.7)
+ mime-types (>= 1.16)
rmagick (2.13.2)
roxml (3.1.6)
activesupport (>= 2.3.0)
@@ -464,6 +470,7 @@ DEPENDENCIES
omniauth-facebook (= 1.4.1)
omniauth-tumblr (= 1.1)
omniauth-twitter (= 1.0.0)
+ opengraph (= 0.0.4)
rack-cors (= 0.2.8)
rack-google-analytics (= 0.11.0)
rack-piwik (= 0.2.2)
diff --git a/app/assets/javascripts/app/helpers/open_graph.js b/app/assets/javascripts/app/helpers/open_graph.js
new file mode 100644
index 000000000..cc22e312f
--- /dev/null
+++ b/app/assets/javascripts/app/helpers/open_graph.js
@@ -0,0 +1,8 @@
+(function(){
+ app.helpers.openGraph = {
+ html : function (open_graph_cache) {
+ if (!open_graph_cache) { return "" }
+ return '
'
+ }
+ }
+})();
diff --git a/app/assets/javascripts/app/views/content_view.js b/app/assets/javascripts/app/views/content_view.js
index 17762e057..b175aae1c 100644
--- a/app/assets/javascripts/app/views/content_view.js
+++ b/app/assets/javascripts/app/views/content_view.js
@@ -44,9 +44,13 @@ app.views.Content = app.views.Base.extend({
var collHeight = 200
, elem = this.$(".collapsible")
, oembed = elem.find(".oembed")
+ , opengraph = elem.find(".opengraph")
, addHeight = 0;
if($.trim(oembed.html()) != "") {
- addHeight = oembed.height();
+ addHeight += oembed.height();
+ }
+ if($.trim(opengraph.html()) != "") {
+ addHeight += opengraph.height();
}
// only collapse if height exceeds collHeight+20%
@@ -102,3 +106,7 @@ app.views.OEmbed = app.views.Base.extend({
this.$el.html(insertHTML);
}
});
+
+app.views.OpenGraph = app.views.Base.extend({
+ templateName : "opengraph"
+});
diff --git a/app/assets/javascripts/app/views/post/small_frame.js b/app/assets/javascripts/app/views/post/small_frame.js
index fe847d45d..6920c19aa 100644
--- a/app/assets/javascripts/app/views/post/small_frame.js
+++ b/app/assets/javascripts/app/views/post/small_frame.js
@@ -10,7 +10,8 @@ app.views.Post.SmallFrame = app.views.Post.extend({
},
subviews : {
- '.embed-frame' : "oEmbedView"
+ '.embed-frame' : "oEmbedView",
+ '.open-graph-frame' : 'openGraphView'
},
initialize : function(options) {
@@ -22,6 +23,10 @@ app.views.Post.SmallFrame = app.views.Post.extend({
return new app.views.OEmbed({model : this.model})
},
+ openGraphView : function(){
+ return new app.views.OpenGraph({model : this.model})
+ },
+
smallFramePresenter : function(){
//todo : we need to have something better for small frame text, probably using the headline() scenario.
return _.extend(this.defaultPresenter(),
@@ -50,10 +55,10 @@ app.views.Post.SmallFrame = app.views.Post.extend({
var text = this.model.get("text")
, baseClass = $.trim(text).length == 0 ? "no-text" : "has-text";
- if(this.model.get("photos").length > 0 || this.model.get("o_embed_cache"))
+ if(this.model.get("photos").length > 0 || this.model.get("o_embed_cache") || this.model.get("open_graph_cache"))
baseClass += " has-media";
- if(baseClass == "no-text" || this.model.get("photos").length > 0 || this.model.get("o_embed_cache")) { return baseClass }
+ if(baseClass == "no-text" || this.model.get("photos").length > 0 || this.model.get("o_embed_cache") || this.model.get("open_graph_cache")) { return baseClass }
var randomColor = _.first(_.shuffle(['cyan', 'green', 'yellow', 'purple', 'lime-green', 'orange', 'red', 'turquoise', 'sand']));
diff --git a/app/assets/javascripts/app/views/stream_post_views.js b/app/assets/javascripts/app/views/stream_post_views.js
index 9be5a63b8..0c8faafe7 100644
--- a/app/assets/javascripts/app/views/stream_post_views.js
+++ b/app/assets/javascripts/app/views/stream_post_views.js
@@ -8,6 +8,7 @@ app.views.StreamPost = app.views.Post.extend({
".comments" : "commentStreamView",
".post-content" : "postContentView",
".oembed" : "oEmbedView",
+ ".opengraph" : "openGraphView",
".status-message-location" : "postLocationStreamView"
},
@@ -29,6 +30,7 @@ app.views.StreamPost = app.views.Post.extend({
//subviews
this.commentStreamView = new app.views.CommentStream({model : this.model});
this.oEmbedView = new app.views.OEmbed({model : this.model});
+ this.openGraphView = new app.views.OpenGraph({model : this.model});
},
diff --git a/app/assets/stylesheets/application.css.sass b/app/assets/stylesheets/application.css.sass
index 9c7ea04bc..bcf92ee20 100644
--- a/app/assets/stylesheets/application.css.sass
+++ b/app/assets/stylesheets/application.css.sass
@@ -1953,6 +1953,23 @@ ul#press_logos
iframe, .thumb img
:width 100%
+ .opengraph
+ :float left
+ :width 100%
+ a
+ :display block
+ :text-decoration none
+ :color #000
+ :margin-top 10px
+ :border-top solid 1px #DDD
+ :border-bottom solid 1px #DDD
+ :padding 10px 0px 10px 0px
+
+ img
+ :margin 10px 0px 10px 0px
+ :float left
+ br
+ :clear both
.conversation_participants
:background
diff --git a/app/assets/templates/opengraph_tpl.jst.hbs b/app/assets/templates/opengraph_tpl.jst.hbs
new file mode 100644
index 000000000..dc4f1fca5
--- /dev/null
+++ b/app/assets/templates/opengraph_tpl.jst.hbs
@@ -0,0 +1,10 @@
+{{#if open_graph_cache}}
+
+
+
{{open_graph_cache.title}}
+

+
{{open_graph_cache.description}}
+
+
+
+{{/if}}
diff --git a/app/assets/templates/small-frame/default_tpl.jst.hbs b/app/assets/templates/small-frame/default_tpl.jst.hbs
index f1da9f9c5..6c6c6ca39 100644
--- a/app/assets/templates/small-frame/default_tpl.jst.hbs
+++ b/app/assets/templates/small-frame/default_tpl.jst.hbs
@@ -16,6 +16,7 @@
{{/if}}
+
{{#if text}}
diff --git a/app/assets/templates/status-message_tpl.jst.hbs b/app/assets/templates/status-message_tpl.jst.hbs
index 3eb3cda82..166c8cb1d 100644
--- a/app/assets/templates/status-message_tpl.jst.hbs
+++ b/app/assets/templates/status-message_tpl.jst.hbs
@@ -17,4 +17,5 @@
\ No newline at end of file
+
+
diff --git a/app/helpers/open_graph_helper.rb b/app/helpers/open_graph_helper.rb
index a9fd791a2..009ee0ed5 100644
--- a/app/helpers/open_graph_helper.rb
+++ b/app/helpers/open_graph_helper.rb
@@ -44,6 +44,25 @@ module OpenGraphHelper
content_tag(:meta, '', :property => name, :content => content)
end
+ def og_html(cache)
+ title = cache.title
+ html =
+ ""
+ return html
+ 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
private
# This method compensates for hosting assets off of s3
diff --git a/app/models/open_graph_cache.rb b/app/models/open_graph_cache.rb
new file mode 100644
index 000000000..f146da52f
--- /dev/null
+++ b/app/models/open_graph_cache.rb
@@ -0,0 +1,49 @@
+class OpenGraphCache < ActiveRecord::Base
+ attr_accessible :title
+ attr_accessible :ob_type
+ attr_accessible :image
+ attr_accessible :url
+ attr_accessible :description
+
+ validates :title, :presence => true
+ validates :ob_type, :presence => true
+ validates :image, :presence => true
+ validates :url, :presence => true
+
+ has_many :posts
+
+ acts_as_api
+ api_accessible :backbone do |t|
+ t.add :title
+ t.add :ob_type
+ t.add :image
+ t.add :description
+ t.add :url
+ end
+
+ def self.find_or_create_by_url(url)
+ cache = OpenGraphCache.find_or_initialize_by_url(url)
+ return cache if cache.persisted?
+ cache.fetch_and_save_opengraph_data!
+ return cache if cache.persisted?
+ return nil
+ end
+
+ def fetch_and_save_opengraph_data!
+ begin
+ response = OpenGraph.fetch(self.url)
+ if !response
+ return
+ end
+ rescue => e
+ # noop
+ else
+ self.title = response.title
+ self.ob_type = response.type
+ self.image = response.image
+ self.url = response.url
+ self.description = response.description
+ self.save
+ end
+ end
+end
diff --git a/app/models/post.rb b/app/models/post.rb
index 59968356f..671d4660d 100644
--- a/app/models/post.rb
+++ b/app/models/post.rb
@@ -24,13 +24,14 @@ class Post < ActiveRecord::Base
has_many :resharers, :class_name => 'Person', :through => :reshares, :source => :author
belongs_to :o_embed_cache
+ belongs_to :open_graph_cache
after_create do
self.touch(:interacted_at)
end
#scopes
- 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
+ scope :includes_for_a_stream, includes(:o_embed_cache, :open_graph_cache, {:author => :profile}, :mentions => {:person => :profile}) #note should include root and photos, but i think those are both on status_message
scope :commented_by, lambda { |person|
diff --git a/app/models/reshare.rb b/app/models/reshare.rb
index e5728429b..338f389e6 100644
--- a/app/models/reshare.rb
+++ b/app/models/reshare.rb
@@ -33,6 +33,10 @@ class Reshare < Post
self.root ? root.o_embed_cache : super
end
+ def open_graph_cache
+ self.root ? root.open_graph_cache : super
+ end
+
def raw_message
self.root ? root.raw_message : super
end
diff --git a/app/models/status_message.rb b/app/models/status_message.rb
index f580be28d..52dbb7b24 100644
--- a/app/models/status_message.rb
+++ b/app/models/status_message.rb
@@ -26,10 +26,12 @@ class StatusMessage < Post
before_destroy :presence_of_content
attr_accessor :oembed_url
+ attr_accessor :open_graph_url
before_create :filter_mentions
after_create :create_mentions
after_create :queue_gather_oembed_data, :if => :contains_oembed_url_in_text?
+ after_create :queue_gather_open_graph_data, :if => :contains_open_graph_url_in_text?
#scopes
scope :where_person_is_mentioned, lambda { |person|
@@ -143,11 +145,19 @@ class StatusMessage < Post
Workers::GatherOEmbedData.perform_async(self.id, self.oembed_url)
end
+ def queue_gather_open_graph_data
+ Workers::GatherOpenGraphData.perform_async(self.id, self.open_graph_url)
+ end
+
def contains_oembed_url_in_text?
urls = URI.extract(self.raw_message, ['http', 'https'])
self.oembed_url = urls.find{ |url| !TRUSTED_OEMBED_PROVIDERS.find(url).nil? }
end
+ def contains_open_graph_url_in_text?
+ self.open_graph_url = URI.extract(self.raw_message, ['http', 'https'])[0]
+ end
+
def address
location.try(:address)
end
diff --git a/app/presenters/post_presenter.rb b/app/presenters/post_presenter.rb
index 919123266..db4fdf90b 100644
--- a/app/presenters/post_presenter.rb
+++ b/app/presenters/post_presenter.rb
@@ -29,6 +29,7 @@ class PostPresenter
:nsfw => @post.nsfw,
:author => @post.author.as_api_response(:backbone),
:o_embed_cache => @post.o_embed_cache.try(:as_api_response, :backbone),
+ :open_graph_cache => @post.open_graph_cache.try(:as_api_response, :backbone),
:mentioned_people => @post.mentioned_people.as_api_response(:backbone),
:photos => @post.photos.map {|p| p.as_api_response(:backbone)},
:frame_name => @post.frame_name || template_name,
diff --git a/app/workers/gather_open_graph_data.rb b/app/workers/gather_open_graph_data.rb
new file mode 100644
index 000000000..d73954d86
--- /dev/null
+++ b/app/workers/gather_open_graph_data.rb
@@ -0,0 +1,22 @@
+# 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 Workers
+ class GatherOpenGraphData < Base
+ sidekiq_options queue: :http_service
+
+ def perform(post_id, url, retry_count=1)
+ post = Post.find(post_id)
+ post.open_graph_cache = OpenGraphCache.find_or_create_by_url(url)
+ post.save
+ rescue ActiveRecord::RecordNotFound
+ # User created a post and deleted it right afterwards before we
+ # we had a chance to run the job.
+ # On the other hand sometimes the job runs before the Post is
+ # fully persisted. So we just reduce the amount of retries.
+ GatherOpenGraphData.perform_in(1.minute, post_id, url, retry_count+1) unless retry_count > 3
+ end
+ end
+end
diff --git a/db/migrate/20130608171134_add_open_graph_cache.rb b/db/migrate/20130608171134_add_open_graph_cache.rb
new file mode 100644
index 000000000..ccf4aecc4
--- /dev/null
+++ b/db/migrate/20130608171134_add_open_graph_cache.rb
@@ -0,0 +1,19 @@
+class AddOpenGraphCache < ActiveRecord::Migration
+ def up
+ create_table :open_graph_caches do |t|
+ t.string :title
+ t.string :ob_type
+ t.string :image
+ t.string :url
+ t.text :description
+ end
+ change_table :posts do |t|
+ t.integer :open_graph_cache_id
+ end
+ end
+
+ def down
+ remove_column :posts, :open_graph_cache_id
+ drop_table :open_graph_caches
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index cbbfa94c2..f5b374c6b 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -215,6 +215,14 @@ ActiveRecord::Schema.define(:version => 20130801063213) do
add_index "o_embed_caches", ["url"], :name => "index_o_embed_caches_on_url", :length => {"url"=>255}
+ create_table "open_graph_caches", :force => true do |t|
+ t.string "title"
+ t.string "ob_type"
+ t.string "image"
+ t.string "url"
+ t.text "description"
+ end
+
create_table "participations", :force => true do |t|
t.string "guid"
t.integer "target_id"
@@ -309,6 +317,7 @@ ActiveRecord::Schema.define(:version => 20130801063213) do
t.string "facebook_id"
t.string "tweet_id"
t.text "tumblr_ids"
+ t.integer "open_graph_cache_id"
end
add_index "posts", ["author_id", "root_guid"], :name => "index_posts_on_author_id_and_root_guid", :unique => true