2016-03-07 07:09:53 -05:00
|
|
|
module Gitlab
|
|
|
|
module Gfm
|
|
|
|
##
|
2016-03-16 05:24:44 -04:00
|
|
|
# Class that unfolds local references in text.
|
2016-03-07 07:09:53 -05:00
|
|
|
#
|
2016-03-16 05:24:44 -04:00
|
|
|
# The initializer takes text in Markdown and project this text is valid
|
|
|
|
# in context of.
|
|
|
|
#
|
|
|
|
# `unfold` method tries to find all local references and unfold each of
|
2016-03-18 09:48:55 -04:00
|
|
|
# those local references to cross reference format, assuming that the
|
|
|
|
# argument passed to this method is a project that references will be
|
|
|
|
# viewed from (see `Referable#to_reference method).
|
2016-03-16 05:24:44 -04:00
|
|
|
#
|
|
|
|
# Examples:
|
|
|
|
#
|
|
|
|
# 'Hello, this issue is related to #123 and
|
|
|
|
# other issues labeled with ~"label"', will be converted to:
|
|
|
|
#
|
|
|
|
# 'Hello, this issue is related to gitlab-org/gitlab-ce#123 and
|
|
|
|
# other issue labeled with gitlab-org/gitlab-ce~"label"'.
|
|
|
|
#
|
|
|
|
# It does respect markdown lexical rules, so text in code block will not be
|
|
|
|
# replaced, see another example:
|
|
|
|
#
|
|
|
|
# 'Merge request for issue #1234, see also link:
|
|
|
|
# http://gitlab.com/some/link/#1234, and code `puts #1234`' =>
|
|
|
|
#
|
|
|
|
# 'Merge request for issue gitlab-org/gitlab-ce#1234, se also link:
|
|
|
|
# http://gitlab.com/some/link/#1234, and code `puts #1234`'
|
2016-03-07 07:09:53 -05:00
|
|
|
#
|
2016-03-20 05:11:26 -04:00
|
|
|
class ReferenceRewriter
|
2017-09-20 05:55:54 -04:00
|
|
|
RewriteError = Class.new(StandardError)
|
|
|
|
|
2016-03-20 05:11:26 -04:00
|
|
|
def initialize(text, source_project, current_user)
|
2016-03-07 07:09:53 -05:00
|
|
|
@text = text
|
2016-03-20 05:11:26 -04:00
|
|
|
@source_project = source_project
|
|
|
|
@current_user = current_user
|
|
|
|
@original_html = markdown(text)
|
2016-03-29 07:21:57 -04:00
|
|
|
@pattern = Gitlab::ReferenceExtractor.references_pattern
|
2016-03-07 07:09:53 -05:00
|
|
|
end
|
|
|
|
|
2016-03-20 05:11:26 -04:00
|
|
|
def rewrite(target_project)
|
2016-03-30 04:42:39 -04:00
|
|
|
return @text unless needs_rewrite?
|
|
|
|
|
2016-03-29 07:21:57 -04:00
|
|
|
@text.gsub(@pattern) do |reference|
|
2016-03-20 05:11:26 -04:00
|
|
|
unfold_reference(reference, Regexp.last_match, target_project)
|
2016-03-07 07:09:53 -05:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2016-03-29 07:21:57 -04:00
|
|
|
def needs_rewrite?
|
2016-03-30 04:56:25 -04:00
|
|
|
@text =~ @pattern
|
2016-03-29 07:21:57 -04:00
|
|
|
end
|
|
|
|
|
2016-03-07 07:09:53 -05:00
|
|
|
private
|
|
|
|
|
2016-03-20 05:11:26 -04:00
|
|
|
def unfold_reference(reference, match, target_project)
|
2016-03-11 05:49:04 -05:00
|
|
|
before = @text[0...match.begin(0)]
|
2016-03-20 05:11:26 -04:00
|
|
|
after = @text[match.end(0)..-1]
|
2016-03-11 05:49:04 -05:00
|
|
|
|
2016-03-21 09:12:49 -04:00
|
|
|
referable = find_referable(reference)
|
2016-03-11 05:49:04 -05:00
|
|
|
return reference unless referable
|
2016-03-21 09:12:49 -04:00
|
|
|
|
2016-09-28 23:21:47 -04:00
|
|
|
cross_reference = build_cross_reference(referable, target_project)
|
2016-03-21 09:12:49 -04:00
|
|
|
return reference if reference == cross_reference
|
2016-03-11 05:49:04 -05:00
|
|
|
|
2017-09-20 05:55:54 -04:00
|
|
|
if cross_reference.nil?
|
|
|
|
raise RewriteError, "Unspecified reference detected for #{referable.class.name}"
|
|
|
|
end
|
|
|
|
|
2016-03-21 09:12:49 -04:00
|
|
|
new_text = before + cross_reference + after
|
2016-03-11 05:49:04 -05:00
|
|
|
substitution_valid?(new_text) ? cross_reference : reference
|
|
|
|
end
|
|
|
|
|
2016-03-20 05:52:01 -04:00
|
|
|
def find_referable(reference)
|
2016-03-20 05:11:26 -04:00
|
|
|
extractor = Gitlab::ReferenceExtractor.new(@source_project,
|
|
|
|
@current_user)
|
2016-03-20 05:52:01 -04:00
|
|
|
extractor.analyze(reference)
|
|
|
|
extractor.all.first
|
2016-03-11 05:49:04 -05:00
|
|
|
end
|
|
|
|
|
2016-09-28 23:21:47 -04:00
|
|
|
def build_cross_reference(referable, target_project)
|
|
|
|
if referable.respond_to?(:project)
|
|
|
|
referable.to_reference(target_project)
|
|
|
|
else
|
2016-12-21 11:41:33 -05:00
|
|
|
referable.to_reference(@source_project, target_project: target_project)
|
2016-09-28 23:21:47 -04:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2016-03-11 05:49:04 -05:00
|
|
|
def substitution_valid?(substituted)
|
2016-03-20 05:11:26 -04:00
|
|
|
@original_html == markdown(substituted)
|
2016-03-11 05:49:04 -05:00
|
|
|
end
|
|
|
|
|
|
|
|
def markdown(text)
|
2016-03-20 05:11:26 -04:00
|
|
|
Banzai.render(text, project: @source_project, no_original_data: true)
|
2016-03-07 07:09:53 -05:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|