Project

General

Profile

Feature #32424 » 0001-CommonMark-Markdown-text-formatter.patch

Jens Krämer, 2019-11-06 09:27

View differences:

Gemfile
39 39
  gem "redcarpet", "~> 3.5.0"
40 40
end
41 41

  
42
# Optional CommonMark support, not for JRuby
43
group :common_mark do
44
  gem "html-pipeline", "~> 2.12"
45
  gem "commonmarker", "~> 0.20"
46
  gem "sanitize", "~> 5.1"
47
end
48

  
42 49
# Include database gems for the adapters found in the database
43 50
# configuration file
44 51
require 'erb'
lib/redmine.rb
29 29
rescue LoadError
30 30
  # Redcarpet is not available
31 31
end
32
begin
33
  require 'commonmarker' unless Object.const_defined?(:CommonMarker)
34
rescue LoadError
35
  # CommonMarker is not available
36
end
32 37

  
33 38
require 'redmine/acts/positioned'
34 39

  
......
328 333
Redmine::WikiFormatting.map do |format|
329 334
  format.register :textile
330 335
  format.register :markdown if Object.const_defined?(:Redcarpet)
336
  if Object.const_defined?(:CommonMarker)
337
    format.register :common_mark, label: 'CommonMark Markdown (GitHub Flavored)'
338
  end
331 339
end
332 340

  
333 341
ActionView::Template.register_template_handler :rsb, Redmine::Views::ApiTemplateHandler
lib/redmine/wiki_formatting/common_mark/external_links_filter.rb
1
# frozen_string_literal: true
2

  
3
# Redmine - project management software
4
# Copyright (C) 2006-2019  Jean-Philippe Lang
5
#
6
# This program is free software; you can redistribute it and/or
7
# modify it under the terms of the GNU General Public License
8
# as published by the Free Software Foundation; either version 2
9
# of the License, or (at your option) any later version.
10
#
11
# This program is distributed in the hope that it will be useful,
12
# but WITHOUT ANY WARRANTY; without even the implied warranty of
13
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
# GNU General Public License for more details.
15
#
16
# You should have received a copy of the GNU General Public License
17
# along with this program; if not, write to the Free Software
18
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
19

  
20
require 'uri'
21

  
22
module Redmine
23
  module WikiFormatting
24
    module CommonMark
25

  
26
      # adds class="external" to external links, and class="email" to mailto
27
      # links
28
      class ExternalLinksFilter < HTML::Pipeline::Filter
29
        def call
30
          doc.search("a").each do |node|
31
            url = node["href"]
32
            next unless url
33
            next if url.starts_with?("/") || url.starts_with?("#") || !url.include?(':')
34

  
35
            scheme = URI.parse(url).scheme
36
            next if scheme.blank?
37

  
38
            klass = node["class"].presence
39
            node["class"] = [
40
              klass,
41
              (scheme == "mailto" ? "email" : "external")
42
            ].compact.join " "
43
          end
44
          doc
45
        end
46
      end
47

  
48
    end
49
  end
50
end
lib/redmine/wiki_formatting/common_mark/fixup_auto_links_filter.rb
1
# frozen_string_literal: true
2

  
3
# Redmine - project management software
4
# Copyright (C) 2006-2019  Jean-Philippe Lang
5
#
6
# This program is free software; you can redistribute it and/or
7
# modify it under the terms of the GNU General Public License
8
# as published by the Free Software Foundation; either version 2
9
# of the License, or (at your option) any later version.
10
#
11
# This program is distributed in the hope that it will be useful,
12
# but WITHOUT ANY WARRANTY; without even the implied warranty of
13
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
# GNU General Public License for more details.
15
#
16
# You should have received a copy of the GNU General Public License
17
# along with this program; if not, write to the Free Software
18
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
19

  
20

  
21
module Redmine
22
  module WikiFormatting
23
    module CommonMark
24

  
25
      # fixes:
26
      # - autolinked email addresses that are actually references to users:
27
      #   user:<a href="mailto:user@example.org">user@example.org</a>
28
      #   @<a href="mailto:user@example.org">user@example.org</a>
29
      # - autolinked hi res image names that look like email addresses:
30
      #   <a href="mailto:printscreen@2x.png">printscreen@2x.png</a>
31
      class FixupAutoLinksFilter < HTML::Pipeline::Filter
32
        USER_LINK_PREFIX = /(@|user:)\z/.freeze
33
        HIRES_IMAGE = /.+@\dx\.(bmp|gif|jpg|jpe|jpeg|png)\z/.freeze
34

  
35
        def call
36
          doc.search("a").each do |node|
37
            unless url = node['href'] and url.starts_with?('mailto:')
38
              next
39
            end
40

  
41
            if (p = node.previous and p.text? and
42
                p.text =~ USER_LINK_PREFIX) or
43
               (node.text =~ HIRES_IMAGE)
44

  
45
              node.replace node.text
46
            end
47
          end
48
          doc
49
        end
50
      end
51

  
52
    end
53
  end
54
end
55

  
lib/redmine/wiki_formatting/common_mark/formatter.rb
1
# frozen_string_literal: true
2

  
3
# Redmine - project management software
4
# Copyright (C) 2006-2019  Jean-Philippe Lang
5
#
6
# This program is free software; you can redistribute it and/or
7
# modify it under the terms of the GNU General Public License
8
# as published by the Free Software Foundation; either version 2
9
# of the License, or (at your option) any later version.
10
#
11
# This program is distributed in the hope that it will be useful,
12
# but WITHOUT ANY WARRANTY; without even the implied warranty of
13
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
# GNU General Public License for more details.
15
#
16
# You should have received a copy of the GNU General Public License
17
# along with this program; if not, write to the Free Software
18
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
19

  
20
require 'html/pipeline'
21

  
22
module Redmine
23
  module WikiFormatting
24
    module CommonMark
25

  
26
      # configuration of the rendering pipeline
27
      PIPELINE_CONFIG = {
28
        # https://github.com/gjtorikian/commonmarker#extensions
29
        commonmarker_extensions: %i(
30
          table
31
          strikethrough
32
          tagfilter
33
          autolink
34
        ).freeze,
35

  
36
        # https://github.com/gjtorikian/commonmarker#parse-options
37
        commonmarker_parse_options: %i(
38
          FOOTNOTES
39
          STRIKETHROUGH_DOUBLE_TILDE
40
          UNSAFE
41
          VALIDATE_UTF8
42
        ).freeze,
43

  
44
        # https://github.com/gjtorikian/commonmarker#render-options
45
        commonmarker_render_options: %i(
46
          UNSAFE
47
        ).freeze,
48
      }.freeze
49

  
50

  
51
      MarkdownPipeline = HTML::Pipeline.new [
52
        MarkdownFilter,
53
        SanitizationFilter,
54
        SyntaxHighlightFilter,
55
        FixupAutoLinksFilter,
56
        ExternalLinksFilter,
57
      ], PIPELINE_CONFIG
58

  
59
      class Formatter < Redmine::WikiFormatting::Markdown::Formatter
60
        def to_html(*args)
61
          result = MarkdownPipeline.call @text
62
          result[:output].to_s
63
        end
64
      end
65

  
66
    end
67
  end
68
end
lib/redmine/wiki_formatting/common_mark/helper.rb
1
# frozen_string_literal: true
2

  
3
# Redmine - project management software
4
# Copyright (C) 2006-2019  Jean-Philippe Lang
5
#
6
# This program is free software; you can redistribute it and/or
7
# modify it under the terms of the GNU General Public License
8
# as published by the Free Software Foundation; either version 2
9
# of the License, or (at your option) any later version.
10
#
11
# This program is distributed in the hope that it will be useful,
12
# but WITHOUT ANY WARRANTY; without even the implied warranty of
13
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
# GNU General Public License for more details.
15
#
16
# You should have received a copy of the GNU General Public License
17
# along with this program; if not, write to the Free Software
18
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
19

  
20

  
21
module Redmine
22
  module WikiFormatting
