Row actions live in the far-right toolbar rendered by kas/core/table_actions.php. Configure them in the owning page before including core/table_view.php. This differs from $actionButton, which renders section-level buttons above a Table/Form View, and from $multiselect, which operates on selected rows.
Manual link actions
$rowActions is an ordered list of full configuration arrays. It requires no extra option letter. The table query must select the actual $idfield and every placeholder/condition column. Arrays are the modern extensible configuration; legacy strings elsewhere in the framework remain shorthand, not this API.
// A manual link to the existing Core invoice editor; the query selects INID and type.
$rowActions = [[
'link' => getPath([
'menuName'=>'BILLING', 'submenuName'=>'CLIENTS',
'pageName'=>'EDIT INVOICE', 'optiontype'=>'FVUKQWCDEPSLpa', 'id'=>'%INID%'
]),
'title' => translate(['en'=>'Open invoice editor',
'es'=>'Abrir editor de factura', 'fr'=>'Ouvrir l’éditeur de facture']),
'icon' => '<i class="far fa-edit faactions" aria-hidden="true"></i>',
'visible' => function ($row) { return $row['type'] === 'invoice'; },
'disabled' => false,
'target' => '_self'
]];
| Key | Supported value and default |
link | Required page-owned relative or HTTP(S) URL for an enabled action. JavaScript/data schemes, whitespace and protocol-relative URLs are rejected. Use getPath() for KAS pages. |
title | Required translated string. Supplies the anchor's accessible name and hover tooltip. %COLUMN% substitutes a selected row value as text. |
icon | Trusted page-owned icon HTML; defaults to an arrow. Use aria-hidden="true" on a decorative icon. Never put untrusted row HTML here. |
visible | Boolean or server-side callable receiving the selected row array; defaults to true. False omits the action. |
disabled | Boolean or server-side callable receiving the row; defaults to false. True renders a labeled aria-disabled span without an actionable URL. Explain why in title. |
confirm | Optional translated text for native browser confirmation before following a link. %COLUMN% uses text substitution. It does not replace a server-validated confirmation form for sending or changing data. |
target | _self (default) or _blank; blank targets include noopener noreferrer. |
id | Optional ID with row placeholders. Include a stable selected primary key to keep IDs unique. |
class | Optional page-owned CSS classes appended to kas-row-action. |
URL placeholders use rawurlencode; title/confirmation/ID substitutions are escaped for HTML after replacement. A missing or nonscalar placeholder column is a configuration error. Placeholder names are case-sensitive, matching selected result keys. Use several entries for several actions; order follows the array after existing modal controls and before image/video/Edit/Clone/Delete controls. Conditions run in PHP, not JavaScript. There is no JavaScript callback/eval property and no arbitrary attribute passthrough; a link should open a native form or a separately authorized existing workflow. Rendering does not send or mutate data.
The table's existing privilege threshold2 still controls toolbar rendering. Each sensitive destination must independently reload account permissions, authorize its operation, validate CSRF on writes, reload the real row, check ownership and reject tampered input. Hiding/disabling an action is presentation, not authorization. Do not trust a placeholder or encrypted metadata alone as proof of ownership.
When composing several tables on one page, set or reset $rowActions for each table, just as its other page-owned options. Use $rowActions=[] when the next table has no custom actions.
Existing native controls
Option E enables Edit. $editOption supports link, title, and trusted icon HTML, including %COLUMN% link placeholders; the renderer retains legacy $edit_case and colon strings for compatibility. The legacy Edit substitution is not the URL-encoding boundary supplied by $rowActions. Keep its inputs page-owned and authorize the destination.
Option D enables the native KAS_FORM Delete control. $deleteOption=['column'=>'INVOICE_NO','prompt'=>'Invoice #'] supplies the selected row value and confirmation prefix. Its encrypted metadata follows the ordinary delete handler; inspect authorization before enabling it. Confirmation is not permission.
Option Q enables Clone. $cloneOption supports exclude (CSV), relData (FKTable, FKPrimaryKey, ID) and files, retaining legacy $clone_exclude, $cloneRelData, $cloneFiles. The existing clone JavaScript/endpoint owns copying, not $rowActions. Do not implement another generic clone action.
Option O enables the existing modal arrays: $modalActionBtn (icon class), $modalpage (endpoint), $modaltitle, $modalCols (selected columns), and $modalSize. Use aligned, contiguous indexes. Optional $modalVisible[index] accepts a boolean or a PHP callable receiving the row; false omits that modal control. The renderer iterates the icon array; $noModals does not determine that loop. The click posts id and a comma-separated cols payload, with embedded commas represented as %%comma%%. The current renderer does not emit $modalSize as data-size, so it must not be documented as a reliable sizing contract. These posted values are untrusted: reload authoritative records. CLIENT_SUMMARY.php uses this mechanism for recurring invoices, Email/SMS/WhatsApp invoice sends and saved-card payment. Preserve their owning handlers.
The invoice sends stay together in that native modal toolbar. This source pattern appends SMS after any existing optional payment action; the handler reloads the invoice using its ID and current client:
$invoiceModalIndex = count($modalActionBtn);
$modalActionBtn[$invoiceModalIndex] = 'fas fa-comment-sms';
$modalpage[$invoiceModalIndex] = 'ajax/billingMail.php?channel=SMS&client='.(int)$idvalue;
$modaltitle[$invoiceModalIndex] = translate('Send invoice by SMS', 'Enviar factura por SMS', 'Envoyer la facture par SMS');
$modalCols[$invoiceModalIndex] = ['INID'];
$modalVisible[$invoiceModalIndex] = function ($row) { return ($row['type'] ?? null) === 'invoice'; };
Client-level Messaging permissions and Message History belong to $actionButton above the table. They use the current CLID; History retains its separate administrator restriction and client filter. A modal send is not a link to a separate notification page. Its native Form View fields submit through KAS_FORM/db_actions, including forms loaded after page initialization.
Keyboard and narrow screens
The toolbar trigger is a native button. Hover and focus within the toolbar reveal its actions; links stay in normal keyboard order. Shared styles provide 28px action targets, a 32px trigger and a visible focus outline. The original native action count allocates 36px per control including wrapper spacing; the existing right-anchored panel is viewport-bounded and wraps only when necessary. Existing modal controls expose a button role/tab stop; Enter/Space reuse their click handler. Native form keyboard handling preserves Enter on links/buttons rather than swallowing their activation. Wide Table Views retain native horizontal scrolling; action toolbars do not change the table's column layout or bypass its viewport.
For page-level buttons see Action buttons; for native Edit/Delete/Clone examples see Creating Table Views, and for selected-row operations see Multiple Selection.