171 lines
5.9 KiB
Ruby
171 lines
5.9 KiB
Ruby
require 'uri'
|
|
|
|
module Carto
|
|
module Api
|
|
class OembedController < ::Api::ApplicationController
|
|
include VisualizationSearcher
|
|
|
|
ssl_allowed :show
|
|
|
|
skip_before_filter :api_authorization_required
|
|
|
|
# Returns oembed data as required
|
|
def show
|
|
url = params[:url]
|
|
width = params[:maxwidth] || '100%'
|
|
height = params[:maxheight] || '520px'
|
|
format = request.query_parameters[:format]
|
|
force_https = true if params[:allow_http].nil?
|
|
|
|
raise ActionController::RoutingError.new('Incorrect width') if (width =~ /\A[0-9]+(%|px)?\z/).nil?
|
|
raise ActionController::RoutingError.new('Incorrect height') if (height =~ /\A[0-9]+(%|px)?\z/).nil?
|
|
|
|
uri = URI.parse(url)
|
|
|
|
uuid = extract_uuid_from_string(uri.path)
|
|
raise ActionController::RoutingError.new('UUID not found in URL') if uuid.nil?
|
|
|
|
vis = Carto::Visualization.where(id: @id).first
|
|
if !vis.nil?
|
|
name = vis.name
|
|
end
|
|
|
|
fields = url_fields_from_fragments(url, force_https)
|
|
|
|
# build the url using full schema because any visuaization should work with any user
|
|
if fields[:organization_name].nil?
|
|
url = CartoDB.base_url(fields[:username])
|
|
else
|
|
url = CartoDB.base_url(fields[:organization_name], fields[:username])
|
|
end
|
|
url += CartoDB.path(self, 'public_visualizations_embed_map', id: uuid, user_domain: nil)
|
|
|
|
# force the schema
|
|
if fields[:protocol] == 'https' && !url.include?('https')
|
|
url = url.sub('http', 'https')
|
|
end
|
|
|
|
html = "<iframe width='#{width}' height='#{height}' frameborder='0' src='#{url}' allowfullscreen webkitallowfullscreen mozallowfullscreen oallowfullscreen msallowfullscreen></iframe>"
|
|
|
|
response_data = {
|
|
:type => 'rich',
|
|
:version => '1.0',
|
|
:width => width,
|
|
:height => height,
|
|
:title => name,
|
|
:html => html,
|
|
:author_name => fields[:username],
|
|
:author_url => fields[:user_profile_url],
|
|
:provider_name => 'CartoDB',
|
|
:provider_url => "#{fields[:protocol]}://carto.com/"
|
|
}
|
|
|
|
if format == 'xml'
|
|
render xml: response_data.to_xml(root: 'oembed')
|
|
else
|
|
render json: response_data.to_json, :callback => params['callback']
|
|
end
|
|
end
|
|
|
|
private
|
|
|
|
def url_fields_from_fragments(url, force_https)
|
|
domain = CartoDB.session_domain
|
|
# @see http://ruby-doc.org/stdlib-1.9.3/libdoc/uri/rdoc/URI.html#method-c-split
|
|
url_fragments = URI.split(url)
|
|
protocol = force_https ? "https" : URI.parse(url).scheme
|
|
|
|
data = nil
|
|
unless CartoDB.subdomainless_urls?
|
|
begin
|
|
data = from_url(url_fragments, protocol, domain)
|
|
rescue UrlFRagmentsError
|
|
# URL is subdomainless so do nothing
|
|
end
|
|
end
|
|
|
|
# Either subdomains disallowed or url doesn't uses them
|
|
if data.nil?
|
|
data = from_domainless_url(url_fragments, protocol)
|
|
end
|
|
|
|
raise UrlFRagmentsError.new("Couldn't extract URL fields") if data.nil?
|
|
|
|
{
|
|
organization_name: data[:organization_name],
|
|
username: data[:username],
|
|
user_profile_url: data[:user_profile_url],
|
|
protocol: protocol
|
|
}
|
|
end
|
|
|
|
# testuser.carto.com || testorg.carto.com/u/user
|
|
def from_url(url_fragments, protocol, domain)
|
|
# To ease testing don't request eactly all URI.split params
|
|
raise UrlFRagmentsError.new("Invalid url_fragments parameter") unless url_fragments.length > 5
|
|
|
|
subdomain = url_fragments[2].sub(domain, '.').split('.')[0]
|
|
raise UrlFRagmentsError.new("Subdomain not found at url") if subdomain.nil?
|
|
|
|
# org-based
|
|
if url_fragments[5][0..2] == "/u/"
|
|
organization_name = subdomain
|
|
username = username_from_url_fragments(url_fragments)
|
|
else
|
|
organization_name = nil
|
|
username = subdomain
|
|
end
|
|
|
|
{
|
|
username: username,
|
|
organization_name: organization_name,
|
|
user_profile_url: CartoDB.base_url(subdomain, organization_name.nil? ? nil : username, protocol)
|
|
}
|
|
end
|
|
|
|
# https://carto.com/u/testuser/...
|
|
def from_domainless_url(url_fragments, protocol)
|
|
# To ease testing don't request eactly all URI.split params
|
|
raise UrlFRagmentsError.new("Invalid url_fragments parameter") unless url_fragments.length > 5
|
|
|
|
# url_fragments[5]: Path
|
|
raise UrlFRagmentsError.new("URL needs username specified in the Path") if url_fragments[5][0..2] != "/u/"
|
|
|
|
# url_fragments[3]: Host
|
|
port_fragment =
|
|
url_fragments[3].nil? || url_fragments[3] == '' || url_fragments[3].to_i == 80 ? '' : ":#{url_fragments[3]}"
|
|
|
|
username = username_from_url_fragments(url_fragments)
|
|
{
|
|
username: username,
|
|
organization_name: nil,
|
|
# url_fragments[2]: Host
|
|
user_profile_url: "#{protocol}://#{url_fragments[2]}#{port_fragment}/u/#{username}"
|
|
}
|
|
end
|
|
|
|
def username_from_url_fragments(url_fragments)
|
|
path_fragments = url_fragments[5].split('/')
|
|
raise UrlFRagmentsError.new("Username not found at url") if path_fragments.length < 3 || path_fragments[2].length == 0
|
|
path_fragments[2]
|
|
end
|
|
|
|
def extract_uuid_from_string(str)
|
|
# TODO: move this method to app/helpers/carto/uuidhelper.rb. Not used yet because this changed was pushed before
|
|
# UUIDTools::UUID_REGEXP cannot be reused because of /^ $/
|
|
matches = /([0-9a-f]{8})-([0-9a-f]{4})-([0-9a-f]{4})-([0-9a-f]{2})([0-9a-f]{2})-([0-9a-f]{12})/.match(str)
|
|
if matches
|
|
matches[0]
|
|
else
|
|
nil
|
|
end
|
|
end
|
|
|
|
end
|
|
|
|
class UrlFRagmentsError < StandardError
|
|
end
|
|
|
|
end
|
|
end
|