Defect #43730 » patch-issue-43730-fix-collapse-macro-invalid-html.diff
| app/helpers/application_helper.rb | ||
|---|---|---|
| 949 | 949 |
send method_name, txt, project, obj, attr, only_path, options |
| 950 | 950 |
end |
| 951 | 951 |
end |
| 952 |
unwrap_paragraphs_containing_block_macros(text) |
|
| 952 | 953 |
parse_headings(text, project, obj, attr, only_path, options) |
| 953 | 954 | |
| 954 | 955 |
if @parsed_headings.any? |
| ... | ... | |
| 1468 | 1469 |
end |
| 1469 | 1470 |
end |
| 1470 | 1471 | |
| 1472 |
# Fixes invalid HTML where block-level elements from macros (e.g. collapse) |
|
| 1473 |
# are nested inside <p> tags. Replaces the <p></p> with <div></div> so that |
|
| 1474 |
# consecutive paragraphs remain visually separated as block-level elements. |
|
| 1475 |
# Uses a pattern that treats <div class="collapsed-text">...</div> as atomic |
|
| 1476 |
# so that </p> inside the div's content does not end the outer match early. |
|
| 1477 |
def unwrap_paragraphs_containing_block_macros(text) |
|
| 1478 |
collapsed_div = /<div[^>]*class="[^"]*collapsed-text[^"]*"[^>]*>(?:(?!<\/div>).)*<\/div>/m |
|
| 1479 |
non_collapsed = /(?!<\/p>)(?!<div[^>]*class="[^"]*collapsed-text)./m |
|
| 1480 |
text.gsub!( |
|
| 1481 |
/<p>(#{non_collapsed}*(?:#{collapsed_div}#{non_collapsed}*)+)<\/p>/m,
|
|
| 1482 |
'<div>\1</div>' |
|
| 1483 |
) |
|
| 1484 |
end |
|
| 1485 | ||
| 1471 | 1486 |
TOC_RE = /<p>\{\{((<|<)|(>|>))?toc\}\}<\/p>/i unless const_defined?(:TOC_RE)
|
| 1472 | 1487 | |
| 1473 | 1488 |
# Renders the TOC with given headings |
| test/unit/lib/redmine/wiki_formatting/macros_test.rb | ||
|---|---|---|
| 314 | 314 |
end |
| 315 | 315 |
end |
| 316 | 316 | |
| 317 |
COLLAPSE_INVALID_HTML_RE = /<p>(?:(?!<\/p>).)*<div[^>]*class="[^"]*collapsed-text/m |
|
| 318 | ||
| 319 |
# Example 1: text between collapses in the same paragraph |
|
| 320 |
def test_macro_multiple_collapse_with_text_between_should_not_generate_div_inside_p |
|
| 321 |
text = <<~RAW |
|
| 322 |
1 {{collapse(1)
|
|
| 323 |
content 1 |
|
| 324 |
}} |
|
| 325 |
2 {{collapse(2)
|
|
| 326 |
content 2 |
|
| 327 |
}} |
|
| 328 |
3 {{collapse(3)
|
|
| 329 |
content 3 |
|
| 330 |
}} |
|
| 331 |
RAW |
|
| 332 |
with_locale 'en' do |
|
| 333 |
with_settings :text_formatting => 'textile' do |
|
| 334 |
assert_no_match(COLLAPSE_INVALID_HTML_RE, textilizable(text)) |
|
| 335 |
end |
|
| 336 |
end |
|
| 337 |
end |
|
| 338 | ||
| 339 |
# Example 2: adjacent collapses with no text between |
|
| 340 |
def test_macro_multiple_collapse_adjacent_should_not_generate_div_inside_p |
|
| 341 |
text = <<~RAW |
|
| 342 |
{{collapse(1)
|
|
| 343 |
content 1 |
|
| 344 |
}} |
|
| 345 |
{{collapse(2)
|
|
| 346 |
content 2 |
|
| 347 |
}} |
|
| 348 |
{{collapse(3)
|
|
| 349 |
content 3 |
|
| 350 |
}} |
|
| 351 |
RAW |
|
| 352 |
with_locale 'en' do |
|
| 353 |
with_settings :text_formatting => 'textile' do |
|
| 354 |
assert_no_match(COLLAPSE_INVALID_HTML_RE, textilizable(text)) |
|
| 355 |
end |
|
| 356 |
end |
|
| 357 |
end |
|
| 358 | ||
| 359 |
# Example 3: multiple groups of collapses separated by a blank line |
|
| 360 |
def test_macro_multiple_collapse_groups_should_not_generate_div_inside_p |
|
| 361 |
text = <<~RAW |
|
| 362 |
1 {{collapse(1)
|
|
| 363 |
content 1 |
|
| 364 |
}} |
|
| 365 |
2 {{collapse(2)
|
|
| 366 |
content 2 |
|
| 367 |
}} |
|
| 368 | ||
| 369 |
3 {{collapse(3)
|
|
| 370 |
content 3 |
|
| 371 |
}} |
|
| 372 |
4 {{collapse(4)
|
|
| 373 |
content 4 |
|
| 374 |
}} |
|
| 375 |
RAW |
|
| 376 |
with_locale 'en' do |
|
| 377 |
with_settings :text_formatting => 'textile' do |
|
| 378 |
assert_no_match(COLLAPSE_INVALID_HTML_RE, textilizable(text)) |
|
| 379 |
end |
|
| 380 |
end |
|
| 381 |
end |
|
| 382 | ||
| 317 | 383 |
def test_macro_child_pages |
| 318 | 384 |
expected = |
| 319 | 385 |
"<p><ul class=\"pages-hierarchy\">\n" \ |