2012-08-02 20:28:02 -04:00
module GitlabMarkdownHelper
2012-09-05 16:06:03 -04:00
include Gitlab :: Markdown
2012-08-02 20:28:02 -04:00
2012-08-23 14:10:06 -04:00
# Use this in places where you would normally use link_to(gfm(...), ...).
#
# It solves a problem occurring with nested links (i.e.
# "<a>outer text <a>gfm ref</a> more outer text</a>"). This will not be
# interpreted as intended. Browsers will parse something like
# "<a>outer text </a><a>gfm ref</a> 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.
# "<a>outer text </a><a>gfm ref</a><a> more outer text</a>").
2012-08-02 20:28:02 -04:00
def link_to_gfm ( body , url , html_options = { } )
2012-09-10 11:32:31 -04:00
return " " if body . blank?
2012-09-19 19:42:26 -04:00
2013-01-29 04:00:56 -05:00
escaped_body = if body =~ / ^ \ <img /
body
else
escape_once ( body )
end
gfm_body = gfm ( escaped_body , html_options )
2012-08-02 20:28:02 -04:00
gfm_body . gsub! ( %r{ <a.*?>.*?</a> }m ) do | match |
" </a> #{ match } #{ link_to ( " " , url , html_options ) [ 0 .. - 5 ] } " # "</a>".length +1
end
link_to ( gfm_body . html_safe , url , html_options )
end
2012-08-08 04:52:09 -04:00
def markdown ( text )
2012-08-27 15:20:13 -04:00
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 ,
2013-10-08 06:24:50 -04:00
hard_wrap : true ,
safe_links_only : true )
2012-09-07 19:11:32 -04:00
@markdown = Redcarpet :: Markdown . new ( gitlab_renderer ,
2012-08-27 15:20:13 -04:00
# 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
2012-08-08 04:52:09 -04:00
2012-08-27 15:20:13 -04:00
@markdown . render ( text ) . html_safe
2012-08-08 04:52:09 -04:00
end
2013-03-14 02:31:08 -04:00
def render_wiki_content ( wiki_page )
if wiki_page . format == :markdown
markdown ( wiki_page . content )
else
wiki_page . formatted_content . html_safe
end
end
2013-10-08 06:24:50 -04:00
2013-10-10 05:16:15 -04:00
# text - whole text from a markdown file
2013-10-10 08:29:16 -04:00
# project_path_with_namespace - namespace/projectname, eg. gitlabhq/gitlabhq
# ref - name of the branch or reference, eg. stable
# requested_path - path of request, eg. doc/api/README.md, used in special case when path is pointing to the .md file were the original request is coming from
2013-10-10 05:16:15 -04:00
# wiki - whether the markdown is from wiki or not
2013-11-08 10:34:15 -05:00
def create_relative_links ( text , project , ref , requested_path , wiki = false )
@path_to_satellite = project . satellite . path
project_path_with_namespace = project . path_with_namespace
2013-10-10 05:16:15 -04:00
paths = extract_paths ( text )
2013-10-10 08:29:16 -04:00
paths . each do | file_path |
new_path = rebuild_path ( project_path_with_namespace , file_path , requested_path , ref )
2013-10-10 05:16:15 -04:00
# 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
2013-10-10 08:29:16 -04:00
text . gsub! ( " ]( #{ file_path } ) " , " ](/ #{ new_path } ) " )
2013-10-08 06:24:50 -04:00
end
text
end
2013-10-09 07:28:25 -04:00
2013-10-10 05:16:15 -04:00
def extract_paths ( markdown_text )
all_markdown_paths = pick_out_paths ( markdown_text )
paths = remove_empty ( all_markdown_paths )
select_relative ( paths )
2013-10-09 07:28:25 -04:00
end
2013-10-10 05:16:15 -04:00
# 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
2013-10-10 08:29:16 -04:00
def rebuild_path ( path_with_namespace , path , requested_path , ref )
file_path = relative_file_path ( path , requested_path )
2013-10-09 07:28:25 -04:00
[
path_with_namespace ,
2013-10-10 08:29:16 -04:00
path_with_ref ( file_path , ref ) ,
file_path
2013-10-09 07:28:25 -04:00
] . compact . join ( " / " )
end
2013-10-10 05:16:15 -04:00
# 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 )
2013-10-10 08:29:16 -04:00
if file_exists? ( path )
2013-10-10 05:16:15 -04:00
" #{ local_path ( path ) } / #{ correct_ref ( ref ) } "
2013-10-09 07:28:25 -04:00
else
" wikis "
end
end
2013-10-10 08:29:16 -04:00
def relative_file_path ( path , requested_path )
nested_path = build_nested_path ( path , requested_path )
return nested_path if file_exists? ( nested_path )
path
end
# Covering a special case, when the link is referencing file in the same directory eg:
# If we are at doc/api/README.md and the README.md contains relative links like [Users](users.md)
# this takes the request path(doc/api/README.md), and replaces the README.md with users.md so the path looks like doc/api/users.md
2013-10-14 09:55:44 -04:00
# If we are at doc/api and the README.md shown in below the tree view
# this takes the rquest path(doc/api) and adds users.md so the path looks like doc/api/users.md
2013-10-10 08:29:16 -04:00
def build_nested_path ( path , request_path )
return path unless request_path
2013-10-14 09:55:44 -04:00
if local_path ( request_path ) == " tree "
base = request_path . split ( " / " ) . push ( path )
base . join ( " / " )
else
base = request_path . split ( " / " )
base . pop
base . push ( path ) . join ( " / " )
end
2013-10-10 08:29:16 -04:00
end
2013-11-08 11:20:13 -05:00
def file_exists? ( path )
return false if path . nil? || path . empty?
File . exists? ( path_on_fs ( path ) )
2013-10-10 08:29:16 -04:00
end
2013-10-10 05:16:15 -04:00
# 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 )
2013-11-08 11:20:13 -05:00
File . directory? ( path_on_fs ( path ) ) ? " tree " : " blob "
end
# Path to the file in the satellites repository on the filesystem
def path_on_fs ( path )
[ @path_to_satellite , path ] . join ( " / " )
2013-10-09 07:28:25 -04:00
end
2013-10-10 05:16:15 -04:00
# We will assume that if no ref exists we can point to master
2013-10-09 07:28:25 -04:00
def correct_ref ( ref )
2013-10-10 05:16:15 -04:00
ref ? ref : " master "
2013-10-09 07:28:25 -04:00
end
2012-08-02 20:28:02 -04:00
end