2506 Commits

Author SHA1 Message Date
Alexander Brandon Coles ae83523492 [chore] cover help texts for filterable tree view
Enables the shared help-text examples for the new tree view input.
2026-06-11 14:38:03 +01:00
Alexander Brandon Coles a3507d8883 [chore] render filterable tree view in fieldset
Fieldset-style inputs never go through the `FormControl` wrapper, so the
legend needs the `FormControl-label` class applied by the component. The
input does not support validation for the time being.

Wrapper arguments stay on the fieldset, while the component-specific and
form arguments flow to the inner tree view.
2026-06-11 14:31:33 +01:00
Eric Schubert 0be3025f64 [chore] add unit tests to new dsl input 2026-06-11 14:29:52 +01:00
Oliver Günther f389bb1c9b Merge pull request #23629 from opf/fix/meeting-backlog-section
Expose the backlog section visibly through the meetings API, ensuring it appears
2026-06-10 14:06:40 +02:00
Oliver Günther cd5ceba958 Allow embed_links to be parameterized, controlling which elements should be embedded 2026-06-10 13:43:06 +02:00
Oliver Günther d851d25524 Merge remote-tracking branch 'origin/release/17.5' into dev 2026-06-10 11:29:44 +02:00
OpenProject Actions CI 24be36fce6 Merge branch 'release/17.4' into release/17.5 2026-06-09 05:55:31 +00:00
Oliver Günther 7bf32598ed Also use message_pack for ConfidentialCache
YAML default coder uses aliases which break on safe_load
2026-06-09 07:54:33 +02:00
Jan Sandbrink 663dc3f182 Merge pull request #23480 from opf/httpx-ssrf-filter
Add SSRF filter for HTTPX
2026-06-04 07:19:42 +02:00
Jan Sandbrink 294611cc59 Add SSRF filter for HTTPX
Filtering in front of HTTPX calls is less secure, because it's vulnerable to
DNS rebinding. In addition to that it's also duplicate work, because all affected
callsites would have to make sure to "remember" SSRF filtering.

