Requirement Specification Viewer - sebiboga/testlink-upgraded GitHub Wiki
Type: popup viewer (not an ASIDE entry) โ reached from Search Requirement Specifications, requirement-spec links in requirement views and the TestLink search results. Refs #755.
URL: gui/templates/requirements/reqSpecView.html?id=<req_spec_id>
(&tproject_id=<id> optional โ the API resolves the owning project itself)
BFF API: api/reqspec/index.php, JSON I/O, session-based auth.

Replaces: legacy popup lib/requirements/reqSpecView.php +
gui/templates/dashio/requirements/reqSpecView.tpl.
Read-only detail view of one requirement specification โ the Dashio rewrite of the legacy spec viewer. Nothing from the legacy screen is dropped; the declared/modify/freeze/revision actions that pointed at separate legacy screens are inlined (freeze, new revision) or kept as navigation (spec management, req viewer popup).
| Section | Content |
|---|---|
| Toolbar | Refresh, Open in Spec Management (jumps to the modern reqSpecMgmt.html of the owning project), New Revision and Freeze all Requirements (visible only with mgt_modify_req on the owning project) |
| Overview | identifier chip (doc_id) + spec title, current revision badge, type badge, declared total requirements, requirements in spec, revisions count, created by / on, last modified by / on |
| Scope | full scope note (from the current revision, white-space: pre-wrap) |
| Custom fields | design-time fields linked to requirement_spec, values of the current revision (date/datetime fields localized by the BFF) |
| Attachments | file list with title, file name, size, date and a Download link (lib/attachments/attachmentdownload.php); managers (mgt_modify_req) additionally get an upload form (file + optional title) and a per-attachment Delete action |
| Requirements | DataTable with identifier, title (opens the modern reqView.html?id= popup), version, type and status โ type/status labels localized server-side |
The popup title shows the project name, the toolbar info line shows
#<spec_id> ยท Revision r<N>, and the footer is the standard TestLink footer.
-
Freeze all Requirements โ mirrors legacy
reqSpecCommands::doFreeze(): collects the whole spec subtree (child specs included), takes the latest version of every requirement under it and setsis_open = 0(viarequirement_mgr::updateOpen). Oneaudit_req_version_frozenaudit event is logged per requirement version (legacy parity). The confirm dialog asks first; the info banner reports how many versions were actually frozen. -
New Revision โ mirrors legacy
doCreateRevision()โclone_revision(): clones the latest spec revision with the supplied log message and a new author, then reloads the view (revision badger+1, revisions count +1). Custom field values are copied to the new revision (copy_cfields, legacy behavior). -
Attachment upload (manager) โ mirrors legacy
reqSpecEdit.php?doAction=fileUpload/attachments.inc.tpl: a multipart form (file + attachment title) posts toapi/attachments/index.php?action=uploadwithtable=req_specsand the spec id. The title defaults to the file name when left empty. Errors (size/type limits, e.g.TL_REPOSITORY_MAXFILESIZE= 1 MB) surface as a red toast, legacyfile_upload_koalert parity. -
Attachment delete (manager) โ mirrors legacy
jsCallDeleteFile+delAttachmentURL: awindow.confirmdialog, thenapi/attachments/index.php?action=delete(ownership-hardened: the file must belong to this spec), then the view reloads.
Both attachment controls stay hidden for view-only users (the BFF reports
rights.manage=false), exactly like legacy $attach_downloadOnly=true; the
download link stays available to everyone.

Upload form + per-attachment Delete for mgt_modify_req users (above); a
view-only user sees the download link only (below).

Legacy reqSpecViewButtons.inc.tpl:125-138 renders a req_operations fieldset
only when grants->req_mgmt == yes, and inside it three action links
only when the spec has requirements. The modern viewer keeps the exact gate:
a Requirement Operations group (#reqOpsGroup,
gui/templates/requirements/reqSpecView.html:97-102) appears in the toolbar
iff the BFF spec_view payload reports rights.manage == true
(mgt_modify_req on the owning project) and
spec.requirements_count > 0 (reqSpecView.html:492-496). It hosts three
actions:

| Action | Modern screen | Legacy source |
|---|---|---|
| Create Test Cases |
reqCreateTestCases.html?spec_id=&tproject_id= (already-modernized screen) |
reqSpecEdit.php?doAction=createTestCases (reqSpecView.tpl:41-43) |
| Copy Requirements | reqCopy.html?id=<spec>&tproject_id= |
reqSpecEdit.php?doAction=copyRequirements (reqSpecView.tpl:59-61) |
| Bulk Monitoring | reqBulkMon.html?id=<spec>&tproject_id= |
reqSpecEdit.php?doAction=bulkReqMon (reqSpecView.tpl:69-70) |
Port of legacy reqSpecCommands::copyRequirements() โ reqCopy.tpl. On load
the BFF GET api/reqspec/index.php?action=copy_options&id=<spec> returns the
spec's requirements (latest version each), the project's req-spec
destination containers (tree subtree, createHierarchyMap('dotted', doc_id), non-rspec node types excluded) and project context:

The screen offers a choose target specification select (dotted doc_id
labels, e.g. .SRS-RSV-002:Destination Specification RSV8), a Copy Test
Case Assignments checkbox (on by default), a DataTable of the source
requirements with per-row checkboxes (+ select-all) and Copy โ confirm โ
POST action=copy_reqs โ requirement_mgr::copy_to() per selected requirement
(+ copy_also.testcase_assignment, audit_requirement_copy COPY audit
event), then a localized "Copied requirements" summary. Copied requirements get
the (1) doc-id suffix (legacy parity):

Port of legacy reqSpecCommands::bulkReqMon() โ reqBulkMon.tpl. The BFF
GET action=bulk_mon_options returns the spec's requirements with the current
user's per-requirement monitor flag (getMonitoredByUser scoped to the spec)
and enable_start_btn/enable_stop_btn parity. The screen renders a DataTable
with per-requirement On/Off monitor state and Toggle / Start / Stop
Monitoring buttons, each posting POST action=bulk_mon_toggle (op =
toogleMon/startMon/stopMon), mirroring doBulkReqMon():

