-
Notifications
You must be signed in to change notification settings - Fork 16
Issue 103 Share With Requester Reached Nobody
Ticking Share this with the requester on a note made the Attach button vanish with no explanation, and then delivered the note somewhere the requester might never look. Afterwards there was no way to tell, reading the ticket back, which notes had been shared and which had not.
Reported in issue #103 by dschipfel, who described all three and suggested how Zammad handles the last one.
Fixed across 1a48443aβ¦b1103311, released as updates #1201, #1202, #1203 and #1204.
Only one of the three was a bug in the ordinary sense. The other two were things FreeITSM had decided not to do, without ever saying so β which is its own kind of fault.
Add a note. Tick Share this with the requester. The Attach button under Files disappears. Nothing says why.
Write the note and save it. It appears in the ticket history looking exactly like every other note. The requester does not get an email. Come back a week later and nothing on screen tells you whether that note was private or not.
This one was deliberate. Note attachments arrived knowing the self-service portal had no way to serve a document, so a shared note carrying a file would have promised the requester something they never received. Rather than let that happen, the button hid itself.
The reason was even written down, in a hint beside the button. But:
// before
if (hint) hint.style.display = (isShared && pendingNoteFiles.length) ? '' : 'none';The explanation only appeared if you had already chosen a file. Tick Share first β which is the natural order, since you decide who it is for before you decide what goes on it β and the button vanished in silence.
A control that disappears without saying why is indistinguishable from one that is broken. The report is filed under "bug" for exactly that reason, and fairly.
Rather than explain the restriction better, the restriction is gone. api/self-service/get_document.php now serves a shared note's files to the requester who raised the ticket, so Attach stays put whether or not the note is shared.
The rule that keeps an internal note's files private is one join, not a separate check:
documents β document_links (parent_type='ticket_note')
β ticket_notes (is_internal = 0)
β tickets (user_id = this requester)Internal files are therefore unreachable by construction, rather than by a condition somebody could later reorder or forget.
Attaching a file can still fail β a .txt whose contents are program code, for instance, which the upload guard refuses deliberately. The message named the file and stopped there, leaving you to guess whether the upload broke, the file was too large, or something about it was not allowed.
The server had said exactly what was wrong all along; the message threw the reason away before showing it. assets/js/documents.js collects it per file, and the shared documents panel in the same file displays it β the note path was the copy that dropped it.
That matters more than an ordinary unhelpful message. A note has no per-note file controls, so the file cannot be attached afterwards and the note cannot be un-saved. That one message is the only chance to explain.
Also by design, and also never said out loud.
A shared note appears in the self-service portal. It is not emailed. For a requester who signs into the portal that is fine; for one who only ever emails you β which is most of them, on most service desks β "Share with the requester" reached nobody at all.
The word share reads as send. The reporter read it that way, and so would anyone.
Three things changed:
The hint now says where it goes.
They will see it, and any files you attach, in the self-service portal. It is not emailed β use Reply for that.
It can email, if you want it to. Tickets β Settings β Email templates gained a trigger, Note shared with requester. Write a template against it and every shared note emails the requester. Nothing is sent until you write one, so no existing installation starts emailing customers because of an upgrade.
The template decides how much it says. Include the new [note_text] merge code and the mail carries the note; leave it out and it says there is an update and points at [ticket_url]. That is what stops it duplicating Reply.
Because it must never fire for an internal note, and a name that says "added" implies it fires for all of them. Requiring an administrator to know a hidden rule in order to predict what a setting does is the same failure as the vanishing button β and it is what caused this report in the first place. It is called Note shared with requester, which is what fires it.
The third complaint, and the one with no defence at all: a ticket mixing private remarks with customer-facing ones could not be audited after the fact.
Every note now carries a label β Internal, or Visible to requester β and a shared note takes a green edge down its side.
is_internal had been returned by the API, and cast to a proper boolean, the whole time. The renderer simply never looked at it.
If only shared notes were marked, then no label would have to mean internal by implication. That puts weight on an absence β and any note kind added later would silently inherit the meaning. A tracker-imported note gets no visibility label, because it is not ours to describe as either.
The reporter suggested copying Zammad, which paints internal notes red. FreeITSM does not, for two reasons:
- Internal is the default and the majority here. A note is internal unless somebody deliberately says otherwise, so colouring the majority red makes an ordinary ticket history read as a wall of alarms.
- Red is this design's colour for things that are wrong. A colleague writing a private remark is not one of them.
So the eye goes to the consequential, unusual state instead β this one left the building. That also reuses the signal .note-item already establishes, where the left border says what kind of note something is, rather than inventing a second visual language for the same question.
| File | |
|---|---|
assets/js/inbox.js |
renderNotes() reads is_internal; the Attach explanation always shows while the restriction still existed |
assets/css/inbox.css |
.note-item-shared, .note-visibility
|
lang/*/tickets.php |
note_visibility.* in all 24 languages |
| File | |
|---|---|
api/self-service/get_document.php |
new β the portal twin |
api/self-service/get_ticket_detail.php |
lists a shared note's documents |
self-service/tickets.php |
renders them, plus a safeExternalUrl() the portal did not have |
assets/js/inbox.js |
Attach no longer hides; saveNote() no longer offers to discard files |
| File | |
|---|---|
assets/js/inbox.js |
the toast now carries the server's reason, not just the filename |
| File | |
|---|---|
api/tickets/save_email_template.php |
note_shared in the whitelist |
includes/services/tickets.php |
the guarded send |
includes/template_email.php |
see the escaping fix |
tickets/settings/index.php |
the option, in all three places the trigger list appears |
The labels, on a real ticket carrying one internal and one shared note side by side: correct label, correct class and correct border on each β checked in both themes. The first attempt at that check flipped data-theme-mode, which is not the attribute theme.css keys off, so light and dark returned identical values and it would have passed regardless. Redone against data-theme: contrast 5.04 and 6.49 in light, 6.21 and 9.79 in dark.
The portal endpoint, over HTTP as a real portal user, with a positive control so a blanket 404 could not pass as security β see the table in the developer guide.
The trigger: off before configured, found once configured, the guard structurally outermost, and a note containing a tag, an ampersand, a quote and a newline escaped exactly once.
Attach files to a note whether or not you share it β the requester gets them.
Reading a ticket back, every note says who could see it.
And if your requesters live in email rather than the portal, add a template under Tickets β Settings β Email templates against Note shared with requester, and sharing a note will reach them.
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)