This SSRF filter is inspired by the original HTTPX SSRF Filter, but using our custom
IP address matcher that allows to configure safe IP addresses or ranges.
2026-06-03 09:56:48 +02:00
Alexander Brandon Coles 53fad52076 Add spec coverage for segmented control form input
Covers the SegmentedControl DSL input behind boolean filter rows: label
and button rendering, the hidden value field, value defaulting to the
first item, and wrapper data attributes.
2026-06-02 19:52:15 +02:00
OpenProject Actions CI 523ef39065 Merge branch 'release/17.5' into dev 2026-06-01 14:42:29 +00:00
Oliver Günther 64422d3c63 Merge remote-tracking branch 'origin/release/17.4' into release/17.5 2026-06-01 13:57:42 +02:00
Oliver Günther 38ab5af1ff Merge remote-tracking branch 'origin/release/17.3' into release/17.4 2026-06-01 13:57:33 +02:00
Oliver Günther 9ed3392dae Better whitelisting of allowed macros and data-attributes 2026-06-01 13:57:18 +02:00
OpenProject Actions CI b2f72fd486 Merge branch 'release/17.5' into dev 2026-05-30 04:49:04 +00:00
Tom Hykel 1a22f59d21 Merge pull request #23367 from opf/fix/74942-numeric-id-instead-of-semantic-one-on-th
[#74942] fix(ui): Support semantic identifiers on related work packages table
2026-05-29 13:10:37 +02:00
Oliver Günther a852d46cb6 Be more cautious when parsing charset from file 2026-05-29 10:30:07 +02:00
Oliver Günther 6f63faeed1 Extract and use charset to properly encode attachments 2026-05-29 10:26:43 +02:00
OpenProject Actions CI 49b802ca02 Merge branch 'release/17.5' into dev 2026-05-28 08:51:09 +00:00
Tomas Hykel cee98d16bb fix(ui): Numeric ID instead of semantic one on the table of related work packages (WP #74942) 2026-05-27 15:03:40 +02:00
Kabiru Mwenja 499d7820a2 Add render_mode flag and MailFormattingHelper
`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.
2026-05-27 13:04:26 +03:00
Kabiru Mwenja f5957d800f Collapse plain-text formatter into a context option
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.
2026-05-26 22:10:29 +03:00
OpenProject Actions CI a147cf2168 Merge branch 'release/17.5' into dev 2026-05-26 13:14:53 +00:00
Kabiru Mwenja 94c13c11fa Collapse static-HTML formatter into a context option
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.
2026-05-26 14:50:59 +03:00
Kabiru Mwenja 23d52fcf1c Drop semantic_work_package_ids flag annotations
The feature flag is gone on release/17.5 (PR #23324); the
`with_settings: { work_packages_identifier: ... }` annotation alone
is enough to pin classic vs semantic behaviour in each context.
2026-05-26 14:24:03 +03:00
Kabiru Mwenja e70b9ab21c Tighten mention pipeline: helper extraction, principal preload, coverage
Lift the static-anchor label composition out of LinkHandlers and into
a small Helpers::StaticMacroLabel module so the envelope path
(MentionFilter) and the text-reference path (LinkHandlers) share one
shape — same module called from both, no cross-class reach-through.

Batch the User and Group mention preloads alongside the existing WP
preload so a note with N principals costs one SELECT per type rather
than N. Class lookup now reads from indexed hashes; visibility-gating
stays where it was (at the find for principals, separate from the
label for WPs).

Rename SemanticIdentifier.format → with_hash_prefix; the prior name
was broad enough to invite misuse for arbitrary work-package values.
Override StaticHtmlFormatter#filters explicitly so a future filter
appearing in Formatter#filters is a deliberate decision to apply to
mailer-side rendering, not an automatic one.

Spec coverage: classic-mode quickinfo and inaccessible-WP paths
(symmetric with the existing semantic-mode contexts), a principal
preload N+1 guard, and an anonymous current_user smoke test that
confirms the static-HTML pipeline doesn't raise when invoked without
an authenticated viewer.
2026-05-26 14:24:02 +03:00
Kabiru Mwenja ee4c9aee59 Render WP quickinfo macros as static HTML in mailer notes
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.
2026-05-26 14:19:23 +03:00
Kabiru Mwenja a54dd3dc0b Rename :plain_text format to :markdown_as_text
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.
2026-05-26 14:19:11 +03:00
Kabiru Mwenja 5d8929a7c3 Resolve invisible WP mentions to their current formatted_id
The mention filter previously dropped to the envelope's stored text
when the recipient lacked view permission on the referenced work
package, which left stale identifiers in mailer bodies after a
project rename and diverged from the `#N` text-reference path on
the same render.

Adopts the two-SELECT pattern ResourceLinksMatcher uses for `#N`
references: a single unscoped batched lookup for label resolution
plus a visibility-scoped id pluck for anchor gating. Invisible WPs
render as plain text with the current formatted_id; the per-mention
`WorkPackage.visible.find_by` is gone.
2026-05-26 14:19:11 +03:00
Kabiru Mwenja 8d9aa18ad3 Collapse work-package preload state into one cache value object
Pairs unscoped label resolution and viewer-scoped link gating in a
WorkPackagePreloadCache instead of two RequestStore keys with a
five-method save/restore protocol. Exposes one `current_cache` reader;
consumers ask the cache directly via `fetch` and `visible?`.

Extracts a `text_only?` predicate in the WP link handler so the
`context[:plain_text]` and invisible-WP guards collapse into a single
call site. `SemanticIdentifier.format` renames its parameter to
reflect that the input may or may not be semantic.
2026-05-26 14:19:10 +03:00
Kabiru Mwenja 878048f8e8 Resolve WP labels across visibility boundaries in text macros
The macro preload was visibility-scoped — references to work packages the
recipient cannot see would fall through to the literal `#43` shape, even
when the same reference rendered as `DCP-1` for an author with full view
permission. Notification recipients saw misleading numeric ids for cross-
project references in journal notes.

Splits label resolution from link gating:

- `ResourceLinksMatcher.build_lookup` now does an unscoped fetch for the
  primary identifier and a separate visibility-scoped id pluck. The link
  handler reads `visible_to_current_user?` to decide between a navigable
  anchor and a plain-text label.
- `UpdateAncestorsService#set_journal_note` writes `#display_id` so new
  notes carry the semantic shape at the source; render-time resolution
  heals legacy `#N` content for users with view permission.

Tradeoff: a recipient without view permission now sees the WP's semantic
identifier (e.g. `DCP-1`) as plain text rather than `#43`. The reference's
existence was already disclosed by the stored journal text; the project
identifier is the only new piece of information surfaced, and is not
treated as a secret elsewhere in the system (URLs, exports, API).
2026-05-26 14:18:48 +03:00
Kabiru Mwenja 7c9d15e506 Render WP identifiers per current mode in plain-text mailer notes
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
2026-05-26 14:18:17 +03:00
David F 542792764e Add input_methods spec for segmented_control. wp/74380
As per review feedback from myabc.
2026-05-26 08:14:56 +02:00
OpenProject Actions CI 1e62f01690 Merge branch 'release/17.4' into release/17.5 2026-05-26 05:10:56 +00:00
Oliver Günther 61cd01b784 Add associated_visible_resource to properly handle undisclosed work packages 2026-05-26 07:09:59 +02:00
Tomas Hykel 948fa43321 chore: Remove feature flag for project-based work package identifiers 2026-05-25 17:45:06 +02:00
OpenProject Actions CI 9a74b5356b Merge branch 'release/17.4' into release/17.5 2026-05-22 08:36:48 +00:00
Oliver Günther d6d7abea19 Merge pull request #23304 from opf/fix/message-pack-safe-buffer-serialization
Allow serialization of safebuffer without encoding issues
2026-05-22 10:35:50 +02:00
Kabiru Mwenja b181e1fde1 Follow-up refactors for the text-macro preload cache (#23285)
* Scope SQL log assertions to target SELECTs in alias-fold-in spec

The N+1 guard was counting the entire QueryRecorder log, which made it
brittle against any incidental Setting/permission query that landed on
the same render path. Switch to scoped greps against the two SELECTs
we actually care about: the work_packages batched query and the
sidecar alias pluck. A regression on either now fails with a clear
message pointing at the offending source.

* Flatten matcher preload wrapping into an iterative fold

The recursive shift-and-recurse shape mutated a duplicated array and
forwarded an anonymous block at every frame, which obscured what the
loop was actually doing: wrap each opt-in matcher's preload hook
around the inner block, first matcher outermost. The iterative form
walks the matcher list once in reverse and rebinds a lambda, so the
nesting order is visible without unwinding a recursion.

* Make the WP preload cache's stringified-key invariant explicit

Both ends of the cache assumed every key was a string, but the
contract lived only in the read site's `identifier.to_s` and an
ambient confidence that the identifier column is text. Normalize at
write time too, swap the safe-navigation lookup for `dig`, and leave
a one-line note at the canonical builder so a future reader doesn't
have to grep the call sites to convince themselves a numeric input
will resolve.
2026-05-20 21:36:09 +03:00
Kabiru Mwenja e2631598c2 Merge pull request #23204 from opf/feature/editor-mention-semantic-id
Render work-package mentions with the current display identifier
2026-05-20 17:26:50 +03:00
Jens Ulferts 0ceaa2617c Merge pull request #23181 from opf/code-maintenance/74768-remove-calculated_value_project_attribute-feature-flag
[#74768] Remove calculated_value_project_attribute feature flag
2026-05-20 16:09:46 +02:00
Oliver Günther 274f7c6e3d Merge pull request #23230 from opf/feat/monthly-meeting-option
Meeting series: Add monthly scheduling options
2026-05-20 16:02:36 +02:00
Kabiru Mwenja 70931762d9 Merge remote-tracking branch 'origin/dev' into feature/editor-mention-semantic-id
# Conflicts:
#	frontend/src/vendor/ckeditor/ckeditor.js
#	frontend/src/vendor/ckeditor/ckeditor.js.map
2026-05-20 16:33:47 +03:00
Oliver Günther 6295d90346 Add monthly scheduling options 2026-05-20 15:20:16 +02:00
Kabiru Mwenja a1440eb9bb Merge pull request #23221 from opf/feature/text-macro-preload-cache
Implement doc-level preload cache for WP text macros
2026-05-20 13:19:59 +03:00
Kabiru Mwenja 559d56ae16 Scope text-macro preload cache to WorkPackage.visible
The lookup cache in `ResourceLinksMatcher` resolved `#42` / `#PROJ-1`
/ `#OLDPROJ-1` references without any permission check, so the link
handler could render `formatted_id` / `display_id` for work packages
the current user had no read access to. Both the main query and the
historical-alias pluck now scope through `WorkPackage.visible`,
matching `MentionFilter` and the other link handlers.

The historical-alias query-count spec now asserts statement count
rather than table-name grep — the visibility CTE references
`work_packages` in both statements, so the old regex over-matched.

Quickinfo macro emission for `##PROJ-1` / `###PROJ-1` is unchanged
and queued as a separate PR. The data-id only echoes user input and
the API endpoints enforce auth, but the Angular custom element still
bootstraps and renders a "not found" error chip for inaccessible
WPs. Fixing that cleanly needs the cache to distinguish `denied`
from `absent`.

Refs #23221.
2026-05-20 12:03:22 +03:00
Oliver Günther e396a5bb53 Merge remote-tracking branch 'origin/release/17.4' into dev 2026-05-19 18:26:45 +02:00
Oliver Günther 637ec7d10f Merge remote-tracking branch 'origin/release/17.3' into release/17.4 2026-05-19 18:26:23 +02:00
Ivan Kuchin 00e06f4958 test more custom field format methods 2026-05-19 17:42:46 +02:00