Panel Screen Data Hooks

3k visualizações Markdown

The nineteen hooks over what the customer, service, support and tool screens show, including client badges and the AI field helper.

Overview

The hooks that change what panel screens show live here: the customer detail and list, the service management cards, add-on buttons, critical notifications and the theme version lookup. On the ticket detail you can badge the related service.

Two differ from the rest: the profile tabs and the security accordion hand you a component object rather than an array, and you add through a method call.

Reference

Changing the customer extra details

filteradmin.client_detail.info
admin/users passed by link

Runs once the extra detail fields on a customer page are read.

Parameters 2
$client_infoarrayby linkThe extra detail pairs. You may overwrite an existing key or add a new one.
$user_idintThe customer id.
Return 1
voidThe return is ignored; you write over the data.
Listener
Hook::add('filter:admin.client_detail.info', 10, function (&$client_info, $user_id) {
    $client_info['acme_tier'] = Acme::tierOf($user_id);
});

Adding a sub-tab to a customer profile

filteradmin.client_detail.profile_subtabs
admin/users a component object

Runs while the vertical tabs of a customer profile are built. Add a tab of your own here.

Parameters 3
$profileTabobjectby linkThe tab component. It is an object, not an array: you add a tab by calling its add method, not by appending to a list.
$userarrayThe active customer record.
$user_idintThe customer id.
Return 1
voidThe return is ignored; the change happens through the object’s method.
Listener
Hook::add('filter:admin.client_detail.profile_subtabs', 10,
    function (&$profileTab, $user, $user_id) {
        // An object, not an array: call its method.
        $profileTab->add('acme', 'Acme', Acme::renderPanel($user_id));
    });

Adding a section to the security area

filteradmin.client_detail.security_accordion
admin/users a component object

Runs while the security settings accordion of a customer is built.

Parameters 3
$secAccordionobjectby linkThe accordion component; add a section through its add method.
$client_infoarrayThe extra details of the customer.
$user_idintThe customer id.
Return 1
voidThe return is ignored; the change happens through the object.
Listener
Hook::add('filter:admin.client_detail.security_accordion', 10,
    function (&$secAccordion, $client_info, $user_id) {
        $secAccordion->add('acme-sessions', 'Acme sessions', Acme::sessions($user_id));
    });

Changing the customer statistics

filteradmin.client_detail.statistics
admin/users passed by link

Runs once the counters on a customer page are worked out.

Parameters 2
$user_statsarrayby linkThe statistics: active and inactive services, invoices and tickets.
$user_idintThe customer id.
Return 1
voidThe return is ignored; you write over the data.
Listener
Hook::add('filter:admin.client_detail.statistics', 10, function (&$user_stats, $user_id) {
    $user_stats['acme_open_cases'] = Acme::openCases($user_id);
});

Changing the customer list filters

filteradmin.client_list.filters
admin/users the model must support it

Runs once the filter criteria of the customer list are built.

Parameters 1
$dynamic_filterarrayby linkThe filter criteria: group, status, language, country, address and date. 🔴 A key you add must also be handled in the listing query; otherwise the filter appears but narrows nothing.
Return 1
voidThe return is ignored; you write over the data.
Listener
Hook::add('filter:admin.client_list.filters', 10, function (&$dynamic_filter) {
    // A new key must be handled in the listing query too.
    $dynamic_filter['acme_tier'] = Acme::tierFilterValue();
});

Changing the service management cards

filteradmin.service_detail.management_cards
admin/services two lists together

Runs once the management cards on a service detail are prepared. Add a card of your own, hide one, or change the order.

Parameters 1
$contextarrayby linkTwo lists together: cards holds the card content and layout says which card sits in which column and in what order. 🔴 Update both: a card missing from the layout never appears, and a layout key with no card is quietly skipped.
Return 1
voidThe return is ignored; you write over the context.
Listener
Hook::add('filter:admin.service_detail.management_cards', 10, function (&$context) {
    // Update BOTH: the card and the layout.
    $context['cards']['acme'] = Acme::renderCard($context['service'] ?? []);
    $context['layout']['right'][] = 'acme';
});

Badging the related service of a ticket

filteradmin.ticket_detail.service_badges
admin/tickets one badge per service

Runs once when a ticket detail opens. Give a short badge for any service of the customer, such as the version a license reports. The badge of the selected service shows in the header of the Related Service card.

Parameters 3
$badgesarrayby linkArrives empty; key it by service id. Each value takes label (required, plain text) and optional title (tooltip), tone, icon (a bi-* class) and tab. With tab the badge opens that tab of the service page in a new window, for example management. The panel builds the address. A tab with anything other than letters, digits, - and _ leaves a plain badge.
$servicesarrayThe customer's services in groups; each row carries id, product_id, options and status. Read it; changing it does not change the service list.
$ticketarrayThe ticket record. Read it.
Return 1
voidThe return is ignored; you write into $badges. The label and the tooltip show as plain text, never as HTML.
Listener
Hook::add('filter:admin.ticket_detail.service_badges', 20, function (&$badges, $services = []) {
    foreach ((array) $services as $group)
        foreach ((array) ($group['items'] ?? []) as $row) {
            $plan = (string) ($row['options']['acme_plan'] ?? '');
            if ($plan !== '') $badges[(int) $row['id']] = ['label' => $plan, 'icon' => 'bi-tag', 'tab' => 'management'];
        }
});