23
    module CommonMark
24
      module Helper
25
        include Redmine::WikiFormatting::Markdown::Helper
26

  
27
        def wikitoolbar_for(field_id, preview_url = preview_text_path)
28
          heads_for_wiki_formatter
29
          help_file = "/help/#{current_language.to_s.downcase}/wiki_syntax_common_mark.html"
30
          # fall back to the english help page if there is none for the current
31
          # language
32
          unless File.readable? Rails.root.join("public", help_file)
33
            help_file = "/help/en/wiki_syntax_common_mark.html"
34
          end
35
          url = "#{Redmine::Utils.relative_url_root}#{help_file}"
36
          javascript_tag("var wikiToolbar = new jsToolBar(document.getElementById('#{field_id}')); wikiToolbar.setHelpLink('#{escape_javascript url}'); wikiToolbar.setPreviewUrl('#{escape_javascript preview_url}'); wikiToolbar.draw();")
37
        end
38

  
39
        # removes the 'underline' icon from the markdown toolbar since there
40
        # is no such thing in CommonMark
41
        def heads_for_wiki_formatter
42
          unless @common_mark_heads_for_wiki_formatter_included
43
            super
44
            content_for :header_tags do
45
              javascript_tag(%[delete jsToolBar.prototype.elements.ins;])
46
            end
47
            @common_mark_heads_for_wiki_formatter_included = true
48
          end
49
        end
50
      end
51
    end
52
  end
53
end
54

  
lib/redmine/wiki_formatting/common_mark/html_parser.rb
1
# frozen_string_literal: true
2

  
3
# Redmine - project management software
4
# Copyright (C) 2006-2019  Jean-Philippe Lang
5
#
6
# This program is free software; you can redistribute it and/or
7
# modify it under the terms of the GNU General Public License
8
# as published by the Free Software Foundation; either version 2
9
# of the License, or (at your option) any later version.
10
#
11
# This program is distributed in the hope that it will be useful,
12
# but WITHOUT ANY WARRANTY; without even the implied warranty of
13
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
# GNU General Public License for more details.
15
#
16
# You should have received a copy of the GNU General Public License
17
# along with this program; if not, write to the Free Software
18
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
19

  
20

  
21
module Redmine
22
  module WikiFormatting
23
    module CommonMark
24
      HtmlParser = Redmine::WikiFormatting::Markdown::HtmlParser
25
    end
26
  end
27
end
lib/redmine/wiki_formatting/common_mark/markdown_filter.rb
1
# frozen_string_literal: true
2

  
3
# Redmine - project management software
4
# Copyright (C) 2006-2019  Jean-Philippe Lang
5
#
6
# This program is free software; you can redistribute it and/or
7
# modify it under the terms of the GNU General Public License
8
# as published by the Free Software Foundation; either version 2
9
# of the License, or (at your option) any later version.
10
#
11
# This program is distributed in the hope that it will be useful,
12
# but WITHOUT ANY WARRANTY; without even the implied warranty of
13
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
# GNU General Public License for more details.
15
#
16
# You should have received a copy of the GNU General Public License
17
# along with this program; if not, write to the Free Software
18
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
19

  
20

  
21
module Redmine
22
  module WikiFormatting
23
    module CommonMark
24

  
25
      # Converts Markdown to HTML using CommonMarker
26
      #
27
      # We do not use the stock HTML::Pipeline::MarkdownFilter because this
28
      # does not allow for straightforward configuration of render and parsing
29
      # options
30
      class MarkdownFilter < HTML::Pipeline::TextFilter
31
        def initialize(text, context = nil, result = nil)
32
          super text, context, result
33
          @text = @text.delete "\r"
34
        end
35

  
36
        def call
37
          doc = CommonMarker.render_doc(@text, parse_options, extensions)
38
          html = doc.to_html render_options, extensions
39
          html.rstrip!
40
          html
41
        end
42

  
43
        private
44

  
45
        def extensions
46
          context.fetch :commonmarker_extensions, []
47
        end
48

  
49
        def parse_options
50
          context.fetch :commonmarker_parse_options, :DEFAULT
51
        end
52

  
53
        def render_options
54
          context.fetch :commonmarker_render_options, :DEFAULT
55
        end
56
      end
57

  
58
    end
59
  end
60
end
lib/redmine/wiki_formatting/common_mark/sanitization_filter.rb
1
# frozen_string_literal: true
2

  
3
# Redmine - project management software
4
# Copyright (C) 2006-2019  Jean-Philippe Lang
5
#
6
# This program is free software; you can redistribute it and/or
7
# modify it under the terms of the GNU General Public License
8
# as published by the Free Software Foundation; either version 2
9
# of the License, or (at your option) any later version.
10
#
11
# This program is distributed in the hope that it will be useful,
12
# but WITHOUT ANY WARRANTY; without even the implied warranty of
13
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
# GNU General Public License for more details.
15
#
16
# You should have received a copy of the GNU General Public License
17
# along with this program; if not, write to the Free Software
18
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
19

  
20

  
21
module Redmine
22
  module WikiFormatting
23
    module CommonMark
24

  
25
      # sanitizes rendered HTML using the Sanitize gem
26
      class SanitizationFilter < HTML::Pipeline::SanitizationFilter
27
        def whitelist
28
          @@whitelist ||= customize_whitelist(super.deep_dup)
29
        end
30

  
31
        private
32

  
33
        # customizes the whitelist defined in
34
        # https://github.com/jch/html-pipeline/blob/master/lib/html/pipeline/sanitization_filter.rb
35
        def customize_whitelist(whitelist)
36
          # Disallow `name` attribute globally, allow on `a`
37
          whitelist[:attributes][:all].delete("name")
38
          whitelist[:attributes]["a"].push("name")
39

  
40
          # allow class on code tags (this holds the language info from fenced
41
          # code bocks and has the format language-foo)
42
          whitelist[:attributes]["code"] = %w(class)
43
          whitelist[:transformers].push lambda{|env|
44
            node = env[:node]
45
            return unless node.name == "code"
46
            return unless node.has_attribute?("class")
47
            unless node["class"] =~ /\Alanguage-(\w+)\z/
48
              node.remove_attribute("class")
49
            end
50
          }
51

  
52
          # Allow table cell alignment by style attribute
53
          #
54
          # Only necessary if we used the TABLE_PREFER_STYLE_ATTRIBUTES
55
          # commonmarker option (which we do not, currently).
56
          # By default, the align attribute is used (which is allowed on all
57
          # elements).
58
          # whitelist[:attributes]["th"] = %w(style)
59
          # whitelist[:attributes]["td"] = %w(style)
60
          # whitelist[:css] = { properties: ["text-align"] }
61

  
62
          # Allow `id` in a and li elements for footnotes
63
          # and remove any `id` properties not matching for footnotes
64
          whitelist[:attributes]["a"].push "id"
65
          whitelist[:attributes]["li"] = %w(id)
66
          whitelist[:transformers].push lambda{|env|
67
            node = env[:node]
68
            return unless node.name == "a" || node.name == "li"
69
            return unless node.has_attribute?("id")
70
            return if node.name == "a" && node["id"] =~ /\Afnref\d+\z/
71
            return if node.name == "li" && node["id"] =~ /\Afn\d+\z/
72
            node.remove_attribute("id")
73
          }
