-
-
Notifications
You must be signed in to change notification settings - Fork 29
Feature Bingo Developer Guide
Added in 2.8.0 Β· User page: Feature Bingo
One card per feature, with a star that lights when the feature is set up. A card is data, not code, and every new feature should arrive with its card.
| File | |
|---|---|
includes/feature_bingo.php |
the format (documented at the top), modules, categories, tiers, the loader and validator (featureBingoCards()), the check runner (featureBingoRunCheck()), featureBingoEvaluate()
|
includes/feature_bingo/cards/*.php |
the cards that ship - one file per module or area, each return [ ...cards ]
|
local/feature_bingo/*.php |
an install's own cards - same format, never shipped or overwritten (below). FEATURE_BINGO_LOCAL_DIR moves it |
api/system/feature_bingo.php |
GET the cards with their state; POST dismiss / restore / dismiss_module / restore_module (admins only; only real card ids) |
system/feature-bingo/index.php |
the page |
scripts/feature_bingo_check.php |
runs every check against a database and reports errors and bad links |
feature_bingo_dismissed |
the only data: cards marked Not for us (card_id primary key - listed in $primaryKeys in api/system/db_verify.php, because it is not id) |
[
'id' => 'tickets.sla_calendar', // unique, stable - Not for us is stored against it
'module' => 'tickets', // featureBingoModules()
'tier' => 'recommended', // essential | recommended | extra
'category' => 'governance', // featureBingoCategories()
'title' => 'Business hours for SLAs',
'what' => 'What it is, 1-2 sentences.',
'why' => 'Why it is worth it, 1-2 sentences.',
'done' => 'Exactly what the check counts, in plain words.',
'link' => 'tickets/settings/#sla', // app-relative; Tickets settings opens #<tab>
'check' => ['rows', 'sla_business_calendars'],
]| Check | Lights when |
|---|---|
['rows', 'table'] / ['rows', 'table', 'where', min]
|
at least min (1) matching rows |
['setting', 'key', 'eq'|'neq'|'in'|'nonempty', value] |
a system_settings value; a never-saved key is never configured
|
['sql', 'SELECT COUNT(*) ...', min] |
one number β₯ min. One statement, SELECT only |
['any', [...]] / ['all', [...]]
|
combinations |
featureBingoAssertReadOnly() refuses anything but a single SELECT - judging the SQL with quoted strings blanked out, so WHERE x <> 'delete' is allowed and ; DROP is not. Cards are repository code, so this guards against mistakes, not attackers.
A check that errors reads as not configured and never breaks the page - which is exactly why scripts/feature_bingo_check.php exists: an unknown table or column would otherwise leave a star dark forever with nothing saying why.
Feature Bingo is meant to be extended. Two different cases:
| You are... | Put the card in | Why |
|---|---|---|
| contributing to FreeITSM - a new feature for everyone |
includes/feature_bingo/cards/<module>.php, in the same pull request as the feature |
it ships with the release |
| running your own copy - your own processes, or something you added to your install | local/feature_bingo/<anything>.php |
no release ships, overwrites or deletes that folder |
Why not just drop a file into includes/feature_bingo/cards/ on your own install? It works until the next update. With git, an extra file survives a pull, but a release that changes that folder can collide with it. In Docker the code is part of the image, so the file is simply gone after the next docker compose pull. local/ is in .gitignore and nothing in FreeITSM writes to it.
- Create
local/feature_bingo/in the FreeITSM folder (next toincludes/), and a file in it - sayour-cards.php:
<?php
return [
[
'id' => 'local.ops_team', // unique across ALL cards - prefix yours with local.
'module' => 'system', // any key of featureBingoModules()
'tier' => 'recommended', // essential | recommended | extra
'category' => 'organisation', // any key of featureBingoCategories()
'title' => 'An Operations team',
'what' => 'A team called Operations that our out-of-hours rota is built on.',
'why' => 'Our escalation policy assumes it exists.',
'done' => 'A team named Operations exists.',
'link' => 'system/teams/', // app-relative; must exist
'check' => ['rows', 'teams', "name = 'Operations'"],
],
];- Check it:
php scripts/feature_bingo_check.php- it loads your folder too, and reports a malformed card, a duplicate id, a check that errors (a wrong table or column) or a link to a page that does not exist. Run it every time you add a card: on the page, a broken check just reads as "not set up", forever. - Open System β Feature Bingo. Your cards appear in their module with a small Added here tag, count in the score, and can be marked Not for us like any other.
To keep the folder somewhere else, add to config.php:
define('FEATURE_BINGO_LOCAL_DIR', '/srv/freeitsm-local/feature_bingo');Docker: put the folder on a volume, or it goes with the container. In docker-compose.yml, under the app's volumes::
- ./my-bingo-cards:/var/www/html/local/feature_bingoEverything under Writing a check that tells the truth below applies - especially seeded rows and pages that save every setting at once. Checks can only read: rows, setting, sql (a single SELECT returning one number), any, all. To check a feature of your own, count the rows or the setting it writes.
-
Seeded rows.
database/freeitsm.sqlandapi/system/db_verify.phpinsert defaults (statuses, priorities, ticket types, origins, resolution codes, checklist roles, dashboard widgets...). Count only rows outside the seed, or a fresh install lights the card. -
Demo data. Exclude
is_demo = 1wherever the column exists. -
Settings pages that save everything at once (Tickets β General, CSAT, Branding, Colours, System β Managers) - one Save writes every key. A "was saved" check then lights with its neighbours; compare against the default where "changed" is what matters, and say which in
done. -
Defaults that are ON. A switch that is on unless turned off cannot be told apart from never-visited; those cards light once saved switched on. Say so in
done. -
JSON in columns (workflow actions, form configs) - match the exact shape the editor writes (
"type":"send_email", no spaces), and check it against real rows.
2.8.0 shipped 572 cards across 24 modules (Tickets 143, System 86, Workflows 41, Self-service portal 40...), written by module from each settings page, its manifest's setting_keys, the schema and the help. Duplicates were removed mechanically - two cards with an identical check are the same feature - keeping the one in the module where it is used or set up. By 3.0.0 there are 613, across 25 modules (People joined with its own three; LMS gained three for competency tests).
Tested: the validator on two databases (0 malformed, 0 errors, 0 bad links; a deliberately broken card was reported); the whole evaluation in ~0.33 s; Not for us on a card and a module, restored; a made-up card id and a non-admin refused; Tickets β Settings opening #sla / #csat, and an unknown tab falling back.
Known gaps: Docker HTTPS (leaves nothing in the database) and Topology (read-only) have no card; per-analyst preferences are deliberately not cards.
FreeITSM β an open-source IT Service Management platform Β· github.com/edmozley/freeitsm Β· MIT licence
- Installation
- β° Scheduled tasks (cron jobs)
- Architecture
- π§ͺ Developer tests
- AI Providers
- Internationalisation (i18n)
- Timezones & Time Handling
- π Date & Time Formats
- Theming & Dark Mode
- ποΈ Recent β getting back to what you were doing
- β¨οΈ 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: Forms
- β³ π Mobile: Contracts
- β³ π Mobile: Domains
- β³ π Mobile: People
- β³ π Mobile: LMS
- β³ πΊοΈ Mobile: CMDB
- β³ πΊοΈ Mobile: Network Mapper
- β³ π§ Mobile: Process Mapper
- β³ βοΈ Mobile: Workflow
- β³ π₯οΈ Mobile: System
- β³ π Mobile: Reporting
- β³ π Mobile: System Wiki
- β³ π Mobile: Self-Service Portal
- β³ π§° 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
- β³ π‘οΈ CSRF protection (S4) β Developer Guide
- Single Sign-On (SSO)
- ποΈ LDAP & Active Directory
- π CardDAV contact sync
- 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: Domains
- β³ π¦ REST API: Service Status
- β³ βοΈ REST API: Morning Checks
- β³ π REST API: Forms
- β³ βοΈ REST API: Workflow
- β³ π·οΈ REST API: Cost centres
- β³ πΊοΈ REST API: Network Mapper
- β³ π§ Using the API docs page
- β³ π OpenAPI specification
- β³ β OpenAPI: kept correct
- β³ π οΈ Maintaining the catalogue
- Watchtower
-
Tickets
- β³ π Rota copy and paste β Developer Deep Dive
- β³ β Checklists & SOPs
- β³ βοΈ Mandatory fields
- β³ π·οΈ Ticket categories
- β³ π₯ Assigning tickets to a team, and escalation
- β³ π’ One board across every company
- β³ Mailbox Authentication
- β³ π€ Email send log
- β³ Basic IMAP mailboxes
- β³ Email rendering & images
- β³ SLA Management
- β³ WhatsApp channel
-
β³
βοΈ Telegram channel - β³ β CSAT company scope and filters β Developer Guide
- β³ π₯ Microsoft Teams channel
- β³ π¨οΈ Mattermost channel
- β³ π¬ Web chat channel
- β³ π£ Slack channel
- β³ π Linking tickets
- β³ β Record previews
- β³ π Ticket notes: internal or shared
- β³ ποΈ Canned responses
- β³ βοΈ Limiting replies to particular senders
- β³ π¨ Telling the analyst a ticket is theirs
- β³ βοΈ Email signatures
- β³ π The public web address
- β³ π’ Ticket numbering
- β³ π Raising a ticket for someone else
- β³ π Merging tickets
- β³ π Confidential tickets
- β³ π₯ Portal managers
- β³ π Who has seen a ticket
- β³ π Reading long tickets
- β³ β Splitting tickets
- β³ β Selecting several tickets
- β³ ποΈ The folder pane
- β³ π½ Just my tickets, or no closed ones
- β³ π οΈ Snoozing tickets β Developer Guide
- β³ π₯ Collision detection
- β³ β±οΈ Time tracking
- β³ π Scheduled work in your own calendar
- Problem Management
- Tasks
-
Assets
- β³ π’ Moving an asset between companies
- β³ π Shared asset locations
- β³ π§βπΌ Assigning assets to analysts
- β³ π Warranty and lease alerts
- β³ π Saved table views
- β³ π¨οΈ Recording anything, and importing it
- β³ π·οΈ QR asset labels
- β³ π Who holds what, and handover documents
- β³ π₯οΈ The inventory agent (PowerShell)
- β³ ποΈ Proxmox VE servers
- β³ βοΈ VMware Cloud Director servers
- β³ π Linking equipment to tickets
- β³ βοΈ Follow-up tasks on a ticket
- Knowledge
- Change Management
- Calendar
- Morning Checks
- Reporting
- Software
-
Forms
- β³ π¨ The form designer β Developer Guide
- β³ π Layout & the grid β Developer Guide
- β³ ποΈ Collections β grouping submissions
- β³ π Submissions as PDFs
- β³ β‘ What happens next β a form's own actions
- β³ π οΈ Sections & conditional logic β Developer Guide
- β³ π οΈ Lookup fields β Developer Guide
- β³ π‘οΈ Catalogue request approvals
- People
- Domains
- 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
- β³ π’ One board across every company
- β³ 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)
- What this is
-
π Bugs resolved
- β³ π’ Chat tickets ignored your ticket numbering
- β³ π Dates shown as a dash, or in server time
- β³ π Assets β Users showed people from other companies
- β³ π Restricted analysts could read other modules' data
- β³ πΌοΈ Replies with a picture in the thread failed to send
- β³ π Reply attachments never reached the customer
- β³ π οΈ Outbound email attachments β Developer Guide
- β³ π A global SSO provider was missing from the portal
- β³ π Behind a proxy, the SSO redirect said http
- β³ βοΈ The portal tagline moved when you saved it
- β³ π¨ The portal settings screen forgot what you saved
- β³ π‘οΈ The approvals inbox said "Error" and nothing else
- β³ π A table's answers were missing from the PDF
- β³ β A single-select column let you tick every option
- β³ π The portal ignored a form's field widths
- β³ π The tasks board stopped taking clicks
- β³ ποΈ #121 The index list is out of date after upgrading
- β³ π #133 The calendar subscription was empty
- β³ π #131 Tasks always reopened on the board
- β³ π₯ #129 Every page returned HTTP 500 after upgrading
- β³ π³ #127 A PHP warning above the System page
- β³ π #126 Notes stamped with the server's clock
- β³ π Storing every date in UTC
- β³ πͺ The portal was down for everyone signed in
- β³ βοΈ #120 Workflow notes could never be written
- β³ βοΈ #123 Three errors when running Database Verification
- β³ π #122 The description box was a stub in the corner
- β³ π£ Demo data deleted real accounts
- β³ π #117 Sign-in redirected to the wrong address
- β³ π¨ #108 The priority dot was invisible
- β³ β±οΈ #116 Time logged from the right-click menu
- β³ π #114 API keys refused by our own guard
- β³ ποΈ #110 Assigning a task told nobody
- β³ πͺ #107 Signed out while still working
- β³ π #103 "Share with Requester" reached nobody
- β³ π #102 Search found nothing for hyphens
- β³ πͺ #101 Source code editor opened behind
- β³ βοΈ #88 Subtasks could not be ticked off
- β³ π» #84 Asset deep link selected nothing
- β³ π« #79 A new ticket arrived with no status
- β³ π§ #79 A ticket from email did not say so
- β³ π #78 Bell opened to nothing
- β³ π¬ #77 Mail only collected from Inbox
- β³ π #74 The default password could not be changed
- β³ π¦ #70 Renaming an impact level
- β³ π€ #67 App-only mailboxes could not send
- β³ π #45 Verify only ever worked for Microsoft
- β³ π #45 IMAP reported as not authenticated
- β³ βοΈ An email template stopped escaping itself
- β³ π The portal dashboard showed the wrong time
- β³ π’ The folder said 99 and the list showed 96