-
Notifications
You must be signed in to change notification settings - Fork 15
Issue 70 Renaming an Impact Level
Rename Operational under Service status β Settings β Impact levels and the board stopped agreeing with itself. Some services showed the new name; the rest carried on showing the old one, and their badges lost their colour.
Reported in issue #70, following on from discussion #59.
Fixed in ee171d07, released as update #1103.
The general guide to the module is Service Status.
Rename the level to anything at all β the reporter's example, and the one used to reproduce it here, was appending a single letter β and the board looked like this:
| Service | Impact level shown | Badge colour |
|---|---|---|
| File Services |
Operationalx β
the new name |
green β |
| VPN |
Operational β the old name |
grey β |
| Internet |
Operational β |
grey β |
| Printing |
Operational β |
grey β |
One level, two different names, on the same screen. And the ones showing the stale name were the ones that lost their colour.
The pattern looks arbitrary until you know which services were which. File Services was the only one caught up in an open incident. Every service showing the wrong name was a healthy one.
This is the whole bug, and it is worth understanding because the same shape of mistake can happen anywhere.
When a service is affected by an incident, FreeITSM writes down which impact level it is at β a row in status_incident_services saying "service 4 is at level 2". That is a real, stored fact. Rename level 2 and the row still points at level 2, so the board reads the new name straight out of the table. File Services was correct because its status was recorded.
A healthy service has no such row. There is nothing to record: it isn't in an incident, so nothing links it to any level. Its status doesn't exist anywhere in the database β it is worked out at the moment the page is drawn, from the absence of any incident.
So the code has to answer the question "what do we call a service that nothing is wrong with?" β and six different places in the codebase answered it by writing the word Operational into the reply as a piece of text:
COALESCE(
(SELECT il.name FROM status_incident_services sis ... ),
'Operational' -- <- the entire bug
) AS current_statusThat literal was correct the day it was written and wrong the moment anybody renamed the level.
The badge is not coloured by a stylesheet rule per level β that could never work, because you can add your own levels and pick your own colours. The page looks the name up in the list of impact levels and uses whatever colour it finds.
Once the level was called Operationalx, the string Operational matched nothing. No match, no colour, grey badge. The lost colour and the stale name were the same fault twice.
The general lesson: wherever a value means "the absence of a record", something has to invent it at read time. That invented value is where a hardcoded assumption hides, and it will not show up in any table you can inspect.
The board was simply the most visible face of the same literal. Four more places were broken by the same rename, and two of them were worse than the thing that got reported.
The self-service portal shows a green banner when nothing is wrong. It decided that by checking whether every service's status was the text Operational.
After a rename, no service ever matched that word β including services that were perfectly healthy. The banner did not flicker or misfire; it silently retired for good. Nothing looked broken, because a missing banner looks exactly like a busy day.
Watchtower's list of services currently in trouble worked by excluding the healthy ones by name:
AND il.name <> 'Operational'Rename the level and that clause stops excluding anything. The "services affected right now" panel would have filled up with every service you own, all of them fine. Nobody had hit this yet only because it needs both a rename and an incident open at the renamed level.
Creating an incident through the API without naming an impact level fell back to the same literal, then looked it up, then failed to find it:
422 Unknown or inactive impact level
This is the one that would waste the most time, because the error names a field the caller never sent.
Separate cause, same family. The portal picked a badge style from a hardcoded list of five level names. Any level you added yourself matched nothing and fell through to the default branch β which was impact-operational, i.e. green.
So a level called Degraded Performance, which is a warning, was displayed to your users in the colour that means fine. This one needed no rename at all: it was wrong for anybody who had ever added a level.
There is already a flag in the database that means exactly "this is the level that means nothing is wrong" β service_impact_levels.is_default. It was being used for other things and had a good claim to being authoritative:
- saving a level as the default clears the flag on every other level, so there is only ever one;
- deleting the default is refused outright, so it cannot vanish;
- an installation that somehow ends up with none promotes the least severe level automatically.
That is a rule the database already enforces, so the fix is to ask the flag instead of asserting a name. One small helper does it, and every one of the six places calls it:
// includes/service_impact_levels.php
function defaultImpactLevel(PDO $conn): array // β ['id' =>, 'name' =>, 'colour' =>]It deliberately has no require statements of its own. The portal dashboard needs it, and making the portal load the workflow engine to find out what "healthy" is called would be a poor trade.
Both dashboards now return current_status_colour alongside current_status, rather than leaving the browser to look the colour up by name.
That is not tidiness β it fixes a second case nobody had reported. The browser's list of levels is filtered to active ones, so a level that was switched off while an incident was still open at it also lost its badge colour. Sending the colour from the row that was actually resolved makes a renamed or a deactivated level paint correctly.
The Add affected service row in the incident dialogue defaulted its impact by naming 'Degraded' literally. Rename that level and a new row silently fell back to the baseline β an incident affecting a service at the level that means nothing is wrong.
It now asks by meaning rather than by name: the mildest level that still counts as downtime. On the stock lookup that is Degraded, and it stays right whatever the levels are called.
| File | Role |
|---|---|
includes/service_impact_levels.php |
New. defaultImpactLevel() β reads the is_default row, falls back to the least severe active level, then to a literal only if the table is empty. Dependency-free by design |
| File | Role |
|---|---|
api/service-status/get_dashboard.php |
Analyst board. Fallback name is now bound from the default level; returns current_status_colour and default_impact
|
api/self-service/get_dashboard.php |
Portal dashboard. Same change, so both surfaces derive status identically |
service-status/index.php |
Board rendering β badge colour now comes from the resolved row; the incident dialogue's default impact asks by meaning |
self-service/index.php |
Portal rendering β "all clear" banner compares against the default level; badge style comes from the level's own colour |
| File | Role |
|---|---|
includes/watchtower_queries.php |
The affected-services panel excludes the healthy level with il.is_default = 0 instead of by name |
includes/services/service_status.php |
An incident write with no impact named falls back to the default level, not to the word |
api/v1/resources/service_status.php |
The REST derived-status subquery and serialiser |
| File | Role |
|---|---|
api/v1/spec.json |
Endpoint descriptions no longer promise "else Operational" |
api/v1/lib/openapi_schemas.php |
The current_status field description |
Run against a database left in the broken state, with the level genuinely renamed to Operationalx β not simulated.
| What was checked | Result |
|---|---|
Analyst board (get_dashboard.php) |
All six services report Operationalx, all green |
Portal dashboard (self-service/get_dashboard.php) |
Same, and Degraded Performance now shows in its own colour rather than green |
GET /api/v1/service-status/services |
Same derived status as both dashboards |
| Watchtower affected-services query | Two genuinely affected services; no healthy ones |
| Both pages driven in a real browser | Labels and computed badge colours read back from the rendered page, not from the source |
Renaming the level again mid-test, to All Good Mate with a purple colour, moved the name and the colour on every healthy service β proving it follows the flag rather than any particular word.
The positive control matters more than any of the above. With both incidents temporarily resolved, the portal's All systems operational banner reappeared. That banner is the one thing the old code could not possibly produce once the level had been renamed, so seeing it is proof the check is genuinely dynamic β and not merely that nothing crashed.
- You can rename the default impact level to whatever you like. "Operational", "Available", "Normal", something in your own language β the board, the portal, Watchtower and the API all follow it.
- You can change its colour, and healthy services will use it.
- Custom levels of your own now show in their own colour in the portal, instead of everything unfamiliar coming out green.
- Which level counts as "nothing is wrong" is the one marked as the default on Service status β Settings β Impact levels. If you want a different level to be the baseline, mark that one as default and everything follows it.
- Nothing needs migrating and no setting needs changing. An installation that never renamed anything behaves exactly as before, because the default level is still called Operational.
- Bugs resolved β the index of write-ups like this one
- Service Status β the module, including impact levels and settings
-
Service uptime and history β where
counts_as_downtimeon an impact level matters - Self-Service Portal β the second place service status is shown
- Watchtower β the affected-services panel
-
REST API: Service Status β the derived
current_statusfield
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
- Theming & Dark Mode
- β¨οΈ Command palette (βK)
- π Searching inside tickets
- π Attached documents
- MobileβFriendly
-
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
- β³ ποΈ Canned responses
- β³ βοΈ Limiting replies to particular senders
- β³ βοΈ Email signatures
- β³ π The public web address
- β³ π Raising a ticket for someone else
- β³ π Merging tickets
- β³ β Splitting tickets
- β³ β Selecting several tickets
- β³ π οΈ Snoozing tickets β Developer Guide
- β³ π₯ Collision detection
- β³ β±οΈ Time tracking
- 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)