Ticket View Hooks

1 views Markdown

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

filterticket.departments
ClientTickets passed by link

Runs once the department list shown to a customer is built. Hiding a department also stops tickets being opened there.

Parameters 2
$departmentsarrayby linkThe visible departments.
$langstringThe active language.
Return 1
voidThe return is ignored; you write over the data.
Listener
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

filterticket.priorities
ClientTickets selected flag included

Runs once the priorities a customer may pick are prepared.

Parameters 1
$prioritiesarrayby linkThe options, each carrying a value, a label and whether it is selected. Drop the selected one and the form is left with nothing marked.
Return 1
voidThe return is ignored; you write over the data.
Listener
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

filterticket.related_services
ClientTickets grouped structure

Runs once the service picker shown while opening a ticket is prepared.

Parameters 2
$serviceGroupsarrayby linkThe grouped picker data; each group has a label and items. Do not flatten it: the template expects the group layer.
$uidintThe active account opening the ticket. Not the login: a sub-user may be listing another account’s services.
Return 1
voidThe return is ignored; you write over the data.
Listener
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

filterticket.thread
ClientTickets newest first

Runs before the reply thread of a ticket reaches the screen.

Parameters 2
$threadarrayby linkThe reply rows, newest first. Each carries its author, date and whether it came from staff.
$ctxarrayContext: the ticket id, the login id and whether it is a guest. For a guest the login id is zero.
Return 1
voidThe return is ignored; you write over the data.
Listener
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

filterticket.detail_data
AdminTickets all the page data

Runs once the ticket detail page is prepared. What you hold is all the data going to the template.

Parameters 2
$dataarrayby linkAll the page data. Take care not to overwrite existing keys: a value lost here leaves a blank area on the page.
$ticketarrayThe active ticket.
Return 1
voidThe return is ignored; you write over the data.
Listener
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

filterticket.list_query
AdminTickets the query builder

Runs before the ticket list query is executed. What you hold is not ready rows but the query itself.

Parameters 3
$stmtobjectby linkThe query builder; you may chain conditions onto it.
$filtersarrayThe resolved filters: status, department, customer, assignment and priority.
$rCountboolWhether this is the counting query. ? The hook runs twice for one listing: once to count, once for the data. Add your condition to only one and the paging stops matching the rows.
Return 1
voidThe return is ignored; you chain the condition onto the builder.
Listener
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

filterticket.list_rows
ClientTickets per page

Runs before the customer ticket list reaches the screen.

Parameters 2
$rowsarrayby linkThe rows of that page: reference, subject, status, rating and department. Only the page being viewed arrives, not the whole list.
$uidintThe active account; the rows are already scoped to it.
Return 1
voidThe return is ignored; you write over the data.
Listener
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

filterticket.bulk_action_ids
AdminTickets narrowing only

Runs before a bulk action is applied. This is how you keep certain tickets out of it.

Parameters 2
$idsarrayby linkThe tickets to be handled. This list is for narrowing: remove entries. Adding one closes or deletes a ticket the administrator never picked.
$actionstringThe action applied: closing, deleting and their blocking variants.
Return 1
voidThe return is ignored; you write over the data.
Listener
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

filterticket.access_groups
ClientTickets passed by link

Runs once the access groups shown while opening a ticket are prepared.

Parameters 2
$groupsarrayby linkThe visible groups: id, name and icon.
$langstringThe active language of the page.
Return 1
voidThe return is ignored; you write over the data.
Listener
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 list query runs twice for one listing

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.

The bulk list is for narrowing

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.

Was this helpful?

Thanks for your feedback!

Still Need Help?

Our support team is here around the clock for anything you can't find above.