gollum-lib 5.0.a.3-java → 5.0.a.4-java

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 43de530f54acdb12ffac21a4bb96db1844f59119
4
- data.tar.gz: f4cf906bf8898dfb676f1130a2c3e2b270d15f41
2
+ SHA256:
3
+ metadata.gz: f3579bcfc342afcfe2b398241c190fbba84cfe2c5c922576aaaecb512bab5cd8
4
+ data.tar.gz: 9387c63c0f369902cf50439b750864fc2c4f86834da46576da7e1c8dc51464c6
5
5
  SHA512:
6
- metadata.gz: d0e79ae8f96d9688bbd45cc9b38e5630fd38bcc4d52a437ed72e949f292c7c6a2f1c973250af0828a83cb09e6be138387ce49b7f064d8d58829ba3b852d8a3a3
7
- data.tar.gz: fff6e1c352190df905559ad9d53fea8dd3feb9973696e5dfbb5ce2576d67f6c2529b737ce9600ec5d78ab7a1295121445cda73f9df9a4a5b13f2611f1b1f149e
6
+ metadata.gz: 4a9bfdbb8a78a024d9c66c4cfa3d185b4156c3bd86068006ea26977db3dcb456691863a19359917cd3e5d70bf5c92f261bd4b47b977d21727608c783b6f0ce58
7
+ data.tar.gz: 29ac662c80e6b90fad991b7bb421486cf2b1a5b5a3838f342bbdfb0d26f23a6680a98cd6372077a5ed3e8e07578a9470600a4c9a427346fcc36700e735f8f9c5
data/HISTORY.md CHANGED
@@ -1,3 +1,10 @@
1
+ # v5.0
2
+
3
+ For a detailed overview of changes in 5.0 and a guide to migrating your wiki, see https://github.com/gollum/gollum/wiki/5.0-release-notes
4
+
5
+ * Removed support for Web Sequence Diagroms, PlantUML now default.
6
+ ** PlantUML users in 4.x please note: in this release PlantUML uses the server at https://plantuml.com by default, not `localhost`. Use the config option to keep using your own server.
7
+
1
8
  # v4.2.1
2
9
 
3
10
  * Performances improvements
data/gemspec.rb CHANGED
@@ -8,6 +8,7 @@ def specification(version, default_adapter, platform = nil)
8
8
  s.name = 'gollum-lib'
9
9
  s.version = version
10
10
  s.platform = platform if platform
11
+ s.date = '2018-09-17'
11
12
  s.date = '2017-04-13'
12
13
  s.rubyforge_project = 'gollum-lib'
13
14
  s.license = 'MIT'
@@ -25,19 +26,22 @@ def specification(version, default_adapter, platform = nil)
25
26
  s.extra_rdoc_files = %w(README.md LICENSE)
26
27
 
27
28
  s.add_dependency *default_adapter
28
- s.add_dependency 'rouge', '~> 2.0'
29
- s.add_dependency 'nokogiri', '~> 1.7', '>= 1.7.1'
29
+ s.add_dependency 'rouge', '~> 3.1'
30
+ s.add_dependency 'nokogiri', '~> 1.8'
30
31
  s.add_dependency 'stringex', '~> 2.6'
31
32
  s.add_dependency 'sanitize', '~> 2.1'
32
33
  s.add_dependency 'github-markup', '~> 1.6'
33
34
  s.add_dependency 'gemojione', '~> 3.2'
35
+ s.add_dependency 'twitter-text', '1.14.7'
34
36
 
35
37
  s.add_development_dependency 'org-ruby', '~> 0.9.9'
36
38
  s.add_development_dependency 'kramdown', '~> 1.13'
37
39
  s.add_development_dependency 'RedCloth', '~> 4.2.9'
38
- s.add_development_dependency 'mocha', '~> 1.1.0'
40
+ s.add_development_dependency 'mocha', '~> 1.2.0'
39
41
  s.add_development_dependency 'shoulda', '~> 3.5.0'
40
42
  s.add_development_dependency 'wikicloth', '~> 0.8.3'
43
+ s.add_development_dependency 'bibtex-ruby', '~> 4.3'
44
+ s.add_development_dependency 'citeproc-ruby', '~> 1.1'
41
45
  s.add_development_dependency 'rake', '~> 10.4.0'
42
46
  s.add_development_dependency 'pry', '~> 0.10.1'
43
47
  # required by pry
@@ -71,9 +75,11 @@ def specification(version, default_adapter, platform = nil)
71
75
  lib/gollum-lib/file.rb
72
76
  lib/gollum-lib/file_view.rb
