From 781c6a13fd0401d63784c20c046af5b25e2fbf4b Mon Sep 17 00:00:00 2001 From: Marius BALTEANU Date: Sat, 3 Jul 2021 17:49:14 +0300 Subject: [PATCH 01/12] Adds CommonMark Markdown (GitHub Flavored) as third text formatting option (#32424). MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Patch by Jens Krämer. --- Gemfile | 7 + lib/redmine.rb | 8 + .../common_mark/external_links_filter.rb | 48 +++ .../common_mark/fixup_auto_links_filter.rb | 51 +++ .../wiki_formatting/common_mark/formatter.rb | 65 ++++ .../wiki_formatting/common_mark/helper.rb | 57 +++ .../common_mark/html_parser.rb | 26 ++ .../common_mark/markdown_filter.rb | 57 +++ .../common_mark/sanitization_filter.rb | 86 +++++ .../common_mark/syntax_highlight_filter.rb | 50 +++ public/help/en/wiki_syntax_common_mark.html | 87 +++++ .../en/wiki_syntax_detailed_common_mark.html | 361 ++++++++++++++++++ public/help/wiki_syntax_detailed.css | 3 + .../common_mark/application_helper_test.rb | 66 ++++ .../common_mark/external_links_filter_test.rb | 48 +++ .../fixup_auto_links_filter_test.rb | 50 +++ .../common_mark/formatter_test.rb | 270 +++++++++++++ .../common_mark/markdown_filter_test.rb | 35 ++ .../common_mark/sanitization_filter_test.rb | 211 ++++++++++ .../syntax_highlight_filter_test.rb | 75 ++++ 20 files changed, 1661 insertions(+) create mode 100644 lib/redmine/wiki_formatting/common_mark/external_links_filter.rb create mode 100644 lib/redmine/wiki_formatting/common_mark/fixup_auto_links_filter.rb create mode 100644 lib/redmine/wiki_formatting/common_mark/formatter.rb create mode 100644 lib/redmine/wiki_formatting/common_mark/helper.rb create mode 100644 lib/redmine/wiki_formatting/common_mark/html_parser.rb create mode 100644 lib/redmine/wiki_formatting/common_mark/markdown_filter.rb create mode 100644 lib/redmine/wiki_formatting/common_mark/sanitization_filter.rb create mode 100644 lib/redmine/wiki_formatting/common_mark/syntax_highlight_filter.rb create mode 100644 public/help/en/wiki_syntax_common_mark.html create mode 100644 public/help/en/wiki_syntax_detailed_common_mark.html create mode 100644 test/unit/lib/redmine/wiki_formatting/common_mark/application_helper_test.rb create mode 100644 test/unit/lib/redmine/wiki_formatting/common_mark/external_links_filter_test.rb create mode 100644 test/unit/lib/redmine/wiki_formatting/common_mark/fixup_auto_links_filter_test.rb create mode 100644 test/unit/lib/redmine/wiki_formatting/common_mark/formatter_test.rb create mode 100644 test/unit/lib/redmine/wiki_formatting/common_mark/markdown_filter_test.rb create mode 100644 test/unit/lib/redmine/wiki_formatting/common_mark/sanitization_filter_test.rb create mode 100644 test/unit/lib/redmine/wiki_formatting/common_mark/syntax_highlight_filter_test.rb diff --git a/Gemfile b/Gemfile index 984cc078a..56f881098 100644 --- a/Gemfile +++ b/Gemfile @@ -46,6 +46,13 @@ group :markdown do gem 'redcarpet', '~> 3.5.1' end +# Optional CommonMark support, not for JRuby +group :common_mark do + gem "html-pipeline", "~> 2.12" + gem "commonmarker", "~> 0.20" + gem "sanitize", "~> 5.1" +end + # Include database gems for the adapters found in the database # configuration file require 'erb' diff --git a/lib/redmine.rb b/lib/redmine.rb index caf23abbc..4ba16fdb3 100644 --- a/lib/redmine.rb +++ b/lib/redmine.rb @@ -29,6 +29,11 @@ begin rescue LoadError # Redcarpet is not available end +begin + require 'commonmarker' unless Object.const_defined?(:CommonMarker) +rescue LoadError + # CommonMarker is not available +end require 'redmine/acts/positioned' @@ -442,6 +447,9 @@ end Redmine::WikiFormatting.map do |format| format.register :textile format.register :markdown if Object.const_defined?(:Redcarpet) + if Object.const_defined?(:CommonMarker) + format.register :common_mark, label: 'CommonMark Markdown (GitHub Flavored)' + end end ActionView::Template.register_template_handler :rsb, Redmine::Views::ApiTemplateHandler diff --git a/lib/redmine/wiki_formatting/common_mark/external_links_filter.rb b/lib/redmine/wiki_formatting/common_mark/external_links_filter.rb new file mode 100644 index 000000000..023593b5a --- /dev/null +++ b/lib/redmine/wiki_formatting/common_mark/external_links_filter.rb @@ -0,0 +1,48 @@ +# frozen_string_literal: true + +# Redmine - project management software +# Copyright (C) 2006-2021 Jean-Philippe Lang +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +require 'uri' + +module Redmine + module WikiFormatting + module CommonMark + # adds class="external" to external links, and class="email" to mailto + # links + class ExternalLinksFilter < HTML::Pipeline::Filter + def call + doc.search("a").each do |node| + url = node["href"] + next unless url + next if url.starts_with?("/") || url.starts_with?("#") || !url.include?(':') + + scheme = URI.parse(url).scheme + next if scheme.blank? + + klass = node["class"].presence + node["class"] = [ + klass, + (scheme == "mailto" ? "email" : "external") + ].compact.join " " + end + doc + end + end + end + end +end diff --git a/lib/redmine/wiki_formatting/common_mark/fixup_auto_links_filter.rb b/lib/redmine/wiki_formatting/common_mark/fixup_auto_links_filter.rb new file mode 100644 index 000000000..94120bccf --- /dev/null +++ b/lib/redmine/wiki_formatting/common_mark/fixup_auto_links_filter.rb @@ -0,0 +1,51 @@ +# frozen_string_literal: true + +# Redmine - project management software +# Copyright (C) 2006-2021 Jean-Philippe Lang +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +module Redmine + module WikiFormatting + module CommonMark + # fixes: + # - autolinked email addresses that are actually references to users: + # user:user@example.org + # @user@example.org + # - autolinked hi res image names that look like email addresses: + # printscreen@2x.png + class FixupAutoLinksFilter < HTML::Pipeline::Filter + USER_LINK_PREFIX = /(@|user:)\z/.freeze + HIRES_IMAGE = /.+@\dx\.(bmp|gif|jpg|jpe|jpeg|png)\z/.freeze + + def call + doc.search("a").each do |node| + unless (url = node['href']) && url.starts_with?('mailto:') + next + end + + if ((p = node.previous) && p.text? && + p.text =~(USER_LINK_PREFIX)) || + (node.text =~ HIRES_IMAGE) + + node.replace node.text + end + end + doc + end + end + end + end +end diff --git a/lib/redmine/wiki_formatting/common_mark/formatter.rb b/lib/redmine/wiki_formatting/common_mark/formatter.rb new file mode 100644 index 000000000..d0db8c9b8 --- /dev/null +++ b/lib/redmine/wiki_formatting/common_mark/formatter.rb @@ -0,0 +1,65 @@ +# frozen_string_literal: true + +# Redmine - project management software +# Copyright (C) 2006-2021 Jean-Philippe Lang +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +require 'html/pipeline' + +module Redmine + module WikiFormatting + module CommonMark + # configuration of the rendering pipeline + PIPELINE_CONFIG = { + # https://github.com/gjtorikian/commonmarker#extensions + commonmarker_extensions: [ + :table, + :strikethrough, + :tagfilter, + :autolink + ].freeze, + + # https://github.com/gjtorikian/commonmarker#parse-options + commonmarker_parse_options: [ + :FOOTNOTES, + :STRIKETHROUGH_DOUBLE_TILDE, + :UNSAFE, + :VALIDATE_UTF8 + ].freeze, + + # https://github.com/gjtorikian/commonmarker#render-options + commonmarker_render_options: [ + :UNSAFE + ].freeze, + }.freeze + + MarkdownPipeline = HTML::Pipeline.new [ + MarkdownFilter, + SanitizationFilter, + SyntaxHighlightFilter, + FixupAutoLinksFilter, + ExternalLinksFilter, + ], PIPELINE_CONFIG + + class Formatter < Redmine::WikiFormatting::Markdown::Formatter + def to_html(*args) + result = MarkdownPipeline.call @text + result[:output].to_s + end + end + end + end +end diff --git a/lib/redmine/wiki_formatting/common_mark/helper.rb b/lib/redmine/wiki_formatting/common_mark/helper.rb new file mode 100644 index 000000000..471835708 --- /dev/null +++ b/lib/redmine/wiki_formatting/common_mark/helper.rb @@ -0,0 +1,57 @@ +# frozen_string_literal: true + +# Redmine - project management software +# Copyright (C) 2006-2021 Jean-Philippe Lang +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +module Redmine + module WikiFormatting + module CommonMark + module Helper + include Redmine::WikiFormatting::Markdown::Helper + + def wikitoolbar_for(field_id, preview_url = preview_text_path) + heads_for_wiki_formatter + help_file = "/help/#{current_language.to_s.downcase}/wiki_syntax_common_mark.html" + # fall back to the english help page if there is none for the current + # language + unless File.readable? Rails.root.join("public", help_file) + help_file = "/help/en/wiki_syntax_common_mark.html" + end + url = "#{Redmine::Utils.relative_url_root}#{help_file}" + javascript_tag( + "var wikiToolbar = new jsToolBar(document.getElementById('#{field_id}')); " \ + "wikiToolbar.setHelpLink('#{escape_javascript url}'); " \ + "wikiToolbar.setPreviewUrl('#{escape_javascript preview_url}'); " \ + "wikiToolbar.draw();" + ) + end + + # removes the 'underline' icon from the markdown toolbar since there + # is no such thing in CommonMark + def heads_for_wiki_formatter + unless @common_mark_heads_for_wiki_formatter_included + super + content_for :header_tags do + javascript_tag(%[delete jsToolBar.prototype.elements.ins;]) + end + @common_mark_heads_for_wiki_formatter_included = true + end + end + end + end + end +end diff --git a/lib/redmine/wiki_formatting/common_mark/html_parser.rb b/lib/redmine/wiki_formatting/common_mark/html_parser.rb new file mode 100644 index 000000000..707829fb6 --- /dev/null +++ b/lib/redmine/wiki_formatting/common_mark/html_parser.rb @@ -0,0 +1,26 @@ +# frozen_string_literal: true + +# Redmine - project management software +# Copyright (C) 2006-2021 Jean-Philippe Lang +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +module Redmine + module WikiFormatting + module CommonMark + HtmlParser = Redmine::WikiFormatting::Markdown::HtmlParser + end + end +end diff --git a/lib/redmine/wiki_formatting/common_mark/markdown_filter.rb b/lib/redmine/wiki_formatting/common_mark/markdown_filter.rb new file mode 100644 index 000000000..b93b35b68 --- /dev/null +++ b/lib/redmine/wiki_formatting/common_mark/markdown_filter.rb @@ -0,0 +1,57 @@ +# frozen_string_literal: true + +# Redmine - project management software +# Copyright (C) 2006-2021 Jean-Philippe Lang +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +module Redmine + module WikiFormatting + module CommonMark + # Converts Markdown to HTML using CommonMarker + # + # We do not use the stock HTML::Pipeline::MarkdownFilter because this + # does not allow for straightforward configuration of render and parsing + # options + class MarkdownFilter < HTML::Pipeline::TextFilter + def initialize(text, context = nil, result = nil) + super text, context, result + @text = @text.delete "\r" + end + + def call + doc = CommonMarker.render_doc(@text, parse_options, extensions) + html = doc.to_html render_options, extensions + html.rstrip! + html + end + + private + + def extensions + context.fetch :commonmarker_extensions, [] + end + + def parse_options + context.fetch :commonmarker_parse_options, :DEFAULT + end + + def render_options + context.fetch :commonmarker_render_options, :DEFAULT + end + end + end + end +end diff --git a/lib/redmine/wiki_formatting/common_mark/sanitization_filter.rb b/lib/redmine/wiki_formatting/common_mark/sanitization_filter.rb new file mode 100644 index 000000000..a76201dfd --- /dev/null +++ b/lib/redmine/wiki_formatting/common_mark/sanitization_filter.rb @@ -0,0 +1,86 @@ +# frozen_string_literal: true + +# Redmine - project management software +# Copyright (C) 2006-2021 Jean-Philippe Lang +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +module Redmine + module WikiFormatting + module CommonMark + # sanitizes rendered HTML using the Sanitize gem + class SanitizationFilter < HTML::Pipeline::SanitizationFilter + def whitelist + @@whitelist ||= customize_whitelist(super.deep_dup) + end + + private + + # customizes the whitelist defined in + # https://github.com/jch/html-pipeline/blob/master/lib/html/pipeline/sanitization_filter.rb + def customize_whitelist(whitelist) + # Disallow `name` attribute globally, allow on `a` + whitelist[:attributes][:all].delete("name") + whitelist[:attributes]["a"].push("name") + + # allow class on code tags (this holds the language info from fenced + # code bocks and has the format language-foo) + whitelist[:attributes]["code"] = %w(class) + whitelist[:transformers].push lambda{|env| + node = env[:node] + return unless node.name == "code" + return unless node.has_attribute?("class") + + unless /\Alanguage-(\w+)\z/.match?(node["class"]) + node.remove_attribute("class") + end + } + + # Allow table cell alignment by style attribute + # + # Only necessary if we used the TABLE_PREFER_STYLE_ATTRIBUTES + # commonmarker option (which we do not, currently). + # By default, the align attribute is used (which is allowed on all + # elements). + # whitelist[:attributes]["th"] = %w(style) + # whitelist[:attributes]["td"] = %w(style) + # whitelist[:css] = { properties: ["text-align"] } + + # Allow `id` in a and li elements for footnotes + # and remove any `id` properties not matching for footnotes + whitelist[:attributes]["a"].push "id" + whitelist[:attributes]["li"] = %w(id) + whitelist[:transformers].push lambda{|env| + node = env[:node] + return unless node.name == "a" || node.name == "li" + return unless node.has_attribute?("id") + return if node.name == "a" && node["id"] =~ /\Afnref\d+\z/ + return if node.name == "li" && node["id"] =~ /\Afn\d+\z/ + + node.remove_attribute("id") + } + + # allow the same set of URL schemes for links as is the default in + # Redmine::Helpers::URL#uri_with_safe_scheme? + whitelist[:protocols]["a"]["href"] = [ + 'http', 'https', 'ftp', 'mailto', :relative + ] + + whitelist + end + end + end + end +end diff --git a/lib/redmine/wiki_formatting/common_mark/syntax_highlight_filter.rb b/lib/redmine/wiki_formatting/common_mark/syntax_highlight_filter.rb new file mode 100644 index 000000000..a027e6a17 --- /dev/null +++ b/lib/redmine/wiki_formatting/common_mark/syntax_highlight_filter.rb @@ -0,0 +1,50 @@ +# frozen_string_literal: true + +# Redmine - project management software +# Copyright (C) 2006-2021 Jean-Philippe Lang +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +module Redmine + module WikiFormatting + module CommonMark + # Redmine Syntax highlighting for

+      # blocks as generated by commonmarker
+      class SyntaxHighlightFilter < HTML::Pipeline::Filter
+        def call
+          doc.search("pre > code").each do |node|
+            next unless lang = node["class"].presence
+            next unless lang =~ /\Alanguage-(\w+)\z/
+
+            lang = $1
+            text = node.inner_text
+
+            if Redmine::SyntaxHighlighting.language_supported?(lang)
+              html = Redmine::SyntaxHighlighting.highlight_by_language(text, lang)
+              next if html.nil?
+
+              node.inner_html = html
+              node["class"] = "#{lang} syntaxhl"
+            else
+              # unsupported language, strip out the code tag
+              node.parent.inner_html = text
+            end
+          end
+          doc
+        end
+      end
+    end
+  end
+end
diff --git a/public/help/en/wiki_syntax_common_mark.html b/public/help/en/wiki_syntax_common_mark.html
new file mode 100644
index 000000000..bda25b13b
--- /dev/null
+++ b/public/help/en/wiki_syntax_common_mark.html
@@ -0,0 +1,87 @@
+
+
+
+Wiki formatting
+
+
+
+
+

Wiki Syntax Quick Reference (CommonMark Markdown (GitHub Flavored))

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Font Styles (more)
Strong**Strong**Strong
Italic*Italic*Italic
Deleted~~Deleted~~Deleted
Inline Code`Inline Code`Inline Code
Preformatted text```
 lines
 of code
```
+
+ lines
+ of code
+
+
Highlighted code (more)
Highlighted code```ruby
3.times do
  puts 'Hello'
end
```
+
3.times do
+  puts 'Hello'
+end
+
+
Line breaks and Paragraphs
An empty line

creates
a new paragraph.

An empty line

creates a new paragraph.

End a line with a backslash\
or two spaces to insert a manual line break.

End a line with a backslash
or two spaces to insert a manual line break.

Lists
Unordered list* Item 1
  * Sub
* Item 2
  • Item 1
    • Sub
  • Item 2
Ordered list1. Item 1
   1. Sub
2. Item 2
  1. Item 1
    1. Sub
  2. Item 2
Headings (more)
Heading 1# Title 1

Title 1

Heading 2## Title 2

Title 2

Heading 3### Title 3

Title 3

Links (more)
www.foo.barwww.foo.bar
http://foo.barhttp://foo.bar
[Foo](http://foo.bar)Foo
Redmine links (more)
Link to a Wiki page[[Wiki page]]Wiki page
Issue #12Issue #12
##12Bug #12: The issue subject
Revision r43Revision r43
commit:f30e13e43f30e13e4
source:some/filesource:some/file
Inline images (more)
Image![](image_url)
![](attached_image)
Tables
| A | B | C |
|---|---|---|
| A | B | C |
| D | E | F |
+ + + + + + +
ABC
ABC
DEF
+
Raw HTML (more)
HTML is <del>not</del> <u>allowed</u>.HTML is not allowed. +
+ +

More Information

+ + + diff --git a/public/help/en/wiki_syntax_detailed_common_mark.html b/public/help/en/wiki_syntax_detailed_common_mark.html new file mode 100644 index 000000000..64198fd17 --- /dev/null +++ b/public/help/en/wiki_syntax_detailed_common_mark.html @@ -0,0 +1,361 @@ + + + +RedmineWikiFormatting (CommonMark Markdown (GitHub Flavored)) + + + + + +

Wiki formatting (CommonMark Markdown (GitHub Flavored))

+ + + +

Links

+ +

Redmine links

+ +

Redmine allows hyperlinking between resources (issues, changesets, wiki pages...) from anywhere wiki formatting is used.

+
    +
  • Link to an issue: #124 (displays #124, link is striked-through if the issue is closed)
  • +
  • Link to an issue including tracker name and subject: ##124 (displays Bug #124: bulk edit doesn't change the category or fixed version properties)
  • +
  • Link to an issue note: #124-6, or #124#note-6
  • +
  • Link to an issue note within the same issue: #note-6
  • +
+ +

Wiki links:

+ +
    +
  • [[Guide]] displays a link to the page named 'Guide': Guide
  • +
  • [[Guide#further-reading]] takes you to the anchor "further-reading". Headings get automatically assigned anchors so that you can refer to them: Guide
  • +
  • [[#further-reading]] link to the anchor "further-reading" of the current page: #further-reading
  • +
  • [[Guide|User manual]] displays a link to the same page but with a different text: User manual
  • +
+ +

You can also link to pages of an other project wiki:

+ +
    +
  • [[sandbox:some page]] displays a link to the page named 'Some page' of the Sandbox wiki
  • +
  • [[sandbox:]] displays a link to the Sandbox wiki main page
  • +
+ +

Wiki links are displayed in red if the page doesn't exist yet, eg: Nonexistent page.

+ +

Links to other resources:

+ +
    +
  • Documents: +
      +
    • document#17 (link to document with id 17)
    • +
    • document:Greetings (link to the document with title "Greetings")
    • +
    • document:"Some document" (double quotes can be used when document title contains spaces)
    • +
    • sandbox:document:"Some document" (link to a document with title "Some document" in other project "sandbox")
    • +
    +
  • +
+ +
    +
  • Versions: +
      +
    • version#3 (link to version with id 3)
    • +
    • version:1.0.0 (link to version named "1.0.0")
    • +
    • version:"1.0 beta 2"
    • +
    • sandbox:version:1.0.0 (link to version "1.0.0" in the project "sandbox")
    • +
    +
  • +
+ +
    +
  • Attachments: +
      +
    • attachment:file.zip (link to the attachment of the current object named file.zip)
    • +
    • 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)
    • +
    +
  • +
+ +
    +
  • Changesets: +
      +
    • r758 (link to a changeset)
    • +
    • commit:c6f4d0fd (link to a changeset with a non-numeric hash)
    • +
    • svn1|r758 (link to a changeset of a specific repository, for projects with multiple repositories)
    • +
    • commit:hg|c6f4d0fd (link to a changeset with a non-numeric hash of a specific repository)
    • +
    • sandbox:r758 (link to a changeset of another project)
    • +
    • sandbox:commit:c6f4d0fd (link to a changeset with a non-numeric hash of another project)
    • +
    +
  • +
+ +
    +
  • Repository files: +
      +
    • source:some/file (link to the file located at /some/file in the project's repository)
    • +
    • source:some/file@52 (link to the file's revision 52)
    • +
    • source:some/file#L120 (link to line 120 of the file)
    • +
    • source:some/file@52#L120 (link to line 120 of the file's revision 52)
    • +
    • source:"some file@52#L120" (use double quotes when the URL contains spaces
    • +
    • export:some/file (force the download of the file)
    • +
    • source:svn1|some/file (link to a file of a specific repository, for projects with multiple repositories)
    • +
    • sandbox:source:some/file (link to the file located at /some/file in the repository of the project "sandbox")
    • +
    • sandbox:export:some/file (force the download of the file)
    • +
    +
  • +
+ +
    +
  • Forums: +
      +
    • forum#1 (link to forum with id 1
    • +
    • forum:Support (link to forum named Support)
    • +
    • forum:"Technical Support" (use double quotes if forum name contains spaces)
    • +
    +
  • +
+ +
    +
  • Forum messages: +
      +
    • message#1218 (link to message with id 1218)
    • +
    +
  • +
+ +
    +
  • Projects: +
      +
    • project#3 (link to project with id 3)
    • +
    • project:some-project (link to project with name or slug of "some-project")
    • +
    • project:"Some Project" (use double quotes for project name containing spaces)
    • +
    +
  • +
+ +
    +
  • News: +
      +
    • news#2 (link to news item with id 2)
    • +
    • news:Greetings (link to news item named "Greetings")
    • +
    • news:"First Release" (use double quotes if news item name contains spaces)
    • +
    +
  • +
+ +
    +
  • Users: +
      +
    • user#2 (link to user with id 2)
    • +
    • user:jsmith (Link to user with login jsmith)
    • +
    • @jsmith (Link to user with login jsmith)
    • +
    +
  • +
+ +

Escaping:

+ +
    +
  • You can prevent Redmine links from being parsed by preceding them with an exclamation mark: !
  • +
+ + +

External links

+ +

URLs (starting with: www, http, https, ftp, ftps, sftp and sftps) and email addresses are automatically turned into clickable links:

+ +
+http://www.redmine.org, someone@foo.bar
+
+ +

displays: http://www.redmine.org,

+ +

If you want to display a specific text instead of the URL, you can use the standard markdown syntax:

+ +
+[Redmine web site](http://www.redmine.org)
+
+ +

displays: Redmine web site

+ + +

Text formatting

+ +

For things such as headlines, bold, tables, lists, Redmine supports Markdown syntax according to CommonMark including some extensions commonly referred to as GitHub flavored Markdown. See the GitHub Flavored Markdown Spec for information on using any of these features. A few samples are included below, but the engine is capable of much more of that.

+ +

Font style

+ +
+* **bold**
+* *Italic*
+* ***bold italic***
+* ~~strike-through~~
+
+ +

Display:

+ +
    +
  • bold
  • +
  • italic
  • +
  • bold italic
  • +
  • strike-through
  • +
+ +

Inline images

+ +
    +
  • ![](image_url) displays an image located at image_url (markdown syntax)
  • +
  • If you have an image attached to your wiki page, it can be displayed inline using its filename: ![](attached_image)
  • +
  • Images in your computer's clipboard can be pasted directly using Ctrl-v or Command-v.
  • +
  • Image files can be dragged onto the text area in order to be uploaded and embedded.
  • +
+ +

Headings

+ +
+# Heading
+## Subheading
+### Subsubheading
+
+ +

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

+ + +

Blockquotes

+ +

Start the paragraph with >

+ +
+> Rails is a full-stack framework for developing database-backed web applications according to the Model-View-Control pattern.
+To go live, all you need to add is a database and a web server.
+
+ +

Display:

+ +
+

Rails is a full-stack framework for developing database-backed web applications according to the Model-View-Control pattern.
To go live, all you need to add is a database and a web server.

+
+ + +

Table of content

+ +
+{{toc}} => left aligned toc
+{{>toc}} => right aligned toc
+
+ +

Horizontal Rule

+ +
+---
+
+ +

Macros

+ +

Redmine has the following builtin macros:

+ +

+

+
hello_world
+

Sample macro.

+ +
macro_list
+

Displays a list of all available macros, including description if available.

+ +
child_pages
+

Displays a list of child pages. With no argument, it displays the child pages of the current wiki page. Examples:

+
{{child_pages}} -- can be used from a wiki page only
+{{child_pages(depth=2)}} -- display 2 levels nesting only
+ +
include
+

Include a wiki page. Example:

+
{{include(Foo)}}
+

or to include a page of a specific project wiki:

+
{{include(projectname:Foo)}}
+ +
collapse
+

Inserts of collapsed block of text. Example:

+
{{collapse(View details...)
+This is a block of text that is collapsed by default.
+It can be expanded by clicking a link.
+}}
+ +
thumbnail
+

Displays a clickable thumbnail of an attached image. Examples:

+
{{thumbnail(image.png)}}
+{{thumbnail(image.png, size=300, title=Thumbnail)}}
+ +
issue
+

Inserts a link to an issue with flexible text. Examples:

+
{{issue(123)}}                              -- Issue #123: Enhance macro capabilities
+{{issue(123, project=true)}}                -- Andromeda - Issue #123:Enhance macro capabilities
+{{issue(123, tracker=false)}}               -- #123: Enhance macro capabilities
+{{issue(123, subject=false, project=true)}} -- Andromeda - Issue #123
+
+

+ +

Code highlighting

+ +

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

+ +

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

+ +
+```ruby
+  Place your code here.
+```
+
+ +

Example:

+ +
# The Greeter class
+class Greeter
+  def initialize(name)
+    @name = name.capitalize
+  end
+
+  def salute
+    puts "Hello #{@name}!"
+  end
+end
+
+ +

Raw HTML

+ +

You may use raw HTML for more complex formatting tasks, i.e. complex tables with cells spanning multiple rows or columns:

+ +

+    <table width="50%">
+      <tr><td rowspan="2">Two rows</td><td>foo</td></tr>
+      <tr><td>bar</td></tr>
+      <tr><td align="center" colspan="2">bar</td></tr>
+    </table>
+  
+ +

yields

+ + + + + +
Two rowsfoo
bar
bar
+ + + + diff --git a/public/help/wiki_syntax_detailed.css b/public/help/wiki_syntax_detailed.css index ab37aff7b..4e99410fe 100644 --- a/public/help/wiki_syntax_detailed.css +++ b/public/help/wiki_syntax_detailed.css @@ -15,6 +15,9 @@ a, a:link, a:visited{ color: #169; text-decoration: none; } a:hover, a:active{ color: #c61a1a; text-decoration: underline;} a.new { color: #b73535; } +table.sample { border-collapse: collapse; border-spacing: 0; margin: 4px; margin-left: 30px;} +table.sample th, table.sample td { border: solid 1px #bbb; padding: 4px; height: 1em; } + .syntaxhl .c1 { color: #888888 } .syntaxhl .k { color: #008800; font-weight: bold } .syntaxhl .nc { color: #BB0066; font-weight: bold } diff --git a/test/unit/lib/redmine/wiki_formatting/common_mark/application_helper_test.rb b/test/unit/lib/redmine/wiki_formatting/common_mark/application_helper_test.rb new file mode 100644 index 000000000..fecafd660 --- /dev/null +++ b/test/unit/lib/redmine/wiki_formatting/common_mark/application_helper_test.rb @@ -0,0 +1,66 @@ +# frozen_string_literal: true + +# Redmine - project management software +# Copyright (C) 2006-2021 Jean-Philippe Lang +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +require File.expand_path('../../../../../../test_helper', __FILE__) + +class Redmine::WikiFormatting::CommonMark::ApplicationHelperTest < Redmine::HelperTest + if Object.const_defined?(:CommonMarker) + + include ERB::Util + include Rails.application.routes.url_helpers + + fixtures :projects, :enabled_modules, + :users, :email_addresses, + :members, :member_roles, :roles, + :repositories, :changesets, + :projects_trackers, + :trackers, :issue_statuses, :issues, :versions, :documents, :journals, + :wikis, :wiki_pages, :wiki_contents, + :boards, :messages, :news, + :attachments, :enumerations, + :custom_values, :custom_fields, :custom_fields_projects + + def setup + super + set_tmp_attachments_directory + end + + def test_attached_images_with_markdown_and_non_ascii_filename + to_test = { + 'CAFÉ.JPG' => 'CAF%C3%89.JPG', + 'crème.jpg' => 'cr%C3%A8me.jpg', + } + with_settings :text_formatting => 'common_mark' do + to_test.each do |filename, result| + attachment = Attachment.generate!(:filename => filename) + assert_include %(), textilizable("![](#{filename})", :attachments => [attachment]) + end + end + end + + def test_toc_with_markdown_formatting_should_be_parsed + with_settings :text_formatting => 'common_mark' do + assert_select_in textilizable("{{toc}}\n\n# Heading"), 'ul.toc li', :text => 'Heading' + assert_select_in textilizable("{{ 'Heading' + assert_select_in textilizable("{{>toc}}\n\n# Heading"), 'ul.toc.right li', :text => 'Heading' + end + end + + end +end diff --git a/test/unit/lib/redmine/wiki_formatting/common_mark/external_links_filter_test.rb b/test/unit/lib/redmine/wiki_formatting/common_mark/external_links_filter_test.rb new file mode 100644 index 000000000..d4e8fa0df --- /dev/null +++ b/test/unit/lib/redmine/wiki_formatting/common_mark/external_links_filter_test.rb @@ -0,0 +1,48 @@ +# frozen_string_literal: true + +# Redmine - project management software +# Copyright (C) 2006-2021 Jean-Philippe Lang +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +require File.expand_path('../../../../../../test_helper', __FILE__) + +if Object.const_defined?(:CommonMarker) + require 'redmine/wiki_formatting/common_mark/external_links_filter' + + class Redmine::WikiFormatting::CommonMark::ExternalLinksFilterTest < ActiveSupport::TestCase + def filter(html) + Redmine::WikiFormatting::CommonMark::ExternalLinksFilter.to_html(html, @options) + end + + def setup + @options = { } + end + + def test_external_links_should_have_external_css_class + assert_equal %(link), filter(%(link)) + end + + def test_locals_links_should_not_have_external_css_class + assert_equal %(home), filter(%(home)) + assert_equal %(relative), filter(%(relative)) + assert_equal %(anchor), filter(%(anchor)) + end + + def test_mailto_links_should_have_email_class + assert_equal %(), filter(%(user)) + end + end +end diff --git a/test/unit/lib/redmine/wiki_formatting/common_mark/fixup_auto_links_filter_test.rb b/test/unit/lib/redmine/wiki_formatting/common_mark/fixup_auto_links_filter_test.rb new file mode 100644 index 000000000..92b324151 --- /dev/null +++ b/test/unit/lib/redmine/wiki_formatting/common_mark/fixup_auto_links_filter_test.rb @@ -0,0 +1,50 @@ +# frozen_string_literal: true + +# Redmine - project management software +# Copyright (C) 2006-2021 Jean-Philippe Lang +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +require File.expand_path('../../../../../../test_helper', __FILE__) + +if Object.const_defined?(:CommonMarker) + require 'redmine/wiki_formatting/common_mark/fixup_auto_links_filter' + + class Redmine::WikiFormatting::CommonMark::FixupAutoLinksFilterTest < ActiveSupport::TestCase + def filter(html) + Redmine::WikiFormatting::CommonMark::FixupAutoLinksFilter.to_html(html, @options) + end + + def format(markdown) + Redmine::WikiFormatting::CommonMark::MarkdownFilter.to_html(markdown, Redmine::WikiFormatting::CommonMark::PIPELINE_CONFIG) + end + + def setup + @options = { } + end + + def test_should_fixup_autolinked_user_references + text = "user:user@example.org" + assert_equal "

#{text}

", filter(format(text)) + text = "@user@example.org" + assert_equal "

#{text}

", filter(format(text)) + end + + def test_should_fixup_autolinked_hires_files + text = "printscreen@2x.png" + assert_equal "

#{text}

", filter(format(text)) + end + end +end diff --git a/test/unit/lib/redmine/wiki_formatting/common_mark/formatter_test.rb b/test/unit/lib/redmine/wiki_formatting/common_mark/formatter_test.rb new file mode 100644 index 000000000..9f23dba30 --- /dev/null +++ b/test/unit/lib/redmine/wiki_formatting/common_mark/formatter_test.rb @@ -0,0 +1,270 @@ +# frozen_string_literal: true + +# Redmine - project management software +# Copyright (C) 2006-2021 Jean-Philippe Lang +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +require File.expand_path('../../../../../../test_helper', __FILE__) + +class Redmine::WikiFormatting::CommonMark::FormatterTest < ActionView::TestCase + if Object.const_defined?(:CommonMarker) + + def setup + @formatter = Redmine::WikiFormatting::CommonMark::Formatter + end + + def format(text) + @formatter.new(text).to_html + end + + def test_should_render_hard_breaks + html ="

foo
\nbar

" + assert_equal html, format("foo\\\nbar") + assert_equal html, format("foo \nbar") + end + + def test_should_ignore_soft_breaks + assert_equal "

foo\nbar

", format("foo\nbar") + end + + def test_syntax_error_in_image_reference_should_not_raise_exception + assert format("!>[](foo.png)") + end + + def test_empty_image_should_not_raise_exception + assert format("![]()") + end + + def test_inline_style + assert_equal "

foo

", format("**foo**") + end + + def test_not_set_intra_emphasis + assert_equal "

foo_bar_baz

", format("foo_bar_baz") + end + + def test_wiki_links_should_be_preserved + text = 'This is a wiki link: [[Foo]]' + assert_include '[[Foo]]', format(text) + end + + def test_redmine_links_with_double_quotes_should_be_preserved + text = 'This is a redmine link: version:"1.0"' + assert_include 'version:"1.0"', format(text) + end + + def test_links_by_id_should_be_preserved + text = "[project#3]" + assert_equal "

#{text}

", format(text) + end + + def test_links_to_users_should_be_preserved + text = "[@login]" + assert_equal "

#{text}

", format(text) + text = "[user:login]" + assert_equal "

#{text}

", format(text) + text = "user:user@example.org" + assert_equal "

#{text}

", format(text) + text = "[user:user@example.org]" + assert_equal "

#{text}

", format(text) + text = "@user@example.org" + assert_equal "

#{text}

", format(text) + text = "[@user@example.org]" + assert_equal "

#{text}

", format(text) + end + + def test_files_with_at_should_not_end_up_as_mailto_links + text = "printscreen@2x.png" + assert_equal "

#{text}

", format(text) + text = "[printscreen@2x.png]" + assert_equal "

#{text}

", format(text) + end + + def test_should_support_syntax_highlight + text = <<-STR + ~~~ruby + def foo + end + ~~~ + STR + assert_select_in format(text), 'pre code.ruby.syntaxhl' do + assert_select 'span.k', :text => 'def' + end + end + + def test_should_not_allow_invalid_language_for_code_blocks + text = <<-STR + ~~~foo + test + ~~~ + STR + assert_equal "
test\n
", format(text) + end + + def test_external_links_should_have_external_css_class + text = 'This is a [link](http://example.net/)' + assert_equal '

This is a link

', format(text) + end + + def test_locals_links_should_not_have_external_css_class + text = 'This is a [link](/issues)' + assert_equal '

This is a link

', format(text) + end + + def test_markdown_should_not_require_surrounded_empty_line + text = <<-STR + This is a list: + * One + * Two + STR + assert_equal "

This is a list:

\n
    \n
  • One
  • \n
  • Two
  • \n
", format(text) + end + + def test_footnotes + text = <<-STR + This is some text[^1]. + + [^1]: This is the foot note + STR + + expected = <<-EXPECTED +

This is some text1.

+
    +
  1. +

    This is the foot note

    +
  2. +
+ EXPECTED + + assert_equal expected.gsub(%r{[\r\n\t]}, ''), format(text).gsub(%r{[\r\n\t]}, '') + end + + STR_WITH_PRE = [ + # 0 + <<~STR.chomp, + # Title + + Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Maecenas sed libero. + STR + # 1 + <<~STR.chomp, + ## Heading 2 + + ~~~ruby + def foo + end + ~~~ + + Morbi facilisis accumsan orci non pharetra. + + ~~~ ruby + def foo + end + ~~~ + + ``` + Pre Content: + + ## Inside pre + + inside pre block + + Morbi facilisis accumsan orci non pharetra. + ``` + STR + # 2 + <<~STR.chomp, + ### Heading 3 + + Nulla nunc nisi, egestas in ornare vel, posuere ac libero. + STR + ] + + def test_get_section_should_ignore_pre_content + text = STR_WITH_PRE.join("\n\n") + + assert_section_with_hash STR_WITH_PRE[1..2].join("\n\n"), text, 2 + assert_section_with_hash STR_WITH_PRE[2], text, 3 + end + + def test_update_section_should_not_escape_pre_content_outside_section + text = STR_WITH_PRE.join("\n\n") + replacement = "New text" + + assert_equal [STR_WITH_PRE[0..1], "New text"].flatten.join("\n\n"), + @formatter.new(text).update_section(3, replacement) + end + + def test_should_emphasize_text + text = 'This _text_ should be emphasized' + assert_equal '

This text should be emphasized

', format(text) + end + + def test_should_strike_through_text + text = 'This ~~text~~ should be striked through' + assert_equal '

This text should be striked through

', format(text) + end + + def test_should_autolink_urls_and_emails + [ + ["http://example.org", '

http://example.org

'], + ["http://www.redmine.org/projects/redmine/issues?utf8=✓", + '

http://www.redmine.org/projects/redmine/issues?utf8=✓

'], + ['[Letters](https://yandex.ru/search/?text=кол-во)', '

Letters

'], + ["www.example.org", '

www.example.org

'], + ["user@example.org", '

'] + ].each do |text, html| + assert_equal html, format(text) + end + end + + def test_should_support_html_tables + text = '
Cell
' + assert_equal '
Cell
', format(text) + end + + def test_should_remove_unsafe_uris + [ + ['', ''], + ['click me', '

click me

'], + ].each do |text, html| + assert_equal html, format(text) + end + end + + def test_should_escape_unwanted_tags + [ + [ + %[

sit
amet <style>.foo { color: #fff; }</style> <script>alert("hello world");</script>

], + %[sit
amet ] + ] + ].each do |expected, input| + assert_equal expected, format(input) + end + end + + private + + def assert_section_with_hash(expected, text, index) + result = @formatter.new(text).get_section(index) + + assert_kind_of Array, result + assert_equal 2, result.size + assert_equal expected, result.first, "section content did not match" + assert_equal Digest::MD5.hexdigest(expected), result.last, "section hash did not match" + end + end +end diff --git a/test/unit/lib/redmine/wiki_formatting/common_mark/markdown_filter_test.rb b/test/unit/lib/redmine/wiki_formatting/common_mark/markdown_filter_test.rb new file mode 100644 index 000000000..195c3e11e --- /dev/null +++ b/test/unit/lib/redmine/wiki_formatting/common_mark/markdown_filter_test.rb @@ -0,0 +1,35 @@ +# frozen_string_literal: true + +# Redmine - project management software +# Copyright (C) 2006-2021 Jean-Philippe Lang +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +require File.expand_path('../../../../../../test_helper', __FILE__) + +if Object.const_defined?(:CommonMarker) + require 'redmine/wiki_formatting/common_mark/markdown_filter' + + class Redmine::WikiFormatting::CommonMark::MarkdownFilterTest < ActiveSupport::TestCase + def filter(markdown) + Redmine::WikiFormatting::CommonMark::MarkdownFilter.to_html(markdown) + end + + # just a basic sanity test. more formatting tests in the formatter_test + def test_should_render_markdown + assert_equal "

bold

", filter("**bold**") + end + end +end diff --git a/test/unit/lib/redmine/wiki_formatting/common_mark/sanitization_filter_test.rb b/test/unit/lib/redmine/wiki_formatting/common_mark/sanitization_filter_test.rb new file mode 100644 index 000000000..72ef52a63 --- /dev/null +++ b/test/unit/lib/redmine/wiki_formatting/common_mark/sanitization_filter_test.rb @@ -0,0 +1,211 @@ +# frozen_string_literal: true + +# Redmine - project management software +# Copyright (C) 2006-2021 Jean-Philippe Lang +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +require File.expand_path('../../../../../../test_helper', __FILE__) + +if Object.const_defined?(:CommonMarker) + require 'redmine/wiki_formatting/common_mark/sanitization_filter' + + class Redmine::WikiFormatting::CommonMark::SanitizationFilterTest < ActiveSupport::TestCase + def filter(html) + Redmine::WikiFormatting::CommonMark::SanitizationFilter.to_html(html, @options) + end + + def setup + @options = { } + end + + def test_should_filter_tags + input = %( dont blink) + assert_equal %(foo dont blink), filter(input) + end + + def test_should_sanitize_attributes + input = %(link) + assert_equal %(link), filter(input) + end + + def test_should_allow_relative_links + input = %(foo/bar) + assert_equal input, filter(input) + end + + def test_should_support_footnotes + input = %(foo) + assert_equal input, filter(input) + input = %(
  1. footnote
) + assert_equal input, filter(input) + end + + def test_should_remove_invalid_ids + input = %(foo) + assert_equal %(foo), filter(input) + input = %(
  1. footnote
) + assert_equal %(
  1. footnote
), filter(input) + end + + def test_should_allow_class_on_code_only + input = %(

bar

) + assert_equal %(

bar

), filter(input) + + input = %(foo) + assert_equal input, filter(input) + + input = %(foo) + assert_equal %(foo), filter(input) + end + + # samples taken from the Sanitize test suite + # rubocop:disable Layout/LineLength + STRINGS = [ + [ + 'Lorem ipsum dolor sit
amet ', + 'Lorem ipsum dolor sit
amet .foo { color: #fff; } ' + ], + [ + 'Lorem dolor sit
amet ', + 'Lorem ipsum dolor sit
amet <script>alert("hello world");' + ] + ] + # rubocop:enable Layout/LineLength + + def test_should_sanitize_html_strings + STRINGS.each do |input, expected| + assert_equal expected, filter(input) + end + end + + # samples taken from the Sanitize test suite + PROTOCOLS = { + 'protocol-based JS injection: simple, no spaces' => [ + 'foo', + 'foo' + ], + + 'protocol-based JS injection: simple, spaces before' => [ + 'foo', + 'foo' + ], + + 'protocol-based JS injection: simple, spaces after' => [ + 'foo', + 'foo' + ], + + 'protocol-based JS injection: simple, spaces before and after' => [ + 'foo', + 'foo' + ], + + 'protocol-based JS injection: preceding colon' => [ + 'foo', + 'foo' + ], + + 'protocol-based JS injection: UTF-8 encoding' => [ + 'foo', + 'foo' + ], + + 'protocol-based JS injection: long UTF-8 encoding' => [ + 'foo', + 'foo' + ], + + # rubocop:disable Layout/LineLength + 'protocol-based JS injection: long UTF-8 encoding without semicolons' => [ + 'foo', + 'foo' + ], + # rubocop:enable Layout/LineLength + + 'protocol-based JS injection: hex encoding' => [ + 'foo', + 'foo' + ], + + 'protocol-based JS injection: long hex encoding' => [ + 'foo', + 'foo' + ], + + 'protocol-based JS injection: hex encoding without semicolons' => [ + 'foo', + 'foo' + ], + + 'protocol-based JS injection: null char' => [ + "", + '' + # '' + ], + + 'protocol-based JS injection: invalid URL char' => [ + '', + '' + ], + + 'protocol-based JS injection: spaces and entities' => [ + '', + '' + # '' + ], + + 'protocol whitespace' => [ + '', + '' + ], + + 'data images sources' => [ + '', + '' + ], + + 'data URIs' => [ + 'XSS', + 'XSS' + ], + + 'vbscript URIs' => [ + 'XSS', + 'XSS' + ], + + 'invalid URIs' => [ + 'link', + 'link' + ], + } + + PROTOCOLS.each do |name, strings| + test "should not allow #{name}" do + input, expected = *strings + assert_equal expected, filter(input) + end + end + end +end diff --git a/test/unit/lib/redmine/wiki_formatting/common_mark/syntax_highlight_filter_test.rb b/test/unit/lib/redmine/wiki_formatting/common_mark/syntax_highlight_filter_test.rb new file mode 100644 index 000000000..e7e782d53 --- /dev/null +++ b/test/unit/lib/redmine/wiki_formatting/common_mark/syntax_highlight_filter_test.rb @@ -0,0 +1,75 @@ +# frozen_string_literal: true + +# Redmine - project management software +# Copyright (C) 2006-2021 Jean-Philippe Lang +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +require File.expand_path('../../../../../../test_helper', __FILE__) +if Object.const_defined?(:CommonMarker) + require 'redmine/wiki_formatting/common_mark/syntax_highlight_filter' + + class Redmine::WikiFormatting::CommonMark::SyntaxHighlightFilterTest < ActiveSupport::TestCase + def filter(html) + Redmine::WikiFormatting::CommonMark::SyntaxHighlightFilter.to_html(html, @options) + end + + def setup + @options = { } + end + + def test_should_highlight_supported_language + input = <<~HTML +

+        def foo
+        end
+        
+ HTML + expected = <<~HTML +

+        def foo
+        end
+        
+ HTML + assert_equal expected, filter(input) + end + + def test_should_strip_code_for_unknown_lang + input = <<~HTML +

+        def foo
+        end
+        
+ HTML + expected = <<~HTML +
+        def foo
+        end
+        
+ HTML + assert_equal expected, filter(input) + end + + def test_should_ignore_code_without_class + input = <<~HTML +

+        def foo
+        end
+        
+ HTML + assert_equal input, filter(input) + end + end +end -- 2.22.0