gollum-lib 4.0.3-java
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +15 -0
- data/Gemfile +3 -0
- data/HISTORY.md +11 -0
- data/LICENSE +21 -0
- data/README.md +286 -0
- data/Rakefile +187 -0
- data/docs/sanitization.md +33 -0
- data/gemspec.rb +100 -0
- data/gollum-lib.gemspec +4 -0
- data/gollum-lib_java.gemspec +4 -0
- data/lib/gollum-lib.rb +64 -0
- data/lib/gollum-lib/blob_entry.rb +95 -0
- data/lib/gollum-lib/committer.rb +243 -0
- data/lib/gollum-lib/file.rb +158 -0
- data/lib/gollum-lib/file_view.rb +155 -0
- data/lib/gollum-lib/filter.rb +78 -0
- data/lib/gollum-lib/filter/code.rb +145 -0
- data/lib/gollum-lib/filter/macro.rb +57 -0
- data/lib/gollum-lib/filter/metadata.rb +29 -0
- data/lib/gollum-lib/filter/plain_text.rb +16 -0
- data/lib/gollum-lib/filter/remote_code.rb +63 -0
- data/lib/gollum-lib/filter/render.rb +20 -0
- data/lib/gollum-lib/filter/sanitize.rb +18 -0
- data/lib/gollum-lib/filter/tags.rb +320 -0
- data/lib/gollum-lib/filter/toc.rb +109 -0
- data/lib/gollum-lib/filter/wsd.rb +54 -0
- data/lib/gollum-lib/git_access.rb +247 -0
- data/lib/gollum-lib/gitcode.rb +48 -0
- data/lib/gollum-lib/helpers.rb +13 -0
- data/lib/gollum-lib/hook.rb +35 -0
- data/lib/gollum-lib/macro.rb +43 -0
- data/lib/gollum-lib/macro/all_pages.rb +11 -0
- data/lib/gollum-lib/markup.rb +197 -0
- data/lib/gollum-lib/markups.rb +20 -0
- data/lib/gollum-lib/page.rb +491 -0
- data/lib/gollum-lib/pagination.rb +62 -0
- data/lib/gollum-lib/sanitization.rb +176 -0
- data/lib/gollum-lib/version.rb +5 -0
- data/lib/gollum-lib/wiki.rb +925 -0
- data/licenses/licenses.txt +6 -0
- metadata +410 -0
@@ -0,0 +1,57 @@
|
|
1
|
+
# ~*~ encoding: utf-8 ~*~
|
2
|
+
|
3
|
+
# Replace specified tokens with dynamically generated content.
|
4
|
+
class Gollum::Filter::Macro < Gollum::Filter
|
5
|
+
def extract(data)
|
6
|
+
quoted_arg = %r{".*?"}
|
7
|
+
unquoted_arg = %r{[^,)]+}
|
8
|
+
named_arg = %r{[a-z0-9_]+=".*?"}
|
9
|
+
|
10
|
+
arg = %r{(?:#{quoted_arg}|#{unquoted_arg}|#{named_arg})}
|
11
|
+
arg_list = %r{(\s*|#{arg}(?:\s*,\s*#{arg})*)}
|
12
|
+
|
13
|
+
data.gsub(/('?)\<\<\s*([A-Z][A-Za-z0-9]*)\s*\(#{arg_list}\)\s*\>\>/) do
|
14
|
+
next CGI.escape_html($&[1..-1]) unless $1.empty?
|
15
|
+
id = Digest::SHA1.hexdigest($2 + $3)
|
16
|
+
macro = $2
|
17
|
+
argstr = $3
|
18
|
+
args = []
|
19
|
+
opts = {}
|
20
|
+
|
21
|
+
argstr.scan /,?\s*(#{arg})\s*/ do |arg|
|
22
|
+
# Stabstabstab
|
23
|
+
arg = arg.first
|
24
|
+
|
25
|
+
if arg =~ /^([a-z0-9_]+)="(.*?)"/
|
26
|
+
opts[$1] = $2
|
27
|
+
elsif arg =~ /^"(.*)"$/
|
28
|
+
args << $1
|
29
|
+
else
|
30
|
+
args << arg
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
args << opts unless opts.empty?
|
35
|
+
|
36
|
+
@map[id] = { :macro => macro, :args => args }
|
37
|
+
id
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def process(data)
|
42
|
+
@map.each do |id, spec|
|
43
|
+
macro = spec[:macro]
|
44
|
+
args = spec[:args]
|
45
|
+
|
46
|
+
data.gsub!(id) do
|
47
|
+
begin
|
48
|
+
Gollum::Macro.instance(macro, @markup.wiki, @markup.page).render(*args)
|
49
|
+
rescue StandardError => e
|
50
|
+
"!!!Macro Error: #{e.message}!!!"
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
data
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# Extract metadata for data and build metadata table. Metadata consists of
|
2
|
+
# key/value pairs in "key:value" format found between markers. Each
|
3
|
+
# key/value pair must be on its own line. Internal whitespace in keys and
|
4
|
+
# values is preserved, but external whitespace is ignored.
|
5
|
+
#
|
6
|
+
# Because ri and ruby 1.8.7 are awesome, the markers can't
|
7
|
+
# be included in this documentation without triggering
|
8
|
+
# `Unhandled special: Special: type=17`
|
9
|
+
# Please read the source code for the exact markers
|
10
|
+
class Gollum::Filter::Metadata < Gollum::Filter
|
11
|
+
def extract(data)
|
12
|
+
# The markers are `<!-- ---` and `-->`
|
13
|
+
data.gsub(/\<\!--+\s+---(.*?)--+\>/m) do
|
14
|
+
@markup.metadata ||= {}
|
15
|
+
# Split untrusted input on newlines, then remove bits that look like
|
16
|
+
# HTML elements before parsing each line.
|
17
|
+
$1.split("\n").each do |line|
|
18
|
+
line.gsub!(/<[^>]*>/, '')
|
19
|
+
k, v = line.split(':', 2)
|
20
|
+
@markup.metadata[k.strip] = (v ? v.strip : '') if k
|
21
|
+
end
|
22
|
+
''
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def process(d)
|
27
|
+
d
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# ~*~ encoding: utf-8 ~*~
|
2
|
+
|
3
|
+
# Plain Text
|
4
|
+
#
|
5
|
+
# Render plain text documents in a <pre> block without any special markup.
|
6
|
+
|
7
|
+
class Gollum::Filter::PlainText < Gollum::Filter
|
8
|
+
|
9
|
+
def extract(data)
|
10
|
+
@markup.format == :txt ? "<pre>#{CGI.escapeHTML(data)}</pre>" : data
|
11
|
+
end
|
12
|
+
|
13
|
+
def process(data)
|
14
|
+
data
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
# ~*~ encoding: utf-8 ~*~
|
2
|
+
require 'net/http'
|
3
|
+
require 'net/https' # ruby 1.8.7 fix, remove at upgrade
|
4
|
+
require 'uri'
|
5
|
+
require 'open-uri'
|
6
|
+
|
7
|
+
# Remote code - fetch code from url and replace the contents to a
|
8
|
+
# code-block that gets run the next parse.
|
9
|
+
# Acceptable formats:
|
10
|
+
# ```language:local-file.ext```
|
11
|
+
# ```language:/abs/other-file.ext```
|
12
|
+
# ```language:https://example.com/somefile.txt```
|
13
|
+
#
|
14
|
+
class Gollum::Filter::RemoteCode < Gollum::Filter
|
15
|
+
def extract(data)
|
16
|
+
return data if @markup.format == :txt
|
17
|
+
data.gsub /^[ \t]*``` ?([^:\n\r]+):((http)?[^`\n\r]+)```/ do
|
18
|
+
language = $1
|
19
|
+
uri = $2
|
20
|
+
protocol = $3
|
21
|
+
|
22
|
+
# Detect local file
|
23
|
+
if protocol.nil?
|
24
|
+
if file = @markup.find_file(uri, @markup.wiki.ref)
|
25
|
+
contents = file.raw_data
|
26
|
+
else
|
27
|
+
# How do we communicate a render error?
|
28
|
+
next html_error("File not found: #{CGI::escapeHTML(uri)}")
|
29
|
+
end
|
30
|
+
else
|
31
|
+
contents = req(uri)
|
32
|
+
end
|
33
|
+
|
34
|
+
"```#{language}\n#{contents}\n```\n"
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def process(d)
|
39
|
+
d
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
def req uri, cut = 1
|
45
|
+
uri = URI(uri)
|
46
|
+
return "Too many redirects or retries" if cut >= 10
|
47
|
+
http = Net::HTTP.new uri.host, uri.port
|
48
|
+
http.use_ssl = true
|
49
|
+
resp = http.get uri.path, {
|
50
|
+
'Accept' => 'text/plain',
|
51
|
+
'Cache-Control' => 'no-cache',
|
52
|
+
'Connection' => 'keep-alive',
|
53
|
+
'User-Agent' => 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:15.0) Gecko/20100101 Firefox/15.0'
|
54
|
+
}
|
55
|
+
code = resp.code.to_i
|
56
|
+
return resp.body if code == 200
|
57
|
+
return "Not Found" if code == 404
|
58
|
+
return "Unhandled Response Code #{code}" unless code == 304 or not resp.header['location'].nil?
|
59
|
+
loc = URI.parse resp.header['location']
|
60
|
+
uri2 = loc.relative? ? (uri + loc) : loc # overloads (+)
|
61
|
+
req uri2, (cut + 1)
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# ~*~ encoding: utf-8 ~*~
|
2
|
+
|
3
|
+
class Gollum::Filter::Render < Gollum::Filter
|
4
|
+
def extract(data)
|
5
|
+
begin
|
6
|
+
data = GitHub::Markup.render(@markup.name, data)
|
7
|
+
if data.nil?
|
8
|
+
raise "There was an error converting #{@markup.name} to HTML."
|
9
|
+
end
|
10
|
+
rescue Object => e
|
11
|
+
data = html_error("Failed to render page: #{e.message}")
|
12
|
+
end
|
13
|
+
|
14
|
+
data
|
15
|
+
end
|
16
|
+
|
17
|
+
def process(d)
|
18
|
+
d
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# ~*~ encoding: utf-8 ~*~
|
2
|
+
|
3
|
+
class Gollum::Filter::Sanitize < Gollum::Filter
|
4
|
+
def extract(d)
|
5
|
+
d
|
6
|
+
end
|
7
|
+
|
8
|
+
def process(data)
|
9
|
+
if @markup.sanitize
|
10
|
+
doc = Nokogiri::HTML::DocumentFragment.parse(data)
|
11
|
+
doc = @markup.sanitize.clean_node!(doc)
|
12
|
+
|
13
|
+
doc.to_xml(@markup.to_xml_opts)
|
14
|
+
else
|
15
|
+
data
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,320 @@
|
|
1
|
+
# ~*~ encoding: utf-8 ~*~
|
2
|
+
|
3
|
+
# Render all tags (things in double-square-brackets). This one's a biggie.
|
4
|
+
class Gollum::Filter::Tags < Gollum::Filter
|
5
|
+
# Extract all tags into the tagmap and replace with placeholders.
|
6
|
+
def extract(data)
|
7
|
+
return data if @markup.format == :txt || @markup.format == :asciidoc
|
8
|
+
data.gsub!(/(.?)\[\[(.+?)\]\]([^\[]?)/m) do
|
9
|
+
if $1 == "'" && $3 != "'"
|
10
|
+
"[[#{$2}]]#{$3}"
|
11
|
+
elsif $2.include?('][')
|
12
|
+
if $2[0..4] == 'file:'
|
13
|
+
pre = $1
|
14
|
+
post = $3
|
15
|
+
parts = $2.split('][')
|
16
|
+
parts[0][0..4] = ""
|
17
|
+
link = "#{parts[1]}|#{parts[0].sub(/\.org/, '')}"
|
18
|
+
id = register_tag(link)
|
19
|
+
"#{pre}#{id}#{post}"
|
20
|
+
else
|
21
|
+
$&
|
22
|
+
end
|
23
|
+
else
|
24
|
+
id = register_tag($2)
|
25
|
+
"#{$1}#{id}#{$3}"
|
26
|
+
end
|
27
|
+
end
|
28
|
+
data
|
29
|
+
end
|
30
|
+
|
31
|
+
def register_tag(tag)
|
32
|
+
id = "TAG#{Digest::SHA1.hexdigest(tag)}TAG"
|
33
|
+
@map[id] = tag
|
34
|
+
id
|
35
|
+
end
|
36
|
+
|
37
|
+
# Process all text nodes from the doc and replace the placeholders with the
|
38
|
+
# final markup.
|
39
|
+
def process(rendered_data)
|
40
|
+
doc = Nokogiri::HTML::DocumentFragment.parse(rendered_data)
|
41
|
+
doc.traverse do |node|
|
42
|
+
if node.text? then
|
43
|
+
content = node.content
|
44
|
+
content.gsub!(/TAG[a-f0-9]+TAG/) do |id|
|
45
|
+
if tag = @map[id] then
|
46
|
+
if is_preformatted?(node) then
|
47
|
+
"[[#{tag}]]"
|
48
|
+
else
|
49
|
+
process_tag(tag).gsub('%2f', '/')
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
node.replace(content) if content != node.content
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
doc.to_html
|
58
|
+
end
|
59
|
+
|
60
|
+
private
|
61
|
+
|
62
|
+
PREFORMATTED_TAGS = %w(code tt)
|
63
|
+
|
64
|
+
def is_preformatted?(node)
|
65
|
+
node && (PREFORMATTED_TAGS.include?(node.name) ||
|
66
|
+
node.ancestors.any? { |a| PREFORMATTED_TAGS.include?(a.name) })
|
67
|
+
end
|
68
|
+
|
69
|
+
# Process a single tag into its final HTML form.
|
70
|
+
#
|
71
|
+
# tag - The String tag contents (the stuff inside the double
|
72
|
+
# brackets).
|
73
|
+
#
|
74
|
+
# Returns the String HTML version of the tag.
|
75
|
+
def process_tag(tag)
|
76
|
+
if tag =~ /^_TOC_$/
|
77
|
+
%{[[#{tag}]]}
|
78
|
+
elsif tag =~ /^_$/
|
79
|
+
%{<div class="clearfloats"></div>}
|
80
|
+
elsif html = process_include_tag(tag)
|
81
|
+
html
|
82
|
+
elsif html = process_image_tag(tag)
|
83
|
+
html
|
84
|
+
elsif html = process_external_link_tag(tag)
|
85
|
+
html
|
86
|
+
elsif html = process_file_link_tag(tag)
|
87
|
+
html
|
88
|
+
else
|
89
|
+
process_page_link_tag(tag)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
# Attempt to process the tag as an include tag
|
94
|
+
#
|
95
|
+
# tag - The String tag contents (the stuff inside the double brackets).
|
96
|
+
#
|
97
|
+
# Returns the String HTML if the tag is a valid image tag or nil
|
98
|
+
# if it is not.
|
99
|
+
#
|
100
|
+
def process_include_tag(tag)
|
101
|
+
return unless /^include:/.match(tag)
|
102
|
+
page_name = tag[8..-1]
|
103
|
+
resolved_page_name = ::File.expand_path(page_name, "/"+@markup.dir)
|
104
|
+
|
105
|
+
if @markup.include_levels > 0
|
106
|
+
page = find_page_from_name(resolved_page_name)
|
107
|
+
if page
|
108
|
+
page.formatted_data(@markup.encoding, @markup.include_levels-1)
|
109
|
+
else
|
110
|
+
html_error("Cannot include #{process_page_link_tag(resolved_page_name)} - does not exist yet")
|
111
|
+
end
|
112
|
+
else
|
113
|
+
html_error("Too many levels of included pages, will not include #{process_page_link_tag(resolved_page_name)}")
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
# Attempt to process the tag as an image tag.
|
118
|
+
#
|
119
|
+
# tag - The String tag contents (the stuff inside the double brackets).
|
120
|
+
#
|
121
|
+
# Returns the String HTML if the tag is a valid image tag or nil
|
122
|
+
# if it is not.
|
123
|
+
def process_image_tag(tag)
|
124
|
+
parts = tag.split('|')
|
125
|
+
return if parts.size.zero?
|
126
|
+
|
127
|
+
name = parts[0].strip
|
128
|
+
path = if file = @markup.find_file(name)
|
129
|
+
::File.join @markup.wiki.base_path, file.path
|
130
|
+
elsif name =~ /^https?:\/\/.+(jpg|png|gif|svg|bmp)$/i
|
131
|
+
name
|
132
|
+
end
|
133
|
+
|
134
|
+
if path
|
135
|
+
opts = parse_image_tag_options(tag)
|
136
|
+
|
137
|
+
containered = false
|
138
|
+
|
139
|
+
classes = [] # applied to whatever the outermost container is
|
140
|
+
attrs = [] # applied to the image
|
141
|
+
|
142
|
+
align = opts['align']
|
143
|
+
if opts['float']
|
144
|
+
containered = true
|
145
|
+
align ||= 'left'
|
146
|
+
if %w{left right}.include?(align)
|
147
|
+
classes << "float-#{align}"
|
148
|
+
end
|
149
|
+
elsif %w{top texttop middle absmiddle bottom absbottom baseline}.include?(align)
|
150
|
+
attrs << %{align="#{align}"}
|
151
|
+
elsif align
|
152
|
+
if %w{left center right}.include?(align)
|
153
|
+
containered = true
|
154
|
+
classes << "align-#{align}"
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
if width = opts['width']
|
159
|
+
if width =~ /^\d+(\.\d+)?(em|px)$/
|
160
|
+
attrs << %{width="#{width}"}
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
if height = opts['height']
|
165
|
+
if height =~ /^\d+(\.\d+)?(em|px)$/
|
166
|
+
attrs << %{height="#{height}"}
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
if alt = opts['alt']
|
171
|
+
attrs << %{alt="#{alt}"}
|
172
|
+
end
|
173
|
+
|
174
|
+
attr_string = attrs.size > 0 ? attrs.join(' ') + ' ' : ''
|
175
|
+
|
176
|
+
if opts['frame'] || containered
|
177
|
+
classes << 'frame' if opts['frame']
|
178
|
+
%{<span class="#{classes.join(' ')}">} +
|
179
|
+
%{<span>} +
|
180
|
+
%{<img src="#{path}" #{attr_string}/>} +
|
181
|
+
(alt ? %{<span>#{alt}</span>} : '') +
|
182
|
+
%{</span>} +
|
183
|
+
%{</span>}
|
184
|
+
else
|
185
|
+
%{<img src="#{path}" #{attr_string}/>}
|
186
|
+
end
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
# Parse any options present on the image tag and extract them into a
|
191
|
+
# Hash of option names and values.
|
192
|
+
#
|
193
|
+
# tag - The String tag contents (the stuff inside the double brackets).
|
194
|
+
#
|
195
|
+
# Returns the options Hash:
|
196
|
+
# key - The String option name.
|
197
|
+
# val - The String option value or true if it is a binary option.
|
198
|
+
def parse_image_tag_options(tag)
|
199
|
+
tag.split('|')[1..-1].inject({}) do |memo, attr|
|
200
|
+
parts = attr.split('=').map { |x| x.strip }
|
201
|
+
memo[parts[0]] = (parts.size == 1 ? true : parts[1])
|
202
|
+
memo
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
# Return the String HTML if the tag is a valid external link tag or
|
207
|
+
# nil if it is not.
|
208
|
+
def process_external_link_tag(tag)
|
209
|
+
parts = tag.split('|')
|
210
|
+
return if parts.size.zero?
|
211
|
+
if parts.size == 1
|
212
|
+
url = parts[0].strip
|
213
|
+
else
|
214
|
+
name, url = *parts.compact.map(&:strip)
|
215
|
+
end
|
216
|
+
accepted_protocols = @markup.wiki.sanitization.protocols['a']['href'].dup
|
217
|
+
if accepted_protocols.include?(:relative)
|
218
|
+
accepted_protocols.select!{|protocol| protocol != :relative}
|
219
|
+
regexp = %r{^((#{accepted_protocols.join("|")}):)?(//)}
|
220
|
+
else
|
221
|
+
regexp = %r{^((#{accepted_protocols.join("|")}):)}
|
222
|
+
end
|
223
|
+
if url =~ regexp
|
224
|
+
if name.nil?
|
225
|
+
%{<a href="#{url}">#{url}</a>}
|
226
|
+
else
|
227
|
+
%{<a href="#{url}">#{name}</a>}
|
228
|
+
end
|
229
|
+
else
|
230
|
+
nil
|
231
|
+
end
|
232
|
+
|
233
|
+
end
|
234
|
+
|
235
|
+
# Attempt to process the tag as a file link tag.
|
236
|
+
#
|
237
|
+
# tag - The String tag contents (the stuff inside the double
|
238
|
+
# brackets).
|
239
|
+
#
|
240
|
+
# Returns the String HTML if the tag is a valid file link tag or nil
|
241
|
+
# if it is not.
|
242
|
+
def process_file_link_tag(tag)
|
243
|
+
parts = tag.split('|')
|
244
|
+
return if parts.size.zero?
|
245
|
+
|
246
|
+
name = parts[0].strip
|
247
|
+
path = parts[1] && parts[1].strip
|
248
|
+
path = if path && file = @markup.find_file(path)
|
249
|
+
::File.join @markup.wiki.base_path, file.path
|
250
|
+
else
|
251
|
+
nil
|
252
|
+
end
|
253
|
+
|
254
|
+
if name && path && file
|
255
|
+
%{<a href="#{::File.join @markup.wiki.base_path, file.path}">#{name}</a>}
|
256
|
+
elsif name && path
|
257
|
+
%{<a href="#{path}">#{name}</a>}
|
258
|
+
else
|
259
|
+
nil
|
260
|
+
end
|
261
|
+
end
|
262
|
+
|
263
|
+
# Attempt to process the tag as a page link tag.
|
264
|
+
#
|
265
|
+
# tag - The String tag contents (the stuff inside the double
|
266
|
+
# brackets).
|
267
|
+
#
|
268
|
+
# Returns the String HTML if the tag is a valid page link tag or nil
|
269
|
+
# if it is not.
|
270
|
+
def process_page_link_tag(tag)
|
271
|
+
parts = tag.split('|')
|
272
|
+
parts.reverse! if @markup.format == :mediawiki
|
273
|
+
|
274
|
+
name, page_name = *parts.compact.map(&:strip)
|
275
|
+
cname = @markup.wiki.page_class.cname(page_name || name)
|
276
|
+
|
277
|
+
presence = "absent"
|
278
|
+
link_name = cname
|
279
|
+
page, extra = find_page_from_name(cname)
|
280
|
+
if page
|
281
|
+
link_name = @markup.wiki.page_class.cname(page.name)
|
282
|
+
presence = "present"
|
283
|
+
end
|
284
|
+
link = ::File.join(@markup.wiki.base_path, page ? page.escaped_url_path : CGI.escape(link_name))
|
285
|
+
|
286
|
+
# //page is invalid
|
287
|
+
# strip all duplicate forward slashes using helpers.rb trim_leading_slash
|
288
|
+
# //page => /page
|
289
|
+
link = trim_leading_slash link
|
290
|
+
|
291
|
+
%{<a class="internal #{presence}" href="#{link}#{extra}">#{name}</a>}
|
292
|
+
end
|
293
|
+
|
294
|
+
# Find a page from a given cname. If the page has an anchor (#) and has
|
295
|
+
# no match, strip the anchor and try again.
|
296
|
+
#
|
297
|
+
# cname - The String canonical page name including path.
|
298
|
+
#
|
299
|
+
# Returns a Gollum::Page instance if a page is found, or an Array of
|
300
|
+
# [Gollum::Page, String extra] if a page without the extra anchor data
|
301
|
+
# is found.
|
302
|
+
def find_page_from_name(cname)
|
303
|
+
slash = cname.rindex('/')
|
304
|
+
|
305
|
+
unless slash.nil?
|
306
|
+
name = cname[slash+1..-1]
|
307
|
+
path = cname[0..slash]
|
308
|
+
page = @markup.wiki.paged(name, path)
|
309
|
+
else
|
310
|
+
page = @markup.wiki.paged(cname, '/') || @markup.wiki.page(cname)
|
311
|
+
end
|
312
|
+
|
313
|
+
if page
|
314
|
+
return page
|
315
|
+
end
|
316
|
+
if pos = cname.index('#')
|
317
|
+
[@markup.wiki.page(cname[0...pos]), cname[pos..-1]]
|
318
|
+
end
|
319
|
+
end
|
320
|
+
end
|