# Management Panel Data and Gates

https://dev.wisecp.com/es/management-panel-data-and-gates

The management panel away from the screen. 23 filters change lists and forms, 6 gates sit on staff actions, and 19 hooks report an event.

## Overview

The panel's strongest hooks are here. **List and form filters** touch nearly every screen, because lists and forms come from one structure. You write one filter and it runs on dozens of screens.

That power cuts both ways. A filter that does not pick its target touches **the whole panel**. Each filter hands you the table or form name, and checking it is your job.

## Reference

### The ones that change data

The main ones among twenty-three. Most hand the value over by reference and ignore the return. The operation filters carry a **mixed** contract acting on what you return.

- **filter:admin.table.rows**: Every list row — the cells, the row classes, the raw record. The row changes by reference; the return is ignored.
- **filter:admin.table.columns**: The list columns; you add, drop and reorder them. The array changes by reference; the return is ignored.
- **filter:admin.form_builder**: The fields the form builder produces.
- **filter:admin.dashboard.statistics**: The number cards on the dashboard.
- **filter:admin.dashboard.widgets**: The list of dashboard boxes.
- **filter:admin.menu_management**: The menu tree.
- **filter:admin.notifications**: The notifications reaching staff.
- **filter:admin.bubble_counts**: The counter bubbles on the menu.
- **filter:admin.health_status**: The system health indicator.
- **filter:admin.settings_save**: The settings about to be saved.
- **filter:admin.operation.before**: Before an operation runs — you can stop it or hand back its output.
- **filter:admin.operation.after**: After an operation ran, before the answer leaves.
- **filter:api.response**: An API answer — the body going to consumers outside the panel.

### The ones that stop

All six sit on staff and permission work. Returning a non-empty string stops the action, and the operator reads that text as the error.

- **gate:admin.staff_create**: Opening a new staff account. Returning a non-empty string vetoes it; that text becomes the error.
- **gate:admin.staff_delete**: Deleting a staff account. Returning a non-empty string blocks the deletion; that text becomes the error.
- **gate:admin.privilege_save**: Saving a privilege group.
- **gate:admin.department_delete**: Deleting a department.
- **gate:admin.editor_upload**: Uploading a file from the editor.
- **gate:admin.password_change**: Changing a password.

### The ones that report

- **action:admin.staff_created**: A staff account was opened.
- **action:admin.settings_saved**: Settings were saved.
- **action:admin.activity_logged**: An activity was recorded in the panel.
- **action:security.settings_updated**: Security settings changed.
- **action:admin.two_factor_changed**: Two-step verification was switched on or off.

### A filter with a target

```php
// The second parameter IS THE TABLE NAME. Without checking it the filter runs on every list.
Hook::add('filter:admin.table.rows', 10, function (&$row, $table_name) {
    if ($table_name !== 'serviceList') return null;

    $id = (int) ($row['model']['id'] ?? 0);
    if (Acme::flagged($id)) $row['attributes']['class'] = 'table-warning';
});
```

## Pitfalls

> **A filter that skips the table name hits every list**
> 
> Row and column filters run on **every** list in the panel. Skip the name check in the second parameter and a rule meant for the service list lands on invoices, customers and tickets. Make your first line the **name check**.

> **Adding a column does not fill the cell**
> 
> The column filter opens **the heading** and nothing else. The cell value for that column comes from the row filter. Writing one without the other leaves an empty column, or a cell that shows up nowhere.

> **The operation filter acts on what you return**
> 
> The before-operation filter carries a **mixed** contract. An error array stops the operation. A variables array changes the inputs. An output array answers without running it at all. A wrong key name **silently** means "carry on".

> **The row filter runs once per row**
> 
> On a hundred-row list the row filter runs **a hundred times**. A query inside it turns the list into a hundred queries. Gather what you need **once** into a static variable and read only that inside the listener.

## Related Articles

- [Hooks in the Management Panel](https://dev.wisecp.com/en/hooks-in-the-management-panel)
- [Customer Site Data and Gates](https://dev.wisecp.com/en/customer-site-data-and-gates)
- [Customer Account Hooks](https://dev.wisecp.com/en/customer-account-hooks)
