Files
openproject/lib
Kabiru Mwenja 8bddd2d24b Accept semantic work-package identifiers in text macros
`#1234` text macros render `formatted_id`/`display_id` already, but the
*input* side still requires the numeric primary key. Authors typing or
pasting `#PROJ-1` (or `##PROJ-1` / `###PROJ-1`) in a comment, WP
description, or meeting body would see literal text rather than a
resolved link.

Three changes that move together:

1. `ResourceLinksMatcher.regexp` — the hash-separator branch now accepts
   either the numeric shape `\d+` or the semantic shape
   `[A-Z][A-Z0-9_]*-\d+`, mirroring `WorkPackage::SemanticIdentifier::
   ID_ROUTE_CONSTRAINT`. The revision branch (`r\d+`) stays numeric-only
   via a separate alternation. `parse_match` is the single site that
   maps the new regex group indices to semantic field names; everything
   else flows from there.

2. `LinkHandlers::WorkPackages#call` — splits into a numeric path
   (preserving the leading-zero rejection from before) and a semantic
   path. Semantic-shape input only links when `semantic_mode_active?` is
   true; classic instances render literal text. Plain `#PROJ-N` requires
   a cache hit (literal-text fallback when missing); `##PROJ-N` /
   `###PROJ-N` quickinfo elements emit unconditionally with `data-id`
   set to the user-facing identifier — APIv3 already resolves either
   shape, and the frontend Angular component handles missing WPs.
   Hover-card URLs now also speak `display_id` so the URL matches the
   user-facing identifier (the route accepts both shapes — see
   `HoverCardComponent#initialize`).

3. The preload cache extends to string keys via the
   `WorkPackage.where_display_id_in` batch finder added in #23016.
   `with_preloaded_resources` runs one WP SELECT for the common case
   (numerics + current semantic identifiers); historical alias
   references add a second targeted alias-table pluck, so an
   alias-heavy doc costs at most two round-trips per render.

Specs cover: `#PROJ-N` resolves with formatted_id / display_id href in
semantic mode; classic mode leaves it as literal text and issues zero
WP SELECTs; `##/###` quickinfo carries `display_id` in `data-id`; mixed
numeric+semantic resolves in 1 SELECT; alias references resolve in 2
round-trips; `#GHOST-99` falls through cleanly; nested `format_text`
calls preserve outer save/restore semantics.
2026-05-15 08:24:14 +03:00
..