2018-11-18 11:00:15 +05:30
|
|
|
# frozen_string_literal: true
|
|
|
|
|
2015-09-11 14:41:01 +05:30
|
|
|
require 'uri'
|
|
|
|
|
2015-12-23 02:04:40 +05:30
|
|
|
module Banzai
|
|
|
|
module Filter
|
2018-03-17 18:26:18 +05:30
|
|
|
# HTML filter that "fixes" relative links to uploads or files in a repository.
|
2015-09-11 14:41:01 +05:30
|
|
|
#
|
|
|
|
# Context options:
|
|
|
|
# :commit
|
2018-03-17 18:26:18 +05:30
|
|
|
# :group
|
2015-09-11 14:41:01 +05:30
|
|
|
# :project
|
|
|
|
# :project_wiki
|
|
|
|
# :ref
|
|
|
|
# :requested_path
|
|
|
|
class RelativeLinkFilter < HTML::Pipeline::Filter
|
2018-03-17 18:26:18 +05:30
|
|
|
include Gitlab::Utils::StrongMemoize
|
2015-09-11 14:41:01 +05:30
|
|
|
|
2018-03-17 18:26:18 +05:30
|
|
|
def call
|
2016-06-22 15:30:34 +05:30
|
|
|
@uri_types = {}
|
2018-03-17 18:26:18 +05:30
|
|
|
clear_memoization(:linkable_files)
|
2016-06-22 15:30:34 +05:30
|
|
|
|
2015-12-23 02:04:40 +05:30
|
|
|
doc.search('a:not(.gfm)').each do |el|
|
2015-09-11 14:41:01 +05:30
|
|
|
process_link_attr el.attribute('href')
|
|
|
|
end
|
|
|
|
|
2016-08-24 12:49:21 +05:30
|
|
|
doc.css('img, video').each do |el|
|
2015-09-11 14:41:01 +05:30
|
|
|
process_link_attr el.attribute('src')
|
2017-09-10 17:25:29 +05:30
|
|
|
process_link_attr el.attribute('data-src')
|
2015-09-11 14:41:01 +05:30
|
|
|
end
|
|
|
|
|
|
|
|
doc
|
|
|
|
end
|
|
|
|
|
|
|
|
protected
|
|
|
|
|
|
|
|
def linkable_files?
|
2018-03-17 18:26:18 +05:30
|
|
|
strong_memoize(:linkable_files) do
|
|
|
|
context[:project_wiki].nil? && repository.try(:exists?) && !repository.empty?
|
|
|
|
end
|
2015-09-11 14:41:01 +05:30
|
|
|
end
|
|
|
|
|
|
|
|
def process_link_attr(html_attr)
|
|
|
|
return if html_attr.blank?
|
2016-09-13 17:45:13 +05:30
|
|
|
return if html_attr.value.start_with?('//')
|
2015-09-11 14:41:01 +05:30
|
|
|
|
2018-03-17 18:26:18 +05:30
|
|
|
if html_attr.value.start_with?('/uploads/')
|
|
|
|
process_link_to_upload_attr(html_attr)
|
|
|
|
elsif linkable_files?
|
|
|
|
process_link_to_repository_attr(html_attr)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def process_link_to_upload_attr(html_attr)
|
|
|
|
path_parts = [Addressable::URI.unescape(html_attr.value)]
|
|
|
|
|
|
|
|
if group
|
|
|
|
path_parts.unshift(relative_url_root, 'groups', group.full_path, '-')
|
|
|
|
elsif project
|
|
|
|
path_parts.unshift(relative_url_root, project.full_path)
|
2019-03-02 22:35:43 +05:30
|
|
|
else
|
|
|
|
path_parts.unshift(relative_url_root)
|
2018-03-17 18:26:18 +05:30
|
|
|
end
|
|
|
|
|
2018-12-05 23:21:45 +05:30
|
|
|
begin
|
|
|
|
path = Addressable::URI.escape(File.join(*path_parts))
|
|
|
|
rescue Addressable::URI::InvalidURIError
|
|
|
|
return
|
|
|
|
end
|
2018-03-17 18:26:18 +05:30
|
|
|
|
|
|
|
html_attr.value =
|
|
|
|
if context[:only_path]
|
|
|
|
path
|
|
|
|
else
|
|
|
|
Addressable::URI.join(Gitlab.config.gitlab.base_url, path).to_s
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def process_link_to_repository_attr(html_attr)
|
2015-09-11 14:41:01 +05:30
|
|
|
uri = URI(html_attr.value)
|
|
|
|
if uri.relative? && uri.path.present?
|
|
|
|
html_attr.value = rebuild_relative_uri(uri).to_s
|
|
|
|
end
|
2018-03-17 18:26:18 +05:30
|
|
|
rescue URI::Error, Addressable::URI::InvalidURIError
|
2015-09-11 14:41:01 +05:30
|
|
|
# noop
|
|
|
|
end
|
|
|
|
|
|
|
|
def rebuild_relative_uri(uri)
|
2017-08-17 22:00:37 +05:30
|
|
|
file_path = relative_file_path(uri)
|
2015-09-11 14:41:01 +05:30
|
|
|
|
|
|
|
uri.path = [
|
|
|
|
relative_url_root,
|
2018-03-17 18:26:18 +05:30
|
|
|
project.full_path,
|
2016-06-22 15:30:34 +05:30
|
|
|
uri_type(file_path),
|
2018-05-09 12:01:36 +05:30
|
|
|
Addressable::URI.escape(ref).gsub('#', '%23'),
|
2017-08-17 22:00:37 +05:30
|
|
|
Addressable::URI.escape(file_path)
|
2015-09-11 14:41:01 +05:30
|
|
|
].compact.join('/').squeeze('/').chomp('/')
|
|
|
|
|
|
|
|
uri
|
|
|
|
end
|
|
|
|
|
2017-08-17 22:00:37 +05:30
|
|
|
def relative_file_path(uri)
|
|
|
|
path = Addressable::URI.unescape(uri.path)
|
|
|
|
request_path = Addressable::URI.unescape(context[:requested_path])
|
|
|
|
nested_path = build_relative_path(path, request_path)
|
2015-09-11 14:41:01 +05:30
|
|
|
file_exists?(nested_path) ? nested_path : path
|
|
|
|
end
|
|
|
|
|
2015-09-25 12:07:36 +05:30
|
|
|
# Convert a relative path into its correct location based on the currently
|
|
|
|
# requested path
|
|
|
|
#
|
|
|
|
# path - Relative path String
|
|
|
|
# request_path - Currently-requested path String
|
|
|
|
#
|
|
|
|
# Examples:
|
|
|
|
#
|
|
|
|
# # File in the same directory as the current path
|
|
|
|
# build_relative_path("users.md", "doc/api/README.md")
|
|
|
|
# # => "doc/api/users.md"
|
|
|
|
#
|
|
|
|
# # File in the same directory, which is also the current path
|
|
|
|
# build_relative_path("users.md", "doc/api")
|
|
|
|
# # => "doc/api/users.md"
|
|
|
|
#
|
|
|
|
# # Going up one level to a different directory
|
|
|
|
# build_relative_path("../update/7.14-to-8.0.md", "doc/api/README.md")
|
|
|
|
# # => "doc/update/7.14-to-8.0.md"
|
|
|
|
#
|
|
|
|
# Returns a String
|
|
|
|
def build_relative_path(path, request_path)
|
2015-09-11 14:41:01 +05:30
|
|
|
return request_path if path.empty?
|
|
|
|
return path unless request_path
|
2016-09-13 17:45:13 +05:30
|
|
|
return path[1..-1] if path.start_with?('/')
|
2015-09-11 14:41:01 +05:30
|
|
|
|
|
|
|
parts = request_path.split('/')
|
2016-06-22 15:30:34 +05:30
|
|
|
parts.pop if uri_type(request_path) != :tree
|
2015-09-25 12:07:36 +05:30
|
|
|
|
2016-09-13 17:45:13 +05:30
|
|
|
path.sub!(%r{\A\./}, '')
|
|
|
|
|
2016-01-14 18:37:52 +05:30
|
|
|
while path.start_with?('../')
|
2015-09-25 12:07:36 +05:30
|
|
|
parts.pop
|
|
|
|
path.sub!('../', '')
|
|
|
|
end
|
|
|
|
|
2015-09-11 14:41:01 +05:30
|
|
|
parts.push(path).join('/')
|
|
|
|
end
|
|
|
|
|
|
|
|
def file_exists?(path)
|
2016-06-22 15:30:34 +05:30
|
|
|
path.present? && !!uri_type(path)
|
2015-09-11 14:41:01 +05:30
|
|
|
end
|
|
|
|
|
2016-06-22 15:30:34 +05:30
|
|
|
def uri_type(path)
|
2019-05-18 00:54:41 +05:30
|
|
|
# https://gitlab.com/gitlab-org/gitlab-ce/issues/58011
|
|
|
|
Gitlab::GitalyClient.allow_n_plus_1_calls do
|
|
|
|
@uri_types[path] ||= current_commit.uri_type(path)
|
|
|
|
end
|
2015-09-11 14:41:01 +05:30
|
|
|
end
|
|
|
|
|
2016-06-22 15:30:34 +05:30
|
|
|
def current_commit
|
2016-09-13 17:45:13 +05:30
|
|
|
@current_commit ||= context[:commit] || repository.commit(ref)
|
2015-09-11 14:41:01 +05:30
|
|
|
end
|
|
|
|
|
|
|
|
def relative_url_root
|
|
|
|
Gitlab.config.gitlab.relative_url_root.presence || '/'
|
|
|
|
end
|
|
|
|
|
|
|
|
def ref
|
2018-03-17 18:26:18 +05:30
|
|
|
context[:ref] || project.default_branch
|
|
|
|
end
|
|
|
|
|
|
|
|
def group
|
|
|
|
context[:group]
|
|
|
|
end
|
|
|
|
|
|
|
|
def project
|
|
|
|
context[:project]
|
2015-09-11 14:41:01 +05:30
|
|
|
end
|
|
|
|
|
|
|
|
def repository
|
2018-03-17 18:26:18 +05:30
|
|
|
@repository ||= project&.repository
|
2015-09-11 14:41:01 +05:30
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|