73
77
  lib/gollum-lib/filter.rb
78
+ lib/gollum-lib/filter/bibtex.rb
74
79
  lib/gollum-lib/filter/code.rb
75
80
  lib/gollum-lib/filter/emoji.rb
76
81
  lib/gollum-lib/filter/macro.rb
82
+ lib/gollum-lib/filter/pandoc_bib.rb
77
83
  lib/gollum-lib/filter/plain_text.rb
78
84
  lib/gollum-lib/filter/plantuml.rb
79
85
  lib/gollum-lib/filter/remote_code.rb
@@ -81,7 +87,6 @@ def specification(version, default_adapter, platform = nil)
81
87
  lib/gollum-lib/filter/sanitize.rb
82
88
  lib/gollum-lib/filter/tags.rb
83
89
  lib/gollum-lib/filter/toc.rb
84
- lib/gollum-lib/filter/wsd.rb
85
90
  lib/gollum-lib/filter/yaml.rb
86
91
  lib/gollum-lib/git_access.rb
87
92
  lib/gollum-lib/helpers.rb
@@ -91,6 +96,7 @@ def specification(version, default_adapter, platform = nil)
91
96
  lib/gollum-lib/macro/global_toc.rb
92
97
  lib/gollum-lib/macro/navigation.rb
93
98
  lib/gollum-lib/macro/series.rb
99
+ lib/gollum-lib/macro/video.rb
94
100
  lib/gollum-lib/markup.rb
95
101
  lib/gollum-lib/markups.rb
96
102
  lib/gollum-lib/page.rb
@@ -5,13 +5,10 @@ require 'digest/sha1'
5
5
  require 'ostruct'
6
6
  require 'pathname'
7
7
 
8
- DEFAULT_ADAPTER = RUBY_PLATFORM == 'java' ? 'rjgit_adapter' : 'grit_adapter'
8
+ DEFAULT_ADAPTER = RUBY_PLATFORM == 'java' ? 'rjgit' : 'grit'
9
9
 
10
- if defined?(Gollum::GIT_ADAPTER)
11
- require "#{Gollum::GIT_ADAPTER.downcase}_adapter"
12
- else
13
- require DEFAULT_ADAPTER
14
- end
10
+ Gollum::GIT_ADAPTER = DEFAULT_ADAPTER if !defined?(Gollum::GIT_ADAPTER)
11
+ require "#{Gollum::GIT_ADAPTER.downcase}_adapter"
15
12
 
16
13
  # external
17
14
  require 'github/markup'
@@ -26,6 +26,13 @@ module Gollum
26
26
  path
27
27
  end
28
28
 
29
+ # Public: The SHA hash identifying this file
30
+ #
31
+ # Returns the String SHA.
32
+ def sha
33
+ @blob && @blob.id
34
+ end
35
+
29
36
  # Public: The url_path, but CGI escaped.
30
37
  #
31
38
  # Returns the String url_path
@@ -55,12 +55,12 @@ module Gollum
55
55
  @map = {}
56
56
  end
57
57
 
58
- def extract(_d)
58
+ def extract(data)
59
59
  raise RuntimeError,
60
60
  "#{self.class} has not implemented ##extract!"
61
61
  end
62
62
 
63
- def process(_d)
63
+ def process(data)
64
64
  raise RuntimeError,
65
65
  "#{self.class} has not implemented ##process!"
66
66
  end
@@ -0,0 +1,55 @@
1
+ begin
2
+ require 'bibtex'
3
+ require 'citeproc'
4
+ require 'csl'
5
+ require 'csl/styles'
6
+ rescue LoadError => error
7
+ end
8
+
9
+ # Render BibTeX files.
10
+ class Gollum::Filter::BibTeX < Gollum::Filter
11
+
12
+ def extract(data)
13
+ return data unless supported_format? && gems_available? && bib = ::BibTeX.parse(data).convert(:latex)
14
+ style = find_csl_data('csl') || ::CSL::Style.default
15
+ locale = find_csl_data('locale') || ::CSL::Locale.default
16
+
17
+ begin
18
+ style = ::CSL::Style.load(style)
19
+ ::CSL::Locale.load(locale)
20
+ rescue ::CSL::ParseError => error
21
+ log_failure(error.to_s)
22
+ return CGI.escapeHTML(data)
23
+ end
24
+
25
+ citeproc = ::CiteProc::Processor.new(style: style, locale: locale, format: 'html')
26
+ citeproc.import(bib.to_citeproc)
27
+ citeproc.bibliography.references.join('<br/>')
28
+ end
29
+
30
+ def process(data)
31
+ data
32
+ end
33
+
34
+ private
35
+
36
+ def log_failure(msg)
37
+ @markup.metadata = {} unless @markup.metadata
38
+ @markup.metadata['errors'] = [] unless @markup.metadata['errors']
39
+ @markup.metadata['errors'] << "Could not render the bibliography because no valid CSL or locale file was found in the wiki or in the CSL directory. Please commited a valid file, or install the csl-styles gem. The message from the parser was: #{msg.to_s}."
40
+ end
41
+
42
+ def supported_format?
43
+ @markup.format == :bib
44
+ end
45
+
46
+ def gems_available?
47
+ ::Gollum::Markup.formats[:bib][:enabled]
48
+ end
49
+
50
+ def find_csl_data(key)
51
+ path = @markup.metadata ? @markup.metadata[key] : nil
52
+ file = path ? @markup.wiki.file(path) : nil
53
+ file.nil? ? path : file.raw_data
54
+ end
55
+ end
@@ -6,8 +6,6 @@
6
6
  class Gollum::Filter::Code < Gollum::Filter