74

  
75
          # allw the same set of URL schemes for links as is the default in
76
          # Redmine::Helpers::URL#uri_with_safe_scheme?
77
          whitelist[:protocols][:a] = [
78
            'http', 'https', 'ftp', 'mailto', :relative
79
          ]
80

  
81
          whitelist
82
        end
83
      end
84

  
85
    end
86
  end
87
end
lib/redmine/wiki_formatting/common_mark/syntax_highlight_filter.rb
1
# frozen_string_literal: true
2

  
3
# Redmine - project management software
4
# Copyright (C) 2006-2019  Jean-Philippe Lang
5
#
6
# This program is free software; you can redistribute it and/or
7
# modify it under the terms of the GNU General Public License
8
# as published by the Free Software Foundation; either version 2
9
# of the License, or (at your option) any later version.
10
#
11
# This program is distributed in the hope that it will be useful,
12
# but WITHOUT ANY WARRANTY; without even the implied warranty of
13
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
# GNU General Public License for more details.
15
#
16
# You should have received a copy of the GNU General Public License
17
# along with this program; if not, write to the Free Software
18
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
19

  
20

  
21
module Redmine
22
  module WikiFormatting
23
    module CommonMark
24

  
25
      # Redmine Syntax highlighting for <pre><code class="language-foo">
26
      # blocks as generated by commonmarker
27
      class SyntaxHighlightFilter < HTML::Pipeline::Filter
28
        def call
29
          doc.search("pre > code").each do |node|
30
            next unless lang = node["class"].presence
31
            next unless lang =~ /\Alanguage-(\w+)\z/
32

  
33
            lang = $1
34
            text = node.inner_text
35

  
36
            if Redmine::SyntaxHighlighting.language_supported?(lang)
37
              html = Redmine::SyntaxHighlighting.highlight_by_language(text, lang)
38
              next if html.nil?
39
              node.inner_html = html
40
              node["class"] = "#{lang} syntaxhl"
41
            else
42
              # unsupported language, strip out the code tag
43
              node.parent.inner_html = text
44
            end
45
          end
46
          doc
47
        end
48
      end
49

  
50
    end
51
  end
52
end
public/help/en/wiki_syntax_common_mark.html
1
<html xmlns="http://www.w3.org/1999/xhtml">
2
<head>
3
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
4
<title>Wiki formatting</title>
5
<link rel="stylesheet" type="text/css" href="../wiki_syntax.css" />
6
</head>
7
<body>
8

  
9
<h1>Wiki Syntax Quick Reference (CommonMark Markdown (GitHub Flavored))</h1>
10

  
11
<table style="width:100%">
12
<tr><th colspan="3">Font Styles <span class="more_info">(<a href="wiki_syntax_detailed_common_mark.html#5" target="_blank">more</a>)</span></th></tr>
13
<tr><th><img src="../../images/jstoolbar/bt_strong.png" style="border: 1px solid #bbb;" alt="Strong" /></th><td style="width:50%;">**Strong**</td><td style="width:50%;"><strong>Strong</strong></td></tr>
14
<tr><th><img src="../../images/jstoolbar/bt_em.png" style="border: 1px solid #bbb;" alt="Italic" /></th><td>*Italic*</td><td><em>Italic</em></td></tr>
15
<tr><th><img src="../../images/jstoolbar/bt_del.png" style="border: 1px solid #bbb;" alt="Deleted" /></th><td>~~Deleted~~</td><td><del>Deleted</del></td></tr>
16
<tr><th><img src="../../images/jstoolbar/bt_code.png" style="border: 1px solid #bbb;" alt="Inline Code" /></th><td>`Inline Code`</td><td><code>Inline Code</code></td></tr>
17
<tr><th><img src="../../images/jstoolbar/bt_pre.png" style="border: 1px solid #bbb;" alt="Preformatted text" /></th><td>```<br />&nbsp;lines<br />&nbsp;of code<br />```</td><td>
18
<pre>
19
 lines
20
 of code
21
</pre>
22
</td></tr>
23

  
24
<tr><th colspan="3">Highlighted code <span class="more_info">(<a href="wiki_syntax_detailed_common_mark.html#13" target="_blank">more</a>)</span></th></tr>
25
<tr><th><img src="../../images/jstoolbar/bt_precode.png" style="border: 1px solid #bbb;" alt="Highlighted code" /></th><td>```ruby<br />3.times do<br />&nbsp;&nbsp;puts 'Hello'<br />end<br />```</td><td>
26
<pre><code class="ruby syntaxhl"><span class="mi">3</span><span class="p">.</span><span class="nf">times</span> <span class="k">do</span>
27
  <span class="nb">puts</span> <span class="s1">'Hello'</span>
