summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMarius Balteanu <marius.balteanu@zitec.com>2025-04-29 21:13:55 +0000
committerMarius Balteanu <marius.balteanu@zitec.com>2025-04-29 21:13:55 +0000
commit8fa4c4458136cfef4f138749b6655f94242a3c17 (patch)
treeb4e159a6da5013074fa92900a44a6914922542b9
parent0dc7337c7d45977378b0a2c40beb301da4d02a05 (diff)
downloadredmine-8fa4c4458136cfef4f138749b6655f94242a3c17.tar.gz
redmine-8fa4c4458136cfef4f138749b6655f94242a3c17.zip
Enable CommonMark alert extension (#42603).
Patch by Mizuki ISHIKAWA (user:ishikawa999). git-svn-id: https://svn.redmine.org/redmine/trunk@23724 e93f8b46-1217-0410-a6f0-8f06a7374b81
-rw-r--r--app/assets/stylesheets/application.css32
-rw-r--r--lib/redmine/wiki_formatting/common_mark/formatter.rb1
-rw-r--r--lib/redmine/wiki_formatting/common_mark/sanitization_filter.rb20
-rw-r--r--test/unit/lib/redmine/wiki_formatting/common_mark/formatter_test.rb57
-rw-r--r--test/unit/lib/redmine/wiki_formatting/common_mark/sanitization_filter_test.rb26
5 files changed, 127 insertions, 9 deletions
diff --git a/app/assets/stylesheets/application.css b/app/assets/stylesheets/application.css
index 7dc1a64b4..c63a89a75 100644
--- a/app/assets/stylesheets/application.css
+++ b/app/assets/stylesheets/application.css
@@ -1262,6 +1262,38 @@ div.flash.warning svg.icon-svg, .conflict svg.icon-svg {
.conflict-details {font-size:93%;}
+/***** CommonMark Alerts *****/
+.markdown-alert {
+ border-left: 4px solid;
+ padding: 10px 10px 1px 10px;
+ margin: 10px 0;
+}
+
+.markdown-alert-title + p {
+ margin-top: 2px;
+}
+
+.markdown-alert-title {
+ font-weight: bold;
+ margin-bottom: 0.5em;
+ margin: 0;
+}
+
+.markdown-alert-tip { border-color: #5db651; }
+.markdown-alert-tip .markdown-alert-title { color: #005f00; }
+
+.markdown-alert-important { border-color: #800080; }
+.markdown-alert-important .markdown-alert-title { color: #4b006e; }
+
+.markdown-alert-caution { border-color: #c22; }
+.markdown-alert-caution .markdown-alert-title { color: #880000; }
+
+.markdown-alert-warning { border-color: #e4bc4b; }
+.markdown-alert-warning .markdown-alert-title { color: #a7760c; }
+
+.markdown-alert-note { border-color: #169; }
+.markdown-alert-note .markdown-alert-title { color: #1e40af; }
+
/***** Ajax indicator ******/
#ajax-indicator {
position: absolute; /* fixed not supported by IE */
diff --git a/lib/redmine/wiki_formatting/common_mark/formatter.rb b/lib/redmine/wiki_formatting/common_mark/formatter.rb
index b695fb854..f2b9bca04 100644
--- a/lib/redmine/wiki_formatting/common_mark/formatter.rb
+++ b/lib/redmine/wiki_formatting/common_mark/formatter.rb
@@ -34,6 +34,7 @@ module Redmine
header_ids: nil,
tasklist: true,
shortcodes: false,
+ alerts: true,
}.freeze,
# https://github.com/gjtorikian/commonmarker#parse-options
diff --git a/lib/redmine/wiki_formatting/common_mark/sanitization_filter.rb b/lib/redmine/wiki_formatting/common_mark/sanitization_filter.rb
index e603d9f7f..584df9e0d 100644
--- a/lib/redmine/wiki_formatting/common_mark/sanitization_filter.rb
+++ b/lib/redmine/wiki_formatting/common_mark/sanitization_filter.rb
@@ -68,6 +68,26 @@ module Redmine
end
}
+ # Allow class on div and p tags only for alert blocks
+ # (must be exactly: "markdown-alert markdown-alert-*" for div, and "markdown-alert-title" for p)
+ (allowlist[:attributes]["div"] ||= []) << "class"
+ (allowlist[:attributes]["p"] ||= []) << "class"
+ allowlist[:transformers].push lambda{|env|
+ node = env[:node]
+ return unless node.element?
+
+ case node.name
+ when 'div'
+ unless node['class'] =~ /\Amarkdown-alert markdown-alert-[a-z]+\z/
+ node.remove_attribute('class')
+ end
+ when 'p'
+ unless node['class'] == 'markdown-alert-title'
+ node.remove_attribute('class')
+ end
+ end
+ }
+
# Allow table cell alignment by style attribute
#
# Only necessary if we used the TABLE_PREFER_STYLE_ATTRIBUTES
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
index 79581a928..d9a84a2c2 100644
--- a/test/unit/lib/redmine/wiki_formatting/common_mark/formatter_test.rb
+++ b/test/unit/lib/redmine/wiki_formatting/common_mark/formatter_test.rb
@@ -163,39 +163,39 @@ class Redmine::WikiFormatting::CommonMark::FormatterTest < ActionView::TestCase
# 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
-
+
<tag> 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
]
@@ -299,6 +299,45 @@ class Redmine::WikiFormatting::CommonMark::FormatterTest < ActionView::TestCase
assert_equal expected.gsub(%r{[\r\n\t]}, ''), to_html(text).gsub(%r{[\r\n\t]}, '').rstrip
end
+ def test_should_render_alert_blocks
+ text = <<~MD
+ > [!note]
+ > This is a note.
+
+ > [!tip]
+ > This is a tip.
+
+ > [!warning]
+ > This is a warning.
+
+ > [!caution]
+ > This is a caution.
+
+ > [!important]
+ > This is a important.
+ MD
+
+ html = to_html(text)
+ %w[note tip warning caution important].each do |alert|
+ assert_include "<div class=\"markdown-alert markdown-alert-note\">\n<p class=\"markdown-alert-title\">Note</p>\n<p>This is a note.</p>\n</div>", html
+ end
+ end
+
+ def test_should_not_render_unknown_alert_type
+ text = <<~MD
+ > [!unknown]
+ > This should not become an alert.
+ MD
+
+ html = to_html(text)
+
+ assert_include "<blockquote>", html
+ assert_include "[!unknown]", html
+ assert_include "This should not become an alert.", html
+
+ assert_not_include 'markdown-alert', html
+ end
+
private
def assert_section_with_hash(expected, text, index)
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
index 4c0282f2d..d17c2a09f 100644
--- 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
@@ -71,6 +71,32 @@ if Object.const_defined?(:Commonmarker)
assert_equal %(<code>foo</code>), filter(input)
end
+ def test_should_allow_valid_alert_div_and_p_classes
+ html = <<~HTML
+ <div class="markdown-alert markdown-alert-tip">
+ <p class="markdown-alert-title">Tip</p>
+ <p>Useful tip.</p>
+ </div>
+ HTML
+
+ sanitized = filter(html)
+
+ assert_include 'class="markdown-alert markdown-alert-tip"', sanitized
+ assert_include 'class="markdown-alert-title"', sanitized
+ end
+
+ def test_should_remove_invalid_div_class
+ html = '<div class="bad-class">Text</div>'
+ sanitized = filter(html)
+ refute_include 'bad-class', sanitized
+ end
+
+ def test_should_remove_invalid_p_class
+ html = '<p class="bad-class">Text</p>'
+ sanitized = filter(html)
+ assert_not_include 'bad-class', sanitized
+ end
+
def test_should_allow_links_with_safe_url_schemes
%w(http https ftp ssh foo).each do |scheme|
input = %(<a href="#{scheme}://example.org/">foo</a>)