The notification-row partial and the mention email's "see in center"
button built their /notifications/details/<id> links from the numeric
primary key, so mention, digest and reminder emails still linked to the
numeric URL on a semantic-identifier instance.
The split view already resolves the id through find_by_display_id, which
accepts both the numeric id and the semantic identifier, so handing it
the work package lets to_param render the display id. Also drops the
unused split_view_work_package_id helper, whose to_i wrongly suggested
the route only accepts a numeric id.
`format_text` accepts `render_mode:` (`:in_app_html`, `:external_html`,
`:external_text`), which resolves the `only_path`, `static_html` and
`plain_text` context flags as a set. External surfaces (mailer HTML
body, future RSS/PDF/webhook) need absolute URLs and static rendering
together; pinning the trio at the public API keeps callers from
forgetting one. Explicit primitive kwargs still override.
`MailFormattingHelper` exposes `format_mail_html` and `format_mail_text`
thin wrappers around `format_text(render_mode:)`. The `_html` / `_text`
suffix matches the `.html.erb` / `.text.erb` template extension so
caller intent stays visible in the view, with no introspection of
`formats`.
The five WorkPackageMailer view sites use the helpers; `_work_package_details`,
`mentioned.html`, `mentioned.text`, `watcher_changed.html`, `watcher_changed.text`
drop the `static_html:`/`only_path:`/`plain_text:` boilerplate.
Same pattern as the static-HTML collapse: the `markdown_as_text` format
symbol was a thin subclass setting a context flag and swapping the filter
list. Replace it with `plain_text: true` on the existing rich formatter,
which now picks between `RICH_FILTERS` and `TEXT_FILTERS` constants based
on the flag. `static_html:` and `plain_text:` now sit as peer options on
one format.
Rename the `as_text` context key to `plain_text` for symmetry with
`static_html`. Update both mailer `.text.erb` views and the two handler
predicates that branch on the flag.
`:rich` is the default at every layer of the `format_text` chain
(view helper → `OpenProject::TextFormatting#format_text` →
`Renderer.format_text`), so the explicit keyword adds noise.
The static-HTML pipeline differs from the rich pipeline only by a
context flag - both share the same filter chain. The dedicated
`Markdown::StaticHtmlFormatter` and `:markdown_as_static_html`
format symbol were pure boilerplate around that one-line override.
Callers now pass `format: :rich, static_html: true` and the matchers
read `context[:static_html]` directly.
The `##N` and `###N` work-package macros emit JS-hydrated
`<opce-macro-wp-quickinfo>` custom elements, which mail clients
collapse to empty bullets. Introduce a `:markdown_as_static_html`
format that shares the rich filter chain but signals
`context[:as_static_html]` so the matcher and `MentionFilter` emit a
server-rendered anchor — formatted_id, type name, subject, and (for
`###`) status name — closely mirroring the in-app widget once
flattened.
Mailer HTML templates (`mentioned`, `watcher_changed`,
`_work_package_details`) opt into the new format. Invisible WPs still
render as plain-text labels, matching the cross-project visibility
policy.
`ResourceLinksMatcher.build_cache` and
`MentionFilter#preload_work_package_mentions` eager-load `:type` and
`:status` only when `:as_static_html` is set, leaving the default web
path's two-SELECT shape untouched. Classic-mode preload now also runs
under `:as_static_html` so the link handler can resolve type/subject
for `##`/`###`.
Renames the internal flag `context[:plain_text]` to `context[:as_text]`
to restore symmetry with the user-facing `:markdown_as_text` format.
The format runs the full markdown pipeline and then collapses the DOM
to text — it has nothing to do with the existing `:plain` format,
which strips markdown entirely. Moves the formatter under the Markdown
namespace next to the rich-output formatter whose pipeline it mirrors,
and renames the symbol so the relationship is legible from the
formatter_for case clause.
The `mentioned` and `watcher_changed` text-mailer bodies surfaced raw
journal markdown — numeric `#42` references stayed numeric in semantic
mode, and `<mention>` envelopes leaked as HTML source.
Introduces `:plain_text` as a sibling format inside the existing Plain
module. The filter chain mirrors the markdown pipeline (markdown,
sanitization, mention, pattern-matcher) and finishes with a new
`PlainTextOutputFilter` that collapses the DOM to text. The
`WorkPackages` link handler and `MentionFilter` get plain-text branches
keyed off `context[:plain_text]` so identifier resolution stays in one
place across rich and plain channels.
Closes https://community.openproject.org/wp/74762