7
7
  def extract(data)
8
8
  case @markup.format
9
- when :txt
10
- return data
11
9
  when :asciidoc
12
10
  data.gsub!(/^(\[source,([^\r\n]*)\]\n)?----\n(.+?)\n----$/m) do
13
11
  cache_codeblock(Regexp.last_match[2], Regexp.last_match[3])
@@ -2,12 +2,15 @@
2
2
 
3
3
  # Emoji
4
4
  #
5
- # Render emoji such as :smile:
5
+ # Render an emoji tag such as ":smile:". In some rare situations, you have
6
+ # to escape emoji tags e.g. when your content contains something like
7
+ # "hh:mm:ss" or "rake app:shell:install". Prefix the leading colon with a
8
+ # backslash to disable this emoji tag e.g. "hh\:mm:ss".
6
9
  class Gollum::Filter::Emoji < Gollum::Filter
7
10
 
8
11
  EXTRACT_PATTERN = %r{
9
12
  (?<!\[{2})
10
- :(?<name>[\w-]+):
13
+ (?<escape>\\)?:(?<name>[\w-]+):
11
14
  (?!\]{^2})
12
15
  }ix
13
16
 
@@ -19,7 +22,11 @@ class Gollum::Filter::Emoji < Gollum::Filter
19
22
 
20
23
  def extract(data)
21
24
  data.gsub! EXTRACT_PATTERN do
22
- emoji_exists?($~[:name]) ? "=EEMMOOJJII=#{$~[:name]}=IIJJOOMMEE=" : $&
25
+ case
26
+ when $~[:escape] then $&[1..-1]
27
+ when emoji_exists?($~[:name]) then "=EEMMOOJJII=#{$~[:name]}=IIJJOOMMEE="
28
+ else $&
29
+ end
23
30
  end
24
31
  data
25
32
  end
@@ -0,0 +1,52 @@
1
+ require 'yaml'
2
+ require 'pathname'
3
+
4
+ # When using pandoc, put relevant bibliography metadata extracted in the YAML filter back in the document so it gets passed on to pandoc.
5
+ class Gollum::Filter::PandocBib < Gollum::Filter
6
+
7
+ BIB_PATH_KEYS = ['bibliography', 'csl']
8
+ BIB_KEYS = ['link-citations', 'nocite']
9
+ ALL_BIB_KEYS = BIB_PATH_KEYS + BIB_KEYS
10
+
11
+ def process(data)
12
+ data
13
+ end
14
+
15
+ def extract(data)
16
+ return data unless supported_format? && bibliography_metadata_present?
17
+ bib_metadata = {}
18
+ bib_metadata.merge!(@markup.metadata.select {|key, _value| BIB_KEYS.include?(key)})
19
+
20
+ BIB_PATH_KEYS.each do |bibliography_key|
21
+ if path = @markup.metadata[bibliography_key]
22
+ next unless file = @markup.wiki.file(path)
23
+ bib_metadata[bibliography_key] = path_for_bibfile(file)
24
+ end
25
+ end
26
+ bib_metadata.empty? ? data : "#{bib_metadata.to_yaml}---\n#{data}"
27
+ end
28
+
29
+ private
30
+
31
+ def path_for_bibfile(file)
32
+ if @markup.wiki.repo_is_bare
33
+ path = Pathname.new("#{::File.join(::Dir.tmpdir, file.sha)}#{::File.extname(file.path)}")
34
+ unless path.exist?
35
+ path.open('w') do |copy_file|
36
+ copy_file.write(file.raw_data)
37
+ end
38
+ end
39
+ path.to_s
40
+ else
41
+ ::File.expand_path(::File.join(@markup.wiki.path, file.path))
42
+ end
43
+ end
44
+
45
+ def supported_format?
46
+ @markup.format == :markdown
47
+ end
48
+
49
+ def bibliography_metadata_present?
50
+ @markup.metadata && @markup.metadata.keys.any? {|key| ALL_BIB_KEYS.include?(key)}
51
+ end
52
+ end
@@ -6,6 +6,10 @@
6
6
 
