opengraph POC

Fixed small-frame opengraph view

Fixed incompletely saved OpenGraphCache bug
This commit is contained in:
Fábián Tamás László 2013-06-09 01:38:54 +02:00 committed by Jonne Haß
parent 9d6ac1abe5
commit 176c6826e0
19 changed files with 200 additions and 6 deletions

View file

@ -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

View file

@ -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)

View file

@ -0,0 +1,8 @@
(function(){
app.helpers.openGraph = {
html : function (open_graph_cache) {
if (!open_graph_cache) { return "" }
return '<img src="' + open_graph_cache.image + '" />'
}
}
})();

View file

@ -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"
});

View file

@ -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']));

View file

@ -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});
},

View file

@ -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

View file

@ -0,0 +1,10 @@
{{#if open_graph_cache}}
<a href="{{open_graph_cache.url}}" target="_blank">
<div>
<h3>{{open_graph_cache.title}}</h3>
<img src="{{open_graph_cache.image}}" />
<p>{{open_graph_cache.description}}</p>
<br />
</div>
</a>
{{/if}}

View file

@ -16,6 +16,7 @@
{{/if}}
<div class="embed-frame" />
<div class="open-graph-frame" />
{{#if text}}
<div class="text-content">

View file

@ -17,4 +17,5 @@
<div class="collapsible">
{{{text}}}
<div class="oembed"></div>
<div class="opengraph"></div>
</div>

View file

@ -44,6 +44,25 @@ module OpenGraphHelper
content_tag(:meta, '', :property => name, :content => content)
end
def og_html(cache)
title = cache.title
html =
"<div class=\"og-concent\">" +
"<a class=\"og-link\" href=\"#{cache.url}\">" +
"<img class=\"og-image\" src=\"#{cache.image}\"/>"
"<h1 class=\"og-title\">#{cache.title}</h1>" +
"<p class=\"og-description\">#{cache.description}</p>"
"</a></div>"
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

View file

@ -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

View file

@ -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|

View file

@ -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

View file

@ -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

View file

@ -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,

View file

@ -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

View file

@ -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

View file

@ -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