Requirement Specification Management - sebiboga/testlink-upgraded GitHub Wiki
Modernized screen: Requirement Specification Management (ASIDE:
Requirements Design → Requirement Specification). Tracking issue
#571.
Legacy reference: lib/requirements/reqSpecListTree.php (feature
reqSpecMgmt, ExtJS tree) + reqSpecEdit.php / reqEdit.php popups.
Replaces the legacy ExtJS tree with a flat, searchable DataTables view of
every requirement specification of the active test project, plus a
per-specification requirements panel — all as a standalone Dashio page
backed by the JSON BFF api/reqspec/index.php.
| Column | Notes |
|---|---|
| Doc ID | specification document id |
| Title | click to open/close the requirements panel of that spec |
| Type | localized label from req_spec_cfg.type_labels
|
| Reqs | number of requirements linked to the spec |
| Expected | expected number of requirements (total_req) |
| Author | login of the latest revision author |
| Last Update | modification timestamp |
| Actions | edit pen + delete (only with modify rights), otherwise a read-only eye icon |
Opened by clicking a specification title:
| Column | Notes |
|---|---|
| Doc ID | requirement document id |
| Title | click to edit; hover shows plain-text scope excerpt |
| Status | color badge with localized status label (Draft/Review/Rework/Finish/Implemented/Valid/Not testable/Obsolete) |
| Type | localized requirement type |
| Ver | latest version number |
| Coverage | expected coverage |
| Author | version author login |
| Actions | edit pen + delete (modify rights only) |
- Create Spec — modal with Document ID*, Title*, Specification Type (localized domain), Expected number of requirements, Scope (HTML allowed).
- Create Requirement (in panel) — modal with Doc ID*, Title*, Status, Type, Expected coverage, Scope.
- Locale switcher (all 10 bundles).
- ASIDE entry switched in
lib/functions/common.php($actions->reqSpecMgmt→/gui/templates/requirements/reqSpecMgmt.html), same rights split as legacy: viewing needsmgt_view_reqormgt_modify_req; every write needsmgt_modify_req. Without rights the BFF answers HTTP 403 and the UI renders read-only. - Unauthenticated API calls get HTTP 401.
- Create attaches the spec to the test project node itself (same as the legacy root-level create); orphaned parent 0 would make the spec invisible to every tree walk (see #569 fix inside the BFF).
- Duplicate document ids inside one spec are rejected server-side with the legacy message ("Duplicated document id …").
- Spec delete uses
delete_deep()— removes contained requirements, versions and revisions, exactly like legacyreqSpecCommands::doDelete. - Requirement delete removes ALL versions (
requirement_mgr::delete()), like the legacy GUI action.



Requirements Filter Panel (#1025)
The 2.0.1 rewrite originally dropped the entire filter panel that 1.9.20
provides through gui/templates/dashio/include/inc_filter_panel.tpl +
tlRequirementFilterControl and get_filtered_req_map()
(lib/functions/treeMenu.inc.php:1753-1889). It has been ported end to end.
Document ID · Title · Status · Requirement Type · Req. Spec. Type · Expected No. of Testcases · Has Relation of Type · Test Case ID · project Custom Fields (opt-in). Plus Apply, Reset Filters, Simple / Advanced Filters and the "Update list after every operation" setting. Advanced turns the single selects into multi-selects; Simple warns before trimming a multi-selection.
Apply is authoritative — typing alone does not re-query, matching the legacy button-driven panel.

-
Anydisables the filter.0anywhere in a multi-select returnsnullfor that filter, i.e. unfiltered — not "match nothing". -
A bare Test Case prefix is not a filter.
filter_tc_id=REQA-is treated as "not filled in";REQA-101is a real filter. -
Expected coverage
0is not a filter; only a value> 0applies. - Subtree pruning. A specification whose own plus subtree requirements no longer match disappears from the list, exactly as legacy did.
-
Relation sides.
3/ "related to" is an equal relation and matches either side ofreq_relations;2_source/2_destinationare directional. Equal relations are published under the bare relation id, which is what legacyinit_relation_type_select()sends.
GET ?action=options&tproject_id=N -> also tcPrefix, childRequirementsManagement,
expectedCoverageManagement, filterCFields,
relationTypes, reqStatuses, reqTypes, specTypes
GET ?action=specs&tproject_id=N[&filter_*] -> filtered spec list
GET ?action=reqs&spec_id=N&tproject_id=N[&filter_*] -> filtered requirements
filter_doc_id, filter_title, filter_status, filter_type,
filter_spec_type, filter_relation, filter_coverage, filter_tc_id and
filter_cf_<fieldId> (comma-separated values allowed). All values are escaped
with $db->prepare_string(); an injection probe returns 0 rows with no error.
Filtering is evaluated on the latest spec revision / requirement version. Legacy matched any revision, which on the modern tables (built from the latest revision) could return rows the table never shows.
20 new rsf.* keys in all 10 locale bundles (de, en, es, fr, it, ja, pt, ro, ru, zh).
42 new keys (rs.*) added to all locale bundles: en, de, es, fr, it,
ja, pt, ro, ru, zh. Every label, column header, modal title and message is
translated; validated with python3 -m json.tool.
GET ?action=options&tproject_id=N -> domains, defaults, rights, project name
GET ?action=specs&tproject_id=N -> specs list (latest revision + req counts)
POST ?action=create_spec {tproject_id,doc_id,title,type,total_req,scope}
POST ?action=update_spec&id=N {…}
POST ?action=delete_spec&id=N&tproject_id=N
GET ?action=reqs&spec_id=N&tproject_id=N -> requirements of a spec (latest versions)
POST ?action=create_req {tproject_id,spec_id,req_doc_id,title,status,type,expected_coverage,scope}
POST ?action=update_req&id=N {…} (applies to latest version)
POST ?action=delete_req&id=N&tproject_id=N
Session-based auth; JSON I/O; per-request right checks
(mgt_view_req / mgt_modify_req).
-
#572 —
requirement_mgr::delete()emitted a PHP 8 E_WARNING ("Trying to access array offset on null") on every requirement deletion because$notifyOnis initializednulland only set viasetNotifyOn(). Fixed with anis_array()guard (behavior unchanged). Verified clean Event Viewer after the fix.
Expected-Coverage Configuration Gate (#1516)
The Create/Edit Requirement modal now honours the two legacy expected-coverage configuration gates, mirroring the fix applied to the standalone reqEdit screen in #1376:
-
Global gate —
req_cfg->expected_coverage_management(config.inc.php:1666, defaultENABLED). When set toDISABLEDthe Expected Coverage field is hidden entirely and the saved value is forced to 0. -
Per-type gate —
req_cfg->type_expected_coverage(cfg/const.inc.php:759). By default, type "1" (Informational) is disabled. For a disabled type the field is hidden and the stored value is forced to 0.
The BFF (api/reqspec/index.php) exposes expectedCoverageManagement (bool)
and expectedCoverageByType (map) in the options action. Save flows
(create_req / update_req) persist through effectiveExpectedCoverage(),
returning 0 when either gate blocks the field.
Suite 46 in tmp/TLU_Test_Cases.md: 16 / 16 PASS — CRUD round trip,
duplicate-id validation, permission paths (admin/guest/anonymous),
Romanian locale render, DB orphan check after deletes, Event Viewer check.
Suite for #1516: 12/12 PASS.
Suite for #1025 (filter panel): 40/40 PASS — 22 BFF filter cases across both actions, the legacy Any / prefix-only / equal-relation / subtree-pruning rules, an SQLi probe, 7 live browser interactions, clean console, 0 new Event Viewer Error/Warning rows.