Changing the add-on settings buttons

filteradmin.addon_configure.buttons
admin/tools emptying removes them all

Runs once the buttons on an add-on settings page are prepared.

Parameters 1
$buttonsarrayby linkThe button map; each carries its text, class, icon and click action. Empty the array entirely and no button appears.
Return 1
voidThe return is ignored; you write over the data.
Listener
Hook::add('filter:admin.addon_configure.buttons', 10, function (&$buttons) {
    // Emptying it entirely leaves no buttons at all.
    $buttons['acme-sync'] = [
        'text'    => 'Sync Acme',
        'class'   => 'btn btn-outline-primary',
        'icon'    => 'bi bi-arrow-repeat',
        'onclick' => 'acmeSync()',
    ];
});

Changing the address of an uploaded image

filteradmin.editor_upload_url
admin/tools passed by link

Runs after the address of an image uploaded into the editor is produced. This is how you move images onto a delivery network.

Parameters 2
$urlstringby linkThe address produced. Leave it alone and the local one stays.
$file_pathstringThe full path of the file on the server.
Return 1
voidThe return is ignored; you write over the data.
Listener
Hook::add('filter:admin.editor_upload_url', 10, function (&$url, $file_path) {
    $url = Acme::pushToCdn($file_path) ?: $url;
});

Following an image upload

actionadmin.editor_image_uploaded
admin/tools address after the filter

Runs after an image is uploaded into the editor.

Parameters 2
$file_pathstringThe full path of the file on the server.
$urlstringThe final address, as it stands after the address filter ran.
Return 1
voidThe return is ignored.
Listener
Hook::add('action:admin.editor_image_uploaded', 10, function ($file_path, $url) {
    // The address is the one AFTER the filter ran.
    Acme::noteAsset($file_path, $url);
});

Reporting where a library image is used

filteradmin.icon_library.usage
admin/products admin/tickets before the delete

Runs before the operator deletes an image from the Icon Library. Add your module's records: when the list is not empty the panel refuses the delete and shows their names. Runs from 5.0-beta.4 (with its latest fix package) onward; earlier versions do not run it.

Parameters 2
$usagearrayby linkThe records using the image, each ['type', 'id', 'name']. It arrives with product, add-on, requirement and support access group records; you append your own.
$namestringThe stored name of the image (<date>/<file>), the value records keep in their icon field.
Return 1
voidThe return is ignored; you add to the list. An empty name becomes #<id>, and the refusal shows the first ten names.
Listener
Hook::add('filter:admin.icon_library.usage', 10, function (&$usage, $name) {
    foreach (Acme::itemsUsingIcon($name) as $item)
        $usage[] = ['type' => 'acme-item', 'id' => (int) $item['id'], 'name' => $item['title']];
});

Adding to the critical notification list

filteradmin.notifications.critical
admin/inc fixed item shape

Runs once the critical notification list in the panel header is prepared.

Parameters 1
$critical_transaction_notificationsarrayby linkThe notification items. Each must carry the fields the template expects: id, icon, type, message, date, read state and buttons. An item missing one breaks the display.
Return 1
voidThe return is ignored; you write over the data.
Listener
Hook::add('filter:admin.notifications.critical', 10, function (&$critical_transaction_notifications) {
    // Fill every field the template expects.
    foreach (Acme::criticalAlerts() as $a) $critical_transaction_notifications[] = $a;
});

Following a panel notification

actionadmin.notified
Notification zero means not written

Runs when a system notification lands in the panel.

Parameters 4
$namestringThe event name.
$dataarrayThe event data: message placeholders and what is used to spot repeats.
$levelstringThe level: error, warning, information or success.
$event_idintThe id of the record created. A zero means nothing was written: either the same notice already existed or the write failed. Test before using the id.
Return 1
voidThe return is ignored.
Listener
Hook::add('action:admin.notified', 10, function ($name, $data, $level, $event_id) {
    // Zero means nothing was written.
    if ($level === 'error' && $event_id) Acme::page($name, $data);
});

Taking over the theme version lookup

filteradmin.theme_version_check
admin/settings first filled wins

Runs before a theme is asked about a new version. Return an answer and no remote call is made.

Parameters 2
$keystringThe theme folder key.
$manifestarrayThe theme manifest: its update address and installed version.
Return 1
string|nullA filled text is taken as the raw response and the call is skipped. The first filled return by priority wins and the rest never run. With an empty return the core makes its usual request.
Listener
Hook::add('filter:admin.theme_version_check', 10, function ($key, $manifest) {
    // A filled return means NO remote call is made.
    if (!str_starts_with($key, 'Acme')) return null;

    return Acme::versionJson($key);
});