7
7
  class Gollum::Filter::PlainText < Gollum::Filter
8
8
 
9
+ def do_process(_d)
10
+ skip? ? _d : process(_d)
11
+ end
12
+
9
13
  def extract(data)
10
14
  @markup.format == :txt ? "<pre>#{CGI.escapeHTML(data)}</pre>" : data
11
15
  end
@@ -37,7 +37,7 @@ require 'zlib'
37
37
  #
38
38
  class Gollum::Filter::PlantUML < Gollum::Filter
39
39
 
40
- DEFAULT_URL = "http://localhost:8080/plantuml/png"
40
+ DEFAULT_URL = "http://www.plantuml.com/plantuml/png"
41
41
 
42
42
  # Configuration class used to change the behaviour of the PlatnUML filter.
43
43
  #
@@ -69,7 +69,6 @@ class Gollum::Filter::PlantUML < Gollum::Filter
69
69
  # Extract all sequence diagram blocks into the map and replace with
70
70
  # placeholders.
71
71
  def extract(data)
72
- return data if @markup.format == :txt
73
72
  data.gsub(/(@startuml\r?\n.+?\r?\n@enduml\r?$)/m) do
74
73
  id = Digest::SHA1.hexdigest($1)
75
74
  @map[id] = { :code => $1 }
@@ -115,9 +114,9 @@ class Gollum::Filter::PlantUML < Gollum::Filter
115
114
  # Transcoder class in the PlantUML java code.
116
115
  def gen_url(text)
117
116
  result = ""
118
- compressedData = Zlib::Deflate.deflate(text)
117
+ compressedData = Zlib::Deflate.new(nil, -Zlib::MAX_WBITS).deflate(text, Zlib::FINISH)
118
+
119
119
  compressedData.chars.each_slice(3) do |bytes|
120
- #print bytes[0], ' ' , bytes[1] , ' ' , bytes[2]
121
120
  b1 = bytes[0].nil? ? 0 : (bytes[0].ord & 0xFF)
122
121
  b2 = bytes[1].nil? ? 0 : (bytes[1].ord & 0xFF)
123
122
  b3 = bytes[2].nil? ? 0 : (bytes[2].ord & 0xFF)
@@ -13,7 +13,6 @@ require 'open-uri'
13
13
  #
14
14
  class Gollum::Filter::RemoteCode < Gollum::Filter
15
15
  def extract(data)
