Task Issue 1298 ReqView Relation Management - sebiboga/testlink-upgraded GitHub Wiki
Restoration of the legacy Relations block on the modernized Requirement Viewer — GitHub issue #1298.
Path: Requirements → Requirement Specification → (requirement) → Relations
URL: gui/templates/requirements/reqView.html?id=<req_id>&tproject_id=<id>
BFF API: api/requirements/index.php — GET /view, POST /relation, DELETE /relation
Gates: req_cfg->relations->enable, legacy $isAlien (req_relations.rw),
viewed version not frozen
Modernized screen: Requirement Viewer (gui/templates/requirements/reqView.html)
backed by api/requirements/index.php. Refs
#1298.
Legacy TestLink 1.9.20 lets you wire requirements to each other straight from the
Requirement Viewer, in a "Relations" block
(gui/templates/dashio/requirements/reqViewVersions.tpl:323-419):
| Legacy control | Legacy source |
|---|---|
"New relation" row: relation-type select + destination req_doc_id input + optional cross-project select + Add
|
reqViewVersions.tpl:340-365 |
validate_req_docid_input() client-side check |
reqViewVersions.tpl:110-121, wired at :327
|
| per-row trash icon → confirm dialog → delete | reqViewVersions.tpl:408-419 |
disabled-delete image with img_title_relation_frozen tooltip |
reqViewVersions.tpl:415-418 |
7-column list (#, type, document, status, project, set-by, delete) |
reqViewVersions.tpl:368-404 |
title="Created <ts> by <author>" tooltip on the author |
reqViewVersions.tpl:404 |
relation_add_result_msg feedback line |
reqViewVersions.tpl:334-338 |
Write handling lived in lib/requirements/reqCommands.class.php:
doAddRelation() (:666-733) and doDeleteRelation() (:745-769), both reached
by POSTing to reqEdit.php with a doAction field.
The modern screen only ported the read projection: a 4-column DataTable
(Relation | Target | Project | Status) with no action column, no add form and
no write route at all (grep "segments[0] === 'relation" on the BFF → no
match).
The read side was lossy too — relation_id, the author column and the creation
tooltip were dropped, and the Project column was rendered unconditionally even
though legacy shows it only with interproject_linking on.
Worst of all: renderRelations() hid the whole card when the list was empty.
Legacy renders the add row outside the {if $gui->req_relations.num_relations}
guard (reqViewVersions.tpl:340 vs :368), so on a fresh requirement the modern
screen offered no way at all to create the first relation.
-
GET /viewnow also returns-
grant.req_relations_rw— legacy$gui->req_relations['rw'] = !$isAlien(reqView.php:97-99, 222): relations are read-only when the screen was reached through atproject_idthat does not own the requirement, -
relation_config—enabled,interproject_linking, thetypesdropdown (port ofrequirement_mgr::init_relation_type_select(),requirement_mgr.class.php:2779-2815) and the cross-projectprojectsselector (port ofreqView.php::initTestprojectSelect()), -
relations[].author,relations[].creation_ts,relations[].is_openandrelations[].can_delete(the legacy trash-icon gate).
-
-
POST /relation— port ofdoAddRelation(). Reproduces the whole error chain in order: destination doc id lookup on the (optionally cross-) destination project →_destinationsuffix swaps source and destination → self-relation →check_if_relation_exists()→ destination's last version must be open →add_relation(). Every branch returns the legacylang_get()text plus amessage_keyso the client can localize it. -
DELETE /relation— port ofdoDeleteRelation(). The relation id must actually hang on the requirement being viewed, so a forged/foreign id cannot delete somebody else's link. - Both verbs re-check server-side, never trusting the form: relations enabled,
!$isAlien(403) and the viewed version not frozen (403). The UI gates are cosmetic; the server is the authority.
- "New relation" button in the Relations card title → Dashio modal with the type
select, the destination doc-id input and the cross-project selector (shown only
with
interproject_linkingon, like legacy). - The table is now the 7 legacy columns;
Projecthides itself when inter-project linking is off.Set bykeeps theCreated <ts> by <author>tooltip. - Per-row trash icon → confirm modal (
Really delete relation #N?) →DELETE /relation. When deletion is not permitted the icon renders greyed withcursor:not-allowedand the read-only/frozen tooltip, mirroring$tlImages.delete_disabled+img_title_relation_frozen. - The card is shown whenever relations are enabled, even with zero relations — the blocker that made the first relation impossible to create.
29 new keys × 10 bundles (de, en, es, fr, it, ja, pt, ro, ru, zh). Translations
come from the matching legacy locale/<L>/strings.txt entries where they exist.
This includes rel_add_error_dest_frozen, which the legacy tree defines in only
5 of its 19 locale files (en_GB, fr_FR, ja_JP, pt_BR, pt_PT) — a de,
es, it, ro, ru, zh or en_US user got the raw key printed by
lang_get(), and now gets a real message.
-
The
_source/_destinationsuffix in the dropdown value is load-bearing, not a display artifact:doAddRelation()doesstrpos($relType, '_destination')to reverse the direction (reqCommands.class.php:694-698). The existing/searchrelation_typeslist collapses equal relations to bare numeric ids, so the viewer needs its own list — that is whyrelation_config.typesis built separately instead of reusing it. -
Requirement doc IDs are free-form strings, not numbers.
getByDocID()comparesreq_doc_idwith a string=, andvalidate_req_docid_input()only rejects an empty/whitespace value or the untouched placeholder label. A numeric check (the natural first guess) breaks the feature on any real dataset. -
An "equal" relation type appears once in the dropdown.
parent of/child ofandblocks/depends onare asymmetric and get two entries;related tois symmetric and gets one, preselected. - The
rwgate is about project ownership, not roles: an admin viewing requirement 7 through the URL of another project gets a read-only relations block. That is$isAlien, and the modern screen reproduces it.
25/25 cases in tmp/TLU_Test_Cases.md suite 976 (all PASS), covering: the
empty-state add affordance, dropdown contents, the add round-trip, the mirrored
direction seen from the other requirement, the delete confirm round-trip, all 7
legacy error branches, the $isAlien and frozen-version gates on both the UI and
the server, the forged-relation-id rejection, the Romanian locale, XSS escaping of
a hostile requirement title, syntax gates, and the Event Viewer.
events gained no new Error/Warning after the fixes; every row written afterwards
is log_level=16 (audit).
- Coverage / test-case link management, the sibling feature:
docs/Issue-1299-reqView-Coverage-Link-Management.md.