Adjusting the theme version response

filteradmin.theme_version_response
admin/settings the raw response

Runs after the version response arrives and before it is decoded. Its sibling picks the source; this hook adjusts what came back.

Parameters 2
$keystringby linkThe theme key. It is passed by link but already consumed: changing it has no effect.
$manifestarrayby linkThe theme manifest, also consumed.
Return 1
voidThe return is ignored; you write over the raw response. The core then decodes it; with no version field the result is empty, but it is not treated as up to date and is not cached.
Listener
Hook::add('filter:admin.theme_version_response', 10, function (&$key, &$manifest) {
    // Both parameters are consumed: changing them has no effect.
    Acme::noteVersionCheck($key);
});

Adding a badge beside a client name

filteradmin.client_badges
Utilities every client name

Lets a module mark a client account ("developer", "partner", "VIP") once. The core shows the mark wherever the panel names the client, in that screen's badge style: list cells (cell), the client info card (card) and the client detail header (detail). The core has no badge of its own; the slot is for modules.

Parameters 4
$badgesarrayby linkThe badge list; add items to it. Each item: label (required), icon (a bi-* class), link, title, key and tone (detail header only). An item without a label is dropped; every value is escaped by the core, so pass plain text.
$user_idintThe client id.
$contextstringcell, card or detail.
$extraarrayExtra context from the caller; empty today.
Return 1
voidThe return is ignored; you change the list by link.
Listener
Hook::add('filter:admin.client_badges', 20, function (&$badges, $user_id, $context = '', $extra = []) {
    if (!Acme::isPartner((int) $user_id)) return null;
    $badges[] = ['key' => 'acme-partner', 'label' => 'Partner', 'icon' => 'bi-handshake'];
    return null;
});

Offering an engine to the AI field helper

filteradmin.ai_assist.engines
AiFieldAssist the AI field helper

The AI helper beside admin form fields has no provider of its own. A module declares itself here as an engine. It names its actions, its menu label and icon, and its models. With no engine declared, the trigger is not shown at all.

Parameters 1
$enginesarrayby linkThe engine list; add one item: key (prose for text actions, translate for translation), actions, label, icon (without the bi- prefix), models and default_model. An item with no key or no actions is dropped.
Return 1
voidThe return is ignored; you change the list by link.
Listener
Hook::add('filter:admin.ai_assist.engines', 1, function (&$engines) {
    if (!License::valid_addon('acme-ai')) return null;
    $engines[] = ['key' => 'prose', 'actions' => ['improve', 'shorten', 'expand'], 'label' => 'Acme AI', 'icon' => 'robot'];
});

Describing a field kind to the AI helper

filteradmin.ai_assist.profiles
AiFieldAssist the field kinds

A module that puts the AI trigger on its own screens adds its field kinds here. The description tells the model what the field is. It also shapes the suggestion: a name or a sentence, with or without a full stop. An unknown kind falls back to the core text profile.

Parameters 1
$profilesarrayby linkA kind => description map seeded with the core profiles. Write the description in English: it is an instruction to the model and does not follow the panel language.
Return 1
voidThe return is ignored; you change the list by link.
Listener
Hook::add('filter:admin.ai_assist.profiles', 1, function (&$profiles) {
    $profiles['acme-plan-title'] = 'the title of a hosting plan shown on the price table. A short noun phrase, no closing period.';
});

Answering an AI field request

filteradmin.ai_assist.run
AiFieldAssist one AI request

Hands the prepared request to the engine that took the action. Your module calls its provider and writes the text into $result. The core never knows a provider name. If no engine took the action, this hook is not called at all.

Parameters 2
$resultarrayby linkStarts empty. Fill text (required) and, if you like, model, engine, icon and plain. Put the provider's message in error and the operator sees it; an empty text becomes a "no suggestion" error.
$requestarrayThe ready request: action, engine, kind, text, html, lang, target, model, system and user. Leave it alone when engine is not yours or the result is already filled.
Return 1
voidThe return is ignored; you change the list by link.
Listener
Hook::add('filter:admin.ai_assist.run', 1, function (&$result, $request = []) {
    if ($result || (string) ($request['engine'] ?? '') !== 'prose') return null;
    $answer = AcmeProvider::complete($request['system'], $request['user'], $request['model']);
    $result = $answer === false ? ['error' => 'Provider is unreachable.'] : ['text' => $answer];
});

Pitfalls

Adding a card takes two lists

In the service management cards the content sits in one list and the layout in another. Add only the content and the card never appears; write only a name into the layout and that name is quietly skipped. Update both.

The tabs and the accordion are objects, not arrays

The profile sub-tabs and the security accordion hand you a component object. A listener treating it like an array fails; you add through the object’s own method.

Foi útil?

Obrigado pela sua opinião!

Ainda precisa de ajuda?

A nossa equipa de apoio está disponível a qualquer hora para ajudar com o que não encontrar acima.