-
Notifications
You must be signed in to change notification settings - Fork 16
Issue 84 Asset Deep Link Selected Nothing
Clicking a row on the asset table took you to the split-pane asset screen, with the right asset id in the address bar β and then sat there with nothing selected and an empty details panel. The asset appeared only if you found it in the list and clicked it a second time.
Reported in issue #84 by dschipfel, who called it correctly: "This appears to be an initialization issue⦠users may assume the asset failed to load."
Fixed in 681cda74, released as update #1198.
Asset Management β table view, click any row.
You arrive at asset-management/index.php?asset=2. The page loads. The asset list on the left loads. The panel on the right, where the asset should be, is empty. Nothing is highlighted.
The id is right there in the URL, which is what makes it read as broken rather than as a mis-click. And clicking the same asset in the list works perfectly β so the asset is fine, the panel is fine, and only the arrival is wrong.
Two screens had drifted onto different spellings of the same thing.
The asset screen reads the id like this:
// asset-management/index.php β before
var aid = new URLSearchParams(window.location.search).get('asset_id');
if (aid) { β¦ selectAsset(n); }The table view wrote it like this:
// assets/js/asset-table.js β before
onRowClick: row => { window.location.href = `index.php?asset=${row.id}`; },asset is not asset_id. get('asset_id') returned null, the if never ran, and nothing was ever selected.
An unrecognised query parameter is not an error. Nothing validates it, nothing warns about it, and the browser is perfectly happy to carry it. So the page did exactly what it was told: it opened the asset module and selected nothing.
That is the whole reason this reads as a fault in the asset rather than a fault in the link. A broken link normally announces itself with a 404. This one delivered you to the right module, on the right page, with the right id visible in the address bar, and then quietly did nothing with it.
FreeITSM already has a single declared answer for "what is the address of an asset":
// includes/entity_links.php
case 'asset':
return 'asset-management/?asset_id=' . $id;And the ticket inbox already links that way:
// assets/js/inbox.js
href="../asset-management/index.php?asset_id=${link.asset_id}"So of the three places in the codebase that address an asset, two agreed and one did not. asset-table.js was the outlier, not a second valid convention.
Issue #91 was the same shape: several places writing record addresses, disagreeing, and the disagreement showing up as a page that opens and does nothing. The lesson recorded then was that tolerance hides drift β the modules that accepted anything never surfaced the problem, and the two strict ones were where the dead links finally appeared.
The asset screen was one of the strict ones. It is strict because being strict is what made this findable.
Three changes, and only the first is the bug.
1. The table now writes the canonical spelling.
// assets/js/asset-table.js β after
onRowClick: row => { window.location.href = `index.php?asset_id=${row.id}`; },2. The old spelling is still accepted β but not silently.
The table sent ?asset= until this fix, so those URLs are already in people's history and bookmarks. Refusing them would turn one bug into another.
var params = new URLSearchParams(window.location.search);
var aid = params.get('asset_id') || params.get('asset');The important part is what happens next, in change 3: the address bar is rewritten to the canonical form. An old link works once and then corrects itself. That is deliberately different from being quietly tolerant, which is how the two spellings drifted apart in the first place.
3. The address bar now follows what you are looking at.
// asset-management/index.php β in selectAsset()
try {
if (window.history && window.history.replaceState) {
window.history.replaceState({ assetId: Number(assetId) }, '',
window.location.pathname + '?asset_id=' + Number(assetId));
}
} catch (e) { /* not fatal */ }Click an asset in the list and the URL becomes that asset's URL β so it can be copied to a colleague, bookmarked, or reloaded and land back where you were.
Why replaceState and not pushState. The list stays on screen the whole time, so choosing an asset is not navigation. Pushing would fill the Back button with entries that never visibly go anywhere β press Back five times and the page appears not to move while the URL ticks backwards. The same shape already exists in the portal's ticket list (self-service/tickets.php), which uses replaceState for the same reason.
Why the try. History writes throw in some contexts β file:// origins, strict embedders. A URL nicety must never be the thing that stops an asset loading. Copied from the note already in self-service/tickets.php.
| File | What changed |
|---|---|
assets/js/asset-table.js |
Row click writes ?asset_id=. The bug.
|
asset-management/index.php |
Accepts the legacy ?asset=; selectAsset() writes the open asset into the address bar. |
asset-management/table.php |
Cache-buster asset-table.js?v=6. |
| File | |
|---|---|
includes/entity_links.php |
Declared asset_id all along. It was right; the table did not consult it. |
assets/js/inbox.js |
Already linked with asset_id. |
The real pages were driven in a headless browser.
Reproduced first. Before any change, ?asset=11 left no row selected while ?asset_id=11 selected one β the two spellings side by side, one working and one not.
After the fix, four checks:
?asset=11 (legacy) |
details shown, and the URL self-corrects to ?asset_id=11
|
?asset_id=12 (canonical) |
details shown |
| no parameter, then select an asset | URL becomes ?asset_id=13
|
table.php |
serves the updated script |
Clicking a row on the asset table now opens that asset properly, first time.
Any links you already made with the old address still work, and quietly become proper ones. And the asset screen's URL now tracks whichever asset you have open, so it is worth copying out of the address bar when you want to send somebody straight to a machine.
FreeITSM β an open-source IT Service Management platform Β· github.com/edmozley/freeitsm Β· MIT licence
- Installation
- β° Scheduled tasks (cron jobs)
- Architecture
- AI Providers
- Internationalisation (i18n)
- Timezones & Time Handling
- π Date & Time Formats
- Theming & Dark Mode
- β¨οΈ Command palette (βK)
- π Searching inside tickets
- π Attached documents
-
MobileβFriendly
- β³ π« Mobile: Tickets
- β³ π» Mobile: Assets
- β³ π Mobile: Calendar
- β³ π Mobile: Knowledge
- β³ π¦ Mobile: Service Status
- β³ πΌ Mobile: Watchtower
- β³ π§© Mobile: Problem Management
- β³ π Mobile: Change Management
- β³ πΏ Mobile: Software
- β³ β Mobile: Tasks
- β³ π§° Mobile: Techniques & Tricks
-
Security
- Layer 1 β which modules you can enter
- β³ π§© Module Access Control
- β³ π οΈ Module Access β Developer Guide
- Layer 2 β what you can administer
- β³ π Roles & Permissions
- β³ π οΈ Roles β Developer Guide
- β³ π€ Why capabilities are constants
- Layer 3 β the System module
- β³ π Admin Access Control
- Hardening
- β³ π Security review response 2026-08
- β³ π‘οΈ Security hardening 2026-08
- β³ π οΈ Security hardening 2026-08 β Developer Guide
- β³ π‘οΈ Round three β plain English
- β³ π οΈ Round three β Developer Guide
- Single Sign-On (SSO)
- ποΈ LDAP & Active Directory
- Browser Extension
- API Reference
-
π REST API β how it works
- β³ π« REST API: Tickets
- β³ π» REST API: Assets
- β³ π΄ REST API: Problems
- β³ π REST API: Changes
- β³ π REST API: Knowledge
- β³ β REST API: Tasks
- β³ ποΈ REST API: CMDB
- β³ π REST API: Contracts
- β³ ποΈ REST API: Calendar
- β³ πΏ REST API: Software
- β³ π¦ REST API: Service Status
- β³ βοΈ REST API: Morning Checks
- β³ π REST API: Forms
- β³ βοΈ REST API: Workflow
- β³ πΊοΈ REST API: Network Mapper
- β³ π§ Using the API docs page
- β³ π OpenAPI specification
- β³ β OpenAPI: kept correct
- β³ π οΈ Maintaining the catalogue
- Watchtower
-
Tickets
- β³ Mailbox Authentication
- β³ π€ Email send log
- β³ Basic IMAP mailboxes
- β³ Email rendering & images
- β³ SLA Management
- β³ WhatsApp channel
- β³ π¬ Web chat channel
- β³ π£ Slack channel
- β³ π Linking tickets
- β³ π Ticket notes: internal or shared
- β³ ποΈ Canned responses
- β³ βοΈ Limiting replies to particular senders
- β³ βοΈ Email signatures
- β³ π The public web address
- β³ π’ Ticket numbering
- β³ π Raising a ticket for someone else
- β³ π Merging tickets
- β³ β Splitting tickets
- β³ β Selecting several tickets
- β³ ποΈ The folder pane
- β³ π οΈ Snoozing tickets β Developer Guide
- β³ π₯ Collision detection
- β³ β±οΈ Time tracking
- β³ π Scheduled work in your own calendar
- Problem Management
- Tasks
- Assets
- Knowledge
- Change Management
- Calendar
- Morning Checks
- Reporting
- Software
- Forms
- Contracts
- Service Status
- π Notifications
- π¨ War Room
- Self-Service Portal
- LMS
- Process Mapper
- CMDB
- Network Mapper
- Workflows
- Issue trackers (Jira, Azure DevOps)
- System
-
Overview
- β³ π Progress tracker
- β³ Concepts & vocabulary
- β³ Email routing & mailboxes
- β³ Settings: global vs per-company
- β³ Users & self-service
- β³ Staff cross-company access
- β³ Worked examples
- β³ Pitfalls & gotchas
- β³ Scope: what it's for
- β³ π οΈ Developer Guide (make a module multi-company)
- β³ ποΈ Case study: CMDB (a linked graph)
- β³ π§ͺ Test harness (prove it's isolated)