module GitlabMarkdownHelper include Gitlab::Markdown # Use this in places where you would normally use link_to(gfm(...), ...). # # It solves a problem occurring with nested links (i.e. # "outer text gfm ref more outer text"). This will not be # interpreted as intended. Browsers will parse something like # "outer text gfm ref more outer text" (notice the last part is # not linked any more). link_to_gfm corrects that. It wraps all parts to # explicitly produce the correct linking behavior (i.e. # "outer text gfm ref more outer text"). def link_to_gfm(body, url, html_options = {}) return "" if body.blank? escaped_body = if body =~ /^\.*?}m) do |match| "#{match}#{link_to("", url, html_options)[0..-5]}" # "".length +1 end link_to(gfm_body.html_safe, url, html_options) end def markdown(text) unless @markdown gitlab_renderer = Redcarpet::Render::GitlabHTML.new(self, # see https://github.com/vmg/redcarpet#darling-i-packed-you-a-couple-renderers-for-lunch- filter_html: true, with_toc_data: true, hard_wrap: true, safe_links_only: true) @markdown = Redcarpet::Markdown.new(gitlab_renderer, # see https://github.com/vmg/redcarpet#and-its-like-really-simple-to-use no_intra_emphasis: true, tables: true, fenced_code_blocks: true, autolink: true, strikethrough: true, lax_html_blocks: true, space_after_headers: true, superscript: true) end @markdown.render(text).html_safe end def render_wiki_content(wiki_page) if wiki_page.format == :markdown markdown(wiki_page.content) else wiki_page.formatted_content.html_safe end end # text - whole text from a markdown file # project_path_with_namespace - namespace/projectname # ref - name of the branch or reference # wiki - whether the markdown is from wiki or not def create_relative_links(text, project_path_with_namespace, ref, wiki = false) paths = extract_paths(text) paths.each do |path| new_path = rebuild_path(project_path_with_namespace, path, ref) # Replacing old string with a new one with brackets ]() to prevent replacing occurence of a word # e.g. If we have a markdown like [test](test) this will replace ](test) and not the word test text.gsub!("](#{path})", "](/#{new_path})") end text end def extract_paths(markdown_text) all_markdown_paths = pick_out_paths(markdown_text) paths = remove_empty(all_markdown_paths) select_relative(paths) end # Split the markdown text to each line and find all paths, this will match anything with - ]("some_text") def pick_out_paths(markdown_text) markdown_text.split("\n").map { |text| text.scan(/\]\(([^(]+)\)/) } end # Removes any empty result produced by not matching the regexp def remove_empty(paths) paths.reject{|l| l.empty? }.flatten end # Reject any path that contains ignored protocol # eg. reject "https://gitlab.org} but accept "doc/api/README.md" def select_relative(paths) paths.reject{|path| ignored_protocols.map{|protocol| path.include?(protocol)}.any?} end def ignored_protocols ["http://","https://", "ftp://", "mailto:"] end def rebuild_path(path_with_namespace, string, ref) [ path_with_namespace, path_with_ref(string, ref), string ].compact.join("/") end # Checks if the path exists in the repo # eg. checks if doc/README.md exists, if it doesn't then it is a wiki link def path_with_ref(path, ref) if File.exists?(Rails.root.join(path)) "#{local_path(path)}/#{correct_ref(ref)}" else "wikis" end end # Check if the path is pointing to a directory(tree) or a file(blob) # eg. doc/api is directory and doc/README.md is file def local_path(path) File.directory?(Rails.root.join(path)) ? "tree" : "blob" end # We will assume that if no ref exists we can point to master def correct_ref(ref) ref ? ref : "master" end end