Both copy/monitor BFF routes enforce the legacy rights: mgt_view_req to reach
the payload, mgt_modify_req to write.
The viewer is no longer a read-only dead end for managers: the toolbar now
leads with + Create Requirement (reqSpecView.html #createReqBtn),
mirroring the legacy req_operations fieldset create_req button
(gui/templates/dashio/requirements/include/reqSpecViewButtons.inc.tpl:104-107 +
reqSpecView.tpl:25 req_edit_url = reqEdit.php?doAction=create&req_spec_id=).

- Gated only on
req_mgmt(BFFspec_viewrights.manage) โ a manager sees it even when the spec contains 0 requirements, exactly like legacy; viewers without the right never see it (display:nonedefault +showHideActions()). It sits outside thereqOpsGroup, whose Create Test Cases / Copy / Bulk Monitoring links keep their ownrequirements_count > 0gate (Refs #1348). - Clicking opens
gui/templates/requirements/reqEdit.html?spec_id=<id>&tproject_id=<tid>in a new popup โ the modern Requirement Editor enters create mode whenever noidis given (the form URL branches tospec_idwhenREQ_ID <= 0,reqEdit.html:233;MODEdefaults tocreate,reqEdit.html:142), pre-bound to the current spec (title, doc-id scheme, types, scope scaffold). This is the exact analogue of the legacyreqEdit.php?doAction=create&req_spec_id=<id>URL; the modern screen readsspec_id(reqEdit.html:579) instead ofreq_spec_id, anddoActionis ignored. - After saving, click Refresh in the viewer โ the Requirements table then
re-renders with the new requirement (confirmed in E2E:
QAM-REQ-001created from the button flow appears in the spec's table) and the Requirement Operations group activates oncerequirements_count > 0. (Legacy returned to the viewer automatically only becausecreate_reqwas a same-windowlocationredirect throughreqEdit.php.)
Legacy reqSpecViewButtons.inc.tpl:38-52 renders a Requirement Specification
Operations fieldset with New Req Spec (btn_new_req_spec), Edit
(btn_edit) and Delete (btn_delete) โ all gated on grants->req_mgmt == yes, with New Req Spec additionally gated on
$tlCfg->req_cfg->child_requirements_mgmt != DISABLED
(config.inc.php:1661). The modern viewer now renders the same three actions as
a #specOpsGroup (label + ghost buttons) in the toolbar:

| Button | Modern behaviour | Legacy source |
|---|---|---|
| + New Req Spec | opens an inline Bootstrap modal (#specModal, openSpecChildCreate()) pre-bound to the current spec as parent โ doc id, title, type (sourced from the option list, current spec type preselected), declared total requirements, scope โ and POSTes action=create_spec with parent_id=<spec_id> to BFF api/reqspec/index.php; on success the toast "Requirement specification saved." shows and the viewer reloads onto the new child spec |
reqSpecEdit.php?doAction=createChild&parentID=<spec_id> |
| Edit | opens the same modal prefilled from the current spec (openSpecEdit() โ doc id, title, type, total_req, scope of the latest revision) and POSTs action=update_spec
|
reqSpecEdit.php?doAction=edit |
| Delete |
confirmDeleteSpec() โ #delModal confirm dialog (spec title + "will be deleted together with ALL its requirements, revisions and coverage") โ POST action=delete_spec; on success the toast "Requirement specification deleted." shows and the screen falls to the 404 state (the spec is gone, refresh resolves the parent tree) |
reqSpecEdit.php?doAction=doDelete&req_spec_id= |

-
create_specnow accepts an optionalparent_id(validated withneedOwnedSpec) and passesparentId(child semantics) โrequirement_spec_mgr::create($tproject_id,$parentId,...)creates the node under the parent spec's tree node (deep link #1345); withoutparent_idthe behaviour is unchanged (root-level spec under the test project). -
optionsexposeschildRequirementsManagement(boolishConfig, default ENABLED) so the client can apply the legacy New Req Spec gate. -
spec_viewnow returnsspec.child_requirements_mgmt(same flag) and a top-levelspecTypesmap (id โ label) when?full=1&id=is used, so the modal's type dropdown renders even on deep links that never hitoptions.
When child_requirements_mgmt = DISABLED the group shrinks to Edit + Delete
only โ verified live by flipping config.inc.php:1661.
Parity note (not a regression): the legacy engine itself never persists the "expected total requirements" number on create โ
requirement_spec_mgr::create_revision()(lib/functions/requirement_spec_mgr.class.php) omitstotal_reqfrom its INSERT (column defaults 0) and plainupdate_revision()(no new revision) only touchesscope/modifier. The moderncreate_spec/update_specreuse the same engine calls, sototal_reqbehaviour is bit-for-bit legacy parity (and matches the siblingreqSpecMgmt.htmlscreen).
The whole group is hidden for viewers without mgt_modify_req (BFF spec_view
rights.manage โ same gate as Freeze / New Revision / attachment upload). The
BFF write routes enforce needManageRight server-side. Verified E2E on a fresh
DB: child spec created via the modal appears under the parent (DB node parent
check), edit persists title/scope/doc-id on the latest revision and the node,
delete removes the subtree incl. revision nodes, deep link
reqSpecView.html?id=<child> resolves the project, disabled-config gate hides
only New Req Spec, Event Viewer clean.
- Nonexistent spec is probed with a direct
SELECT testproject_id FROM req_specs WHERE id=Nfirst; the API answers HTTP 404 and the screen shows "Failed to load requirement specification: Requirement specification not found" while hiding the body. This covers orphaned specs too (areq_specsrow with no revision): since the #1714 fix,requirement_spec_mgr::get_by_id()itself returnsnullwhenget_last_child_info()finds no revision, so no E_WARNING / SQL-1064 pair is ever emitted and the screen degrades to the same clean 404. - Without
mgt_view_reqthe API answers HTTP 403 and the screen shows "No permission"; the Freeze / New Revision buttons are hidden in that case. - Opening the screen with no
tproject_id(e.g. from the search results deep link) works too:spec_viewresolves the owning project itself and carries its own localized type/status labels.
New keys under the rsv.* family were added to all ten locale bundles
(gui/templates/i18n/{de,en,es,fr,it,ja,pt,ro,ru,zh}.json); the requirement type/status and
spec type labels come from the existing server-side label maps.
-
gui/templates/requirements/searchReqSpec.htmlโopenReqSpec()now opensreqSpecView.html?id=(wasreqSpecView.php?req_spec_id=). -
gui/javascript/testlink_library.jsโopenLinkedReqSpecWindow()(used by remaining legacy search screens) now targets the modern viewer URL. - The revision links (
rev. N) still point to the legacyreqSpecViewRevision.phpโ those are the next piece of #755.