16
- return data if @markup.format == :txt
17
16
  data.gsub(/^[ \t]*``` ?([^:\n\r]+):((http)?[^`\n\r]+)```/) do
18
17
  language = Regexp.last_match[1]
19
18
  uri = Regexp.last_match[2]
@@ -10,7 +10,7 @@ class Gollum::Filter::Sanitize < Gollum::Filter
10
10
  doc = Nokogiri::HTML::DocumentFragment.parse(data)
11
11
  doc = @markup.sanitize.clean_node!(doc)
12
12
 
13
- doc.to_xml(@markup.to_xml_opts)
13
+ doc.to_xml(@markup.to_xml_opts).gsub(/<p><\/p>/, '')
14
14
  else
15
15
  data
16
16
  end
@@ -4,7 +4,6 @@
4
4
  class Gollum::Filter::Tags < Gollum::Filter
5
5
  # Extract all tags into the tagmap and replace with placeholders.
6
6
  def extract(data)
7
- return data if @markup.format == :txt || @markup.format == :asciidoc
8
7
  data.gsub!(/(.?)\[\[(.+?)\]\]([^\[]?)/) do
9
8
  if Regexp.last_match[1] == "'" && Regexp.last_match[3] != "'"
10
9
  "[[#{Regexp.last_match[2]}]]#{Regexp.last_match[3]}"
@@ -28,12 +27,6 @@ class Gollum::Filter::Tags < Gollum::Filter
28
27
  data
29
28
  end
30
29
 
31
- def register_tag(tag)
32
- id = "TAG#{Digest::SHA1.hexdigest(tag)}TAG"
33
- @map[id] = tag
34
- id
35
- end
36
-
37
30
  # Process all text nodes from the doc and replace the placeholders with the
38
31
  # final markup.
39
32
  def process(rendered_data)
@@ -60,6 +53,13 @@ class Gollum::Filter::Tags < Gollum::Filter
60
53
  private
61
54
 
62
55
  PREFORMATTED_TAGS = %w(code tt)
56
+ INCLUDE_TAG = 'include:'
57
+
58
+ def register_tag(tag)
59
+ id = "TAG#{Digest::SHA1.hexdigest(tag)}TAG"
60
+ @map[id] = tag
61
+ id
62
+ end
63
63
 
64
64
  def is_preformatted?(node)
65
65
  node && (PREFORMATTED_TAGS.include?(node.name) ||
@@ -73,20 +73,43 @@ class Gollum::Filter::Tags < Gollum::Filter
73
73
  #
74
74
  # Returns the String HTML version of the tag.
75
75
  def process_tag(tag)
76
- if tag =~ /^_TOC_/
76
+ link_part, extra = parse_tag_parts(tag)
77
+ return generate_link('', nil, nil, :page_absent) if link_part.nil?
78
+ img_args = extra ? [extra, link_part] : [link_part]
79
+ mime = MIME::Types.type_for(::File.extname(img_args.first.to_s)).first
80
+
81
+ result = if tag =~ /^_TOC_/
77
82
  %{[[#{tag}]]}
78
- elsif tag =~ /^_$/
83
+ elsif link_part =~ /^_$/
79
84
  %{<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
85
+ elsif link_part =~ /^#{INCLUDE_TAG}/
86
+ process_include_tag(link_part)
87
+ elsif mime && mime.content_type =~ /^image/
88
+ process_image_tag(*img_args)
89
+ elsif external = process_external_link_tag(link_part, extra)
90
+ external
91
+ end
92
+ result ? result : process_link_tag(link_part, extra)
93
+ end
94
+
95
+ # Process the tag parts as an internal link to a File or Page.
96
+ def process_link_tag(link_part, pretty_name)
97
+ process_file_link_tag(link_part, pretty_name) || process_page_link_tag(link_part, pretty_name)
98
+ end
99
+
100
+ # Parse the tag (stuff between the double brackets) into a link part and additional information (a pretty name, description, or image options).
101
+ #
102
+ # tag - The String tag contents (the stuff inside the double
103
+ # brackets).
104
+ #
105
+ # Returns an Array of the form [link_part, extra], where both elements are Strings and the second element may be nil.
106
+ def parse_tag_parts(tag)
107
+ parts = tag.split('|').map(&:strip)[0..1]
108
+ parts.reverse! if @markup.reverse_links?
109
+ if parts[1]
110
+ return parts[1], parts[0]
88
111
  else
89
- process_page_link_tag(tag)
112
+ return parts[0], nil
90
113
  end
91
114
  end
92
115
 
@@ -94,16 +117,14 @@ class Gollum::Filter::Tags < Gollum::Filter
94
117
  #
95
118
  # tag - The String tag contents (the stuff inside the double brackets).
96
119
  #
97
- # Returns the String HTML if the tag is a valid image tag or nil
98
- # if it is not.
99
- #
120
+ # Returns the String HTML if the tag includes a valid page or an error message if the page could not be found.
100
121
  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
-
122
+ len = INCLUDE_TAG.length
123
+ return html_error('Cannot process include directive: no page name given') if tag.length <= len
124
+ page_name = tag[len..-1]
125
+ resolved_page_name = ::File.expand_path(page_name, "#{::File::SEPARATOR}#{@markup.dir}")
105
126
  if @markup.include_levels > 0
106
- page = find_page_from_name(resolved_page_name)
127
+ page = find_page_from_path(resolved_page_name)
107
128
  if page
108
129
  page.formatted_data(@markup.encoding, @markup.include_levels-1)
109
130
  else
@@ -116,110 +137,42 @@ class Gollum::Filter::Tags < Gollum::Filter
116
137
 
117
138
  # Attempt to process the tag as an image tag.
118
139
  #
119
- # tag - The String tag contents (the stuff inside the double brackets).
140
+ # path - The String path to the image.
141
+ # options - The String of options for the image (the stuff after the '|'). Optional.
120
142
  #
121
143
  # Returns the String HTML if the tag is a valid image tag or nil
122
144
  # 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
- if (file = @markup.find_file(name))
129
- path = ::File.join @markup.wiki.base_path, file.path
130
- elsif name =~ /^https?:\/\/.+(jpg|png|gif|svg|bmp)$/i
131
- path = name
132
- elsif name =~ /.+(jpg|png|gif|svg|bmp)$/i
133
- # If is image, file not found and no link, then populate with empty String
134
- # We can than add an image not found alt attribute for this later
135
- path = ""
136
- end
137
-
138
- if path
139
- opts = parse_image_tag_options(tag)
140
-
141
- containered = false
142
-
143
- classes = [] # applied to whatever the outermost container is
144
- attrs = [] # applied to the image
145
-
146
- align = opts['align']
147
- if opts['float']
148
- containered = true
149
- align ||= 'left'
150
- if %w{left right}.include?(align)
151
- classes << "float-#{align}"
152
- end
153
- elsif %w{top texttop middle absmiddle bottom absbottom baseline}.include?(align)
154
- attrs << %{align="#{align}"}
155
- elsif align
156
- if %w{left center right}.include?(align)
157
- containered = true
158
- classes << "align-#{align}"
159
- end
160
- end
161
-
162
- if (width = opts['width'])
163
- if width =~ /^\d+(\.\d+)?(em|px)$/
164
- attrs << %{width="#{width}"}
165
- end
166
- end
167
-
168
- if (height = opts['height'])
169
- if height =~ /^\d+(\.\d+)?(em|px)$/
170
- attrs << %{height="#{height}"}
171
- end
172
- end
173
-
174
- if path != "" && (alt = opts['alt'])
175
- attrs << %{alt="#{alt}"}
176
- elsif path == ""
177
- attrs << %{alt="Image not found"}
178
- end
179
-
180
- attr_string = attrs.size > 0 ? attrs.join(' ') + ' ' : ''
181
-
182
- if opts['frame'] || containered
183
- classes << 'frame' if opts['frame']
184
- %{<span class="#{classes.join(' ')}">} +
185
- %{<span>} +
186
- %{<img src="#{path}" #{attr_string}/>} +
187
- (alt ? %{<span>#{alt}</span>} : '') +
188
- %{</span>} +
189
- %{</span>}
190
- else
191
- %{<img src="#{path}" #{attr_string}/>}
192
- end
145
+ def process_image_tag(path, options = nil)
146
+ opts = parse_image_tag_options(options)
147
+ if path =~ /^https?:\/\/.+$/i
148
+ generate_image(path, opts)
149
+ elsif file = @markup.find_file(path)
150
+ generate_image(generate_href_for_path(file.path), opts)
151
+ else
152
+ generate_image('', opts)
193
153
  end
194
154
  end
195
155
 
196
- # Parse any options present on the image tag and extract them into a
156
+ # Parse any options present on the image tag (comma separated) and extract them into a
197
157
  # Hash of option names and values.
198
158
  #
199
- # tag - The String tag contents (the stuff inside the double brackets).
159
+ # options - The String image options (the stuff in the after '|').
200
160
  #
201
161
  # Returns the options Hash:
202
162
  # key - The String option name.
203
163
  # val - The String option value or true if it is a binary option.
204
- def parse_image_tag_options(tag)
205
- tag.split('|')[1..-1].inject({}) do |memo, attr|
206
- parts = attr.split('=').map { |x| x.strip }
207
- memo[parts[0]] = (parts.size == 1 ? true : parts[1])
164
+ def parse_image_tag_options(options)
165
+ return {} if options.nil?
166
+ options.split(',').inject({}) do |memo, attr|
167
+ parts = attr.split('=').map { |x| x.strip }
168
+ memo[parts[0].to_sym] = (parts.size == 1 ? true : parts[1])
208
169
  memo
209
170
  end
210
171
  end
211
172
 
212
173
  # Return the String HTML if the tag is a valid external link tag or
213
174
  # nil if it is not.
214
- def process_external_link_tag(tag)
215
- parts = tag.split('|')
216
- parts.reverse! if @markup.reverse_links?
217
- return if parts.size.zero?
218
- if parts.size == 1
219
- url = parts[0].strip
220
- else
221
- name, url = *parts.compact.map(&:strip)
222
- end
175
+ def process_external_link_tag(url, pretty_name = nil)
223
176
  accepted_protocols = @markup.wiki.sanitization.protocols['a']['href'].dup
224
177
  if accepted_protocols.include?(:relative)
225
178
  accepted_protocols.select!{|protocol| protocol != :relative}
@@ -228,40 +181,22 @@ class Gollum::Filter::Tags < Gollum::Filter
228
181
  regexp = %r{^((#{accepted_protocols.join("|")}):)}
229
182
  end
230
183
  if url =~ regexp
231
- if name.nil?
232
- %{<a href="#{url}">#{url}</a>}
233
- else
234
- %{<a href="#{url}">#{name}</a>}
235
- end
184
+ generate_link(url, pretty_name, nil, :external)
236
185
  else
237
186
  nil
238
187
  end
239
-
240
188
  end
241
189
 
242
190
  # Attempt to process the tag as a file link tag.
243
191
  #
244
- # tag - The String tag contents (the stuff inside the double
245
- # brackets).
192
+ # link_part - The String part of the tag containing the link
193
+ # pretty_name - The String name for the link (optional)
246
194
  #
247
195
  # Returns the String HTML if the tag is a valid file link tag or nil
248
196
  # if it is not.
249
- def process_file_link_tag(tag)
250
- parts = tag.split('|')
251
- return if parts.size.zero?
252
-
253
- name = parts[0].strip
254
- path = parts[1] && parts[1].strip
255
- if path && file = @markup.find_file(path)
256
- path = ::File.join @markup.wiki.base_path, file.path
257
- else
258
- path = nil
259
- end
260
-
261
- if name && path && file
262
- %{<a href="#{::File.join @markup.wiki.base_path, file.path}">#{name}</a>}
263
- elsif name && path
264
- %{<a href="#{path}">#{name}</a>}
197
+ def process_file_link_tag(link_part, pretty_name)
198
+ if file = @markup.find_file(link_part)
199
+ generate_link(file.path, pretty_name, nil, :file)
265
200
  else
266
201
  nil
267
202
  end
@@ -269,59 +204,147 @@ class Gollum::Filter::Tags < Gollum::Filter
269
204
 
270
205
  # Attempt to process the tag as a page link tag.
271
206
  #
272
- # tag - The String tag contents (the stuff inside the double
273
- # brackets).
207
+ # link_part - The String part of the tag containing the link
208
+ # pretty_name - The String name for the link (optional)
274
209
  #
275
210
  # Returns the String HTML if the tag is a valid page link tag or nil
276
211
  # if it is not.
277
- def process_page_link_tag(tag)
278
- parts = tag.split('|')
279
- parts.reverse! if @markup.reverse_links?
212
+ def process_page_link_tag(link_part, pretty_name = nil)
213
+ presence = :page_absent
214
+ link = link_part
215
+ page = find_page_from_path(link)
216
+
217
+ # If no match yet, try finding page with anchor removed
218
+ if (page.nil? && pos = link.rindex('#'))
219
+ extra = link[pos..-1]
220
+ link = link[0...pos]
221
+ page = find_page_from_path(link)
222
+ end
223
+ presence = :page_present if page
224
+
225
+ name = pretty_name ? pretty_name : link
226
+ link = page ? page.escaped_url_path : CGI.escape(link)
227
+ generate_link(link, name, extra, presence)
228
+ end
280
229
 
281
- name, page_name = *parts.compact.map(&:strip)
282
- cname = page_name ? page_name : name.to_s
230
+ # Find a page from a given path
231
+ #
232
+ # path - The String path to search for.
233
+ #
234
+ # Returns a Gollum::Page instance if a page is found, or nil otherwise
235
+ def find_page_from_path(path)
236
+ slash = path.rindex('/')
283
237
 
284
- presence = "absent"
285
- link_name = cname
286
- page, extra = find_page_from_name(cname)
287
- if page
288
- link_name = page.name
289
- presence = "present"
238
+ unless slash.nil?
239
+ name = path[slash+1..-1]
240
+ path = path[0..slash]
241
+ @markup.wiki.paged(name, path)
242
+ else
243
+ @markup.wiki.page(path)
290
244
  end
291
- link = ::File.join(@markup.wiki.base_path, page ? page.escaped_url_path : CGI.escape(link_name))
292
-
293
- # //page is invalid
294
- # strip all duplicate forward slashes using helpers.rb trim_leading_slash
295
- # //page => /page
296
- link = trim_leading_slash link
245
+ end
297
246
 
298
- %{<a class="internal #{presence}" href="#{link}#{extra}">#{name}</a>}
247
+ # Generate an HTML link tag.
248
+ #
249
+ # path - The String path (href) to construct a link to.
250
+ # name - The String name of the link (text inside the link tag). Optional.
251
+ # extra - The String anchor to add the link. Optional.
252
+ # kind - A Symbol indicating whether this is a Page, File, or External link.
253
+ #
254
+ # Returns a String HTML link tag.
255
+ def generate_link(path, name = nil, extra = nil, kind = nil)
256
+ url = kind == :external ? path : generate_href_for_path(path, extra)
257
+ %{<a #{css_options_for_link(kind)} href="#{url}">#{name || path}</a>}
299
258
  end
300
259
 
301
- # Find a page from a given cname. If the page has an anchor (#) and has
302
- # no match, strip the anchor and try again.
260
+ # Generate a normalized href for a path, taking into consideration the wiki's path settings.
303
261
  #
304
- # cname - The String canonical page name including path.
262
+ # path - The String path to generate an href for.
263
+ # extra - The String anchor to add to the href. Optional.
305
264
  #
306
- # Returns a Gollum::Page instance if a page is found, or an Array of
307
- # [Gollum::Page, String extra] if a page without the extra anchor data
308
- # is found.
309
- def find_page_from_name(cname)
310
- slash = cname.rindex('/')
265
+ # Returns a String href.
266
+ def generate_href_for_path(path, extra = nil)
267
+ "#{trim_leading_slash(::File.join(@markup.wiki.base_path, path))}#{extra}"
268
+ end
311
269
 
312
- unless slash.nil?
313
- name = cname[slash+1..-1]
314
- path = cname[0..slash]
315
- page = @markup.wiki.paged(name, path)
270
+ # Construct a CSS class and attribute string for different kinds of links: internal Pages (absent or present) and Files, and External links.
271
+ #
272
+ # kind - The Symbol indicating the kind of link. Can be one of: :page_absent, :page_present, :file, :external.
273
+ #
274
+ # Returns the String CSS class and attributes.
275
+ def css_options_for_link(kind)
276
+ case kind
277
+ when :page_absent
278
+ 'class="internal absent"'
279
+ when :page_present
280
+ 'class="internal present"'
281
+ when :file
282
+ nil
283
+ when :external
284
+ nil
285
+ else
286
+ nil
287
+ end
288
+ end
289
+
290
+ # Generate an HTML image tag.
291
+ #
292
+ # path - The String path (href) of the image.
293
+ # options - The Hash of parsed image options.
294
+ #
295
+ # Returns a String HTML img tag.
296
+ def generate_image(path, options = nil)
297
+ classes, attrs, containered = generate_image_attributes(options)
298
+ attrs[:alt] = 'Image not found' if path.empty?
299
+ attr_string = attrs.map {|key, value| "#{key}=\"#{value}\""}.join(' ')
300
+
301
+ if containered
302
+ %{<span class="#{classes.join(' ')}">} +
303
+ %{<span>} +
304
+ %{<img src="#{path}" #{attr_string}/>} +
305
+ (attrs[:alt] ? %{<span>#{attrs[:alt]}</span>} : '') +
306
+ %{</span>} +
307
+ %{</span>}
316
308
  else
317
- page = @markup.wiki.paged(cname, '/') || @markup.wiki.page(cname)
309
+ %{<img src="#{path}" #{attr_string}/>}
318
310
  end
311
+ end
319
312
 
320
- if page
321
- return page
313
+ # Helper method to generate the styling attributes and elements for an image tag.
314
+ #
315
+ # options - The Hash of parsed image options.
316
+ #
317
+ # Returns an Array of CSS classes, a Hash of CSS attributes, and a Boolean indicating whether or not the image is containered.
318
+ def generate_image_attributes(options)
319
+ containered = false
320
+ classes = [] # applied to whatever the outermost container is
321
+ attrs = {} # applied to the image
322
+
323
+ align = options[:align]
324
+ if options[:float]
325
+ containered = true
326
+ align ||= 'left'
327
+ if %w{left right}.include?(align)
328
+ classes << "float-#{align}"
329
+ end
330
+ elsif %w{top texttop middle absmiddle bottom absbottom baseline}.include?(align)
331
+ attrs[:align] = align
332
+ elsif align
333
+ if %w{left center right}.include?(align)
334
+ containered = true
335
+ classes << "align-#{align}"
336
+ end
322
337
  end
323
- if (pos = cname.index('#'))
324
- [@markup.wiki.page(cname[0...pos]), cname[pos..-1]]
338
+
339
+ if options[:frame]
340
+ containered = true
341
+ classes << 'frame'
325
342
  end
343
+
344
+ attrs[:alt] = options[:alt] if options[:alt]
345
+ attrs[:width] = options[:width] if options[:width] =~ /^\d+(\.\d+)?(em|px)$/
346
+ attrs[:height] = options[:height] if options[:height] =~ /^\d+(\.\d+)?(em|px)$/
347
+
348
+ return classes, attrs, containered
326
349
  end
327
350
  end