gollum-lib 4.0.3-java
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|