discourse/lib/oneboxer.rb

151 lines
4.0 KiB
Ruby
Raw Normal View History

2013-02-06 03:16:51 +08:00
require 'open-uri'
require_dependency 'oneboxer/base'
require_dependency 'oneboxer/whitelist'
2013-02-26 00:42:20 +08:00
Dir["#{Rails.root}/lib/oneboxer/*_onebox.rb"].each {|f|
require_dependency(f.split('/')[-2..-1].join('/'))
2013-02-06 03:16:51 +08:00
}
module Oneboxer
extend Oneboxer::Base
Dir["#{Rails.root}/lib/oneboxer/*_onebox.rb"].sort.each do |f|
2013-02-06 03:16:51 +08:00
add_onebox "Oneboxer::#{Pathname.new(f).basename.to_s.gsub(/\.rb$/, '').classify}".constantize
end
def self.default_expiry
1.month
end
# Return a oneboxer for a given URL
def self.onebox_for_url(url)
matchers.each do |matcher|
regexp = matcher.regexp
klass = matcher.klass
2013-02-06 13:22:11 +08:00
regexp = regexp.call if regexp.class == Proc
return klass.new(url) if url =~ regexp
2013-02-06 03:16:51 +08:00
end
nil
end
# Retrieve the onebox for a url without caching
def self.onebox_nocache(url)
oneboxer = onebox_for_url(url)
return oneboxer.onebox if oneboxer.present?
whitelist_entry = Whitelist.entry_for_url(url)
2013-02-06 03:16:51 +08:00
if whitelist_entry.present?
2013-02-06 03:16:51 +08:00
page_html = open(url).read
if page_html.present?
2013-02-12 22:46:45 +08:00
doc = Nokogiri::HTML(page_html)
2013-02-06 03:16:51 +08:00
if whitelist_entry.allows_oembed?
# See if if it has an oembed thing we can use
(doc/"link[@type='application/json+oembed']").each do |oembed|
return OembedOnebox.new(oembed[:href]).onebox
end
(doc/"link[@type='text/json+oembed']").each do |oembed|
return OembedOnebox.new(oembed[:href]).onebox
end
end
2013-02-06 03:16:51 +08:00
# Check for opengraph
open_graph = Oneboxer.parse_open_graph(doc)
return OpenGraphOnebox.new(url, open_graph).onebox if open_graph.present?
end
end
nil
rescue OpenURI::HTTPError
nil
2013-02-06 03:16:51 +08:00
end
# Parse URLs out of HTML, returning the document when finished.
def self.each_onebox_link(string_or_doc)
doc = string_or_doc
2013-02-12 22:46:45 +08:00
doc = Nokogiri::HTML(doc) if doc.is_a?(String)
2013-02-06 03:16:51 +08:00
onebox_links = doc.search("a.onebox")
if onebox_links.present?
onebox_links.each do |link|
if link['href'].present?
yield link['href'], link
end
end
end
doc
end
def self.create_post_reference(result, args={})
result.post_onebox_renders.create(post_id: args[:post_id]) if args[:post_id].present?
2013-02-26 00:42:20 +08:00
rescue ActiveRecord::RecordNotUnique
2013-02-06 03:16:51 +08:00
end
def self.render_from_cache(url, args={})
result = OneboxRender.where(url: url).first
# Return the result but also create a reference to it
if result.present?
create_post_reference(result, args)
return result
end
2013-02-26 00:42:20 +08:00
nil
2013-02-06 03:16:51 +08:00
end
# Cache results from a onebox call
def self.fetch_and_cache(url, args)
cooked, preview = onebox_nocache(url)
return nil if cooked.blank?
# Store a cooked version in the database
OneboxRender.transaction do
begin
render = OneboxRender.create(url: url, preview: preview, cooked: cooked, expires_at: Oneboxer.default_expiry.from_now)
create_post_reference(render, args)
rescue ActiveRecord::RecordNotUnique
2013-02-26 00:42:20 +08:00
end
end
2013-02-06 03:16:51 +08:00
2013-02-26 00:42:20 +08:00
[cooked, preview]
2013-02-06 03:16:51 +08:00
end
2013-02-26 00:42:20 +08:00
# Retrieve a preview of a onebox, caching the result for performance
2013-02-06 03:16:51 +08:00
def self.preview(url, args={})
cached = render_from_cache(url, args) unless args[:no_cache].present?
# If we have a preview stored, return that. Otherwise return cooked content.
if cached.present?
return cached.preview if cached.preview.present?
return cached.cooked
end
cooked, preview = fetch_and_cache(url, args)
return preview if preview.present?
cooked
end
def self.invalidate(url)
OneboxRender.destroy_all(url: url)
end
# Return the cooked content for a url, caching the result for performance
def self.onebox(url, args={})
if args[:invalidate_oneboxes].present?
# Remove the onebox from the cache
Oneboxer.invalidate(url)
else
cached = render_from_cache(url, args) unless args[:no_cache].present?
return cached.cooked if cached.present?
end
cooked, preview = fetch_and_cache(url, args)
cooked
end
end