Project

General

Profile

Actions

Defect #43730

open

Redmine generates invalid HTML when there are multiple `collapse` macros in one paragraph in Textile

Added by Artem Varaksa 4 months ago. Updated 8 days ago.

Status:
Confirmed
Priority:
Normal
Category:
Text formatting
Resolution:
Affected version:

Description

The issue has been observed last year on around January 21, 2025, though the behavior has been noted earlier. It's reproducible in the Redmine version on redmine.org and on 6+ versions as well.

Example 1 (one group - incorrect) + description

1 {{collapse(1)
1
}}
2 {{collapse(2)
2
}}
3 {{collapse(3)
3
}}

Resulting HTML

Formatted HTML

Validation with https://validator.w3.org/nu/#textarea highlights the following main issue:

Browsers fix these kinds of HTML issues (in this case, by opening the "p" tag in the end before its closure), but this is not the intended markup. All of these collapses should be inside a single "p" tag.

In Redmine, the result is this:

1 1


2 2
3 3

Screenshot:

I.e. the first collapse in a group is separated from others by a big blank space.

Workaround 1: splitting collapses by newlines. However, this results in a different markup - between each collapse, there will be a blank space (though, a smaller one) - and it's impossible to group several collapses together in one logical section.

Workaround 2: using a list. However, this is also a different markup that may not be always applicable and it increases logical complexity of the formatting.

See more examples below.

Example 2 (without extra text - incorrect)

{{collapse(1)
1
}}
{{collapse(2)
2
}}
{{collapse(3)
3
}}

Result:

1

2 3

Screenshot:

Example 3 (several groups - incorrect)

1 {{collapse(1)
1
}}
2 {{collapse(2)
2
}}
3 {{collapse(3)
3
}}

4 {{collapse(4)
4
}}
5 {{collapse(5)
5
}}
6 {{collapse(6)
6
}}

Result:

1 1


2 2
3 3

4 4


5 5
6 6

Screenshot:

Example 4: Workaround 1 (all separate - correct, but impossible to group)

1 {{collapse(1)
1
}}

2 {{collapse(2)
2
}}

3 {{collapse(3)
3
}}

Result:

1 1

2 2

3 3

Screenshot:

Example 5: Workaround 2 (list - correct)

* 1 {{collapse(1)
1
}}
* 2 {{collapse(2)
2
}}
* 3 {{collapse(3)
3
}}

Result:

Screenshot:


Files

clipboard-202601281242-jimgt.png (22.5 KB) clipboard-202601281242-jimgt.png Artem Varaksa, 2026-01-28 10:42
clipboard-202601281301-csq3d.png (2.53 KB) clipboard-202601281301-csq3d.png Artem Varaksa, 2026-01-28 11:01
clipboard-202601281302-i4mif.png (1.92 KB) clipboard-202601281302-i4mif.png Artem Varaksa, 2026-01-28 11:02
clipboard-202601281302-7ecn5.png (5.27 KB) clipboard-202601281302-7ecn5.png Artem Varaksa, 2026-01-28 11:02
clipboard-202601281302-aofew.png (2.93 KB) clipboard-202601281302-aofew.png Artem Varaksa, 2026-01-28 11:02
clipboard-202601281303-mfua2.png (2.95 KB) clipboard-202601281303-mfua2.png Artem Varaksa, 2026-01-28 11:03
patch-issue-43730-fix-collapse-macro-invalid-html.diff (3.45 KB) patch-issue-43730-fix-collapse-macro-invalid-html.diff Akihiro Kubota, 2026-05-06 10:05
after.png (342 KB) after.png Akihiro Kubota, 2026-05-06 10:05
priority_settings.png (71.1 KB) priority_settings.png Akihiro Kubota, 2026-05-06 10:05
before.png (353 KB) before.png Akihiro Kubota, 2026-05-06 10:05
after.png (191 KB) after.png Akihiro Kubota, 2026-05-06 12:02
before.png (182 KB) before.png Akihiro Kubota, 2026-05-06 12:02
Actions #1

Updated by Artem Varaksa 3 months ago

We were able to reproduce this behavior on a clean Redmine installation with the following configuration (changed formatting to "Textile" and adjusted the "Welcome text" field by adding the aforementioned examples):

Environment:
  Redmine version                6.1.1.stable
  Ruby version                   3.3.8-p144 (2025-04-09) [x86_64-linux]
  Rails version                  7.2.3
  Environment                    production
  Database adapter               Mysql2
  Mailer queue                   ActiveJob::QueueAdapters::AsyncAdapter
  Mailer delivery                smtp
Redmine settings:
  Redmine theme                  Default
SCM:
  Git                            2.50.1
  Filesystem                     
Redmine plugins:
  no plugin installed
Actions #2

Updated by Marius BĂLTEANU 3 months ago

  • Status changed from New to Confirmed
  • Assignee set to Marius BĂLTEANU
  • Target version set to 6.0.9

The problem is that the text (in your example, 1, 2 and 3) is wrapped in a p tag, the macro is wrapped in a div tag and div inside p is not valid.

Actions #3

Updated by Marius BĂLTEANU 3 months ago

  • Target version changed from 6.0.9 to Candidate for next minor release
Actions #4

Updated by Akihiro Kubota 8 days ago

I have created a patch for this issue. I noticed this is assigned to you, Marius, so please feel free to use or ignore this patch as you see fit.

Root cause:
As identified by Marius BĂLTEANU in comment #2, the collapse macro outputs a
block-level `<div>` element. When a collapse macro appears inline (surrounded by
text or adjacent to other macros in the same paragraph), Textile wraps the
surrounding content in a `<p>` tag, resulting in `<div>` nested inside `<p>`,
which is invalid HTML. This causes browsers to implicitly close the `<p>` tag,
producing large unwanted blank spaces between consecutive collapse macros.

Fix:
Added a post-processing step `unwrap_paragraphs_containing_block_macros` in
`textilizable` (application_helper.rb). After macros are injected into the
formatted HTML, this method finds any `<p>` tags that contain a
`<div class="collapsed-text">` and replaces the `<p></p>` wrapper with
`<div></div>`. Using `<div>` rather than simply stripping `<p>` preserves the
block-level boundary between consecutive paragraph groups, so that collapse
macros separated by a blank line remain visually distinct.

Examples from the issue:
- Example 1 (text between collapses in the same paragraph): fixed ✓
- Example 2 (adjacent collapses with no text between): fixed ✓
- Example 3 (two groups separated by a blank line): fixed ✓
- Example 4 (workaround: each collapse on its own paragraph): already worked, still works ✓
- Example 5 (workaround: list format): already worked, still works ✓
- Example 6 (single collapse): already worked, still works ✓

Tests added:
- `test_macro_multiple_collapse_with_text_between_should_not_generate_div_inside_p`
— Example 1: text between collapses in the same paragraph
- `test_macro_multiple_collapse_adjacent_should_not_generate_div_inside_p`
— Example 2: adjacent collapses with no text between them
- `test_macro_multiple_collapse_groups_should_not_generate_div_inside_p`
— Example 3: two groups of collapses separated by a blank line

Actions #5

Updated by Artem Varaksa 8 days ago

Thanks Akihiro Kubota, wanted to note that the files you've attached (besides the patch) seem to be related to a different issue?

Actions #6

Updated by Akihiro Kubota 8 days ago

Artem sviridov I'm so sorry that images I have attached were different.
these are true ones.
Is there a way to delete the old one?

Actions

Also available in: Atom PDF