Repository navigation
Emailed Reports Developer Guide
Since 5.3.0 Β· Asked on #261 Β· First user: the "what if" in Imported people (PR #264 by vsc55) Β· User guide: Emailed reports Β· The sender's attachments: Outbound email attachments Β§6
A page that builds a PDF in the browser can email it to the analyst who is signed in - and only to them. What the email carries is decided once, by an administrator, on System β Emailed reports: the report in the body, the PDF attached, a guarded download link, or any mix.
Why the attachment exists at all: some installs are not reachable from the internet, so a link in an email opens nowhere there - the report has to travel in the email (Ed, 2026-11-14). Why the link is guarded: a link in a mailbox is easy to forward, so it opens only for its owner, signed in, until it expires or is revoked, and every use is audited (Ed, 2026-11-14).
| File | Role |
|---|---|
assets/js/email-report.js |
window.emailReportToMe() - the one call a page makes |
api/system/email_report.php |
POST: checks the request, applies the settings, sends through ssSendSystemEmail()
|
includes/self_service_email.php |
ssSendSystemEmail(..., $attachments), ssSystemEmailAttachmentParts(), ssAttachmentFilename(), SYSTEM_EMAIL_ATTACH_MAX_BYTES - see Β§6
|
includes/report_downloads.php |
The settings (reportEmailSettings(), reportEmailSaveSettings()), the links (reportDownloadCreate(), reportDownloadAuthorise(), reportDownloadRevoke(), reportDownloadsTidy()), the audit (reportDownloadAudit()), the readiness check (reportDownloadsReady()), the address warning (reportLinkReachabilityWarning()) |
api/system/report_download.php |
GET ?t=<token> - the link itself |
index.php |
Reads the freeitsm_report_return cookie after sign-in and goes back to the link |
system/emailed-reports/index.php + api/system/emailed_reports.php
|
The administrators' page: the ticks, the links with Revoke, recent activity |
cron/report_downloads_tidy.php |
The daily tidy-up, job report_downloads_tidy in includes/job_registry.php
|
report_downloads table |
database/freeitsm.sql and includes/db_verify_schema.php
|
system/help/emailed-reports.php |
In-app help |
tests/system-email-attachments.php |
86 checks - see the end |
Load the script (or load it the first time Email is pressed, as directory-maintenance.js does), build the PDF with pdf-writer.js, and pass both the PDF and an HTML version of the report:
<script src="../../assets/js/email-report.js?v=1"></script>const r = await emailReportToMe({
subject: 'What if: delete directory source "Contoso AD"',
html: reportHtml, // for the body - optional, but pass it when there is one
filename: 'what-if-contoso-ad.pdf',
pdf: doc // a pdf-writer document, Blob, ArrayBuffer or Uint8Array
});
showToast(r.ok ? r.message : r.error, r.ok ? 'success' : 'error');
// r = { ok: true, sentTo, delivered: {body, attachment, link}, message }
// | { ok: false, code, error }- It never rejects - a dialog can
awaitit without atry.messageanderrorare already translated (common.email_report.*). - It works out the endpoint from its own
src, so it works from any page depth (TRAP in the file:document.currentScriptis only set while the script first runs). -
csrf.jsadds the token; the script sets it too. - Codes:
not_signed_in,bad_request,not_pdf,too_large,no_email,no_mailbox,send_failed,network.
Do not add a recipient. The caller decides nothing about where it goes or what the email holds; that is the point.
- Any signed-in analyst - no module check: the report is already theirs.
-
Only to their own address:
analysts.emailof the signed-in, active analyst. There is no recipient field; atoin the request is ignored. No valid address βno_email. -
POST with the CSRF token (
csrfRequireWriteMethod(), thencsrfRequireToken()again in the file). -
PDF only: the decoded bytes must start
%PDF-; the name is cleaned byssAttachmentFilename()and given.pdfif it lacks it. -
3 MB (
SYSTEM_EMAIL_ATTACH_MAX_BYTES): checked onContent-Lengthbefore the body is read, and again through the sender's own rules. -
The HTML goes through
sanitiseUserHtml()- it came from a browser, even though it only ever goes back to the person who made it. -
Logged in
email_send_logas routereport_to_self, sent or failed. - A failed send revokes the link it made, so no link exists that nobody was sent.
What the email holds:
$how = reportEmailSettings($conn);
$wantBody = $how['body'];
$report = $wantBody ? sanitiseUserHtml((string)($in['html'] ?? '')) : '';
$hasReport = trim(strip_tags($report)) !== '';
$wantLink = $how['link'] && reportDownloadsReady($conn);
// Attached when ticked - and whenever nothing else ticked would carry the report.
$wantAttach = $how['attach'] || (!$wantLink && !$hasReport);So the report always arrives: a link before Database Verification, or a body with no HTML sent, falls back to the attachment.
system_settings, read by reportEmailSettings() with defaults for anything missing:
| Key | Values | Default |
|---|---|---|
report_email_body |
'1' / '0'
|
on |
report_email_attach |
'1' / '0'
|
on |
report_email_link |
'1' / '0'
|
off |
report_link_days |
1, 7, 30 (REPORT_LINK_DAY_CHOICES) |
7 |
reportEmailSaveSettings() refuses nothing ticked and any other number of days, with a sentence fit to show. If "nothing ticked" is ever found stored, it reads as body + attachment. The page renders the ticks server-side (as Date and time formats does), so a failed fetch can never show a guess that Save then writes back.
reportLinkReachabilityWarning() returns the address links would start with when it looks unreachable from elsewhere - nothing configured, localhost, a .local name, a private or loopback IP, a name with no dot - and the page shows it under the link tick. The address is publicBaseUrl() (Tickets β Settings, Public web address).
$token = bin2hex(random_bytes(32)); // 64 hex, in the email only
$rel = bin2hex(random_bytes(16)) . '.bin'; // the stored file's name
// ... file_put_contents(reportDownloadsDir() . '/' . $rel, $bytes)
INSERT INTO report_downloads (token_hash, analyst_id, filename, mime, size_bytes, storage_path, created_at, expires_at)
VALUES (hash('sha256', $token), ...)- Only the SHA-256 of the token is stored, so a copy of the database cannot be turned back into working links.
- The file lives in
uploads/report-downloads/, denied to the web byuploadPrepareDir(); nothing from the caller reaches the file system (a random name, not the report's). - Audited:
create.
-
Not signed in: a well-formed token goes in the
freeitsm_report_returncookie (15 minutes,HttpOnly,SameSite=Lax), and the browser goes to sign in.index.phpreads the cookie, clears it, and goes back to the link - only a 64-hex token is accepted and the destination is fixed, so the cookie cannot send anybody anywhere else. -
reportDownloadAuthorise(): looks the row up by hash and compares again withhash_equals(); then, in order:
| Outcome | Status | Page |
|---|---|---|
not_ready (no table), unknown (malformed or no row) |
404 | Link not found |
wrong_analyst |
403 | This link is not yours |
revoked, missing_file
|
410 | This link no longer works |
expired |
410 | This link has expired |
ok |
200 | the file, Content-Disposition: attachment, nosniff, private, no-store; download_count + 1 |
Every answer is audited - download or refused with the outcome. It is a GET that writes (the count and the audit row) because a link in an email can only be a GET: a log of a read, as api/documents/download.php's is.
One system_logs row per event, log_type = 'report_download', analyst_id (null when nobody is signed in), and details JSON with action (create, download, refused, revoke, tidy), outcome, ip_address, user_agent (255 characters), and download_id / filename / owner_id where there is a link. It never throws: an audit row that cannot be written must not break a download. System β Emailed reports shows the latest 50 (reportDownloadsActivity()).
Administrators, on System β Emailed reports (POST {action:'revoke', id}): sets revoked_at / revoked_by, deletes the file at once (file_deleted_at), audits revoke. Also called by the endpoint when a send fails.
The report_downloads_tidy job, daily (interval 1440), and only while some link still has a file (needed):
- deletes the files of expired and revoked links - the row is kept,
file_deleted_atset, so the list and the audit still make sense; - deletes rows
REPORT_DL_KEEP_DAYS(90) days after they expired; - deletes stored files no row knows, once a day old (an analyst deleted with their links);
- audits
tidywith the counts, only when it did something.
CREATE TABLE IF NOT EXISTS `report_downloads` (
`id` INT NOT NULL AUTO_INCREMENT,
`token_hash` CHAR(64) NOT NULL,
`analyst_id` INT NOT NULL,
`filename` VARCHAR(255) NOT NULL,
`mime` VARCHAR(100) NOT NULL DEFAULT 'application/pdf',
`size_bytes` INT NOT NULL DEFAULT 0,
`storage_path` VARCHAR(100) NOT NULL,
`created_at` DATETIME NOT NULL,
`expires_at` DATETIME NOT NULL,
`revoked_at` DATETIME NULL,
`revoked_by` INT NULL,
`download_count` INT NOT NULL DEFAULT 0,
`last_downloaded_at` DATETIME NULL,
`file_deleted_at` DATETIME NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `uq_report_downloads_token` (`token_hash`),
KEY `idx_report_downloads_analyst` (`analyst_id`, `id`),
KEY `idx_report_downloads_expires` (`expires_at`)
);All times UTC.
Microsoft Graph refuses a sendMail request over 4 MB, and base64 makes an attachment a third bigger. 3 MB is the most that sends on every provider; a bigger cap would fail only on Microsoft 365, which is the worst kind of limit. Going past it needs Graph upload sessions. A report over the cap is refused up front with too_large and a sentence saying to download it instead.
reportDownloadsReady() (SELECT 1 FROM report_downloads LIMIT 0) is the one test, and every function copes with it being false:
- the link option steps aside - the email carries the body and/or the attachment, and the attachment if the link was the only way ticked;
- the page says Run Database Verification to add download links.; Revoke refuses with the same sentence;
- a link that somehow arrives is
not_readyβ 404; the tidy-up does nothing.
Nothing errors.
-
TRAP: never take a recipient from the request.
email_report.phpmust stay unable to send to anybody but the signed-in analyst, or it becomes a relay for any signed-in account. -
TRAP: never store the token. Only its hash; compare with
hash_equals()after the lookup. - TRAP: a failed send must revoke the link it made.
- TRAP: a new caller passes both the PDF and the HTML. The settings decide which is used; a caller that sends only one breaks the setting it did not expect.
cd c:/wamp64/www/freeitsm-prtest
FREEITSM_TEST_DATABASE=freeitsm_prtest FREEITSM_TEST_URL=http://localhost/freeitsm-prtest FREEITSM_BASE_URL=http://localhost/freeitsm-prtest php tests/system-email-attachments.php
Six parts: the attachment rules (names, types, the 3 MB / 10-file caps); the Graph body (no attachments key without files - the shape sent before 5.3.0); the raw MIME for SMTP and Gmail (the PDF byte-for-byte after base64); ssSendSystemEmail() through the real SMTP client to a fake SMTP server the test starts on 127.0.0.1; the download links (hash only, owner downloads, the four refusals, every answer audited, the tidy-up, the table missing); and over HTTP, the endpoint's refusals, that it sends only to the signed-in analyst whatever to says, and that it follows the settings. Nothing is really emailed: the only sending mailbox during the run is the ZZ one, and every ZZ row, file, session and setting is put back in finally.
FreeITSM β an open-source IT Service Management platform Β· github.com/edmozley/freeitsm Β· MIT licence
- Installation
- β° Scheduled tasks (cron jobs)
-
Architecture
- β³ π§Ή Root folder tidy (URL routing)
- β³ π Database Integrity (FKs & constraints)
- β³ π οΈ Database Verification β Developer Guide
- β³ π§© Service Layer (UI + API share code)
- β³ πͺ€ Code traps (the TRAP: comments)
- β³ π Commenting for the Engineering wiki
- β³ π οΈ Engineering wiki scanner - Developer Guide
- β³ π Service-Layer progress
- π€ Contributing (read before a pull request)
- π§ͺ Developer tests
- π PDF exports moved to PDFKit (#254)
- AI Providers
- Internationalisation (i18n)
- Timezones & Time Handling
- π Date & Time Formats
- π± Currencies
- π§ The sidebar (optional module rail)
- π§© Preference templates
- π Display order (modules, Watchtower cards) β Developer Guide
- 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: Projects
- β³ ποΈ Mobile: Files
- β³ π 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
- β³ π Switching modules off
- β³ π οΈ Module availability β Developer Guide
- β³ π Module availability β design (#217)
- 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
-
π¦ Microsoft Entra ID
- β³ π₯ Importing people and groups
- β³ π» Intune: assets to their primary user
- β³ π’ Organisation chart
- β³ π οΈ Entra directory sync β Developer Guide
- β³ π οΈ Entra import - review (#235)
- β³ π οΈ Entra follow-ups - review (#237-#240)
- β³ π οΈ Intune primary user - review (#236)
- β³ π οΈ Intune after an Entra import - review (#334)
- β³ π οΈ Organisation chart - reviews (#242, #265)
- π CardDAV contact sync
- Browser Extension
- API Reference
-
π REST API β how it works
- β³ π Proposal: custom fields, inventory import and change evidence (#194)
- β³ π οΈ Custom fields, evidence + inventory - review (#195-#197)
- β³ π οΈ Long forms in a task's Work view - review (#198)
- β³ π οΈ Empty Procedures fold - review (#247)
- β³ π« 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: Companies
- β³ π REST API: Chart of accounts
- β³ π REST API: Projects
- β³ π‘οΈ API threat protection
- β³ π§βπΌ REST API: Analysts
- β³ πΊοΈ REST API: Network Mapper
- β³ π§ Using the API docs page
- β³ π OpenAPI specification
- β³ β OpenAPI: kept correct
- β³ π οΈ Maintaining the catalogue
- Watchtower
-
Tickets
- β³ Reply subject and replying to one message
- β³ π Live updates
- β³ π οΈ Watching a ticket - Developer Guide (1 of 2)
- β³ βοΈ Ticket address suggestions - Developer Guide
- β³ π οΈ CSAT delay and reminder - Developer Guide
- β³ π οΈ Ticket checklist step types - Developer Guide (1 of 2)
- β³ π οΈ Right-click Attach checklist - Developer Guide
- β³ π οΈ Checklists in the REST API - Developer Guide
- β³ π A ticket's related block and attachments - Developer Guide
- β³ π οΈ Inbox.js map β Developer Guide
- β³ π οΈ Floating windows & the file viewer β Developer Guide
- β³ π οΈ Tickets settings β Developer Guide
- β³ π Simpler ticket and task forms - design (#218)
- β³ ποΈ The ticket workspace
- β³ π Forms on tickets
- β³ π The ticket table
- β³ π Rota copy and paste β Developer Deep Dive
- β³ β Checklists & SOPs
- β³ βοΈ Mandatory fields
- β³ π§Ύ Custom ticket fields
- β³ π·οΈ Ticket categories
- β³ π§Ύ A type for every new ticket
- β³ π₯ Assigning tickets to a team, and escalation
- β³ π’ One board across every company
- β³ π Ticket dashboard β Developer Guide
- β³ Mailbox Authentication
- β³ π€ Email send log
- β³ βοΈ Emailed reports
- β³ π Attachment size limits and large emails
-
β³ Basic IMAP mailboxes
- β³ π Basic IMAP without the PHP extension (#257)
- β³ π οΈ Basic IMAP client β Developer Guide
- β³ π§ͺ Basic IMAP client β Testing and verifying
- β³ π οΈ Basic IMAP - Sent copy - Developer Guide (#270)
- β³ π οΈ Verifying a mail folder β Developer Guide
- β³ π οΈ Mailbox providers β Developer Guide
- β³ π οΈ How Gmail mail collection works β Developer Guide
- β³ 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
- β³ π Case reports - a ticket and its work as one PDF
- β³ π Scheduled work in your own calendar
- Problem Management
-
Tasks
- β³ π οΈ Task board and subtasks β Developer Guide
- β³ π§βπ€βπ§ People on a task ("Involved")
- β³ π Repeating tasks
- β³ π’ Moving a task between companies
- π Projects
-
Assets
- β³ π’ Moving an asset between companies
- β³ π Shared asset locations
- β³ π§βπΌ Assigning assets to analysts
- β³ π Warranty and lease alerts
- β³ π Leases
- β³ π Saved table views
- β³ π¨οΈ Recording anything, and importing it
- β³ π§© Asset type layouts
- β³ π Inventory freshness
- β³ π Linked configuration item
- β³ π Connected assets
- β³ π Asset review
-
β³ π·οΈ QR asset labels
- β³ π οΈ QR asset labels β Developer Guide
- β³ π Asset label designer - design (#229)
- β³ π· Asset scanning β Developer Guide
- β³ π οΈ Asset tag numbering β Developer Guide
- β³ π οΈ Asset reconciliation β Developer Guide
- β³ π οΈ Reconciliation, tags + labels - review (#164)
- β³ π Who holds what, and handover documents
- β³ π₯οΈ The inventory agent (PowerShell)
- β³ ποΈ Proxmox VE servers
- β³ βοΈ VMware Cloud Director servers
- β³ πͺ Hyper-V and Failover Clusters
- β³ π 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
- Starters & leavers
- Domains
- ποΈ Files
- Contracts
- Service Status
- π Notifications
- π¨ War Room
- Self-Service Portal
- LMS
- Process Mapper
-
CMDB
- β³ π§± Designing your CMDB
- β³ π οΈ CMDB β Developer Guide
- β³ π οΈ Impact analysis β Developer Guide
- β³ π οΈ Data quality β Developer Guide
- β³ π οΈ Record references β Developer Guide
- β³ π οΈ CMDB assistant - Developer Guide
- β³ π Linked assets
- β³ π Linking an asset to its CI - design (#201)
- β³ π Discovery and redundancy-aware impact - design (#199)
- β³ π οΈ Relationship wording - review (#297)
- Network Mapper
-
Workflows
- β³ π οΈ Workflow engine β Developer Guide
- β³ π οΈ Workflows for tasks β Developer Guide
- β³ π οΈ Workflow notes on a ticket β Developer Guide
- β³ π οΈ Workflow category routing β Developer Guide
- β³ β° Time-Based Triggers
- β³ πͺ Webhooks
- β³ π¬ Webhook Message Formats
- β³ π HTTPS Certificates & CA Bundles
- β³ π οΈ SSL Verification β the Global Setting
- β³ π Workflow & Webhook Pitfalls
- Issue trackers (Jira, Azure DevOps)
- π‘ Zabbix (monitoring)
- π¬ Microsoft Teams alerts
- π 3CX (phone system)
-
System
- β³ π οΈ Preferences β Developer Guide
- β³ π§Ή Maintenance
- β³ βοΈ Emailed reports
- β³ π Finding people in the Analysts list
- β³ πͺ When an analyst leaves
- β³ π οΈ System β Branding β Developer Guide
- β³ π©Ί Page health (D019) β Developer Guide
- β³ β Feature Bingo
- β³ π Locations
- β³ π·οΈ Cost centres
- β³ π Chart of accounts
- β³ π· Photo Album
- β³ Demo Data
- β³ π οΈ Demo Data β Developer Guide
-
Overview
- β³ π Progress tracker
- β³ Concepts & vocabulary
- β³ Email routing & mailboxes
- β³ Settings: global vs per-company
- β³ Users & self-service
- β³ Staff cross-company access
- β³ One company supporting another
- β³ π’ 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
- β³ π #332 Files viewer would not open
- β³ π #333 Note attachments downloaded
- β³ πͺ #326/#327 Form webhook Full record
- β³ π #325 Form PDF download failed
- β³ π Small fixes in 5.1.0
- β³ π CMDB relationships read backwards (5.1.0)
- β³ π #301 CSAT delay was never applied
- β³ π #281 Comments imported from Jira never arrived
- β³ π #288 A change failed to open with Enforce approvals on
- β³ π Long non-Latin articles never got an embedding
- β³ π« #280 Email lookups scanned the whole table
- β³ β #266 Dragging a task told nobody
- β³ π #255 Article previews showed HTML codes
- β³ π #252 A link to a Knowledge article did not open
- β³ π #254 PDFs could not print Polish
- β³ π #250 Clicking a folder left the open article on screen
- β³ π« #260 The dashboard cog did nothing
- β³ π« #248 A department rule never matched on four routes
- β³ π A directory account could be matched to someone else
- β³ π« #244 Reply went to the assigned analyst
- β³ π Mailbox sign-in was not checked
- β³ π Four weaknesses around signing in
- β³ π A lookup answer could name any record
- β³ π« #227 A mailbox could not be deleted
- β³ π« #226 A picture on a note could not be closed
- β³ π» #222 The Assets page never loaded in 4.3.0
- β³ βοΈ #214 A "contains" condition did not match capitals
- β³ βοΈ #216 A workflow email from a chosen mailbox always failed
- β³ π³ The Docker image was missing PHP extensions
- β³ π A failed DNS lookup read as a missing record
- β³ π A link could change data
- β³ π Access outlived its removal
- β³ π Knowledge articles could run scripts
- β³ π Knowledge writes ignored permissions
- β³ π Company boundaries (Oct 2026)
- β³ π Smaller gaps (Oct 2026)
- β³ π Escaped text could leave its attribute
- β³ π Task descriptions were not cleaned
- β³ π Task documents ignored company scope
- β³ π Repeating tasks ignored company scope
- β³ π¬ #183 Mailbox checks overlapped
- β³ π #188 Transfer lock shown off when unknown
- β³ β #184 Subtask names and Overdue
- β³ π§© #186 Teams: the buttons did nothing
- β³ π Text in a change could run script
- β³ π AI settings changed without the AI permission
- β³ πͺ #178 Deleting an analyst failed
- β³ π« A team with no departments showed 0 tickets
- β³ π Projects: adding a phase did nothing on Save
- β³ π Leavers could still sign in
- β³ πΌοΈ Logo and courses broke on Apache with PHP-FPM
- β³ π’ 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