mirror of
https://github.com/discourse/discourse.git
synced 2025-05-30 15:28:37 +08:00
DEV: Absorb onebox gem into core (#12979)
* Move onebox gem in core library * Update template file path * Remove warning for onebox gem caching * Remove onebox version file * Remove onebox gem * Add sanitize gem * Require onebox library in lazy-yt plugin * Remove onebox web specific code This code was used in standalone onebox Sinatra application * Merge Discourse specific AllowlistedGenericOnebox engine in core * Fix onebox engine filenames to match class name casing * Move onebox specs from gem into core * DEV: Rename `response` helper to `onebox_response` Fixes a naming collision. * Require rails_helper * Don't use `before/after(:all)` * Whitespace * Remove fakeweb * Remove poor unit tests * DEV: Re-add fakeweb, plugins are using it * Move onebox helpers * Stub Instagram API * FIX: Follow additional redirect status codes (#476) Don’t throw errors if we encounter 303, 307 or 308 HTTP status codes in responses * Remove an empty file * DEV: Update the license file Using the copy from https://choosealicense.com/licenses/gpl-2.0/# Hopefully this will enable GitHub to show the license UI? * DEV: Update embedded copyrights * DEV: Add Onebox copyright notice * DEV: Add MIT license, convert COPYRIGHT.txt to md * DEV: Remove an incorrect copyright claim Co-authored-by: Jarek Radosz <jradosz@gmail.com> Co-authored-by: jbrw <jamie@goatforce5.org>
This commit is contained in:
184
lib/onebox/engine/google_maps_onebox.rb
Normal file
184
lib/onebox/engine/google_maps_onebox.rb
Normal file
@ -0,0 +1,184 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Onebox
|
||||
module Engine
|
||||
class GoogleMapsOnebox
|
||||
include Engine
|
||||
|
||||
class << self
|
||||
def ===(other)
|
||||
if other.kind_of? URI
|
||||
@@matchers && @@matchers.any? { |m| other.to_s =~ m[:regexp] }
|
||||
else
|
||||
super
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def matches_regexp(key, regexp)
|
||||
(@@matchers ||= []) << { key: key, regexp: regexp }
|
||||
end
|
||||
end
|
||||
|
||||
always_https
|
||||
requires_iframe_origins("https://maps.google.com", "https://google.com")
|
||||
|
||||
# Matches shortened Google Maps URLs
|
||||
matches_regexp :short, %r"^(https?:)?//goo\.gl/maps/"
|
||||
|
||||
# Matches URLs for custom-created maps
|
||||
matches_regexp :custom, %r"^(?:https?:)?//www\.google(?:\.(?:\w{2,}))+/maps/d/(?:edit|viewer|embed)\?mid=.+$"
|
||||
|
||||
# Matches URLs with streetview data
|
||||
matches_regexp :streetview, %r"^(?:https?:)?//www\.google(?:\.(?:\w{2,}))+/maps[^@]+@(?<lon>-?[\d.]+),(?<lat>-?[\d.]+),(?:\d+)a,(?<zoom>[\d.]+)y,(?<heading>[\d.]+)h,(?<pitch>[\d.]+)t.+?data=.*?!1s(?<pano>[^!]{22})"
|
||||
|
||||
# Matches "normal" Google Maps URLs with arbitrary data
|
||||
matches_regexp :standard, %r"^(?:https?:)?//www\.google(?:\.(?:\w{2,}))+/maps"
|
||||
|
||||
# Matches URLs for the old Google Maps domain which we occasionally get redirected to
|
||||
matches_regexp :canonical, %r"^(?:https?:)?//maps\.google(?:\.(?:\w{2,}))+/maps\?"
|
||||
|
||||
def initialize(url, timeout = nil)
|
||||
super
|
||||
resolve_url!
|
||||
rescue Net::HTTPServerException, Timeout::Error, Net::HTTPError, Errno::ECONNREFUSED, RuntimeError => err
|
||||
raise ArgumentError, "malformed url or unresolveable: #{err.message}"
|
||||
end
|
||||
|
||||
def streetview?
|
||||
!!@streetview
|
||||
end
|
||||
|
||||
def to_html
|
||||
"<div class='maps-onebox'><iframe src=\"#{link}\" width=\"690\" height=\"400\" frameborder=\"0\" style=\"border:0\">#{placeholder_html}</iframe></div>"
|
||||
end
|
||||
|
||||
def placeholder_html
|
||||
::Onebox::Helpers.map_placeholder_html
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def data
|
||||
{ link: url, title: url }
|
||||
end
|
||||
|
||||
def resolve_url!
|
||||
@streetview = false
|
||||
type, match = match_url
|
||||
|
||||
# Resolve shortened URL, if necessary
|
||||
if type == :short
|
||||
follow_redirect!
|
||||
type, match = match_url
|
||||
end
|
||||
|
||||
# Try to get the old-maps URI, it is far easier to embed.
|
||||
if type == :standard
|
||||
retry_count = 10
|
||||
while (retry_count -= 1) > 0
|
||||
follow_redirect!
|
||||
type, match = match_url
|
||||
break if type != :standard
|
||||
sleep 0.1
|
||||
end
|
||||
end
|
||||
|
||||
case type
|
||||
when :standard
|
||||
# Fallback for map URLs that don't resolve into an easily embeddable old-style URI
|
||||
# Roadmaps use a "z" zoomlevel, satellite maps use "m" the horizontal width in meters
|
||||
# TODO: tilted satellite maps using "a,y,t"
|
||||
match = @url.match(/@(?<lon>[\d.-]+),(?<lat>[\d.-]+),(?<zoom>\d+)(?<mz>[mz])/)
|
||||
raise "unexpected standard url #{@url}" unless match
|
||||
zoom = match[:mz] == "z" ? match[:zoom] : Math.log2(57280048.0 / match[:zoom].to_f).round
|
||||
location = "#{match[:lon]},#{match[:lat]}"
|
||||
url = "https://maps.google.com/maps?ll=#{location}&z=#{zoom}&output=embed&dg=ntvb"
|
||||
url += "&q=#{$1}" if match = @url.match(/\/place\/([^\/\?]+)/)
|
||||
url += "&cid=#{($1 + $2).to_i(16)}" if @url.match(/!3m1!1s0x(\h{16}):0x(\h{16})/)
|
||||
@url = url
|
||||
@placeholder = "https://maps.googleapis.com/maps/api/staticmap?maptype=roadmap¢er=#{location}&zoom=#{zoom}&size=690x400&sensor=false"
|
||||
|
||||
when :custom
|
||||
url = @url.dup
|
||||
@url = rewrite_custom_url(url, "embed")
|
||||
@placeholder = rewrite_custom_url(url, "thumbnail")
|
||||
@placeholder_height = @placeholder_width = 120
|
||||
|
||||
when :streetview
|
||||
@streetview = true
|
||||
panoid = match[:pano]
|
||||
lon = match[:lon].to_f.to_s
|
||||
lat = match[:lat].to_f.to_s
|
||||
heading = match[:heading].to_f.round(4).to_s
|
||||
pitch = (match[:pitch].to_f / 10.0).round(4).to_s
|
||||
fov = (match[:zoom].to_f / 100.0).round(4).to_s
|
||||
zoom = match[:zoom].to_f.round
|
||||
@url = "https://www.google.com/maps/embed?pb=!3m2!2sen!4v0!6m8!1m7!1s#{panoid}!2m2!1d#{lon}!2d#{lat}!3f#{heading}!4f#{pitch}!5f#{fov}"
|
||||
@placeholder = "https://maps.googleapis.com/maps/api/streetview?size=690x400&location=#{lon},#{lat}&pano=#{panoid}&fov=#{zoom}&heading=#{heading}&pitch=#{pitch}&sensor=false"
|
||||
|
||||
when :canonical
|
||||
query = URI::decode_www_form(uri.query).to_h
|
||||
if !query.has_key?("ll")
|
||||
raise ArgumentError, "canonical url lacks location argument" unless query.has_key?("sll")
|
||||
query["ll"] = query["sll"]
|
||||
@url += "&ll=#{query["sll"]}"
|
||||
end
|
||||
location = query["ll"]
|
||||
if !query.has_key?("z")
|
||||
raise ArgumentError, "canonical url has incomplete query arguments" unless query.has_key?("spn") || query.has_key?("sspn")
|
||||
if !query.has_key?("spn")
|
||||
query["spn"] = query["sspn"]
|
||||
@url += "&spn=#{query["sspn"]}"
|
||||
end
|
||||
angle = query["spn"].split(",").first.to_f
|
||||
zoom = (Math.log(690.0 * 360.0 / angle / 256.0) / Math.log(2)).round
|
||||
else
|
||||
zoom = query["z"]
|
||||
end
|
||||
@url = @url.sub('output=classic', 'output=embed')
|
||||
@placeholder = "https://maps.googleapis.com/maps/api/staticmap?maptype=roadmap&size=690x400&sensor=false¢er=#{location}&zoom=#{zoom}"
|
||||
|
||||
else
|
||||
raise "unexpected url type #{type.inspect}"
|
||||
end
|
||||
end
|
||||
|
||||
def match_url
|
||||
@@matchers.each do |matcher|
|
||||
if m = matcher[:regexp].match(@url)
|
||||
return matcher[:key], m
|
||||
end
|
||||
end
|
||||
raise ArgumentError, "\"#{@url}\" does not match any known pattern"
|
||||
end
|
||||
|
||||
def rewrite_custom_url(url, target)
|
||||
uri = URI(url)
|
||||
uri.path = uri.path.sub(/(?<=^\/maps\/d\/)\w+$/, target)
|
||||
uri.to_s
|
||||
end
|
||||
|
||||
def follow_redirect!
|
||||
begin
|
||||
http = Net::HTTP.start(
|
||||
uri.host,
|
||||
uri.port,
|
||||
use_ssl: uri.scheme == 'https',
|
||||
open_timeout: timeout,
|
||||
read_timeout: timeout
|
||||
)
|
||||
|
||||
response = http.head(uri.path)
|
||||
raise "unexpected response code #{response.code}" unless %w(200 301 302).include?(response.code)
|
||||
|
||||
@url = response.code == "200" ? uri.to_s : response["Location"]
|
||||
@uri = URI(@url)
|
||||
ensure
|
||||
http.finish rescue nil
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
Reference in New Issue
Block a user