28
<span class="k">end</span>
29
</code></pre>
30
</td></tr>
31

  
32
<tr><th colspan="3">Line breaks and Paragraphs</th></tr>
33
<tr><th></th><td>An empty line<br><br>creates<br>a new paragraph.</td><td><p>An empty line</p><p>creates a new paragraph.</p></td></tr>
34
<tr><th></th><td>End a line with a backslash\<br> or two spaces to insert a manual line break.</td><td><p>End a line with a backslash<br>or two spaces to insert a manual line break.</p></td></tr>
35

  
36
<tr><th colspan="3">Lists</th></tr>
37
<tr><th><img src="../../images/jstoolbar/bt_ul.png" style="border: 1px solid #bbb;" alt="Unordered list" /></th><td>* Item 1<br />&nbsp;&nbsp;* Sub<br />* Item 2</td><td><ul><li>Item 1<ul><li>Sub</li></ul></li><li>Item 2</li></ul></td></tr>
38
<tr><th><img src="../../images/jstoolbar/bt_ol.png" style="border: 1px solid #bbb;" alt="Ordered list" /></th><td>1. Item 1<br />&nbsp;&nbsp;&nbsp;1. Sub<br />2. Item 2</td><td><ol><li>Item 1<ol><li>Sub</li></ol></li><li>Item 2</li></ol></td></tr>
39

  
40
<tr><th colspan="3">Headings <span class="more_info">(<a href="wiki_syntax_detailed_common_mark.html#8" target="_blank">more</a>)</span></th></tr>
41
<tr><th><img src="../../images/jstoolbar/bt_h1.png" style="border: 1px solid #bbb;" alt="Heading 1" /></th><td># Title 1</td><td><h1>Title 1</h1></td></tr>
42
<tr><th><img src="../../images/jstoolbar/bt_h2.png" style="border: 1px solid #bbb;" alt="Heading 2" /></th><td>## Title 2</td><td><h2>Title 2</h2></td></tr>
43
<tr><th><img src="../../images/jstoolbar/bt_h3.png" style="border: 1px solid #bbb;" alt="Heading 3" /></th><td>### Title 3</td><td><h3>Title 3</h3></td></tr>
44

  
45
<tr><th colspan="3">Links <span class="more_info">(<a href="wiki_syntax_detailed_common_mark.html#4" target="_blank">more</a>)</span></th></tr>
46
<tr><th></th><td>www.foo.bar</td><td><a href="#">www.foo.bar</a></td></tr>
47
<tr><th></th><td>http://foo.bar</td><td><a href="#">http://foo.bar</a></td></tr>
48
<tr><th></th><td>[Foo](http://foo.bar)</td><td><a href="#">Foo</a></td></tr>
49

  
50
<tr><th colspan="3">Redmine links <span class="more_info">(<a href="wiki_syntax_detailed_common_mark.html#3" target="_blank">more</a>)</span></th></tr>
51
<tr><th><img src="../../images/jstoolbar/bt_link.png" style="border: 1px solid #bbb;" alt="Link to a Wiki page" /></th><td>[[Wiki page]]</td><td><a href="#">Wiki page</a></td></tr>
52
<tr><th></th><td>Issue #12</td><td>Issue <a href="#">#12</a></td></tr>
53
<tr><th></th><td>##12</td><td><a href="#">Bug #12</a>: The issue subject</td></tr>
54
<tr><th></th><td>Revision r43</td><td>Revision <a href="#">r43</a></td></tr>
55
<tr><th></th><td>commit:f30e13e43</td><td><a href="#">f30e13e4</a></td></tr>
56
<tr><th></th><td>source:some/file</td><td><a href="#">source:some/file</a></td></tr>
57

  
58
<tr><th colspan="3">Inline images <span class="more_info">(<a href="wiki_syntax_detailed_common_mark.html#7" target="_blank">more</a>)</span></th></tr>
59
<tr><th><img src="../../images/jstoolbar/bt_img.png" style="border: 1px solid #bbb;" alt="Image" /></th><td>![](<em>image_url</em>)</td><td></td></tr>
60
<tr><th></th><td>![](<em>attached_image</em>)</td><td></td></tr>
61

  
62
<tr><th colspan="3">Tables</th></tr>
63
<tr>
64
  <th></th>
65
  <td>| A | B | C |<br />|---|---|---|<br />| A | B | C |<br />| D | E | F |</td>
66
  <td>
67
    <table class="sample">
68
      <tbody>
69
        <th>A</th><th>B</th><th>C</th>
70
        <tr><td>A</td><td>B</td><td>C</td></tr>
71
        <tr><td>D</td><td>E</td><td>F</td></tr>
72
      </tbody>
73
    </table>
74
  </td>
75
</tr>
76

  
77
<tr><th colspan="3">Raw HTML <span class="more_info">(<a href="wiki_syntax_detailed_common_mark.html#15" target="_blank">more</a>)</span></th></tr>
78
<tr>
79
  <th></th><td>HTML is &lt;del&gt;not&lt;/del&gt; &lt;u&gt;allowed&lt;/u&gt;.</td><td>HTML is <del>not</del> <u>allowed</u>.<td>
80
</tr>
81

  
82
</table>
83

  
84
<p><a href="wiki_syntax_detailed_common_mark.html" onclick="window.open('wiki_syntax_detailed_common_mark.html', '', ''); return false;">More Information</a></p>
85
</body>
86
</html>
87

  
public/help/en/wiki_syntax_detailed_common_mark.html
1
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
2
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
3
<head>
4
<title>RedmineWikiFormatting (CommonMark Markdown (GitHub Flavored))</title>
5
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
6
<link rel="stylesheet" type="text/css" href="../wiki_syntax_detailed.css" />
7
</head>
8

  
9
<body>
10
<h1><a name="1" class="wiki-page"></a>Wiki formatting (CommonMark Markdown (GitHub Flavored))</h1>
11

  
12
    <ul class='toc'>
13
        <li><a href='#2'>Links</a></li>
14
        <ul>
15
            <li><a href='#3'>Redmine links</a></li>
16
            <li><a href='#4'>External links</a></li>
17
        </ul>
18
        <li><a href='#5'>Text formatting</a></li>
19
        <ul>
20
            <li><a href='#6'>Font style</a></li>
21
            <li><a href='#7'>Inline images</a></li>
22
            <li><a href='#8'>Headings</a></li>
23
            <li><a href='#10'>Blockquotes</a></li>
24
            <li><a href='#11'>Table of content</a></li>
25
            <li><a href='#14'>Horizontal Rule</a></li>
26
        </ul>
27
        <li><a href='#12'>Macros</a></li>
28
        <li><a href='#13'>Code highlighting</a></li>
29
        <li><a href='#15'>Raw HTML</a></li>
30
    </ul>
31

  
32
    <h2><a name="2" class="wiki-page"></a>Links</h2>
33

  
34
        <h3><a name="3" class="wiki-page"></a>Redmine links</h3>
35

  
36
        <p>Redmine allows hyperlinking between resources (issues, changesets, wiki pages...) from anywhere wiki formatting is used.</p>
37
        <ul>
38
            <li>Link to an issue: <strong>#124</strong> (displays <del><a href="#" class="issue" title="bulk edit doesn't change the category or fixed version properties (Closed)">#124</a></del>, link is striked-through if the issue is closed)</li>
39
            <li>Link to an issue including tracker name and subject: <strong>##124</strong> (displays <a href="#" class="issue" title="bulk edit doesn't change the category or fixed version properties (New)">Bug #124</a>: bulk edit doesn't change the category or fixed version properties)</li>
40
            <li>Link to an issue note: <strong>#124-6</strong>, or <strong>#124#note-6</strong></li>
41
            <li>Link to an issue note within the same issue: <strong>#note-6</strong></li>
42
        </ul>
43

  
44
        <p>Wiki links:</p>
45

  
46
        <ul>
47
            <li><strong>[[Guide]]</strong> displays a link to the page named 'Guide': <a href="#" class="wiki-page">Guide</a></li>
48
            <li><strong>[[Guide#further-reading]]</strong> takes you to the anchor "further-reading". Headings get automatically assigned anchors so that you can refer to them: <a href="#" class="wiki-page">Guide</a></li>
49
            <li><strong>[[#further-reading]]</strong> link to the anchor "further-reading" of the current page: <a href="#" class="wiki-page">#further-reading</a></li>
50
            <li><strong>[[Guide|User manual]]</strong> displays a link to the same page but with a different text: <a href="#" class="wiki-page">User manual</a></li>
51
        </ul>
52

  
53
        <p>You can also link to pages of an other project wiki:</p>
54

  
55
        <ul>
56
            <li><strong>[[sandbox:some page]]</strong> displays a link to the page named 'Some page' of the Sandbox wiki</li>
57
            <li><strong>[[sandbox:]]</strong> displays a link to the Sandbox wiki main page</li>
58
        </ul>
59

  
60
        <p>Wiki links are displayed in red if the page doesn't exist yet, eg: <a href="#" class="wiki-page new">Nonexistent page</a>.</p>
61

  
62
        <p>Links to other resources:</p>
63

  
64
        <ul>
65
            <li>Documents:
66
                <ul>
67
                    <li><strong>document#17</strong> (link to document with id 17)</li>
68
                    <li><strong>document:Greetings</strong> (link to the document with title "Greetings")</li>
69
                    <li><strong>document:"Some document"</strong> (double quotes can be used when document title contains spaces)</li>
70
                    <li><strong>sandbox:document:"Some document"</strong> (link to a document with title "Some document" in other project "sandbox")</li>
71
                </ul>
72
            </li>
73
        </ul>
74

  
75
        <ul>
76
            <li>Versions:
77
                <ul>
78
                    <li><strong>version#3</strong> (link to version with id 3)</li>
79
                    <li><strong>version:1.0.0</strong> (link to version named "1.0.0")</li>
80
                    <li><strong>version:"1.0 beta 2"</strong></li>
81
                    <li><strong>sandbox:version:1.0.0</strong> (link to version "1.0.0" in the project "sandbox")</li>
82
                </ul>
83
            </li>
84
        </ul>
85

  
86
        <ul>
87
            <li>Attachments:
88
                <ul>
89
                    <li><strong>attachment:file.zip</strong> (link to the attachment of the current object named file.zip)</li>
90
                    <li>For now, attachments of the current object can be referenced only (if you're on an issue, it's possible to reference attachments of this issue only)</li>
91
                </ul>
92
            </li>
93
        </ul>
94

  
95
        <ul>
96
            <li>Changesets:
97
                <ul>
98
                    <li><strong>r758</strong>                       (link to a changeset)</li>
99
                    <li><strong>commit:c6f4d0fd</strong>            (link to a changeset with a non-numeric hash)</li>
100
                    <li><strong>svn1|r758</strong>                  (link to a changeset of a specific repository, for projects with multiple repositories)</li>
101
                    <li><strong>commit:hg|c6f4d0fd</strong>         (link to a changeset with a non-numeric hash of a specific repository)</li>
102
                    <li><strong>sandbox:r758</strong>               (link to a changeset of another project)</li>
103
                    <li><strong>sandbox:commit:c6f4d0fd</strong>    (link to a changeset with a non-numeric hash of another project)</li>
104
                </ul>
105
            </li>
106
        </ul>
107

  
108
        <ul>
109
             <li>Repository files:
110
                <ul>
111
                    <li><strong>source:some/file</strong>           (link to the file located at /some/file in the project's repository)</li>
112
                    <li><strong>source:some/file@52</strong>        (link to the file's revision 52)</li>
113
                    <li><strong>source:some/file#L120</strong>      (link to line 120 of the file)</li>
114
                    <li><strong>source:some/file@52#L120</strong>   (link to line 120 of the file's revision 52)</li>
115
                    <li><strong>source:"some file@52#L120"</strong> (use double quotes when the URL contains spaces</li>
116
                    <li><strong>export:some/file</strong>           (force the download of the file)</li>
117
                    <li><strong>source:svn1|some/file</strong>      (link to a file of a specific repository, for projects with multiple repositories)</li>
118
                    <li><strong>sandbox:source:some/file</strong>   (link to the file located at /some/file in the repository of the project "sandbox")</li>
119
                    <li><strong>sandbox:export:some/file</strong>   (force the download of the file)</li>
120
                </ul>
121
            </li>
122
        </ul>
123

  
124
        <ul>
125
            <li>Forums:
126
                <ul>
127
                    <li><strong>forum#1</strong> (link to forum with id 1</li>
128
                    <li><strong>forum:Support</strong> (link to forum named Support)</li>
129
                    <li><strong>forum:"Technical Support"</strong> (use double quotes if forum name contains spaces)</li>
130
                </ul>
131
            </li>
132
        </ul>
133

  
134
        <ul>
135
            <li>Forum messages:
136
                <ul>
137
                    <li><strong>message#1218</strong> (link to message with id 1218)</li>
138
                </ul>
139
            </li>
140
        </ul>
141

  
142
        <ul>
143
            <li>Projects:
144
                <ul>
145
                    <li><strong>project#3</strong> (link to project with id 3)</li>
146
                    <li><strong>project:some-project</strong> (link to project with name or slug of "some-project")</li>
147
                    <li><strong>project:"Some Project"</strong> (use double quotes for project name containing spaces)</li>
148
                </ul>
149
             </li>
150
        </ul>
151

  
152
        <ul>
153
            <li>News:
154
                <ul>
155
                    <li><strong>news#2</strong> (link to news item with id 2)</li>
156
                    <li><strong>news:Greetings</strong> (link to news item named "Greetings")</li>
157
                    <li><strong>news:"First Release"</strong> (use double quotes if news item name contains spaces)</li>
158
                </ul>
159
            </li>
160
        </ul>
161

  
162
        <ul>
163
            <li>Users:
164
                <ul>
165
                    <li><strong>user#2</strong> (link to user with id 2)</li>
166
                    <li><strong>user:jsmith</strong> (Link to user with login jsmith)</li>
167
                    <li><strong>@jsmith</strong> (Link to user with login jsmith)</li>
168
                </ul>
169
            </li>
170
        </ul>
171

  
172
        <p>Escaping:</p>
173

  
174
        <ul>
175
            <li>You can prevent Redmine links from being parsed by preceding them with an exclamation mark: !</li>
176
        </ul>
177

  
178

  
179
        <h3><a name="4" class="wiki-page"></a>External links</h3>
180

  
181
        <p>URLs (starting with: www, http, https, ftp, ftps, sftp and sftps) and email addresses are automatically turned into clickable links:</p>
182

  
183
<pre>
184
http://www.redmine.org, someone@foo.bar
185
</pre>
186

  
187
        <p>displays: <a class="external" href="http://www.redmine.org">http://www.redmine.org</a>, <a href="mailto:someone@foo.bar" class="email">someone@foo.bar</a></p>
188

  
189
        <p>If you want to display a specific text instead of the URL, you can use the standard markdown syntax:</p>
190

  
191
<pre>
192
[Redmine web site](http://www.redmine.org)
193
</pre>
194

  
195
        <p>displays: <a href="http://www.redmine.org" class="external">Redmine web site</a></p>
196

  
197

  
198
    <h2><a name="5" class="wiki-page"></a>Text formatting</h2>
199

  
200
<p>For things such as headlines, bold, tables, lists, Redmine supports Markdown syntax according to <a class="external" href="https://commonmark.org/">CommonMark</a> including some extensions commonly referred to as <em>GitHub flavored Markdown</em>. See the <a class="external" href="https://github.github.com/gfm">GitHub Flavored Markdown Spec</a> for information on using any of these features.  A few samples are included below, but the engine is capable of much more of that.</p>
201

  
202
        <h3><a name="6" class="wiki-page"></a>Font style</h3>
203

  
204
<pre>
205
* **bold**
206
* *Italic*
207
* ***bold italic***
208
* ~~strike-through~~
209
</pre>
210

  
211
        <p>Display:</p>
212

  
213
        <ul>
214
            <li><strong>bold</strong></li>
215
            <li><em>italic</em></li>
216
            <li><em><strong>bold italic</strong></em></li>
217
            <li><del>strike-through</del></li>
218
        </ul>
219

  
220
        <h3><a name="7" class="wiki-page"></a>Inline images</h3>
221

  
222
        <ul>
223
            <li><strong>![](image_url)</strong> displays an image located at image_url (markdown syntax)</li>
224
            <li>If you have an image attached to your wiki page, it can be displayed inline using its filename: <strong>![](attached_image)</strong></li>
225
            <li>Images in your computer's clipboard can be pasted directly using Ctrl-v or Command-v.</li>
226
            <li>Image files can be dragged onto the text area in order to be uploaded and embedded.</li>
227
        </ul>
228

  
229
        <h3><a name="8" class="wiki-page"></a>Headings</h3>
230

  
231
<pre>
232
# Heading
233
## Subheading
234
### Subsubheading
235
</pre>
236

  
237
        <p>Redmine assigns an anchor to each of those headings thus you can link to them with "#Heading", "#Subheading" and so forth.</p>
238

  
239

  
240
        <h3><a name="10" class="wiki-page"></a>Blockquotes</h3>
241

  
242
        <p>Start the paragraph with <strong>&gt;</strong></p>
243

  
244
<pre>
245
&gt; Rails is a full-stack framework for developing database-backed web applications according to the Model-View-Control pattern.
246
To go live, all you need to add is a database and a web server.
247
</pre>
248

  
249
        <p>Display:</p>
250

  
251
        <blockquote>
252
                <p>Rails is a full-stack framework for developing database-backed web applications according to the Model-View-Control pattern.<br />To go live, all you need to add is a database and a web server.</p>
253
        </blockquote>
254

  
255

  
256
        <h3><a name="11" class="wiki-page"></a>Table of content</h3>
257

  
258
<pre>
259
{{toc}} =&gt; left aligned toc
260
{{&gt;toc}} =&gt; right aligned toc
261
</pre>
262

  
263
        <h3><a name="14" class="wiki-page"></a>Horizontal Rule</h3>
264

  
265
<pre>
266
---
267
</pre>
268

  
269
    <h2><a name="12" class="wiki-page"></a>Macros</h2>
270

  
271
    <p>Redmine has the following builtin macros:</p>
272

  
273
    <p>
274
    <dl>
275
      <dt><code>hello_world</code></dt>
276
      <dd><p>Sample macro.</p></dd>
277

  
278
      <dt><code>macro_list</code></dt>
279
      <dd><p>Displays a list of all available macros, including description if available.</p></dd>
280

  
281
      <dt><code>child_pages</code></dt>
282
      <dd><p>Displays a list of child pages. With no argument, it displays the child pages of the current wiki page. Examples:</p>
283
      <pre><code>{{child_pages}} -- can be used from a wiki page only
284
{{child_pages(depth=2)}} -- display 2 levels nesting only</code></pre></dd>
285

  
286
      <dt><code>include</code></dt>
287
      <dd><p>Include a wiki page. Example:</p>
288
      <pre><code>{{include(Foo)}}</code></pre>
289
      <p>or to include a page of a specific project wiki:</p>
290
      <pre><code>{{include(projectname:Foo)}}</code></pre></dd>
291

  
292
      <dt><code>collapse</code></dt>
293
      <dd><p>Inserts of collapsed block of text. Example:</p>
294
      <pre><code>{{collapse(View details...)
295
This is a block of text that is collapsed by default.
296
It can be expanded by clicking a link.
297
}}</code></pre></dd>
298

  
299
      <dt><code>thumbnail</code></dt>
300
      <dd><p>Displays a clickable thumbnail of an attached image. Examples:</p>
301
      <pre>{{thumbnail(image.png)}}
302
{{thumbnail(image.png, size=300, title=Thumbnail)}}</pre></dd>
303

  
304
      <dt><code>issue</code></dt>
305
      <dd><p>Inserts a link to an issue with flexible text. Examples:</p>
306
      <pre>{{issue(123)}}                              -- Issue #123: Enhance macro capabilities
307
{{issue(123, project=true)}}                -- Andromeda - Issue #123:Enhance macro capabilities
308
{{issue(123, tracker=false)}}               -- #123: Enhance macro capabilities
309
{{issue(123, subject=false, project=true)}} -- Andromeda - Issue #123</pre></dd>
310
    </dl>
311
    </p>
312

  
313
    <h2><a name="13" class="wiki-page"></a>Code highlighting</h2>
314

  
315
    <p>Default code highlighting relies on <a href="http://rouge.jneen.net/" class="external">Rouge</a>, a syntax highlighting library written in pure Ruby. It supports many commonly used languages such as <strong>c</strong>, <strong>cpp</strong> (c++), <strong>csharp</strong> (c#, cs), <strong>css</strong>, <strong>diff</strong> (patch, udiff), <strong>go</strong> (golang), <strong>groovy</strong>, <strong>html</strong>, <strong>java</strong>, <strong>javascript</strong> (js), <strong>kotlin</strong>, <strong>objective_c</strong> (objc), <strong>perl</strong> (pl), <strong>php</strong>, <strong>python</strong> (py), <strong>r</strong>, <strong>ruby</strong> (rb), <strong>sass</strong>, <strong>scala</strong>, <strong>shell</strong> (bash, zsh, ksh, sh), <strong>sql</strong>, <strong>swift</strong>, <strong>xml</strong> and <strong>yaml</strong> (yml) languages, where the names inside parentheses are aliases. Please refer to <a href="https://www.redmine.org/projects/redmine/wiki/RedmineCodeHighlightingLanguages" class="external">https://www.redmine.org/projects/redmine/wiki/RedmineCodeHighlightingLanguages</a> for the full list of supported languages.</p>
316

  
317
    <p>You can highlight code at any place that supports wiki formatting using this syntax (note that the language name or alias is case-insensitive):</p>
318

  
319
<pre>
320
```ruby
321
  Place your code here.
322
```
323
</pre>
324

  
325
    <p>Example:</p>
326

  
327
<pre><code class="ruby syntaxhl"><span class="c1"># The Greeter class</span>
328
<span class="k">class</span> <span class="nc">Greeter</span>
329
  <span class="k">def</span> <span class="nf">initialize</span><span class="p">(</span><span class="nb">name</span><span class="p">)</span>
330
    <span class="vi">@name</span> <span class="o">=</span> <span class="nb">name</span><span class="p">.</span><span class="nf">capitalize</span>
331
  <span class="k">end</span>
332

  
333
  <span class="k">def</span> <span class="nf">salute</span>
334
    <span class="nb">puts</span> <span class="s2">"Hello </span><span class="si">#{</span><span class="vi">@name</span><span class="si">}</span><span class="s2">!"</span>
335
  <span class="k">end</span>
336
<span class="k">end</span>
337
</code></pre>
338

  
339
    <h2><a name="15" class="wiki-page"></a>Raw HTML</h2>
340

  
341
    <p>You may use raw HTML for more complex formatting tasks, i.e. complex tables with cells spanning multiple rows or columns:</p>
342

  
343
  <pre><code>
344
    &lt;table width="50%"&gt;
345
      &lt;tr&gt;&lt;td rowspan="2"&gt;Two rows&lt;/td&gt;&lt;td&gt;foo&lt;/td&gt;&lt;/tr&gt;
346
      &lt;tr&gt;&lt;td&gt;bar&lt;/td&gt;&lt;/tr&gt;
347
      &lt;tr&gt;&lt;td align="center" colspan="2"&gt;bar&lt;/td&gt;&lt;/tr&gt;
348
    &lt;/table&gt;
349
  </code></pre>
350

  
351
  <p>yields</p>
352

  
353
<table width="50%" class="sample">
354
<tr><td rowspan="2">Two rows</td><td>foo</td></tr>
355
<tr><td>bar</td></tr>
356
<tr><td align="center" colspan="2">bar</td></tr>
357
</table>
358

  
359
</body>
360
</html>
361

  
public/help/wiki_syntax_detailed.css
15 15
a:hover, a:active{ color: #c61a1a; text-decoration: underline;}
16 16
a.new { color: #b73535; }
17 17

  
18
table.sample { border-collapse: collapse; border-spacing: 0; margin: 4px; margin-left: 30px;}
19
table.sample th, table.sample td { border: solid 1px #bbb; padding: 4px; height: 1em; }
20

  
18 21
.syntaxhl .c1 { color: #888888 }
19 22
.syntaxhl .k { color: #008800; font-weight: bold }
20 23
.syntaxhl .nc { color: #BB0066; font-weight: bold }
test/unit/lib/redmine/wiki_formatting/common_mark/application_helper_test.rb
1
# frozen_string_literal: true
2

  
3
require File.expand_path('../../../../../../test_helper', __FILE__)
4

  
5
class Redmine::WikiFormatting::CommonMark::ApplicationHelperTest < Redmine::HelperTest
6
  if Object.const_defined?(:CommonMarker)
7

  
8
  include ERB::Util
9
  include Rails.application.routes.url_helpers
10

  
11
  fixtures :projects, :enabled_modules,
12
           :users, :email_addresses,
13
           :members, :member_roles, :roles,
14
           :repositories, :changesets,
15
           :projects_trackers,
16
           :trackers, :issue_statuses, :issues, :versions, :documents, :journals,
17
           :wikis, :wiki_pages, :wiki_contents,
18
           :boards, :messages, :news,
19
           :attachments, :enumerations,
20
           :custom_values, :custom_fields, :custom_fields_projects
21

  
22
  def setup
23
    super
24
    set_tmp_attachments_directory
25
  end
26

  
27
  def test_attached_images_with_markdown_and_non_ascii_filename
28
    to_test = {
29
      'CAFÉ.JPG' => 'CAF%C3%89.JPG',
30
      'crème.jpg' => 'cr%C3%A8me.jpg',
31
    }
32
    with_settings :text_formatting => 'common_mark' do
33
      to_test.each do |filename, result|
34
        attachment = Attachment.generate!(:filename => filename)
35
        assert_include %(<img src="/attachments/download/#{attachment.id}/#{result}" alt="">), textilizable("![](#{filename})", :attachments => [attachment])
36
      end
37
    end
38
  end
39

  
40
  def test_toc_with_markdown_formatting_should_be_parsed
41
    with_settings :text_formatting => 'common_mark' do
42
      assert_select_in textilizable("{{toc}}\n\n# Heading"), 'ul.toc li', :text => 'Heading'
43
      assert_select_in textilizable("{{<toc}}\n\n# Heading"), 'ul.toc.left li', :text => 'Heading'
44
      assert_select_in textilizable("{{>toc}}\n\n# Heading"), 'ul.toc.right li', :text => 'Heading'
45
    end
46
  end
47

  
48
end
49
end
test/unit/lib/redmine/wiki_formatting/common_mark/external_links_filter_test.rb
1
# frozen_string_literal: true
2
#
3
require File.expand_path('../../../../../../test_helper', __FILE__)
4
if Object.const_defined?(:CommonMarker)
5
require 'redmine/wiki_formatting/common_mark/external_links_filter'
6

  
7
class Redmine::WikiFormatting::CommonMark::ExternalLinksFilterTest < ActiveSupport::TestCase
8

  
9
  def filter(html)
10
    Redmine::WikiFormatting::CommonMark::ExternalLinksFilter.to_html(html, @options)
11
  end
12

  
13
  def setup
14
    @options = { }
15
  end
16

  
17
  def test_external_links_should_have_external_css_class
18
    assert_equal %(<a href="http://example.net/" class="external">link</a>), filter(%(<a href="http://example.net/">link</a>))
19
  end
20

  
21
  def test_locals_links_should_not_have_external_css_class
22
    assert_equal %(<a href="/">home</a>), filter(%(<a href="/">home</a>))
23
    assert_equal %(<a href="relative">relative</a>), filter(%(<a href="relative">relative</a>))
24
    assert_equal %(<a href="#anchor">anchor</a>), filter(%(<a href="#anchor">anchor</a>))
25
  end
26

  
27
  def test_mailto_links_should_have_email_class
28
    assert_equal %(<a href="mailto:user@example.org" class="email">user</a>), filter(%(<a href="mailto:user@example.org">user</a>))
29
  end
30

  
31

  
32
end
33
end # if Object.const_defined?(:CommonMarker)
34

  
35

  
test/unit/lib/redmine/wiki_formatting/common_mark/fixup_auto_links_filter_test.rb
1
# frozen_string_literal: true
2
#
3
require File.expand_path('../../../../../../test_helper', __FILE__)
4

  
5
if Object.const_defined?(:CommonMarker)
6
require 'redmine/wiki_formatting/common_mark/fixup_auto_links_filter'
7

  
8
class Redmine::WikiFormatting::CommonMark::FixupAutoLinksFilterTest < ActiveSupport::TestCase
9

  
10
  def filter(html)
11
    Redmine::WikiFormatting::CommonMark::FixupAutoLinksFilter.to_html(html, @options)
12
  end
13

  
14
  def format(markdown)
15
    Redmine::WikiFormatting::CommonMark::MarkdownFilter.to_html(markdown, Redmine::WikiFormatting::CommonMark::PIPELINE_CONFIG)
16
  end
17

  
18
  def setup
19
    @options = { }
20
  end
21

  
22
  def test_should_fixup_autolinked_user_references
23
    text = "user:user@example.org"
24
    assert_equal "<p>#{text}</p>", filter(format(text))
25
    text = "@user@example.org"
26
    assert_equal "<p>#{text}</p>", filter(format(text))
27
  end
28

  
29
  def test_should_fixup_autolinked_hires_files
30
    text = "printscreen@2x.png"
31
    assert_equal "<p>#{text}</p>", filter(format(text))
32
  end
33

  
34
end
35
end # if Object.const_defined?(:CommonMarker)
36

  
37

  
38

  
test/unit/lib/redmine/wiki_formatting/common_mark/formatter_test.rb
1
# frozen_string_literal: true
2

  
3
# Redmine - project management software
4
# Copyright (C) 2006-2019  Jean-Philippe Lang
5
#
6
# This program is free software; you can redistribute it and/or
7
# modify it under the terms of the GNU General Public License
8
# as published by the Free Software Foundation; either version 2
9
# of the License, or (at your option) any later version.
10
#
11
# This program is distributed in the hope that it will be useful,
12
# but WITHOUT ANY WARRANTY; without even the implied warranty of
13
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
# GNU General Public License for more details.
15
#
16
# You should have received a copy of the GNU General Public License
17
# along with this program; if not, write to the Free Software
18
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
19

  
20
require File.expand_path('../../../../../../test_helper', __FILE__)
21

  
22
class Redmine::WikiFormatting::CommonMark::FormatterTest < ActionView::TestCase
23
  if Object.const_defined?(:CommonMarker)
24

  
25
  def setup
26
    @formatter = Redmine::WikiFormatting::CommonMark::Formatter
27
  end
28

  
29
  def format(text)
30
    @formatter.new(text).to_html
31
  end
32

  
33
  def test_should_render_hard_breaks
34
    html ="<p>foo<br>\nbar</p>"
35
    assert_equal html, format("foo\\\nbar")
36
    assert_equal html, format("foo  \nbar")
37
  end
38

  
39
  def test_should_ignore_soft_breaks
40
    assert_equal "<p>foo\nbar</p>", format("foo\nbar")
41
  end
42

  
43
  def test_syntax_error_in_image_reference_should_not_raise_exception
44
    assert format("!>[](foo.png)")
45
  end
46

  
47
  def test_empty_image_should_not_raise_exception
48
    assert format("![]()")
49
  end
50

  
51
  def test_inline_style
52
    assert_equal "<p><strong>foo</strong></p>", format("**foo**")
53
  end
54

  
55
  def test_not_set_intra_emphasis
56
    assert_equal "<p>foo_bar_baz</p>", format("foo_bar_baz")
57
  end
58

  
59
  def test_wiki_links_should_be_preserved
60
    text = 'This is a wiki link: [[Foo]]'
61
    assert_include '[[Foo]]', format(text)
62
  end
63

  
64
  def test_redmine_links_with_double_quotes_should_be_preserved
65
    text = 'This is a redmine link: version:"1.0"'
66
    assert_include 'version:"1.0"', format(text)
67
  end
68

  
69
  def test_links_by_id_should_be_preserved
70
    text = "[project#3]"
71
    assert_equal "<p>#{text}</p>", format(text)
72
  end
73

  
74
  def test_links_to_users_should_be_preserved
75
    text = "[@login]"
76
    assert_equal "<p>#{text}</p>", format(text)
77
    text = "[user:login]"
78
    assert_equal "<p>#{text}</p>", format(text)
79
    text = "user:user@example.org"
80
    assert_equal "<p>#{text}</p>", format(text)
81
    text = "[user:user@example.org]"
82
    assert_equal "<p>#{text}</p>", format(text)
83
    text = "@user@example.org"
84
    assert_equal "<p>#{text}</p>", format(text)
85
    text = "[@user@example.org]"
86
    assert_equal "<p>#{text}</p>", format(text)
87
  end
88

  
89
  def test_files_with_at_should_not_end_up_as_mailto_links
90
    text = "printscreen@2x.png"
91
    assert_equal "<p>#{text}</p>", format(text)
92
    text = "[printscreen@2x.png]"
93
    assert_equal "<p>#{text}</p>", format(text)
94
  end
95

  
96
  def test_should_support_syntax_highlight
97
    text = <<-STR
98
~~~ruby
99
def foo
100
end
101
~~~
102
STR
103
    assert_select_in format(text), 'pre code.ruby.syntaxhl' do
104
      assert_select 'span.k', :text => 'def'
105
    end
106
  end
107

  
108
  def test_should_not_allow_invalid_language_for_code_blocks
109
    text = <<-STR
110
~~~foo
111
test
112
~~~
113
STR
114
    assert_equal "<pre>test\n</pre>", format(text)
115
  end
116

  
117
  def test_external_links_should_have_external_css_class
118
    text = 'This is a [link](http://example.net/)'
119
    assert_equal '<p>This is a <a href="http://example.net/" class="external">link</a></p>', format(text)
120
  end
121

  
122
  def test_locals_links_should_not_have_external_css_class
123
    text = 'This is a [link](/issues)'
124
    assert_equal '<p>This is a <a href="/issues">link</a></p>', format(text)
125
  end
126

  
127
  def test_markdown_should_not_require_surrounded_empty_line
128
    text = <<-STR
129
This is a list:
130
* One
131
* Two
132
STR
133
    assert_equal "<p>This is a list:</p>\n<ul>\n<li>One</li>\n<li>Two</li>\n</ul>", format(text)
134
  end
135

  
136
  def test_footnotes
137
    text = <<-STR
138
This is some text[^1].
139

  
140
[^1]: This is the foot note
141
STR
142

  
143
    expected = <<-EXPECTED
144
<p>This is some text<sup><a href="#fn1" id="fnref1">1</a></sup>.</p>
145
 <ol>
146
<li id="fn1">
147
<p>This is the foot note <a href="#fnref1">↩</a></p>
148
</li>
149
</ol> 
150
EXPECTED
151

  
152
    assert_equal expected.gsub(%r{[\r\n\t]}, ''), format(text).gsub(%r{[\r\n\t]}, '')
153
  end
154

  
155
  STR_WITH_PRE = [
156
    # 0
157
    <<~STR.chomp,
158
      # Title
159

  
160
      Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Maecenas sed libero.
161
    STR
162
    # 1
163
    <<~STR.chomp,
164
      ## Heading 2
165

  
166
      ~~~ruby
167
        def foo
168
        end
169
      ~~~
170

  
171
      Morbi facilisis accumsan orci non pharetra.
172

  
173
      ~~~ ruby
174
      def foo
175
      end
176
      ~~~
177

  
178
      ```
179
      Pre Content:
180

  
181
      ## Inside pre
182

  
183
      <tag> inside pre block
184

  
185
      Morbi facilisis accumsan orci non pharetra.
186
      ```
187
    STR
188
    # 2
189
    <<~STR.chomp,
190
      ### Heading 3
191

  
192
      Nulla nunc nisi, egestas in ornare vel, posuere ac libero.
193
    STR
194
  ]
195

  
196
  def test_get_section_should_ignore_pre_content
197
    text = STR_WITH_PRE.join("\n\n")
198

  
199
    assert_section_with_hash STR_WITH_PRE[1..2].join("\n\n"), text, 2
200
    assert_section_with_hash STR_WITH_PRE[2], text, 3
201
  end
202

  
203
  def test_update_section_should_not_escape_pre_content_outside_section
204
    text = STR_WITH_PRE.join("\n\n")
205
    replacement = "New text"
206

  
207
    assert_equal [STR_WITH_PRE[0..1], "New text"].flatten.join("\n\n"),
208
      @formatter.new(text).update_section(3, replacement)
209
  end
210

  
211
  def test_should_emphasize_text
212
    text = 'This _text_ should be emphasized'
213
    assert_equal '<p>This <em>text</em> should be emphasized</p>', format(text)
214
  end
215

  
216
  def test_should_strike_through_text
217
    text = 'This ~~text~~ should be striked through'
218
    assert_equal '<p>This <del>text</del> should be striked through</p>', format(text)
219
  end
220

  
221
  def test_should_autolink_urls_and_emails
222
    [
223
      [ "http://example.org", '<p><a href="http://example.org" class="external">http://example.org</a></p>' ],
224
      [ "http://www.redmine.org/projects/redmine/issues?utf8=✓", '<p><a href="http://www.redmine.org/projects/redmine/issues?utf8=%E2%9C%93" class="external">http://www.redmine.org/projects/redmine/issues?utf8=✓</a></p>' ],
225
      [ '[Letters](https://yandex.ru/search/?text=кол-во)', '<p><a href="https://yandex.ru/search/?text=%D0%BA%D0%BE%D0%BB-%D0%B2%D0%BE" class="external">Letters</a></p>' ],
226
      [ "www.example.org", '<p><a href="http://www.example.org" class="external">www.example.org</a></p>' ],
227
      [ "user@example.org", '<p><a href="mailto:user@example.org" class="email">user@example.org</a></p>']
228
    ].each do |text, html|
229
      assert_equal html, format(text)
230
    end
231
  end
232

  
233
  def test_should_support_html_tables
234
    text = '<table style="background: red"><tr><td>Cell</td></tr></table>'
235
    assert_equal '<table><tr><td>Cell</td></tr></table>', format(text)
236
  end
237

  
238
  def test_should_remove_unsafe_uris
239
    [
240
      ['<img src="data:foobar">', '<img>'],
241
      ['<a href="javascript:bla">click me</a>', '<p><a>click me</a></p>'],
242
    ].each do |text, html|
243
      assert_equal html, format(text)
244
    end
245
  end
246

  
247
  def test_should_escape_unwanted_tags
248
    [
249
      [
250
        %[<p>sit<br>amet &lt;style&gt;.foo { color: #fff; }&lt;/style&gt; &lt;script&gt;alert("hello world");&lt;/script&gt;</p>],
251
         %[sit<br/>amet <style>.foo { color: #fff; }</style> <script>alert("hello world");</script>]
252
      ]
253
    ].each do |expected, input|
254
      assert_equal expected, format(input)
255
    end
256
  end
257

  
258
  private
259

  
260
  def assert_section_with_hash(expected, text, index)
261
    result = @formatter.new(text).get_section(index)
262

  
263
    assert_kind_of Array, result
264
    assert_equal 2, result.size
265
    assert_equal expected, result.first, "section content did not match"
266
    assert_equal Digest::MD5.hexdigest(expected), result.last, "section hash did not match"
267
  end
268
  end
269
end
270

  
test/unit/lib/redmine/wiki_formatting/common_mark/markdown_filter_test.rb
1
# frozen_string_literal: true
2
#
3
require File.expand_path('../../../../../../test_helper', __FILE__)
4
if Object.const_defined?(:CommonMarker)
5
require 'redmine/wiki_formatting/common_mark/markdown_filter'
6

  
7
class Redmine::WikiFormatting::CommonMark::MarkdownFilterTest < ActiveSupport::TestCase
8

  
9
  def filter(markdown)
10
    Redmine::WikiFormatting::CommonMark::MarkdownFilter.to_html(markdown)
11
  end
12

  
13
  # just a basic sanity test. more formatting tests in the formatter_test
14
  def test_should_render_markdown
15
    assert_equal "<p><strong>bold</strong></p>", filter("**bold**")
... This diff was truncated because it exceeds the maximum size that can be displayed.
(1-1/26)