Feature #42603 » 0001-Enable-common_mark-alerts.patch
| app/assets/stylesheets/application.css | ||
|---|---|---|
| 1262 | 1262 | |
| 1263 | 1263 |
.conflict-details {font-size:93%;}
|
| 1264 | 1264 | |
| 1265 |
/***** CommonMark Alerts *****/ |
|
| 1266 | ||
| 1267 |
.markdown-alert {
|
|
| 1268 |
border-left: 4px solid; |
|
| 1269 |
padding: 10px 10px 1px 10px; |
|
| 1270 |
margin: 10px 0; |
|
| 1271 |
} |
|
| 1272 | ||
| 1273 |
.markdown-alert-title + p {
|
|
| 1274 |
margin-top: 2px; |
|
| 1275 |
} |
|
| 1276 | ||
| 1277 |
.markdown-alert-title {
|
|
| 1278 |
font-weight: bold; |
|
| 1279 |
margin-bottom: 0.5em; |
|
| 1280 |
margin: 0px; |
|
| 1281 |
} |
|
| 1282 | ||
| 1283 |
.markdown-alert-tip { border-color: #5db651; }
|
|
| 1284 |
.markdown-alert-tip .markdown-alert-title { color: #005f00; }
|
|
| 1285 | ||
| 1286 |
.markdown-alert-important { border-color: #800080; }
|
|
| 1287 |
.markdown-alert-important .markdown-alert-title { color: #4b006e; }
|
|
| 1288 | ||
| 1289 |
.markdown-alert-caution { border-color: #c22; }
|
|
| 1290 |
.markdown-alert-caution .markdown-alert-title { color: #880000; }
|
|
| 1291 | ||
| 1292 |
.markdown-alert-warning { border-color: #e4bc4b; }
|
|
| 1293 |
.markdown-alert-warning .markdown-alert-title { color: #a7760c; }
|
|
| 1294 | ||
| 1295 |
.markdown-alert-note { border-color: #169; }
|
|
| 1296 |
.markdown-alert-note .markdown-alert-title { color: #1e40af; }
|
|
| 1297 | ||
| 1265 | 1298 |
/***** Ajax indicator ******/ |
| 1266 | 1299 |
#ajax-indicator {
|
| 1267 | 1300 |
position: absolute; /* fixed not supported by IE */ |
| lib/redmine/wiki_formatting/common_mark/formatter.rb | ||
|---|---|---|
| 34 | 34 |
header_ids: nil, |
| 35 | 35 |
tasklist: true, |
| 36 | 36 |
shortcodes: false, |
| 37 |
alerts: true, |
|
| 37 | 38 |
}.freeze, |
| 38 | 39 | |
| 39 | 40 |
# https://github.com/gjtorikian/commonmarker#parse-options |
| lib/redmine/wiki_formatting/common_mark/sanitization_filter.rb | ||
|---|---|---|
| 68 | 68 |
end |
| 69 | 69 |
} |
| 70 | 70 | |
| 71 |
# Allow class on div and p tags only for alert blocks |
|
| 72 |
# (must be exactly: "markdown-alert markdown-alert-*" for div, and "markdown-alert-title" for p) |
|
| 73 |
(allowlist[:attributes]["div"] ||= []) << "class" |
|
| 74 |
(allowlist[:attributes]["p"] ||= []) << "class" |
|
| 75 |
allowlist[:transformers].push lambda{|env|
|
|
| 76 |
node = env[:node] |
|
| 77 |
return unless node.element? |
|
| 78 | ||
| 79 |
case node.name |
|
| 80 |
when 'div' |
|
| 81 |
unless node['class'] =~ /\Amarkdown-alert markdown-alert-[a-z]+\z/ |
|
| 82 |
node.remove_attribute('class')
|
|
| 83 |
end |
|
| 84 |
when 'p' |
|
| 85 |
unless node['class'] == 'markdown-alert-title' |
|
| 86 |
node.remove_attribute('class')
|
|
| 87 |
end |
|
| 88 |
end |
|
| 89 |
} |
|
| 90 | ||
| 71 | 91 |
# Allow table cell alignment by style attribute |
| 72 | 92 |
# |
| 73 | 93 |
# Only necessary if we used the TABLE_PREFER_STYLE_ATTRIBUTES |
| test/unit/lib/redmine/wiki_formatting/common_mark/formatter_test.rb | ||
|---|---|---|
| 163 | 163 |
# 0 |
| 164 | 164 |
<<~STR.chomp, |
| 165 | 165 |
# Title |
| 166 |
|
|
| 166 | ||
| 167 | 167 |
Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Maecenas sed libero. |
| 168 | 168 |
STR |
| 169 | 169 |
# 1 |
| 170 | 170 |
<<~STR.chomp, |
| 171 | 171 |
## Heading 2 |
| 172 |
|
|
| 172 | ||
| 173 | 173 |
~~~ruby |
| 174 | 174 |
def foo |
| 175 | 175 |
end |
| 176 | 176 |
~~~ |
| 177 |
|
|
| 177 | ||
| 178 | 178 |
Morbi facilisis accumsan orci non pharetra. |
| 179 |
|
|
| 179 | ||
| 180 | 180 |
~~~ ruby |
| 181 | 181 |
def foo |
| 182 | 182 |
end |
| 183 | 183 |
~~~ |
| 184 |
|
|
| 184 | ||
| 185 | 185 |
``` |
| 186 | 186 |
Pre Content: |
| 187 |
|
|
| 187 | ||
| 188 | 188 |
## Inside pre |
| 189 |
|
|
| 189 | ||
| 190 | 190 |
<tag> inside pre block |
| 191 |
|
|
| 191 | ||
| 192 | 192 |
Morbi facilisis accumsan orci non pharetra. |
| 193 | 193 |
``` |
| 194 | 194 |
STR |
| 195 | 195 |
# 2 |
| 196 | 196 |
<<~STR.chomp, |
| 197 | 197 |
### Heading 3 |
| 198 |
|
|
| 198 | ||
| 199 | 199 |
Nulla nunc nisi, egestas in ornare vel, posuere ac libero. |
| 200 | 200 |
STR |
| 201 | 201 |
] |
| ... | ... | |
| 299 | 299 |
assert_equal expected.gsub(%r{[\r\n\t]}, ''), to_html(text).gsub(%r{[\r\n\t]}, '').rstrip
|
| 300 | 300 |
end |
| 301 | 301 | |
| 302 |
def test_should_render_alert_blocks |
|
| 303 |
text = <<~MD |
|
| 304 |
> [!note] |
|
| 305 |
> This is a note. |
|
| 306 | ||
| 307 |
> [!tip] |
|
| 308 |
> This is a tip. |
|
| 309 | ||
| 310 |
> [!warning] |
|
| 311 |
> This is a warning. |
|
| 312 | ||
| 313 |
> [!caution] |
|
| 314 |
> This is a caution. |
|
| 315 | ||
| 316 |
> [!important] |
|
| 317 |
> This is a important. |
|
| 318 |
MD |
|
| 319 | ||
| 320 |
html = to_html(text) |
|
| 321 |
%w[note tip warning caution important].each do |alert| |
|
| 322 |
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 |
|
| 323 |
end |
|
| 324 |
end |
|
| 325 | ||
| 326 |
def test_should_not_render_unknown_alert_type |
|
| 327 |
text = <<~MD |
|
| 328 |
> [!unknown] |
|
| 329 |
> This should not become an alert. |
|
| 330 |
MD |
|
| 331 | ||
| 332 |
html = to_html(text) |
|
| 333 | ||
| 334 |
assert_include "<blockquote>", html |
|
| 335 |
assert_include "[!unknown]", html |
|
| 336 |
assert_include "This should not become an alert.", html |
|
| 337 | ||
| 338 |
assert_not_include 'markdown-alert', html |
|
| 339 |
end |
|
| 340 | ||
| 302 | 341 |
private |
| 303 | 342 | |
| 304 | 343 |
def assert_section_with_hash(expected, text, index) |
| test/unit/lib/redmine/wiki_formatting/common_mark/sanitization_filter_test.rb | ||
|---|---|---|
| 71 | 71 |
assert_equal %(<code>foo</code>), filter(input) |
| 72 | 72 |
end |
| 73 | 73 | |
| 74 |
def test_should_allow_valid_alert_div_and_p_classes |
|
| 75 |
html = <<~HTML |
|
| 76 |
<div class="markdown-alert markdown-alert-tip"> |
|
| 77 |
<p class="markdown-alert-title">Tip</p> |
|
| 78 |
<p>Useful tip.</p> |
|
| 79 |
</div> |
|
| 80 |
HTML |
|
| 81 | ||
| 82 |
sanitized = filter(html) |
|
| 83 | ||
| 84 |
assert_include 'class="markdown-alert markdown-alert-tip"', sanitized |
|
| 85 |
assert_include 'class="markdown-alert-title"', sanitized |
|
| 86 |
end |
|
| 87 | ||
| 88 |
def test_should_remove_invalid_div_class |
|
| 89 |
html = '<div class="bad-class">Text</div>' |
|
| 90 |
sanitized = filter(html) |
|
| 91 |
refute_include 'bad-class', sanitized |
|
| 92 |
end |
|
| 93 | ||
| 94 |
def test_should_remove_invalid_p_class |
|
| 95 |
html = '<p class="bad-class">Text</p>' |
|
| 96 |
sanitized = filter(html) |
|
| 97 |
assert_not_include 'bad-class', sanitized |
|
| 98 |
end |
|
| 99 | ||
| 74 | 100 |
def test_should_allow_links_with_safe_url_schemes |
| 75 | 101 |
%w(http https ftp ssh foo).each do |scheme| |
| 76 | 102 |
input = %(<a href="#{scheme}://example.org/">foo</a>)
|