debian-mirror-gitlab/lib/banzai/filter/relative_link_filter.rb

136 lines
3.4 KiB
Ruby
Raw Normal View History

2015-09-11 14:41:01 +05:30
require 'uri'
2015-12-23 02:04:40 +05:30
module Banzai
module Filter
2015-09-11 14:41:01 +05:30
# HTML filter that "fixes" relative links to files in a repository.
#
# Context options:
# :commit
# :project
# :project_wiki
# :ref
# :requested_path
class RelativeLinkFilter < HTML::Pipeline::Filter
def call
return doc unless linkable_files?
2016-06-22 15:30:34 +05:30
@uri_types = {}
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')
end
doc
end
protected
def linkable_files?
context[:project_wiki].nil? && repository.try(:exists?) && !repository.empty?
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
uri = URI(html_attr.value)
if uri.relative? && uri.path.present?
html_attr.value = rebuild_relative_uri(uri).to_s
end
rescue URI::Error
# noop
end
def rebuild_relative_uri(uri)
file_path = relative_file_path(uri.path)
uri.path = [
relative_url_root,
context[:project].path_with_namespace,
2016-06-22 15:30:34 +05:30
uri_type(file_path),
2016-09-13 17:45:13 +05:30
ref,
2015-09-11 14:41:01 +05:30
file_path
].compact.join('/').squeeze('/').chomp('/')
uri
end
def relative_file_path(path)
2015-09-25 12:07:36 +05:30
nested_path = build_relative_path(path, context[:requested_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\./}, '')
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)
@uri_types[path] ||= begin
unescaped_path = Addressable::URI.unescape(path)
2015-09-11 14:41:01 +05:30
2016-06-22 15:30:34 +05:30
current_commit.uri_type(unescaped_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
2016-09-13 17:45:13 +05:30
context[:ref] || context[:project].default_branch
2015-09-11 14:41:01 +05:30
end
def repository
2016-06-22 15:30:34 +05:30
@repository ||= context[:project].try(:repository)
2015-09-11 14:41:01 +05:30
end
end
end
end