Ticket View Hooks
The nine hooks over the lists, the conversation and the pickers on screen.
Overview
Everything visible on screen passes through these hooks: the lists, the conversation thread, the department and service pickers, the data of the detail page.
One works differently from the rest: the list query hands you not ready rows but the query itself, and it is called twice for one listing.
Reference
Changing the department list
Runs once the department list shown to a customer is built. Hiding a department also stops tickets being opened there.
Hook::add('filter:ticket.departments', 10, function (&$departments, $lang) {
// Hiding one also stops tickets being opened there.
$departments = array_values(array_filter($departments,
fn ($d) => Acme::departmentOpen((int) ($d['id'] ?? 0))));
});Changing the priority options
Runs once the priorities a customer may pick are prepared.
Hook::add('filter:ticket.priorities', 10, function (&$priorities) {
// Drop the selected one and nothing stays marked on the form.
$priorities = array_values(array_filter($priorities,
fn ($p) => (int) ($p['value'] ?? 0) < 4));
});Changing the service picker
Runs once the service picker shown while opening a ticket is prepared.
Hook::add('filter:ticket.related_services', 10, function (&$serviceGroups, $uid) {
// Do not flatten it: the template expects the group layer.
$serviceGroups[] = ['label' => 'Acme', 'items' => Acme::servicesFor($uid)];
});Changing the conversation thread
Runs before the reply thread of a ticket reaches the screen.
Hook::add('filter:ticket.thread', 10, function (&$thread, $ctx) {
// The order is newest first.
foreach ($thread as $i => $row)
if ($row['is_staff'] ?? false) $thread[$i]['author'] = Acme::displayName($row);
});Changing the detail page data
Runs once the ticket detail page is prepared. What you hold is all the data going to the template.
Hook::add('filter:ticket.detail_data', 10, function (&$data, $ticket) {
// Do not overwrite existing keys.
$data['acme_risk'] = Acme::riskScore((int) ($ticket['user_id'] ?? 0));
});Narrowing the list query
Runs before the ticket list query is executed. What you hold is not ready rows but the query itself.
Hook::add('filter:ticket.list_query', 10, function (&$stmt, $filters, $rCount) {
// The hook runs TWICE: to count and for the data. Add it to both.
$stmt->where('t.did', '!=', Acme::INTERNAL_DEPT);
});Changing the list rows
Runs before the customer ticket list reaches the screen.
Hook::add('filter:ticket.list_rows', 10, function (&$rows, $uid) {
// Only the page being viewed arrives.
foreach ($rows as $i => $r) $rows[$i]['subject'] = Acme::shorten($r['subject'] ?? '');
});Narrowing a bulk action
Runs before a bulk action is applied. This is how you keep certain tickets out of it.
Hook::add('filter:ticket.bulk_action_ids', 10, function (&$ids, $action) {
// It is for NARROWING: do not add.
$ids = array_values(array_filter($ids, fn ($id) => !Acme::underLegalHold((int) $id)));
});Changing the access groups
Runs once the access groups shown while opening a ticket are prepared.
Hook::add('filter:ticket.access_groups', 10, function (&$groups, $lang) {
$groups = array_values(array_filter($groups,
fn ($g) => Acme::groupVisible((int) ($g['id'] ?? 0))));
});Pitfalls
The hook is called once for the count and once for the data. Add your condition to only one and the paging stops matching the real row count: the user sees empty pages. Unless you branch on the counting flag, add it to both.
Entries are removed through this filter. Adding one closes or deletes a ticket the administrator never picked, and the list they approved on screen parts ways with the list actually handled.
Related Articles
- Support Ticket Hooks
- Customer Account Hooks
- How Hooks Work
Thanks for your feedback!
Our support team is here around the clock for anything you can't find above.