Skip to content

Reply Subject Developer Guide

Ed Mozley edited this page Nov 13, 2026 · 1 revision

Reply subject, and replying to one message β€” Developer Guide

5.2.0 Β· #306 Β· User guide: Tickets Β§ The subject

πŸ”‘ The one-line version

The Reply / Forward dialog's subject used to be a hidden field built from the ticket's subject. That lost an outside provider's own number (#TCK89633), so every follow-up opened a new ticket on their side. Now:

  • the subject is a visible box;
  • each message in the thread shows its subject and has its own Reply / Forward, which start from that subject;
  • the server's ticketOutboundSubject() makes sure the ticket's [SDREF:...] survives whatever is typed.

πŸ“ Files

File What
includes/email_threading.php ticketOutboundSubject($subject, $ticketNumber)
api/tickets/send_email.php Calls it after the access check, before anything is sent or saved
api/tickets/get_ticket_thread.php Returns each email's subject
tickets/index.php #emailSubject is a text box (.reply-subject, maxlength 255); inbox.js v=198, inbox.css v=87
assets/js/inbox.js composeSubject(), openReplyModal(fromEmail) / openForwardModal(fromEmail), replyFromThread(), the thread's subject line and per-message buttons
assets/css/inbox.css .reply-subject, .thread-subject, .thread-split-btn + .thread-split-btn
lang/en/tickets.php, tickets/help.php reply_modal.subject, thread.*, help.comments.subject_note

πŸ” The flow

// The dialog's starting subject - prefix not doubled, reference added if missing
function composeSubject(prefix, base, ticketNumber)    // ('RE', 'RE: Printer', 'ABC-1') -> 'RE: Printer [SDREF:ABC-1]'

// From the thread: the message's own subject; Reply answers its sender if they wrote to us
replyFromThread(emailId, 'reply' | 'forward')          // looks the message up in window.__threadEmails
openReplyModal(fromEmail)                              // To = fromEmail.from_address when inbound,
                                                       //      else the ticket's reply address (as before)
// api/tickets/send_email.php - whatever the browser sent
$subject = ticketOutboundSubject((string)$subject, $ticketNumber);

ticketOutboundSubject():

  1. Replaces control characters (CR, LF) with a space. A typed line break must never reach a mail header.
  2. Walks every [SDREF:...] (TicketNumbering::REF_PATTERN). It keeps this ticket's and removes any other ticket's, which would otherwise thread replies into that ticket.
  3. Adds this ticket's reference at the end when it was missing. The words are cut so the whole thing fits emails.subject (255), and the reference is never cut.
  4. An empty subject becomes just the reference. The dialog still refuses to send an empty one.

Inbound threading needed no change: REF_PATTERN finds [SDREF:...] anywhere in a subject, so a provider putting its own number first does not matter.

⚠️ Traps

  • TRAP: never send an analyst-typed subject without ticketOutboundSubject(). A removed or foreign [SDREF] silently breaks threading, and the provider's reply lands as a new ticket.
  • TRAP: the thread's buttons work only for email messages (channel empty or email). A WhatsApp or web-chat message has no subject to carry, and its replies go through the channel composer.

πŸ§ͺ Tests

  • tests/ticket-outbound-subject.php (8, no database):
    • the provider's number kept;
    • a removed reference put back;
    • the reference found whatever its case and spacing;
    • another ticket's reference swapped for this one;
    • a CR LF + Bcc: injection flattened to text;
    • an empty subject;
    • a long subject kept within 255 with the reference whole;
    • multi-byte text cut on a character.
  • Checked in Chrome on the test install:
    • inbox.js loads;
    • every message shows its subject and its two buttons;
    • Reply from a message we sent goes to the requester;
    • Forward from a message starts from its subject with an empty To;
    • the toolbar's Reply is unchanged.

See also: Tickets Β§ Email threading Β· Ticket numbering

FreeITSM

Getting Started

Modules

Multi-tenancy (planned)

Responses to feedback

Blue sky thinking

Bugs resolved

Pull requests

Links

Clone this wiki locally