Work in progress: Swap out onebox code for onebox gem
This commit is contained in:
parent
a4e28eadd0
commit
e453bfa073
2
Gemfile
2
Gemfile
|
@ -72,6 +72,8 @@ gem 'redis', :require => ["redis", "redis/connection/hiredis"]
|
|||
gem 'active_model_serializers'
|
||||
|
||||
|
||||
gem 'onebox', git: 'https://github.com/dysania/onebox.git'
|
||||
|
||||
# we had issues with latest, stick to the rev till we figure this out
|
||||
# PR that makes it all hang together welcome
|
||||
gem 'ember-rails'
|
||||
|
|
|
@ -1,3 +1,15 @@
|
|||
GIT
|
||||
remote: https://github.com/dysania/onebox.git
|
||||
revision: 1323ec18966b6398acc290ff451e065d1ff2ecb3
|
||||
specs:
|
||||
onebox (1.1.0)
|
||||
hexpress (~> 1.2)
|
||||
moneta (~> 0.7)
|
||||
multi_json (~> 1.7)
|
||||
mustache (~> 0.99)
|
||||
nokogiri (~> 1.6.1)
|
||||
opengraph_parser (~> 0.2.3)
|
||||
|
||||
PATH
|
||||
remote: vendor/gems/discourse_plugin
|
||||
specs:
|
||||
|
@ -120,6 +132,7 @@ GEM
|
|||
guess_html_encoding (0.0.9)
|
||||
handlebars-source (1.1.2)
|
||||
hashie (2.0.5)
|
||||
hexpress (1.2.0)
|
||||
highline (1.6.20)
|
||||
hike (1.2.3)
|
||||
hiredis (0.4.5)
|
||||
|
@ -158,11 +171,12 @@ GEM
|
|||
metaclass (0.0.1)
|
||||
method_source (0.8.2)
|
||||
mime-types (1.25.1)
|
||||
mini_portile (0.5.1)
|
||||
mini_portile (0.5.2)
|
||||
minitest (4.7.5)
|
||||
mocha (0.14.0)
|
||||
metaclass (~> 0.0.1)
|
||||
mock_redis (0.9.0)
|
||||
moneta (0.7.20)
|
||||
msgpack (0.5.7)
|
||||
multi_json (1.8.2)
|
||||
multipart-post (1.2.0)
|
||||
|
@ -170,7 +184,7 @@ GEM
|
|||
net-scp (1.1.2)
|
||||
net-ssh (>= 2.6.5)
|
||||
net-ssh (2.7.0)
|
||||
nokogiri (1.6.0)
|
||||
nokogiri (1.6.1)
|
||||
mini_portile (~> 0.5.0)
|
||||
oauth (0.4.7)
|
||||
oauth2 (0.8.1)
|
||||
|
@ -208,6 +222,9 @@ GEM
|
|||
omniauth-twitter (1.0.1)
|
||||
multi_json (~> 1.3)
|
||||
omniauth-oauth (~> 1.0)
|
||||
opengraph_parser (0.2.3)
|
||||
addressable
|
||||
nokogiri
|
||||
openid-redis-store (0.0.2)
|
||||
redis
|
||||
ruby-openid
|
||||
|
@ -429,6 +446,7 @@ DEPENDENCIES
|
|||
omniauth-oauth2
|
||||
omniauth-openid
|
||||
omniauth-twitter
|
||||
onebox!
|
||||
openid-redis-store
|
||||
pg (= 0.15.1)
|
||||
pry-nav
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
require_dependency 'oneboxer'
|
||||
|
||||
class PostAnalyzer
|
||||
|
||||
def initialize(raw, topic_id)
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
# Post processing that we can do after a post has already been cooked.
|
||||
# For example, inserting the onebox content, or image sizes/thumbnails.
|
||||
|
||||
require_dependency "oneboxer"
|
||||
require_dependency 'url_helper'
|
||||
|
||||
class CookedPostProcessor
|
||||
|
|
136
lib/oneboxer.rb
136
lib/oneboxer.rb
|
@ -1,14 +1,5 @@
|
|||
require 'open-uri'
|
||||
require 'digest/sha1'
|
||||
|
||||
require_dependency 'oneboxer/base'
|
||||
require_dependency 'oneboxer/whitelist'
|
||||
Dir["#{Rails.root}/lib/oneboxer/*_onebox.rb"].each {|f|
|
||||
require_dependency(f.split('/')[-2..-1].join('/'))
|
||||
}
|
||||
|
||||
module Oneboxer
|
||||
extend Oneboxer::Base
|
||||
|
||||
|
||||
# keep reloaders happy
|
||||
unless defined? Oneboxer::Result
|
||||
|
@ -23,66 +14,26 @@ module Oneboxer
|
|||
end
|
||||
end
|
||||
|
||||
Dir["#{Rails.root}/lib/oneboxer/*_onebox.rb"].sort.each do |f|
|
||||
add_onebox "Oneboxer::#{Pathname.new(f).basename.to_s.gsub(/\.rb$/, '').classify}".constantize
|
||||
def self.preview(url, options=nil)
|
||||
options ||= {}
|
||||
Oneboxer.invalidate(url) if options[:invalidate_oneboxes]
|
||||
Onebox.preview(url, cache: Rails.cache).placeholder_html
|
||||
end
|
||||
|
||||
def self.default_expiry
|
||||
1.day
|
||||
def self.onebox(url, options=nil)
|
||||
options ||= {}
|
||||
Oneboxer.invalidate(url) if options[:invalidate_oneboxes]
|
||||
Onebox.preview(url, cache: Rails.cache).to_s
|
||||
end
|
||||
|
||||
def self.oneboxer_exists_for_url?(url)
|
||||
Whitelist.entry_for_url(url) || matchers.any? { |matcher| url =~ matcher.regexp }
|
||||
def self.oneboxer_exists_for_url?(url)
|
||||
Onebox.has_matcher?(url)
|
||||
end
|
||||
|
||||
# Return a oneboxer for a given URL
|
||||
def self.onebox_for_url(url)
|
||||
matchers.each do |matcher|
|
||||
regexp = matcher.regexp
|
||||
klass = matcher.klass
|
||||
|
||||
regexp = regexp.call if regexp.class == Proc
|
||||
return klass.new(url) if url =~ regexp
|
||||
end
|
||||
nil
|
||||
def self.invalidate(url)
|
||||
Rails.cache.delete(url)
|
||||
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)
|
||||
|
||||
if whitelist_entry.present?
|
||||
# TODO - only download HEAD section
|
||||
# TODO - sane timeout
|
||||
# TODO - FAIL if for any reason you are downloading more that 5000 bytes
|
||||
page_html = open(url).read
|
||||
if page_html.present?
|
||||
doc = Nokogiri::HTML(page_html)
|
||||
|
||||
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
|
||||
|
||||
# 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
|
||||
end
|
||||
|
||||
|
||||
# Parse URLs out of HTML, returning the document when finished.
|
||||
def self.each_onebox_link(string_or_doc)
|
||||
doc = string_or_doc
|
||||
|
@ -126,62 +77,5 @@ module Oneboxer
|
|||
Result.new(doc, changed)
|
||||
end
|
||||
|
||||
def self.cache_key_for(url)
|
||||
"onebox:#{Digest::SHA1.hexdigest(url)}"
|
||||
end
|
||||
|
||||
def self.preview_cache_key_for(url)
|
||||
"onebox:preview:#{Digest::SHA1.hexdigest(url)}"
|
||||
end
|
||||
|
||||
def self.render_from_cache(url)
|
||||
Rails.cache.read(cache_key_for(url))
|
||||
end
|
||||
|
||||
# Cache results from a onebox call
|
||||
def self.fetch_and_cache(url, args)
|
||||
contents, preview = onebox_nocache(url)
|
||||
return nil if contents.blank?
|
||||
|
||||
Rails.cache.write(cache_key_for(url), contents, expires_in: default_expiry)
|
||||
if preview.present?
|
||||
Rails.cache.write(preview_cache_key_for(url), preview, expires_in: default_expiry)
|
||||
end
|
||||
|
||||
[contents, preview]
|
||||
end
|
||||
|
||||
def self.invalidate(url)
|
||||
Rails.cache.delete(cache_key_for(url))
|
||||
end
|
||||
|
||||
def self.preview(url, args={})
|
||||
# Look for a preview
|
||||
cached = Rails.cache.read(preview_cache_key_for(url)) unless args[:no_cache].present?
|
||||
return cached if cached.present?
|
||||
|
||||
# Try the full version
|
||||
cached = render_from_cache(url)
|
||||
return cached if cached.present?
|
||||
|
||||
# If that fails, look it up
|
||||
contents, cached = fetch_and_cache(url, args)
|
||||
return cached if cached.present?
|
||||
contents
|
||||
end
|
||||
|
||||
# Return the cooked content for a url, caching the result for performance
|
||||
def self.onebox(url, args={})
|
||||
|
||||
if args[:invalidate_oneboxes]
|
||||
# Remove the onebox from the cache
|
||||
Oneboxer.invalidate(url)
|
||||
else
|
||||
contents = render_from_cache(url)
|
||||
return contents if contents.present?
|
||||
end
|
||||
|
||||
fetch_and_cache(url, args)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
|
|
@ -1,12 +0,0 @@
|
|||
require_dependency 'oneboxer/base_onebox'
|
||||
|
||||
module Oneboxer
|
||||
class AudioOnebox < BaseOnebox
|
||||
|
||||
matcher /^https?:\/\/.*\.mp3$/
|
||||
|
||||
def onebox
|
||||
"<audio controls><source src='#{@url}'><a href='#{@url}'>#{@url}</a></audio>"
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,17 +0,0 @@
|
|||
require_dependency 'oneboxer/base_onebox'
|
||||
|
||||
module Oneboxer
|
||||
class FlashVideoOnebox < BaseOnebox
|
||||
|
||||
matcher /^https?:\/\/.*\.(swf|flv)$/
|
||||
|
||||
def onebox
|
||||
if SiteSetting.enable_flash_video_onebox
|
||||
"<object width='100%' height='100%' wmode='opaque'><param name='#{@url}' value='#{@url}'><embed src='#{@url}' width='100%' height='100%' wmode='opaque'></embed></object>"
|
||||
else
|
||||
"<a href='#{@url}'>#{@url}</a>"
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
|
@ -1,13 +0,0 @@
|
|||
require_dependency 'oneboxer/base_onebox'
|
||||
|
||||
module Oneboxer
|
||||
class ImageOnebox < BaseOnebox
|
||||
|
||||
matcher /^(https?:)?\/\/.+\.(png|jpg|jpeg|gif|bmp|tif|tiff)$/i
|
||||
|
||||
def onebox
|
||||
Oneboxer::BaseOnebox.image_html(@url, nil, @url)
|
||||
end
|
||||
|
||||
end
|
||||
end
|
|
@ -1,13 +0,0 @@
|
|||
require_dependency 'oneboxer/base_onebox'
|
||||
|
||||
module Oneboxer
|
||||
class VideoOnebox < BaseOnebox
|
||||
|
||||
matcher /^https?:\/\/.*\.(mov|mp4)$/
|
||||
|
||||
def onebox
|
||||
"<video controls><source src='#{@url}'><a href='#{@url}'>#{@url}</a></video>"
|
||||
end
|
||||
|
||||
end
|
||||
end
|
|
@ -1,44 +0,0 @@
|
|||
require_dependency 'oneboxer/handlebars_onebox'
|
||||
|
||||
module Oneboxer
|
||||
class AmazonOnebox < HandlebarsOnebox
|
||||
|
||||
matcher /^https?:\/\/(?:www\.)?amazon\.(com\.au|com|br|mx|ca|at|cn|fr|de|it|es|in|co\.jp|co\.uk)\/.*$/
|
||||
favicon 'amazon.png'
|
||||
|
||||
def template
|
||||
template_path("simple_onebox")
|
||||
end
|
||||
|
||||
# Use the mobile version of the site
|
||||
def translate_url
|
||||
# If we're already mobile don't translate the url
|
||||
return @url if @url =~ /https?:\/\/www\.amazon\.com\/gp\/aw\/d\//
|
||||
|
||||
m = @url.match(/(?:d|g)p\/(?:product\/)?(?<id>[^\/]+)(?:\/|$)/mi)
|
||||
return "http://www.amazon.com/gp/aw/d/" + URI::encode(m[:id]) if m.present?
|
||||
@url
|
||||
end
|
||||
|
||||
def parse(data)
|
||||
html_doc = Nokogiri::HTML(data)
|
||||
|
||||
result = {}
|
||||
result[:title] = html_doc.at("h1")
|
||||
result[:title] = result[:title].inner_text.strip if result[:title].present?
|
||||
|
||||
image = html_doc.at("#main-image")
|
||||
result[:image] = image['src'] if image
|
||||
|
||||
result[:by_info] = html_doc.at("#by-line")
|
||||
result[:by_info] = BaseOnebox.remove_whitespace(BaseOnebox.replace_tags_with_spaces(result[:by_info].inner_html)) if result[:by_info].present?
|
||||
|
||||
# not many CSS selectors to work with here, go with the first span in about-item ID
|
||||
summary = html_doc.at("#about-item span")
|
||||
result[:text] = summary.inner_html if summary.present?
|
||||
|
||||
result
|
||||
end
|
||||
|
||||
end
|
||||
end
|
|
@ -1,35 +0,0 @@
|
|||
require_dependency 'oneboxer/handlebars_onebox'
|
||||
|
||||
module Oneboxer
|
||||
class AndroidAppStoreOnebox < HandlebarsOnebox
|
||||
|
||||
matcher /^https?:\/\/play\.google\.com\/.+$/
|
||||
favicon 'google_play.png'
|
||||
|
||||
def template
|
||||
template_path('simple_onebox')
|
||||
end
|
||||
|
||||
def parse(data)
|
||||
|
||||
html_doc = Nokogiri::HTML(data)
|
||||
|
||||
result = {}
|
||||
|
||||
m = html_doc.at("h1.doc-banner-title")
|
||||
result[:title] = m.inner_text if m
|
||||
|
||||
m = html_doc.at("div#doc-original-text")
|
||||
if m
|
||||
result[:text] = BaseOnebox.replace_tags_with_spaces(m.inner_html)
|
||||
result[:text] = result[:text][0..MAX_TEXT]
|
||||
end
|
||||
|
||||
m = html_doc.at("div.doc-banner-icon img")
|
||||
result[:image] = m['src'] if m
|
||||
|
||||
result
|
||||
end
|
||||
|
||||
end
|
||||
end
|
|
@ -1,37 +0,0 @@
|
|||
require_dependency 'oneboxer/handlebars_onebox'
|
||||
|
||||
module Oneboxer
|
||||
class AppleAppOnebox < HandlebarsOnebox
|
||||
|
||||
matcher /^https?:\/\/itunes\.apple\.com\/.+$/
|
||||
favicon 'apple.png'
|
||||
|
||||
# Don't masquerade as mobile
|
||||
def http_params
|
||||
{}
|
||||
end
|
||||
|
||||
def template
|
||||
template_path('simple_onebox')
|
||||
end
|
||||
|
||||
def parse(data)
|
||||
|
||||
html_doc = Nokogiri::HTML(data)
|
||||
|
||||
result = {}
|
||||
|
||||
m = html_doc.at("h1")
|
||||
result[:title] = m.inner_text if m
|
||||
|
||||
m = html_doc.at("h4 ~ p")
|
||||
result[:text] = m.inner_text[0..MAX_TEXT] if m
|
||||
|
||||
m = html_doc.at(".product img.artwork")
|
||||
result[:image] = m['src'] if m
|
||||
|
||||
result
|
||||
end
|
||||
|
||||
end
|
||||
end
|
|
@ -1,58 +0,0 @@
|
|||
module Oneboxer
|
||||
|
||||
class << self
|
||||
def parse_open_graph(doc)
|
||||
result = {}
|
||||
|
||||
%w(title type image url description image:width image:height).each do |prop|
|
||||
node = doc.at("/html/head/meta[@property='og:#{prop}']")
|
||||
result[prop] = (node['content'] || node['value']) if node
|
||||
end
|
||||
|
||||
# If there's no title, try using the page's title
|
||||
if result['title'].blank?
|
||||
result['title'] = doc.title
|
||||
end
|
||||
|
||||
# If there's no description, try and get one from the meta tags
|
||||
if result['description'].blank?
|
||||
node = doc.at("/html/head/meta[@name='description']")
|
||||
result['description'] = node['content'] if node
|
||||
end
|
||||
if result['description'].blank?
|
||||
node = doc.at("/html/head/meta[@name='Description']")
|
||||
result['description'] = node['content'] if node
|
||||
end
|
||||
|
||||
%w(image:width image:height).each do |prop|
|
||||
# Some sane max width
|
||||
if result[prop] && result[prop].to_i < 100
|
||||
result[prop.sub(":","_")] = result[prop]
|
||||
end
|
||||
result[prop] = nil
|
||||
end
|
||||
|
||||
result
|
||||
end
|
||||
end
|
||||
|
||||
class Matcher
|
||||
attr_reader :regexp, :klass
|
||||
|
||||
def initialize(klass)
|
||||
@klass = klass
|
||||
@regexp = klass.regexp
|
||||
end
|
||||
end
|
||||
|
||||
module Base
|
||||
def matchers
|
||||
@matchers ||= []
|
||||
end
|
||||
|
||||
def add_onebox(klass)
|
||||
matchers << Matcher.new(klass)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
|
@ -1,63 +0,0 @@
|
|||
require 'open-uri'
|
||||
|
||||
module Oneboxer
|
||||
|
||||
class BaseOnebox
|
||||
|
||||
class << self
|
||||
attr_accessor :regexp
|
||||
attr_accessor :favicon_file
|
||||
|
||||
def matcher(regexp=nil,&blk)
|
||||
self.regexp = regexp || blk
|
||||
end
|
||||
|
||||
def favicon(favicon_file)
|
||||
self.favicon_file = "favicons/#{favicon_file}"
|
||||
end
|
||||
|
||||
def remove_whitespace(s)
|
||||
s.gsub /\n/, ''
|
||||
end
|
||||
|
||||
def image_html(url, title, page_url)
|
||||
"<a href='#{page_url}' target='_blank'><img src='#{url}' alt='#{title}'></a>"
|
||||
end
|
||||
|
||||
def replace_tags_with_spaces(s)
|
||||
s.gsub /<[^>]+>/, ' '
|
||||
end
|
||||
|
||||
def uriencode(val)
|
||||
URI.escape(val, Regexp.new("[^#{URI::PATTERN::UNRESERVED}]"))
|
||||
end
|
||||
|
||||
# Replace any occurence of a HTTP or HTTPS URL in the string with the protocol-agnostic variant
|
||||
def replace_agnostic(var)
|
||||
var.gsub! /https?:\/\//, '//' if var.is_a? String
|
||||
end
|
||||
|
||||
# Add wmode=opaque to the iframe src URL so that the flash player is rendered within the document flow instead of on top
|
||||
def append_embed_wmode(var)
|
||||
var.gsub! /(src="[^"]+)/, '\1&wmode=opaque"' if var.is_a? String
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
def initialize(url, opts={})
|
||||
@url = url
|
||||
@opts = opts
|
||||
end
|
||||
|
||||
def translate_url
|
||||
@url
|
||||
end
|
||||
|
||||
def nice_host
|
||||
host = URI.parse(@url).host
|
||||
host.nil? ? '' : host.gsub('www.', '')
|
||||
rescue URI::InvalidURIError
|
||||
'' # In case there is a problem with the URL, we just won't set the host
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,13 +0,0 @@
|
|||
require_dependency 'oneboxer/oembed_onebox'
|
||||
|
||||
module Oneboxer
|
||||
class BliptvOnebox < OembedOnebox
|
||||
|
||||
matcher /^https?\:\/\/blip\.tv\/.+$/
|
||||
|
||||
def oembed_endpoint
|
||||
"http://blip.tv/oembed/?url=#{BaseOnebox.uriencode(@url)}&width=300"
|
||||
end
|
||||
|
||||
end
|
||||
end
|
|
@ -1,26 +0,0 @@
|
|||
require 'net/http'
|
||||
require_dependency 'oneboxer/base_onebox'
|
||||
|
||||
module Oneboxer
|
||||
class ClassicGoogleMapsOnebox < BaseOnebox
|
||||
|
||||
matcher /^(https?:)?\/\/(maps\.google\.[\w.]{2,}|goo\.gl)\/maps?.+$/
|
||||
|
||||
def onebox
|
||||
@url = get_long_url(@url) if @url.include?("//goo.gl/maps/")
|
||||
"<iframe src='#{@url}&output=embed' width='690px' height='400px' frameborder='0' style='border:0'></iframe>" if @url.present?
|
||||
end
|
||||
|
||||
def get_long_url(url)
|
||||
uri = URI(url)
|
||||
http = Net::HTTP.start(uri.host, uri.port)
|
||||
http.open_timeout = 1
|
||||
http.read_timeout = 1
|
||||
response = http.head(uri.path)
|
||||
response["Location"] if response.code == "301"
|
||||
rescue
|
||||
nil
|
||||
end
|
||||
|
||||
end
|
||||
end
|
|
@ -1,14 +0,0 @@
|
|||
require_dependency 'oneboxer/oembed_onebox'
|
||||
|
||||
module Oneboxer
|
||||
class ClikthroughOnebox < OembedOnebox
|
||||
|
||||
matcher /^https?:\/\/(?:www\.)?clikthrough\.com\/theater\/video\/\d+$/
|
||||
|
||||
def oembed_endpoint
|
||||
"http://clikthrough.com/services/oembed?url=#{BaseOnebox.uriencode(@url)}"
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
end
|
|
@ -1,14 +0,0 @@
|
|||
require_dependency 'oneboxer/oembed_onebox'
|
||||
|
||||
module Oneboxer
|
||||
class CollegeHumorOnebox < OembedOnebox
|
||||
|
||||
matcher /^https?\:\/\/www\.collegehumor\.com\/video\/.*$/
|
||||
|
||||
def oembed_endpoint
|
||||
"http://www.collegehumor.com/oembed.json?url=#{BaseOnebox.uriencode(@url)}"
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
end
|
|
@ -1,14 +0,0 @@
|
|||
require_dependency 'oneboxer/oembed_onebox'
|
||||
|
||||
module Oneboxer
|
||||
class DailymotionOnebox < OembedOnebox
|
||||
|
||||
matcher /^https?:\/\/(?:www\.)?dailymotion\.com\/.+$/
|
||||
|
||||
def oembed_endpoint
|
||||
"http://www.dailymotion.com/api/oembed/?url=#{BaseOnebox.uriencode(@url)}"
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
end
|
|
@ -1,97 +0,0 @@
|
|||
require_dependency 'oneboxer/oembed_onebox'
|
||||
require_dependency 'freedom_patches/rails4'
|
||||
|
||||
module Oneboxer
|
||||
class DiscourseLocalOnebox < BaseOnebox
|
||||
include ActionView::Helpers::DateHelper
|
||||
|
||||
matcher do
|
||||
Regexp.new "^#{Discourse.base_url.gsub(".","\\.")}.*$", true
|
||||
end
|
||||
|
||||
def onebox
|
||||
uri = URI::parse(@url)
|
||||
route = Rails.application.routes.recognize_path(uri.path)
|
||||
|
||||
args = {original_url: @url}
|
||||
|
||||
# Figure out what kind of onebox to show based on the URL
|
||||
case route[:controller]
|
||||
# when 'users'
|
||||
# user = User.where(username_lower: route[:username].downcase).first
|
||||
# return nil unless user
|
||||
|
||||
# return @url unless Guardian.new.can_see?(user)
|
||||
|
||||
# args.merge! avatar: PrettyText.avatar_img(user.avatar_template, 'tiny'), username: user.username
|
||||
# args[:bio] = user.bio_cooked if user.bio_cooked.present?
|
||||
|
||||
# @template = 'user'
|
||||
when 'topics'
|
||||
|
||||
linked = "<a href='#{@url}'>#{@url}</a>"
|
||||
if route[:post_number].present? && route[:post_number].to_i > 1
|
||||
# Post Link
|
||||
post = Post.where(topic_id: route[:topic_id], post_number: route[:post_number].to_i).first
|
||||
return linked unless post
|
||||
return linked unless Guardian.new.can_see?(post)
|
||||
|
||||
topic = post.topic
|
||||
slug = Slug.for(topic.title)
|
||||
|
||||
excerpt = post.excerpt(SiteSetting.post_onebox_maxlength)
|
||||
excerpt.gsub!("\n"," ")
|
||||
# hack to make it render for now
|
||||
excerpt.gsub!("[/quote]", "[quote]")
|
||||
quote = "[quote=\"#{post.user.username}, topic:#{topic.id}, slug:#{slug}, post:#{post.post_number}\"]#{excerpt}[/quote]"
|
||||
|
||||
cooked = PrettyText.cook(quote)
|
||||
return cooked
|
||||
|
||||
else
|
||||
# Topic Link
|
||||
topic = Topic.where(id: route[:topic_id].to_i).includes(:user).first
|
||||
return linked unless topic
|
||||
return linked unless Guardian.new.can_see?(topic)
|
||||
|
||||
post = topic.posts.first
|
||||
|
||||
posters = topic.posters_summary.map do |p|
|
||||
{
|
||||
username: p[:user].username,
|
||||
avatar: PrettyText.avatar_img(p[:user].avatar_template, 'tiny'),
|
||||
description: p[:description],
|
||||
extras: p[:extras]
|
||||
}
|
||||
end
|
||||
|
||||
category = topic.category
|
||||
if category
|
||||
category = "<a href=\"/category/#{category.slug}\" class=\"badge badge-category\" style=\"background-color: ##{category.color}; color: ##{category.text_color}\">#{category.name}</a>"
|
||||
end
|
||||
|
||||
quote = post.excerpt(SiteSetting.post_onebox_maxlength)
|
||||
args.merge! title: topic.title,
|
||||
avatar: PrettyText.avatar_img(topic.user.avatar_template, 'tiny'),
|
||||
posts_count: topic.posts_count,
|
||||
last_post: FreedomPatches::Rails4.time_ago_in_words(topic.last_posted_at, false, scope: :'datetime.distance_in_words_verbose'),
|
||||
age: FreedomPatches::Rails4.time_ago_in_words(topic.created_at, false, scope: :'datetime.distance_in_words_verbose'),
|
||||
views: topic.views,
|
||||
posters: posters,
|
||||
quote: quote,
|
||||
category: category,
|
||||
topic: topic.id
|
||||
|
||||
@template = 'topic'
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
return nil unless @template
|
||||
Mustache.render(File.read("#{Rails.root}/lib/oneboxer/templates/discourse_#{@template}_onebox.handlebars"), args)
|
||||
rescue ActionController::RoutingError
|
||||
nil
|
||||
end
|
||||
|
||||
end
|
||||
end
|
|
@ -1,14 +0,0 @@
|
|||
require_dependency 'oneboxer/oembed_onebox'
|
||||
|
||||
module Oneboxer
|
||||
class DotsubOnebox < OembedOnebox
|
||||
|
||||
matcher /^https?:\/\/(?:www\.)?dotsub\.com\/.+$/
|
||||
|
||||
def oembed_endpoint
|
||||
"http://dotsub.com/services/oembed?url=#{BaseOnebox.uriencode(@url)}"
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
end
|
|
@ -1,24 +0,0 @@
|
|||
require_dependency 'oneboxer/oembed_onebox'
|
||||
|
||||
module Oneboxer
|
||||
class FlickrOnebox < BaseOnebox
|
||||
|
||||
matcher /^https?\:\/\/.*\.flickr\.com\/.*$/
|
||||
|
||||
def onebox
|
||||
|
||||
page_html = open(@url).read
|
||||
return nil if page_html.blank?
|
||||
doc = Nokogiri::HTML(page_html)
|
||||
|
||||
# Flikrs oembed just stopped returning images for no reason. Let's use opengraph instead.
|
||||
open_graph = Oneboxer.parse_open_graph(doc)
|
||||
|
||||
# A site is supposed to supply all the basic og attributes, but some don't (like deviant art)
|
||||
# If it just has image and no title, embed it as an image.
|
||||
return BaseOnebox.image_html(open_graph['image'], nil, @url) if open_graph['image'].present?
|
||||
nil
|
||||
end
|
||||
|
||||
end
|
||||
end
|
|
@ -1,10 +0,0 @@
|
|||
require_dependency 'oneboxer/oembed_onebox'
|
||||
|
||||
module Oneboxer
|
||||
class FunnyOrDieOnebox < OembedOnebox
|
||||
matcher /^https?\:\/\/(www\.)?funnyordie\.com\/videos\/.*$/
|
||||
def oembed_endpoint
|
||||
"http://www.funnyordie.com/oembed.json?url=#{BaseOnebox.uriencode(@url)}"
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,29 +0,0 @@
|
|||
require_dependency 'oneboxer/handlebars_onebox'
|
||||
|
||||
module Oneboxer
|
||||
class GistOnebox < HandlebarsOnebox
|
||||
|
||||
matcher /^https?:\/\/gist\.github\.com/
|
||||
favicon 'github.png'
|
||||
|
||||
def translate_url
|
||||
m = @url.match(/gist\.github\.com\/([^\/]+\/)?(?<id>[0-9a-f]+)/mi)
|
||||
return "https://api.github.com/gists/#{m[:id]}" if m
|
||||
end
|
||||
|
||||
def parse(data)
|
||||
parsed = JSON.parse(data)
|
||||
desc = parsed['description']
|
||||
if desc.length > 120
|
||||
desc = desc[0..120]
|
||||
desc << "..."
|
||||
end
|
||||
result = {files: [], title: desc}
|
||||
parsed['files'].each do |filename, attrs|
|
||||
result[:files] << {filename: filename}.merge!(attrs)
|
||||
end
|
||||
result
|
||||
end
|
||||
|
||||
end
|
||||
end
|
|
@ -1,49 +0,0 @@
|
|||
require_dependency 'oneboxer/handlebars_onebox'
|
||||
|
||||
module Oneboxer
|
||||
class GithubBlobOnebox < HandlebarsOnebox
|
||||
|
||||
matcher /^https?:\/\/(?:www\.)?github\.com\/[^\/]+\/[^\/]+\/blob\/.*/
|
||||
favicon 'github.png'
|
||||
|
||||
def translate_url
|
||||
m = @url.match(/github\.com\/(?<user>[^\/]+)\/(?<repo>[^\/]+)\/blob\/(?<sha1>[^\/]+)\/(?<file>[^#]+)(#(L(?<from>[^-]*)(-L(?<to>.*))?))?/mi)
|
||||
if m
|
||||
@from = (m[:from] || -1).to_i
|
||||
@to = (m[:to] || -1).to_i
|
||||
@file = m[:file]
|
||||
return "https://raw.github.com/#{m[:user]}/#{m[:repo]}/#{m[:sha1]}/#{m[:file]}"
|
||||
end
|
||||
nil
|
||||
end
|
||||
|
||||
def parse(data)
|
||||
|
||||
if @from > 0
|
||||
if @to < 0
|
||||
@from = @from - 10
|
||||
@to = @from + 20
|
||||
end
|
||||
if @to > @from
|
||||
data = data.split("\n")[@from..@to].join("\n")
|
||||
end
|
||||
end
|
||||
|
||||
extension = @file.split(".")[-1]
|
||||
@lang = case extension
|
||||
when "rb" then "ruby"
|
||||
when "js" then "javascript"
|
||||
else extension
|
||||
end
|
||||
|
||||
truncated = false
|
||||
if data.length > SiteSetting.onebox_max_chars
|
||||
data = data[0..SiteSetting.onebox_max_chars-1]
|
||||
truncated = true
|
||||
end
|
||||
|
||||
{content: data, truncated: truncated}
|
||||
end
|
||||
|
||||
end
|
||||
end
|
|
@ -1,24 +0,0 @@
|
|||
require_dependency 'oneboxer/handlebars_onebox'
|
||||
|
||||
module Oneboxer
|
||||
class GithubCommitOnebox < HandlebarsOnebox
|
||||
|
||||
matcher /^https?:\/\/(?:www\.)?github\.com\/[^\/]+\/[^\/]+\/commit\/.+/
|
||||
favicon 'github.png'
|
||||
|
||||
def translate_url
|
||||
m = @url.match(/github\.com\/(?<owner>[^\/]+)\/(?<repo>[^\/]+)\/commit\/(?<sha>[^\/]+)/mi)
|
||||
return "https://api.github.com/repos/#{m[:owner]}/#{m[:repo]}/commits/#{m[:sha]}" if m.present?
|
||||
@url
|
||||
end
|
||||
|
||||
def parse(data)
|
||||
result = JSON.parse(data)
|
||||
|
||||
result['commit_date'] = Time.parse(result['commit']['author']['date']).strftime("%I:%M%p - %d %b %y")
|
||||
|
||||
result
|
||||
end
|
||||
|
||||
end
|
||||
end
|
|
@ -1,26 +0,0 @@
|
|||
require_dependency 'oneboxer/handlebars_onebox'
|
||||
|
||||
module Oneboxer
|
||||
class GithubPullrequestOnebox < HandlebarsOnebox
|
||||
|
||||
matcher /^https?:\/\/(?:www\.)?github\.com\/[^\/]+\/[^\/]+\/pull\/.+/
|
||||
favicon 'github.png'
|
||||
|
||||
def translate_url
|
||||
@url.match(
|
||||
/github\.com\/(?<owner>[^\/]+)\/(?<repo>[^\/]+)\/pull\/(?<number>[^\/]+)/mi
|
||||
) do |match|
|
||||
"https://api.github.com/repos/#{match[:owner]}/#{match[:repo]}/pulls/#{match[:number]}"
|
||||
end
|
||||
end
|
||||
|
||||
def parse(data)
|
||||
result = JSON.parse(data)
|
||||
|
||||
result['created_at'] =
|
||||
Time.parse(result['created_at']).strftime("%I:%M%p - %d %b %y")
|
||||
|
||||
result
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,61 +0,0 @@
|
|||
require 'open-uri'
|
||||
require_dependency 'oneboxer/base_onebox'
|
||||
|
||||
module Oneboxer
|
||||
|
||||
class HandlebarsOnebox < BaseOnebox
|
||||
|
||||
unless defined? MAX_TEXT
|
||||
MAX_TEXT = 500
|
||||
end
|
||||
|
||||
def self.template_path(template_name)
|
||||
"#{Rails.root}/lib/oneboxer/templates/#{template_name}.handlebars"
|
||||
end
|
||||
|
||||
def template_path(template_name)
|
||||
HandlebarsOnebox.template_path(template_name)
|
||||
end
|
||||
|
||||
def template
|
||||
template_name = self.class.name.underscore
|
||||
template_name.gsub!(/oneboxer\//, '')
|
||||
template_path(template_name)
|
||||
end
|
||||
|
||||
def default_url
|
||||
"<a href='#{@url}' target='_blank'>#{@url}</a>"
|
||||
end
|
||||
|
||||
def http_params
|
||||
{'User-Agent' => 'Mozilla/5.0 (iPhone; CPU iPhone OS 5_0_1 like Mac OS X) AppleWebKit/534.46 (KHTML, like Gecko) Version/5.1 Mobile/9A405 Safari/7534.48.3'}
|
||||
end
|
||||
|
||||
def fetch_html
|
||||
open(translate_url, http_params).read
|
||||
end
|
||||
|
||||
def onebox
|
||||
html = fetch_html
|
||||
args = parse(html)
|
||||
return default_url unless args.present?
|
||||
|
||||
args[:original_url] = @url
|
||||
args[:lang] = @lang || ""
|
||||
args[:favicon] = ActionController::Base.helpers.asset_path(self.class.favicon_file, digest: false) if self.class.favicon_file.present?
|
||||
args[:host] = nice_host
|
||||
|
||||
HandlebarsOnebox.generate_onebox(template,args)
|
||||
rescue => ex
|
||||
# If there's an exception, just embed the link
|
||||
raise ex if Rails.env.development?
|
||||
default_url
|
||||
end
|
||||
|
||||
def self.generate_onebox(template, args={})
|
||||
Mustache.render(File.read(template), args)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
|
@ -1,13 +0,0 @@
|
|||
require_dependency 'oneboxer/oembed_onebox'
|
||||
|
||||
module Oneboxer
|
||||
class HuluOnebox < OembedOnebox
|
||||
|
||||
matcher /^https?\:\/\/www\.hulu\.com\/watch\/.*$/
|
||||
|
||||
def oembed_endpoint
|
||||
"http://www.hulu.com/api/oembed.json?url=#{BaseOnebox.uriencode(@url)}"
|
||||
end
|
||||
|
||||
end
|
||||
end
|
|
@ -1,29 +0,0 @@
|
|||
require 'open-uri'
|
||||
require_dependency 'oneboxer/base_onebox'
|
||||
|
||||
module Oneboxer
|
||||
class ImgurOnebox < BaseOnebox
|
||||
|
||||
matcher /^https?\:\/\/imgur\.com\/.*$/
|
||||
|
||||
def translate_url
|
||||
m = @url.match(/\/gallery\/(?<hash>[^\/]+)/mi)
|
||||
return "http://api.imgur.com/2/image/#{URI::encode(m[:hash])}.json" if m.present?
|
||||
|
||||
m = @url.match(/imgur\.com\/(?<hash>[^\/]+)/mi)
|
||||
return "http://api.imgur.com/2/image/#{URI::encode(m[:hash])}.json" if m.present?
|
||||
|
||||
nil
|
||||
end
|
||||
|
||||
def onebox
|
||||
url = translate_url
|
||||
return @url if url.blank?
|
||||
|
||||
parsed = JSON.parse(open(translate_url).read)
|
||||
image = parsed['image']
|
||||
BaseOnebox.image_html(image['links']['original'], image['image']['caption'], image['links']['imgur_page'])
|
||||
end
|
||||
|
||||
end
|
||||
end
|
|
@ -1,14 +0,0 @@
|
|||
require_dependency 'oneboxer/oembed_onebox'
|
||||
|
||||
module Oneboxer
|
||||
class KinomapOnebox < OembedOnebox
|
||||
|
||||
matcher /^https?:\/\/(?:www\.)?kinomap\.com/
|
||||
|
||||
def oembed_endpoint
|
||||
"http://www.kinomap.com/oembed?url=#{BaseOnebox.uriencode(@url)}&format=json"
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
end
|
|
@ -1,14 +0,0 @@
|
|||
require_dependency 'oneboxer/oembed_onebox'
|
||||
|
||||
module Oneboxer
|
||||
class NfbOnebox < OembedOnebox
|
||||
|
||||
matcher /^https?:\/\/(?:www\.)?nfb\.ca\/film\/[-\w]+\/?/
|
||||
|
||||
def oembed_endpoint
|
||||
"http://www.nfb.ca/remote/services/oembed/?url=#{BaseOnebox.uriencode(@url)}&format=json"
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
end
|
|
@ -1,42 +0,0 @@
|
|||
require 'open-uri'
|
||||
require_dependency 'oneboxer/handlebars_onebox'
|
||||
|
||||
module Oneboxer
|
||||
|
||||
class OembedOnebox < HandlebarsOnebox
|
||||
|
||||
def oembed_endpoint
|
||||
@url
|
||||
end
|
||||
|
||||
def template
|
||||
template_path('oembed_onebox')
|
||||
end
|
||||
|
||||
def onebox
|
||||
parsed = JSON.parse(open(oembed_endpoint).read)
|
||||
|
||||
# If it's a video, just embed the iframe
|
||||
if %w(video rich).include?(parsed['type'])
|
||||
# Return a preview of the thumbnail url, since iframes don't do well on previews
|
||||
preview = nil
|
||||
preview = "<img src='#{parsed['thumbnail_url']}'>" if parsed['thumbnail_url'].present?
|
||||
return [parsed['html'], preview]
|
||||
end
|
||||
|
||||
if %w(image photo).include?(parsed['type'])
|
||||
return BaseOnebox.image_html(parsed['url'] || parsed['thumbnail_url'], parsed['title'], parsed['web_page'] || @url)
|
||||
end
|
||||
|
||||
parsed['original_url'] = parsed['url']
|
||||
parsed['html'] ||= parsed['abstract']
|
||||
parsed['host'] = nice_host
|
||||
|
||||
Mustache.render(File.read(template), parsed)
|
||||
rescue OpenURI::HTTPError
|
||||
nil
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
|
@ -1,29 +0,0 @@
|
|||
require_dependency 'oneboxer/handlebars_onebox'
|
||||
|
||||
module Oneboxer
|
||||
class OpenGraphOnebox < HandlebarsOnebox
|
||||
|
||||
def template
|
||||
template_path('simple_onebox')
|
||||
end
|
||||
|
||||
def onebox
|
||||
# We expect to have the options we need already
|
||||
return nil unless @opts.present?
|
||||
|
||||
# A site is supposed to supply all the basic og attributes, but some don't (like deviant art)
|
||||
# If it just has image and no title, embed it as an image.
|
||||
return BaseOnebox.image_html(@opts['image'], nil, @url) if @opts['image'].present? && @opts['title'].blank?
|
||||
|
||||
@opts['title'] ||= @opts['description']
|
||||
return nil if @opts['title'].blank?
|
||||
|
||||
@opts[:original_url] = @url
|
||||
@opts[:text] = @opts['description']
|
||||
@opts[:unsafe] = true
|
||||
@opts[:host] = nice_host
|
||||
|
||||
Mustache.render(File.read(template), @opts)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,13 +0,0 @@
|
|||
require_dependency 'oneboxer/oembed_onebox'
|
||||
|
||||
module Oneboxer
|
||||
class QikOnebox < OembedOnebox
|
||||
|
||||
matcher /^https?\:\/\/qik\.com\/video\/.*$/
|
||||
|
||||
def oembed_endpoint
|
||||
"http://qik.com/api/oembed.json?url=#{BaseOnebox.uriencode(@url)}"
|
||||
end
|
||||
|
||||
end
|
||||
end
|
|
@ -1,13 +0,0 @@
|
|||
require_dependency 'oneboxer/oembed_onebox'
|
||||
|
||||
module Oneboxer
|
||||
class RevisionOnebox < OembedOnebox
|
||||
|
||||
matcher /^http\:\/\/(.*\.)?revision3\.com\/.*$/
|
||||
|
||||
def oembed_endpoint
|
||||
"http://revision3.com/api/oembed/?url=#{BaseOnebox.uriencode(@url)}&format=json"
|
||||
end
|
||||
|
||||
end
|
||||
end
|
|
@ -1,75 +0,0 @@
|
|||
require_dependency 'oneboxer/handlebars_onebox'
|
||||
|
||||
module Oneboxer
|
||||
class RottentomatoesOnebox < HandlebarsOnebox
|
||||
|
||||
# keep reloaders happy
|
||||
unless defined? SYNOPSIS_MAX_TEXT
|
||||
SYNOPSIS_MAX_TEXT = 370
|
||||
ROTTEN_IMG = 'http://images.rottentomatoescdn.com/images/icons/rt.rotten.med.png'
|
||||
FRESH_IMG = 'http://images.rottentomatoescdn.com/images/icons/rt.fresh.med.png'
|
||||
POPCORN_IMG = 'http://images.rottentomatoescdn.com/images/icons/popcorn_27x31.png'
|
||||
end
|
||||
|
||||
matcher /^http:\/\/(?:www\.)?rottentomatoes\.com(\/mobile)?\/m\/.*$/
|
||||
favicon 'rottentomatoes.png'
|
||||
|
||||
def http_params
|
||||
{'User-Agent' => 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_0) AppleWebKit/537.31 (KHTML, like Gecko) Chrome/26.0.1410.65 Safari/537.31' }
|
||||
end
|
||||
|
||||
def template
|
||||
template_path('rottentomatoes_onebox')
|
||||
end
|
||||
|
||||
def translate_url
|
||||
m = @url.match(/^http:\/\/(?:www\.)?rottentomatoes\.com(\/mobile)?\/m\/(?<movie>.*)$/mi)
|
||||
"http://rottentomatoes.com/mobile/m/#{m[:movie]}"
|
||||
end
|
||||
|
||||
def parse(data)
|
||||
html_doc = Nokogiri::HTML(data)
|
||||
|
||||
result = {}
|
||||
|
||||
result[:title] = html_doc.at('h1').content
|
||||
result[:poster] = html_doc.at_css('.poster img')['src']
|
||||
|
||||
synopsis = html_doc.at_css('#movieSynopsis').content.squish
|
||||
synopsis.gsub!(/\$\(function\(\).+$/, '')
|
||||
result[:synopsis] = (synopsis.length > SYNOPSIS_MAX_TEXT ? "#{synopsis[0..SYNOPSIS_MAX_TEXT]}..." : synopsis)
|
||||
|
||||
result[:verdict_percentage], result[:user_percentage] = html_doc.css('.rtscores .rating .percentage span').map(&:content)
|
||||
result[:popcorn_image] = POPCORN_IMG
|
||||
if html_doc.at_css('.rtscores .rating .splat')
|
||||
result[:verdict_image] = ROTTEN_IMG
|
||||
elsif html_doc.at_css('.rtscores .rating .tomato')
|
||||
result[:verdict_image] = FRESH_IMG
|
||||
end
|
||||
|
||||
result[:cast] = html_doc.css('.summary .actors a').map(&:content).join(", ")
|
||||
|
||||
html_doc.css('#movieInfo .info').map(&:inner_html).each do |element|
|
||||
case
|
||||
when element.include?('Director:') then result[:director] = clean_up_info(element)
|
||||
when element.include?('Rated:') then result[:rated] = clean_up_info(element)
|
||||
when element.include?('Running Time:') then result[:running_time] = clean_up_info(element)
|
||||
when element.include?('DVD Release:')
|
||||
result[:release_date] = clean_up_info(element)
|
||||
result[:release_type] = 'DVD'
|
||||
# Only show the theater release date if there is no DVD release date
|
||||
when element.include?('Theater Release:') && !result[:release_type]
|
||||
result[:release_date] = clean_up_info(element)
|
||||
result[:release_type] = 'Theater'
|
||||
end
|
||||
end
|
||||
|
||||
result.delete_if { |k, v| v.blank? }
|
||||
end
|
||||
|
||||
def clean_up_info(inner_html)
|
||||
inner_html.squish.gsub(/^.*<\/span>\s*/, '')
|
||||
end
|
||||
|
||||
end
|
||||
end
|
|
@ -1,14 +0,0 @@
|
|||
require_dependency 'oneboxer/oembed_onebox'
|
||||
|
||||
module Oneboxer
|
||||
class SlideshareOnebox < OembedOnebox
|
||||
|
||||
matcher /^https?\:\/\/(www\.)?slideshare\.net\/*\/.*$/
|
||||
|
||||
def oembed_endpoint
|
||||
"http://www.slideshare.net/api/oembed/2?url=#{BaseOnebox.uriencode(@url)}&format=json&maxwidth=600"
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
|
@ -1,13 +0,0 @@
|
|||
require_dependency 'oneboxer/oembed_onebox'
|
||||
|
||||
module Oneboxer
|
||||
class SmugmugOnebox < OembedOnebox
|
||||
|
||||
matcher /^https?\:\/\/.*\.smugmug\.com\/.*$/
|
||||
|
||||
def oembed_endpoint
|
||||
"http://api.smugmug.com/services/oembed/?url=#{BaseOnebox.uriencode(@url)}&format=json"
|
||||
end
|
||||
|
||||
end
|
||||
end
|
|
@ -1,10 +0,0 @@
|
|||
require_dependency 'oneboxer/oembed_onebox'
|
||||
|
||||
module Oneboxer
|
||||
class SoundcloudOnebox < OembedOnebox
|
||||
matcher /^https?:\/\/(?:www\.)?soundcloud\.com\/.+$/
|
||||
def oembed_endpoint
|
||||
"http://soundcloud.com/oembed?url=#{BaseOnebox.uriencode(@url.sub('https://', 'http://'))}&format=json"
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,52 +0,0 @@
|
|||
require_dependency 'oneboxer/handlebars_onebox'
|
||||
|
||||
module Oneboxer
|
||||
class StackExchangeOnebox < HandlebarsOnebox
|
||||
|
||||
unless defined? DOMAINS
|
||||
DOMAINS = [
|
||||
'stackexchange',
|
||||
'stackoverflow',
|
||||
'superuser',
|
||||
'serverfault',
|
||||
'askubuntu'
|
||||
]
|
||||
end
|
||||
|
||||
# http://rubular.com/r/V3T0I1VTPn
|
||||
unless defined? REGEX
|
||||
REGEX = /^http:\/\/(?:(?:(?<subsubdomain>\w*)\.)?(?<subdomain>\w*)\.)?(?<domain>#{DOMAINS.join('|')})\.com\/(?:questions|q)\/(?<question>\d*)/
|
||||
end
|
||||
|
||||
matcher REGEX
|
||||
favicon 'stackexchange.png'
|
||||
|
||||
def translate_url
|
||||
@url.match(REGEX) do |match|
|
||||
site = if match[:domain] == 'stackexchange'
|
||||
[match[:subsubdomain],match[:subdomain]].compact.join('.')
|
||||
else
|
||||
[match[:subdomain],match[:domain]].compact.join('.')
|
||||
end
|
||||
|
||||
["http://api.stackexchange.com/2.1/",
|
||||
"questions/#{match[:question]}",
|
||||
"?site=#{site}"
|
||||
].join
|
||||
end
|
||||
end
|
||||
|
||||
def parse(data)
|
||||
result = JSON.parse(data)['items'].first
|
||||
|
||||
if result
|
||||
result['creation_date'] =
|
||||
Time.at(result['creation_date'].to_i).strftime("%I:%M%p - %d %b %y")
|
||||
|
||||
result['tags'] = result['tags'].take(4).join(', ')
|
||||
end
|
||||
|
||||
result
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,10 +0,0 @@
|
|||
require_dependency 'oneboxer/oembed_onebox'
|
||||
|
||||
module Oneboxer
|
||||
class TedOnebox < OembedOnebox
|
||||
matcher /^https?\:\/\/(www\.)?ted\.com\/talks\/.*$/
|
||||
def oembed_endpoint
|
||||
"http://www.ted.com/talks/oembed.json?url=#{BaseOnebox.uriencode(@url)}"
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,20 +0,0 @@
|
|||
<aside class='quote' data-post="1" data-topic="{{topic}}">
|
||||
<div class='title'>
|
||||
<div class='quote-controls'></div>
|
||||
{{{avatar}}}
|
||||
<a href="{{original_url}}">{{title}}</a> {{{category}}}
|
||||
</div>
|
||||
<blockquote>{{{quote}}}
|
||||
<div class='topic-info'>
|
||||
<div class='info-line'>
|
||||
{{posts_count}} posts, last post {{last_post}}, created {{age}}, {{views}} views
|
||||
</div>
|
||||
<div class='posters'>
|
||||
{{#posters}}
|
||||
{{{avatar}}}
|
||||
{{/posters}}
|
||||
</div>
|
||||
<div class='clearfix'></div>
|
||||
</div>
|
||||
</blockquote>
|
||||
</aside>
|
|
@ -1,8 +0,0 @@
|
|||
<div class='onebox-result'>
|
||||
{{{avatar}}}
|
||||
<h3><a href="{{original_url}}">{{username}}</a></h3>
|
||||
|
||||
{{#bio}}<p>{{bio}}</p>{{/bio}}
|
||||
|
||||
<div class='clearfix'></div>
|
||||
</div>
|
|
@ -1,16 +0,0 @@
|
|||
<div class='onebox-result'>
|
||||
{{#host}}
|
||||
<a href='{{original_url}}' class='source track-link' target="_blank">
|
||||
{{#favicon}}<img class='favicon' src="{{favicon}}"> {{/favicon}}{{host}}
|
||||
</a>
|
||||
{{/host}}
|
||||
<div class='onebox-result-body'>
|
||||
{{#title}}
|
||||
<h3><a href="{{original_url}}" target="_blank">{{title}}</a></h3>
|
||||
{{/title}}
|
||||
{{#files}}
|
||||
<h4>{{filename}}</h4>
|
||||
<pre><code>{{content}}</code></pre>
|
||||
{{/files}}
|
||||
</div>
|
||||
</div>
|
|
@ -1,19 +0,0 @@
|
|||
<div class='onebox-result'>
|
||||
{{#host}}
|
||||
<div class="source">
|
||||
<div class="info">
|
||||
<a href='{{original_url}}' class='source track-link' target="_blank">
|
||||
{{#favicon}}<img class='favicon' src="{{favicon}}"> {{/favicon}}{{host}}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
{{/host}}
|
||||
<div class='onebox-result-body'>
|
||||
<h4><a href="{{original_url}}" target="_blank">{{original_url}}</a></h4>
|
||||
<pre><code class='{{lang}}'>{{content}}</code></pre>
|
||||
|
||||
{{#truncated}}
|
||||
This file has been truncated. <a href="{{original_url}}" target="_blank">show original</a>
|
||||
{{/truncated}}
|
||||
</div>
|
||||
</div>
|
|
@ -1,38 +0,0 @@
|
|||
<div class="onebox-result">
|
||||
{{#host}}
|
||||
<div class="source">
|
||||
<div class="info">
|
||||
<a href="{{html_url}}" class="track-link" target="_blank">
|
||||
{{#favicon}}
|
||||
<img class="favicon" src="{{favicon}}">
|
||||
{{/favicon}}
|
||||
{{host}}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
{{/host}}
|
||||
|
||||
<div class="onebox-result-body">
|
||||
{{#author.avatar_url}}
|
||||
<a href="{{author.html_url}}" target="_blank">
|
||||
<img alt="{{author.login}}" src="{{author.avatar_url}}">
|
||||
</a>
|
||||
{{/author.avatar_url}}
|
||||
|
||||
<h4>
|
||||
<a href="{{html_url}}" target="_blank">{{commit.message}}</a>
|
||||
</h4>
|
||||
|
||||
<div class="date">
|
||||
by <a href="{{author.html_url}}" target="_blank">{{author.login}}</a>
|
||||
on <a href="{{html_url}}" target="_blank">{{commit_date}}</a>
|
||||
</div>
|
||||
|
||||
<div class="github-commit-stats">
|
||||
changed <strong>{{files.length}} files</strong>
|
||||
with <strong>{{stats.additions}} additions</strong>
|
||||
and <strong>{{stats.deletions}} deletions</strong>.
|
||||
</div>
|
||||
</div>
|
||||
<div class="clearfix"></div>
|
||||
</div>
|
|
@ -1,39 +0,0 @@
|
|||
<div class="onebox-result">
|
||||
{{#host}}
|
||||
<div class="source">
|
||||
<div class="info">
|
||||
<a href="{{html_url}}" class="track-link" target="_blank">
|
||||
{{#favicon}}
|
||||
<img class="favicon" src="{{favicon}}">
|
||||
{{/favicon}}
|
||||
{{host}}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
{{/host}}
|
||||
|
||||
<div class="onebox-result-body">
|
||||
{{#user.avatar_url}}
|
||||
<a href="{{user.html_url}}" target="_blank">
|
||||
<img alt="{{user.login}}" src="{{user.avatar_url}}">
|
||||
</a>
|
||||
{{/user.avatar_url}}
|
||||
|
||||
<h4>
|
||||
<a href="{{html_url}}" target="_blank">{{title}}</a>
|
||||
</h4>
|
||||
|
||||
<div class="date">
|
||||
by <a href="{{user.html_url}}" target="_blank">{{user.login}}</a>
|
||||
on <a href="{{html_url}}" target="_blank">{{created_at}}</a>
|
||||
</div>
|
||||
|
||||
<div class="github-commit-stats">
|
||||
<strong>{{commits}} commits</strong>
|
||||
changed <strong>{{changed_files}} files</strong>
|
||||
with <strong>{{additions}} additions</strong>
|
||||
and <strong>{{deletions}} deletions</strong>.
|
||||
</div>
|
||||
</div>
|
||||
<div class="clearfix"></div>
|
||||
</div>
|
|
@ -1,17 +0,0 @@
|
|||
<div class='onebox-result'>
|
||||
{{#host}}
|
||||
<div class="source">
|
||||
<div class="info">
|
||||
<a href='{{original_url}}' class='source track-link' target="_blank">
|
||||
{{#favicon}}<img class='favicon' src="{{favicon}}"> {{/favicon}}{{host}}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
{{/host}}
|
||||
<div class='onebox-result-body'>
|
||||
<h3><a href="{{original_url}}" target="_blank">{{title}}</a></h3>
|
||||
{{#author_info}}<h4>{{author_info}}</h4>{{/author_info}}
|
||||
{{{html}}}
|
||||
</div>
|
||||
<div class='clearfix'></div>
|
||||
</div>
|
|
@ -1,25 +0,0 @@
|
|||
<div class='onebox-result'>
|
||||
{{#host}}
|
||||
<div class='source'>
|
||||
<div class='info'>
|
||||
<a href='{{original_url}}' class="track-link" target="_blank">
|
||||
{{#favicon}}<img class='favicon' src="{{favicon}}"> {{/favicon}}{{host}}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
{{/host}}
|
||||
<div class='onebox-result-body'>
|
||||
{{#poster}}<img src="{{poster}}" class="thumbnail">{{/poster}}
|
||||
<h3><a href="{{original_url}}" target="_blank">{{title}}</a></h3>
|
||||
|
||||
{{#verdict_image}}<img class="verdict" src={{verdict_image}}><b>{{verdict_percentage}}</b> of critics liked it.{{/verdict_image}}
|
||||
{{#user_percentage}}<img class="popcorn" src={{popcorn_image}}><b>{{user_percentage}}</b> of users liked it.<br />{{/user_percentage}}
|
||||
{{#cast}}<b>Cast:</b> {{cast}}<br />{{/cast}}
|
||||
{{#director}}<b>Director:</b> {{director}}<br />{{/director}}
|
||||
{{#release_type}}<b>{{release_type}} Release:</b> {{release_date}}<br />{{/release_type}}
|
||||
{{#running_time}}<b>Running Time:</b> {{running_time}}<br />{{/running_time}}
|
||||
{{#rated}}<b>Rated: </b> {{rated}}<br />{{/rated}}
|
||||
{{synopsis}}
|
||||
</div>
|
||||
<div class='clearfix'></div>
|
||||
</div>
|
|
@ -1,23 +0,0 @@
|
|||
<div class='onebox-result'>
|
||||
{{#host}}
|
||||
<div class='source'>
|
||||
<div class='info'>
|
||||
<a href='{{original_url}}' class="track-link" target="_blank">
|
||||
{{#favicon}}<img class='favicon' src="{{favicon}}"> {{/favicon}}{{host}}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
{{/host}}
|
||||
<div class='onebox-result-body'>
|
||||
{{#image}}<img src="{{image}}" class="thumbnail">{{/image}}
|
||||
<h3><a href="{{original_url}}" target="_blank"{{#image_height}} height="{{image_height}}"{{/image_height}}{{#image_width}} width="{{image_width}}"{{/image_width}}>{{title}}</a></h3>
|
||||
{{#by_info}}<h4>{{by_info}}</h4>{{/by_info}}
|
||||
{{#unsafe}}
|
||||
{{text}}
|
||||
{{/unsafe}}
|
||||
{{^unsafe}}
|
||||
{{{text}}}
|
||||
{{/unsafe}}
|
||||
</div>
|
||||
<div class='clearfix'></div>
|
||||
</div>
|
|
@ -1,38 +0,0 @@
|
|||
<div class="onebox-result">
|
||||
{{#host}}
|
||||
<div class="source">
|
||||
<div class="info">
|
||||
<a href="{{link}}" class="track-link" target="_blank">
|
||||
{{#favicon}}
|
||||
<img class="favicon" src="{{favicon}}">
|
||||
{{/favicon}}
|
||||
{{host}}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
{{/host}}
|
||||
|
||||
<div class="onebox-result-body">
|
||||
{{#owner.profile_image}}
|
||||
<a href="{{owner.link}}" target="_blank">
|
||||
<img alt="{{owner.display_name}}" src="{{owner.profile_image}}">
|
||||
</a>
|
||||
{{/owner.profile_image}}
|
||||
|
||||
<h4>
|
||||
<a href="{{link}}" target="_blank">{{{title}}}</a>
|
||||
</h4>
|
||||
|
||||
<div class="date">
|
||||
asked by <a href="{{owner.link}}" target="_blank">
|
||||
{{owner.display_name}}
|
||||
</a>
|
||||
on <a href="{{link}}" target="_blank">{{creation_date}}</a>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<strong>{{tags}}</strong>
|
||||
</div>
|
||||
</div>
|
||||
<div class="clearfix"></div>
|
||||
</div>
|
|
@ -1,28 +0,0 @@
|
|||
<div class='onebox-result'>
|
||||
{{#host}}
|
||||
<div class="source">
|
||||
<div class="info">
|
||||
<a href='{{original_url}}' class="track-link" target="_blank">
|
||||
{{#favicon}}<img class='favicon' src="{{favicon}}"> {{/favicon}}{{host}}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
{{/host}}
|
||||
|
||||
<div class='onebox-result-body'>
|
||||
{{#user.profile_image_url}}
|
||||
<img src="{{user.profile_image_url}}">{{/user.profile_image_url}}
|
||||
<h4>
|
||||
<a href='https://twitter.com/{{user.screen_name}}'>
|
||||
@{{user.screen_name}}
|
||||
</a>
|
||||
</h4>
|
||||
|
||||
{{{text}}}
|
||||
|
||||
<div class='date'>
|
||||
<a href="{{original_url}}" target="_blank">{{created_at}}</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class='clearfix'></div>
|
||||
</div>
|
|
@ -1,39 +0,0 @@
|
|||
require_dependency 'oneboxer/handlebars_onebox'
|
||||
require_dependency 'twitter_api'
|
||||
|
||||
module Oneboxer
|
||||
class TwitterOnebox < HandlebarsOnebox
|
||||
|
||||
unless defined? BASE_URL
|
||||
BASE_URL = 'https://api.twitter.com'.freeze
|
||||
end
|
||||
|
||||
unless defined? REGEX
|
||||
REGEX = /^https?:\/\/(?:www\.)?twitter.com\/(?<user>[^\/]+)\/status\/(?<id>\d+)$/
|
||||
end
|
||||
|
||||
matcher REGEX
|
||||
|
||||
# TODO: use zocial instead
|
||||
favicon 'twitter.png'
|
||||
|
||||
def fetch_html
|
||||
raise Discourse::SiteSettingMissing if TwitterApi.twitter_credentials_missing?
|
||||
|
||||
# a bit odd, but I think the api expects html
|
||||
TwitterApi.raw_tweet_for(@url.match(REGEX)[:id])
|
||||
end
|
||||
|
||||
def parse(data)
|
||||
result = JSON.parse(data)
|
||||
|
||||
result['created_at'] =
|
||||
Time.parse(result['created_at']).strftime("%I:%M%p - %d %b %y")
|
||||
|
||||
result['text'] = TwitterApi.prettify_tweet(result)
|
||||
|
||||
result
|
||||
end
|
||||
|
||||
end
|
||||
end
|
|
@ -1,13 +0,0 @@
|
|||
require_dependency 'oneboxer/oembed_onebox'
|
||||
|
||||
module Oneboxer
|
||||
class ViddlerOnebox < OembedOnebox
|
||||
|
||||
matcher /^https?:\/\/(?:www\.)?viddler\.com\/.+$/
|
||||
|
||||
def oembed_endpoint
|
||||
"http://lab.viddler.com/services/oembed/?url=#{BaseOnebox.uriencode(@url)}"
|
||||
end
|
||||
|
||||
end
|
||||
end
|
|
@ -1,13 +0,0 @@
|
|||
require_dependency 'oneboxer/oembed_onebox'
|
||||
|
||||
module Oneboxer
|
||||
class VimeoOnebox < OembedOnebox
|
||||
|
||||
matcher /^https?\:\/\/vimeo\.com\/.*$/
|
||||
|
||||
def oembed_endpoint
|
||||
"http://vimeo.com/api/oembed.json?url=#{BaseOnebox.uriencode(@url)}&width=600"
|
||||
end
|
||||
|
||||
end
|
||||
end
|
|
@ -1,124 +0,0 @@
|
|||
#******************************************************************************#
|
||||
# #
|
||||
# Oneboxer already supports most sites using OpenGraph via the OpenGraphOnebox #
|
||||
# class. If the site you want to create a onebox for supports OpenGraph, #
|
||||
# please try adding the site to the whitelist below before creating a custom #
|
||||
# parser or template. #
|
||||
# #
|
||||
#******************************************************************************#
|
||||
|
||||
module Oneboxer
|
||||
module Whitelist
|
||||
def self.entries
|
||||
@entries ||= [
|
||||
Entry.new(/^https?:\/\/(?:www\.)?findery\.com\/.+/),
|
||||
Entry.new(/^https?:\/\/(?:www\.)?zappos\.com\/.+/),
|
||||
Entry.new(/^https?:\/\/(?:www\.)?slideshare\.net\/.+/),
|
||||
Entry.new(/^https?:\/\/(?:www\.)?rottentomatoes\.com\/.+/),
|
||||
Entry.new(/^https?:\/\/(?:www\.)?cnn\.com\/.+/),
|
||||
Entry.new(/^https?:\/\/(?:www\.)?washingtonpost\.com\/.+/),
|
||||
Entry.new(/^https?:\/\/(?:www\.)?funnyordie\.com\/.+/),
|
||||
Entry.new(/^https?:\/\/(?:www\.)?500px\.com\/.+/),
|
||||
Entry.new(/^https?:\/\/(?:www\.)?scribd\.com\/.+/),
|
||||
Entry.new(/^https?:\/\/(?:www\.)?photobucket\.com\/.+/),
|
||||
Entry.new(/^https?:\/\/(?:www\.)?ebay\.(com|ca|co\.uk)\/.+/),
|
||||
Entry.new(/^https?:\/\/(?:www\.)?nytimes\.com\/.+/),
|
||||
Entry.new(/^https?:\/\/(?:www\.)?pinterest\.com\/.+/),
|
||||
# Entry.new(/^https?:\/\/(?:www\.)?imdb\.com\/.+/), # For legal reasons, we cannot include IMDB onebox support
|
||||
Entry.new(/^https?:\/\/(?:www\.)?bbc\.co\.uk\/.+/),
|
||||
Entry.new(/^https?:\/\/(?:www\.)?ask\.com\/.+/),
|
||||
Entry.new(/^https?:\/\/(?:www\.)?huffingtonpost\.com\/.+/),
|
||||
Entry.new(/^https?:\/\/(?:www\.)?aol\.(com|ca)\/.+/),
|
||||
Entry.new(/^https?:\/\/(?:www\.)?espn\.go\.com\/.+/),
|
||||
Entry.new(/^https?:\/\/(?:www\.)?about\.com\/.+/),
|
||||
Entry.new(/^https?:\/\/(?:www\.)?cnet\.com\/.+/),
|
||||
Entry.new(/^https?:\/\/(?:www\.)?ehow\.com\/.+/),
|
||||
Entry.new(/^https?:\/\/(?:www\.)?dailymail\.co\.uk\/.+/),
|
||||
Entry.new(/^https?:\/\/(?:www\.)?indiatimes\.com\/.+/),
|
||||
Entry.new(/^https?:\/\/(?:www\.)?answers\.com\/.+/),
|
||||
Entry.new(/^https?:\/\/(?:www\.)?instagr\.am\/.+/),
|
||||
Entry.new(/^https?:\/\/(?:www\.)?battle\.net\/.+/),
|
||||
Entry.new(/^https?:\/\/(?:www\.)?sourceforge\.net\/.+/),
|
||||
Entry.new(/^https?:\/\/(?:www\.)?myspace\.com\/.+/),
|
||||
Entry.new(/^https?:\/\/(?:www\.)?wikia\.com\/.+/),
|
||||
Entry.new(/^https?:\/\/(?:www\.)?etsy\.com\/.+/),
|
||||
Entry.new(/^https?:\/\/(?:www\.)?walmart\.com\/.+/),
|
||||
Entry.new(/^https?:\/\/(?:www\.)?reference\.com\/.+/),
|
||||
Entry.new(/^https?:\/\/(?:www\.)?yelp\.com\/.+/),
|
||||
Entry.new(/^https?:\/\/(?:www\.)?foxnews\.com\/.+/),
|
||||
Entry.new(/^https?:\/\/(?:www\.)?guardian\.co\.uk\/.+/),
|
||||
Entry.new(/^https?:\/\/(?:www\.)?digg\.com\/.+/),
|
||||
Entry.new(/^https?:\/\/(?:www\.)?squidoo\.com\/.+/),
|
||||
Entry.new(/^https?:\/\/(?:www\.)?wsj\.com\/.+/),
|
||||
Entry.new(/^https?:\/\/(?:www\.)?archive\.org\/.+/),
|
||||
Entry.new(/^https?:\/\/(?:www\.)?nba\.com\/.+/),
|
||||
Entry.new(/^https?:\/\/(?:www\.)?samsung\.com\/.+/),
|
||||
Entry.new(/^https?:\/\/(?:www\.)?mashable\.com\/.+/),
|
||||
Entry.new(/^https?:\/\/(?:www\.)?forbes\.com\/.+/),
|
||||
Entry.new(/^https?:\/\/(?:www\.)?thefreedictionary\.com\/.+/),
|
||||
Entry.new(/^https?:\/\/(?:www\.)?groupon\.com\/.+/),
|
||||
Entry.new(/^https?:\/\/(?:www\.)?ikea\.com\/.+/),
|
||||
Entry.new(/^https?:\/\/(?:www\.)?dell\.com\/.+/),
|
||||
Entry.new(/^https?:\/\/(?:www\.)?mlb\.com\/.+/),
|
||||
Entry.new(/^https?:\/\/(?:www\.)?bestbuy\.(com|ca)\/.+/),
|
||||
Entry.new(/^https?:\/\/(?:www\.)?bloomberg\.com\/.+/),
|
||||
Entry.new(/^https?:\/\/(?:www\.)?ign\.com\/.+/),
|
||||
Entry.new(/^https?:\/\/(?:www\.)?twitpic\.com\/.+/),
|
||||
Entry.new(/^https?:\/\/(?:www\.)?techcrunch\.com\/.+/, false),
|
||||
Entry.new(/^https?:\/\/(?:www\.)?usatoday\.com\/.+/),
|
||||
Entry.new(/^https?:\/\/(?:www\.)?go\.com\/.+/),
|
||||
Entry.new(/^https?:\/\/(?:www\.)?businessinsider\.com\/.+/),
|
||||
Entry.new(/^https?:\/\/(?:www\.)?zillow\.com\/.+/),
|
||||
Entry.new(/^https?:\/\/(?:www\.)?tmz\.com\/.+/),
|
||||
Entry.new(/^https?:\/\/(?:www\.)?thesun\.co\.uk\/.+/),
|
||||
Entry.new(/^https?:\/\/(?:www\.)?thestar\.(com|ca)\/.+/),
|
||||
Entry.new(/^https?:\/\/(?:www\.)?theglobeandmail\.com\/.+/),
|
||||
Entry.new(/^https?:\/\/(?:www\.)?torontosun\.com\/.+/),
|
||||
Entry.new(/^https?:\/\/(?:www\.)?kickstarter\.com\/.+/),
|
||||
Entry.new(/^https?:\/\/(?:www\.)?wired\.com\/.+/),
|
||||
Entry.new(/^https?:\/\/(?:www\.)?time\.com\/.+/),
|
||||
Entry.new(/^https?:\/\/(?:www\.)?npr\.org\/.+/),
|
||||
Entry.new(/^https?:\/\/(?:www\.)?cracked\.com\/.+/),
|
||||
Entry.new(/^https?:\/\/(?:www\.)?deadline\.com\/.+/),
|
||||
Entry.new(/^https?:\/\/(?:www\.)?thinkgeek\.com\/.+/),
|
||||
Entry.new(/^https?:\/\/(?:www\.)?theonion\.com\/.+/),
|
||||
Entry.new(/^https?:\/\/(?:www\.)?screenr\.com\/.+/),
|
||||
Entry.new(/^https?:\/\/(?:www\.)?tumblr\.com\/.+/, false),
|
||||
Entry.new(/^https?:\/\/(?:www\.)?howtogeek\.com\/.+/, false),
|
||||
Entry.new(/^https?:\/\/(?:www\.)?screencast\.com\/.+/),
|
||||
Entry.new(/\/\d{4}\/\d{2}\/\d{2}\//, false), # wordpress
|
||||
Entry.new(/^https?:\/\/[^\/]+\/t\/[^\/]+\/\d+(\/\d+)?(\?.*)?$/),
|
||||
|
||||
# Online learning resources
|
||||
Entry.new(/^https?:\/\/(?:www\.)?coursera\.org\/.+/, false),
|
||||
Entry.new(/^https?:\/\/(?:www\.)?khanacademy\.org\/.+/, false),
|
||||
Entry.new(/^https?:\/\/(?:www\.)?ted\.com\/talks\/.+/, false), # only /talks have meta info
|
||||
Entry.new(/^https?:\/\/(?:www\.)?wikihow\.com\/.+/, false),
|
||||
Entry.new(/^https?:\/\/(?:\w+\.)?wonderhowto\.com\/.+/, false)
|
||||
]
|
||||
end
|
||||
|
||||
def self.entry_for_url(url)
|
||||
entries.each {|e| return e if e.matches?(url) }
|
||||
nil
|
||||
end
|
||||
|
||||
class Entry
|
||||
# oembed = false is probably safer, but this is the least-drastic change
|
||||
def initialize(pattern, oembed = true)
|
||||
@pattern = pattern
|
||||
@oembed = oembed
|
||||
end
|
||||
|
||||
def allows_oembed?
|
||||
@oembed
|
||||
end
|
||||
|
||||
def matches?(url)
|
||||
url =~ @pattern
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
|
@ -1,58 +0,0 @@
|
|||
require_dependency 'oneboxer/handlebars_onebox'
|
||||
|
||||
module Oneboxer
|
||||
class WikipediaOnebox < HandlebarsOnebox
|
||||
|
||||
matcher /^https?:\/\/.*wikipedia\.(com|org)\/.*$/
|
||||
favicon 'wikipedia.png'
|
||||
|
||||
def template
|
||||
template_path('simple_onebox')
|
||||
end
|
||||
|
||||
def translate_url
|
||||
m = @url.match(/^https?:\/\/((?<subdomain>.+)\.)?wikipedia\.(com|org)\/wiki\/(?<identifier>[^#\/]+)/mi)
|
||||
subdomain = m[:subdomain] || "en"
|
||||
article_id = CGI::unescape(m[:identifier])
|
||||
"http://#{subdomain}.m.wikipedia.org/w/index.php?title=#{URI::encode(article_id)}"
|
||||
end
|
||||
|
||||
def parse(data)
|
||||
|
||||
html_doc = Nokogiri::HTML(data)
|
||||
|
||||
result = {}
|
||||
|
||||
title = html_doc.at('title').inner_html
|
||||
result[:title] = title.gsub!(/ - Wikipedia.*$/, '') if title.present?
|
||||
|
||||
# get the first image > 150 pix high
|
||||
images = html_doc.search("img").select { |img| img['height'].to_i > 150 }
|
||||
|
||||
result[:image] = "http:#{images[0]["src"]}" unless images.empty?
|
||||
|
||||
# remove the table from mobile layout, as it can contain paras in some rare cases
|
||||
html_doc.search("table").remove
|
||||
|
||||
# get all the paras
|
||||
paras = html_doc.search("p")
|
||||
text = ""
|
||||
|
||||
unless paras.empty?
|
||||
cnt = 0
|
||||
while text.length < MAX_TEXT && cnt <= 3
|
||||
text << " " unless cnt == 0
|
||||
paragraph = paras[cnt].inner_text[0..MAX_TEXT]
|
||||
paragraph.gsub!(/\[\d+\]/mi, "")
|
||||
text << paragraph
|
||||
cnt += 1
|
||||
end
|
||||
end
|
||||
|
||||
text = "#{text[0..MAX_TEXT]}..." if text.length > MAX_TEXT
|
||||
result[:text] = text
|
||||
result
|
||||
end
|
||||
|
||||
end
|
||||
end
|
|
@ -1,13 +0,0 @@
|
|||
require_dependency 'oneboxer/oembed_onebox'
|
||||
|
||||
module Oneboxer
|
||||
class YfrogOnebox < OembedOnebox
|
||||
|
||||
matcher /^https?:\/\/(?:www\.)?yfrog\.(com|ru|com\.tr|it|fr|co\.il|co\.uk|com\.pl|pl|eu|us)\/[a-zA-Z0-9]+/
|
||||
|
||||
def oembed_endpoint
|
||||
"http://www.yfrog.com/api/oembed/?url=#{BaseOnebox.uriencode(@url)}&format=json"
|
||||
end
|
||||
|
||||
end
|
||||
end
|
|
@ -1,20 +0,0 @@
|
|||
require_dependency 'oneboxer/oembed_onebox'
|
||||
|
||||
module Oneboxer
|
||||
class YoutubeOnebox < OembedOnebox
|
||||
matcher /^https?:\/\/(?:www\.)?(?:youtube\.com|youtu\.be)\/.+$/
|
||||
|
||||
def oembed_endpoint
|
||||
"http://www.youtube.com/oembed?url=#{BaseOnebox.uriencode(@url.sub('https://', 'http://'))}&format=json"
|
||||
end
|
||||
|
||||
def onebox
|
||||
(super || []).each do |entry|
|
||||
# Youtube allows HTTP and HTTPS, so replace them with the protocol-agnostic variant
|
||||
BaseOnebox.replace_agnostic entry
|
||||
# Add wmode=opaque to the iframe src URL so that the flash player is rendered within the document flow instead of on top
|
||||
BaseOnebox.append_embed_wmode entry
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,42 +0,0 @@
|
|||
# encoding: utf-8
|
||||
|
||||
require 'spec_helper'
|
||||
require 'oneboxer'
|
||||
require 'oneboxer/amazon_onebox'
|
||||
|
||||
describe Oneboxer::AmazonOnebox do
|
||||
before(:each) do
|
||||
@o = Oneboxer::AmazonOnebox.new("http://www.amazon.com/Ruby-Programming-Language-David-Flanagan/dp/0596516177")
|
||||
FakeWeb.register_uri(:get, @o.translate_url, response: fixture_file('oneboxer/amazon.response'))
|
||||
end
|
||||
|
||||
it "translates the URL" do
|
||||
@o.translate_url.should == "http://www.amazon.com/gp/aw/d/0596516177"
|
||||
end
|
||||
|
||||
it "generates the expected onebox for Amazon" do
|
||||
@o.onebox.should match_html expected_amazon_result
|
||||
end
|
||||
|
||||
private
|
||||
def expected_amazon_result
|
||||
<<EXPECTED
|
||||
<div class='onebox-result'>
|
||||
<div class='source'>
|
||||
<div class='info'>
|
||||
<a href='http://www.amazon.com/Ruby-Programming-Language-David-Flanagan/dp/0596516177' class="track-link" target="_blank">
|
||||
<img class='favicon' src="/assets/favicons/amazon.png"> amazon.com
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class='onebox-result-body'>
|
||||
<img src="http://ecx.images-amazon.com/images/I/512Cx%2BnJK8L._BO2,204,203,200_PIsitb-sticker-arrow-click,TopRight,35,-76_AA300_SH20_OU01_.jpg" class="thumbnail">
|
||||
<h3><a href="http://www.amazon.com/Ruby-Programming-Language-David-Flanagan/dp/0596516177" target="_blank">The Ruby Programming Language [Paperback]</a></h3>
|
||||
|
||||
|
||||
</div>
|
||||
<div class='clearfix'></div>
|
||||
</div>
|
||||
EXPECTED
|
||||
end
|
||||
end
|
|
@ -1,41 +0,0 @@
|
|||
# encoding: utf-8
|
||||
|
||||
require 'spec_helper'
|
||||
require 'oneboxer'
|
||||
require 'oneboxer/android_app_store_onebox'
|
||||
|
||||
describe Oneboxer::AndroidAppStoreOnebox do
|
||||
before(:each) do
|
||||
@o = Oneboxer::AndroidAppStoreOnebox.new("https://play.google.com/store/apps/details?id=com.moosoft.parrot")
|
||||
FakeWeb.register_uri(:get, @o.translate_url, response: fixture_file('oneboxer/android.response'))
|
||||
end
|
||||
|
||||
it "generates the expected onebox for Android App Store" do
|
||||
@o.onebox.should match_html expected_android_app_store_result
|
||||
end
|
||||
|
||||
private
|
||||
def expected_android_app_store_result
|
||||
<<EXPECTED
|
||||
<div class='onebox-result'>
|
||||
<div class='source'>
|
||||
<div class='info'>
|
||||
<a href='https://play.google.com/store/apps/details?id=com.moosoft.parrot' class="track-link" target="_blank">
|
||||
<img class='favicon' src="/assets/favicons/google_play.png"> play.google.com
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class='onebox-result-body'>
|
||||
<img src="https://lh5.ggpht.com/wrYYVu74XNUu2WHk0aSZEqgdCDCNti9Fl0_dJnhgR6jY04ajQgVg5ABMatfcTDsB810=w124" class="thumbnail">
|
||||
<h3><a href="https://play.google.com/store/apps/details?id=com.moosoft.parrot" target="_blank">Talking Parrot</a></h3>
|
||||
|
||||
Listen to the parrot repeat what you say. A Fun application for all ages. Upgrade to Talking Parrot Pro to save sounds, set them as your ringtone and control recording.
|
||||
Press the MENU button to access the settings where you can change the record time and repeat count.
|
||||
This app uses anonymous usage stats to understand and improve performance.
|
||||
Comments and feedback welcome.
|
||||
</div>
|
||||
<div class='clearfix'></div>
|
||||
</div>
|
||||
EXPECTED
|
||||
end
|
||||
end
|
|
@ -1,38 +0,0 @@
|
|||
# encoding: utf-8
|
||||
|
||||
require 'spec_helper'
|
||||
require 'oneboxer'
|
||||
require 'oneboxer/apple_app_onebox'
|
||||
|
||||
describe Oneboxer::AppleAppOnebox do
|
||||
before(:each) do
|
||||
@o = Oneboxer::AppleAppOnebox.new("https://itunes.apple.com/us/app/minecraft-pocket-edition-lite/id479651754")
|
||||
FakeWeb.register_uri(:get, @o.translate_url, response: fixture_file('oneboxer/apple.response'))
|
||||
end
|
||||
|
||||
it "generates the expected onebox for Apple app" do
|
||||
@o.onebox.should match_html expected_apple_app_result
|
||||
end
|
||||
|
||||
private
|
||||
def expected_apple_app_result
|
||||
<<EXPECTED
|
||||
<div class='onebox-result'>
|
||||
<div class='source'>
|
||||
<div class='info'>
|
||||
<a href='https://itunes.apple.com/us/app/minecraft-pocket-edition-lite/id479651754' class="track-link" target="_blank">
|
||||
<img class='favicon' src="/assets/favicons/apple.png"> itunes.apple.com
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class='onebox-result-body'>
|
||||
<img src="http://a5.mzstatic.com/us/r1000/087/Purple/99/2f/dd/mzl.erzwvjsi.175x175-75.jpg" class="thumbnail">
|
||||
<h3><a href="https://itunes.apple.com/us/app/minecraft-pocket-edition-lite/id479651754" target="_blank">Minecraft – Pocket Edition Lite</a></h3>
|
||||
|
||||
Imagine it, build it. Create worlds on the go with Minecraft - Pocket EditionThis is the Lite version of Minecraft - Pocket Edition. Minecraft - Pocket Edition allows you to build on the go. Use blocks to create masterpieces as you travel, hangout with friends, sit at the park, the possibilities are endless. Move beyond the limits of your computer and play Minecraft everywhere you go.Limitations of the Lite version* The world is not saved between sessions* Multiplayer worlds can not be copied to
|
||||
</div>
|
||||
<div class='clearfix'></div>
|
||||
</div>
|
||||
EXPECTED
|
||||
end
|
||||
end
|
|
@ -1,33 +0,0 @@
|
|||
require 'spec_helper'
|
||||
require 'oneboxer'
|
||||
require 'oneboxer/_flash_video_onebox'
|
||||
|
||||
describe Oneboxer::FlashVideoOnebox do
|
||||
before do
|
||||
@o = Oneboxer::FlashVideoOnebox.new('http://player.56.com/v_OTMyNTk1MzE.swf')
|
||||
end
|
||||
|
||||
context "when SiteSetting.enable_flash_video_onebox is true" do
|
||||
before do
|
||||
SiteSetting.stubs(:enable_flash_video_onebox).returns(true)
|
||||
end
|
||||
|
||||
it "generates a flash video" do
|
||||
expect(@o.onebox).to match_html(
|
||||
"<object width='100%' height='100%' wmode='opaque'><param name='http://player.56.com/v_OTMyNTk1MzE.swf' value='http://player.56.com/v_OTMyNTk1MzE.swf'><embed src='http://player.56.com/v_OTMyNTk1MzE.swf' width='100%' height='100%' wmode='opaque'></embed></object>"
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
context "when SiteSetting.enable_flash_video_onebox is false" do
|
||||
before do
|
||||
SiteSetting.stubs(:enable_flash_video_onebox).returns(false)
|
||||
end
|
||||
|
||||
it "generates a link" do
|
||||
expect(@o.onebox).to match_html(
|
||||
"<a href='http://player.56.com/v_OTMyNTk1MzE.swf'>http://player.56.com/v_OTMyNTk1MzE.swf</a>"
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,21 +0,0 @@
|
|||
# encoding: utf-8
|
||||
|
||||
require 'spec_helper'
|
||||
require 'oneboxer'
|
||||
require 'oneboxer/flickr_onebox'
|
||||
|
||||
describe Oneboxer::FlickrOnebox do
|
||||
before(:each) do
|
||||
@o = Oneboxer::FlickrOnebox.new("http://www.flickr.com/photos/jaimeiniesta/3303881265")
|
||||
FakeWeb.register_uri(:get, @o.translate_url, response: fixture_file('oneboxer/flickr.response'))
|
||||
end
|
||||
|
||||
it "generates the expected onebox for Flickr" do
|
||||
@o.onebox.should match_html expected_flickr_result
|
||||
end
|
||||
|
||||
private
|
||||
def expected_flickr_result
|
||||
"<a href='http://www.flickr.com/photos/jaimeiniesta/3303881265' target='_blank'><img src='http://farm4.staticflickr.com/3419/3303881265_c6924748e8_z.jpg' alt=''></a>"
|
||||
end
|
||||
end
|
|
@ -1,16 +0,0 @@
|
|||
require 'spec_helper'
|
||||
require 'oneboxer'
|
||||
require 'oneboxer/gist_onebox'
|
||||
|
||||
describe Oneboxer::GistOnebox do
|
||||
it "does not trip on user names" do
|
||||
o = Oneboxer::GistOnebox.new('https://gist.github.com/aaa/4599619')
|
||||
o.translate_url.should == 'https://api.github.com/gists/4599619'
|
||||
end
|
||||
|
||||
it "works for old school urls too" do
|
||||
o = Oneboxer::GistOnebox.new('https://gist.github.com/4599619')
|
||||
o.translate_url.should == 'https://api.github.com/gists/4599619'
|
||||
end
|
||||
end
|
||||
|
|
@ -1,16 +0,0 @@
|
|||
# encoding: utf-8
|
||||
|
||||
require 'spec_helper'
|
||||
require 'oneboxer'
|
||||
require 'oneboxer/github_commit_onebox'
|
||||
|
||||
describe Oneboxer::GithubCommitOnebox do
|
||||
before(:each) do
|
||||
@o = Oneboxer::GithubCommitOnebox.new("https://github.com/discourse/discourse/commit/ee76f1926defa8309b3a7ea64a25707519529a13")
|
||||
FakeWeb.register_uri(:get, @o.translate_url, response: fixture_file('oneboxer/github_commit_onebox.response'))
|
||||
end
|
||||
|
||||
it "translates the URL" do
|
||||
@o.translate_url.should == "https://api.github.com/repos/discourse/discourse/commits/ee76f1926defa8309b3a7ea64a25707519529a13"
|
||||
end
|
||||
end
|
|
@ -1,17 +0,0 @@
|
|||
require 'spec_helper'
|
||||
require 'oneboxer'
|
||||
require 'oneboxer/github_pullrequest_onebox'
|
||||
|
||||
describe Oneboxer::GithubPullrequestOnebox do
|
||||
describe '#translate_url' do
|
||||
it 'returns the api url for the given pull request' do
|
||||
onebox = described_class.new(
|
||||
'https://github.com/discourse/discourse/pull/988'
|
||||
)
|
||||
expect(onebox.translate_url).to eq(
|
||||
'https://api.github.com/repos/discourse/discourse/pulls/988'
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -1,35 +0,0 @@
|
|||
require 'spec_helper'
|
||||
require 'oneboxer'
|
||||
require 'oneboxer/handlebars_onebox'
|
||||
|
||||
describe Oneboxer::HandlebarsOnebox do
|
||||
|
||||
describe 'simple onebox' do
|
||||
H = Oneboxer::HandlebarsOnebox
|
||||
|
||||
it "is able to render image size when specified" do
|
||||
template = H.template_path('simple_onebox')
|
||||
result = H.generate_onebox(template, 'image_width' => 100, 'image_height' => 100, image: 'http://my.com/image.png')
|
||||
|
||||
result.should =~ /width=/
|
||||
result.should =~ /height=/
|
||||
end
|
||||
|
||||
class SimpleOnebox < Oneboxer::HandlebarsOnebox
|
||||
favicon 'stackexchange.png'
|
||||
|
||||
def parse(html)
|
||||
{ testing: true }
|
||||
end
|
||||
end
|
||||
|
||||
it "does not use fingerprint on favicons" do
|
||||
onebox = SimpleOnebox.new "http://domain.com"
|
||||
onebox.stubs(:fetch_html).returns("")
|
||||
ActionController::Base.helpers.expects(:asset_path).with('favicons/stackexchange.png', digest: false)
|
||||
result = onebox.onebox
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
|
@ -1,115 +0,0 @@
|
|||
# encoding: utf-8
|
||||
|
||||
require 'spec_helper'
|
||||
require 'oneboxer'
|
||||
require 'oneboxer/rottentomatoes_onebox'
|
||||
|
||||
describe Oneboxer::RottentomatoesOnebox do
|
||||
it 'translates the URL' do
|
||||
o = Oneboxer::RottentomatoesOnebox.new('http://www.rottentomatoes.com/m/mud_2012/')
|
||||
expect(o.translate_url).to eq('http://rottentomatoes.com/mobile/m/mud_2012/')
|
||||
end
|
||||
|
||||
it 'generates the expected onebox for a fresh movie' do
|
||||
o = Oneboxer::RottentomatoesOnebox.new('http://www.rottentomatoes.com/m/mud_2012/')
|
||||
FakeWeb.register_uri(:get, o.translate_url, response: fixture_file('oneboxer/rottentomatoes_fresh.response'))
|
||||
o.onebox.should match_html expected_fresh_result
|
||||
end
|
||||
|
||||
it 'generates the expected onebox for a rotten movie' do
|
||||
o = Oneboxer::RottentomatoesOnebox.new('http://www.rottentomatoes.com/m/the_big_wedding_2013/')
|
||||
FakeWeb.register_uri(:get, o.translate_url, response: fixture_file('oneboxer/rottentomatoes_rotten.response'))
|
||||
o.onebox.should match_html expected_rotten_result
|
||||
end
|
||||
|
||||
it 'generates the expected onebox for a movie with an incomplete description' do
|
||||
o = Oneboxer::RottentomatoesOnebox.new('http://www.rottentomatoes.com/m/gunde_jaari_gallanthayyinde/')
|
||||
FakeWeb.register_uri(:get, o.translate_url, response: fixture_file('oneboxer/rottentomatoes_incomplete.response'))
|
||||
o.onebox.should match_html expected_incomplete_result
|
||||
end
|
||||
|
||||
private
|
||||
def expected_fresh_result
|
||||
<<EXPECTED
|
||||
<div class='onebox-result'>
|
||||
<div class='source'>
|
||||
<div class='info'>
|
||||
<a href='http://www.rottentomatoes.com/m/mud_2012/' class="track-link" target="_blank">
|
||||
<img class='favicon' src="/assets/favicons/rottentomatoes.png"> rottentomatoes.com
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class='onebox-result-body'>
|
||||
<img src="http://content7.flixster.com/movie/11/16/93/11169361_pro.jpg" class="thumbnail">
|
||||
<h3><a href="http://www.rottentomatoes.com/m/mud_2012/" target="_blank">Mud</a></h3>
|
||||
|
||||
<img class="verdict" src=http://images.rottentomatoescdn.com/images/icons/rt.fresh.med.png><b>98%</b> of critics liked it.
|
||||
<img class="popcorn" src=http://images.rottentomatoescdn.com/images/icons/popcorn_27x31.png><b>85%</b> of users liked it.<br />
|
||||
<b>Cast:</b> Matthew McConaughey, Reese Witherspoon<br />
|
||||
<b>Director:</b> Jeff Nichols<br />
|
||||
<b>Theater Release:</b> Apr 26, 2013<br />
|
||||
<b>Running Time:</b> 2 hr. 10 min.<br />
|
||||
<b>Rated: </b> PG-13<br />
|
||||
Mud is an adventure about two boys, Ellis and his friend Neckbone, who find a man named Mud hiding out on an island in the Mississippi. Mud describes fantastic scenarios-he killed a man in Texas and vengeful bounty hunters are coming to get him. He says he is planning to meet and escape with the love of his life, Juniper, who is waiting for him in town. Skeptical but i...
|
||||
</div>
|
||||
<div class='clearfix'></div>
|
||||
</div>
|
||||
EXPECTED
|
||||
end
|
||||
|
||||
def expected_rotten_result
|
||||
<<EXPECTED
|
||||
<div class='onebox-result'>
|
||||
<div class='source'>
|
||||
<div class='info'>
|
||||
<a href='http://www.rottentomatoes.com/m/the_big_wedding_2013/' class="track-link" target="_blank">
|
||||
<img class='favicon' src="/assets/favicons/rottentomatoes.png"> rottentomatoes.com
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class='onebox-result-body'>
|
||||
<img src="http://content8.flixster.com/movie/11/16/87/11168754_pro.jpg" class="thumbnail">
|
||||
<h3><a href="http://www.rottentomatoes.com/m/the_big_wedding_2013/" target="_blank">The Big Wedding</a></h3>
|
||||
|
||||
<img class="verdict" src=http://images.rottentomatoescdn.com/images/icons/rt.rotten.med.png><b>6%</b> of critics liked it.
|
||||
<img class="popcorn" src=http://images.rottentomatoescdn.com/images/icons/popcorn_27x31.png><b>80%</b> of users liked it.<br />
|
||||
<b>Cast:</b> Robert De Niro, Diane Keaton<br />
|
||||
<b>Director:</b> Justin Zackham<br />
|
||||
<b>Theater Release:</b> Apr 26, 2013<br />
|
||||
<b>Running Time:</b> 1 hr. 29 min.<br />
|
||||
<b>Rated: </b> R<br />
|
||||
With an all-star cast led by Robert De Niro, Katherine Heigl, Diane Keaton, Amanda Seyfried, Topher Grace, with Susan Sarandon and Robin Williams, THE BIG WEDDING is an uproarious romantic comedy about a charmingly modern family trying to survive a weekend wedding celebration that has the potential to become a full blown family fiasco. To the amusement of their adult c...
|
||||
</div>
|
||||
<div class='clearfix'></div>
|
||||
</div>
|
||||
EXPECTED
|
||||
end
|
||||
|
||||
def expected_incomplete_result
|
||||
<<EXPECTED
|
||||
<div class='onebox-result'>
|
||||
<div class='source'>
|
||||
<div class='info'>
|
||||
<a href='http://www.rottentomatoes.com/m/gunde_jaari_gallanthayyinde/' class="track-link" target="_blank">
|
||||
<img class='favicon' src="/assets/favicons/rottentomatoes.png"> rottentomatoes.com
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class='onebox-result-body'>
|
||||
<img src="http://images.rottentomatoescdn.com/images/redesign/poster_default.gif" class="thumbnail">
|
||||
<h3><a href="http://www.rottentomatoes.com/m/gunde_jaari_gallanthayyinde/" target="_blank">Gunde Jaari Gallanthayyinde</a></h3>
|
||||
|
||||
|
||||
|
||||
<b>Cast:</b> Nithin, Nithya Menon<br />
|
||||
<b>Director:</b> Vijay Kumar Konda<br />
|
||||
<b>Theater Release:</b> Apr 19, 2013<br />
|
||||
<b>Running Time:</b> 2 hr. 35 min.<br />
|
||||
<b>Rated: </b> Unrated<br />
|
||||
Software engineer Karthik thinks he is the smartest guy on the earth, but he turns out be the biggest fool at the end.
|
||||
</div>
|
||||
<div class='clearfix'></div>
|
||||
</div>
|
||||
EXPECTED
|
||||
end
|
||||
end
|
|
@ -1,73 +0,0 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe Oneboxer::StackExchangeOnebox do
|
||||
describe '#translate_url' do
|
||||
let(:question) { '15622543' }
|
||||
let(:api_url) {
|
||||
"http://api.stackexchange.com/2.1/questions/#{question}?site=#{site}"
|
||||
}
|
||||
|
||||
context 'when the question is from Stack Overflow' do
|
||||
let(:site) { 'stackoverflow' }
|
||||
|
||||
it 'returns the correct api url for an expanded url' do
|
||||
onebox = described_class.new([
|
||||
"http://#{site}.com/",
|
||||
"questions/#{question}/discourse-ruby-2-0-rails-4"
|
||||
].join)
|
||||
|
||||
expect(onebox.translate_url).to eq api_url
|
||||
end
|
||||
|
||||
it 'returns the correct api url for a share url' do
|
||||
onebox = described_class.new("http://#{site}.com/q/#{question}")
|
||||
|
||||
expect(onebox.translate_url).to eq api_url
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the question is from Super User' do
|
||||
let(:site) { 'superuser' }
|
||||
|
||||
it 'returns the correct api url' do
|
||||
onebox = described_class.new("http://#{site}.com/q/#{question}")
|
||||
|
||||
expect(onebox.translate_url).to eq api_url
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the question is from Meta Stack Overflow' do
|
||||
let(:site) { 'meta.stackoverflow' }
|
||||
|
||||
it 'returns the correct api url' do
|
||||
onebox = described_class.new("http://meta.stackoverflow.com/q/#{question}")
|
||||
|
||||
expect(onebox.translate_url).to eq api_url
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the question is from a Meta Stack Exchange subdomain' do
|
||||
let(:site) { 'meta.gamedev' }
|
||||
|
||||
it 'returns the correct api url' do
|
||||
onebox = described_class.new("http://meta.gamedev.stackexchange.com/q/#{question}")
|
||||
|
||||
expect(onebox.translate_url).to eq api_url
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
context 'when the question is from a Stack Exchange subdomain' do
|
||||
let(:site) { 'gamedev' }
|
||||
|
||||
it 'returns the correct api url' do
|
||||
onebox = described_class.new([
|
||||
"http://#{site}.stackexchange.com/",
|
||||
"questions/#{question}/how-to-prevent-the-too-awesome-to-use-syndrome"
|
||||
].join)
|
||||
|
||||
expect(onebox.translate_url).to eq api_url
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,56 +0,0 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe Oneboxer::TwitterOnebox do
|
||||
subject { described_class.new(nil, nil) }
|
||||
|
||||
let(:data) { %({ "text":"#{text}", "created_at":"#{created_at}" }) }
|
||||
|
||||
let(:text) { '' }
|
||||
let(:created_at) { '2013-06-13T22:37:05Z' }
|
||||
|
||||
describe '#parse' do
|
||||
it 'formats the timestamp' do
|
||||
expect(subject.parse(data)['created_at']).to eq '10:37PM - 13 Jun 13'
|
||||
end
|
||||
|
||||
context 'when text contains a url' do
|
||||
let(:text) { 'Twitter http://twitter.com' }
|
||||
|
||||
it 'wraps eack url in a link' do
|
||||
expect(subject.parse(data)['text']).to eq([
|
||||
"Twitter ",
|
||||
'<a href="http://twitter.com" target="_blank">',
|
||||
"http://twitter.com",
|
||||
"</a>"
|
||||
].join)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the text contains a twitter handle' do
|
||||
let(:text) { 'I like @chrishunt' }
|
||||
|
||||
it 'wraps each handle in a link' do
|
||||
expect(subject.parse(data)['text']).to eq([
|
||||
"I like ",
|
||||
"<a href='https://twitter.com/chrishunt' target='_blank'>",
|
||||
"@chrishunt",
|
||||
"</a>"
|
||||
].join)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the text contains a hashtag' do
|
||||
let(:text) { 'No secrets. #NSA' }
|
||||
|
||||
it 'wraps each hashtag in a link' do
|
||||
expect(subject.parse(data)['text']).to eq([
|
||||
"No secrets. ",
|
||||
"<a href='https://twitter.com/search?q=%23NSA' target='_blank'>",
|
||||
"#NSA",
|
||||
"</a>"
|
||||
].join)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
require 'spec_helper'
|
||||
require 'oneboxer'
|
||||
require 'oneboxer/whitelist'
|
||||
|
||||
describe Oneboxer::Whitelist do
|
||||
it "matches an arbitrary Discourse post link" do
|
||||
Oneboxer::Whitelist.entry_for_url('http://meta.discourse.org/t/scrolling-up-not-loading-in-firefox/3340/6?123').should_not be_nil
|
||||
end
|
||||
|
||||
it "matches an arbitrary Discourse topic link" do
|
||||
Oneboxer::Whitelist.entry_for_url('http://meta.discourse.org/t/scrolling-up-not-loading-in-firefox/3340?123').should_not be_nil
|
||||
end
|
||||
|
||||
it "Does not match on slight variation" do
|
||||
Oneboxer::Whitelist.entry_for_url('http://meta.discourse.org/t/scrolling-up-not-loading-in-firefox/3340a?123').should be_nil
|
||||
end
|
||||
|
||||
end
|
|
@ -1,57 +0,0 @@
|
|||
# encoding: utf-8
|
||||
|
||||
require 'spec_helper'
|
||||
require 'oneboxer'
|
||||
require 'oneboxer/wikipedia_onebox'
|
||||
|
||||
describe Oneboxer::WikipediaOnebox do
|
||||
|
||||
it "generates the expected onebox for Wikipedia" do
|
||||
o = Oneboxer::WikipediaOnebox.new('http://en.wikipedia.org/wiki/Ruby')
|
||||
FakeWeb.register_uri(:get, o.translate_url, response: fixture_file('oneboxer/wikipedia.response'))
|
||||
FakeWeb.register_uri(:get, 'http://en.m.wikipedia.org/wiki/Ruby', response: fixture_file('oneboxer/wikipedia_redirected.response'))
|
||||
o.onebox.should match_html expected_wikipedia_result
|
||||
end
|
||||
|
||||
it "accepts .com extention" do
|
||||
o = Oneboxer::WikipediaOnebox.new('http://en.wikipedia.com/wiki/Postgres')
|
||||
o.translate_url.should == 'http://en.m.wikipedia.org/w/index.php?title=Postgres'
|
||||
end
|
||||
|
||||
it "encodes identifier" do
|
||||
o = Oneboxer::WikipediaOnebox.new('http://en.wikipedia.com/wiki/Café')
|
||||
o.translate_url.should == 'http://en.m.wikipedia.org/w/index.php?title=Caf%C3%A9'
|
||||
end
|
||||
|
||||
it "defaults to en locale" do
|
||||
o = Oneboxer::WikipediaOnebox.new('http://wikipedia.org/wiki/Ruby_on_rails')
|
||||
o.translate_url.should == 'http://en.m.wikipedia.org/w/index.php?title=Ruby_on_rails'
|
||||
end
|
||||
|
||||
it "generates localized url" do
|
||||
o = Oneboxer::WikipediaOnebox.new('http://fr.wikipedia.org/wiki/Ruby')
|
||||
o.translate_url.should == 'http://fr.m.wikipedia.org/w/index.php?title=Ruby'
|
||||
end
|
||||
|
||||
private
|
||||
def expected_wikipedia_result
|
||||
<<EXPECTED
|
||||
<div class='onebox-result'>
|
||||
<div class='source'>
|
||||
<div class='info'>
|
||||
<a href='http://en.wikipedia.org/wiki/Ruby' class="track-link" target="_blank">
|
||||
<img class='favicon' src="/assets/favicons/wikipedia.png"> en.wikipedia.org
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class='onebox-result-body'>
|
||||
<img src="http://upload.wikimedia.org/wikipedia/commons/thumb/8/80/Ruby_-_Winza%2C_Tanzania.jpg/220px-Ruby_-_Winza%2C_Tanzania.jpg" class="thumbnail">
|
||||
<h3><a href="http://en.wikipedia.org/wiki/Ruby" target="_blank">Ruby</a></h3>
|
||||
|
||||
A ruby is a pink to blood-red colored gemstone, a variety of the mineral corundum (aluminium oxide). The red color is caused mainly by the presence of the element chromium. Its name comes from ruber, Latin for red. Other varieties of gem-quality corundum are called sapphires. The ruby is considered one of the four precious stones, together with the sapphire, the emerald, and the diamond. Prices of rubies are primarily determined by color. The brightest and most valuable "red" called pigeon blood-...
|
||||
</div>
|
||||
<div class='clearfix'></div>
|
||||
</div>
|
||||
EXPECTED
|
||||
end
|
||||
end
|
|
@ -1,206 +0,0 @@
|
|||
require 'spec_helper'
|
||||
require 'oneboxer'
|
||||
|
||||
describe "Dynamic Oneboxer" do
|
||||
class DummyDynamicOnebox < Oneboxer::BaseOnebox
|
||||
matcher do
|
||||
/^https?:\/\/dummy2.localhost/
|
||||
end
|
||||
|
||||
def onebox
|
||||
"dummy2!"
|
||||
end
|
||||
end
|
||||
|
||||
before do
|
||||
Oneboxer.add_onebox DummyDynamicOnebox
|
||||
@dummy_onebox_url = "http://dummy2.localhost/dummy-object"
|
||||
end
|
||||
|
||||
context 'find onebox for url' do
|
||||
|
||||
it 'returns blank with an unknown url' do
|
||||
Oneboxer.onebox_for_url('http://asdfasdfasdfasdf.asdf').should be_blank
|
||||
end
|
||||
|
||||
it 'returns something when matched' do
|
||||
Oneboxer.onebox_for_url(@dummy_onebox_url).should be_present
|
||||
end
|
||||
|
||||
it 'returns an instance of our class when matched' do
|
||||
Oneboxer.onebox_for_url(@dummy_onebox_url).kind_of?(DummyDynamicOnebox).should be_true
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
describe Oneboxer do
|
||||
|
||||
# A class to help us test
|
||||
class DummyOnebox < Oneboxer::BaseOnebox
|
||||
matcher /^https?:\/\/dummy.localhost/
|
||||
|
||||
def onebox
|
||||
"dummy!"
|
||||
end
|
||||
end
|
||||
|
||||
let(:dummy_onebox_url) { "http://dummy.localhost/dummy-object" }
|
||||
|
||||
before do
|
||||
Oneboxer.add_onebox DummyOnebox
|
||||
end
|
||||
|
||||
it 'should have matchers set up by default' do
|
||||
Oneboxer.matchers.should be_present
|
||||
end
|
||||
|
||||
context 'caching' do
|
||||
|
||||
let(:result) { "onebox result string" }
|
||||
|
||||
context "with invalidate_oneboxes true" do
|
||||
|
||||
it "invalidates the url" do
|
||||
Oneboxer.expects(:invalidate).with(dummy_onebox_url)
|
||||
Oneboxer.onebox(dummy_onebox_url, invalidate_oneboxes: true)
|
||||
end
|
||||
|
||||
it "doesn't render from cache" do
|
||||
Oneboxer.expects(:render_from_cache).never
|
||||
Oneboxer.onebox(dummy_onebox_url, invalidate_oneboxes: true)
|
||||
end
|
||||
|
||||
it "calls fetch and cache" do
|
||||
Oneboxer.expects(:fetch_and_cache).returns(result)
|
||||
Oneboxer.onebox(dummy_onebox_url, invalidate_oneboxes: true).should == result
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
context 'with invalidate_oneboxes false' do
|
||||
|
||||
it "doesn't invalidate the url" do
|
||||
Oneboxer.expects(:invalidate).with(dummy_onebox_url).never
|
||||
Oneboxer.onebox(dummy_onebox_url, invalidate_oneboxes: false)
|
||||
end
|
||||
|
||||
it "returns render_from_cache if present" do
|
||||
Oneboxer.expects(:render_from_cache).with(dummy_onebox_url).returns(result)
|
||||
Oneboxer.onebox(dummy_onebox_url, invalidate_oneboxes: false).should == result
|
||||
end
|
||||
|
||||
it "doesn't call fetch_and_cache" do
|
||||
Oneboxer.expects(:render_from_cache).with(dummy_onebox_url).returns(result)
|
||||
Oneboxer.expects(:fetch_and_cache).never
|
||||
Oneboxer.onebox(dummy_onebox_url, invalidate_oneboxes: false)
|
||||
end
|
||||
|
||||
|
||||
it "calls fetch_and_cache if render from cache is blank" do
|
||||
Oneboxer.stubs(:render_from_cache)
|
||||
Oneboxer.expects(:fetch_and_cache).returns(result)
|
||||
Oneboxer.onebox(dummy_onebox_url, invalidate_oneboxes: false).should == result
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
context 'find onebox for url' do
|
||||
|
||||
it 'returns blank with an unknown url' do
|
||||
Oneboxer.onebox_for_url('http://asdfasdfasdfasdf.asdf').should be_blank
|
||||
end
|
||||
|
||||
it 'returns something when matched' do
|
||||
Oneboxer.onebox_for_url(dummy_onebox_url).should be_present
|
||||
end
|
||||
|
||||
it 'returns an instance of our class when matched' do
|
||||
Oneboxer.onebox_for_url(dummy_onebox_url).kind_of?(DummyOnebox).should be_true
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
describe '#nice_host' do
|
||||
it 'strips www from the domain' do
|
||||
DummyOnebox.new('http://www.cnn.com/thing').nice_host.should eq 'cnn.com'
|
||||
end
|
||||
|
||||
it 'respects double TLDs' do
|
||||
DummyOnebox.new('http://news.bbc.co.uk/thing').nice_host.should eq 'news.bbc.co.uk'
|
||||
end
|
||||
|
||||
it 'returns an empty string if the URL is bogus' do
|
||||
DummyOnebox.new('whatever').nice_host.should eq ''
|
||||
end
|
||||
|
||||
it 'returns an empty string if the URL unparsable' do
|
||||
DummyOnebox.new(nil).nice_host.should eq ''
|
||||
end
|
||||
end
|
||||
|
||||
context 'without caching' do
|
||||
it 'calls the onebox method of our matched class' do
|
||||
Oneboxer.onebox_nocache(dummy_onebox_url).should == 'dummy!'
|
||||
end
|
||||
end
|
||||
|
||||
context 'each_onebox_link' do
|
||||
|
||||
before do
|
||||
@html = "<a href='http://discourse.org' class='onebox'>Discourse Link</a>"
|
||||
end
|
||||
|
||||
it 'yields each url and element when given a string' do
|
||||
result = Oneboxer.each_onebox_link(@html) do |url, element|
|
||||
element.is_a?(Nokogiri::XML::Element).should be_true
|
||||
url.should == 'http://discourse.org'
|
||||
end
|
||||
result.kind_of?(Nokogiri::HTML::DocumentFragment).should be_true
|
||||
end
|
||||
|
||||
it 'yields each url and element when given a doc' do
|
||||
doc = Nokogiri::HTML(@html)
|
||||
Oneboxer.each_onebox_link(doc) do |url, element|
|
||||
element.is_a?(Nokogiri::XML::Element).should be_true
|
||||
url.should == 'http://discourse.org'
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
context "apply_onebox" do
|
||||
it "is able to nuke wrapping p" do
|
||||
doc = Oneboxer.apply "<p><a href='http://bla.com' class='onebox'>bla</p>" do |url, element|
|
||||
"<div>foo</div>" if url == "http://bla.com"
|
||||
end
|
||||
|
||||
doc.changed? == true
|
||||
doc.to_html.should match_html "<div>foo</div>"
|
||||
end
|
||||
|
||||
it "is able to do nothing if nil is returned" do
|
||||
orig = "<p><a href='http://bla.com' class='onebox'>bla</p>"
|
||||
doc = Oneboxer.apply orig do |url, element|
|
||||
nil
|
||||
end
|
||||
|
||||
doc.changed? == false
|
||||
doc.to_html.should match_html orig
|
||||
end
|
||||
|
||||
it "does not strip if there is a br in same node" do
|
||||
doc = Oneboxer.apply "<p><br><a href='http://bla.com' class='onebox'>bla</p>" do |url, element|
|
||||
"<div>foo</div>" if url == "http://bla.com"
|
||||
end
|
||||
|
||||
doc.changed? == true
|
||||
doc.to_html.should match_html "<p><br><div>foo</div></p>"
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
|
@ -191,7 +191,9 @@ describe PostRevisor do
|
|||
|
||||
before do
|
||||
SiteSetting.stubs(:newuser_max_images).returns(0)
|
||||
subject.revise!(changed_by, "So, post them here!\nhttp://i.imgur.com/FGg7Vzu.gif")
|
||||
url = "http://i.imgur.com/wfn7rgU.jpg"
|
||||
Oneboxer.stubs(:onebox).with(url, anything).returns("<img src='#{url}'>")
|
||||
subject.revise!(changed_by, "So, post them here!\n#{url}")
|
||||
end
|
||||
|
||||
it "allows an admin to insert images into a new user's post" do
|
||||
|
@ -209,11 +211,11 @@ describe PostRevisor do
|
|||
SiteSetting.stubs(:newuser_max_images).returns(0)
|
||||
url = "http://i.imgur.com/FGg7Vzu.gif"
|
||||
# this test is problamatic, it leaves state in the onebox cache
|
||||
Oneboxer.invalidate(url)
|
||||
Oneboxer.stubs(:onebox).with(url, anything).returns("<img src='#{url}'>")
|
||||
subject.revise!(post.user, "So, post them here!\n#{url}")
|
||||
end
|
||||
|
||||
it "allows an admin to insert images into a new user's post" do
|
||||
it "doesn't allow images to be inserted" do
|
||||
post.errors.should be_present
|
||||
end
|
||||
|
||||
|
|
Loading…
Reference in New Issue