Listing pages can invoke page commands from individual rows, from the page header, and over multiple items selected by the user at once. Add the actions within an override of the ConfigurePage method, either on the listing page itself or on its page extender.
The listing template allows you to assign a page command that gets invoked when users select a row in the listing.
A typical use case for this is opening an item's view or edit page (e.g., for users, contacts). For this purpose, the system provides the PageConfiguration.AddEditRowAction<TInfoEditPage> extension method. The method generates a link to an edit page for each item (by ensuring the corresponding object's ID in the URL). Specify the System.Type of the corresponding edit layout page as the method's generic parameter.
See the Page URLs and routing section on UI pages for more information about generating URL structures for admin pages.
The listing page allows you to add actions (page commands) to the page header (displayed to the left of the search box) and to each item in the grid (displayed in the Actions column).
Add actions to the listing by calling the following methods on either PageConfiguration.HeaderActions or PageConfiguration.TableActions:
AddDeleteAction – adds a delete action to individual items in the list. The base class provides a Delete page command that deletes items based on an object's ID.
[PageCommand]
public override Task<ICommandResponse<RowActionResult>> Delete(int id) => base.Delete(id);AddLink<TPage> – adds a link to a specified page in the admin UI. Supply the System.Type of the target page as the method's generic parameter.
AddCommand – adds a page command to the page header or an item's Actions column.
AddCommandWithConfirmation – adds a page command in exactly the same way as AddCommand, but guarantees that users are asked to confirm the action before the command is invoked. Both methods accept the same parameters and place the same requirements on the command's handler.
Commands are referenced by the name of their handler method, not by a delegate. Pass nameof(YourHandler) as the commandName parameter and implement the handler on the listing page class (or on its page extender), annotated with the PageCommand attribute.
The required handler signature depends on where the action is displayed:
Item actions (added to PageConfiguration.TableActions) are invoked for a single listed item. The system passes the identifier of the row whose action was selected (the value of the listed object type's ID column) to the handler. The handler returns ICommandResponse<RowActionResult>, which instructs the client what to refresh once the command finishes.
[PageCommand]
public async Task<ICommandResponse<RowActionResult>> MyItemAction(int id, CancellationToken cancellationToken)Header actions (added to PageConfiguration.HeaderActions) are not bound to any listed item and therefore receive no identifier. Header actions are rendered as standalone buttons that do not evaluate RowActionResult, so their handlers return the general ICommandResponse.
[PageCommand]
public async Task<ICommandResponse> MyHeaderAction(CancellationToken cancellationToken)Apart from an optional CancellationToken parameter and an optional confirmation model parameter, command handlers can declare at most one parameter, which receives the data sent with the command. You can declare the parameters in any order.
Set the PageCommand attribute's Permission property to require a permission for the action's invocation.
[PageCommand(Permission = SystemPermissions.UPDATE)]Actions can either invoke a page command that the ListingPage base class already implements, as AddDeleteAction does with the Delete command, or a command you implement yourself. To implement your own, declare a handler annotated with the PageCommand attribute and reference it from AddCommand or AddCommandWithConfirmation.
Item action handlers construct their ICommandResponse<RowActionResult> using the ResponseFrom method, which is available both on listing pages and on page extenders. You can optionally chain the message extension methods described under UI page commands.
return ResponseFrom(new RowActionResult(reload: true))
.AddSuccessMessage("The item was updated.");RowActionResult instructs the client what to refresh once the command finishes. Both of its properties are read-only and set via the constructor:
Parameter | Description |
|---|---|
| Reloads the data of the listing's table – the system re-executes the listing's data query and re-renders the grid, including the action state evaluators of all rows. Set to Set to |
| Re-fetches the data of all UI page templates currently rendered on the page, not just the listing's table. Use it when the command changes state displayed outside of the listing itself, for example a counter in a parent template, a header, or a sidebar. Because the listing template is one of the re-fetched templates, its data is reloaded as well. Defaults to |
The reload and refetchAll flags are only processed for actions in PageConfiguration.TableActions and PageConfiguration.MassActions. Actions in PageConfiguration.HeaderActions do not evaluate the returned result. To refresh the page after a header action, return NavigateTo(url, refetchAllTemplates: true) instead, where url is the path of the page to display (for example, the listing's own path obtained from IPageLinkGenerator).
The following example adds a Disable action to each item of a listing of MemberInfo objects. The example assumes an IInfoProvider<MemberInfo> service retrieved into the memberProvider field via dependency injection.
using System.Threading.Tasks;
using CMS.DataEngine;
using CMS.Membership;
using Kentico.Xperience.Admin.Base;
// ...
// Declaration of the containing class and the 'IInfoProvider<MemberInfo> memberProvider' field
// ...
public override Task ConfigurePage()
{
PageConfiguration.TableActions
// References the 'Disable' page command by its name
.AddCommand(new AddCommandParameters(commandName: nameof(Disable), label: "Disable")
{
Icon = Icons.Lock,
Title = "Prevent the member from signing in"
});
return base.ConfigurePage();
}
// Handles the 'Disable' action. The system binds the identifier of the row
// whose action was invoked to the 'id' parameter.
[PageCommand(Permission = SystemPermissions.UPDATE)]
public Task<ICommandResponse<RowActionResult>> Disable(int id)
{
var member = memberProvider.Get(id);
if (member is null)
{
// The listing is out of sync with the database - reloads it to reflect the current state
return Task.FromResult(ResponseFrom(new RowActionResult(reload: true))
.AddErrorMessage("The member no longer exists."));
}
member.MemberEnabled = false;
memberProvider.Set(member);
// Reloads the listing's data, which displays the member's new state.
// 'refetchAll' is not needed - the change is contained within this listing.
return Task.FromResult(ResponseFrom(new RowActionResult(reload: true))
.AddSuccessMessage($"'{member.MemberName}' was disabled."));
}Declare both members either directly on the listing page class, or on a page extender of the listing page. Page extenders access the listing's configuration through their Page property – use Page.PageConfiguration.TableActions instead of PageConfiguration.TableActions.
To run custom logic over multiple items at once, add a mass action instead.
You can optionally display a confirmation dialog when users select an action or link. Call the AddCommandWithConfirmation or AddLinkWithConfirmation<TPage> methods.
AddCommandWithConfirmation accepts the same parameters as AddCommand and requires the same command handler. The two methods only differ in how they behave when ConfirmationParameters are not set – AddCommandWithConfirmation falls back to a generic confirmation dialog, while AddCommand invokes the command without prompting the user. Setting ConfirmationParameters therefore raises a confirmation dialog with either method.
public override Task ConfigurePage()
{
PageConfiguration
.TableActions
.AddCommandWithConfirmation
(
new AddCommandParameters(commandName: nameof(DoSomething), label: "Title")
{
Icon = Icons.ArrowsCrooked,
Title = "Text of the tooltip.",
// Providing these properties raises a confirmation dialog
// when a user invokes the corresponding action
ConfirmationParameters = new ConfirmationWithContentParameters
{
Confirmation = "What do you want to do?",
ConfirmationButton = "This, please",
}
});
return base.ConfigurePage();
}When users invoke the action, they are prompted with the following confirmation.
Additionally, the dialog can also display a form that allows users to submit data to the corresponding command handler. For example, this can be a multiple-choice selector that determines how the back end processes the action.
The form must be defined within a dedicated model class and annotated with Editing components. Validation rules are also supported. The class must be annotated with CommandConfirmationModel.
[CommandConfirmationModel]
public class ConfirmationDialogFormModel
{
[RequiredValidationRule]
[RadioGroupComponent(Inline = false, Order = 1,
Options = "option1;Option 1\r\noption2;Option 2\r\n")]
public string ChooseOne { get; set; } = "option1";
}Specify the model class as a parameter within AddCommandWithConfirmation. The other parameters of the method are identical to AddCommand.
public override Task ConfigurePage()
{
PageConfiguration
.TableActions
.AddCommandWithConfirmation
(
new AddCommandParameters(commandName: nameof(DoSomething), label: "Title")
{
Icon = Icons.ArrowsCrooked,
Title = "Text of the tooltip.",
// Providing these properties raises a confirmation dialog
// when a user invokes the corresponding action
ConfirmationParameters = new ConfirmationWithContentParameters
{
Confirmation = "What do you want to do?",
ConfirmationButton = "This, please",
// Setting the confirmation model displays a dialog with form
ConfirmationModel = typeof(ConfirmationDialogFormModel)
}
});
return base.ConfigurePage();
}The corresponding command handler method must expect the form model in its signature. The system automatically binds the form received from the client to the corresponding parameter.
[PageCommand]
public Task<ICommandResponse<RowActionResult>> ActionWithFormConfirmation(ConfirmationDialogFormModel model)
{
// Decide what to do based on the received form data...
return Task.FromResult(ResponseFrom(new RowActionResult(reload: true))
.AddSuccessMessage($"You have selected {model.ChooseOne}"));
}Some methods optionally take an Action delegate that allows you to programmatically disable or modify the commands they add for items meeting your specified criteria.
.AddDeleteAction(
new AddDeleteActionParameters(commandName: nameof(Delete))
{
ActionStateEvaluator = DisableDeleteForSpecificUsers,
});
// ...
// Disables the delete action for the default global administrator and public users
private Task DisableDeleteForSpecificUsers(ActionConfiguration actionConfiguration, IDataContainer rowData, CancellationToken cancellationToken)
{
if (!actionConfiguration.Disabled)
{
var userName = Convert.ToString(rowData[nameof(UserInfo.UserName)]);
if (String.Equals(userName, UserInfoProvider.AdministratorUserName, StringComparison.OrdinalIgnoreCase) ||
String.Equals(userName, UserInfoProvider.PublicUserName, StringComparison.OrdinalIgnoreCase))
{
actionConfiguration.Disabled = true;
}
}
return Task.CompletedTask;
}The listing page template allows you to add actions executed upon a collection of items selected from the listing.
Mass actions appear at the top of the listing, immediately below the search bar, when users select at least one listed item.
Page commands that implement mass actions must use the following signature:
public async Task<ICommandResponse<MassActionResult>> MassActionCommand(IEnumerable<int> identifiers, CancellationToken cancellationToken)where IEnumerable<int> identifiers is a collection of object type IDs identifying the items selected by the user. Use conventional provider APIs to work with these objects.
Add mass actions using the following methods on PageConfiguration.MassActions:
AddCommand – adds a command to execute.AddCommandWithConfirmation – adds a command with execution approved via a confirmation prompt.AddActionWithCustomComponent – adds an action that is handled by a custom React component.Note that the actionStateEvaluator parameter offered by these methods is not supported by mass actions.
public override Task ConfigurePage()
{
PageConfiguration
.MassActions
.AddCommandWithConfirmation
(
label: "Title",
command: nameof(MyMassActionPageCommand),
icon: Icons.ArrowsCrooked,
// Providing these properties raises a confirmation
// dialog when users invoke the added action
confirmation: "Confirmation title",
confirmationButton: "Confirm"
);
return base.ConfigurePage();
}For advanced action scenarios, you can use a custom React component to handle:
PageConfiguration.TableActions, and PageConfiguration.HeaderActions)PageConfiguration.MassActions)on listing templates.
Start by preparing the React component's front end (e.g., a .tsx file). The name of the component must end with the Component suffix, e.g., @acme/web-admin/CustomActionComponent. Ensure that the component is exported in your custom admin module's entry.tsx file.
In addition, you also need to create the following back-end classes for each action component:
ActionComponent<TProperties, TClientProperties>.
ClientComponentName property, and set the value to the name of the corresponding front-end component. The Component suffix is automatically added to this name when loading the component.ConfigureClientProperties method to populate the component's client properties (typically from the value of the corresponding back-end properties class).IActionComponentProperties.IActionComponentClientProperties.public sealed class CustomActionComponent : ActionComponent<CustomActionProperties, CustomActionClientProperties>
{
// The name of the front-end type implementing the action component without the "Component" suffix,
// which is automatically added to this name.
public override string ClientComponentName => "@acme/web-admin/CustomAction";
protected override Task ConfigureClientProperties(CustomActionClientProperties clientProperties)
{
clientProperties.MyProperty = Properties.MyProperty;
// ...
return base.ConfigureClientProperties(clientProperties);
}
}To add the action in the listing template configuration, call AddActionWithCustomComponent on the appropriate action collection (e.g., TableActions or MassActions).
Permissions and custom component actions
If you wish to implement permission checks for actions handled by a custom component, we recommend setting the AddActionWithCustomComponent method's disabled parameter (see the example below). You can also propagate permission information to your component via members of the client properties class.
private readonly IUIPermissionEvaluator uiPermissionEvaluator;
// ...
public override async Task ConfigurePage()
{
bool userHasPermission = await UserHasUpdatePermission();
PageConfiguration.MassActions.AddActionWithCustomComponent(
clientComponent: new CustomActionComponent()
{
Properties = new CustomActionProperties()
{
MyProperty = "MyValue"
}
},
label: "Title",
icon: Icons.PaperPlane,
disabled: !userHasPermission
);
await base.ConfigurePage();
}
private async Task<bool> UserHasUpdatePermission()
{
return (await uiPermissionEvaluator.Evaluate(SystemPermissions.UPDATE)).Succeeded;
}