# ECA - Drupal Events, Conditions, Actions
> Documentation, tutorials and resources for Drupal's ECA module and its eco-system.
Documentation for Drupal's ECA (Events-Conditions-Actions) module, the recommended rules engine for Drupal 10/11+. Covers core concepts, plugin reference, visual modelers, example models, and developer guides.
# ECA Guide
# Welcome to Drupal ECA
This document explains how to use the Events - Conditions - Actions (ECA) module for Drupal.
## What is ECA?
ECA (Events - Conditions - Actions) is a Drupal module that processes business process models based on Drupal events. The module integrates with Drupal core components and provides plugin management for:
- [Events](https://ecaguide.org/eca/concepts/events/index.md)
- [Conditions](https://ecaguide.org/eca/concepts/conditions/index.md)
- [Actions](https://ecaguide.org/eca/concepts/actions/index.md)
Note
ECA does not include a built-in user interface. Instead, it provides a modeler plugin manager that integrates with existing tools. If a modeler supports templates, ECA provides them for all plugins available on your Drupal site.
## Contents
- [Installation guide](https://ecaguide.org/eca/install/index.md)
- [Key concepts](https://ecaguide.org/eca/concepts/index.md)
- [Extension guide](https://ecaguide.org/eca/extend/index.md)
- [Plugin documentation](https://ecaguide.org/plugins/index.md)
- [Available modelers](https://ecaguide.org/modeler/index.md)
- [Example models](https://ecaguide.org/library/index.md)
- [Additional resources](#additional-resources)
## Additional resources
You can find articles, tutorials, videos, and other learning materials in the Resources section.
## Get support
Contact the ECA maintainers through:
- Their Drupal.org user profiles
- The [ECA Slack channel](https://drupal.slack.com/archives/C0287U62CSG)
## Contribute
We welcome contributions including:
- Bug reports and feature requests
- Documentation improvements
- Example models
- Custom plugins
- General feedback
To contribute:
1. Request an account on the [ECA Guide GitLab](https://gitlab.lakedrops.com). Contact the ECA maintainers on the [ECA Slack channel](https://drupal.slack.com/archives/C0287U62CSG), and provide an email address for the account.
1. Visit the [ECA Guide Project](https://gitlab.lakedrops.com/drupal/documentation/eca)
1. Open an issue or create a merge request
When writing documentation, please follow the [documentation styleguide](https://ecaguide.org/eca/styleguide/index.md).
For more information about ECA, visit the [project page](https://www.drupal.org/project/eca).
# Installation
ECA can be installed in two ways: using the recommended Starterkit recipe or manually installing the modules.
## Starterkit (Recommended)
The [ECA Starterkit](https://www.drupal.org/project/eca_starterkit) provides a complete setup for ECA. It installs a recommended set of modules, configures the Workflow Modeler, and includes a demo model to get you started immediately.
### What gets installed
- **Core ECA components**: A comprehensive set of 20 sub-modules covering access control, caching, content entities, forms, logging, users, views, and more.
- **Workflow Modeler**: The recommended visual editor with drag-and-drop capabilities.
- **Demo Model**: A "User Registration Form" model that demonstrates field customization and validation.
### Installation steps
1. **Require the recipe**:
```
composer require drupal/eca_starterkit
```
1. **Apply the recipe**:
```
# Using Drush (version 13 or later)
drush recipe ../recipes/eca_starterkit
# Or without Drush
cd web && php core/scripts/drupal recipe ../recipes/eca_starterkit
```
After applying the recipe, navigate to **Administration > Configuration > Workflow > ECA** to see the installed demo model and start building your own.
## Manual Installation
If you prefer to install only specific components, you can install the modules manually.
Download the ECA module and Workflow Modeler module:
```
composer require drupal/eca drupal/modeler
```
Install the ECA and Workflow Modeler module and their dependencies via the GUI at `/admin/modules` or with Drush:
```
drush -y pm:install eca eca_ui modeler
```
The list of additional ECA sub-modules you may want to install depends on your specific use-case (e.g., `eca_content`, `eca_user`, `eca_views`).
# Usage
Actually, ECA is processing all enabled models in the background and there is nothing a user needs to do, when everything has been set up. In fact, there isn't anything a user could do to prevent ECA from processing all these models
Attention
Should there ever be a problem, that ECA breaks your site, please refer to the [troubleshooting](https://ecaguide.org/eca/troubleshooting/index.md) section which explains how to fix that.
## How it works
ECA is a processor that works in the background by subscribing to all events on a Drupal site and executing ECA models that define processes for any of those events. There is no UI required for that, ECA just operates based on the available and enabled models.
Recommended setup for production sites
Creating and maintaining ECA models might be a task for your development environment. On the production site, there is often no access to either view or edit those models. If that applies to your Drupal site, you only need to enable ECA and all the sub-modules that are required by the configured ECA models. There won't be any UI and no modeler available for any user on the live site, ECA just operates on the configured models.
Subscribing to all events on a Drupal site may sound worrisome in regard to site performance. We've taken much care to avoid unnecessary overhead and ECA is able to decide very quickly, whether an event is relevant for any of the configured models or not. All information, that's required to make those decisions are stored in cache separately and will only be loaded once for each request, so that the extra processing for each event on top of Symfony's event dispatching is in fact negligible, and we haven't been able to even measure any impact.
### ECA model config files
The ECA models have been mentioned a lot, and this chapter explains how those models are stored and describe the structure of their config files. This should help you plan the maintenance and deployment of your own models on all your different Drupal sites.
Each ECA model is a set of 2 config entities, each of which being exported by Drupal's configuration management into a separate file. The config entities (and files) for a single model are named with the following pattern:
- `eca.eca.[MODEL-ID]`: contains all events, conditions and actions that ECA operates upon
- `eca.model.[MODEL-ID]`: contains the proprietary model data of the modeler that has been used to create and edit this model
In other words, the config entity `eca.eca.[MODEL-ID]` is being used by ECA to operate on the Drupal site. The other one is not needed for that at any time. That second config entity is only required by the modeler, when the model gets edited. When editing a model with a modeler, ECA will recreate its `eca.eca.[MODEL.ID]` config entity by converting the modeler data when the model gets saved by the modeler.
Danger
Always keep the 2 config entities together - ECA always keeps them in sync. And never edit them manually, the eca.eca entity will always be recreated from the eca.model entity.
Having said that, the `eca.model` config entity contains everything needed to recreate the `eca.eca` config entity, should you ever loose that. That implies that you could send the `eca.model` config entity to someone else only, and they could load that into their Drupal site which would then recreate the companion `eca.eca` config entity, if they had the same modeler available to interpret the proprietary data. More about that below in [Import and export models](#import-and-export-models).
## Admin UI
As mentioned above, to let ECA do its work by processing available ECA models, no UI is required whatsoever. Only if you need to maintain your ECA models, the ECA UI is required. Very much like Drupal core has implemented this for the fields and views modules, where the UIs can also be disabled when not required.
When the ECA UI is being required, you can enable it separately:
```
drush en eca_ui
```
This enables the admin UI which can be found in the admin menu under Administration / Configuration / Workflow / ECA with the path `/admin/config/workflow/eca`:
On this ECA admin page you can manage all aspects of ECA: Changing the order of the models by re-arranging them, importing new models and performing operations on each of the available models. That's all described in the chapters below.
Creating and editing models
The ECA admin UI does not provide you access to create new models or to edit existing ones. Those actions are only available, when at least one modeler is enabled which provides support for integrated editing, like e.g. the [Workflow Modeler](https://ecaguide.org/modeler/workflow/index.md).
### Settings
`Log level` : *Default: Error*
```
Determines, which ECA log messages will be logged to which ever logging backend is enabled on your site. By default,
ECA only logs errors or more severe log messages. You can change that to e.g. `Debug` to also get much more detailed
logs, see also [Logs](#logs) and [Debugging](https://ecaguide.org/eca/debugging/index.md).
```
`Documentation domain` : *Default: https://ecaguide.org*
```
This domain is being used to provide links to the documentation when working with a modeler, and it points to this
official ECA guide by default. You can change this to any other domain, if you provide your own documentation, or
make this field empty, so that ECA won't display any links to documentation.
```
### Logs
This is only available, if you've also enabled the `Database Logging` module. It will display all the logged ECA messages depending on the log level you've configured in the [settings](#settings). You can filter the messages by content and/or time range and the list is sorted in exactly that order how the messages got produced.
Each log message for a condition or an action will also contain a list of tokens, that have been available at the time when that component has been evaluated or executed. For more details about token, see [Concepts: Token](https://ecaguide.org/eca/concepts/tokens/index.md).
### ECA model operations
In the list of models, the operations column contains a list of operations for each model, that's available on that Drupal site:
#### Creating and editing models
These actions are only available, if you have a modeler enabled that supports integrated editing, like e.g. the [Workflow Modeler](https://ecaguide.org/modeler/workflow/index.md).
#### Import and export models
Models can be imported by clicking on the `Import` button in the top right corner. This will open a form where you can select a file to import. There are actually 2 different file types supported for import:
- an `eca.model.[MODEL-ID].yml` config file, see [ECA model config files](#eca-model-config-files)
- an archive file (e.g. `[MODELLER-TYPE]-[MODEL-ID].tar.gz`) which has been previously exported (see below) or downloaded from the [Library](https://ecaguide.org/library/index.md)
Once you've selected the file and click on the `Import` button, ECA will validate the file and if all dependencies are available, import the model and show it on the list of models for further operations.
For a comprehensive guide on all import methods, common issues and troubleshooting, see [Importing Models](https://ecaguide.org/eca/importing/index.md).
To export a model from your Drupal site, e.g. because you want to use it on a different Drupal site, or you need to attach it to an issue on drupal.org, select the `Export` operation in the list of models for that model, that you want to export. This will create an archive with a filename `[MODELLER-TYPE]-[MODEL-ID].tar.gz` which contains the id of the used modeler and the ID of the model itself.
Note
It's important to keep that filename as it is part of the validation when later importing that model again.
The exported archive contains 3 or more files:
- `dependencies.yml`: a metadata file containing the names of ECA config entities and a list of required modules
- `eca.eca.[MODEL.ID].yml`: the ECA config entity for processing
- `eca.model.[MODEL.ID].yml`: the ECA config entity with proprietary model data
- optionally more config entities, if the exported ECA model depends on other config entities
#### Clone
This will clone an existing ECA model and create a random ID for the new model.
#### Enable / disable
Only one of those 2 operations is available, depending on the state of each model. Enabled model can be disabled and vice versa. Disabled models will not be processed by ECA, but they remain available on the Drupal site for editing or other operations. Only if you re-enable a disabled model will it be processed by ECA again.
### Delete
This will delete an ECA model entirely from the Drupal site.
# Importing Models
ECA models can be imported into your Drupal site in several ways. This guide covers all available import methods, explains common issues that may arise during import, and provides solutions for resolving them.
## Import methods
### Using recipes (recommended)
Since Drupal 10.3, the recommended way to import models from the [ECA Library](https://ecaguide.org/library/index.md) is via recipes. Each library model is available as a Composer package that can be applied as a Drupal recipe.
```
# Require the recipe package
composer require drupal-eca-recipe/eca_lib_XXXX
# Apply the recipe with Drush (version 13 or later):
drush recipe ../recipes/eca_lib_XXXX
# Or apply the recipe without Drush:
cd web && php core/scripts/drupal recipe ../recipes/eca_lib_XXXX
# Rebuilding caches is optional, sometimes required:
drush cr
```
Replace `XXXX` with the library model number. Each library model page shows the exact commands to use.
Tip
Recipes handle dependencies automatically. If the model requires additional configuration entities (e.g. content types, fields, or vocabularies), the recipe will create them for you.
### Using the ECA admin UI
Models can also be imported manually through the ECA admin interface at `/admin/config/workflow/eca`:
1. Click the **Import** button in the top right corner.
1. Select the file to import.
1. Click **Import** to validate and import the model.
Two file types are supported:
`eca.model.[MODEL-ID].yml` : A single YAML config file containing the proprietary model data. ECA will recreate the companion `eca.eca.[MODEL-ID]` config entity automatically, provided the appropriate modeler is enabled. See [ECA model config files](https://ecaguide.org/eca/usage/#eca-model-config-files) for details.
`[MODELER-TYPE]-[MODEL-ID].tar.gz` : An archive file previously exported from another Drupal site or downloaded from the [ECA Library](https://ecaguide.org/library/index.md). The archive contains:
```
- `dependencies.yml` — metadata listing required config entities and modules
- `eca.eca.[MODEL-ID].yml` — the ECA processing config entity
- `eca.model.[MODEL-ID].yml` — the modeler-specific model data
- optionally, additional config entities that the model depends on
```
Note
When importing an archive, do not rename the file. The filename is validated during import and must match the pattern `[MODELER-TYPE]-[MODEL-ID].tar.gz`.
### Using Drupal's configuration management
ECA models are standard Drupal configuration. You can also deploy them through Drupal's configuration synchronization system by placing the two config files (`eca.eca.[MODEL-ID].yml` and `eca.model.[MODEL-ID].yml`) into your configuration sync directory and running a config import:
```
drush config:import
```
This is particularly useful for deploying models from a development environment to production. See also the [recommended setup for production sites](https://ecaguide.org/eca/usage/#how-it-works).
## Troubleshooting import failures
Sometimes, importing a model may fail. Below are the most common causes and their solutions.
### Existing entities prevent import
**Symptom:** The import fails with a message similar to:
> The import failed due to the following reason: There were errors validating the config synchronization. Entities exist of type *Content* and *Content type* *Basic page*. These entities need to be deleted before importing.
**Cause:** The model archive includes configuration for entities such as content types, vocabularies or fields. If those entities already exist on your site with content attached to them, Drupal's configuration validation rejects the import because it would overwrite existing configuration that has dependent data.
**Solutions:**
1. **Check for conflicting entities.** Compare the configuration in the archive with what already exists on your site. If the existing configuration matches what the model expects, the import may succeed after removing the conflicting config entities from the archive. Extract the archive, remove the unnecessary files, update `dependencies.yml` accordingly, and repackage it.
1. **Delete conflicting content first.** If the existing entities contain test data or content you do not need, delete the content and then the configuration entities before retrying the import.
1. **Import only the ECA model files.** Instead of importing the full archive, import just the `eca.model.[MODEL-ID].yml` file through the admin UI. This skips the dependency configuration but requires that all dependencies already exist on your site with a compatible setup.
1. **Use the recipe method.** Recipes handle existing configuration more gracefully. If the configuration already exists and is compatible, the recipe will skip creating it and only add the ECA model.
Tip
For more context on this issue, see [drupal.org issue #3381483](https://www.drupal.org/project/eca/issues/3381483).
### Missing required modules
**Symptom:** The import fails because one or more required modules are not enabled.
**Cause:** The model uses plugins provided by modules that are not installed on your site. The `dependencies.yml` file in the archive lists all required modules.
**Solution:** Enable the required modules before importing:
```
# Check which modules are required by extracting the archive
# and reading the dependencies.yml file, then enable them:
drush en module_name_1 module_name_2
```
When using the recipe method, missing modules are listed as Composer dependencies. Running `composer require` for the recipe package will automatically install the required modules.
### Modeler not available
**Symptom:** The model imports but cannot be edited, or the import of a `.yml` file fails to recreate the `eca.eca` config entity.
**Cause:** The model was created with a specific modeler (e.g. the [Workflow Modeler](https://ecaguide.org/modeler/workflow/index.md) or BPMN.iO), and that modeler is not enabled on your site.
**Solution:** Enable the modeler that was used to create the model. All [ECA Library](https://ecaguide.org/library/index.md) models are built with the [Workflow Modeler](https://ecaguide.org/modeler/workflow/index.md), so enabling `eca_ui` and `eca_modeller_bpmn` is sufficient for library models:
```
drush en eca_ui eca_modeller_bpmn
```
### Archive filename has been changed
**Symptom:** The import fails during file validation.
**Cause:** The archive file was renamed after export. ECA validates the filename to extract the modeler type and model ID.
**Solution:** Restore the original filename matching the pattern `[MODELER-TYPE]-[MODEL-ID].tar.gz`. If you do not know the original name, extract the archive and check the config filenames inside — the model ID is part of the `eca.model.[MODEL-ID].yml` filename.
### Configuration validation errors
**Symptom:** The import fails with generic configuration validation errors.
**Cause:** This can happen when the model was created on a different version of Drupal or ECA, and there are schema differences between versions.
**Solution:**
1. Ensure you are running a compatible version of ECA. Check the model's documentation or the library page for version requirements.
1. Update ECA to the latest version: `composer update drupal/eca`
1. If the issue persists, try importing just the `eca.model.[MODEL-ID].yml` file instead of the full archive.
## Best practices for importing
- **Back up your site** before importing models, especially on production environments.
- **Review the model** before importing. Each library model page includes a visual representation of the model and a list of its dependencies.
- **Use a development environment** to test imported models before deploying them to production.
- **Prefer recipes** when importing library models — they handle dependencies automatically and are more resilient to existing configuration.
- **Keep your ECA installation up to date** to ensure compatibility with the latest library models.
# Understanding ECA concepts
ECA (Events - Conditions - Actions) models help you automate business processes in Drupal by responding to events that happen on your site. Think of ECA as a watchful assistant that can detect when something happens, check if certain conditions apply, and then take appropriate actions.
## Core components
### Events
Events are triggers that start an ECA process. These could be things like:
- A user logging in
- Content being created or updated
- A form being submitted
- A scheduled time arriving
- A custom event you define
When an event occurs, ECA checks if any models are listening for that event and runs them if so.
For a detailed guide on event types, token availability, and how to add events to models in each modeler, see the dedicated [Events](https://ecaguide.org/eca/concepts/events/index.md) page.
### Conditions
Conditions are checks that determine whether actions should run. They act as filters or guards, ensuring actions only happen when appropriate. For example, you might want to:
- Check if a user has a specific role
- Verify content has certain field values
- Ensure a threshold has been met
- Compare dates or times
- Combine multiple conditions with AND/OR logic
Conditions go on connections
A common point of confusion for newcomers: conditions are not standalone elements on the canvas. Instead, they are attached to the **connections** (edges) between components. See the [Conditions](https://ecaguide.org/eca/concepts/conditions/index.md) page for details on how to add them in each modeler.
### Actions
Actions are the tasks ECA performs when an event occurs and conditions are met. Actions can:
- Create or update content
- Send notifications
- Modify user accounts
- Interact with external systems
- Trigger other events
- Perform custom operations
For a detailed guide on action categories, chaining, token interaction, and how to add actions in each modeler, see the dedicated [Actions](https://ecaguide.org/eca/concepts/actions/index.md) page.
## How components work together
Let's look at a common example: sending a welcome email to new users in a specific role.
1. **Event**: User account is created
1. **Condition**: User has the "member" role
1. **Action**: Send customized welcome email
ECA processes this flow by:
1. Detecting the account creation event
1. Checking the user's roles
1. Sending the email if the condition is met
## Next steps
Learn more about specific concepts:
- [Events](https://ecaguide.org/eca/concepts/events/index.md) -- types of events, tokens they provide, and how to add them
- [Conditions](https://ecaguide.org/eca/concepts/conditions/index.md) -- condition types, negation, AND/OR logic, and how to add them
- [Actions](https://ecaguide.org/eca/concepts/actions/index.md) -- action categories, chaining, token interaction, and how to add them
- [Managing permissions](https://ecaguide.org/eca/concepts/permissions/index.md)
- [Understanding tokens](https://ecaguide.org/eca/concepts/tokens/index.md)
- [Using loops](https://ecaguide.org/eca/concepts/loops/index.md)
- [Working with custom events](https://ecaguide.org/eca/concepts/custom_events/index.md)
- [Entity events](https://ecaguide.org/eca/concepts/entity_events/index.md)
- [Using gateways](https://ecaguide.org/eca/concepts/gateways/index.md)
For practical examples, visit our [model library](https://ecaguide.org/library/index.md).
# Actions
Actions are the tasks that ECA performs when an event occurs and any associated conditions are met. They are the "do" part of the Events-Conditions-Actions pattern -- the components that actually make changes to your site.
## What is an action?
An action is an operation that ECA executes as part of a model's processing flow. Unlike conditions (which only evaluate and never modify), actions carry out real work: creating content, sending emails, modifying entities, setting token values, and more.
Actions can be chained together. The output of one action (often stored as a [token](https://ecaguide.org/eca/concepts/tokens/index.md)) can be used as input for the next, allowing you to build complex automated workflows step by step.
## Common action categories
ECA provides a large number of actions. Here are some of the most frequently used categories:
Content actions : Create, update, save, or delete content entities. Examples include [Save entity](https://ecaguide.org/plugins/eca/content/actions/eca_save_entity/index.md) and [Entity: set field value](https://ecaguide.org/plugins/eca/content/actions/eca_set_field_value/index.md).
Token actions : Set, modify, or manipulate token values for use later in the process. The most common is [Token: set value](https://ecaguide.org/plugins/eca/base/actions/eca_token_set_value/index.md).
User actions : Manage user accounts -- assign roles, switch the current user context, block or unblock accounts. See also [Permissions](https://ecaguide.org/eca/concepts/permissions/index.md) for how user context affects processing.
Notification actions : Send emails, display messages to the user, or write to the log.
Trigger actions : Trigger other ECA events, including [custom events](https://ecaguide.org/eca/concepts/custom_events/index.md), to create modular, reusable workflows.
Form actions : Modify forms by setting field values, adding validation errors, or altering form elements.
External actions : Interact with external systems via HTTP requests, webhooks, or queue processing.
Browsing available actions
The full list of actions depends on which modules are installed. See the [Plugins section](https://ecaguide.org/plugins/index.md) for a complete reference, organised by module.
## How actions work in ECA
When ECA reaches an action during processing, it:
1. Evaluates any [conditions](https://ecaguide.org/eca/concepts/conditions/index.md) on the incoming connection. If a condition returns FALSE, the action (and all its successors) is skipped.
1. Executes the action with the configured parameters, replacing any [token](https://ecaguide.org/eca/concepts/tokens/index.md) references with their current values.
1. If the action produces a result (e.g. a loaded entity, a computed value), it stores that result as a token under the name you specify.
1. Proceeds to any successor actions connected from this action.
Actions are processed sequentially in the order their connections were created.
## Successors and chaining
An action can have one or more successor connections, each optionally guarded by conditions. This allows you to:
- **Chain actions**: Connect actions in sequence to build multi-step workflows.
- **Branch based on outcome**: Use conditions on outgoing connections to create conditional paths after an action.
- **Trigger sub-processes**: Use the trigger custom event action to delegate work to another model.
## Adding an action to a model
### Workflow Modeler
1. Open your model at **Configuration > Workflow > ECA**.
1. From the toolbar or canvas, add an **Action** node.
1. Select the action type from the categorized list.
1. Configure the action's settings in the side panel (e.g. field values, token names, email recipients).
1. Connect the action to its predecessor (event, gateway, or another action) using an edge.
1. Optionally add conditions to the incoming edge.
You can also use the **Quick-Add** feature: click the `+` on any existing node to add a connected successor action directly.
### BPMN.iO
1. On the canvas, add a **Task** element (the rounded rectangle).
1. Use the wrench icon to configure the task type as an ECA action.
1. In the configuration panel on the right, select the action from the dropdown.
1. Configure the action's settings.
1. Draw sequence flows to connect the action to its predecessor and any successors.
### Classic Modeler
1. Use the form interface to add an action by selecting from the available action types.
1. Fill in the configuration fields.
1. Connect the action to its predecessor and any successors using the form-based interface.
## Tokens and actions
Many actions interact with the token system:
- **Consuming tokens**: Actions can use tokens in their configuration fields. Any field marked with "This property supports tokens." accepts token references like `[node:title]` or `[myToken]`.
- **Producing tokens**: Actions that produce a result (e.g. loading an entity, performing a calculation) typically offer a "Token name" field where you specify the name under which the result is stored.
- **Token replacement in non-ECA actions**: Actions from Drupal core or other modules (not specifically developed for ECA) may have a "Replace tokens" setting at the bottom of their configuration. Set this to "yes" if tokens are not being replaced automatically. See [Tokens](https://ecaguide.org/eca/concepts/tokens/index.md) for details.
## Tips for working with actions
- **Order matters**: Actions execute in the order they are connected. Ensure that any tokens or entities needed by an action have been made available by a preceding event or action.
- **Check the "Token name" field**: When an action loads or creates something, always provide a meaningful token name so you can reference the result later.
- **Use "Switch account" for elevated permissions**: Actions execute under the current user's permissions. If an action requires higher privileges, use [User: switch current account](https://ecaguide.org/plugins/eca/user/actions/eca_switch_account/index.md) before it. See [Permissions](https://ecaguide.org/eca/concepts/permissions/index.md).
- **Save entities explicitly**: Modifying an entity's fields does not automatically save it. You typically need a separate [Save entity](https://ecaguide.org/plugins/eca/content/actions/eca_save_entity/index.md) action afterwards.
- **Debug with logging**: Use the [debugging tools](https://ecaguide.org/eca/debugging/index.md) to trace which actions executed, which were skipped, and what token values were available at each step.
# Conditions
Conditions act as guards or filters in an ECA model. They determine whether a particular path of actions should be executed. If a condition evaluates to TRUE, the connected actions proceed; if FALSE, that branch is skipped.
## What is a condition?
A condition is a yes-or-no check applied to a connection between components in your model. It answers the question: **"Should this path be followed?"**
Conditions do not perform any changes themselves -- they only evaluate a situation and return TRUE or FALSE. This makes them safe to use: a condition never modifies data.
## Common condition types
ECA offers a wide range of conditions. Some of the most frequently used include:
Scalar comparison : Compare two values (equal, not equal, greater than, less than, contains, etc.). Useful for checking field values, token contents, or any text/number comparison.
Entity type and bundle : Check whether an entity is of a specific type (e.g. node, user) or bundle (e.g. article, page). Essential when an event applies to multiple content types but you only want to act on one.
User role : Verify whether the current user has a specific role. Commonly used to restrict actions to administrators or specific user groups.
Entity field value : Check whether a specific field on an entity has a particular value, is empty, or matches a pattern.
Route match : Evaluate the current Drupal route or path, useful for request/response event models.
Browsing available conditions
The full list of conditions depends on which modules are installed. See the [Plugins section](https://ecaguide.org/plugins/index.md) for a complete reference, organised by module.
## Negating conditions
Every condition in ECA can be negated. When you negate a condition, the logic is inverted: a condition that would normally return TRUE returns FALSE instead, and vice versa.
This is useful when you want to express "if NOT this condition" without needing a separate condition plugin. For example, negating a "user has role" condition effectively becomes "user does NOT have role".
## Combining conditions (AND / OR logic)
You can combine multiple conditions on different connections leading from the same component:
- **AND logic**: Attach multiple conditions to the same connection (or chain conditions in sequence). All must be TRUE for the path to proceed.
- **OR logic**: Create separate connections from the same source to the same target, each with a different condition. If any one connection's condition is TRUE, the target action executes.
For more complex conditional branching, consider using [gateways](https://ecaguide.org/eca/concepts/gateways/index.md).
## Adding conditions to a model
Conditions are not standalone components that sit on the canvas on their own. Instead, they are attached to the **connections** (edges) between events, actions, and gateways. This is an important distinction that newcomers often find surprising.
### Workflow Modeler
1. First, ensure you have an event and at least one action connected by an edge.
1. Click on the **edge** (the line connecting two components).
1. In the side panel, add a condition by selecting from the available condition types.
1. Configure the condition's settings (comparison values, fields to check, etc.).
1. Optionally enable **Negate** to invert the condition logic.
You can also use the **Quick-Add** feature: when adding a successor from a node, the popup allows you to insert a condition on the new edge directly.
### BPMN.iO
1. Draw a **sequence flow** (arrow) from an event or action to the next element.
1. Click on the sequence flow arrow.
1. In the configuration panel on the right, select a condition from the dropdown.
1. Configure the condition's settings.
1. Optionally enable negation.
Note
In BPMN terminology, a condition on a sequence flow is called a "conditional flow". The condition determines whether the flow is followed.
### Classic Modeler
1. When connecting components, the form interface provides a field for adding conditions.
1. Select the condition type and configure its parameters.
1. Use the negate option if needed.
## Tips for working with conditions
- **Conditions go on connections, not nodes**: This is the most common point of confusion for new users. You do not add a condition as a separate box on the canvas. Instead, you attach it to the arrow/edge between two components.
- **Keep conditions simple**: If a condition requires complex logic, consider breaking it into multiple simpler conditions or using [custom events](https://ecaguide.org/eca/concepts/custom_events/index.md) to separate concerns.
- **Use negation wisely**: Rather than creating a separate "is not" condition, negate the existing one.
- **Test conditions with debugging**: Use the [debugging tools](https://ecaguide.org/eca/debugging/index.md) to verify that conditions evaluate as expected. The logs show which conditions passed and which were skipped.
- **Mind token availability**: Conditions often reference tokens. Ensure the token you are checking has been made available by the event or a preceding action. See [Tokens](https://ecaguide.org/eca/concepts/tokens/index.md).
# Custom events
Custom events are user-defined events that you create and trigger yourself from within ECA models. They allow you to build modular, reusable workflows by breaking complex logic into smaller, self-contained processes.
## What are custom events used for?
Custom events serve several important purposes:
- **Modularity**: Split large models into smaller, focused models that communicate via custom events. This makes models easier to understand and maintain.
- **Looping**: Iterate over lists of items by triggering a custom event for each item. See [Loops](https://ecaguide.org/eca/concepts/loops/index.md).
- **Reusability**: Define a process once and trigger it from multiple places. For example, a "send notification" custom event model can be triggered from different models throughout your site.
- **Token forwarding**: Pass data between models by forwarding tokens to the custom event.
## Types of custom events
ECA provides two types of custom events:
Custom event : The basic [Custom event](https://ecaguide.org/plugins/eca/base/events/eca_base_eca_custom/index.md) listens for a specific event ID and receives forwarded tokens. It does not carry an entity context by default.
Entity-aware custom event : The [Entity-aware custom event](https://ecaguide.org/plugins/eca/content/events/content_entity_custom/index.md) works like a standard custom event but also carries a content entity. The entity is always available under the token name `entity` in the receiving model, in addition to any forwarded tokens.
## Triggering custom events
Custom events are triggered explicitly using action plugins:
Trigger custom event : The [Trigger custom event](https://ecaguide.org/plugins/eca/base/actions/eca_trigger_custom_event/index.md) action fires a custom event with a specified event ID. You can list tokens to forward to the receiving model.
Trigger entity-aware custom event : The [Trigger entity-aware custom event](https://ecaguide.org/plugins/eca/content/actions/eca_trigger_content_entity_custom_event/index.md) action fires an entity-aware custom event, forwarding both the specified entity and any additional tokens.
Trigger via Drush : Custom events can also be triggered from the command line using Drush:
````
```
drush eca:trigger:custom-event my_event_id
```
This is useful for cron jobs, scripts, or manual triggering during development and testing.
````
## The event ID
Every custom event has an **event ID** -- a string that identifies which custom event is being triggered. The event ID is how the triggering action and the listening event are matched.
- The event ID can be any string value you choose (e.g. `send_welcome_email`, `process_order`, `notify_admins`).
- The event ID is case-sensitive.
- Multiple models can listen for the same event ID -- they will all be triggered.
### Empty event IDs
An event ID can be left empty. A custom event with an empty event ID acts as a **catch-all**: it responds to every custom event trigger, regardless of the ID used. This can be useful for logging or auditing purposes but should be used with care to avoid unintended processing.
## Forwarding tokens
When triggering a custom event, the **Tokens to forward** field specifies which tokens from the current process should be made available in the triggered event's process.
- Enter a comma-separated list of token names (without brackets): e.g. `node, user, myValue`.
- The tokens will be available in the receiving model under the same names.
- For entity-aware custom events, the entity is always forwarded automatically -- you do not need to include it in the tokens list.
Common pitfall
If tokens appear blank or missing in the custom event's process, the most likely cause is that they were not listed in the "Tokens to forward" field. This is one of the most frequently encountered issues when working with custom events.
## Practical example
Consider a model that processes new article nodes and needs to notify all editors:
**Model 1: Content created**
1. **Event**: Insert content entity (bundle: article)
1. **Action**: Trigger custom event with ID `notify_editors`, forwarding the token `node`
**Model 2: Notify editors**
1. **Event**: Custom event with ID `notify_editors`
1. **Action**: Execute a View returning all users with the "editor" role
1. For each user, send an email using `[node:title]` and `[user:mail]`
This separation keeps the content creation model clean and the notification logic reusable.
## Best practices
- **Use descriptive event IDs**: Choose clear, meaningful names like `order_completed` rather than vague ones like `event1`.
- **Document your custom events**: Since custom events are user-defined, they are not documented automatically. Add comments or notes in your models to explain what each custom event does.
- **Forward only what is needed**: Keep the "Tokens to forward" list focused on the tokens the receiving model actually uses.
- **Avoid circular triggers**: Be careful not to create situations where model A triggers a custom event handled by model B, which in turn triggers a custom event handled by model A. This can cause infinite loops.
# Order of events when entities get saved
When working with entity events in ECA, it is crucial to understand the sequence of operations that Drupal performs when an entity is saved. This knowledge helps in choosing the right event for your models and avoiding common pitfalls like infinite loops or data inconsistencies.
## The saving process
When an entity is saved, Drupal executes a specific sequence of steps. While the complete process involves many hooks and internal operations, the key events relevant to ECA occur in the following order:
1. [**Initialize content entity**](https://ecaguide.org/plugins/eca/content/events/content_entity_fieldvaluesinit/index.md): The entity object is initialized and default field values are populated.
1. [**Presave content entity**](https://ecaguide.org/plugins/eca/content/events/content_entity_presave/index.md): This event is triggered before the entity is written to the storage. This is the last chance to modify the entity's properties before they are persisted.
1. **Physical save**: The entity data is written to the database (or other storage).
1. **Insert/Update content entity**:
- If the entity is new, the [**Insert content entity**](https://ecaguide.org/plugins/eca/content/events/content_entity_insert/index.md) event is triggered.
- If the entity already existed, the [**Update content entity**](https://ecaguide.org/plugins/eca/content/events/content_entity_update/index.md) event is triggered.
Other entity lifecycle events
Beyond the save process, Drupal also triggers events for other operations on entities:
- [**Create content entity**](https://ecaguide.org/plugins/eca/content/events/content_entity_create/index.md): Fired when an entity object is first created in memory (before it is saved).
- [**Load content entity**](https://ecaguide.org/plugins/eca/content/events/content_entity_load/index.md): Fired when an entity is loaded from the database.
- [**Validate content entity**](https://ecaguide.org/plugins/eca/content/events/content_entity_validate/index.md): Fired during entity validation.
- [**Predelete content entity**](https://ecaguide.org/plugins/eca/content/events/content_entity_predelete/index.md): Fired before an entity is deleted from the database.
- [**Delete content entity**](https://ecaguide.org/plugins/eca/content/events/content_entity_delete/index.md): Fired after an entity has been deleted.
## Best practices
### Modifying the entity being saved
If you want to modify the entity that triggered the event (e.g., set a field value based on another field):
- **Use the `Presave content entity` event.**
- **Do NOT add an action to save the entity.**
Since the `Presave` event happens *before* the physical save, any changes you make to the entity object in your ECA model will be automatically included when Drupal proceeds to step 3 (Physical save).
Do not save in Presave
If you add an action to save the entity within a `Presave` event, you will likely cause an infinite loop or an error, because that save action will trigger the `Presave` event again.
In the "Entity: save" action, ensure the "Save entity" option is set to **no** or simply omit the save action if you only modified fields using "Entity: set field value".
### Reacting after save
If you need to perform actions that depend on the entity being fully persisted (e.g., sending a notification, creating a related entity, or triggering an external API call):
- **Use the `Insert content entity` or `Update content entity` event.**
- If you modify the entity in these events and want those changes to persist, you **MUST** explicitly save the entity again.
Recursion prevention
Be careful when saving an entity within an `Insert` or `Update` event, as this will trigger the save process again (starting from `Presave`). Ensure your model has conditions to prevent infinite loops (e.g., checking if the change has already been applied).
## Related resources
- [Entity Events Part 1](https://ecaguide.org/library/test%20models/entity_events_part_1/index.md) — Library model demonstrating entity event behavior
- [Entity Events Part 2](https://ecaguide.org/library/test%20models/entity_events_part_2/index.md) — Library model with additional entity event scenarios
# Events
Events are the starting point of every ECA model. An event represents something that happens on your Drupal site -- a trigger that tells ECA to begin processing. Without at least one event, an ECA model has nothing to respond to.
## What is an event?
In Drupal, events are signals dispatched by the system whenever something noteworthy occurs. ECA listens for these signals and, when one matches a model's configuration, starts processing the associated conditions and actions.
Think of events as the "when" in the sentence: **"When** *this happens*, check *these conditions*, then do *these actions*."
## Common event categories
ECA provides events across many categories. Here are some of the most frequently used:
Content events : Triggered when content entities are created, updated, deleted, or viewed. Examples include [Presave content entity](https://ecaguide.org/plugins/eca/content/events/content_entity_presave/index.md) and [Insert content entity](https://ecaguide.org/plugins/eca/content/events/content_entity_insert/index.md). For details on entity lifecycle ordering, see [Entity events](https://ecaguide.org/eca/concepts/entity_events/index.md).
User events : Triggered by user-related activities such as logging in, logging out, or account creation.
Form events : Triggered during form operations -- building, validating, or submitting a form. See [Build form](https://ecaguide.org/plugins/eca/form/events/form_form_build/index.md) for an example.
Cron events : Triggered at scheduled intervals when Drupal's cron runs, useful for periodic tasks.
Custom events : Events you define yourself, triggered explicitly from within an ECA model using the [Trigger custom event](https://ecaguide.org/plugins/eca/base/actions/eca_trigger_custom_event/index.md) action. See [Custom events](https://ecaguide.org/eca/concepts/custom_events/index.md) for details.
Request/response events : Triggered during the HTTP request lifecycle, allowing you to intercept and modify requests or responses. See [Choosing the right request and response events](https://ecaguide.org/eca/tips/request_response_events/index.md).
Discovering available events
The full list of events available on your site depends on which modules are installed. Browse the [Plugins section](https://ecaguide.org/plugins/index.md) for a comprehensive reference of all events, organised by module.
## How events work in ECA
When Drupal dispatches an event, ECA checks whether any enabled models are listening for that event. If a match is found, ECA begins processing:
1. The event fires and ECA receives it.
1. ECA identifies all models subscribed to that event.
1. For each matching model, ECA follows the connections from the event to its successors -- evaluating [conditions](https://ecaguide.org/eca/concepts/conditions/index.md) and executing [actions](https://ecaguide.org/eca/concepts/actions/index.md).
A single event can be used in multiple models, and a single model can listen for multiple events.
## Tokens provided by events
Most events automatically make certain [tokens](https://ecaguide.org/eca/concepts/tokens/index.md) available for use in subsequent conditions and actions. For example:
- **Entity events** provide the related entity as `entity` and also under its type name (e.g. `node`, `user`).
- **Form events** provide the form as `form` and `current_form`.
- The tokens `user` and `current_user` (the current user entity) are always available regardless of the event type.
Refer to each event's documentation page for details on which tokens it provides.
## Adding an event to a model
Every ECA model needs at least one event. How you add one depends on which modeler you use:
### Workflow Modeler
1. Open or create a model at **Configuration > Workflow > ECA**.
1. From the toolbar or canvas, add an **Event** node.
1. Select the event type from the categorized list.
1. Configure the event's settings in the side panel (e.g. which content type, which form ID).
1. Connect the event to conditions or actions using edges.
### BPMN.iO
1. Open or create a BPMN.iO model.
1. Click the **Start Event** circle on the canvas (or drag one from the palette).
1. Use the wrench icon to change the event type, then select the ECA event from the configuration panel on the right.
1. Configure the event's settings in the properties panel.
1. Draw sequence flows from the event to subsequent tasks or gateways.
### Classic Modeler
1. Open or create a model using the Classic Modeler.
1. Use the form interface to add an event by selecting from the available event types.
1. Fill in the configuration fields.
1. Connect the event to conditions and actions using the form-based interface.
## Tips for working with events
- **Be specific**: Choose the most specific event for your use case. For example, use a content type-specific event rather than a generic "any entity" event to avoid unnecessary processing.
- **Mind the execution order**: If multiple models listen for the same event, they all run. Use conditions to guard against unintended side-effects.
- **Check token availability**: Before referencing a token in a condition or action, ensure the event (or a preceding action) makes that token available. See [Debugging](https://ecaguide.org/eca/debugging/index.md) for techniques to inspect available tokens at runtime.
- **Performance**: Every event subscription adds a small overhead. Disable models you are not actively using.
# Gateways
## Current Status (ECA 2.x)
**Important**: Gateways are **not fully implemented** in ECA 2.x, this is a work in progress (WiP). The current implementation provides basic functionality but lacks proper BPMN gateway logic.
### Current Behaviour
- **Multiple outgoing links**: All paths are processed sequentially in the order they were added;
- **Multiple incoming links**: Each incoming link processes everything after the gateway independently;
- **No exclusive logic**: Despite being labelled "exclusive gateway" or "x-gateway", a gateway currently behaves like an inclusive gateway;
- **Optional condition handling**: Each outgoing path can have explicit conditions, a path without conditions defaults to TRUE and is therefore always open;
## How Gateways Currently Work
### Sequential Processing
```
Event/Action → Gateway → [Path 1] → Actions
→ [Path 2] → Actions
→ [Path 3] → Actions
```
All paths execute in sequence, not based on conditions.
### Multiple Inputs
When multiple paths flow into a gateway, each arrival triggers separate processing of all subsequent actions.
## Current Use Cases
### 1. Flow Merging
Combine multiple event flows into a single processing path:
```
Event A → [Specific prep] ↘
Event B → [Specific prep] → Gateway → [Common processing]
Event C → [Specific prep] ↗
Event D → [Specific prep] ↗
```
**Note**: In the above model, [Common processing] will potentially get executed 4 times if all 4 events are triggered.
### 2. Action Chaining (Alternative to AND Task)
Gateways can be used instead of AND tasks for chaining actions when there's only one successor:
```
Action → Gateway → Next Action
```
**Note**: This is equivalent to using an AND task but quicker to implement.
### 3. Conditional Branching (Manual Implementation)
For true conditional logic, you must add explicit conditions to each outgoing path:
```
Action → Gateway → [Condition: field > 0] → Path A
→ [Condition: field ≤ 0] → Path B
```
# Loops
ECA does not have a dedicated "loop" element in its modeling palette. However, iterating over a set of items is a common requirement, and ECA provides several approaches to achieve loop-like behavior.
## Iterating over lists with Views
The most common pattern for looping in ECA uses a **Views** query combined with a **custom event**:
1. **Execute a View** using the [Views: execute a query](https://ecaguide.org/plugins/eca/views/actions/eca_views_query/index.md) action. The View returns a list of entities matching your criteria.
1. For each entity in the result set, ECA automatically triggers a **custom event**, forwarding the current entity as a token.
1. A separate ECA model (or the same model listening for the custom event) processes each entity individually.
```
Event → [Execute View] → for each result → [Custom Event per entity]
↓
[Process single entity]
```
This pattern effectively creates a loop: the View provides the list, and the custom event fires once per item, allowing you to perform actions on each entity.
## Iterating with list operations
ECA provides actions for working with lists (arrays) of items:
- **List: loop through items** -- iterates over a list token and triggers a custom event for each item. This is the most straightforward loop mechanism.
- **List: count items** -- retrieves the number of items in a list, useful for conditional logic based on list size.
- **List: add/remove items** -- modifies a list, which can be combined with other patterns for accumulating results.
## Recursive custom events
For scenarios where the number of iterations is not known in advance, or where you need conditional looping (similar to a "while" loop in programming), you can use a recursive pattern:
1. An action triggers a [custom event](https://ecaguide.org/eca/concepts/custom_events/index.md) with relevant tokens.
1. The model listening for that custom event performs work and checks a condition.
1. If the condition indicates more iterations are needed, the model triggers the same custom event again with updated tokens.
Avoid infinite loops
When using recursive custom events, always ensure there is a clear termination condition. An infinite loop will eventually exhaust PHP's execution time or memory limits and may cause your site to become unresponsive. Use a counter token or a clear exit condition, and consider adding a safety limit (e.g. stop after 100 iterations).
## Entity reference traversal
When you need to process entities related to a given entity (e.g. all paragraphs in a node, all nodes in a taxonomy term), you can:
1. Use [Entity: load referenced entities](https://ecaguide.org/plugins/eca/content/actions/eca_token_load_entity_ref/index.md) to retrieve the referenced entities.
1. Iterate over the results using a list loop or Views-based pattern.
## Practical example: send email to all users with a role
A common use case is notifying all users who have a specific role:
1. **Event**: Content is created (or any other trigger).
1. **Action**: Execute a View that returns all users with the target role.
1. **Custom event**: Fires once per user found by the View.
1. **Action** (in the custom event model): Send an email to the current user, using tokens from both the original content and the user entity.
Token forwarding
When using custom events for looping, remember to list all required tokens in the "Tokens to forward" field of the [Trigger custom event](https://ecaguide.org/plugins/eca/base/actions/eca_trigger_custom_event/index.md) action. Without this, the tokens from the original process will not be available inside the loop iteration.
## Performance considerations
- **Views-based loops** are efficient for entity-based iteration because they use database queries.
- **Recursive loops** add overhead per iteration. Keep the loop body lightweight and avoid deeply nested recursion.
- If you need to process a very large number of items, consider using cron-based processing or a queue system to avoid timeout issues.
# Permissions
There are two main aspects regarding permissions in ECA: permissions required to maintain ECA models, and permissions applied during the execution of those models.
## Maintaining ECA models
Permissions for creating, editing and managing ECA models are provided by the [Modeler API](https://www.drupal.org/project/modeler_api) module — a dependency of the ECA UI module. The Modeler API dynamically generates a set of granular permissions for each installed modeler (e.g. BPMN.iO, Workflow Modeler).
The most important permissions are:
`modeler api collection eca` : View the ECA model collection at `/admin/config/workflow/eca`.
`modeler api view eca` : View individual ECA models.
`modeler api edit eca` : Create, edit, enable, disable, clone, import and export ECA models.
`modeler api delete eca` : Delete ECA models.
`modeler api administer eca` : Access the ECA settings page at `/admin/config/workflow/eca/settings`. This is a restricted permission.
In addition, there are per-modeler permissions that control which modeler a user may use:
`modeler api edit eca with bpmn_io` : Edit ECA models with the BPMN.iO modeler.
`modeler api view eca with bpmn_io` : View ECA models rendered by the BPMN.iO modeler.
Similar permissions are generated for each additional modeler plugin (e.g. `workflow_modeler`).
Further permissions cover metadata editing, context switching, templates, testing and replay:
- `modeler api edit metadata eca` — Edit model metadata.
- `modeler api switch context eca` — Switch the modeler context.
- `modeler api create template eca` — Create ECA templates.
- `modeler api edit template eca` — Edit ECA templates.
- `modeler api test eca` — Test ECA models.
- `modeler api replay eca` — Replay ECA models.
Finally, the ECA core module defines one additional permission via its entity type:
`administer eca` : Full administrative access to ECA configuration entities. This permission is used by the ECA log view and the External Workflows settings page.
## User permission when executing models
All ECA models are processed under the context of the **current user**. This means that all conditions and actions respect the permissions of the user who triggered the event.
- **Web requests**: The model executes with the permissions of the currently logged-in user (or Anonymous if not logged in).
- **Cron**: Drupal runs cron as the **Anonymous** user (UID 0). Models triggered by the [ECA: Cron](https://ecaguide.org/plugins/eca/base/events/eca_base_eca_cron/index.md) event therefore execute without any authenticated session.
- **Drush**: Commands such as `drush cron` or `drush eca:trigger:custom_event` run as **Anonymous** (UID 0) by default, unless the `--uid` option is passed to Drush.
If the current user lacks the necessary permissions to perform an action (e.g. publishing content, viewing a specific entity, or executing a View), that action will fail or return incomplete results.
### Global execution user
ECA provides a global setting (`eca.settings.user`) that allows **all** model executions to run under a specific user account, regardless of who triggered the event. When this setting is configured (via the ECA settings page at `/admin/config/workflow/eca/settings`):
- Every model execution automatically switches to the configured user **before** processing begins.
- The original user who triggered the event is available as the `[session_user]` token, so your model can still reference or compare against the real user.
- After model execution completes, the user context reverts automatically.
This is particularly useful for sites where many models need elevated permissions, avoiding the need to add explicit user-switch actions to every model.
### Switching user context within a model
When only certain parts of a model need elevated permissions, you can switch the user context explicitly. ECA provides three actions for this:
[User: switch current account](https://ecaguide.org/plugins/eca/user/actions/eca_switch_account/index.md) : Switch to a specific user by UID. The switch applies only to the **successors** of this action. Once that branch of the model execution completes, the user context automatically reverts to the previous user. This includes any custom events triggered from within the switched context.
[User: switch to service user](https://ecaguide.org/plugins/eca/user/actions/eca_switch_service_account/index.md) : Switch to the globally configured **service user** (`eca.settings.service_user`), set on the ECA settings page. This works identically to the regular switch but avoids hard-coding a UID into each model. Using a service user is recommended for portability — the same model can work across different environments without modification.
[User: switch back](https://ecaguide.org/plugins/eca/user/actions/eca_switch_back/index.md) : Explicitly switch back to the previous user account before the current branch completes. This is useful when you need to return to the original user context mid-way through a sequence of actions rather than waiting for the automatic revert at the end of the branch.
### Executing Views and queries
A common scenario involves using the [Views: Execute query](https://ecaguide.org/plugins/eca/views/actions/eca_views_query/index.md) action. Views checks entity access permissions by default, which means:
- If the current user (e.g. Anonymous during cron) does not have access to the entities returned by the View, the result set will be empty — even if the entities exist.
- View-level access settings such as "Unrestricted" only control who can *run* the View, not which entities are returned. Entity-level access checks still apply unless SQL rewriting is explicitly disabled in the View configuration.
**Solution**: Switch the user context to a privileged account before executing the View. Remember to store the original user in a token first if you need to reference it afterwards.
Example: Querying a View during cron
A model triggered by the ECA cron event needs to load nodes from a View:
1. The model starts as **Anonymous** (UID 0).
1. Add a [User: switch current account](https://ecaguide.org/plugins/eca/user/actions/eca_switch_account/index.md) action to switch to a user with the necessary permissions.
1. Execute the View — results now include entities the switched user has access to.
1. The user context reverts automatically when the branch completes.
If you also need to compare values against the original user, load the current user entity into a token *before* switching.
# Tokens
Tokens are a key component in ECA. They can be seen like variables, that store values and can be referenced later in the process again. That token functionality is provided by Drupal core out of the box and doesn't require any extra module or configuration.
However, there is also a [Token module](https://www.drupal.org/project/token) in contrib space, which is totally optional. Using ECA **does not** require that module to be available. Having said that, you may want to consider installing it as it provides a number of useful extra tokens as well as the [Token browser](#token-browser) which is a handy widget to provide you with a list of available tokens and their syntax.
Tokens vs. Token names
A token is a placeholder, like a variable in programming languages or scripts. They get replaced with their corresponding value during run-time. A token can always be identified when it's enclosed in brackets, e.g. `[node:title]` or `[myId]`. This is the syntax to be used when a configuration field contains "This property supports tokens." in its description.
Other fields in ECA action configuration ask for a "Token name" instead, not for a token. Such a token name comes without brackets and is the first part up to the first (if available) colon, e.g. `node` or `myId` from the two token examples above.
## Making tokens available
Tokens in the traditional Drupal context are provided by modules, e.g. all the node or user properties. An overview of all defined tokens can be given by the [Token Browser](#token-browser). In ECA, a model can also define tokens on the fly, but there is an important distinction between the two types of tokens:
Read-only vs. read-write tokens
All tokens provided by Drupal core and contrib/custom modules are read-only and can be found in the [Token Browser](#token-browser). ECA models can use all these tokens and all their properties, if they were made available, which is described in this chapter.
On the other hand, there are dynamically created tokens by ECA. Those can be read from and written to. They only have a distinct token name, e.g. `myNode`. If the token value is not a scalar value but an object, e.g. a list, an entity, or other DataTransferObjects (DTO), then you can access their (nested) properties just like with the read-only tokens described above.
However, in all cases, tokens have to be made available before they can be used later in the processing chain. These are the options on how that could happen:
### Tokens implicitly made available by events
Most events implicitly provide some tokens under pre-defined names.
Feature announcement (3348422)
There is a [feature on the roadmap](https://www.drupal.org/project/eca/issues/3348422) which will expose all the implicit tokens from events in the modeler UI and also here in the ECA Guide. This is currently not possible yet and the user needs to find such tokens either in support channels, in the code or by using the debugging techniques described in the [Debugging](https://ecaguide.org/eca/debugging/index.md) section.
The tokens `user` and `current_user` are always available, they contain the current user entity of the request that triggered the event.
When an entity related event gets triggered, e.g. [Presave content entity](https://ecaguide.org/plugins/eca/content/events/content_entity_presave/index.md), then ECA automatically puts the related entity onto the token stack with the name `entity` and also under the name of the entity type, e.g. `node`, `user`, etc.
When a form related event gets triggered, e.g. [Build form](https://ecaguide.org/plugins/eca/form/events/form_form_build/index.md), then ECA automatically puts the related form onto the token stack with the names `form` and `current_form`.
Others will be documented as soon as the above feature got implemented.
### Tokens declared by actions
Most action plugins make their result available as a token, and their configuration form allows you to define the token name for that. Example actions are:
- [Token: Set value](https://ecaguide.org/plugins/eca/base/actions/eca_token_set_value/index.md)
- [Entity: load](https://ecaguide.org/plugins/eca/content/actions/eca_token_load_entity/index.md)
@todo Tag action plugins
Let's tag action plugins that provide tokens so that we can link to that tag here.
### Tokens being forwarded
When triggering a [Custom event](https://ecaguide.org/plugins/eca/base/events/eca_base_eca_custom/index.md) or an [Entity-aware custom event](https://ecaguide.org/plugins/eca/content/events/content_entity_custom/index.md) with the [Trigger custom event](https://ecaguide.org/plugins/eca/base/actions/eca_trigger_custom_event/index.md) action or the [Trigger entity-aware custom event](https://ecaguide.org/plugins/eca/content/actions/eca_trigger_content_entity_custom_event/index.md) action, a list of tokens, that are available in the triggering process, can be forwarded to the triggered event. They will then be available in the triggered event and its process under the same name as the triggering process.
In case of the entity-aware custom events, there will always be an entity being forwarded in addition to the tokens mentioned above. That entity will always be known under the name `entity` in the triggered events and its process.
### A word about token names
No matter how a token gets made available to the ECA process, it always comes with a name. There is no naming convention and you can call tokens what ever you like. This is important for two reasons:
1. A token representing e.g. a node, does not have to be called `node`. Instead, you can call it `currentArticle` or `referencedLocation` or what else. This allows you to have multiple nodes in the token stack available at the same time. Token names need to be alpha-numeric, though. So, no spaces or other special characters are allowed.
1. When you're looking for token property syntax in the [Token browser](#token-browser), you only always find the tokens under their originally declared name. So, if you want to use the title of your current article, which can be found as `[node:title:value]` in the token browser, just replace the token name `node` with the name of your own token, i.e. `[currentArticle:title:value]`.
## Scope of tokens
Tokens only ever exist, if they [have been made available](#making-tokens-available), and they get quickly forgotten automatically as soon as the process of the current event has been completed. They are never available outside of that event process. You can pass a token to a custom event though, but otherwise no token exists outside of the process where it has been made available.
This may sounds too strict, but it follows modern programming language design which keeps variables protected from being accessible from outside their declaring context.
Should you require more global or even persistent values/variables/token, then there are storage mechanisms required. In the ECA ecosystem there are many options available to do that:
- [Read](https://ecaguide.org/plugins/eca/base/actions/eca_keyvaluestore_read/index.md) and [write](https://ecaguide.org/plugins/eca/base/actions/eca_keyvaluestore_write/index.md) the key/value store
- [Read](https://ecaguide.org/plugins/eca/base/actions/eca_keyvalueexpirablestore_read/index.md) and [write](https://ecaguide.org/plugins/eca/base/actions/eca_keyvalueexpirablestore_write/index.md) the expirable key/value store
- [Read](https://ecaguide.org/plugins/eca/base/actions/eca_state_read/index.md) and [write](https://ecaguide.org/plugins/eca/base/actions/eca_state_write/index.md) the state
- [Read](https://ecaguide.org/plugins/eca/base/actions/eca_privatetempstore_read/index.md) and [write](https://ecaguide.org/plugins/eca/base/actions/eca_privatetempstore_write/index.md) the private temporary store
- [Read](https://ecaguide.org/plugins/eca/base/actions/eca_sharedtempstore_read/index.md) and [write](https://ecaguide.org/plugins/eca/base/actions/eca_sharedtempstore_write/index.md) the shared temporary store
- [Read](https://ecaguide.org/plugins/eca/config/actions/eca_config_read/index.md) and [write](https://ecaguide.org/plugins/eca/config/actions/eca_config_write/index.md) config
- [Get](https://ecaguide.org/plugins/eca/parameters/actions/eca_parameter_get/index.md) and [set](https://ecaguide.org/plugins/eca/parameters/actions/eca_parameter_set/index.md) parameters
## Token Browser
While tokens are provided by Drupal core, the token browser is provided by the [Token](https://www.drupal.org/project/token) contrib module and is only available, if that module is installed and enabled. In the context of any ECA modeler, that token browser can be opened as an overlay and contains all potential tokens that are available on that Drupal site. This doesn't mean that all those tokens are available during run-time, when ECA processes you models. It rather is a "directory" of all token names and their properties. It's up to the ECA model to make sure that a required token is available to the processor prior to it being used.
## What else you need to know about tokens
### Tokens may be linked to their data source
There is an important difference on how field values are made available as tokens:
- [Token: set value](https://ecaguide.org/plugins/eca/base/actions/eca_token_set_value/index.md): when using e.g. `[entity:field_name]` to set a token, that keeps the reference back to the field and if the token value changes within that ECA model, this will also change the original entity field value.
- [Entity: get field value](https://ecaguide.org/plugins/eca/content/actions/eca_get_field_value/index.md): a field value stored in a token this way will not keep the link to the original entity field and the token value can be modified safely without having to worry about unintentionally changing entity field values.
@todo Verify statement
Need to verify, if this is correct or if it's the other way round. Ideally we should write tests for this.
### Nested tokens
If a token contains references to other tokens, Drupal's token replacement function does not replace those nested tokens by design.
Example: you may have a token called `myText` which has a value like `Some longer text containing a [site:name]`, when you're using your token somewhere like `The text containing [myText] and more.` will be replaced into `The text containing Some longer text containing a [site:name] and more.`, so the nested token `[site:name]` won't be replaced.
ECA 2.0 comes with an action [Token: replace](https://ecaguide.org/plugins/eca/base/actions/eca_token_replace/index.md) which resolves that issue such that it replaces tokens up to 5 nesting levels deep.
### Using square brackets in token-enabled fields
Since tokens are identified by square brackets, using literal `[` and `]` characters in token-enabled fields can cause unintended token replacement. For example, Tailwind CSS classes like `[&_.label]:hidden` would be misinterpreted as a token reference. See the [Tips & Tricks section on using brackets](https://ecaguide.org/eca/tips/brackets/index.md) for a recommended workaround using custom bracket tokens.
### Entity fields: value or rendered
Using the token `[entity:field_name]` will return the rendered field value, so the default field formatter will be called before the field value is being replaced.
In contrast, the token `[entity:field_name:value]` returns the original field value without using any rendering or field formatter.
### String values: plain text or markup
All string values are returned as markup rather than plain text by default. This is important for security reasons to prevent malicious data to cause cross-site scripting issues. However, that's not helpful in many case in the context of ECA processing, when you e.g. want to copy the value from one text field to another in an entity.
An example, of what's different in markup and plain text, is the `Terms & conditions` string. If that's returned as markup and afterwards stored in a string field again, you'll get `Terms & conditions` as a result. Other HTML entities will cause similar issues.
Feature announcement (3373537)
In ECA 2.0 there is an option to force token replacement to plain text only. By default, token replacement remains the same as it is implemented in Drupal core, so that your model only changes behavior intentionally.
### Replacing tokens in non-ECA actions
Action plugins that are not developed specifically for ECA often do not contain a token-replace function, and therefore may not automatically replace any tokens that are passed into them. Those action plugins (for example, "send mail" and "display message to user" and others listed under "Drupal core" in the task's "Choose element template" dialog) will have a "Replace tokens" field at the bottom of their configuration panes. Setting that field to "yes" may be necessary for proper token replacement. Tip: If you're having trouble with token replace in an action plugin, and the "Replace tokens" field is visible and set to "no," try changing it to "yes" as a first step in debugging.
# ECA Best Practices
This section collects recommendations and best practices for building, organizing, and maintaining ECA models.
## Structuring your models
One common question when working with ECA is whether to create many small, individual models for each task or to combine related logic into fewer, larger models.
### Individual vs. Combined Models
**Recommendation:** Group related functionality into a single model, but keep unrelated logic separate. Prioritize organization and maintainability over theoretical performance concerns.
#### Why group related logic?
If you have multiple reactions to the same event (e.g., "Presave content"), or a set of events that are logically part of the same workflow (e.g., a "New User Onboarding" process that involves account creation, role assignment, and a welcome email), it makes sense to keep them in one model. This keeps the "story" of that feature in one place, making it easier to understand and maintain.
#### Why separate unrelated logic?
If you have a model for "User Management" and another for "Article Publishing", keep them separate. This has two benefits:
1. **Organization:** It is easier to find what you are looking for.
1. **Performance:** ECA models are stored as configuration. When an event occurs, ECA loads the models that are listening for that event. By separating unrelated models, you ensure that ECA only loads the configuration necessary for the current context, avoiding unnecessary memory usage and processing overhead.
### Performance Implications
While splitting models *can* have a performance benefit by reducing the configuration loaded for any given event, the difference is often negligible for typical sites. **Do not** sacrifice code readability or logical organization for the sake of micro-optimizations.
The primary driver for your decision should be: **"Does this structure make it easier for me (and others) to understand and maintain this workflow?"**
# Extending ECA
ECA is designed to be extensible. Its plugin-based architecture means that modules can provide new events, conditions, and actions without modifying ECA itself.
## Ways to extend ECA
There are two primary ways to extend ECA's capabilities:
### Install additional modules
Many Drupal contrib modules provide ECA plugins out of the box. Installing a module that integrates with ECA immediately makes its events, conditions, and actions available in your models. See [Modules extending ECA](https://ecaguide.org/eca/extend/modules/index.md) for a list of known modules.
ECA itself ships with several sub-modules that add support for specific Drupal subsystems (content, forms, users, views, workflows, and more). The [ECA Starterkit recipe](https://ecaguide.org/eca/install/index.md) installs the most commonly used sub-modules automatically.
### Develop custom plugins
If no existing module covers your use case, you can develop custom ECA plugins for your site or contribute them back to the community. ECA provides a scaffolding tool via Drush to help you get started quickly. See [Developing plugins](https://ecaguide.org/eca/extend/plugins/index.md) for details.
# Modules extending ECA
The ECA project maintains a [list of ecosystem modules](https://www.drupal.org/project/eca/ecosystem) on Drupal.org. The sections below organize those modules by type.
## ECA submodules
These submodules ship with the [ECA project](https://www.drupal.org/project/eca) itself.
### Providing ECA functionality
- ECA Access: Events and actions to control access on entities and fields - [plugins](https://ecaguide.org/plugins/eca/access/index.md)
- ECA Base: Base events, conditions and actions - [plugins](https://ecaguide.org/plugins/eca/base/index.md)
- ECA Cache: Actions to read, write or invalidate cache items - [plugins](https://ecaguide.org/plugins/eca/cache/index.md)
- ECA Config: Config events - [plugins](https://ecaguide.org/plugins/eca/config/index.md)
- ECA Content: Content entity events, conditions and actions - [plugins](https://ecaguide.org/plugins/eca/content/index.md)
- ECA Content Access: Grant or revoke content access - [plugins](https://ecaguide.org/plugins/eca/content_access/index.md)
- ECA Endpoint: Custom HTTP endpoints with ECA - [plugins](https://ecaguide.org/plugins/eca/endpoint/index.md)
- ECA File: File actions such as rename and write - [plugins](https://ecaguide.org/plugins/eca/file/index.md)
- ECA Form: Form API events, conditions and actions - [plugins](https://ecaguide.org/plugins/eca/form/index.md)
- ECA Language: Language-related actions - [plugins](https://ecaguide.org/plugins/eca/language/index.md)
- ECA Log: Events and actions for Drupal log messages - [plugins](https://ecaguide.org/plugins/eca/log/index.md)
- ECA Migrate: Migrate events - [plugins](https://ecaguide.org/plugins/eca/migrate/index.md)
- ECA Misc: Miscellaneous events and conditions from Drupal core and the kernel - [plugins](https://ecaguide.org/plugins/eca/misc/index.md)
- ECA Queue: Events, conditions and actions for queued operations - [plugins](https://ecaguide.org/plugins/eca/queue/index.md)
- ECA Render: Render-related actions - [plugins](https://ecaguide.org/plugins/eca/render/index.md)
- ECA User: User events, conditions and actions - [plugins](https://ecaguide.org/plugins/eca/user/index.md)
- ECA Views: Execute and export Views query results within ECA - [plugins](https://ecaguide.org/plugins/eca/views/index.md)
- ECA Workflow: Content entity workflow actions - [plugins](https://ecaguide.org/plugins/eca/workflow/index.md)
### Others
- ECA UI: Provide access to ECA in Drupal's admin UI
- ECA Development: Provides Drush commands and code generators for developers
## Drupal contrib modules
Separate projects on Drupal.org that extend ECA with extra events, conditions or actions. Where auto-generated plugin documentation exists, the list links to the corresponding plugin pages.
### Integrations with other Drupal modules
- [Commerce ECA](https://www.drupal.org/project/commerce_eca) - [plugins](https://ecaguide.org/plugins/eca/commerce/index.md)
- [CrowdSec](https://www.drupal.org/project/crowdsec) - [plugins](https://ecaguide.org/plugins/eca/crowdsec/index.md)
- [DANSE](https://www.drupal.org/project/danse) - [plugins](https://ecaguide.org/plugins/eca/danse/index.md)
- [Drupal Remote Dashboard](https://www.drupal.org/project/drd) - [plugins](https://ecaguide.org/plugins/drd/index.md)
- [Easy Email](https://www.drupal.org/project/easy_email) - [plugins](https://ecaguide.org/plugins/easy_email/index.md)
- [ECA Commerce](https://www.drupal.org/project/eca_commerce)
- [ECA Condition](https://www.drupal.org/project/eca_condition)
- [ECA Entity Print](https://www.drupal.org/project/eca_entity_print) - [plugins](https://ecaguide.org/plugins/eca/entity_print/index.md)
- [ECA Entity Share](https://www.drupal.org/project/eca_entity_share)
- Client - [plugins](https://ecaguide.org/plugins/eca/entity_share_client/index.md)
- Server - [plugins](https://ecaguide.org/plugins/eca/entity_share_server/index.md)
- [ECA Expression Language](https://www.drupal.org/project/eca_expression_language)
- [ECA External Workflows](https://www.drupal.org/project/eca_external_workflows)
- [ECA Field Widget Actions](https://www.drupal.org/project/eca_field_widget_actions)
- [ECA Flag](https://www.drupal.org/project/eca_flag) - [plugins](https://ecaguide.org/plugins/eca/flag/index.md)
- [ECA Group](https://www.drupal.org/project/eca_group)
- [ECA Helper](https://www.drupal.org/project/eca_helper) - [plugins](https://ecaguide.org/plugins/eca/helper/index.md)
- [ECA HTML Parser](https://www.drupal.org/project/eca_html_parser)
- [ECA Inline Entity Form](https://www.drupal.org/project/eca_inline_entity_form)
- [ECA Maestro](https://www.drupal.org/project/eca_maestro)
- [ECA Metatag](https://www.drupal.org/project/eca_metatag) - [plugins](https://ecaguide.org/plugins/eca/metatag/index.md)
- [ECA Mustache](https://www.drupal.org/project/eca_mustache)
- [ECA Parameters](https://www.drupal.org/project/eca_parameters) - [plugins](https://ecaguide.org/plugins/eca/parameters/index.md)
- [ECA Project Browser](https://www.drupal.org/project/eca_project_browser) - [plugins](https://ecaguide.org/plugins/eca/project_browser/index.md)
- [ECA Remote Request](https://www.drupal.org/project/eca_remote_request)
- [ECA Report](https://www.drupal.org/project/eca_report)
- [ECA Simplenews](https://www.drupal.org/project/eca_simplenews)
- [ECA Site Building Tools](https://www.drupal.org/project/eca_site_building)
- [ECA Starterkit](https://www.drupal.org/project/eca_starterkit)
- [ECA State Machine](https://www.drupal.org/project/eca_state_machine) - [plugins](https://ecaguide.org/plugins/eca/state_machine/index.md)
- [ECA Tamper](https://www.drupal.org/project/eca_tamper) - [plugins](https://ecaguide.org/plugins/eca/tamper/index.md)
- [ECA Telegram](https://www.drupal.org/project/eca_tg)
- [ECA Tool](https://www.drupal.org/project/eca_tool)
- [ECA Tour](https://www.drupal.org/project/eca_tour)
- [ECA Twilio action](https://www.drupal.org/project/eca_twilio_action)
- [ECA Variety Pack](https://www.drupal.org/project/eca_variety_pack)
- [ECA VBO](https://www.drupal.org/project/eca_vbo) - [plugins](https://ecaguide.org/plugins/eca/vbo/index.md)
- [ECA Views data export](https://www.drupal.org/project/eca_views_data_export) - [plugins](https://ecaguide.org/plugins/eca/views_data_export/index.md)
- [ECA Webform](https://www.drupal.org/project/eca_webform) - [plugins](https://ecaguide.org/plugins/eca/webform/index.md)
- [ECA Webprofiler](https://www.drupal.org/project/eca_webprofiler)
- [ECA: Google](https://www.drupal.org/project/eca_google)
- [ECA: HubSpot](https://www.drupal.org/project/eca_hubspot)
- [ECA: Zoom](https://www.drupal.org/project/eca_zoom)
- [GitLab API v4](https://www.drupal.org/project/gitlab_api) - [plugins](https://ecaguide.org/plugins/eca/gitlab_api/index.md)
- [Group Actions](https://www.drupal.org/project/group_action) - [plugins](https://ecaguide.org/plugins/group_action/index.md)
- [Maestro](https://www.drupal.org/project/maestro)
- [Mail Actions](https://www.drupal.org/project/mail_action)
- [Mautic ECA](https://www.drupal.org/project/mautic_eca) - [plugins](https://ecaguide.org/plugins/mautic_eca/index.md)
- [Message Notify ECA](https://www.drupal.org/project/message_notify_eca)
- [Orchestration](https://www.drupal.org/project/orchestration) - [plugins](https://ecaguide.org/plugins/orchestration_eca/index.md)
- [Push Framework](https://www.drupal.org/project/push_framework) - [plugins](https://ecaguide.org/plugins/push_framework/index.md)
- [Salesforce ECA Integration](https://www.drupal.org/project/salesforce_eca)
- [Token ECA Alter](https://www.drupal.org/project/token_eca_alter)
- [Workflow ECA](https://www.drupal.org/project/workflow_eca)
### AI and LLM integrations
- [AI Integration - ECA](https://www.drupal.org/project/ai_integration_eca) - [plugins](https://ecaguide.org/plugins/ai_integration_eca/index.md)
- [Augmentor AI](https://www.drupal.org/project/augmentor) - [plugins](https://ecaguide.org/plugins/augmentor/index.md)
- [Prompt AI](https://www.drupal.org/project/prompt) - [plugins](https://ecaguide.org/plugins/prompt/index.md)
### Communication integrations
- [BlueSky Integration](https://www.drupal.org/project/bsky) - [plugins](https://ecaguide.org/plugins/bsky/index.md)
- [DiscordPHP](https://www.drupal.org/project/discord_php)
- [DiscordPHP - Echo](https://www.drupal.org/project/discord_php_echo)
- [DiscordPHP Recipe - Echo](https://www.drupal.org/project/discord_php_recipe_echo)
- [ECA Kafka connection](https://www.drupal.org/project/eca_kafka)
### External services and APIs
- [Bookable Calendar](https://www.drupal.org/project/bookable_calendar)
- [HTTP Client Manager](https://www.drupal.org/project/http_client_manager)
- [HTTP Client Manager Issuu Oembed](https://www.drupal.org/project/http_client_manager_issuu_oembed)
- [Shelly](https://www.drupal.org/project/shelly)
- [Solcast](https://www.drupal.org/project/solcast)
### Other related projects
- [ECA Context](https://www.drupal.org/project/eca_context)
- [ECA Drush](https://www.drupal.org/project/eca_drush)
- [IOC: Internet Of Contributors](https://www.drupal.org/project/ioc)
- [Laravel Helpers](https://www.drupal.org/project/laravel_helpers)
- [Laravel Process](https://www.drupal.org/project/laravel_process)
- [SPARC](https://www.drupal.org/project/sparc)
## Known issues
### Variety of workflow modules
Currently 3 modules provide workflows, states and transitions:
- Workflows: provided by Drupal core
- Workflow: contrib module
- State machine
All three provide similar functionality, but they have nothing to do with each other. This can be confusing when reporting issues about ECA modeling around workflows. Always state which workflow system you are using.
Mixing events, conditions and actions from different workflow systems in the same ECA model can cause unexpected results or errors. Pay extra attention when you enable more than one workflow module.
# Developing plugins
## Code generator
Want to jumpstart ECA plugin development? Try the `eca_development` sub-module (not for use on production sites):
```
drush en eca_development
drush gen eca-event # Generates files for ECA event support.
drush gen eca-condition # Generates an ECA condition plugin.
drush gen eca-action # Generates an ECA action plugin.
```
## Developer guidelines
Let's collect guidelines for developers, so that the code structure becomes more clear and stays consistent.
- Default values in plugins: they can be defined in `::defaultConfiguration()` which will pre-set the fields in the UI. But since the user could remove those values, it still may be possible that the `::access` and `::execute` methods could find empty values, even though the default already had a value in them. Therefore, configuration values need to be checked in all use cases and set to default if expectation isn't met.
# Tips & Tricks
This section contains practical tips and tricks for working with ECA. These are solutions to common challenges that users encounter when building ECA models.
## Available tips
- [Using square brackets without unintended token replacement](https://ecaguide.org/eca/tips/brackets/index.md) - How to use `[` and `]` characters in your ECA configuration without ECA treating them as token references.
- [Choosing the right request and response events](https://ecaguide.org/eca/tips/request_response_events/index.md) - How to pick the correct kernel event for redirects, response modifications, and route-based logic, and how to avoid caching pitfalls.
- [Building HTMX-powered interfaces with ECA](https://ecaguide.org/eca/tips/htmx/index.md) - How to build auto-refreshing regions, click-to-load fragments, and dynamic responses with the `eca_htmx` module, without writing JavaScript.
# Using square brackets without unintended token replacement
## The problem
ECA uses square brackets `[` and `]` to identify [tokens](https://ecaguide.org/eca/concepts/tokens/index.md). When ECA encounters text like `[something]`, it attempts to replace it with the corresponding token value. If the token does not exist, the content inside the brackets is removed, resulting in an empty string.
This becomes a problem when you need to use square brackets for purposes other than tokens. A common example is using CSS frameworks like [Tailwind CSS](https://tailwindcss.com/), which uses bracket notation for arbitrary values. For instance, a class like `[&_.cf-form-label]:tw-hidden` would be interpreted by ECA as a token reference, and the content between the brackets would be stripped away.
The same issue arises in any context where square brackets are part of the intended value, such as:
- Tailwind CSS arbitrary value classes (e.g. `[&_.label]:hidden`)
- Form element machine names containing brackets (e.g. `wrapper][form][submit`)
- Regular expressions or other syntax that uses square brackets
## The solution
The recommended approach is to define two custom tokens that resolve to the bracket characters themselves:
| Token | Value |
| ----------------- | ----- |
| `[bracket:open]` | `[` |
| `[bracket:close]` | `]` |
Since token replacement is **not recursive** by default, the resulting bracket characters will not trigger further token replacement. This means you can safely use these tokens wherever you need literal bracket characters.
For example, instead of writing:
```
[&_.cf-form-label]:tw-hidden
```
You would write:
```
[bracket:open]&_.cf-form-label[bracket:close]:tw-hidden
```
After token replacement, this produces the intended result: `[&_.cf-form-label]:tw-hidden`.
## Making bracket tokens available globally
Rather than defining these tokens in every model that needs them, you can make them available globally using the [ECA token generate event](https://ecaguide.org/plugins/eca/base/events/eca_base_eca_token_generate/index.md). This event fires before ECA processes tokens for any model, making the bracket tokens available everywhere on your site.
### Step-by-step setup
1. **Create a new ECA model** and give it a descriptive name, such as "Global Tokens".
1. **Add the event** [ECA token generate event](https://ecaguide.org/plugins/eca/base/events/eca_base_eca_token_generate/index.md) as the trigger. In the "Name of token" field, enter `bracket:*` to react to all tokens in the `bracket` group.
1. **Add the first action** [Token: set value](https://ecaguide.org/plugins/eca/base/actions/eca_token_set_value/index.md) and configure it:
- **Token name**: `bracket:open`
- **Token value**: `[`
1. **Add a second action** [Token: set value](https://ecaguide.org/plugins/eca/base/actions/eca_token_set_value/index.md) and configure it:
- **Token name**: `bracket:close`
- **Token value**: `]`
1. **Save and enable** the model.
Once this model is active, the tokens `[bracket:open]` and `[bracket:close]` are automatically available in the token browser and in text fields within any ECA model on your site. You only need to set this up once.
Tip
This approach of using the ECA token generate event to define utility tokens can be extended to other global tokens you might need across your models. Consider adding other commonly used special characters or values to the same model.
# Building HTMX-powered interfaces with ECA
[HTMX](https://htmx.org/) lets you build dynamic, interactive interfaces by adding attributes to HTML elements, without writing JavaScript. Drupal core ships HTMX and a small integration layer, and the `eca_htmx` module exposes that integration to ECA models. This lets you build auto-refreshing regions, click-to-load fragments, and dynamic responses entirely from the modeler.
This guide explains what Drupal core provides, which ECA plugins map to those features, and how to combine them into working patterns.
## How HTMX works in Drupal
HTMX is a declarative markup system. You add `data-hx-*` attributes to an element, and the HTMX JavaScript library turns them into AJAX requests that swap the response into the page. For example, an element with `data-hx-get="/clock"` and `data-hx-trigger="every 1s"` polls `/clock` once a second and replaces its own content with the response.
Drupal core provides three building blocks:
- **The `core/drupal.htmx` library** — the HTMX runtime plus Drupal glue that re-attaches Drupal behaviors to swapped-in content and loads any new CSS and JavaScript the response requires.
- **The minimal response renderer** — when a request is marked as an HTMX request, core returns only the page's main content wrapped in a bare HTML document instead of the full themed page (header, blocks, and footer are omitted). The minimal response also carries an `X-Robots-Tag: noindex` header so the fragment is not indexed by search engines.
- **HTMX request and response headers** — HTMX sends `HX-*` request headers (such as `HX-Request` and `HX-Trigger`) and honors `HX-*` response headers (such as `HX-Redirect` and `HX-Trigger`) to coordinate client behavior.
The `eca_htmx` module builds entirely on these core building blocks. It never ships its own HTMX JavaScript, and it produces all attributes and headers through core's HTMX helper, so the markup stays consistent with the rest of Drupal.
## The two ways to request a minimal response
A recurring source of confusion is that core offers **two** independent ways to get the minimal (main-content-only) response, and they take effect at different stages of the request lifecycle. They are not interchangeable.
`_htmx_route` route option : Set on a **route** while routes are being built. Any request to a route carrying this option returns the minimal response. Use the [HTMX: mark route](https://ecaguide.org/plugins/eca/htmx/actions/eca_htmx_set_route/index.md) action, which reacts to the [Alter route](https://ecaguide.org/plugins/eca/misc/events/routing_alter/index.md) event.
`_wrapper_format=drupal_htmx` query parameter : Added to an individual **request** at runtime. Only that request returns the minimal response. Use the [HTMX: minimal response](https://ecaguide.org/plugins/eca/htmx/actions/eca_htmx_minimal_response/index.md) action, or let the polling and element actions add it automatically through their **only main content** option.
Which one to use
Mark the **route** when you control the route and want every request to it to return a fragment (for example, a dedicated endpoint that only ever serves a fragment). Mark the **request** when you want a specific interaction to fetch a fragment from a route that otherwise renders a full page.
## The plugins
### Building HTMX elements
[HTMX: render element](https://ecaguide.org/plugins/eca/htmx/actions/eca_htmx_element/index.md) : Builds a single element carrying the HTMX attributes you configure: the request method and URL, the trigger, the swap strategy, the target and select selectors, and options such as a confirmation dialog, indicators, pushed URLs, and request values. Use this for click-to-load fragments, infinite scroll, and similar interactions.
[HTMX: poll region](https://ecaguide.org/plugins/eca/htmx/actions/eca_htmx_poll/index.md) : A specialized element that polls a URL on an interval and swaps in the response. Use it for content that refreshes on its own, such as a clock, a status indicator, or a notification count. The **only main content** option is enabled by default, so the polled URL automatically returns a fragment.
Both actions render an element. Place them in a block, an entity display, or any other render context, and they attach `core/drupal.htmx` automatically.
### Reacting to HTMX requests
[HTMX: is request](https://ecaguide.org/plugins/eca/htmx/conditions/eca_htmx_is_request/index.md) : A condition that is true when the current request is an HTMX request. Use it to branch a model so that it builds a fragment for HTMX requests and a full page otherwise.
[HTMX: compare request value](https://ecaguide.org/plugins/eca/htmx/conditions/eca_htmx_header/index.md) : A condition that compares a value from the current HTMX request — such as the target element, the trigger, or the current URL — against an expected value.
The same request information is available as tokens, so you can read the HTMX target or trigger anywhere a token is accepted.
### Shaping HTMX responses
[HTMX: set response header](https://ecaguide.org/plugins/eca/htmx/actions/eca_htmx_response_header/index.md) : Sets any of the HTMX `HX-*` response headers on the current response, for example `HX-Redirect` to redirect the client, `HX-Trigger` to fire a client-side event, or `HX-Retarget` to change where the response is swapped. The `HX-Location` header supports its full form (source, event, handler, target, swap, values, headers, and select).
Because this action shapes the response, it reacts to a response event such as the [Response created](https://ecaguide.org/plugins/eca/misc/events/kernel_response/index.md) event or an [ECA endpoint response](https://ecaguide.org/plugins/eca/endpoint/events/eca_endpoint_response/index.md).
## Serving fragments with ECA endpoints
The natural source for an HTMX fragment is an [ECA endpoint](https://ecaguide.org/plugins/eca/endpoint/events/eca_endpoint_response/index.md). An endpoint gives you a URL that an ECA model fully controls: the model builds the response content and can attach HTMX response headers. Because you control the endpoint route, its response is already a fragment — no full page chrome to strip — and it pairs naturally with a polling region or a click-to-load element that points at it.
## Common patterns
### Auto-refreshing region
1. **Event**: any event that builds a render element, such as a block build.
1. **Action**: [HTMX: poll region](https://ecaguide.org/plugins/eca/htmx/actions/eca_htmx_poll/index.md) — set the URL to an endpoint that returns the fragment, and the interval in seconds.
The region polls the endpoint and swaps in the response on every interval. Leave **only main content** enabled so the endpoint returns a fragment.
### Click-to-load content
1. **Event**: any event that builds a render element.
1. **Action**: [HTMX: render element](https://ecaguide.org/plugins/eca/htmx/actions/eca_htmx_element/index.md) — set the method to `GET`, the URL to the fragment endpoint, the trigger to `click`, and the target to the element that should receive the content.
### Serve a fragment from an endpoint
1. **Event**: [ECA endpoint response](https://ecaguide.org/plugins/eca/endpoint/events/eca_endpoint_response/index.md) — define the endpoint path.
1. **Action**: [Set response content](https://ecaguide.org/plugins/eca/endpoint/actions/eca_endpoint_set_response_content/index.md) — build the fragment markup.
1. **Action** (optional): [HTMX: set response header](https://ecaguide.org/plugins/eca/htmx/actions/eca_htmx_response_header/index.md) — fire a client-side event with `HX-Trigger`, for example to notify other parts of the page that the fragment refreshed.
### Turn an existing route into a fragment
1. **Event**: [Alter route](https://ecaguide.org/plugins/eca/misc/events/routing_alter/index.md).
1. **Action**: [HTMX: mark route](https://ecaguide.org/plugins/eca/htmx/actions/eca_htmx_set_route/index.md) — set the route name (a `*` wildcard is supported) to mark as an HTMX route.
Every request to a marked route then returns the minimal response.
## Related resources
- [HTMX: render element](https://ecaguide.org/plugins/eca/htmx/actions/eca_htmx_element/index.md) — action reference
- [HTMX: poll region](https://ecaguide.org/plugins/eca/htmx/actions/eca_htmx_poll/index.md) — action reference
- [HTMX: mark route](https://ecaguide.org/plugins/eca/htmx/actions/eca_htmx_set_route/index.md) — action reference
- [HTMX: minimal response](https://ecaguide.org/plugins/eca/htmx/actions/eca_htmx_minimal_response/index.md) — action reference
- [HTMX: set response header](https://ecaguide.org/plugins/eca/htmx/actions/eca_htmx_response_header/index.md) — action reference
- [HTMX: is request](https://ecaguide.org/plugins/eca/htmx/conditions/eca_htmx_is_request/index.md) — condition reference
- [HTMX: compare request value](https://ecaguide.org/plugins/eca/htmx/conditions/eca_htmx_header/index.md) — condition reference
- [Choosing the right request and response events](https://ecaguide.org/eca/tips/request_response_events/index.md) — guide
- [HTMX Blocks](https://ecaguide.org/library/use%20case/htmx_blocks/index.md) — library example
# Choosing the right request and response events
When building ECA models that react to incoming HTTP requests — for example to perform redirects, enforce access rules, or modify response headers — it is essential to pick the correct kernel event. The Symfony HTTP kernel dispatches several events during the lifecycle of every request, and each one fires at a different stage. Using the wrong event is the most common source of problems such as redirects working only once, status messages disappearing after a redirect, or models that never fire on cached pages.
This guide walks through the available kernel events in the order they are dispatched, explains when to use each one, and highlights the caching pitfalls that trip up many users.
## The HTTP request lifecycle
Drupal uses Symfony's HTTP kernel, which dispatches events in the following order for a normal, successful request:
1. **[Start dispatching request](https://ecaguide.org/plugins/eca/misc/events/kernel_request/index.md)** (`kernel.request`)
1. **[Controller found to handle request](https://ecaguide.org/plugins/eca/misc/events/kernel_controller/index.md)** (`kernel.controller`)
1. **[Controller arguments have been resolved](https://ecaguide.org/plugins/eca/misc/events/kernel_controller_arguments/index.md)** (`kernel.controller_arguments`)
1. **[Controller does not return a Response instance](https://ecaguide.org/plugins/eca/misc/events/kernel_view/index.md)** (`kernel.view`) — only when the controller returns a render array rather than a full Response object
1. **[Response created](https://ecaguide.org/plugins/eca/misc/events/kernel_response/index.md)** (`kernel.response`)
1. **[Response for request created](https://ecaguide.org/plugins/eca/misc/events/kernel_finish_request/index.md)** (`kernel.finish_request`)
1. **[Response was sent](https://ecaguide.org/plugins/eca/misc/events/kernel_terminate/index.md)** (`kernel.terminate`)
Two additional events fire only on the error path:
- **[Uncaught exception](https://ecaguide.org/plugins/eca/misc/events/kernel_exception/index.md)** (`kernel.exception`)
- **[Exception status code](https://ecaguide.org/plugins/eca/misc/events/kernel_exception_status_code/index.md)** — fires on routing exceptions that produce 4xx or 5xx status codes (available since ECA 2.1.0)
## Choosing the right event
The table below summarizes typical use cases and the recommended event for each.
| Use case | Recommended event | Why |
| ------------------------------------- | ------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Redirect a page request | [Response created](https://ecaguide.org/plugins/eca/misc/events/kernel_response/index.md) | Works reliably with all caches enabled, for both anonymous and authenticated users. |
| Display a message and redirect | [Response created](https://ecaguide.org/plugins/eca/misc/events/kernel_response/index.md) | Messages stored in the session survive the redirect when this event is used. |
| Check the current route and act on it | [Controller found to handle request](https://ecaguide.org/plugins/eca/misc/events/kernel_controller/index.md) | Fires even when pages are served from the dynamic page cache. |
| Set or modify response headers | [Response created](https://ecaguide.org/plugins/eca/misc/events/kernel_response/index.md) | The response object is available at this stage. |
| Redirect 403 pages to login | [Response created](https://ecaguide.org/plugins/eca/misc/events/kernel_response/index.md) | Inspect the response status code and redirect accordingly. See the library model [Redirect 403 to Login Page](https://ecaguide.org/library/use%20case/redirect_403_to_login_page/index.md). |
| Redirect unpublished content | [Start dispatching request](https://ecaguide.org/plugins/eca/misc/events/kernel_request/index.md) | Intercept the request before the controller runs. See the library model [Redirect unpublished](https://ecaguide.org/library/use%20case/redirect_unpublished/index.md). |
| Perform expensive background work | [Response was sent](https://ecaguide.org/plugins/eca/misc/events/kernel_terminate/index.md) | Runs after the response has been delivered to the client, so it does not delay the page load. |
| Custom error handling | [Uncaught exception](https://ecaguide.org/plugins/eca/misc/events/kernel_exception/index.md) | Allows you to log, redirect, or display a custom error page. |
## Caching and kernel events
Drupal ships with two page-level cache layers that can prevent kernel events from firing at all:
`Page Cache` : The **Internal Page Cache** module (`page_cache`) caches full responses for anonymous users. When a cached response exists, the kernel short-circuits the entire request and **none of the kernel events fire** — not even `kernel.request`.
`Dynamic Page Cache` : The **Internal Dynamic Page Cache** module (`dynamic_page_cache`) caches rendered responses but still dispatches early kernel events. However, once a response is cached, later events in the chain may receive a cached response rather than a freshly built one.
### Which events fire on cached pages?
| Event | Fires with Page Cache (anonymous)? | Fires with Dynamic Page Cache? |
| ----------------------------- | ---------------------------------- | --------------------------------------- |
| `kernel.request` | No | Yes |
| `kernel.controller` | No | Yes |
| `kernel.controller_arguments` | No | Yes |
| `kernel.view` | No | Sometimes (only if the controller runs) |
| `kernel.response` | No | Yes |
| `kernel.finish_request` | No | Yes |
| `kernel.terminate` | No | Yes |
Page Cache bypasses everything
When the Internal Page Cache module serves a cached page for an anonymous user, **no ECA model will fire**. If your model must react to every anonymous request, you have two options:
1. Use the [Page Cache Kill Switch](https://ecaguide.org/plugins/eca/cache/actions/eca_page_cache_kill_switch/index.md) action in an earlier model to disable page caching for specific routes.
1. Uninstall the Internal Page Cache module (not recommended for production sites with high traffic).
### The Dynamic Page Cache pitfall
Even with the Dynamic Page Cache enabled, not all events behave the same way. A common pitfall:
- **`kernel.request`** fires before the controller is resolved. This makes it tempting for redirects, but the core redirect action (`system.action.redirect`) sets the redirect on the **response** event internally. When a cached response is served, the redirect set during `kernel.request` may not take effect because the cached response overwrites it.
- **`kernel.controller`** fires after the controller is found, even when the Dynamic Page Cache eventually serves a cached response. This makes it a reliable choice for inspecting the current route.
- **`kernel.response`** fires every time a response is being prepared, whether from cache or freshly built. This makes it the most reliable event for redirects and response modifications.
Rule of thumb
For redirects, prefer the **Response created** event (`kernel.response`). For route-based logic that does not modify the response, the **Controller found** event (`kernel.controller`) is a safe choice.
## Common patterns
### Redirect based on a route
1. **Event**: [Response created](https://ecaguide.org/plugins/eca/misc/events/kernel_response/index.md)
1. **Condition**: [Route match](https://ecaguide.org/plugins/eca/misc/conditions/eca_route_match/index.md) — compare to the route name, e.g. `entity.taxonomy_term.canonical`
1. **Action**: [Redirect to URL](https://ecaguide.org/plugins/core/actions/action_goto_action/index.md)
This pattern works reliably with all caches enabled, for both anonymous and authenticated users.
### Redirect and display a status message
1. **Event**: [Response created](https://ecaguide.org/plugins/eca/misc/events/kernel_response/index.md)
1. **Condition**: [Route match](https://ecaguide.org/plugins/eca/misc/conditions/eca_route_match/index.md)
1. **Action**: Display a message (e.g. using the Drupal messenger)
1. **Action**: [Redirect to URL](https://ecaguide.org/plugins/core/actions/action_goto_action/index.md)
Note
Status messages are stored in the user's session. For anonymous users without a session, messages set before a redirect will be lost. This is standard Drupal behavior and not specific to ECA.
### Inspect or modify the response
1. **Event**: [Response created](https://ecaguide.org/plugins/eca/misc/events/kernel_response/index.md)
1. **Action**: [Set headers](https://ecaguide.org/plugins/eca/helper/actions/eca_helper_header_set/index.md) — add or modify response headers such as `Cache-Control`, `X-Robots-Tag`, or custom headers.
### React to a specific route without modifying the response
1. **Event**: [Controller found to handle request](https://ecaguide.org/plugins/eca/misc/events/kernel_controller/index.md)
1. **Condition**: [Route match](https://ecaguide.org/plugins/eca/misc/conditions/eca_route_match/index.md)
1. **Action**: any action that does not depend on the response object (e.g. logging, setting tokens, triggering custom events)
See the library model [Route test](https://ecaguide.org/library/simple/route_test/index.md) for a working example.
## Why "View Content Entity" is not the right event for redirects
The **View Content Entity** event might seem like the natural choice when you want to react to a user visiting a node or taxonomy term page. However, this event fires whenever the entity is **rendered**, which can happen multiple times per request (e.g. in views, blocks, or embedded references) and is not limited to the canonical page of that entity.
Additionally, because this event fires during the rendering phase, any redirect action triggered by it is processed too late — the response may already be cached by the Dynamic Page Cache, causing the redirect to work only on the first uncached request.
Use the **Response created** or **Controller found** events instead, combined with a [Route match](https://ecaguide.org/plugins/eca/misc/conditions/eca_route_match/index.md) condition to target the correct page.
## Related resources
- [Response created](https://ecaguide.org/plugins/eca/misc/events/kernel_response/index.md) — event reference
- [Controller found to handle request](https://ecaguide.org/plugins/eca/misc/events/kernel_controller/index.md) — event reference
- [Start dispatching request](https://ecaguide.org/plugins/eca/misc/events/kernel_request/index.md) — event reference
- [Route match](https://ecaguide.org/plugins/eca/misc/conditions/eca_route_match/index.md) — condition reference
- [Page Cache Kill Switch](https://ecaguide.org/plugins/eca/cache/actions/eca_page_cache_kill_switch/index.md) — action reference
- [Redirect 403 to Login Page](https://ecaguide.org/library/use%20case/redirect_403_to_login_page/index.md) — library example
- [Redirect unpublished](https://ecaguide.org/library/use%20case/redirect_unpublished/index.md) — library example
- [Route test](https://ecaguide.org/library/simple/route_test/index.md) — library example
# Debugging
ECA processes models in the background in response to events. Because there is no visible output during processing, finding out why a model does not behave as expected requires dedicated debugging tools.
ECA provides three approaches, each suited to different situations:
- **Visual debug mode** (ECA 3.1+) -- interactive, detailed, built into the modeler UI.
- **Database logging** -- lightweight, works with every ECA version.
- **Webprofiler** (ECA 2+) -- per-request isolation of ECA log messages.
## Visual debug mode
*Requires ECA 3.1 or later and a modeler that supports it. Currently only the [Workflow Modeler](https://ecaguide.org/modeler/workflow/index.md) supports visual debugging.*
Visual debug mode collects detailed processing data -- including the full list of available tokens and their values at every step -- and displays the results directly in the modeler UI.
### Enable debug mode
There are two ways to activate it:
Manually : Go to **Administration > Configuration > Workflow > ECA > Settings** (`/admin/config/workflow/eca/settings`) and enable the **Debug mode** checkbox.
Via the test button : In the Workflow Modeler, click the **Test** button on an event. This temporarily enables debug mode for the duration of the test. If debug mode was not already on, ECA enables it automatically and disables it again when the test completes or when the configured timeout expires.
Performance impact
Debug mode has a significant performance impact because it normalizes and stores token data on every ECA processing run. Do not leave it enabled on production sites. See [Troubleshooting](https://ecaguide.org/eca/troubleshooting/index.md) for instructions on disabling it via Drush if the UI is not accessible.
### Debug settings
The following settings are available at **Administration > Configuration > Workflow > ECA > Settings** or via `drush state:set`:
Debug data depth : Maximum recursion depth for normalizing token data (default: `5`, minimum: `2`). Higher values expose more detail in nested data structures but increase processing time.
Debug data cases : Maximum number of history cases stored per event (default: `10`, minimum: `1`). Each case captures one complete processing run. Older cases are discarded when the limit is reached.
Debug test timeout : Timeout in seconds for test-triggered debug mode (default: `300`, i.e. 5 minutes). When the test button auto-enables debug mode and the test is not completed or canceled within this duration, debug mode is automatically disabled. Set to `0` to disable the timeout.
### Using the test button
1. Open a model in the Workflow Modeler.
1. Click the **Test** button on an event node.
1. Trigger the event (for example, save a node or submit a form).
1. The modeler polls for results and displays the processing history, including all tokens and their values at each step.
If you close the browser tab before the test completes, the timeout (default: 5 minutes) ensures debug mode does not stay on indefinitely.
### Disabling debug mode from the command line
If debug mode is stuck or the UI is not accessible:
```
drush state:set _eca_internal_debug_mode 0
```
See the [Troubleshooting](https://ecaguide.org/eca/troubleshooting/index.md) page for the full list of debug-related state keys.
## Database logging
This method works with all ECA versions and does not require a specific modeler.
**Requirements:**
- Enable the **Database Logging** (`dblog`) module.
- Go to **Administration > Configuration > Workflow > ECA > Settings** (`/admin/config/workflow/eca/settings`) and set the log level to **Debug**.
ECA writes detailed messages about every processing step to the database log. Review them at the dedicated ECA log page:
**Administration > Configuration > Workflow > ECA > Log** (`/admin/config/workflow/eca/log`)
Messages appear in chronological order. Each message describes the processing step and lists the tokens (name and value) available at that point.
Tokens are the key
Most unexpected behavior in ECA models comes from a missing token or a token present under a different name. The token list in each log message tells you exactly what is available at every step.
This approach is straightforward to enable and works well for simple models. For complex setups with many concurrent models, the volume of log messages can make it harder to isolate specific processing runs.
Reset the log level
After debugging, set the ECA log level back to a less verbose setting (for example, **Warning** or **Error**). Leaving it at **Debug** generates a large number of log messages that can affect site performance.
## Webprofiler
This method provides per-request isolation of ECA log messages, making it easier to debug specific page requests.
**Requirements:**
- For ECA 1:
- Install the Webprofiler module: `composer require drupal/webprofiler`
- Enable the **Webprofiler** module.
- For ECA 2 and later:
- Install the ECA Webprofiler module: `composer require drupal/eca_webprofiler`
- Enable the **ECA Webprofiler** module and its dependencies.
- Go to **Administration > Configuration > Development > Devel > Webprofiler** (`/admin/config/development/devel/webprofiler`) and enable the ECA toolbar item.
The database logging module is not required when using Webprofiler. There is no need to change the ECA log level either. As soon as ECA is enabled as a toolbar item in the Webprofiler settings, debugging data is collected automatically.
The Webprofiler toolbar appears at the bottom of the screen with information about the latest page request:
Click the ECA icon to open a page with the ECA log messages for that single request:
The messages are the same as in database logging, but isolated per request. This makes it much easier to trace ECA processing on pages where multiple models or events are involved.
# Troubleshooting
## The site is not working anymore
It may happen, that an ECA model contains instructions, that may break your site. As ECA tries catching all the known risks automatically, this shouldn't happen really, but you never know. However, should you find yourself in such a situation, it's not too easy to get yourself out of it again. The UI may not be accessible any longer and disabling a model with e.g. drush won't work either then, because that bootstraps the Drupal site as well before it could change the config.
Only for situations like that, ECA provides an easy kill-switch:
```
$settings['eca_disable'] = TRUE;
```
Put this into your `settings.php` somewhere and ECA will be completely disabled. Users with the permission `administer eca` will be notified with a message on the screen, to make them aware that ECA is actually not working.
In this disabled mode you can fix the model, that's causing the issue by either editing that model or you can disable or delete that model. When done, remove that line from your settings again and ECA will continue working right away.
## Debug mode causes slow performance
ECA's debug mode collects detailed processing data on every request. If debug mode is left enabled unintentionally, it can cause significant performance degradation -- particularly on sites with many entity operations or JSON:API requests.
A common scenario: you click the **Test** button in the Workflow Modeler, which temporarily enables debug mode. If you close the browser tab or lose connectivity before the test completes, debug mode stays on indefinitely. Starting with ECA 3.1, a configurable timeout (default: 5 minutes) automatically disables test-triggered debug mode, but manually enabled debug mode has no timeout.
### Check if debug mode is active
```
drush state:get _eca_internal_debug_mode
```
A return value of `1` means debug mode is active.
### Disable debug mode
```
drush state:set _eca_internal_debug_mode 0
```
### All debug-related state keys
You can view and change all debug settings with `drush state:get` and `drush state:set`:
| State key | Default | Description |
| ---------------------------------- | --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `_eca_internal_debug_mode` | `0` (off) | Master toggle for debug mode. Set to `1` to enable, `0` to disable. |
| `_eca_internal_debug_data_depth` | `5` | Maximum recursion depth for normalizing token data. Higher values provide more detail but increase processing time. Minimum: `2`. |
| `_eca_internal_debug_data_cases` | `10` | Maximum number of history cases stored per event. Each case captures one complete processing run. Minimum: `1`. |
| `_eca_internal_debug_test_timeout` | `300` | Timeout in seconds for test-triggered debug mode. Set to `0` to disable the timeout. |
| `_eca_internal_debug_test_started` | not set | Timestamp recorded when the test button auto-enables debug mode. Delete this value to cancel a stuck test timeout: `drush state:delete _eca_internal_debug_test_started`. |
These settings are also available in the UI at **Administration > Configuration > Workflow > ECA > Settings** (`/admin/config/workflow/eca/settings`).
## Can't save ECA model due to a missing plugin
When saving a model, ECA is validating the config entity to make sure that no obviously wrong configuration gets stored. This will refuse to save, e.g. if a model is missing a value for a required field or if it contains a plugin for either an event, a condition or an action, that no longer exists.
A missing plugin happens rarely. It could actually happen, if a module which provides one or more plugins gets updated and no longer delivers all the previous plugins any longer. Another scenario could be with dynamically created plugins that get derived from some other config values. In such cases, plugins could either disappear or simply get renamed, which is the equivalence to a plugin, that no longer exists, i.e. not under the original name.
In such a situation, it won't be possible to save the ECA model any longer, not even after removing or replacing the object in the model. This is caused by the way Drupal core's configuration validation is being implemented where the original config entity gets validated once before updating it as well.
To circumvent this issue, you can manually add a query argument to the URL in the browser to temporarily turn off the model validation when saving the updated version once. So, if the URL to edit your model looks like this:
```
https://www.example.com/admin/config/workflow/eca/process_someid/edit
```
You should add the query argument `eca_validation=off` to it:
```
https://www.example.com/admin/config/workflow/eca/process_someid/edit?eca_validation=off
```
Then you can make your corrections to the model and save it. From then on, you no longer need that query argument anymore.
## Workflow transition sometimes not recognized
When using the workflows module from Drupal core together with content moderation, then the event [Workflow: state transition](https://ecaguide.org/plugins/eca/workflow/events/workflow_transition/index.md) may not recognize the changed state, because the pre-save method in Drupal core may load the wrong revision under certain circumstances.
There is a fix in the works for Drupal 11, but it's not yet committed and is unlikely to be back ported to Drupal 10 or even 9.
As a workaround, Drupal core can be patched with [this file](https://git.drupalcode.org/project/drupal/-/merge_requests/3599.diff) and which is discussed and provided by [this Drupal issue](https://www.drupal.org/project/drupal/issues/3346430).
## Using non-stable releases
If you want to use non-stable releases (e.g. RC, beta or dev), then you should configure your root project to allow that by configuring the minimum acceptable stability with e.g. `composer config minimum-stability beta` for accepting beta releases. Without this, Composer may run into dependency issues.
# Frequently Asked Questions
This page collects answers to common questions raised in the [ECA support issue queue](https://www.drupal.org/project/issues/eca?categories=4). Each entry links back to the original discussion for full context.
______________________________________________________________________
## Tokens and field names
### How do I reference multi-value field deltas without conflicting with token replacement?
ECA's token system uses square brackets (`[` and `]`) as delimiters. When you need to target a specific delta in a multi-value field, the traditional Drupal bracket notation (e.g. `field_skills[0][target_id]`) clashes with token replacement.
**Use dot notation instead:**
```
field_skills.0.target_id
```
To use a dynamic delta from a token (e.g. inside a loop), write:
```
field_skills.[my_delta_token].target_id
```
This avoids the token system misinterpreting your brackets as token references.
Tip
Dot notation for field deltas is supported in ECA 2.1.x and 3.x.
[Original discussion (d.o #3565516)](https://www.drupal.org/project/eca/issues/3565516)
______________________________________________________________________
### How do I use literal square brackets in ECA field values?
If you need literal square brackets in a value — for example Tailwind CSS utility classes like `[&_.cf-form-label]:tw-hidden` or render array keys — ECA will attempt to replace anything inside `[...]` as a token.
**Workaround:** Define two global tokens that output literal brackets:
1. Use the **"ECA token generate"** custom event so the tokens are available site-wide.
1. In that event's model, use **"Token: set value"** to create:
- Token name: `bracket:open` with value `[`
- Token name: `bracket:close` with value `]`
1. Then use `[bracket:open]` and `[bracket:close]` wherever you need literal brackets.
Since token replacement is not recursive by default, the brackets will be output literally after the first replacement pass.
[Original discussion (d.o #3468841)](https://www.drupal.org/project/eca/issues/3468841)
______________________________________________________________________
### What does the `:value` suffix do on tokens?
When you use a token like `[entity:nid]`, ECA may return a typed data object rather than a plain scalar value. If you need the raw value (e.g. to pass an entity ID as a string to a queue worker or another action), append `:value`:
```
[entity:nid:value]
```
This is particularly important when forwarding tokens to queue workers or comparing values.
[Original discussion (d.o #3545797)](https://www.drupal.org/project/eca/issues/3545797)
______________________________________________________________________
## Entities
### Can I use "Entity: delete" with Paragraphs and other entity types?
Yes. The **Entity: delete** action works with *any* entity type, not just nodes. This includes Paragraphs, Media, Taxonomy Terms, Users, and custom entity types.
Warning
When triggering entity operations from Drush or cron, the current user is anonymous. Ensure the anonymous role has the necessary permissions, or switch the user context within your ECA model.
[Original discussion (d.o #3562153)](https://www.drupal.org/project/eca/issues/3562153)
______________________________________________________________________
### How do I load a User entity by username or email?
When using **Entity: Load** with properties, the property values field expects **YAML object syntax with curly braces**:
```
{name: "testuser"}
```
or:
```
{mail: "user@example.com"}
```
Writing `name: "testuser"` without the braces will not be parsed correctly.
[Original discussion (d.o #3556182)](https://www.drupal.org/project/eca/issues/3556182)
______________________________________________________________________
### What does "Defined by token" mean in the "Entity: Load" action?
The **"Load entity from"** dropdown in the "Entity: Load" action has a "Defined by token" option. This does *not* mean you provide the entity ID via a token. Instead, it means the **loading mode itself** is determined dynamically by the `eca_token_load_entity_from` token.
To load an entity by type and ID, select **"Entity type and ID"** in the dropdown and provide the entity ID in the "Entity ID" field (which supports tokens).
[Original discussion (d.o #3550157)](https://www.drupal.org/project/eca/issues/3550157)
______________________________________________________________________
### How do I clone a node and modify a field before saving?
Use this three-step pattern:
1. **Entity: clone** — creates an unsaved copy of the entity.
1. **Entity: set field value** — modify the field(s) you need to change on the cloned entity.
1. **Entity: save** — persist the modified clone.
This pattern works well with Views Bulk Operations (VBO): select entities in a view, trigger a VBO action, and let ECA handle the clone-modify-save workflow.
[Original discussion (d.o #3540136)](https://www.drupal.org/project/eca/issues/3540136)
______________________________________________________________________
### Why do I get "Entity: set field value" vs "Entity: get field value" confused?
A common mistake when creating a new entity and setting its properties:
- **"Entity: set field value"** — *writes* a value to a field. Use this to assign a title, body, or any other field value.
- **"Entity: get field value"** — *reads* a value from a field into a token.
When targeting the entity, use the **token name** you assigned when creating it (e.g. `newpromotion`), not the entity type machine name.
[Original discussion (d.o #3558803)](https://www.drupal.org/project/eca/issues/3558803)
______________________________________________________________________
## Conditions
### How do I check for multiple user roles?
The **"User has role(s)"** condition allows selecting only a single role. This is by design.
To check for multiple roles (e.g. Manager OR Employee OR Admin), use **multiple conditions connected with gateways**:
- For **OR logic**: connect multiple "User has role" conditions to separate branches from an exclusive gateway, each leading to the same action.
- For **AND logic**: chain conditions as successors so all must pass before the action executes.
Tip
See the [Combined Conditions](https://ecaguide.org/library/simple/combined_conditions/index.md) library model for a working example of AND/OR condition patterns.
[Original discussion (d.o #3558514)](https://www.drupal.org/project/eca/issues/3558514)
______________________________________________________________________
### How do I check if a value is contained in a list of values?
If you need to check whether the current node's ID is in a specific list of IDs (similar to an "IN" operator), use the **"Compare two scalar values"** condition with the `contains` operator:
- **Value 1:** `,26122,26119,26567,` (the list, wrapped with leading and trailing commas)
- **Value 2:** `,[node:id],` (the value to find, also wrapped in commas)
- **Operator:** `contains`
The leading and trailing commas prevent false positives (e.g. ID `261` matching inside `26122`).
Note
The `eca_list_contains` condition is designed for token-based lists, not inline comma-separated values. For hardcoded lists, the scalar comparison approach above is more reliable.
[Original discussion (d.o #3556083)](https://www.drupal.org/project/eca/issues/3556083)
______________________________________________________________________
## Forms
### How do I populate a multi-value entity reference field?
The approach depends on the widget type and timing:
**On form build** (before the user sees the form): : Use **"Form field: set default value"** with just the entity ID.
**After an Ajax event** (e.g. after user interaction): : Use **"Form field: set value"** with the format `Entity Name (ID)` for autocomplete widgets.
**For multiple values**, use a loop with a delta counter and dot notation:
```
field_entity_reference.0.target_id
field_entity_reference.1.target_id
```
Tip
**Tags-style**, **Select**, and **Checkboxes** widgets accept multiple values more easily than the single-value **Autocomplete** widget. Consider switching the widget type if multi-value population is a core requirement.
[Original discussion (d.o #3565511)](https://www.drupal.org/project/eca/issues/3565511)
______________________________________________________________________
### How do I distinguish "Add more" Ajax clicks from real form submissions?
The form validation event fires for every form submission, including when a user clicks "Add another item" on a multi-value field. To distinguish real form saves from Ajax requests:
Check the **`[entity:nid]`** token immediately after the submission event. If the entity has not been saved yet (because it is an Ajax request rather than a real save), the NID will not be available. Use this to skip validation logic during "Add more" clicks.
[Original discussion (d.o #3564434)](https://www.drupal.org/project/eca/issues/3564434)
______________________________________________________________________
### How do I add a custom submit button and handle its click?
Adding a custom button (e.g. "Cancel") requires two separate events:
1. **"Build form" event** — use the **"Form: Add Submit Button"** action to add the button to the form.
1. **"Form submit" event** — use the **"Form: Compare Triggered Submission"** condition to detect which button was clicked.
Warning
Use the **"Build form"** event, not "After Build form", to add the button. The "After Build" event fires too late for button additions.
[Original discussion (d.o #3539759)](https://www.drupal.org/project/eca/issues/3539759)
______________________________________________________________________
## Events and redirects
### Why does my redirect not work with the "Response created" event?
The **"Response created"** (`kernel.response`) event fires after Drupal has already started building the response. At that point, it is too late to replace the response with a redirect.
Use the **"Controller found to handle request"** (`kernel.controller`) event instead. This event fires early enough for the **"Redirect to URL"** action to take effect.
Note
The `kernel.response` event still works for modifying response headers or for handling 403/404 error pages, because in those cases the response is being built differently.
[Original discussion (d.o #3566396)](https://www.drupal.org/project/eca/issues/3566396)
______________________________________________________________________
### Can I prevent entity deletion from a pre-delete event?
No. By the time the **pre-delete** event fires, Drupal is already committed to the delete operation. Only throwing an exception would stop it, which is not a clean approach.
**Alternative:** Use access control to conditionally hide the delete link. For example, use the **"ECA Views Field"** action with the **"Views field"** event: evaluate each entity row in a View to determine whether deletion should be allowed, and show or hide the delete link accordingly.
[Original discussion (d.o #3537710)](https://www.drupal.org/project/eca/issues/3537710)
______________________________________________________________________
### How do I use "Views: Set filter value" with "Views: Execute query"?
These two actions cannot be chained sequentially. ECA is **event-driven**: when you execute a View via the **"Views: Execute query"** action, Drupal dispatches the **"Views: Pre Build"** event for that View.
Create a separate event listener for the **"Views: Pre Build"** event and use **"Views: Set filter value"** there. This way, the filter is applied automatically whenever the View is built — whether triggered by your "Execute query" action or by any other mechanism.
Tip
Use the `filter_id` value matching the machine name of the exposed filter field in your View configuration.
[Original discussion (d.o #3488727)](https://www.drupal.org/project/eca/issues/3488727)
______________________________________________________________________
## Queue tasks
### How do I pass data to a queue worker?
When using the **"Enqueue task"** action, do not use the `task_value` field to pass entity IDs or other data. Instead:
1. Use the **"Tokens to forward"** field — enter the token *name* (without brackets), e.g. `prelim_nid`.
1. That token will be available in the queue worker model under the same name.
1. For entity field values, use the `:value` suffix to get the raw scalar value: `[entity:nid:value]` rather than `[entity:nid]`.
Warning
Queue workers run as the **anonymous user**. Ensure the anonymous role has the necessary permissions to load and manipulate the entities your worker needs to access.
[Original discussion (d.o #3545797)](https://www.drupal.org/project/eca/issues/3545797)
______________________________________________________________________
## Workflow and content moderation
### Why does the workflow transition event not fire on re-publishing?
When a content-moderated entity is published, moved to draft, and then published again, the **"Workflow: state transition"** event may not recognize the second transition. This is a [known Drupal core bug](https://www.drupal.org/project/drupal/issues/2839195) where `$entity->original` loads the latest *default* revision instead of the latest *revision*, making the from/to states appear identical.
**Workaround:** Apply the patch from [Drupal core issue #3346430](https://www.drupal.org/project/drupal/issues/3346430) or the community-provided ECA patch from the discussion linked below. This issue is also documented in the [Troubleshooting](https://ecaguide.org/eca/troubleshooting/index.md) page.
[Original discussion (d.o #3417834)](https://www.drupal.org/project/eca/issues/3417834)
______________________________________________________________________
## Upgrading
### How do I upgrade from ECA 2.x to 3.x?
When upgrading ECA and its modeler modules (e.g. bpmn_io) to version 3.x, you must follow the [ECA 3.0.0 release notes](https://www.drupal.org/project/eca/releases/3.0.0) upgrade procedure. ECA 3 includes update scripts that enable the required new modules.
Danger
Do not upgrade bpmn_io to 3.x without also upgrading ECA to 3.x. The modeler depends on services that moved in the ECA 3.x architecture (e.g. `modeler_api.service`), and upgrading one without the other will cause fatal errors.
[Original discussion (d.o #3571323)](https://www.drupal.org/project/bpmn_io/issues/3571323)
# Modeler
# ECA Modeler
ECA allows you to use different modelers to create and manage your workflows. These modelers are plugins that implement the [Modeler API](https://www.drupal.org/project/modeler_api).
## Workflow Modeler (Recommended)
The [Workflow Modeler](https://ecaguide.org/modeler/workflow/index.md) is the recommended visual editor for ECA. It provides a modern, React-based interface with features like execution replay, live testing, and an improved user experience.
## Classic Modeler
The [Classic Modeler](https://ecaguide.org/modeler/eca_cm/index.md) is the original modeler built into ECA. It allows editing models directly within the Drupal administrative interface using standard Drupal forms.
## BPMN Modelers
ECA also supports BPMN-based modelers:
- [BPMN.iO](https://ecaguide.org/modeler/bpmn/bpmn_io/index.md): A popular web-based BPMN modeler.
- [Camunda](https://ecaguide.org/modeler/bpmn/camunda/index.md): The Camunda desktop modeler (Deprecated).
# Workflow Modeler (Recommended)
The [Workflow Modeler](https://www.drupal.org/project/modeler) provides a modern, React-based visual workflow editor for Drupal. Built on React Flow, it offers a professional drag-and-drop interface for creating and editing workflow models — complete with real-time configuration, execution replay, live testing, and full keyboard accessibility.
This modeler is the recommended editor for ECA and is the successor to the BPMN.iO modeler.
## Key Features
- **Visual Workflow Editing**: Drag-and-drop nodes onto an infinite canvas, connect them with edges, and configure components through dynamic forms.
- **Quick-Add**: Context-aware popup for adding successors (actions, gateways, conditions) directly from nodes or edges.
- **Execution Replay**: Load past execution data, step through it visually, and inspect token values.
- **Live Testing**: Trigger workflow execution directly from the modeler.
- **Export**: Export models as Drupal Recipes, archives, JSON, or SVG.
- **Accessibility**: WCAG AA compliant with full keyboard navigation and screen reader support.
- **Dark Mode**: Built-in support for light and dark themes.
## Installation
Install the module using Composer:
```
composer require drupal/modeler
drush en modeler
```
If you are using ECA, ensure you have the `eca_ui` module enabled as well:
```
composer require drupal/eca
drush en eca eca_ui
```
## Usage
Once installed, the Workflow Modeler activates automatically when editing models:
1. Navigate to the ECA configuration page (**Configuration > Workflow > ECA**).
1. Click **Add** to create a new model, or **Edit** on an existing model.
1. The modeler interface will open, allowing you to design your workflow.
For detailed documentation, visit the [official Workflow Modeler documentation](https://project.pages.drupalcode.org/modeler/).
## Screenshots
# BPMN Modelers
ECA supports the BPMN (Business Process Model and Notation) standard for defining workflows. While the [Workflow Modeler](https://ecaguide.org/modeler/workflow/index.md) is the recommended editor, you can also use other BPMN-compatible tools.
# BPMN Modeler FAQ
This page collects answers to common questions raised in the [BPMN.iO support issue queue](https://www.drupal.org/project/issues/bpmn_io?categories=4). Each entry links back to the original discussion for full context.
______________________________________________________________________
## How do I upgrade from BPMN.iO 2.x to 3.x?
BPMN.iO 3.x depends on ECA 3.x. Upgrading the modeler without upgrading ECA will cause a fatal error:
```
"bpmn_io.prepare_components" has a dependency on a non-existent service "modeler_api.service"
```
**Always upgrade ECA and BPMN.iO together** and follow the [ECA 3.0.0 release notes](https://www.drupal.org/project/eca/releases/3.0.0) upgrade procedure. ECA 3 includes update scripts that enable the required new modules.
[Original discussion (d.o #3571323)](https://www.drupal.org/project/bpmn_io/issues/3571323)
______________________________________________________________________
## The status message overlaps the modeler UI in Claro
When using the Claro admin theme, the "Model saved" status message may appear in the centre of the screen, cannot be dismissed, and never disappears. This is caused by a CSS selector mismatch: the bpmn_io JavaScript that makes messages dismissible uses a selector that does not match Claro's message markup.
**Solution:** Switch to the **Gin** admin theme, where message handling is already optimized. Claro is being deprecated in Drupal core in favor of Gin.
[Original discussion (d.o #3544342)](https://www.drupal.org/project/bpmn_io/issues/3544342)
# Support field types
- textfield
- textarea
- checkbox
- checkboxes
- radios
- select
# BPMN.iO
BPMN.iO is a modeler for ECA and is fully integrated into Drupal's administration user interface. It provides a graphical user interface that allows to define models using the BPMN ([Business Process Model and Notation](https://en.wikipedia.org/wiki/Business_Process_Model_and_Notation) standard.
[Try BPMN.iO online](https://demo.bpmn.io/)
## Links
- [Examples](https://github.com/bpmn-io/bpmn-js-examples)
- [Awesome List](https://github.com/bpmn-io/awesome-bpmn-io)
- [Viewer html](https://github.com/bpmn-io/bpmn-js-examples/blob/master/url-viewer/index.html)
- [Viewer with simulation](https://bpmn-io.github.io/bpmn-js-token-simulation/viewer.html)
## Used components
| Library | Version |
| ----------------------------------------------------------------------------------------------------------------------- | ------- |
| [@bpmn-io/a11y](https://github.com/bpmn-io/a11y/blob/main/CHANGELOG.md) | 0.1.0 |
| [@bpmn-io/element-template-chooser](https://github.com/bpmn-io/element-template-chooser/blob/master/CHANGELOG.md) | 1.1.1 |
| [@bpmn-io/element-templates-validator](https://github.com/bpmn-io/element-templates-validator/blob/master/CHANGELOG.md) | 2.23.0 |
| [@bpmn-io/properties-panel](https://github.com/bpmn-io/properties-panel/blob/master/CHANGELOG.md) | 3.47.0 |
| [@camunda/element-templates-json-schema](https://github.com/camunda/element-templates-json-schema) | 0.12.1 |
| [bpmn-auto-layout](https://github.com/bpmn-io/bpmn-auto-layout/blob/main/CHANGELOG.md) | 1.3.0 |
| [bpmn-js](https://github.com/bpmn-io/bpmn-js/blob/master/CHANGELOG.md) | 18.19.0 |
| [bpmn-js-color-picker](https://github.com/bpmn-io/bpmn-js-color-picker/blob/master/CHANGELOG.md) | 0.7.2 |
| [bpmn-js-element-templates](https://github.com/bpmn-io/bpmn-js-element-templates/blob/master/CHANGELOG.md) | 2.27.0 |
| [bpmn-js-properties-panel](https://github.com/bpmn-io/bpmn-js-properties-panel/blob/master/CHANGELOG.md) | 5.60.0 |
| [bpmn-moddle](https://github.com/bpmn-io/bpmn-moddle/blob/master/CHANGELOG.md) | 9.0.4 |
| [camunda-bpmn-moddle](https://github.com/camunda/camunda-bpmn-moddle/blob/master/CHANGELOG.md) | 7.0.1 |
| [diagram-js](https://github.com/bpmn-io/diagram-js/blob/master/CHANGELOG.md) | 15.18.1 |
| [diagram-js-minimap](https://github.com/bpmn-io/diagram-js-minimap/blob/master/CHANGELOG.md) | 5.3.0 |
| [jquery](https://github.com/jquery/jquery) | 3.7.1 |
| [min-dash](https://github.com/bpmn-io/min-dash) | 4.2.3 |
# Installation
To download the module with composer use the following command:
```
composer require drupal/bpmn_io
```
**Note:** if you want to use non-stable releases (e.g. RC, beta or dev), then you should configure your root project to allow that by configuring the minimum acceptable stability with e.g. `composer config minimum-stability beta` for accepting beta releases. Without this, composer may run into dependency issues.
Enable the module(s) in the admin ui of your site (`/admin/modules`) or with drush:
```
drush en bpmn_io
```
# Usage
## Add a new model
1. In the Manage administrative menu, navigate to Configuration > Workflow > ECA (/admin/config/workflow/eca). A page appears where existing models are listed.
1. Click on *Add new BPMN.iO model*. The BPMN.iO modeler appears.
1. On the right-hand side of the BPMN.iO modeler interface you will find a form to configure the currently selected business process element (click on arrows beside the label to show or hide the form input elements):
- **Name**: The name of your model.
- **ID**: generated automatically by default.
- **Version tag**: version of your model.
- **Executable**: uncheck to disable the model.
- **Documentation**:
- Click *Save*.
## Keyboard Shortcuts
When the focus is inside the BPMN canvas, there is support for the following shortcuts:
| Function | Shortcut |
| -------------- | ------------- |
| Undo | ctrl + Z |
| Redo | ctrl + Y |
| Select All | ctrl + A |
| Copy | ctrl + C |
| Paste | ctrl + V |
| Find | ctrl + F |
| Scrolling | ctrl + Arrows |
| Direct Editing | E |
| Hand Tool | H |
| Lasso Tool | L |
| Space Tool | S |
# Camunda Modeler
Deprecated
The Camunda Modeler integration is deprecated. We recommend using the [Workflow Modeler](https://ecaguide.org/modeler/workflow/index.md) which runs directly within Drupal and offers a superior, integrated experience.
### Camunda BPMN
Camunda BPMN provides a user interface that allows to export templates for events, conditions and actions from Drupal to the [Camunda Modeler](https://camunda.com/products/camunda-platform/modeler/) desktop application and to import BPMN files created in Camunda Modeller into Drupal.
[Camunda Modeller](https://camunda.com/products/camunda-platform/modeler/) is a desktop application for designing automated processes using the BPMN standard.
# Installation
To download the module with composer use the following command:
```
composer require drupal/camunda
```
**Note:** if you want to use non-stable releases (e.g. RC, beta or dev), then you should configure your root project to allow that by configuring the minimum acceptable stability with e.g. `composer config minimum-stability beta` for accepting beta releases. Without this, composer may run into dependency issues.
Enable the module(s) in the admin ui of your site (`/admin/modules`) or with drush:
```
drush en camunda
```
# Usage
TODO: export templates, import and update models, etc.
# Classic Modeler
The Classic Modeler is the original, basic modeler integrated into ECA. It allows for creating and editing models using standard Drupal form elements within the administrative interface.
While the new [Workflow Modeler](https://ecaguide.org/modeler/workflow/index.md) is recommended for its visual capabilities and advanced features, the Classic Modeler remains available for users who prefer a form-based approach or for environments where visual editing is not feasible.
[Project Page](https://www.drupal.org/project/eca_cm)
# Classic Modeler FAQ
This page collects answers to common questions raised in the [ECA Classic Modeler support issue queue](https://www.drupal.org/project/issues/eca_cm?categories=4). Each entry links back to the original discussion for full context.
______________________________________________________________________
## What are "Successors" and how do they work?
If you are coming from the Rules module, the concept of **successors** may be unfamiliar. In ECA, successors define the execution chain — they connect events, conditions and actions into a processing flow.
A successor is a link consisting of:
- One configured **action** (required)
- One configured **condition** (optional)
The event triggers first. Its successors are the actions that should execute next. Each action can in turn have its own successors, forming a chain. If a successor link includes a condition, the action only executes when that condition returns true.
```
Event → [Condition?] → Action 1 → [Condition?] → Action 2 → ...
```
Note
Conditions do not have their own successors. They are attached to the successor link between two elements (an event-to-action or action-to-action connection).
[Original discussion (d.o #3302674)](https://www.drupal.org/project/eca_cm/issues/3302674)
______________________________________________________________________
## How do I combine multiple conditions with AND logic?
To require that **all** conditions pass before an action executes, chain them as successive links. Each successor link can have one condition, so chaining multiple links effectively ANDs them:
```
Event → [Condition A] → Action (no-op) → [Condition B] → Action (no-op) → [Condition C] → Final Action
```
Alternatively, use the combined conditions pattern documented in the [library](https://ecaguide.org/library/simple/combined_conditions/index.md).
Tip
For **OR logic**, connect multiple branches from an exclusive gateway, each with its own condition, leading to the same target action.
[Original discussion (d.o #3320234)](https://www.drupal.org/project/eca_cm/issues/3320234)
# Plugins
# Plugins
Here we are going to describe and explain all known ECA plugins.
# AI Integration - ECA
## Installation
```
composer require drupal/ai_integration_eca
drush pm:install ai_integration_eca
```
Instead of using Drush to enable the module, you can also go to "Administration / Extend" (`/admin/modules`) and enable the module **AI Integration - ECA** from there.
# Chat
Run text through the AI chat model.
## Fields
`Model` : The applicable model for this operation type.
`Token input` : The data input for AI. Please provide the token name only, without brackets.
`Token result` : The response from AI will be stored into the token result field to be used in future steps. Please provide the token name only, without brackets.
`Specific configuration for the model` : Some models require specific configuration settings, like temperature, voice or response_format.\
The "profile" helps set the behavior of the LLM response. You can change/influence how it response by adjusting the system prompt. Eg.
````
```
system_name: system
system_prompt: you are a helpful assistant
```
````
`Prompt` : Enter your text here. When submitted, AI will generate a response from its Chats endpoint. Based on the complexity of your text, AI traffic, and other factors, a response can sometimes take up to 10-15 seconds to complete. Please allow the operation to finish. Be cautious not to exceed the requests per minute quota (20/Minute by default), or you may be temporarily blocked.
`Schema` : Provide an optional schema for the output.
# Embedding
Generate a text embedding from an input.
## Fields
`Model` : The applicable model for this operation type.
`Token input` : The data input for AI. Please provide the token name only, without brackets.
`Token result` : The response from AI will be stored into the token result field to be used in future steps. Please provide the token name only, without brackets.
`Specific configuration for the model` : Some models require specific configuration settings, like temperature, voice or response_format.
# Moderation
Determine if a piece of text violates any usage policies.
## Fields
`Model` : The applicable model for this operation type.
`Token input` : The data input for AI. Please provide the token name only, without brackets.
`Token result` : The response from AI will be stored into the token result field to be used in future steps. Please provide the token name only, without brackets.
# Speech to Text
Process an audio file through the AI speech-to-text endpoint.
## Fields
`Model` : The applicable model for this operation type.
`Token input` : The data input for AI. Please provide the token name only, without brackets.
`Token result` : The response from AI will be stored into the token result field to be used in future steps. Please provide the token name only, without brackets.
`Specific configuration for the model` : Some models require specific configuration settings, like temperature, voice or response_format.
# Text to Speech
Run text through the AI text-to-speech model.
## Fields
`Model` : The applicable model for this operation type.
`Token input` : The data input for AI. Please provide the token name only, without brackets.
`Token result` : The response from AI will be stored into the token result field to be used in future steps. Please provide the token name only, without brackets.
`Specific configuration for the model` : Some models require specific configuration settings, like temperature, voice or response_format.
# AI Integration - ECA: Agents
## Installation
```
composer require drupal/ai_integration_eca
drush pm:install ai_integration_eca_agents
```
Instead of using Drush to enable the module, you can also go to "Administration / Extend" (`/admin/modules`) and enable the module **AI Integration - ECA: Agents** from there.
# AI: Trigger Agent
Triggers an AI Agent.
## Fields
`AI Agent` : The AI Agent to trigger.\
When using the "Defined by token" option, make sure there is a token with this name: *eca_ai_trigger_agent_agent*
`Instructions` : The instructions, which should run.\
This field supports tokens.
# Augmentor
## Installation
```
composer require drupal/augmentor
drush pm:install augmentor
```
Instead of using Drush to enable the module, you can also go to "Administration / Extend" (`/admin/modules`) and enable the module **Augmentor** from there.
# Augmentor content block
Available since: unknown
## Fields
`Source fields`
`Advance targeting`
`Augmentor`
`Action over target field`
`Text Format`
`Explode separator` : Split augmentor response into an array using this separator.
`Entity` : Provide the token name of the *block_content* that this action should operate with. Please provide the token name only, without brackets.
`Replace tokens` : When enabled, tokens will be replaced *before* executing the action. **Please note:** Actions might already take care of replacing tokens on their own. Therefore, use this option only with care and when it makes sense.
# Augmentor comment
Available since: unknown
## Fields
`Source fields`
`Advance targeting`
`Augmentor`
`Action over target field`
`Text Format`
`Explode separator` : Split augmentor response into an array using this separator.
`Entity` : Provide the token name of the *comment* that this action should operate with. Please provide the token name only, without brackets.
`Replace tokens` : When enabled, tokens will be replaced *before* executing the action. **Please note:** Actions might already take care of replacing tokens on their own. Therefore, use this option only with care and when it makes sense.
# Augmentor order
Available since: unknown
## Fields
`Source fields`
`Advance targeting`
`Augmentor`
`Action over target field`
`Text Format`
`Explode separator` : Split augmentor response into an array using this separator.
`Entity` : Provide the token name of the *commerce_order* that this action should operate with. Please provide the token name only, without brackets.
`Replace tokens` : When enabled, tokens will be replaced *before* executing the action. **Please note:** Actions might already take care of replacing tokens on their own. Therefore, use this option only with care and when it makes sense.
# Augmentor order item
Available since: unknown
## Fields
`Source fields`
`Advance targeting`
`Augmentor`
`Action over target field`
`Text Format`
`Explode separator` : Split augmentor response into an array using this separator.
`Entity` : Provide the token name of the *commerce_order_item* that this action should operate with. Please provide the token name only, without brackets.
`Replace tokens` : When enabled, tokens will be replaced *before* executing the action. **Please note:** Actions might already take care of replacing tokens on their own. Therefore, use this option only with care and when it makes sense.
# Augmentor product
Available since: unknown
## Fields
`Source fields`
`Advance targeting`
`Augmentor`
`Action over target field`
`Text Format`
`Explode separator` : Split augmentor response into an array using this separator.
`Entity` : Provide the token name of the *commerce_product* that this action should operate with. Please provide the token name only, without brackets.
`Replace tokens` : When enabled, tokens will be replaced *before* executing the action. **Please note:** Actions might already take care of replacing tokens on their own. Therefore, use this option only with care and when it makes sense.
# Augmentor product attribute value
Available since: unknown
## Fields
`Source fields`
`Advance targeting`
`Augmentor`
`Action over target field`
`Text Format`
`Explode separator` : Split augmentor response into an array using this separator.
`Entity` : Provide the token name of the *commerce_product_attribute_value* that this action should operate with. Please provide the token name only, without brackets.
`Replace tokens` : When enabled, tokens will be replaced *before* executing the action. **Please note:** Actions might already take care of replacing tokens on their own. Therefore, use this option only with care and when it makes sense.
# Augmentor product variation
Available since: unknown
## Fields
`Source fields`
`Advance targeting`
`Augmentor`
`Action over target field`
`Text Format`
`Explode separator` : Split augmentor response into an array using this separator.
`Entity` : Provide the token name of the *commerce_product_variation* that this action should operate with. Please provide the token name only, without brackets.
`Replace tokens` : When enabled, tokens will be replaced *before* executing the action. **Please note:** Actions might already take care of replacing tokens on their own. Therefore, use this option only with care and when it makes sense.
# Augmentor store
Available since: unknown
## Fields
`Source fields`
`Advance targeting`
`Augmentor`
`Action over target field`
`Text Format`
`Explode separator` : Split augmentor response into an array using this separator.
`Entity` : Provide the token name of the *commerce_store* that this action should operate with. Please provide the token name only, without brackets.
`Replace tokens` : When enabled, tokens will be replaced *before* executing the action. **Please note:** Actions might already take care of replacing tokens on their own. Therefore, use this option only with care and when it makes sense.
# Augmentor contact message
Available since: unknown
## Fields
`Source fields`
`Advance targeting`
`Augmentor`
`Action over target field`
`Text Format`
`Explode separator` : Split augmentor response into an array using this separator.
`Entity` : Provide the token name of the *contact_message* that this action should operate with. Please provide the token name only, without brackets.
`Replace tokens` : When enabled, tokens will be replaced *before* executing the action. **Please note:** Actions might already take care of replacing tokens on their own. Therefore, use this option only with care and when it makes sense.
# Augmentor email
Available since: unknown
## Fields
`Source fields`
`Advance targeting`
`Augmentor`
`Action over target field`
`Text Format`
`Explode separator` : Split augmentor response into an array using this separator.
`Entity` : Provide the token name of the *easy_email* that this action should operate with. Please provide the token name only, without brackets.
`Replace tokens` : When enabled, tokens will be replaced *before* executing the action. **Please note:** Actions might already take care of replacing tokens on their own. Therefore, use this option only with care and when it makes sense.
# Augmentor flagging
Available since: unknown
## Fields
`Source fields`
`Advance targeting`
`Augmentor`
`Action over target field`
`Text Format`
`Explode separator` : Split augmentor response into an array using this separator.
`Entity` : Provide the token name of the *flagging* that this action should operate with. Please provide the token name only, without brackets.
`Replace tokens` : When enabled, tokens will be replaced *before* executing the action. **Please note:** Actions might already take care of replacing tokens on their own. Therefore, use this option only with care and when it makes sense.
# Augmentor group
Available since: unknown
## Fields
`Source fields`
`Advance targeting`
`Augmentor`
`Action over target field`
`Text Format`
`Explode separator` : Split augmentor response into an array using this separator.
`Entity` : Provide the token name of the *group* that this action should operate with. Please provide the token name only, without brackets.
`Replace tokens` : When enabled, tokens will be replaced *before* executing the action. **Please note:** Actions might already take care of replacing tokens on their own. Therefore, use this option only with care and when it makes sense.
# Augmentor group relationship
Available since: unknown
## Fields
`Source fields`
`Advance targeting`
`Augmentor`
`Action over target field`
`Text Format`
`Explode separator` : Split augmentor response into an array using this separator.
`Entity` : Provide the token name of the *group_relationship* that this action should operate with. Please provide the token name only, without brackets.
`Replace tokens` : When enabled, tokens will be replaced *before* executing the action. **Please note:** Actions might already take care of replacing tokens on their own. Therefore, use this option only with care and when it makes sense.
# Augmentor media item
Available since: unknown
## Fields
`Source fields`
`Advance targeting`
`Augmentor`
`Action over target field`
`Text Format`
`Explode separator` : Split augmentor response into an array using this separator.
`Entity` : Provide the token name of the *media* that this action should operate with. Please provide the token name only, without brackets.
`Replace tokens` : When enabled, tokens will be replaced *before* executing the action. **Please note:** Actions might already take care of replacing tokens on their own. Therefore, use this option only with care and when it makes sense.
# Augmentor Minimal content block
Available since: unknown
## Fields
`Source Field 1`
`Source Field 2`
`Source Field 3`
`Source Field 4`
`Source Field 5`
`Source Field 6`
`Source Field 7`
`Source Field 8`
`Source Field 9`
`Source Field 10`
`Target Field 1`
`Response key to use 1`
`Target Field 2`
`Response key to use 2`
`Target Field 3`
`Response key to use 3`
`Target Field 4`
`Response key to use 4`
`Target Field 5`
`Response key to use 5`
`Target Field 6`
`Response key to use 6`
`Target Field 7`
`Response key to use 7`
`Target Field 8`
`Response key to use 8`
`Target Field 9`
`Response key to use 9`
`Target Field 10`
`Response key to use 10`
`Augmentor`
`Action over target field`
`Text Format`
`Explode separator` : Split augmentor response into an array using this separator.
`Entity` : Provide the token name of the *block_content* that this action should operate with. Please provide the token name only, without brackets.
`Replace tokens` : When enabled, tokens will be replaced *before* executing the action. **Please note:** Actions might already take care of replacing tokens on their own. Therefore, use this option only with care and when it makes sense.
# Augmentor Minimal comment
Available since: unknown
## Fields
`Source Field 1`
`Source Field 2`
`Source Field 3`
`Source Field 4`
`Source Field 5`
`Source Field 6`
`Source Field 7`
`Source Field 8`
`Source Field 9`
`Source Field 10`
`Target Field 1`
`Response key to use 1`
`Target Field 2`
`Response key to use 2`
`Target Field 3`
`Response key to use 3`
`Target Field 4`
`Response key to use 4`
`Target Field 5`
`Response key to use 5`
`Target Field 6`
`Response key to use 6`
`Target Field 7`
`Response key to use 7`
`Target Field 8`
`Response key to use 8`
`Target Field 9`
`Response key to use 9`
`Target Field 10`
`Response key to use 10`
`Augmentor`
`Action over target field`
`Text Format`
`Explode separator` : Split augmentor response into an array using this separator.
`Entity` : Provide the token name of the *comment* that this action should operate with. Please provide the token name only, without brackets.
`Replace tokens` : When enabled, tokens will be replaced *before* executing the action. **Please note:** Actions might already take care of replacing tokens on their own. Therefore, use this option only with care and when it makes sense.
# Augmentor Minimal order
Available since: unknown
## Fields
`Source Field 1`
`Source Field 2`
`Source Field 3`
`Source Field 4`
`Source Field 5`
`Source Field 6`
`Source Field 7`
`Source Field 8`
`Source Field 9`
`Source Field 10`
`Target Field 1`
`Response key to use 1`
`Target Field 2`
`Response key to use 2`
`Target Field 3`
`Response key to use 3`
`Target Field 4`
`Response key to use 4`
`Target Field 5`
`Response key to use 5`
`Target Field 6`
`Response key to use 6`
`Target Field 7`
`Response key to use 7`
`Target Field 8`
`Response key to use 8`
`Target Field 9`
`Response key to use 9`
`Target Field 10`
`Response key to use 10`
`Augmentor`
`Action over target field`
`Text Format`
`Explode separator` : Split augmentor response into an array using this separator.
`Entity` : Provide the token name of the *commerce_order* that this action should operate with. Please provide the token name only, without brackets.
`Replace tokens` : When enabled, tokens will be replaced *before* executing the action. **Please note:** Actions might already take care of replacing tokens on their own. Therefore, use this option only with care and when it makes sense.
# Augmentor Minimal order item
Available since: unknown
## Fields
`Source Field 1`
`Source Field 2`
`Source Field 3`
`Source Field 4`
`Source Field 5`
`Source Field 6`
`Source Field 7`
`Source Field 8`
`Source Field 9`
`Source Field 10`
`Target Field 1`
`Response key to use 1`
`Target Field 2`
`Response key to use 2`
`Target Field 3`
`Response key to use 3`
`Target Field 4`
`Response key to use 4`
`Target Field 5`
`Response key to use 5`
`Target Field 6`
`Response key to use 6`
`Target Field 7`
`Response key to use 7`
`Target Field 8`
`Response key to use 8`
`Target Field 9`
`Response key to use 9`
`Target Field 10`
`Response key to use 10`
`Augmentor`
`Action over target field`
`Text Format`
`Explode separator` : Split augmentor response into an array using this separator.
`Entity` : Provide the token name of the *commerce_order_item* that this action should operate with. Please provide the token name only, without brackets.
`Replace tokens` : When enabled, tokens will be replaced *before* executing the action. **Please note:** Actions might already take care of replacing tokens on their own. Therefore, use this option only with care and when it makes sense.
# Augmentor Minimal product
Available since: unknown
## Fields
`Source Field 1`
`Source Field 2`
`Source Field 3`
`Source Field 4`
`Source Field 5`
`Source Field 6`
`Source Field 7`
`Source Field 8`
`Source Field 9`
`Source Field 10`
`Target Field 1`
`Response key to use 1`
`Target Field 2`
`Response key to use 2`
`Target Field 3`
`Response key to use 3`
`Target Field 4`
`Response key to use 4`
`Target Field 5`
`Response key to use 5`
`Target Field 6`
`Response key to use 6`
`Target Field 7`
`Response key to use 7`
`Target Field 8`
`Response key to use 8`
`Target Field 9`
`Response key to use 9`
`Target Field 10`
`Response key to use 10`
`Augmentor`
`Action over target field`
`Text Format`
`Explode separator` : Split augmentor response into an array using this separator.
`Entity` : Provide the token name of the *commerce_product* that this action should operate with. Please provide the token name only, without brackets.
`Replace tokens` : When enabled, tokens will be replaced *before* executing the action. **Please note:** Actions might already take care of replacing tokens on their own. Therefore, use this option only with care and when it makes sense.
# Augmentor Minimal product attribute value
Available since: unknown
## Fields
`Source Field 1`
`Source Field 2`
`Source Field 3`
`Source Field 4`
`Source Field 5`
`Source Field 6`
`Source Field 7`
`Source Field 8`
`Source Field 9`
`Source Field 10`
`Target Field 1`
`Response key to use 1`
`Target Field 2`
`Response key to use 2`
`Target Field 3`
`Response key to use 3`
`Target Field 4`
`Response key to use 4`
`Target Field 5`
`Response key to use 5`
`Target Field 6`
`Response key to use 6`
`Target Field 7`
`Response key to use 7`
`Target Field 8`
`Response key to use 8`
`Target Field 9`
`Response key to use 9`
`Target Field 10`
`Response key to use 10`
`Augmentor`
`Action over target field`
`Text Format`
`Explode separator` : Split augmentor response into an array using this separator.
`Entity` : Provide the token name of the *commerce_product_attribute_value* that this action should operate with. Please provide the token name only, without brackets.
`Replace tokens` : When enabled, tokens will be replaced *before* executing the action. **Please note:** Actions might already take care of replacing tokens on their own. Therefore, use this option only with care and when it makes sense.
# Augmentor Minimal product variation
Available since: unknown
## Fields
`Source Field 1`
`Source Field 2`
`Source Field 3`
`Source Field 4`
`Source Field 5`
`Source Field 6`
`Source Field 7`
`Source Field 8`
`Source Field 9`
`Source Field 10`
`Target Field 1`
`Response key to use 1`
`Target Field 2`
`Response key to use 2`
`Target Field 3`
`Response key to use 3`
`Target Field 4`
`Response key to use 4`
`Target Field 5`
`Response key to use 5`
`Target Field 6`
`Response key to use 6`
`Target Field 7`
`Response key to use 7`
`Target Field 8`
`Response key to use 8`
`Target Field 9`
`Response key to use 9`
`Target Field 10`
`Response key to use 10`
`Augmentor`
`Action over target field`
`Text Format`
`Explode separator` : Split augmentor response into an array using this separator.
`Entity` : Provide the token name of the *commerce_product_variation* that this action should operate with. Please provide the token name only, without brackets.
`Replace tokens` : When enabled, tokens will be replaced *before* executing the action. **Please note:** Actions might already take care of replacing tokens on their own. Therefore, use this option only with care and when it makes sense.
# Augmentor Minimal store
Available since: unknown
## Fields
`Source Field 1`
`Source Field 2`
`Source Field 3`
`Source Field 4`
`Source Field 5`
`Source Field 6`
`Source Field 7`
`Source Field 8`
`Source Field 9`
`Source Field 10`
`Target Field 1`
`Response key to use 1`
`Target Field 2`
`Response key to use 2`
`Target Field 3`
`Response key to use 3`
`Target Field 4`
`Response key to use 4`
`Target Field 5`
`Response key to use 5`
`Target Field 6`
`Response key to use 6`
`Target Field 7`
`Response key to use 7`
`Target Field 8`
`Response key to use 8`
`Target Field 9`
`Response key to use 9`
`Target Field 10`
`Response key to use 10`
`Augmentor`
`Action over target field`
`Text Format`
`Explode separator` : Split augmentor response into an array using this separator.
`Entity` : Provide the token name of the *commerce_store* that this action should operate with. Please provide the token name only, without brackets.
`Replace tokens` : When enabled, tokens will be replaced *before* executing the action. **Please note:** Actions might already take care of replacing tokens on their own. Therefore, use this option only with care and when it makes sense.
# Augmentor Minimal contact message
Available since: unknown
## Fields
`Source Field 1`
`Source Field 2`
`Source Field 3`
`Source Field 4`
`Source Field 5`
`Source Field 6`
`Source Field 7`
`Source Field 8`
`Source Field 9`
`Source Field 10`
`Target Field 1`
`Response key to use 1`
`Target Field 2`
`Response key to use 2`
`Target Field 3`
`Response key to use 3`
`Target Field 4`
`Response key to use 4`
`Target Field 5`
`Response key to use 5`
`Target Field 6`
`Response key to use 6`
`Target Field 7`
`Response key to use 7`
`Target Field 8`
`Response key to use 8`
`Target Field 9`
`Response key to use 9`
`Target Field 10`
`Response key to use 10`
`Augmentor`
`Action over target field`
`Text Format`
`Explode separator` : Split augmentor response into an array using this separator.
`Entity` : Provide the token name of the *contact_message* that this action should operate with. Please provide the token name only, without brackets.
`Replace tokens` : When enabled, tokens will be replaced *before* executing the action. **Please note:** Actions might already take care of replacing tokens on their own. Therefore, use this option only with care and when it makes sense.
# Augmentor Minimal email
Available since: unknown
## Fields
`Source Field 1`
`Source Field 2`
`Source Field 3`
`Source Field 4`
`Source Field 5`
`Source Field 6`
`Source Field 7`
`Source Field 8`
`Source Field 9`
`Source Field 10`
`Target Field 1`
`Response key to use 1`
`Target Field 2`
`Response key to use 2`
`Target Field 3`
`Response key to use 3`
`Target Field 4`
`Response key to use 4`
`Target Field 5`
`Response key to use 5`
`Target Field 6`
`Response key to use 6`
`Target Field 7`
`Response key to use 7`
`Target Field 8`
`Response key to use 8`
`Target Field 9`
`Response key to use 9`
`Target Field 10`
`Response key to use 10`
`Augmentor`
`Action over target field`
`Text Format`
`Explode separator` : Split augmentor response into an array using this separator.
`Entity` : Provide the token name of the *easy_email* that this action should operate with. Please provide the token name only, without brackets.
`Replace tokens` : When enabled, tokens will be replaced *before* executing the action. **Please note:** Actions might already take care of replacing tokens on their own. Therefore, use this option only with care and when it makes sense.
# Augmentor Minimal flagging
Available since: unknown
## Fields
`Source Field 1`
`Source Field 2`
`Source Field 3`
`Source Field 4`
`Source Field 5`
`Source Field 6`
`Source Field 7`
`Source Field 8`
`Source Field 9`
`Source Field 10`
`Target Field 1`
`Response key to use 1`
`Target Field 2`
`Response key to use 2`
`Target Field 3`
`Response key to use 3`
`Target Field 4`
`Response key to use 4`
`Target Field 5`
`Response key to use 5`
`Target Field 6`
`Response key to use 6`
`Target Field 7`
`Response key to use 7`
`Target Field 8`
`Response key to use 8`
`Target Field 9`
`Response key to use 9`
`Target Field 10`
`Response key to use 10`
`Augmentor`
`Action over target field`
`Text Format`
`Explode separator` : Split augmentor response into an array using this separator.
`Entity` : Provide the token name of the *flagging* that this action should operate with. Please provide the token name only, without brackets.
`Replace tokens` : When enabled, tokens will be replaced *before* executing the action. **Please note:** Actions might already take care of replacing tokens on their own. Therefore, use this option only with care and when it makes sense.
# Augmentor Minimal group
Available since: unknown
## Fields
`Source Field 1`
`Source Field 2`
`Source Field 3`
`Source Field 4`
`Source Field 5`
`Source Field 6`
`Source Field 7`
`Source Field 8`
`Source Field 9`
`Source Field 10`
`Target Field 1`
`Response key to use 1`
`Target Field 2`
`Response key to use 2`
`Target Field 3`
`Response key to use 3`
`Target Field 4`
`Response key to use 4`
`Target Field 5`
`Response key to use 5`
`Target Field 6`
`Response key to use 6`
`Target Field 7`
`Response key to use 7`
`Target Field 8`
`Response key to use 8`
`Target Field 9`
`Response key to use 9`
`Target Field 10`
`Response key to use 10`
`Augmentor`
`Action over target field`
`Text Format`
`Explode separator` : Split augmentor response into an array using this separator.
`Entity` : Provide the token name of the *group* that this action should operate with. Please provide the token name only, without brackets.
`Replace tokens` : When enabled, tokens will be replaced *before* executing the action. **Please note:** Actions might already take care of replacing tokens on their own. Therefore, use this option only with care and when it makes sense.
# Augmentor Minimal group relationship
Available since: unknown
## Fields
`Source Field 1`
`Source Field 2`
`Source Field 3`
`Source Field 4`
`Source Field 5`
`Source Field 6`
`Source Field 7`
`Source Field 8`
`Source Field 9`
`Source Field 10`
`Target Field 1`
`Response key to use 1`
`Target Field 2`
`Response key to use 2`
`Target Field 3`
`Response key to use 3`
`Target Field 4`
`Response key to use 4`
`Target Field 5`
`Response key to use 5`
`Target Field 6`
`Response key to use 6`
`Target Field 7`
`Response key to use 7`
`Target Field 8`
`Response key to use 8`
`Target Field 9`
`Response key to use 9`
`Target Field 10`
`Response key to use 10`
`Augmentor`
`Action over target field`
`Text Format`
`Explode separator` : Split augmentor response into an array using this separator.
`Entity` : Provide the token name of the *group_relationship* that this action should operate with. Please provide the token name only, without brackets.
`Replace tokens` : When enabled, tokens will be replaced *before* executing the action. **Please note:** Actions might already take care of replacing tokens on their own. Therefore, use this option only with care and when it makes sense.
# Augmentor Minimal media item
Available since: unknown
## Fields
`Source Field 1`
`Source Field 2`
`Source Field 3`
`Source Field 4`
`Source Field 5`
`Source Field 6`
`Source Field 7`
`Source Field 8`
`Source Field 9`
`Source Field 10`
`Target Field 1`
`Response key to use 1`
`Target Field 2`
`Response key to use 2`
`Target Field 3`
`Response key to use 3`
`Target Field 4`
`Response key to use 4`
`Target Field 5`
`Response key to use 5`
`Target Field 6`
`Response key to use 6`
`Target Field 7`
`Response key to use 7`
`Target Field 8`
`Response key to use 8`
`Target Field 9`
`Response key to use 9`
`Target Field 10`
`Response key to use 10`
`Augmentor`
`Action over target field`
`Text Format`
`Explode separator` : Split augmentor response into an array using this separator.
`Entity` : Provide the token name of the *media* that this action should operate with. Please provide the token name only, without brackets.
`Replace tokens` : When enabled, tokens will be replaced *before* executing the action. **Please note:** Actions might already take care of replacing tokens on their own. Therefore, use this option only with care and when it makes sense.
# Augmentor Minimal content item
Available since: unknown
## Fields
`Source Field 1`
`Source Field 2`
`Source Field 3`
`Source Field 4`
`Source Field 5`
`Source Field 6`
`Source Field 7`
`Source Field 8`
`Source Field 9`
`Source Field 10`
`Target Field 1`
`Response key to use 1`
`Target Field 2`
`Response key to use 2`
`Target Field 3`
`Response key to use 3`
`Target Field 4`
`Response key to use 4`
`Target Field 5`
`Response key to use 5`
`Target Field 6`
`Response key to use 6`
`Target Field 7`
`Response key to use 7`
`Target Field 8`
`Response key to use 8`
`Target Field 9`
`Response key to use 9`
`Target Field 10`
`Response key to use 10`
`Augmentor`
`Action over target field`
`Text Format`
`Explode separator` : Split augmentor response into an array using this separator.
`Entity` : Provide the token name of the *node* that this action should operate with. Please provide the token name only, without brackets.
`Replace tokens` : When enabled, tokens will be replaced *before* executing the action. **Please note:** Actions might already take care of replacing tokens on their own. Therefore, use this option only with care and when it makes sense.
# Augmentor Minimal Paragraph
Available since: unknown
## Fields
`Source Field 1`
`Source Field 2`
`Source Field 3`
`Source Field 4`
`Source Field 5`
`Source Field 6`
`Source Field 7`
`Source Field 8`
`Source Field 9`
`Source Field 10`
`Target Field 1`
`Response key to use 1`
`Target Field 2`
`Response key to use 2`
`Target Field 3`
`Response key to use 3`
`Target Field 4`
`Response key to use 4`
`Target Field 5`
`Response key to use 5`
`Target Field 6`
`Response key to use 6`
`Target Field 7`
`Response key to use 7`
`Target Field 8`
`Response key to use 8`
`Target Field 9`
`Response key to use 9`
`Target Field 10`
`Response key to use 10`
`Augmentor`
`Action over target field`
`Text Format`
`Explode separator` : Split augmentor response into an array using this separator.
`Entity` : Provide the token name of the *paragraph* that this action should operate with. Please provide the token name only, without brackets.
`Replace tokens` : When enabled, tokens will be replaced *before* executing the action. **Please note:** Actions might already take care of replacing tokens on their own. Therefore, use this option only with care and when it makes sense.
# Augmentor Minimal profile
Available since: unknown
## Fields
`Source Field 1`
`Source Field 2`
`Source Field 3`
`Source Field 4`
`Source Field 5`
`Source Field 6`
`Source Field 7`
`Source Field 8`
`Source Field 9`
`Source Field 10`
`Target Field 1`
`Response key to use 1`
`Target Field 2`
`Response key to use 2`
`Target Field 3`
`Response key to use 3`
`Target Field 4`
`Response key to use 4`
`Target Field 5`
`Response key to use 5`
`Target Field 6`
`Response key to use 6`
`Target Field 7`
`Response key to use 7`
`Target Field 8`
`Response key to use 8`
`Target Field 9`
`Response key to use 9`
`Target Field 10`
`Response key to use 10`
`Augmentor`
`Action over target field`
`Text Format`
`Explode separator` : Split augmentor response into an array using this separator.
`Entity` : Provide the token name of the *profile* that this action should operate with. Please provide the token name only, without brackets.
`Replace tokens` : When enabled, tokens will be replaced *before* executing the action. **Please note:** Actions might already take care of replacing tokens on their own. Therefore, use this option only with care and when it makes sense.
# Augmentor Minimal shortcut link
Available since: unknown
## Fields
`Source Field 1`
`Source Field 2`
`Source Field 3`
`Source Field 4`
`Source Field 5`
`Source Field 6`
`Source Field 7`
`Source Field 8`
`Source Field 9`
`Source Field 10`
`Target Field 1`
`Response key to use 1`
`Target Field 2`
`Response key to use 2`
`Target Field 3`
`Response key to use 3`
`Target Field 4`
`Response key to use 4`
`Target Field 5`
`Response key to use 5`
`Target Field 6`
`Response key to use 6`
`Target Field 7`
`Response key to use 7`
`Target Field 8`
`Response key to use 8`
`Target Field 9`
`Response key to use 9`
`Target Field 10`
`Response key to use 10`
`Augmentor`
`Action over target field`
`Text Format`
`Explode separator` : Split augmentor response into an array using this separator.
`Entity` : Provide the token name of the *shortcut* that this action should operate with. Please provide the token name only, without brackets.
`Replace tokens` : When enabled, tokens will be replaced *before* executing the action. **Please note:** Actions might already take care of replacing tokens on their own. Therefore, use this option only with care and when it makes sense.
# Augmentor Minimal taxonomy term
Available since: unknown
## Fields
`Source Field 1`
`Source Field 2`
`Source Field 3`
`Source Field 4`
`Source Field 5`
`Source Field 6`
`Source Field 7`
`Source Field 8`
`Source Field 9`
`Source Field 10`
`Target Field 1`
`Response key to use 1`
`Target Field 2`
`Response key to use 2`
`Target Field 3`
`Response key to use 3`
`Target Field 4`
`Response key to use 4`
`Target Field 5`
`Response key to use 5`
`Target Field 6`
`Response key to use 6`
`Target Field 7`
`Response key to use 7`
`Target Field 8`
`Response key to use 8`
`Target Field 9`
`Response key to use 9`
`Target Field 10`
`Response key to use 10`
`Augmentor`
`Action over target field`
`Text Format`
`Explode separator` : Split augmentor response into an array using this separator.
`Entity` : Provide the token name of the *taxonomy_term* that this action should operate with. Please provide the token name only, without brackets.
`Replace tokens` : When enabled, tokens will be replaced *before* executing the action. **Please note:** Actions might already take care of replacing tokens on their own. Therefore, use this option only with care and when it makes sense.
# Augmentor Minimal submission
Available since: unknown
## Fields
`Source Field 1`
`Source Field 2`
`Source Field 3`
`Source Field 4`
`Source Field 5`
`Source Field 6`
`Source Field 7`
`Source Field 8`
`Source Field 9`
`Source Field 10`
`Target Field 1`
`Response key to use 1`
`Target Field 2`
`Response key to use 2`
`Target Field 3`
`Response key to use 3`
`Target Field 4`
`Response key to use 4`
`Target Field 5`
`Response key to use 5`
`Target Field 6`
`Response key to use 6`
`Target Field 7`
`Response key to use 7`
`Target Field 8`
`Response key to use 8`
`Target Field 9`
`Response key to use 9`
`Target Field 10`
`Response key to use 10`
`Augmentor`
`Action over target field`
`Text Format`
`Explode separator` : Split augmentor response into an array using this separator.
`Entity` : Provide the token name of the *webform_submission* that this action should operate with. Please provide the token name only, without brackets.
`Replace tokens` : When enabled, tokens will be replaced *before* executing the action. **Please note:** Actions might already take care of replacing tokens on their own. Therefore, use this option only with care and when it makes sense.
# Augmentor content item
Available since: unknown
## Fields
`Source fields`
`Advance targeting`
`Augmentor`
`Action over target field`
`Text Format`
`Explode separator` : Split augmentor response into an array using this separator.
`Entity` : Provide the token name of the *node* that this action should operate with. Please provide the token name only, without brackets.
`Replace tokens` : When enabled, tokens will be replaced *before* executing the action. **Please note:** Actions might already take care of replacing tokens on their own. Therefore, use this option only with care and when it makes sense.
# Augmentor Paragraph
Available since: unknown
## Fields
`Source fields`
`Advance targeting`
`Augmentor`
`Action over target field`
`Text Format`
`Explode separator` : Split augmentor response into an array using this separator.
`Entity` : Provide the token name of the *paragraph* that this action should operate with. Please provide the token name only, without brackets.
`Replace tokens` : When enabled, tokens will be replaced *before* executing the action. **Please note:** Actions might already take care of replacing tokens on their own. Therefore, use this option only with care and when it makes sense.
# Augmentor profile
Available since: unknown
## Fields
`Source fields`
`Advance targeting`
`Augmentor`
`Action over target field`
`Text Format`
`Explode separator` : Split augmentor response into an array using this separator.
`Entity` : Provide the token name of the *profile* that this action should operate with. Please provide the token name only, without brackets.
`Replace tokens` : When enabled, tokens will be replaced *before* executing the action. **Please note:** Actions might already take care of replacing tokens on their own. Therefore, use this option only with care and when it makes sense.
# Augmentor shortcut link
Available since: unknown
## Fields
`Source fields`
`Advance targeting`
`Augmentor`
`Action over target field`
`Text Format`
`Explode separator` : Split augmentor response into an array using this separator.
`Entity` : Provide the token name of the *shortcut* that this action should operate with. Please provide the token name only, without brackets.
`Replace tokens` : When enabled, tokens will be replaced *before* executing the action. **Please note:** Actions might already take care of replacing tokens on their own. Therefore, use this option only with care and when it makes sense.
# Augmentor taxonomy term
Available since: unknown
## Fields
`Source fields`
`Advance targeting`
`Augmentor`
`Action over target field`
`Text Format`
`Explode separator` : Split augmentor response into an array using this separator.
`Entity` : Provide the token name of the *taxonomy_term* that this action should operate with. Please provide the token name only, without brackets.
`Replace tokens` : When enabled, tokens will be replaced *before* executing the action. **Please note:** Actions might already take care of replacing tokens on their own. Therefore, use this option only with care and when it makes sense.
# Augmentor submission
Available since: unknown
## Fields
`Source fields`
`Advance targeting`
`Augmentor`
`Action over target field`
`Text Format`
`Explode separator` : Split augmentor response into an array using this separator.
`Entity` : Provide the token name of the *webform_submission* that this action should operate with. Please provide the token name only, without brackets.
`Replace tokens` : When enabled, tokens will be replaced *before* executing the action. **Please note:** Actions might already take care of replacing tokens on their own. Therefore, use this option only with care and when it makes sense.
# BlueSky Integration
## Installation
```
composer require drupal/bsky
drush pm:install bsky
```
Instead of using Drush to enable the module, you can also go to "Administration / Extend" (`/admin/modules`) and enable the module **BlueSky Integration** from there.
# BlueSky: Add Facets
Available since: unknown
Add facets to a Post object. Facets create links for handles, hashtags, and URLs.
## Fields
`Post` : A previously created Post object.
# BlueSky: Add Image
Available since: unknown
Add an image to a Post object.
## Fields
`Post` : A previously created Post object.
`Image` : Path to an image to upload to BlueSky, attached to the Post.
`Alt Text` : Alt text to attach to the image.
# BlueSky: Create Post
Available since: unknown
Create a Post object to send to BlueSky.
## Fields
`Message` : The message that will be displayed on the post.
`Name of response token` : The response value will be loaded into this specified token. You can access [token_name:body] raw response, [token_name:json] when return is valid json, [token_name:headers], [token_name:status], [token_name:client_error] > 0 when has problem. Please provide the token name only, without brackets.
# BlueSky: Send Post
Available since: unknown
Send a Post object to BlueSky.
## Fields
`Post` : The Post to send to BlueSky.
`Name of response token` : The response value will be loaded into this specified token. Please provide the token name only, without brackets.
# Commerce Product
## Installation
```
composer require drupal/commerce
drush pm:install commerce_product
```
Instead of using Drush to enable the module, you can also go to "Administration / Extend" (`/admin/modules`) and enable the module **Commerce Product** from there.
# Publish selected product
Available since: unknown
## Fields
`Entity` : Provide the token name of the *commerce_product* that this action should operate with. Please provide the token name only, without brackets.
# Unpublish selected product
Available since: unknown
## Fields
`Entity` : Provide the token name of the *commerce_product* that this action should operate with. Please provide the token name only, without brackets.
# Content Moderation
## Installation
```
drush pm:install content_moderation
```
Instead of using Drush to enable the module, you can also go to "Administration / Extend" (`/admin/modules`) and enable the module **Content Moderation** from there.
# Change moderation state of Content block
## Fields
`Workflow`
`workflow_submit`
`Entity` : Provide the token name of the *block_content* that this action should operate with. Please provide the token name only, without brackets.
`Replace tokens` : When enabled, tokens will be replaced *before* executing the action. **Please note:** Actions might already take care of replacing tokens on their own. Therefore, use this option only with care and when it makes sense.
# Change moderation state of Email
Available since: unknown
## Fields
`Workflow`
`workflow_submit`
`Entity` : Provide the token name of the *easy_email* that this action should operate with. Please provide the token name only, without brackets.
`Replace tokens` : When enabled, tokens will be replaced *before* executing the action. **Please note:** Actions might already take care of replacing tokens on their own. Therefore, use this option only with care and when it makes sense.
# Change moderation state of Group
Available since: unknown
## Fields
`Workflow`
`workflow_submit`
`Entity` : Provide the token name of the *group* that this action should operate with. Please provide the token name only, without brackets.
`Replace tokens` : When enabled, tokens will be replaced *before* executing the action. **Please note:** Actions might already take care of replacing tokens on their own. Therefore, use this option only with care and when it makes sense.
# Change moderation state of Media
Available since: unknown
## Fields
`Workflow`
`workflow_submit`
`Entity` : Provide the token name of the *media* that this action should operate with. Please provide the token name only, without brackets.
`Replace tokens` : When enabled, tokens will be replaced *before* executing the action. **Please note:** Actions might already take care of replacing tokens on their own. Therefore, use this option only with care and when it makes sense.
# Change moderation state of Content
## Fields
`Workflow`
`workflow_submit`
`Entity` : Provide the token name of the *node* that this action should operate with. Please provide the token name only, without brackets.
`Replace tokens` : When enabled, tokens will be replaced *before* executing the action. **Please note:** Actions might already take care of replacing tokens on their own. Therefore, use this option only with care and when it makes sense.
# Change moderation state of Profile
Available since: unknown
## Fields
`Workflow`
`workflow_submit`
`Entity` : Provide the token name of the *profile* that this action should operate with. Please provide the token name only, without brackets.
`Replace tokens` : When enabled, tokens will be replaced *before* executing the action. **Please note:** Actions might already take care of replacing tokens on their own. Therefore, use this option only with care and when it makes sense.
# Change moderation state of Taxonomy term
## Fields
`Workflow`
`workflow_submit`
`Entity` : Provide the token name of the *taxonomy_term* that this action should operate with. Please provide the token name only, without brackets.
`Replace tokens` : When enabled, tokens will be replaced *before* executing the action. **Please note:** Actions might already take care of replacing tokens on their own. Therefore, use this option only with care and when it makes sense.
# Drupal core
No separate installation is required.
# Redirect to URL
## Fields
`URL` : The URL to which the user should be redirected. This can be an internal URL like /node/1234 or an external URL like https://example.com.
`Replace tokens` : When enabled, tokens will be replaced *before* executing the action. **Please note:** Actions might already take care of replacing tokens on their own. Therefore, use this option only with care and when it makes sense.
# Display a message to the user
## Fields
`Message` : The message to be displayed to the current user. You may include placeholders like [node:title], [user:account-name], [user:display-name] and [comment:body] to represent data that will be different each time message is sent. Not all placeholders will be available in all contexts.
`Replace tokens` : When enabled, tokens will be replaced *before* executing the action. **Please note:** Actions might already take care of replacing tokens on their own. Therefore, use this option only with care and when it makes sense.
# Send email
## Fields
`Recipient email address` : You may also use tokens: [node:author:mail], [comment:author:mail], etc. Separate recipients with a comma.
`Subject` : The subject of the message.
`Message` : The message that should be sent. You may include placeholders like [node:title], [user:account-name], [user:display-name] and [comment:body] to represent data that will be different each time message is sent. Not all placeholders will be available in all contexts.
`Replace tokens` : When enabled, tokens will be replaced *before* executing the action. **Please note:** Actions might already take care of replacing tokens on their own. Therefore, use this option only with care and when it makes sense.
# Publish content block
## Fields
`Entity` : Provide the token name of the *block_content* that this action should operate with. Please provide the token name only, without brackets.
```
See also: [Tokens being forwarded](https://ecaguide.org/eca/concepts/tokens/#tokens-being-forwarded)
```
# Publish comment
## Fields
`Entity` : Provide the token name of the *comment* that this action should operate with. Please provide the token name only, without brackets.
```
See also: [Tokens being forwarded](https://ecaguide.org/eca/concepts/tokens/#tokens-being-forwarded)
```
# Publish product
Available since: unknown
## Fields
`Entity` : Provide the token name of the *commerce_product* that this action should operate with. Please provide the token name only, without brackets.
# Publish product variation
Available since: unknown
## Fields
`Entity` : Provide the token name of the *commerce_product_variation* that this action should operate with. Please provide the token name only, without brackets.
# Publish group
Available since: unknown
## Fields
`Entity` : Provide the token name of the *group* that this action should operate with. Please provide the token name only, without brackets.
```
See also: [Tokens being forwarded](https://ecaguide.org/eca/concepts/tokens/#tokens-being-forwarded)
```
# Publish media item
Available since: unknown
## Fields
`Entity` : Provide the token name of the *media* that this action should operate with. Please provide the token name only, without brackets.
```
See also: [Tokens being forwarded](https://ecaguide.org/eca/concepts/tokens/#tokens-being-forwarded)
```
# Publish custom menu link
## Fields
`Entity` : Provide the token name of the *menu_link_content* that this action should operate with. Please provide the token name only, without brackets.
```
See also: [Tokens being forwarded](https://ecaguide.org/eca/concepts/tokens/#tokens-being-forwarded)
```
# Publish content item
## Fields
`Entity` : Provide the token name of the *node* that this action should operate with. Please provide the token name only, without brackets.
```
See also: [Tokens being forwarded](https://ecaguide.org/eca/concepts/tokens/#tokens-being-forwarded)
```
# Publish Paragraph
Available since: unknown
## Fields
`Entity` : Provide the token name of the *paragraph* that this action should operate with. Please provide the token name only, without brackets.
# Publish URL alias
## Fields
`Entity` : Provide the token name of the *path_alias* that this action should operate with. Please provide the token name only, without brackets.
```
See also: [Tokens being forwarded](https://ecaguide.org/eca/concepts/tokens/#tokens-being-forwarded)
```
# Publish profile
Available since: unknown
## Fields
`Entity` : Provide the token name of the *profile* that this action should operate with. Please provide the token name only, without brackets.
# Publish taxonomy term
## Fields
`Entity` : Provide the token name of the *taxonomy_term* that this action should operate with. Please provide the token name only, without brackets.
```
See also: [Tokens being forwarded](https://ecaguide.org/eca/concepts/tokens/#tokens-being-forwarded)
```
# Unpublish content block
## Fields
`Entity` : Provide the token name of the *block_content* that this action should operate with. Please provide the token name only, without brackets.
```
See also: [Tokens being forwarded](https://ecaguide.org/eca/concepts/tokens/#tokens-being-forwarded)
```
# Unpublish comment
## Fields
`Entity` : Provide the token name of the *comment* that this action should operate with. Please provide the token name only, without brackets.
```
See also: [Tokens being forwarded](https://ecaguide.org/eca/concepts/tokens/#tokens-being-forwarded)
```
# Unpublish product
Available since: unknown
## Fields
`Entity` : Provide the token name of the *commerce_product* that this action should operate with. Please provide the token name only, without brackets.
# Unpublish product variation
Available since: unknown
## Fields
`Entity` : Provide the token name of the *commerce_product_variation* that this action should operate with. Please provide the token name only, without brackets.
# Unpublish group
Available since: unknown
## Fields
`Entity` : Provide the token name of the *group* that this action should operate with. Please provide the token name only, without brackets.
```
See also: [Tokens being forwarded](https://ecaguide.org/eca/concepts/tokens/#tokens-being-forwarded)
```
# Unpublish media item
Available since: unknown
## Fields
`Entity` : Provide the token name of the *media* that this action should operate with. Please provide the token name only, without brackets.
```
See also: [Tokens being forwarded](https://ecaguide.org/eca/concepts/tokens/#tokens-being-forwarded)
```
# Unpublish custom menu link
## Fields
`Entity` : Provide the token name of the *menu_link_content* that this action should operate with. Please provide the token name only, without brackets.
```
See also: [Tokens being forwarded](https://ecaguide.org/eca/concepts/tokens/#tokens-being-forwarded)
```
# Unpublish content item
## Fields
`Entity` : Provide the token name of the *node* that this action should operate with. Please provide the token name only, without brackets.
```
See also: [Tokens being forwarded](https://ecaguide.org/eca/concepts/tokens/#tokens-being-forwarded)
```
# Unpublish Paragraph
Available since: unknown
## Fields
`Entity` : Provide the token name of the *paragraph* that this action should operate with. Please provide the token name only, without brackets.
# Unpublish URL alias
## Fields
`Entity` : Provide the token name of the *path_alias* that this action should operate with. Please provide the token name only, without brackets.
```
See also: [Tokens being forwarded](https://ecaguide.org/eca/concepts/tokens/#tokens-being-forwarded)
```
# Unpublish profile
Available since: unknown
## Fields
`Entity` : Provide the token name of the *profile* that this action should operate with. Please provide the token name only, without brackets.
# Unpublish taxonomy term
## Fields
`Entity` : Provide the token name of the *taxonomy_term* that this action should operate with. Please provide the token name only, without brackets.
```
See also: [Tokens being forwarded](https://ecaguide.org/eca/concepts/tokens/#tokens-being-forwarded)
```
# Drupal Remote Dashboard
## Installation
```
composer require drupal/drd
drush pm:install drd
```
Instead of using Drush to enable the module, you can also go to "Administration / Extend" (`/admin/modules`) and enable the module **Drupal Remote Dashboard** from there.
# List and render blocks
Available since: 4.1.0
## Fields
`Terms`
`Entity` : Provide the token name of the *drd_domain* that this action should operate with. Please provide the token name only, without brackets.
`Replace tokens` : When enabled, tokens will be replaced *before* executing the action. **Please note:** Actions might already take care of replacing tokens on their own. Therefore, use this option only with care and when it makes sense.
# Cron
Available since: 4.1.0
## Fields
`Terms`
`Entity` : Provide the token name of the *drd_domain* that this action should operate with. Please provide the token name only, without brackets.
`Replace tokens` : When enabled, tokens will be replaced *before* executing the action. **Please note:** Actions might already take care of replacing tokens on their own. Therefore, use this option only with care and when it makes sense.
# Download a database dump
Available since: 4.1.0
## Fields
`Terms`
`Replace tokens` : When enabled, tokens will be replaced *before* executing the action. **Please note:** Actions might already take care of replacing tokens on their own. Therefore, use this option only with care and when it makes sense.
# DNS lookup for all domains
Available since: 4.1.0
## Fields
`Terms`
`Entity` : Provide the token name of the *drd_host* that this action should operate with. Please provide the token name only, without brackets.
`Replace tokens` : When enabled, tokens will be replaced *before* executing the action. **Please note:** Actions might already take care of replacing tokens on their own. Therefore, use this option only with care and when it makes sense.
# Change domain, protocol and port of a domain
Available since: 4.1.0
## Fields
`Terms`
`Entity` : Provide the token name of the *drd_domain* that this action should operate with. Please provide the token name only, without brackets.
`Replace tokens` : When enabled, tokens will be replaced *before* executing the action. **Please note:** Actions might already take care of replacing tokens on their own. Therefore, use this option only with care and when it makes sense.
# Move domain to a different core
Available since: 4.1.0
## Fields
`Terms`
`Entity` : Provide the token name of the *drd_domain* that this action should operate with. Please provide the token name only, without brackets.
`Replace tokens` : When enabled, tokens will be replaced *before* executing the action. **Please note:** Actions might already take care of replacing tokens on their own. Therefore, use this option only with care and when it makes sense.
# Enable all domains
Available since: 4.1.0
## Fields
`Terms`
`Entity` : Provide the token name of the *drd_core* that this action should operate with. Please provide the token name only, without brackets.
`Replace tokens` : When enabled, tokens will be replaced *before* executing the action. **Please note:** Actions might already take care of replacing tokens on their own. Therefore, use this option only with care and when it makes sense.
# Receive domains
Available since: 4.1.0
## Fields
`Terms`
`Entity` : Provide the token name of the *drd_core* that this action should operate with. Please provide the token name only, without brackets.
`Replace tokens` : When enabled, tokens will be replaced *before* executing the action. **Please note:** Actions might already take care of replacing tokens on their own. Therefore, use this option only with care and when it makes sense.
# Download a file
Available since: 4.1.0
## Fields
`Terms`
`Entity` : Provide the token name of the *drd_domain* that this action should operate with. Please provide the token name only, without brackets.
`Replace tokens` : When enabled, tokens will be replaced *before* executing the action. **Please note:** Actions might already take care of replacing tokens on their own. Therefore, use this option only with care and when it makes sense.
# Error Logs
Available since: 4.1.0
## Fields
`Terms`
`Entity` : Provide the token name of the *drd_domain* that this action should operate with. Please provide the token name only, without brackets.
`Replace tokens` : When enabled, tokens will be replaced *before* executing the action. **Please note:** Actions might already take care of replacing tokens on their own. Therefore, use this option only with care and when it makes sense.
# Flush Cache
Available since: 4.1.0
## Fields
`Terms`
`Entity` : Provide the token name of the *drd_domain* that this action should operate with. Please provide the token name only, without brackets.
`Replace tokens` : When enabled, tokens will be replaced *before* executing the action. **Please note:** Actions might already take care of replacing tokens on their own. Therefore, use this option only with care and when it makes sense.
# Info
Available since: 4.1.0
## Fields
`Terms`
`Entity` : Provide the token name of the *drd_domain* that this action should operate with. Please provide the token name only, without brackets.
`Replace tokens` : When enabled, tokens will be replaced *before* executing the action. **Please note:** Actions might already take care of replacing tokens on their own. Therefore, use this option only with care and when it makes sense.
# JobScheduler
Available since: 4.1.0
## Fields
`Terms`
`Entity` : Provide the token name of the *drd_domain* that this action should operate with. Please provide the token name only, without brackets.
`Replace tokens` : When enabled, tokens will be replaced *before* executing the action. **Please note:** Actions might already take care of replacing tokens on their own. Therefore, use this option only with care and when it makes sense.
# List Cores
Available since: 4.1.0
## Fields
`Terms`
`Replace tokens` : When enabled, tokens will be replaced *before* executing the action. **Please note:** Actions might already take care of replacing tokens on their own. Therefore, use this option only with care and when it makes sense.
# List Domains
Available since: 4.1.0
## Fields
`Terms`
`Replace tokens` : When enabled, tokens will be replaced *before* executing the action. **Please note:** Actions might already take care of replacing tokens on their own. Therefore, use this option only with care and when it makes sense.
# List Hosts
Available since: 4.1.0
## Fields
`Terms`
`Replace tokens` : When enabled, tokens will be replaced *before* executing the action. **Please note:** Actions might already take care of replacing tokens on their own. Therefore, use this option only with care and when it makes sense.
# Maintenance Mode
Available since: 4.1.0
## Fields
`Mode`
`Entity` : Provide the token name of the *drd_domain* that this action should operate with. Please provide the token name only, without brackets.
`Replace tokens` : When enabled, tokens will be replaced *before* executing the action. **Please note:** Actions might already take care of replacing tokens on their own. Therefore, use this option only with care and when it makes sense.
# Execute PHP
Available since: 4.1.0
## Fields
`PHP Code`
`Entity` : Provide the token name of the *drd_domain* that this action should operate with. Please provide the token name only, without brackets.
`Replace tokens` : When enabled, tokens will be replaced *before* executing the action. **Please note:** Actions might already take care of replacing tokens on their own. Therefore, use this option only with care and when it makes sense.
# Ping
Available since: 4.1.0
## Fields
`Terms`
`Entity` : Provide the token name of the *drd_domain* that this action should operate with. Please provide the token name only, without brackets.
`Replace tokens` : When enabled, tokens will be replaced *before* executing the action. **Please note:** Actions might already take care of replacing tokens on their own. Therefore, use this option only with care and when it makes sense.
# Get installed projects
Available since: 4.1.0
## Fields
`Terms`
`Entity` : Provide the token name of the *drd_domain* that this action should operate with. Please provide the token name only, without brackets.
`Replace tokens` : When enabled, tokens will be replaced *before* executing the action. **Please note:** Actions might already take care of replacing tokens on their own. Therefore, use this option only with care and when it makes sense.
# Check status for all projects
Available since: 4.1.0
## Fields
`Terms`
`Replace tokens` : When enabled, tokens will be replaced *before* executing the action. **Please note:** Actions might already take care of replacing tokens on their own. Therefore, use this option only with care and when it makes sense.
# Update Projects
Available since: 4.1.0
## Fields
`Terms`
`Entity` : Provide the token name of the *drd_core* that this action should operate with. Please provide the token name only, without brackets.
`Replace tokens` : When enabled, tokens will be replaced *before* executing the action. **Please note:** Actions might already take care of replacing tokens on their own. Therefore, use this option only with care and when it makes sense.
# Show Logs of Update Projects
Available since: 4.1.0
## Fields
`Terms`
`Entity` : Provide the token name of the *drd_core* that this action should operate with. Please provide the token name only, without brackets.
`Replace tokens` : When enabled, tokens will be replaced *before* executing the action. **Please note:** Actions might already take care of replacing tokens on their own. Therefore, use this option only with care and when it makes sense.
# Lock a project release
Available since: 4.1.0
## Fields
`Terms`
`Replace tokens` : When enabled, tokens will be replaced *before* executing the action. **Please note:** Actions might already take care of replacing tokens on their own. Therefore, use this option only with care and when it makes sense.
# Unlock a project release
Available since: 4.1.0
## Fields
`Terms`
`Replace tokens` : When enabled, tokens will be replaced *before* executing the action. **Please note:** Actions might already take care of replacing tokens on their own. Therefore, use this option only with care and when it makes sense.
# Get a session URL
Available since: 4.1.0
## Fields
`Terms`
`Entity` : Provide the token name of the *drd_domain* that this action should operate with. Please provide the token name only, without brackets.
`Replace tokens` : When enabled, tokens will be replaced *before* executing the action. **Please note:** Actions might already take care of replacing tokens on their own. Therefore, use this option only with care and when it makes sense.
# Run update.php
Available since: 4.1.0
## Fields
`Terms`
`Entity` : Provide the token name of the *drd_domain* that this action should operate with. Please provide the token name only, without brackets.
`Replace tokens` : When enabled, tokens will be replaced *before* executing the action. **Please note:** Actions might already take care of replacing tokens on their own. Therefore, use this option only with care and when it makes sense.
# Update Translations
Available since: 4.1.0
## Fields
`Terms`
`Entity` : Provide the token name of the *drd_domain* that this action should operate with. Please provide the token name only, without brackets.
`Replace tokens` : When enabled, tokens will be replaced *before* executing the action. **Please note:** Actions might already take care of replacing tokens on their own. Therefore, use this option only with care and when it makes sense.
# User Credentials
Available since: 4.1.0
## Fields
`User ID`
`User name`
`Password`
`Status`
`Entity` : Provide the token name of the *drd_domain* that this action should operate with. Please provide the token name only, without brackets.
`Replace tokens` : When enabled, tokens will be replaced *before* executing the action. **Please note:** Actions might already take care of replacing tokens on their own. Therefore, use this option only with care and when it makes sense.
# ECA for DRD
## Installation
```
composer require drupal/drd
drush pm:install drd_eca
```
Instead of using Drush to enable the module, you can also go to "Administration / Extend" (`/admin/modules`) and enable the module **ECA for DRD** from there.
# DRD: Action finished
Available since: 4.1.0
Provided tokens
| Token | Description |
| ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `[event]` | The event. |
| `[event:drd_action_id]` | The ID of the DRD action. |
| `[event:entity]` | The entity the action deals with. |
| `[event]` | The event. |
| `[event:machine_name]` | The machine name of the ECA event. |
| `[session_user]` | The user account that dispatched the event, regardless if ECA is processing models under a different account. This is only available if ECA is configured to always run under a specific account. |
Fires when a DRD (Drupal Remote Dashboard) action finishes executing on a remote site.
The `[event:drd_action_id]` token contains the plugin ID of the DRD action and `[event:entity]` provides the remote entity (host, core, or domain) the action operated on, if applicable.
# DRD: Action started
Available since: 4.1.0
Provided tokens
| Token | Description |
| ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `[event]` | The event. |
| `[event:drd_action_id]` | The ID of the DRD action. |
| `[event:entity]` | The entity the action deals with. |
| `[event]` | The event. |
| `[event:machine_name]` | The machine name of the ECA event. |
| `[session_user]` | The user account that dispatched the event, regardless if ECA is processing models under a different account. This is only available if ECA is configured to always run under a specific account. |
Fires when a DRD (Drupal Remote Dashboard) action starts executing on a remote site.
The `[event:drd_action_id]` token contains the plugin ID of the DRD action and `[event:entity]` provides the remote entity (host, core, or domain) the action operates on, if applicable.
# Easy Email
## Installation
```
composer require drupal/easy_email
drush pm:install easy_email
```
Instead of using Drush to enable the module, you can also go to "Administration / Extend" (`/admin/modules`) and enable the module **Easy Email** from there.
# Send Easy Email
Available since: unknown
## Fields
`Entity` : Provide the token name of the *easy_email* that this action should operate with. Please provide the token name only, without brackets.
# ECA Core
## Installation
```
composer require drupal/eca
drush pm:install eca
```
Instead of using Drush to enable the module, you can also go to "Administration / Extend" (`/admin/modules`) and enable the module **ECA Core** from there.
# ECA Access
Sets an access result for the event. Possible results are:
- allowed
- neutral
- forbidden
## Installation
```
composer require drupal/eca
drush pm:install eca_access
```
Instead of using Drush to enable the module, you can also go to "Administration / Extend" (`/admin/modules`) and enable the module **ECA Access** from there.
# Set access result
Available since: 1.0.0
Only works when reacting upon *ECA Access* events.
Action to set an access result.
Attention
When more than one of this action are used for the same event, beware the order if important here. As soon as access got denied once, this can never being overridden afterwards. However, allowing access first, and denying it later if certain conditions apply, is what works as intended and is therefore the recommended approach.
## Fields
`Access result` : Please note: This action only works when reacting upon *access* events.\
When using the "Defined by token" option, make sure there is a token with this name: *eca_access_set_result_access_result*
```
Sets one of the following access results:
```
- allowed
- neutral
- forbidden
# Determining entity create access
Available since: 1.1.0
Provided tokens
| Token | Description |
| ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `[user]` | The current user. Alias: `current_user` |
| `[htmx]` | Information about the current HTMX request. |
| `[htmx:is_request]` | Whether the current request was sent by HTMX ("1" or "0"). |
| `[htmx:boosted]` | Whether the current request was boosted by HTMX ("1" or "0"). |
| `[htmx:history_restore]` | Whether the current request is for HTMX history restoration ("1" or "0"). |
| `[htmx:target]` | The value of the HX-Target request header. |
| `[htmx:trigger]` | The value of the HX-Trigger request header. |
| `[htmx:trigger_name]` | The value of the HX-Trigger-Name request header. |
| `[htmx:current_url]` | The value of the HX-Current-URL request header. |
| `[htmx:prompt]` | The value of the HX-Prompt request header. |
| `[event]` | The event. |
| `[event:context]` | Contains a list of properties depending on the context of the event. |
| `[event:entity_bundle]` | The bundle of the entity. |
| `[event:uid]` | The ID of the user account of the event. |
| `[event]` | The event. |
| `[event:machine_name]` | The machine name of the ECA event. |
| `[session_user]` | The user account that dispatched the event, regardless if ECA is processing models under a different account. This is only available if ECA is configured to always run under a specific account. |
| `[account]` | The user account of the event. |
Fires when Drupal checks whether a user can create a new entity of a given type and bundle. This wraps Drupal's `hook_entity_create_access()`.
Use the [Set access result](https://ecaguide.org/plugins/eca/access/actions/eca_access_set_result/index.md) action to grant or deny access.
No entity available
Since the entity does not exist yet, the `[entity]` token is **not available** in this event. You only have access to the entity type, bundle, language, and the requesting account via `[account]`.
## Fields
For any successor of this event, the account that asks for access is available under the [account] token. Example: [account:uid] provides the user ID of the account.
Furthermore, following data of the event is available:
- [event:operation] holds the requested operation, such as "view".
`Restrict by entity type ID` : Example: *node, taxonomy_term, user*
```
Restricts to the specified entity type (e.g., `node`, `taxonomy_term`). Comma-separated for multiple values.
```
`Restrict by entity bundle` : Example: *article, tags*
```
Restricts to the specified bundle (e.g., `article`, `tags`). For entities without bundles (like users), the bundle equals the entity type.
```
`Restrict by language code` : Example: *en*
```
Restricts to the specified language code (e.g., `en`). Useful in multilingual sites where create access might differ per language. Comma-separated for multiple values.
```
# Determining entity access
Available since: 1.0.0
Provided tokens
| Token | Description |
| ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `[user]` | The current user. Alias: `current_user` |
| `[htmx]` | Information about the current HTMX request. |
| `[htmx:is_request]` | Whether the current request was sent by HTMX ("1" or "0"). |
| `[htmx:boosted]` | Whether the current request was boosted by HTMX ("1" or "0"). |
| `[htmx:history_restore]` | Whether the current request is for HTMX history restoration ("1" or "0"). |
| `[htmx:target]` | The value of the HX-Target request header. |
| `[htmx:trigger]` | The value of the HX-Trigger request header. |
| `[htmx:trigger_name]` | The value of the HX-Trigger-Name request header. |
| `[htmx:current_url]` | The value of the HX-Current-URL request header. |
| `[htmx:prompt]` | The value of the HX-Prompt request header. |
| `[event]` | The event. |
| `[event:entity_bundle]` | The bundle of the entity. |
| `[event:entity_id]` | The entity ID, only available if the entity is not new. |
| `[event:entity_type]` | The entity type. |
| `[event:operation]` | The operation with which the entity should be accessed, e.g. "view", "update", etc. |
| `[event:uid]` | The ID of the user account of the event. |
| `[event]` | The event. |
| `[event:machine_name]` | The machine name of the ECA event. |
| `[session_user]` | The user account that dispatched the event, regardless if ECA is processing models under a different account. This is only available if ECA is configured to always run under a specific account. |
| `[account]` | The user account of the event. |
| `[entity]` | The entity of the event. |
| `[ENTITY_TYPE]` | The entity of the event under the name of its entity type. |
Fires when Drupal checks whether a user can perform an operation on an existing entity. This wraps Drupal's `hook_entity_access()`.
Use the [Set access result](https://ecaguide.org/plugins/eca/access/actions/eca_access_set_result/index.md) action to grant or deny access.
The account token is the checked account
The `[account]` token is the account **being checked for access**, not necessarily the currently logged-in user. When code checks access for a different user (e.g., `$entity->access('view', $other_user)`), the `[account]` token reflects that other user.
Performance consideration
Access results from ECA are not cached (`maxAge=0`). Use specific filters (entity type, bundle, operation) to minimize the performance impact.
## Fields
For any successor of this event, the account that asks for access is available under the [account] token. Example: [account:uid] provides the user ID of the account.
Furthermore, following data of the event is available:- [event:operation] holds the requested operation, such as "view".
`Restrict by entity type ID` : Example: *node, taxonomy_term, user*
```
Restricts to the specified entity type (e.g., `node`, `taxonomy_term`). Comma-separated for multiple values.
```
`Restrict by entity bundle` : Example: *article, tags*
```
Restricts to the specified bundle (e.g., `article`, `tags`). Comma-separated for multiple values.
```
`Restrict by operation` : Example: *view, update, delete*
```
Restricts to the specified operation (e.g., `view`, `update`, `delete`). Comma-separated for multiple values.
```
# Determining entity field access
Available since: 1.0.0
Provided tokens
| Token | Description |
| ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `[user]` | The current user. Alias: `current_user` |
| `[htmx]` | Information about the current HTMX request. |
| `[htmx:is_request]` | Whether the current request was sent by HTMX ("1" or "0"). |
| `[htmx:boosted]` | Whether the current request was boosted by HTMX ("1" or "0"). |
| `[htmx:history_restore]` | Whether the current request is for HTMX history restoration ("1" or "0"). |
| `[htmx:target]` | The value of the HX-Target request header. |
| `[htmx:trigger]` | The value of the HX-Trigger request header. |
| `[htmx:trigger_name]` | The value of the HX-Trigger-Name request header. |
| `[htmx:current_url]` | The value of the HX-Current-URL request header. |
| `[htmx:prompt]` | The value of the HX-Prompt request header. |
| `[event]` | The event. |
| `[event:entity_bundle]` | The bundle of the entity. |
| `[event:entity_id]` | The entity ID, only available if the entity is not new. |
| `[event:entity_type]` | The entity type. |
| `[event:field]` | The name of the field. |
| `[event:operation]` | The operation with which the entity should be accessed, e.g. "view", "update", etc. |
| `[event:uid]` | The ID of the user account of the event. |
| `[event]` | The event. |
| `[event:machine_name]` | The machine name of the ECA event. |
| `[session_user]` | The user account that dispatched the event, regardless if ECA is processing models under a different account. This is only available if ECA is configured to always run under a specific account. |
| `[account]` | The user account of the event. |
| `[entity]` | The entity of the event. |
| `[ENTITY_TYPE]` | The entity of the event under the name of its entity type. |
Fires when Drupal checks whether a user can access a specific field on an entity. This wraps Drupal's `hook_entity_field_access()`.
Use the [Set access result](https://ecaguide.org/plugins/eca/access/actions/eca_access_set_result/index.md) action to grant or deny access.
High frequency event
This event fires for **every field** on **every entity** during page rendering. Always use specific filters (entity type, bundle, field name, operation) to narrow matches and avoid performance issues.
Note: Field access operations are `view` and `edit` (not `update`/`delete` as with entity-level access).
## Fields
For any successor of this event, the account that asks for access is available under the [account] token. Example: [account:uid] provides the user ID of the account.
Furthermore, following data of the event is available:- [event:operation] holds the requested operation, such as "view".
- [event:field] holds the machine name of the field.
`Restrict by entity type ID` : Example: *node, taxonomy_term, user*
```
Restricts to the specified entity type (e.g., `node`, `taxonomy_term`). Comma-separated for multiple values.
```
`Restrict by entity bundle` : Example: *article, tags*
```
Restricts to the specified bundle (e.g., `article`, `tags`). Comma-separated for multiple values.
```
`Restrict by operation` : Example: *view, edit*
```
Restricts to the specified operation. For field access, the operations are `view` and `edit` (not `update` or `delete`).
```
`Restrict by field name` : Example: *title, body, field_myfield*
```
Restricts to the specified field machine name (e.g., `title`, `body`, `field_image`). Comma-separated for multiple values. Highly recommended to set this to avoid triggering on every field access check.
```
# ECA Base
Provides some basic events, conditions and actions, like using the state functionality of Drupal. Furthermore, this module contains:
- general conditions for comparison
- setting tokens
- event trigger
- counting list items
- etc.
## Installation
```
composer require drupal/eca
drush pm:install eca_base
```
Instead of using Drush to enable the module, you can also go to "Administration / Extend" (`/admin/modules`) and enable the module **ECA Base** from there.
# List: count items
Available since: 1.0.0
Counts the items in a list based on several properties.
## Fields
`Name of token` : Provide the name of a new token where the counting result should be stored. Please provide the token name only, without brackets.
`Token containing the list` : Provide the name of the token that contains a list from which the number of items should be counted. Please provide the token name only, without brackets.
# Display an error message to the user
Available since: 1.1.0
## Fields
`Message` : The message to be displayed to the current user. You may include placeholders like [node:title], [user:account-name], [user:display-name] and [comment:body] to represent data that will be different each time message is sent. Not all placeholders will be available in all contexts.
`Replace tokens` : When enabled, tokens will be replaced *before* executing the action. **Please note:** Actions might already take care of replacing tokens on their own. Therefore, use this option only with care and when it makes sense.
# Expirable key value store: delete
Available since: 2.1.5
Deletes a value from the Drupal expirable key value store by the given key.
## Fields
`Collection` : The collection of the store.
`Store key` : The key of the value in the store.
# Expirable key value store: read
Available since: 2.0.0
Reads a value from the Drupal expirable key value store by the given key. The result is stored in a token.
## Fields
`Collection` : The collection of the store.
`Store key` : The key of the value in the store.
`Name of token` : The name of the token, the value is stored into. Please provide the token name only, without brackets.
# Expirable key value store: write
Available since: 2.0.0
Writes a value to the Drupal expirable key value store by the given key.
## Fields
`Expiration` : The time to live for this item, in seconds.
`Collection` : The collection of the store.
`Store key` : The key of the value in the store.
`Value` : The value to store.
`Interpret above value as YAML format` : Nested data can be set using YAML format, for example *mykey: "My value"*. When using this format, this option needs to be enabled. When using tokens and YAML altogether, make sure that tokens are wrapped as a string. Example: *title: "[node:title]"*
`Validate YAML to prevent this from being executed when invalid`
`Only if new` : If enabled, this only stores the value if the key does not exist yet.
# Key value store: delete
Available since: 2.1.5
Delete a value from the Drupal key value store by the given key.
## Fields
`Collection` : The collection of the store.
`Store key` : The key of the value in the store.
# Key value store: read
Available since: 2.0.0
Reads a value from the Drupal key value store by the given key. The result is stored in a token.
## Fields
`Collection` : The collection of the store.
`Store key` : The key of the value in the store.
`Name of token` : The name of the token, the value is stored into. Please provide the token name only, without brackets.
# Key value store: write
Available since: 2.0.0
Write a value to the Drupal key value store by the given key.
## Fields
`Collection` : The collection of the store.
`Store key` : The key of the value in the store.
`Value` : The value to store.
`Interpret above value as YAML format` : Nested data can be set using YAML format, for example *mykey: "My value"*. When using this format, this option needs to be enabled. When using tokens and YAML altogether, make sure that tokens are wrapped as a string. Example: *title: "[node:title]"*
`Validate YAML to prevent this from being executed when invalid`
`Only if new` : If enabled, this only stores the value if the key does not exist yet.
# List: add item
Available since: 1.1.0
Add an item to a list using a specified token.
## Fields
`Value to add` : This field supports tokens.
`Method` : Options of the specific method to add a value to a list.\
When using the "Defined by token" option, make sure there is a token with this name: *eca_list_add_method*
`Index key` : When using the method *Set by specified index*, then an index key must be specified here.\
This field supports tokens.
`Token containing the list` : Provide the name of the token that contains a list of items. If the list does not exist yet, a new one will be created. Please provide the token name only, without brackets.
# List: compare items
Compares the items in two simple lists (contained in tokens), returning the array of results.
## Fields
`Token containing the secondary list` : Provide the name of the token that contains the secondary list in the comparison. Please provide the token name only, without brackets.
`Array Function` : Returns an array of items found by the [Array Function](https://www.php.net/manual/en/ref.array.php) selected.
`Name of result token` : Provide the name of a new token where the resulting array should be stored. Please provide the token name only, without brackets.
`Token containing the primary list` : Provide the name of the token that contains the primary list in the comparison. Please provide the token name only, without brackets.
# List: delete data
Available since: 1.1.0
Transaction to delete contained data of a list from the database.
## Fields
`Token containing the list` : Provide the name of the token that contains a list of items. If the list does not exist yet, a new one will be created. Please provide the token name only, without brackets.
# List: remove item
Available since: 1.1.0
Remove an item from a list and optionally store the item as a token.
## Fields
`Value to remove` : When *Drop by specified value* is selected above, then a value must be specified here.\
This field supports tokens.
`Name of token` : Provide the name of a token that holds the removed item. Please provide the token name only, without brackets.
`Method` : When using the "Defined by token" option, make sure there is a token with this name: *eca_list_remove_method*
`Index key` : When using the method *Drop by specified index key*, then an index key must be specified here.\
This field supports tokens.
`Token containing the list` : Provide the name of the token that contains a list of items. If the list does not exist yet, a new one will be created. Please provide the token name only, without brackets.
# List: save data
Available since: 1.1.0
Transaction to save contained data of a list into the database.
## Fields
`Token containing the list` : Provide the name of the token that contains a list of items. If the list does not exist yet, a new one will be created. Please provide the token name only, without brackets.
# List: sort items
Available since: 3.1.3
Sorts a list.
## Fields
`Sort method`
`Name of token` : The sorted list will be loaded into this specified token. If this is not provided, the list will be sorted in-place.
`Token containing the list` : Provide the name of the token that contains a list of items. If the list does not exist yet, a new one will be created. Please provide the token name only, without brackets.
# Lock: acquire
Available since: 1.1.0
Acquires a lock.
## Fields
`Lock name` : The name identifies a lock, that may be shared by multiple processes. The lock will be automatically released when this ECA process is finished.\
This field supports tokens.
`Lock timeout in seconds` : This timeout is used as a maximum time threshold. When exceeded, the lock is automatically released for other processed again.
`Wait for lock until timeout exceeded` : When checked, this action automatically retries acquiring the lock, until the specified timeout above exceeded.
`Name of token` : Optionally define a token name to store the result of lock acquisition. The result value is **1** when acquisition was successful, and is set to **0** when it was not successful. Please provide the token name only, without brackets.
# Private temporary store: delete
Available since: 2.1.5
Delete a value from the Drupal private temporary store by the given key.
## Fields
`Collection` : The collection of the store.
`Store key` : The key of the value in the store.
# Private temporary store: read
Available since: 2.0.0
Reads a value from the Drupal private temporary store by the given key. The result is stored in a token.
## Fields
`Collection` : The collection of the store.
`Store key` : The key of the value in the store.
`Name of token` : The name of the token, the value is stored into. Please provide the token name only, without brackets.
# Private temporary store: write
Available since: 2.0.0
Write a value to the Drupal private temporary store by the given key.
## Fields
`Collection` : The collection of the store.
`Store key` : The key of the value in the store.
`Value` : The value to store.
`Interpret above value as YAML format` : Nested data can be set using YAML format, for example *mykey: "My value"*. When using this format, this option needs to be enabled. When using tokens and YAML altogether, make sure that tokens are wrapped as a string. Example: *title: "[node:title]"*
`Validate YAML to prevent this from being executed when invalid`
# Set ECA log level
Available since: 2.0.0
The log level for ECA can be changed temporarily for the processing within the current event.
## Fields
`Log level` : When using the "Defined by token" option, make sure there is a token with this name: *eca_set_eca_log_level_log_level*
# Set field widget value
Available since: 2.1.11
This action sets the value for the field widget event.
## Fields
`Field widget value` : This field supports tokens.
# Set tool output
Available since: 2.1.11
This action sets the output for the tool event.
## Fields
`Tool output` : This field supports tokens.
`Interpret above config value as YAML format` : Nested data can be set using YAML format, for example *front: /node*. When using this format, this option needs to be enabled. When using tokens and YAML altogether, make sure that tokens are wrapped as a string. Example: *front: "[myurl:path]"*
`Validate YAML to prevent this from being executed when invalid`
# Shared temporary store: delete
Available since: 2.1.5
Delete a value from the Drupal shared temporary store by the given key.
## Fields
`Collection` : The collection of the store.
`Store key` : The key of the value in the store.
# Shared temporary store: read
Available since: 2.0.0
Reads a value from the Drupal shared temporary store by the given key. The result is stored in a token.
## Fields
`Collection` : The collection of the store.
`Store key` : The key of the value in the store.
`Name of token` : The name of the token, the value is stored into. Please provide the token name only, without brackets.
# Shared temporary store: write
Available since: 2.0.0
Write a value to the Drupal shared temporary store by the given key.
## Fields
`Collection` : The collection of the store.
`Store key` : The key of the value in the store.
`Value` : The value to store.
`Interpret above value as YAML format` : Nested data can be set using YAML format, for example *mykey: "My value"*. When using this format, this option needs to be enabled. When using tokens and YAML altogether, make sure that tokens are wrapped as a string. Example: *title: "[node:title]"*
`Validate YAML to prevent this from being executed when invalid`
# Persistent state: read
Available since: 1.1.0
Reads a value from the Drupal state by the given key. The result is stored in a token.
Reads a value from the Drupal state by the given key. The result is stored in a token.
## Fields
`State key` : The key of the Drupal state.\
This field supports tokens.
`Name of token` : The name of the token, the value is stored into. Please provide the token name only, without brackets.
# Persistent state: write
Available since: 1.1.0
Writes a value into the Drupal state system by the given key.
Writes a key/value pair to the Drupal state. The value can be stored in a token.
## Fields
`State key` : The key of the Drupal state.\
This field supports tokens.
`The value of the state` : The key, where the value is stored into.\
This field supports tokens.
`Interpret above config value as YAML format` : Nested data can be set using YAML format, for example *mykey: myvalue*. When using this format, this option needs to be enabled. When using tokens and YAML altogether, make sure that tokens are wrapped as a string. Example: *title: "[node:title]"*
`Validate YAML to prevent this from being executed when invalid`
# Token: replace
Available since: 2.0.0
Replace all tokens, using a recursive strategy.
## Fields
`Name of token` : Provide the name of a token where the value should be stored. Please provide the token name only, without brackets.
`Value of the token` : The value of the token.\
This field supports tokens.
`Interpret above value as YAML format` : Nested data can be set using YAML format, for example *mykey: "My value"*. When using this format, this option needs to be enabled. When using tokens and YAML altogether, make sure that tokens are wrapped as a string. Example: *title: "[node:title]"*
`Validate YAML to prevent this from being executed when invalid`
# Token: set context
Available since: 1.1.0
Set currently defined token data to be available for any child process.
# Token: set random value
Available since: 2.1.0
Create a random value and store it in a token.
## Fields
`Name of token` : Provide the name of a token where the value should be stored. Please provide the token name only, without brackets.
`Mode` : The type of random value to generate.\
When using the "Defined by token" option, make sure there is a token with this name: *eca_token_set_random_value_mode*
`Length or min/max` : The length or min/max range of the random value. For sentences, this value determines the number of words and for paragraphs it determines the number of paragraphs. For integer mode, please provide comma separated values for minimum and maximum value. Missing values default to PHPs built in mix and max integer values. For the image mode, please provide a comma separated min and max resolution, defaults to "100x100,1920x1080". For the image mode, the token will contain the path to the generated random image file.\
This field supports tokens.
# Token: set value
Available since: 1.1.0
Define a locally available token by a specific name and value.
## Fields
`Name of token` : Provide the name of a token where the value should be stored. Please provide the token name only, without brackets.
`Value of the token` : The value of the token.\
This field supports tokens.
`Interpret above value as YAML format` : Nested data can be set using YAML format, for example *mykey: "My value"*. When using this format, this option needs to be enabled. When using tokens and YAML altogether, make sure that tokens are wrapped as a string. Example: *title: "[node:title]"*
`Validate YAML to prevent this from being executed when invalid`
# Translate
Available since: 1.1.0
Translate a given value, and store the translated value as a token.
## Fields
`Name of token` : The translation result will be stored in this token. Please provide the token name only, without brackets.
`Value to translate`
`Interpret above value as YAML format` : Nested data can be set using YAML format, for example *mykey: "My value"*. When using this format, this option needs to be enabled. When using tokens and YAML altogether, make sure that tokens are wrapped as a string. Example: *title: "[node:title]"*
`Validate YAML to prevent this from being executed when invalid`
`Target language` : Define the language that the given value should be translated to.\
When using the "Defined by token" option, make sure there is a token with this name: *eca_translate_target_langcode*
# Trigger a custom event
Available since: 1.0.0
Triggers a custom event by a specific id.
Note: If an entity is involved, you have to use [Custom Event Entity Aware](https://ecaguide.org/plugins/eca/content/events/content_entity_custom/index.md).
## Fields
`Event ID` : The ID of the event to be triggered.
`Tokens to forward` : Comma separated list of token names from the current context, that will be forwarded to the triggered event. These tokens are then also available for subsequent conditions and actions within the current process.
# Chain action for AND condition
Available since: 1.0.0
This action chains other actions with an explicit AND condition.
# Display a warning message to the user
Available since: 1.1.0
## Fields
`Message` : The message to be displayed to the current user. You may include placeholders like [node:title], [user:account-name], [user:display-name] and [comment:body] to represent data that will be different each time message is sent. Not all placeholders will be available in all contexts.
`Replace tokens` : When enabled, tokens will be replaced *before* executing the action. **Please note:** Actions might already take care of replacing tokens on their own. Therefore, use this option only with care and when it makes sense.
# Compare number of list items
Available since: 1.0.0
Condition to compare the number of list items.
## Fields
`Name of token containing the list` : Provide the name of the token that contains a list from which the number of items should be counted.
`Second value` : The second value to be compared.
```
The list to be counted.
```
`Comparison operator` : The available comparison operators like *equals* or *less than*.\
When using the "Defined by token" option, make sure there is a token with this name: *eca_count_operator*
```
Comparison operator. Possible values:
```
- equal
- greater than
- less than
- starts with
- ends with
- contains
- at least
- at most
`Comparison type` : The type of the comparison.\
When using the "Defined by token" option, make sure there is a token with this name: *eca_count_type*
`Case sensitive comparison` : Compare the values based on case sensitivity.
`Negate the condition` : Negates the condition. Makes TRUE to FALSE and vice versa.
# List: contains item
Available since: 2.0.0
Evaluate whether a certain item is in a list.
## Fields
`Name of token containing the list` : Provide the name of the token that contains a list of items. Please provide the token name only, without brackets.
`Method` : When using the "Defined by token" option, make sure there is a token with this name: *eca_list_contains_method*
`Value to lookup`
`Negate the condition` : Negates the condition. Makes TRUE to FALSE and vice versa.
# Compare two scalar values
Available since: 1.0.0
Compares two scalar values based on several conditions.
Compares two scalar values based on several conditions.
## Fields
`First value` : The first value to be compared.
`Second value` : The second value to be compared.
`Comparison operator` : The available comparison operators like *equals* or *less than*.\
When using the "Defined by token" option, make sure there is a token with this name: *eca_scalar_operator*
```
Comparison operator. Possible values:
```
- equal
- greater than
- less than
- starts with
- ends with
- contains
- at least
- at most
`Comparison type` : The type of the comparison.\
When using the "Defined by token" option, make sure there is a token with this name: *eca_scalar_type*
`Case sensitive comparison` : Compare the values based on case sensitivity.
`Negate the condition` : Negates the condition. Makes TRUE to FALSE and vice versa.
# Persistent state: compare
Available since: 1.0.0
Compares a state value by a given key.
## Fields
`Key` : The key of the state.
`Value` : The value of the state.
`Comparison operator` : The available comparison operators like *equals* or *less than*.\
When using the "Defined by token" option, make sure there is a token with this name: *eca_state_operator*
```
Comparison operator. Possible values:
```
- equal
- greater than
- less than
- starts with
- ends with
- contains
- at least
- at most
`Comparison type` : The type of the comparison.\
When using the "Defined by token" option, make sure there is a token with this name: *eca_state_type*
`Case sensitive comparison` : Compare the values based on case sensitivity.
`Negate the condition` : Negates the condition. Makes TRUE to FALSE and vice versa.
# Token: exists
Available since: 1.1.0
## Fields
`Name of token` : Provide the name of the token to check for. Please provide the token name only, without brackets.
`Negate the condition` : Negates the condition. Makes TRUE to FALSE and vice versa.
# ECA cron event
Available since: 1.0.0
Provided tokens
| Token | Description |
| ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `[user]` | The current user. Alias: `current_user` |
| `[htmx]` | Information about the current HTMX request. |
| `[htmx:is_request]` | Whether the current request was sent by HTMX ("1" or "0"). |
| `[htmx:boosted]` | Whether the current request was boosted by HTMX ("1" or "0"). |
| `[htmx:history_restore]` | Whether the current request is for HTMX history restoration ("1" or "0"). |
| `[htmx:target]` | The value of the HX-Target request header. |
| `[htmx:trigger]` | The value of the HX-Trigger request header. |
| `[htmx:trigger_name]` | The value of the HX-Trigger-Name request header. |
| `[htmx:current_url]` | The value of the HX-Current-URL request header. |
| `[htmx:prompt]` | The value of the HX-Prompt request header. |
| `[event]` | The event. |
| `[event:machine_name]` | The machine name of the ECA event. |
| `[session_user]` | The user account that dispatched the event, regardless if ECA is processing models under a different account. This is only available if ECA is configured to always run under a specific account. |
This event reacts to Drupal's cron system and allows you to schedule ECA processes at specific intervals.
### How it works
Drupal core provides a cron API that all modules can hook into. When cron runs, core asks every registered module — including ECA — whether it has work to do. ECA then checks the **Frequency** field of each cron event in your models to determine whether that particular event is due for execution. If it is, the model's actions are processed; otherwise, the event is skipped.
This means:
1. **System-level cron must run first.** The ECA cron event does not trigger itself — it only fires when Drupal's cron runs. On production sites, it is strongly recommended to disable the `automated_cron` module and instead run `drush cron` from a system-level crontab (e.g. every minute or at least every 5 minutes).
1. **The Frequency field is a schedule filter, not a trigger.** Setting a frequency of `0 1 * * 1` (every Sunday at 1:00 UTC) does not cause ECA to run at that time by itself. It tells ECA to only execute this event *when cron happens to run* at or after the scheduled time. The more frequently your system cron runs, the more precisely ECA can honor the frequency you define.
1. **Cron runs as the anonymous user.** Unless configured otherwise, cron processes run without an authenticated session. If your actions require specific permissions (e.g. querying a view with access restrictions), add a [User: switch current account](https://ecaguide.org/plugins/eca/user/actions/eca_switch_account/index.md) action immediately after this event to switch to an account with the necessary permissions.
Avoid rendering actions in cron context
Actions that render HTML (such as rendering a view) may fail during cron because there is no active HTTP session. You might see errors like *"Failed to start the session because headers have already been sent"*. Consider using non-rendering alternatives or ensure your model accounts for the headless cron context.
## Fields
`Frequency` : The frequency of a cron job is defined by a cron specific notation which is best explained at https://en.wikipedia.org/wiki/Cron. Note: date and time need to be provided in UTC timezone.
```
This field defines *when* the ECA cron event should be considered due for
execution. It does **not** control when Drupal's system cron runs — that is
configured separately at the server or Drupal level.
```
Each time system cron runs, ECA evaluates this frequency expression to decide whether the event should fire. If the current time matches the schedule, the event's actions are executed; otherwise, the event is skipped until the next cron run.
**Examples:**
| Expression | Meaning |
| ------------- | -------------------------------------------------------------------------------- |
| `* * * * *` | Every time cron runs (effectively every minute if system cron runs every minute) |
| `0 */1 * * *` | Once per hour, at the top of the hour |
| `0 2 * * *` | Daily at 02:00 UTC |
| `0 1 * * 1` | Every Monday at 01:00 UTC |
| `0 0 1 * *` | First day of every month at midnight UTC |
Precision depends on system cron frequency
If your system cron runs only every 15 minutes, an ECA frequency of `*/5 * * * *` (every 5 minutes) will not actually fire every 5 minutes. ECA can only evaluate the schedule when cron runs, so the effective precision is limited by how often system cron is triggered.
# ECA custom event
Available since: 1.0.0
Provided tokens
| Token | Description |
| ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `[user]` | The current user. Alias: `current_user` |
| `[htmx]` | Information about the current HTMX request. |
| `[htmx:is_request]` | Whether the current request was sent by HTMX ("1" or "0"). |
| `[htmx:boosted]` | Whether the current request was boosted by HTMX ("1" or "0"). |
| `[htmx:history_restore]` | Whether the current request is for HTMX history restoration ("1" or "0"). |
| `[htmx:target]` | The value of the HX-Target request header. |
| `[htmx:trigger]` | The value of the HX-Trigger request header. |
| `[htmx:trigger_name]` | The value of the HX-Trigger-Name request header. |
| `[htmx:current_url]` | The value of the HX-Current-URL request header. |
| `[htmx:prompt]` | The value of the HX-Prompt request header. |
| `[event]` | The event. |
| `[event:machine_name]` | The machine name of the ECA event. |
| `[VARIOUS]` | All tokens forwarded by the dispatcher. |
| `[session_user]` | The user account that dispatched the event, regardless if ECA is processing models under a different account. This is only available if ECA is configured to always run under a specific account. |
Event listening to a custom event.
Note: If an entity is involved, you have to use [Custom Event Entity Aware](https://ecaguide.org/plugins/eca/content/events/content_entity_custom/index.md).
## Fields
`Event ID` : The custom event ID. Leave empty to trigger all custom events.
```
The custom event ID to listen for. Leave empty to trigger on all custom events.
```
# ECA Field Widget
Available since: 2.1.11
Provided tokens
| Token | Description |
| ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `[entity]` | The entity being edited. |
| `[field_name]` | The name of the entity field for which the field widget is used. |
| `[field_key]` | The index of the field starting with zero and going up if it is a multi-value field. |
| `[event]` | The event. |
| `[event:machine_name]` | The machine name of the ECA event. |
| `[session_user]` | The user account that dispatched the event, regardless if ECA is processing models under a different account. This is only available if ECA is configured to always run under a specific account. |
This event has currently no configuration options. However, you may want to at least give it a label because that's how it will be displayed in the form display configuration.
To get a field widget action from ECA work with form fields, you start with this event and the subsequent actions and conditions receive the above tokens with which you can build your process.
To return a value back to the field widget action, which will put that into the related form field, you can either change the value in the provided entity or you can use the [Set field widget value](https://ecaguide.org/plugins/eca/base/actions/eca_set_field_widget_value/index.md) action to set that value.
# ECA token generate event
Available since: 2.0.0
Provided tokens
| Token | Description |
| ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `[user]` | The current user. Alias: `current_user` |
| `[htmx]` | Information about the current HTMX request. |
| `[htmx:is_request]` | Whether the current request was sent by HTMX ("1" or "0"). |
| `[htmx:boosted]` | Whether the current request was boosted by HTMX ("1" or "0"). |
| `[htmx:history_restore]` | Whether the current request is for HTMX history restoration ("1" or "0"). |
| `[htmx:target]` | The value of the HX-Target request header. |
| `[htmx:trigger]` | The value of the HX-Trigger request header. |
| `[htmx:trigger_name]` | The value of the HX-Trigger-Name request header. |
| `[htmx:current_url]` | The value of the HX-Current-URL request header. |
| `[htmx:prompt]` | The value of the HX-Prompt request header. |
| `[event]` | The event. |
| `[event:machine_name]` | The machine name of the ECA event. |
| `[session_user]` | The user account that dispatched the event, regardless if ECA is processing models under a different account. This is only available if ECA is configured to always run under a specific account. |
Fires when a token matching the configured pattern is encountered during Drupal's token replacement process. This allows providing dynamic token values on demand.
Performance consideration
Token replacement happens very frequently across the system (emails, paths, fields, etc.). Keep ECA reactions lightweight to avoid performance issues.
## Fields
`Name of token` : Example: *mygroup:value*. Wildcards are supported, e.g. to react upon all "mygroup" tokens: *mygroup:\** Please provide the token name only, without brackets.
```
The token pattern to react upon (e.g., `mygroup:value`). Supports wildcards: `mygroup:*` reacts to all tokens in the 'mygroup' group.
```
# ECA Tool
Available since: 2.1.11
Provided tokens
| Token | Description |
| ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `[event]` | The event. |
| `[event:machine_name]` | The machine name of the ECA event. |
| `[VARIOUS]` | All tokens forwarded by the dispatcher. |
| `[session_user]` | The user account that dispatched the event, regardless if ECA is processing models under a different account. This is only available if ECA is configured to always run under a specific account. |
Defines an ECA-based tool that can be invoked by an AI agent or other automated system. The tool description and arguments are critically important for AI agents to understand when and how to use the tool.
## Fields
`Description` : Summarizes what this tool is for. This plain text property is particularly important to the AI agent because it provides context and explicitly defines the use case using natural language.
```
A plain-text description of what this tool does. This is critically important for AI agents as they use it to decide when and how to invoke the tool.
```
`Arguments` : The arguments that the AI agent passes into the tool. They must be written in YAML format. This property is also important to the AI agent. Note that the top-level keys (e.g. node, user) become the names of the tokens that are then available to successors of this event.
```
Provide the list of arguments in YAML format so that the caller of this tool
know which context need to be made available:
```
```
node:
data_type: "entity:node:article"
label: "The article to process"
required: true
description: "The article that should be investigated in the format {node:nid}, so for example node:2."
user:
data_type: "entity:user"
label: "The user"
required: true
description: "Provide the user in the format {user:uid} so that their name can be determined and verified if they are the author of the article, e.g. user:5."
```
Each argument has a name, e.g. "node" or "user" in the above example. For each of them, provide the data type, a label, a description, and a flag whether that argument is required or not.
Those arguments will be available as tokens to the subsequent conditions and actions following that event.
# ECA Cache
Module with actions to read, write and invalidate a cache from a specific cache backend.
## Installation
```
composer require drupal/eca
drush pm:install eca_cache
```
Instead of using Drush to enable the module, you can also go to "Administration / Extend" (`/admin/modules`) and enable the module **ECA Cache** from there.
# Cache ECA: invalidate
Available since: 2.0.0
Invalidates a part or the whole ECA cache.
## Fields
`Cache tags` : Optionally define cache tags for fine-granular cache invalidation. Separate multiple tags with commas. More information about cache tags can be found in the [documentation](https://www.drupal.org/docs/drupal-apis/cache-api/cache-tags). When empty, then the whole cache is being invalidated.
`Cache backend` : When using the "Defined by token" option, make sure there is a token with this name: *eca_cache_invalidate_backend*
```
The following cache backends are available:
```
- default shared cache
- runtime in-memory cache
- chained cache (in-memory plus shared)
`Cache key` : The cache key is a unique machine name and identifies the cache item.\
This field supports tokens.
# Cache ECA: read
Available since: 2.0.0
Read a value item from ECA cache and store it as a token.
## Fields
`Name of token` : The cache item value will be loaded into this specified token. Please provide the token name only, without brackets.
`Cache backend` : When using the "Defined by token" option, make sure there is a token with this name: *eca_cache_read_backend*
```
The following cache backends are available:
```
- default shared cache
- runtime in-memory cache
- chained cache (in-memory plus shared)
`Cache key` : The cache key is a unique machine name and identifies the cache item.\
This field supports tokens.
# Cache ECA: write
Available since: 2.0.0
Write a value item into ECA cache.
## Fields
`Cache item value` : The value to cache.\
This field supports tokens.
`Interpret above config value as YAML format` : Nested data can be set using YAML format, for example *mykey: myvalue*. When using this format, this option needs to be enabled. When using tokens and YAML altogether, make sure that tokens are wrapped as a string. Example: *title: "[node:title]"*
`Validate YAML to prevent this from being executed when invalid`
`Expiration time` : The timestamp in seconds when the cached item expires. Set to -1 for unlimited lifetime.\
This field supports tokens.
`Cache tags` : Optionally add cache tags for fine-granular cache invalidation. Separate multiple tags with commas. More information about cache tags can be found in the [documentation](https://www.drupal.org/docs/drupal-apis/cache-api/cache-tags).\
This field supports tokens.
`Cache backend` : When using the "Defined by token" option, make sure there is a token with this name: *eca_cache_write_backend*
```
The following cache backends are available:
```
- default shared cache
- runtime in-memory cache
- chained cache (in-memory plus shared)
`Cache key` : The cache key is a unique machine name and identifies the cache item.\
This field supports tokens.
# Page Cache Kill Switch
Available since: 2.0.0
Kill the page cache for this page
# Cache Raw: invalidate
Available since: 2.0.0
Invalidates a part or the whole raw cache.
## Fields
`Cache tags` : Optionally define cache tags for fine-granular cache invalidation. Separate multiple tags with commas. More information about cache tags can be found in the [documentation](https://www.drupal.org/docs/drupal-apis/cache-api/cache-tags). When empty, then the whole cache is being invalidated.
# Cache Raw: read
Available since: 2.0.0
Read a value item from raw cache and store it as a token.
## Fields
`Name of token` : The cache item value will be loaded into this specified token. Please provide the token name only, without brackets.
`Cache backend` : When using the "Defined by token" option, make sure there is a token with this name: *eca_raw_cache_read_backend*
```
The following cache backends are available:
```
- default shared cache
- runtime in-memory cache
- chained cache (in-memory plus shared)
`Cache key` : The cache key is a unique machine name and identifies the cache item.\
This field supports tokens.
# Cache Raw: write
Available since: 2.0.0
Write a value item into raw cache.
## Fields
`Cache item value` : The value to cache.\
This field supports tokens.
`Interpret above config value as YAML format` : Nested data can be set using YAML format, for example *mykey: myvalue*. When using this format, this option needs to be enabled. When using tokens and YAML altogether, make sure that tokens are wrapped as a string. Example: *title: "[node:title]"*
`Validate YAML to prevent this from being executed when invalid`
`Expiration time` : The timestamp in seconds when the cached item expires. Set to -1 for unlimited lifetime.\
This field supports tokens.
`Cache tags` : Optionally add cache tags for fine-granular cache invalidation. Separate multiple tags with commas. More information about cache tags can be found in the [documentation](https://www.drupal.org/docs/drupal-apis/cache-api/cache-tags).\
This field supports tokens.
`Cache backend` : When using the "Defined by token" option, make sure there is a token with this name: *eca_raw_cache_write_backend*
```
The following cache backends are available:
```
- default shared cache
- runtime in-memory cache
- chained cache (in-memory plus shared)
`Cache key` : The cache key is a unique machine name and identifies the cache item.\
This field supports tokens.
# ECA Commerce
## Installation
```
composer require drupal/eca_commerce
drush pm:install eca_commerce
```
Instead of using Drush to enable the module, you can also go to "Administration / Extend" (`/admin/modules`) and enable the module **ECA Commerce** from there.
# Order Item: Add Price Adjustment
Available since: 1.0.0
## Fields
`Method` : The method to set an entity, like cleaning the old one, etc..
`Type`
`Locked` : Note: Adjustments added from UI interactions need to be locked to persist after an order refresh.
`Label` : This field supports tokens.
`Amount` : This field supports tokens.
`Currency` : When using the "Defined by token" option, make sure there is a token with this name: *eca_commerce_add_adjustment_currency*
`Percentage` : This field supports tokens.
`Included in the base price`
`Save entity` : Saves the entity or not after setting the value.
# Change Price in Cart
Available since: 1.0.0
## Fields
`Final Price` : This should either be a number or a token that outputs a number or price field. This only supports USD.
# Commerce: Billing address
Available since: 1.0.0
## Fields
`operator`
`zone`
`Negate the condition` : Negates the condition. Makes TRUE to FALSE and vice versa.
`Entity` : Provide the token name of the *entity* that this condition should operate with. Please provide the token name only, without brackets.
# Commerce: Order currency
Available since: 1.0.0
## Fields
`Currencies` : Provide a comma separated list of entity IDs.
`Negate the condition` : Negates the condition. Makes TRUE to FALSE and vice versa.
`Entity` : Provide the token name of the *entity* that this condition should operate with. Please provide the token name only, without brackets.
# Commerce: Customer ID
Available since: 1.0.0
## Fields
`Customers` : Provide a comma separated list of entity IDs.
`Negate the condition` : Negates the condition. Makes TRUE to FALSE and vice versa.
`Entity` : Provide the token name of the *entity* that this condition should operate with. Please provide the token name only, without brackets.
# Commerce: Customer role
Available since: 1.0.0
## Fields
`Negate the condition` : Negates the condition. Makes TRUE to FALSE and vice versa.
`Entity` : Provide the token name of the *entity* that this condition should operate with. Please provide the token name only, without brackets.
# Commerce: Customer email
Available since: 1.0.0
## Fields
`Email`
`Negate the condition` : Negates the condition. Makes TRUE to FALSE and vice versa.
`Entity` : Provide the token name of the *entity* that this condition should operate with. Please provide the token name only, without brackets.
# Commerce: Specific products
Available since: 1.0.0
## Fields
`Products` : Provide a comma separated list of entity IDs.
`Negate the condition` : Negates the condition. Makes TRUE to FALSE and vice versa.
`Entity` : Provide the token name of the *entity* that this condition should operate with. Please provide the token name only, without brackets.
# Commerce: Product categories
Available since: 1.0.0
## Fields
`Categories` : Provide a comma separated list of entity IDs.
`Negate the condition` : Negates the condition. Makes TRUE to FALSE and vice versa.
`Entity` : Provide the token name of the *entity* that this condition should operate with. Please provide the token name only, without brackets.
# Commerce: Product types
Available since: 1.0.0
## Fields
`Product types` : Provide a comma separated list of entity IDs.
`Negate the condition` : Negates the condition. Makes TRUE to FALSE and vice versa.
`Entity` : Provide the token name of the *entity* that this condition should operate with. Please provide the token name only, without brackets.
# Commerce: Specific product variation
Available since: 1.0.0
## Fields
`Negate the condition` : Negates the condition. Makes TRUE to FALSE and vice versa.
`Entity` : Provide the token name of the *entity* that this condition should operate with. Please provide the token name only, without brackets.
# Commerce: Total discounted product quantity
Available since: 1.0.0
## Fields
`Operator`
`Quantity`
`Negate the condition` : Negates the condition. Makes TRUE to FALSE and vice versa.
`Entity` : Provide the token name of the *entity* that this condition should operate with. Please provide the token name only, without brackets.
# Commerce: Product variation types
Available since: 1.0.0
## Fields
`Product variation types` : Provide a comma separated list of entity IDs.
`Negate the condition` : Negates the condition. Makes TRUE to FALSE and vice versa.
`Entity` : Provide the token name of the *entity* that this condition should operate with. Please provide the token name only, without brackets.
# Commerce: Selected payment gateway
Available since: 1.0.0
## Fields
`Payment gateways` : Provide a comma separated list of entity IDs.
`Negate the condition` : Negates the condition. Makes TRUE to FALSE and vice versa.
`Entity` : Provide the token name of the *entity* that this condition should operate with. Please provide the token name only, without brackets.
# Commerce: Order contains specific products
Available since: 1.0.0
## Fields
`Products` : Provide a comma separated list of entity IDs.
`Negate the condition` : Negates the condition. Makes TRUE to FALSE and vice versa.
`Entity` : Provide the token name of the *entity* that this condition should operate with. Please provide the token name only, without brackets.
# Commerce: Order contains product categories
Available since: 1.0.0
## Fields
`Categories` : Provide a comma separated list of entity IDs.
`Negate the condition` : Negates the condition. Makes TRUE to FALSE and vice versa.
`Entity` : Provide the token name of the *entity* that this condition should operate with. Please provide the token name only, without brackets.
# Commerce: Order contains product types
Available since: 1.0.0
## Fields
`Product types` : Provide a comma separated list of entity IDs.
`Negate the condition` : Negates the condition. Makes TRUE to FALSE and vice versa.
`Entity` : Provide the token name of the *entity* that this condition should operate with. Please provide the token name only, without brackets.
# Commerce: Order contains specific product variations
Available since: 1.0.0
## Fields
`Negate the condition` : Negates the condition. Makes TRUE to FALSE and vice versa.
`Entity` : Provide the token name of the *entity* that this condition should operate with. Please provide the token name only, without brackets.
# Commerce: Shipping address
Available since: 1.0.0
## Fields
`operator`
`zone`
`Negate the condition` : Negates the condition. Makes TRUE to FALSE and vice versa.
`Entity` : Provide the token name of the *entity* that this condition should operate with. Please provide the token name only, without brackets.
# Commerce: Store
Available since: 1.0.0
## Fields
`Stores` : Provide a comma separated list of entity IDs.
`Negate the condition` : Negates the condition. Makes TRUE to FALSE and vice versa.
`Entity` : Provide the token name of the *entity* that this condition should operate with. Please provide the token name only, without brackets.
# Commerce: Current order total
Available since: 1.0.0
## Fields
`Operator`
`Amount`
`Total to compare against` : Totals used may include prior price adjustments.
`Negate the condition` : Negates the condition. Makes TRUE to FALSE and vice versa.
`Entity` : Provide the token name of the *entity* that this condition should operate with. Please provide the token name only, without brackets.
# Commerce: Order type
Available since: 1.0.0
## Fields
`Order type` : Provide a comma separated list of entity IDs.
`Negate the condition` : Negates the condition. Makes TRUE to FALSE and vice versa.
`Entity` : Provide the token name of the *entity* that this condition should operate with. Please provide the token name only, without brackets.
# Commerce: Order contains product variation types
Available since: 1.0.0
## Fields
`Product variation types` : Provide a comma separated list of entity IDs.
`Negate the condition` : Negates the condition. Makes TRUE to FALSE and vice versa.
`Entity` : Provide the token name of the *entity* that this condition should operate with. Please provide the token name only, without brackets.
# Cart: The cart was emptied
Available since: 1.0.0
Provided tokens
| Token | Description |
| ------------------------ | ------------------------------------------------------------------------------------------------------------- |
| `[cart]` | The cart entity. |
| `[commerce_order_items]` | An array of order item entities. |
| `[event]` | The event. |
| `[event:machine_name]` | The machine name of the ECA event. |
| `[session_user]` | The user account that dispatched the event, regardless if ECA is processing models under a different account. |
Reacts when all items are removed from a cart. The `[cart]` token provides the cart order entity, and `[commerce_order_items]` provides the removed order items. Fires before the cart order is saved.
# Cart: An entity was added to the cart
Available since: 1.0.0
Provided tokens
| Token | Description |
| ----------------------- | ------------------------------------------------------------------------------------------------------------- |
| `[cart]` | The cart entity. |
| `[commerce_order_item]` | The order item entity. |
| `[entity]` | This is a purchasable entity. |
| `[quantity]` | A float of the quantity. |
| `[event]` | The event. |
| `[event:machine_name]` | The machine name of the ECA event. |
| `[session_user]` | The user account that dispatched the event, regardless if ECA is processing models under a different account. |
Reacts when a purchasable entity (e.g., a product variation) is added to the cart. The `[cart]` token provides the cart order, `[entity]` the purchasable entity, `[commerce_order_item]` the destination order item, and `[quantity]` the quantity. Fires before the cart order is saved.
# Cart: Add order item to cart
Available since: 1.0.0
Provided tokens
| Token | Description |
| ----------------------- | ------------------------------------------------------------------------------------------------------------- |
| `[cart]` | The cart entity. |
| `[commerce_order_item]` | The order item entity. |
| `[quantity]` | A float of the quantity. |
| `[event]` | The event. |
| `[event:machine_name]` | The machine name of the ECA event. |
| `[session_user]` | The user account that dispatched the event, regardless if ECA is processing models under a different account. |
Reacts when an order item is added to the cart. The `[cart]` token provides the cart order, `[commerce_order_item]` the added order item, and `[quantity]` the quantity. Fires before the cart order is saved.
# Cart: Order item comparison fields
Available since: 1.0.0
Provided tokens
| Token | Description |
| ----------------------------------------- | ------------------------------------------------------------------------------------------------------------- |
| `[commerce_order_item]` | The order item entity. |
| `[commerce_order_item_comparison_fields]` | The fields to compare. |
| `[event]` | The event. |
| `[event:machine_name]` | The machine name of the ECA event. |
| `[session_user]` | The user account that dispatched the event, regardless if ECA is processing models under a different account. |
Reacts when determining which fields to compare when deciding if order items can be merged in the cart. Add custom field names to prevent merging when items differ in those fields (e.g., gift messages, custom engravings).
# Cart: Remove order item from cart
Available since: 1.0.0
Provided tokens
| Token | Description |
| ----------------------- | ------------------------------------------------------------------------------------------------------------- |
| `[cart]` | The cart entity. |
| `[commerce_order_item]` | The order item entity. |
| `[event]` | The event. |
| `[event:machine_name]` | The machine name of the ECA event. |
| `[session_user]` | The user account that dispatched the event, regardless if ECA is processing models under a different account. |
Reacts when an order item is removed from the cart. The `[cart]` token provides the cart order and `[commerce_order_item]` the removed item. Fires before the cart order is saved.
# Cart: Update order item on cart
Available since: 1.0.0
Provided tokens
| Token | Description |
| -------------------------------- | ------------------------------------------------------------------------------------------------------------- |
| `[cart]` | The cart entity. |
| `[commerce_order_item]` | The order item entity. |
| `[commerce_order_item_original]` | The original item data before the update. |
| `[event]` | The event. |
| `[event:machine_name]` | The machine name of the ECA event. |
| `[session_user]` | The user account that dispatched the event, regardless if ECA is processing models under a different account. |
Reacts when a cart order item is updated (e.g., quantity change). The `[cart]` token provides the cart order, `[commerce_order_item]` the updated item, and `[commerce_order_item_original]` the item before the update. Fires before the cart order is saved.
# Checkout: Completion
Available since: 1.0.0
Provided tokens
| Token | Description |
| ---------------------- | ------------------------------------------------------------------------------------------------------------- |
| `[commerce_order]` | The order entity. |
| `[event]` | The event. |
| `[event:machine_name]` | The machine name of the ECA event. |
| `[session_user]` | The user account that dispatched the event, regardless if ECA is processing models under a different account. |
Reacts when a customer completes the checkout process. The `[commerce_order]` token provides the completed order.
This is one of the most commonly used Commerce events, ideal for triggering post-purchase workflows such as sending custom notifications, updating inventory in external systems, or enrolling users in programs.
# Checkout: Completion register
Available since: 1.0.0
Provided tokens
| Token | Description |
| ---------------------- | ------------------------------------------------------------------------------------------------------------- |
| `[commerce_order]` | The order entity. |
| `[event]` | The event. |
| `[event:machine_name]` | The machine name of the ECA event. |
| `[session_user]` | The user account that dispatched the event, regardless if ECA is processing models under a different account. |
Reacts when an anonymous customer registers a new account during checkout completion. The `[commerce_order]` token provides the checkout order.
# Core: Filter conditions
Available since: 1.0.0
Provided tokens
| Token | Description |
| ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `[event]` | The event. |
| `[event:machine_name]` | The machine name of the ECA event. |
| `[session_user]` | The user account that dispatched the event, regardless if ECA is processing models under a different account. This is only available if ECA is configured to always run under a specific account. |
Reacts when Commerce filters the available condition plugins (e.g., for promotions or payment gateways). Useful for programmatically removing or adding condition plugins.
# Order: Assign order
Available since: 1.0.0
Provided tokens
| Token | Description |
| ---------------------- | ------------------------------------------------------------------------------------------------------------- |
| `[commerce_customer]` | The current customer, this is a Drupal User. |
| `[commerce_order]` | The order entity. |
| `[event]` | The event. |
| `[event:machine_name]` | The machine name of the ECA event. |
| `[session_user]` | The user account that dispatched the event, regardless if ECA is processing models under a different account. |
Reacts when an order is assigned to a customer -- typically when an anonymous cart is assigned after login or registration, or when an admin reassigns an order. The `[commerce_order]` token provides the order (still with original customer data) and `[commerce_customer]` the new customer.
# Order: Create
Available since: 1.0.0
Provided tokens
| Token | Description |
| ---------------------- | ------------------------------------------------------------------------------------------------------------- |
| `[commerce_order]` | The order entity. |
| `[event]` | The event. |
| `[event:machine_name]` | The machine name of the ECA event. |
| `[session_user]` | The user account that dispatched the event, regardless if ECA is processing models under a different account. |
Reacts when a new order is created in memory, before it is saved. The `[commerce_order]` token provides the new order entity.
# Order: Delete
Available since: 1.0.0
Provided tokens
| Token | Description |
| ---------------------- | ------------------------------------------------------------------------------------------------------------- |
| `[commerce_order]` | The order entity. |
| `[event]` | The event. |
| `[event:machine_name]` | The machine name of the ECA event. |
| `[session_user]` | The user account that dispatched the event, regardless if ECA is processing models under a different account. |
Reacts after a order has been deleted from storage. The `[commerce_order]` token provides the deleted order entity.
# Order: Insert
Available since: 1.0.0
Provided tokens
| Token | Description |
| ---------------------- | ------------------------------------------------------------------------------------------------------------- |
| `[commerce_order]` | The order entity. |
| `[event]` | The event. |
| `[event:machine_name]` | The machine name of the ECA event. |
| `[session_user]` | The user account that dispatched the event, regardless if ECA is processing models under a different account. |
Reacts after a new order is saved for the first time. The `[commerce_order]` token provides the newly saved order entity.
# Order: Item create
Available since: 1.0.0
Provided tokens
| Token | Description |
| ----------------------- | ------------------------------------------------------------------------------------------------------------- |
| `[commerce_order_item]` | The order item entity. |
| `[event]` | The event. |
| `[event:machine_name]` | The machine name of the ECA event. |
| `[session_user]` | The user account that dispatched the event, regardless if ECA is processing models under a different account. |
Reacts when a new order item is created in memory, before it is saved. The `[commerce_order_item]` token provides the new order item entity.
# Order: Item delete
Available since: 1.0.0
Provided tokens
| Token | Description |
| ----------------------- | ------------------------------------------------------------------------------------------------------------- |
| `[commerce_order_item]` | The order item entity. |
| `[event]` | The event. |
| `[event:machine_name]` | The machine name of the ECA event. |
| `[session_user]` | The user account that dispatched the event, regardless if ECA is processing models under a different account. |
Reacts after a order item has been deleted from storage. The `[commerce_order_item]` token provides the deleted order item entity.
# Order: Item insert
Available since: 1.0.0
Provided tokens
| Token | Description |
| ----------------------- | ------------------------------------------------------------------------------------------------------------- |
| `[commerce_order_item]` | The order item entity. |
| `[event]` | The event. |
| `[event:machine_name]` | The machine name of the ECA event. |
| `[session_user]` | The user account that dispatched the event, regardless if ECA is processing models under a different account. |
Reacts after a new order item is saved for the first time. The `[commerce_order_item]` token provides the newly saved order item entity.
# Order: Item load
Available since: 1.0.0
Provided tokens
| Token | Description |
| ----------------------- | ------------------------------------------------------------------------------------------------------------- |
| `[commerce_order_item]` | The order item entity. |
| `[event]` | The event. |
| `[event:machine_name]` | The machine name of the ECA event. |
| `[session_user]` | The user account that dispatched the event, regardless if ECA is processing models under a different account. |
Reacts when a order item is loaded from storage. The `[commerce_order_item]` token provides the loaded order item entity.
# Order: Item predelete
Available since: 1.0.0
Provided tokens
| Token | Description |
| ----------------------- | ------------------------------------------------------------------------------------------------------------- |
| `[commerce_order_item]` | The order item entity. |
| `[event]` | The event. |
| `[event:machine_name]` | The machine name of the ECA event. |
| `[session_user]` | The user account that dispatched the event, regardless if ECA is processing models under a different account. |
Reacts before a order item is deleted. The entity still exists at this point. The `[commerce_order_item]` token provides the order item entity about to be deleted.
# Order: Item presave
Available since: 1.0.0
Provided tokens
| Token | Description |
| ----------------------- | ------------------------------------------------------------------------------------------------------------- |
| `[commerce_order_item]` | The order item entity. |
| `[event]` | The event. |
| `[event:machine_name]` | The machine name of the ECA event. |
| `[session_user]` | The user account that dispatched the event, regardless if ECA is processing models under a different account. |
Reacts before a order item is saved to storage. Fires for both new and existing entities. The `[commerce_order_item]` token provides the order item entity.
# Order: Item update
Available since: 1.0.0
Provided tokens
| Token | Description |
| ----------------------- | ------------------------------------------------------------------------------------------------------------- |
| `[commerce_order_item]` | The order item entity. |
| `[event]` | The event. |
| `[event:machine_name]` | The machine name of the ECA event. |
| `[session_user]` | The user account that dispatched the event, regardless if ECA is processing models under a different account. |
Reacts after an existing order item is saved. The `[commerce_order_item]` token provides the updated order item entity.
# Order: After alter label
Available since: 1.0.0
Provided tokens
| Token | Description |
| ------------------------ | ------------------------------------------------------------------------------------------------------------- |
| `[commerce_order]` | The order entity. |
| `[commerce_order_label]` | The order label. |
| `[event]` | The event. |
| `[event:machine_name]` | The machine name of the ECA event. |
| `[session_user]` | The user account that dispatched the event, regardless if ECA is processing models under a different account. |
Reacts when an order label is being generated, allowing customization of how orders are displayed in admin listings and breadcrumbs.
# Order: Load
Available since: 1.0.0
Provided tokens
| Token | Description |
| ---------------------- | ------------------------------------------------------------------------------------------------------------- |
| `[commerce_order]` | The order entity. |
| `[event]` | The event. |
| `[event:machine_name]` | The machine name of the ECA event. |
| `[session_user]` | The user account that dispatched the event, regardless if ECA is processing models under a different account. |
Reacts when a order is loaded from storage. The `[commerce_order]` token provides the loaded order entity.
# Order: Paid
Available since: 1.0.0
Provided tokens
| Token | Description |
| ---------------------- | ------------------------------------------------------------------------------------------------------------- |
| `[commerce_order]` | The order entity. |
| `[event]` | The event. |
| `[event:machine_name]` | The machine name of the ECA event. |
| `[session_user]` | The user account that dispatched the event, regardless if ECA is processing models under a different account. |
Reacts when an order has been fully paid (balance reaches zero).
Fires only once
This event is guaranteed to fire only once per order, even if the order is later refunded and re-paid. Safe for triggering one-time actions like license grants, enrollment, or shipment initiation.
# Order: Predelete
Available since: 1.0.0
Provided tokens
| Token | Description |
| ---------------------- | ------------------------------------------------------------------------------------------------------------- |
| `[commerce_order]` | The order entity. |
| `[event]` | The event. |
| `[event:machine_name]` | The machine name of the ECA event. |
| `[session_user]` | The user account that dispatched the event, regardless if ECA is processing models under a different account. |
Reacts before a order is deleted. The entity still exists at this point. The `[commerce_order]` token provides the order entity about to be deleted.
# Order: Presave
Available since: 1.0.0
Provided tokens
| Token | Description |
| ---------------------- | ------------------------------------------------------------------------------------------------------------- |
| `[commerce_order]` | The order entity. |
| `[event]` | The event. |
| `[event:machine_name]` | The machine name of the ECA event. |
| `[session_user]` | The user account that dispatched the event, regardless if ECA is processing models under a different account. |
Reacts before a order is saved to storage. Fires for both new and existing entities. The `[commerce_order]` token provides the order entity.
# Order: Profile load
Available since: 1.0.0
Provided tokens
| Token | Description |
| ---------------------- | ------------------------------------------------------------------------------------------------------------- |
| `[commerce_order]` | The order entity. |
| `[profiles]` | An array of customer profiles. |
| `[event]` | The event. |
| `[event:machine_name]` | The machine name of the ECA event. |
| `[session_user]` | The user account that dispatched the event, regardless if ECA is processing models under a different account. |
Reacts when customer profiles (billing, shipping) are being collected for an order. Useful for adding custom profile scopes or overriding which profile is used.
# Order: Update
Available since: 1.0.0
Provided tokens
| Token | Description |
| ---------------------- | ------------------------------------------------------------------------------------------------------------- |
| `[commerce_order]` | The order entity. |
| `[event]` | The event. |
| `[event:machine_name]` | The machine name of the ECA event. |
| `[session_user]` | The user account that dispatched the event, regardless if ECA is processing models under a different account. |
Reacts after an existing order is saved. The `[commerce_order]` token provides the updated order entity.
# Payment: Create
Available since: 1.0.0
Provided tokens
| Token | Description |
| ---------------------- | ------------------------------------------------------------------------------------------------------------- |
| `[commerce_payment]` | The payment entity. |
| `[event]` | The event. |
| `[event:machine_name]` | The machine name of the ECA event. |
| `[session_user]` | The user account that dispatched the event, regardless if ECA is processing models under a different account. |
Reacts when a new payment is created in memory, before it is saved. The `[commerce_payment]` token provides the new payment entity.
# Payment: Delete
Available since: 1.0.0
Provided tokens
| Token | Description |
| ---------------------- | ------------------------------------------------------------------------------------------------------------- |
| `[commerce_payment]` | The payment entity. |
| `[event]` | The event. |
| `[event:machine_name]` | The machine name of the ECA event. |
| `[session_user]` | The user account that dispatched the event, regardless if ECA is processing models under a different account. |
Reacts after a payment has been deleted from storage. The `[commerce_payment]` token provides the deleted payment entity.
# Payment: Filter Payment Gateways
Available since: 1.0.0
Provided tokens
| Token | Description |
| ----------------------------------- | ------------------------------------------------------------------------------------------------------------- |
| `[commerce_order]` | The order entity. |
| `[commerce_order_payment_gateways]` | The payment gateway configuration. |
| `[event]` | The event. |
| `[event:machine_name]` | The machine name of the ECA event. |
| `[session_user]` | The user account that dispatched the event, regardless if ECA is processing models under a different account. |
Reacts when payment gateways are being loaded for an order during checkout. Useful for conditionally hiding or showing payment methods based on order total, customer role, shipping address, or other criteria.
# Payment: Insert
Available since: 1.0.0
Provided tokens
| Token | Description |
| ---------------------- | ------------------------------------------------------------------------------------------------------------- |
| `[commerce_payment]` | The payment entity. |
| `[event]` | The event. |
| `[event:machine_name]` | The machine name of the ECA event. |
| `[session_user]` | The user account that dispatched the event, regardless if ECA is processing models under a different account. |
Reacts after a new payment is saved for the first time. The `[commerce_payment]` token provides the newly saved payment entity.
# Payment: Load
Available since: 1.0.0
Provided tokens
| Token | Description |
| ---------------------- | ------------------------------------------------------------------------------------------------------------- |
| `[commerce_payment]` | The payment entity. |
| `[event]` | The event. |
| `[event:machine_name]` | The machine name of the ECA event. |
| `[session_user]` | The user account that dispatched the event, regardless if ECA is processing models under a different account. |
Reacts when a payment is loaded from storage. The `[commerce_payment]` token provides the loaded payment entity.
# Payment: Predelete
Available since: 1.0.0
Provided tokens
| Token | Description |
| ---------------------- | ------------------------------------------------------------------------------------------------------------- |
| `[commerce_payment]` | The payment entity. |
| `[event]` | The event. |
| `[event:machine_name]` | The machine name of the ECA event. |
| `[session_user]` | The user account that dispatched the event, regardless if ECA is processing models under a different account. |
Reacts before a payment is deleted. The entity still exists at this point. The `[commerce_payment]` token provides the payment entity about to be deleted.
# Payment: Presave
Available since: 1.0.0
Provided tokens
| Token | Description |
| ---------------------- | ------------------------------------------------------------------------------------------------------------- |
| `[commerce_payment]` | The payment entity. |
| `[event]` | The event. |
| `[event:machine_name]` | The machine name of the ECA event. |
| `[session_user]` | The user account that dispatched the event, regardless if ECA is processing models under a different account. |
Reacts before a payment is saved to storage. Fires for both new and existing entities. The `[commerce_payment]` token provides the payment entity.
# Payment: Update
Available since: 1.0.0
Provided tokens
| Token | Description |
| ---------------------- | ------------------------------------------------------------------------------------------------------------- |
| `[commerce_payment]` | The payment entity. |
| `[event]` | The event. |
| `[event:machine_name]` | The machine name of the ECA event. |
| `[session_user]` | The user account that dispatched the event, regardless if ECA is processing models under a different account. |
Reacts after an existing payment is saved. The `[commerce_payment]` token provides the updated payment entity.
# Core: Post Mail Send
Available since: 1.0.0
Provided tokens
| Token | Description |
| ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `[event]` | The event. |
| `[event:machine_name]` | The machine name of the ECA event. |
| `[session_user]` | The user account that dispatched the event, regardless if ECA is processing models under a different account. This is only available if ECA is configured to always run under a specific account. |
Reacts after Commerce sends an email (e.g., order receipts, shipment confirmations). Useful for logging, triggering follow-up actions, or integrating with external systems after order-related emails.
# Price: Number format
Available since: 1.0.0
Provided tokens
| Token | Description |
| ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `[commerce_definition]` | This is the number format definition. |
| `[event]` | The event. |
| `[event:machine_name]` | The machine name of the ECA event. |
| `[session_user]` | The user account that dispatched the event, regardless if ECA is processing models under a different account. This is only available if ECA is configured to always run under a specific account. |
Reacts when a number format is being resolved for price display. Useful for customizing price formatting per-locale (decimal separator, grouping separator, currency patterns).
# Product: Create
Available since: 1.0.0
Provided tokens
| Token | Description |
| ---------------------- | ------------------------------------------------------------------------------------------------------------- |
| `[commerce_product]` | The product entity. This is not the variation. |
| `[event]` | The event. |
| `[event:machine_name]` | The machine name of the ECA event. |
| `[session_user]` | The user account that dispatched the event, regardless if ECA is processing models under a different account. |
Reacts when a new product is created in memory, before it is saved. The `[commerce_product]` token provides the new product entity.
# Product: Default variation
Available since: 1.0.0
Provided tokens
| Token | Description |
| -------------------------------------- | ------------------------------------------------------------------------------------------------------------- |
| `[commerce_product]` | The product entity. This is not the variation. |
| `[commerce_product_variation_default]` | This is the default product variation. |
| `[event]` | The event. |
| `[event:machine_name]` | The machine name of the ECA event. |
| `[session_user]` | The user account that dispatched the event, regardless if ECA is processing models under a different account. |
Reacts when the default variation for a product is being determined. Useful for dynamically selecting which variation is shown by default based on user preferences, inventory, or pricing.
# Product: Delete
Available since: 1.0.0
Provided tokens
| Token | Description |
| ---------------------- | ------------------------------------------------------------------------------------------------------------- |
| `[commerce_product]` | The product entity. This is not the variation. |
| `[event]` | The event. |
| `[event:machine_name]` | The machine name of the ECA event. |
| `[session_user]` | The user account that dispatched the event, regardless if ECA is processing models under a different account. |
Reacts after a product has been deleted from storage. The `[commerce_product]` token provides the deleted product entity.
# Product: Filter Variations
Available since: 1.0.0
Provided tokens
| Token | Description |
| ------------------------------ | ------------------------------------------------------------------------------------------------------------- |
| `[commerce_product]` | The product entity. This is not the variation. |
| `[commerce_product_variation]` | The product variation entity. This is not the product entity. |
| `[event]` | The event. |
| `[event:machine_name]` | The machine name of the ECA event. |
| `[session_user]` | The user account that dispatched the event, regardless if ECA is processing models under a different account. |
Reacts when filtering the list of enabled variations for a product. Useful for hiding out-of-stock variations, restricting by user role, or implementing location-based variation filtering.
# Product: Insert
Available since: 1.0.0
Provided tokens
| Token | Description |
| ---------------------- | ------------------------------------------------------------------------------------------------------------- |
| `[commerce_product]` | The product entity. This is not the variation. |
| `[event]` | The event. |
| `[event:machine_name]` | The machine name of the ECA event. |
| `[session_user]` | The user account that dispatched the event, regardless if ECA is processing models under a different account. |
Reacts after a new product is saved for the first time. The `[commerce_product]` token provides the newly saved product entity.
# Product: Load
Available since: 1.0.0
Provided tokens
| Token | Description |
| ---------------------- | ------------------------------------------------------------------------------------------------------------- |
| `[commerce_product]` | The product entity. This is not the variation. |
| `[event]` | The event. |
| `[event:machine_name]` | The machine name of the ECA event. |
| `[session_user]` | The user account that dispatched the event, regardless if ECA is processing models under a different account. |
Reacts when a product is loaded from storage. The `[commerce_product]` token provides the loaded product entity.
# Product: Predelete
Available since: 1.0.0
Provided tokens
| Token | Description |
| ---------------------- | ------------------------------------------------------------------------------------------------------------- |
| `[commerce_product]` | The product entity. This is not the variation. |
| `[event]` | The event. |
| `[event:machine_name]` | The machine name of the ECA event. |
| `[session_user]` | The user account that dispatched the event, regardless if ECA is processing models under a different account. |
Reacts before a product is deleted. The entity still exists at this point. The `[commerce_product]` token provides the product entity about to be deleted.
# Product: Presave
Available since: 1.0.0
Provided tokens
| Token | Description |
| ---------------------- | ------------------------------------------------------------------------------------------------------------- |
| `[commerce_product]` | The product entity. This is not the variation. |
| `[event]` | The event. |
| `[event:machine_name]` | The machine name of the ECA event. |
| `[session_user]` | The user account that dispatched the event, regardless if ECA is processing models under a different account. |
Reacts before a product is saved to storage. Fires for both new and existing entities. The `[commerce_product]` token provides the product entity.
# Product: Translation delete
Available since: 1.0.0
Provided tokens
| Token | Description |
| ---------------------- | ------------------------------------------------------------------------------------------------------------- |
| `[commerce_product]` | The product entity. This is not the variation. |
| `[event]` | The event. |
| `[event:machine_name]` | The machine name of the ECA event. |
| `[session_user]` | The user account that dispatched the event, regardless if ECA is processing models under a different account. |
Reacts after a translation of a product is deleted. The `[commerce_product]` token provides the product entity.
# Product: Translation insert
Available since: 1.0.0
Provided tokens
| Token | Description |
| ---------------------- | ------------------------------------------------------------------------------------------------------------- |
| `[commerce_product]` | The product entity. This is not the variation. |
| `[event]` | The event. |
| `[event:machine_name]` | The machine name of the ECA event. |
| `[session_user]` | The user account that dispatched the event, regardless if ECA is processing models under a different account. |
Reacts after a new translation of a product is saved. The `[commerce_product]` token provides the translated product entity.
# Product: Update
Available since: 1.0.0
Provided tokens
| Token | Description |
| ---------------------- | ------------------------------------------------------------------------------------------------------------- |
| `[commerce_product]` | The product entity. This is not the variation. |
| `[event]` | The event. |
| `[event:machine_name]` | The machine name of the ECA event. |
| `[session_user]` | The user account that dispatched the event, regardless if ECA is processing models under a different account. |
Reacts after an existing product is saved. The `[commerce_product]` token provides the updated product entity.
# Product: Variation Ajax Change
Available since: 1.0.0
Provided tokens
| Token | Description |
| ---------------------------------------- | ------------------------------------------------------------------------------------------------------------- |
| `[commerce_product_variation]` | The product variation entity. This is not the product entity. |
| `[commerce_product_variation_response]` | The response when changing product variations via ajax. |
| `[commerce_product_variation_view_mode]` | The view mode for the product variation. |
| `[event]` | The event. |
| `[event:machine_name]` | The machine name of the ECA event. |
| `[session_user]` | The user account that dispatched the event, regardless if ECA is processing models under a different account. |
Reacts when a product variation is changed via AJAX (e.g., user selects a different size or color). Useful for adding custom AJAX commands to update price displays, stock information, or image galleries.
# Product: Variation create
Available since: 1.0.0
Provided tokens
| Token | Description |
| ------------------------------ | ------------------------------------------------------------------------------------------------------------- |
| `[commerce_product_variation]` | The product variation entity. This is not the product entity. |
| `[event]` | The event. |
| `[event:machine_name]` | The machine name of the ECA event. |
| `[session_user]` | The user account that dispatched the event, regardless if ECA is processing models under a different account. |
Reacts when a new product variation is created in memory, before it is saved. The `[commerce_product_variation]` token provides the new product variation entity.
# Product: Variation delete
Available since: 1.0.0
Provided tokens
| Token | Description |
| ------------------------------ | ------------------------------------------------------------------------------------------------------------- |
| `[commerce_product_variation]` | The product variation entity. This is not the product entity. |
| `[event]` | The event. |
| `[event:machine_name]` | The machine name of the ECA event. |
| `[session_user]` | The user account that dispatched the event, regardless if ECA is processing models under a different account. |
Reacts after a product variation has been deleted from storage. The `[commerce_product_variation]` token provides the deleted product variation entity.
# Product: Variation insert
Available since: 1.0.0
Provided tokens
| Token | Description |
| ------------------------------ | ------------------------------------------------------------------------------------------------------------- |
| `[commerce_product_variation]` | The product variation entity. This is not the product entity. |
| `[event]` | The event. |
| `[event:machine_name]` | The machine name of the ECA event. |
| `[session_user]` | The user account that dispatched the event, regardless if ECA is processing models under a different account. |
Reacts after a new product variation is saved for the first time. The `[commerce_product_variation]` token provides the newly saved product variation entity.
# Product: Variation load
Available since: 1.0.0
Provided tokens
| Token | Description |
| ------------------------------ | ------------------------------------------------------------------------------------------------------------- |
| `[commerce_product_variation]` | The product variation entity. This is not the product entity. |
| `[event]` | The event. |
| `[event:machine_name]` | The machine name of the ECA event. |
| `[session_user]` | The user account that dispatched the event, regardless if ECA is processing models under a different account. |
Reacts when a product variation is loaded from storage. The `[commerce_product_variation]` token provides the loaded product variation entity.
# Product: Variation predelete
Available since: 1.0.0
Provided tokens
| Token | Description |
| ------------------------------ | ------------------------------------------------------------------------------------------------------------- |
| `[commerce_product_variation]` | The product variation entity. This is not the product entity. |
| `[event]` | The event. |
| `[event:machine_name]` | The machine name of the ECA event. |
| `[session_user]` | The user account that dispatched the event, regardless if ECA is processing models under a different account. |
Reacts before a product variation is deleted. The entity still exists at this point. The `[commerce_product_variation]` token provides the product variation entity about to be deleted.
# Product: Variation presave
Available since: 1.0.0
Provided tokens
| Token | Description |
| ------------------------------ | ------------------------------------------------------------------------------------------------------------- |
| `[commerce_product_variation]` | The product variation entity. This is not the product entity. |
| `[event]` | The event. |
| `[event:machine_name]` | The machine name of the ECA event. |
| `[session_user]` | The user account that dispatched the event, regardless if ECA is processing models under a different account. |
Reacts before a product variation is saved to storage. Fires for both new and existing entities. The `[commerce_product_variation]` token provides the product variation entity.
# Product: Variation translation delete
Available since: 1.0.0
Provided tokens
| Token | Description |
| ------------------------------ | ------------------------------------------------------------------------------------------------------------- |
| `[commerce_product_variation]` | The product variation entity. This is not the product entity. |
| `[event]` | The event. |
| `[event:machine_name]` | The machine name of the ECA event. |
| `[session_user]` | The user account that dispatched the event, regardless if ECA is processing models under a different account. |
Reacts after a translation of a product variation is deleted. The `[commerce_product_variation]` token provides the product variation entity.
# Product: Variation translation insert
Available since: 1.0.0
Provided tokens
| Token | Description |
| ------------------------------ | ------------------------------------------------------------------------------------------------------------- |
| `[commerce_product_variation]` | The product variation entity. This is not the product entity. |
| `[event]` | The event. |
| `[event:machine_name]` | The machine name of the ECA event. |
| `[session_user]` | The user account that dispatched the event, regardless if ECA is processing models under a different account. |
Reacts after a new translation of a product variation is saved. The `[commerce_product_variation]` token provides the translated product variation entity.
# Product: Variation update
Available since: 1.0.0
Provided tokens
| Token | Description |
| ------------------------------ | ------------------------------------------------------------------------------------------------------------- |
| `[commerce_product_variation]` | The product variation entity. This is not the product entity. |
| `[event]` | The event. |
| `[event:machine_name]` | The machine name of the ECA event. |
| `[session_user]` | The user account that dispatched the event, regardless if ECA is processing models under a different account. |
Reacts after an existing product variation is saved. The `[commerce_product_variation]` token provides the updated product variation entity.
# Promotion: Coupon create
Available since: 1.0.0
Provided tokens
| Token | Description |
| ---------------------- | ------------------------------------------------------------------------------------------------------------- |
| `[commerce_coupon]` | The coupon entity. |
| `[event]` | The event. |
| `[event:machine_name]` | The machine name of the ECA event. |
| `[session_user]` | The user account that dispatched the event, regardless if ECA is processing models under a different account. |
Reacts when a new coupon is created in memory, before it is saved. The `[commerce_coupon]` token provides the new coupon entity.
# Promotion: Coupon delete
Available since: 1.0.0
Provided tokens
| Token | Description |
| ---------------------- | ------------------------------------------------------------------------------------------------------------- |
| `[commerce_coupon]` | The coupon entity. |
| `[event]` | The event. |
| `[event:machine_name]` | The machine name of the ECA event. |
| `[session_user]` | The user account that dispatched the event, regardless if ECA is processing models under a different account. |
Reacts after a coupon has been deleted from storage. The `[commerce_coupon]` token provides the deleted coupon entity.
# Promotion: Coupon insert
Available since: 1.0.0
Provided tokens
| Token | Description |
| ---------------------- | ------------------------------------------------------------------------------------------------------------- |
| `[commerce_coupon]` | The coupon entity. |
| `[event]` | The event. |
| `[event:machine_name]` | The machine name of the ECA event. |
| `[session_user]` | The user account that dispatched the event, regardless if ECA is processing models under a different account. |
Reacts after a new coupon is saved for the first time. The `[commerce_coupon]` token provides the newly saved coupon entity.
# Promotion: Coupon load
Available since: 1.0.0
Provided tokens
| Token | Description |
| ---------------------- | ------------------------------------------------------------------------------------------------------------- |
| `[commerce_coupon]` | The coupon entity. |
| `[event]` | The event. |
| `[event:machine_name]` | The machine name of the ECA event. |
| `[session_user]` | The user account that dispatched the event, regardless if ECA is processing models under a different account. |
Reacts when a coupon is loaded from storage. The `[commerce_coupon]` token provides the loaded coupon entity.
# Promotion: Coupon predelete
Available since: 1.0.0
Provided tokens
| Token | Description |
| ---------------------- | ------------------------------------------------------------------------------------------------------------- |
| `[commerce_coupon]` | The coupon entity. |
| `[event]` | The event. |
| `[event:machine_name]` | The machine name of the ECA event. |
| `[session_user]` | The user account that dispatched the event, regardless if ECA is processing models under a different account. |
Reacts before a coupon is deleted. The entity still exists at this point. The `[commerce_coupon]` token provides the coupon entity about to be deleted.
# Promotion: Coupon presave
Available since: 1.0.0
Provided tokens
| Token | Description |
| ---------------------- | ------------------------------------------------------------------------------------------------------------- |
| `[commerce_coupon]` | The coupon entity. |
| `[event]` | The event. |
| `[event:machine_name]` | The machine name of the ECA event. |
| `[session_user]` | The user account that dispatched the event, regardless if ECA is processing models under a different account. |
Reacts before a coupon is saved to storage. Fires for both new and existing entities. The `[commerce_coupon]` token provides the coupon entity.
# Promotion: Coupon update
Available since: 1.0.0
Provided tokens
| Token | Description |
| ---------------------- | ------------------------------------------------------------------------------------------------------------- |
| `[commerce_coupon]` | The coupon entity. |
| `[event]` | The event. |
| `[event:machine_name]` | The machine name of the ECA event. |
| `[session_user]` | The user account that dispatched the event, regardless if ECA is processing models under a different account. |
Reacts after an existing coupon is saved. The `[commerce_coupon]` token provides the updated coupon entity.
# Promotion: Create
Available since: 1.0.0
Provided tokens
| Token | Description |
| ---------------------- | ------------------------------------------------------------------------------------------------------------- |
| `[commerce_promotion]` | The promotion entity. |
| `[event]` | The event. |
| `[event:machine_name]` | The machine name of the ECA event. |
| `[session_user]` | The user account that dispatched the event, regardless if ECA is processing models under a different account. |
Reacts when a new promotion is created in memory, before it is saved. The `[commerce_promotion]` token provides the new promotion entity.
# Promotion: Delete
Available since: 1.0.0
Provided tokens
| Token | Description |
| ---------------------- | ------------------------------------------------------------------------------------------------------------- |
| `[commerce_promotion]` | The promotion entity. |
| `[event]` | The event. |
| `[event:machine_name]` | The machine name of the ECA event. |
| `[session_user]` | The user account that dispatched the event, regardless if ECA is processing models under a different account. |
Reacts after a promotion has been deleted from storage. The `[commerce_promotion]` token provides the deleted promotion entity.
# Promotion: Filter
Available since: 1.0.0
Provided tokens
| Token | Description |
| ----------------------- | ------------------------------------------------------------------------------------------------------------- |
| `[commerce_order]` | The order entity. |
| `[commerce_promotions]` | An array of promotion entities. |
| `[event]` | The event. |
| `[event:machine_name]` | The machine name of the ECA event. |
| `[session_user]` | The user account that dispatched the event, regardless if ECA is processing models under a different account. |
Reacts when promotions are being evaluated for an order. Useful for implementing custom promotion exclusion logic beyond what Commerce conditions provide (e.g., restrict to first-time customers or based on external loyalty data).
# Promotion: Insert
Available since: 1.0.0
Provided tokens
| Token | Description |
| ---------------------- | ------------------------------------------------------------------------------------------------------------- |
| `[commerce_promotion]` | The promotion entity. |
| `[event]` | The event. |
| `[event:machine_name]` | The machine name of the ECA event. |
| `[session_user]` | The user account that dispatched the event, regardless if ECA is processing models under a different account. |
Reacts after a new promotion is saved for the first time. The `[commerce_promotion]` token provides the newly saved promotion entity.
# Promotion: Load
Available since: 1.0.0
Provided tokens
| Token | Description |
| ---------------------- | ------------------------------------------------------------------------------------------------------------- |
| `[commerce_promotion]` | The promotion entity. |
| `[event]` | The event. |
| `[event:machine_name]` | The machine name of the ECA event. |
| `[session_user]` | The user account that dispatched the event, regardless if ECA is processing models under a different account. |
Reacts when a promotion is loaded from storage. The `[commerce_promotion]` token provides the loaded promotion entity.
# Promotion: Predelete
Available since: 1.0.0
Provided tokens
| Token | Description |
| ---------------------- | ------------------------------------------------------------------------------------------------------------- |
| `[commerce_promotion]` | The promotion entity. |
| `[event]` | The event. |
| `[event:machine_name]` | The machine name of the ECA event. |
| `[session_user]` | The user account that dispatched the event, regardless if ECA is processing models under a different account. |
Reacts before a promotion is deleted. The entity still exists at this point. The `[commerce_promotion]` token provides the promotion entity about to be deleted.
# Promotion: Presave
Available since: 1.0.0
Provided tokens
| Token | Description |
| ---------------------- | ------------------------------------------------------------------------------------------------------------- |
| `[commerce_promotion]` | The promotion entity. |
| `[event]` | The event. |
| `[event:machine_name]` | The machine name of the ECA event. |
| `[session_user]` | The user account that dispatched the event, regardless if ECA is processing models under a different account. |
Reacts before a promotion is saved to storage. Fires for both new and existing entities. The `[commerce_promotion]` token provides the promotion entity.
# Promotion: Translate Delete
Available since: 1.0.0
Provided tokens
| Token | Description |
| ---------------------- | ------------------------------------------------------------------------------------------------------------- |
| `[commerce_promotion]` | The promotion entity. |
| `[event]` | The event. |
| `[event:machine_name]` | The machine name of the ECA event. |
| `[session_user]` | The user account that dispatched the event, regardless if ECA is processing models under a different account. |
Reacts after a translation of a promotion is deleted. The `[commerce_promotion]` token provides the promotion entity.
# Promotion: Translate Insert
Available since: 1.0.0
Provided tokens
| Token | Description |
| ---------------------- | ------------------------------------------------------------------------------------------------------------- |
| `[commerce_promotion]` | The promotion entity. |
| `[event]` | The event. |
| `[event:machine_name]` | The machine name of the ECA event. |
| `[session_user]` | The user account that dispatched the event, regardless if ECA is processing models under a different account. |
Reacts after a new translation of a promotion is saved. The `[commerce_promotion]` token provides the translated promotion entity.
# Promotion: Update
Available since: 1.0.0
Provided tokens
| Token | Description |
| ---------------------- | ------------------------------------------------------------------------------------------------------------- |
| `[commerce_promotion]` | The promotion entity. |
| `[event]` | The event. |
| `[event:machine_name]` | The machine name of the ECA event. |
| `[session_user]` | The user account that dispatched the event, regardless if ECA is processing models under a different account. |
Reacts after an existing promotion is saved. The `[commerce_promotion]` token provides the updated promotion entity.
# Core: Referenceable Plugin Types
Available since: 1.0.0
Provided tokens
| Token | Description |
| ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `[event]` | The event. |
| `[event:machine_name]` | The machine name of the ECA event. |
| `[session_user]` | The user account that dispatched the event, regardless if ECA is processing models under a different account. This is only available if ECA is configured to always run under a specific account. |
Reacts when building the list of Commerce plugin types that can be referenced by entity reference fields.
# Store: Create
Available since: 1.0.0
Provided tokens
| Token | Description |
| ---------------------- | ------------------------------------------------------------------------------------------------------------- |
| `[commerce_store]` | The store entity. |
| `[event]` | The event. |
| `[event:machine_name]` | The machine name of the ECA event. |
| `[session_user]` | The user account that dispatched the event, regardless if ECA is processing models under a different account. |
Reacts when a new store is created in memory, before it is saved. The `[commerce_store]` token provides the new store entity.
# Store: Delete
Available since: 1.0.0
Provided tokens
| Token | Description |
| ---------------------- | ------------------------------------------------------------------------------------------------------------- |
| `[commerce_store]` | The store entity. |
| `[event]` | The event. |
| `[event:machine_name]` | The machine name of the ECA event. |
| `[session_user]` | The user account that dispatched the event, regardless if ECA is processing models under a different account. |
Reacts after a store has been deleted from storage. The `[commerce_store]` token provides the deleted store entity.
# Store: Insert
Available since: 1.0.0
Provided tokens
| Token | Description |
| ---------------------- | ------------------------------------------------------------------------------------------------------------- |
| `[commerce_store]` | The store entity. |
| `[event]` | The event. |
| `[event:machine_name]` | The machine name of the ECA event. |
| `[session_user]` | The user account that dispatched the event, regardless if ECA is processing models under a different account. |
Reacts after a new store is saved for the first time. The `[commerce_store]` token provides the newly saved store entity.
# Store: Load
Available since: 1.0.0
Provided tokens
| Token | Description |
| ---------------------- | ------------------------------------------------------------------------------------------------------------- |
| `[commerce_store]` | The store entity. |
| `[event]` | The event. |
| `[event:machine_name]` | The machine name of the ECA event. |
| `[session_user]` | The user account that dispatched the event, regardless if ECA is processing models under a different account. |
Reacts when a store is loaded from storage. The `[commerce_store]` token provides the loaded store entity.
# Store: Predelete
Available since: 1.0.0
Provided tokens
| Token | Description |
| ---------------------- | ------------------------------------------------------------------------------------------------------------- |
| `[commerce_store]` | The store entity. |
| `[event]` | The event. |
| `[event:machine_name]` | The machine name of the ECA event. |
| `[session_user]` | The user account that dispatched the event, regardless if ECA is processing models under a different account. |
Reacts before a store is deleted. The entity still exists at this point. The `[commerce_store]` token provides the store entity about to be deleted.
# Store: Presave
Available since: 1.0.0
Provided tokens
| Token | Description |
| ---------------------- | ------------------------------------------------------------------------------------------------------------- |
| `[commerce_store]` | The store entity. |
| `[event]` | The event. |
| `[event:machine_name]` | The machine name of the ECA event. |
| `[session_user]` | The user account that dispatched the event, regardless if ECA is processing models under a different account. |
Reacts before a store is saved to storage. Fires for both new and existing entities. The `[commerce_store]` token provides the store entity.
# Store: Translation delete
Available since: 1.0.0
Provided tokens
| Token | Description |
| ---------------------- | ------------------------------------------------------------------------------------------------------------- |
| `[commerce_store]` | The store entity. |
| `[event]` | The event. |
| `[event:machine_name]` | The machine name of the ECA event. |
| `[session_user]` | The user account that dispatched the event, regardless if ECA is processing models under a different account. |
Reacts after a translation of a store is deleted. The `[commerce_store]` token provides the store entity.
# Store: Translation insert
Available since: 1.0.0
Provided tokens
| Token | Description |
| ---------------------- | ------------------------------------------------------------------------------------------------------------- |
| `[commerce_store]` | The store entity. |
| `[event]` | The event. |
| `[event:machine_name]` | The machine name of the ECA event. |
| `[session_user]` | The user account that dispatched the event, regardless if ECA is processing models under a different account. |
Reacts after a new translation of a store is saved. The `[commerce_store]` token provides the translated store entity.
# Store: Update
Available since: 1.0.0
Provided tokens
| Token | Description |
| ---------------------- | ------------------------------------------------------------------------------------------------------------- |
| `[commerce_store]` | The store entity. |
| `[event]` | The event. |
| `[event:machine_name]` | The machine name of the ECA event. |
| `[session_user]` | The user account that dispatched the event, regardless if ECA is processing models under a different account. |
Reacts after an existing store is saved. The `[commerce_store]` token provides the updated store entity.
# Tax: Build
Available since: 1.0.0
Provided tokens
| Token | Description |
| ----------------------- | ------------------------------------------------------------------------------------------------------------- |
| `[commerce_tax_plugin]` | The local tax type plugin. |
| `[commerce_tax_zones]` | An array of tax zones. |
| `[event]` | The event. |
| `[event:machine_name]` | The machine name of the ECA event. |
| `[session_user]` | The user account that dispatched the event, regardless if ECA is processing models under a different account. |
Reacts when tax zones are being built for a local tax type plugin. Useful for dynamically adding special tax zones, modifying rates, or removing zones based on business logic.
# Tax: Customer profile
Available since: 1.0.0
Provided tokens
| Token | Description |
| ----------------------- | ------------------------------------------------------------------------------------------------------------- |
| `[commerce_order_item]` | The order item entity. |
| `[customer_profile]` | A customer profile. |
| `[event]` | The event. |
| `[event:machine_name]` | The machine name of the ECA event. |
| `[session_user]` | The user account that dispatched the event, regardless if ECA is processing models under a different account. |
Reacts when determining which customer profile to use for tax calculation per order item. By default the billing profile is used.
Shipping-based tax calculation
Set the customer profile to the shipping profile to calculate tax based on the ship-to address instead of the billing address.
# ECA Config
Module with actions and events about the Drupal configuration. You can read and write configuration and also react to specific configuration events.
## Installation
```
composer require drupal/eca
drush pm:install eca_config
```
Instead of using Drush to enable the module, you can also go to "Administration / Extend" (`/admin/modules`) and enable the module **ECA Config** from there.
# Config Action
Available since: 3.0.0
Execute a Drupal Core ConfigAction.
## Fields
`Config action` : The config action to be applied.\
When using the "Defined by token" option, make sure there is a token with this name: *eca_config_action_action_id*
`Config name` : The config name, for example *system.site*.\
This field supports tokens.
`Data` : The data for the config action, provided in YAML format.\
This field supports tokens.
# Config: get list
Available since: 3.0.8
Gets a list of configuration names and stores it as a token.
## Fields
`Token name` : The name of the token to store the configuration list. Please provide the token name only, without brackets.
`Configuration prefix` : Optional prefix to filter configuration names.\
This field supports tokens.
# Config: read
Available since: 1.0.0
Read configuration and store it as a token.
## Fields
`Include overridden` : Whether to apply module and settings.php overrides to values.
`Name of token` : The targeted configuration value will be loaded into this specified token. Please provide the token name only, without brackets.
`Config name` : The config name, for example *system.site*.
`Config key` : The config key, for example *page.front*. Leave empty to use the whole config.
# Config: write
Available since: 1.0.0
Writes into configuration from a token.
## Fields
`Config value` : The value to set.\
This field supports tokens.
`Interpret above config value as YAML format` : Nested data can be set using YAML format, for example *front: /node*. When using this format, this option needs to be enabled. When using tokens and YAML altogether, make sure that tokens are wrapped as a string. Example: *front: "[myurl:path]"*
`Validate YAML to prevent this from being executed when invalid`
`Save configuration` : Save the given config to the Drupal database.
`Config name` : The config name, for example *system.site*.
`Config key` : The config key, for example *page.front*. Leave empty to use the whole config.
# Config installer sync mode
Available since: 2.1.3
Determine if config installer is in sync mode.
## Fields
`Negate the condition` : Negates the condition. Makes TRUE to FALSE and vice versa.
# Collect information on all config collections
Available since: 1.0.0
Provided tokens
| Token | Description |
| ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `[user]` | The current user. Alias: `current_user` |
| `[htmx]` | Information about the current HTMX request. |
| `[htmx:is_request]` | Whether the current request was sent by HTMX ("1" or "0"). |
| `[htmx:boosted]` | Whether the current request was boosted by HTMX ("1" or "0"). |
| `[htmx:history_restore]` | Whether the current request is for HTMX history restoration ("1" or "0"). |
| `[htmx:target]` | The value of the HX-Target request header. |
| `[htmx:trigger]` | The value of the HX-Trigger request header. |
| `[htmx:trigger_name]` | The value of the HX-Trigger-Name request header. |
| `[htmx:current_url]` | The value of the HX-Current-URL request header. |
| `[htmx:prompt]` | The value of the HX-Prompt request header. |
| `[config]` | The configuration with all current values. |
| `[config_original]` | The configuration with all original values plus overrides. |
| `[config_name]` | The name of the configuration. |
| `[event]` | The event. |
| `[event:machine_name]` | The machine name of the ECA event. |
| `[session_user]` | The user account that dispatched the event, regardless if ECA is processing models under a different account. This is only available if ECA is configured to always run under a specific account. |
An information of all Drupal configuration was collected.
# Delete config
Available since: 1.0.0
Provided tokens
| Token | Description |
| ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `[user]` | The current user. Alias: `current_user` |
| `[htmx]` | Information about the current HTMX request. |
| `[htmx:is_request]` | Whether the current request was sent by HTMX ("1" or "0"). |
| `[htmx:boosted]` | Whether the current request was boosted by HTMX ("1" or "0"). |
| `[htmx:history_restore]` | Whether the current request is for HTMX history restoration ("1" or "0"). |
| `[htmx:target]` | The value of the HX-Target request header. |
| `[htmx:trigger]` | The value of the HX-Trigger request header. |
| `[htmx:trigger_name]` | The value of the HX-Trigger-Name request header. |
| `[htmx:current_url]` | The value of the HX-Current-URL request header. |
| `[htmx:prompt]` | The value of the HX-Prompt request header. |
| `[config]` | The configuration with all current values. |
| `[config_original]` | The configuration with all original values plus overrides. |
| `[config_name]` | The name of the configuration. |
| `[event]` | The event. |
| `[event:machine_name]` | The machine name of the ECA event. |
| `[session_user]` | The user account that dispatched the event, regardless if ECA is processing models under a different account. This is only available if ECA is configured to always run under a specific account. |
A Drupal configuration was deleted.
## Fields
`Config name` : Leave empty to apply to all events. To limit this event to just specific events, provide the machine name of the configuration, e.g. "system.site". This can also be limited to a range of configuration, e.g. with "node.type.", this applies to all configurations with a name starting with this pattern.
```
The configuration name to filter on (e.g., `system.site`). Supports prefix matching. Leave empty to react to all configuration deletions.
```
`Sync Mode` : During config sync, the Drupal site is in sync mode.
```
Filter by sync mode: 'Always' fires regardless, 'Yes' only during config sync, 'No' only for runtime changes.
```
# Import config
Available since: 1.0.0
Provided tokens
| Token | Description |
| ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `[user]` | The current user. Alias: `current_user` |
| `[htmx]` | Information about the current HTMX request. |
| `[htmx:is_request]` | Whether the current request was sent by HTMX ("1" or "0"). |
| `[htmx:boosted]` | Whether the current request was boosted by HTMX ("1" or "0"). |
| `[htmx:history_restore]` | Whether the current request is for HTMX history restoration ("1" or "0"). |
| `[htmx:target]` | The value of the HX-Target request header. |
| `[htmx:trigger]` | The value of the HX-Trigger request header. |
| `[htmx:trigger_name]` | The value of the HX-Trigger-Name request header. |
| `[htmx:current_url]` | The value of the HX-Current-URL request header. |
| `[htmx:prompt]` | The value of the HX-Prompt request header. |
| `[config]` | The configuration with all current values. |
| `[config_original]` | The configuration with all original values plus overrides. |
| `[config_name]` | The name of the configuration. |
| `[event]` | The event. |
| `[event:machine_name]` | The machine name of the ECA event. |
| `[session_user]` | The user account that dispatched the event, regardless if ECA is processing models under a different account. This is only available if ECA is configured to always run under a specific account. |
A Drupal configuration was imported.
# Import config but content missing
Available since: 1.0.0
Provided tokens
| Token | Description |
| ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `[user]` | The current user. Alias: `current_user` |
| `[htmx]` | Information about the current HTMX request. |
| `[htmx:is_request]` | Whether the current request was sent by HTMX ("1" or "0"). |
| `[htmx:boosted]` | Whether the current request was boosted by HTMX ("1" or "0"). |
| `[htmx:history_restore]` | Whether the current request is for HTMX history restoration ("1" or "0"). |
| `[htmx:target]` | The value of the HX-Target request header. |
| `[htmx:trigger]` | The value of the HX-Trigger request header. |
| `[htmx:trigger_name]` | The value of the HX-Trigger-Name request header. |
| `[htmx:current_url]` | The value of the HX-Current-URL request header. |
| `[htmx:prompt]` | The value of the HX-Prompt request header. |
| `[config]` | The configuration with all current values. |
| `[config_original]` | The configuration with all original values plus overrides. |
| `[config_name]` | The name of the configuration. |
| `[event]` | The event. |
| `[event:machine_name]` | The machine name of the ECA event. |
| `[session_user]` | The user account that dispatched the event, regardless if ECA is processing models under a different account. This is only available if ECA is configured to always run under a specific account. |
An imported Drupal configuration content was missed.
# Import config validation
Available since: 1.0.0
Provided tokens
| Token | Description |
| ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `[user]` | The current user. Alias: `current_user` |
| `[htmx]` | Information about the current HTMX request. |
| `[htmx:is_request]` | Whether the current request was sent by HTMX ("1" or "0"). |
| `[htmx:boosted]` | Whether the current request was boosted by HTMX ("1" or "0"). |
| `[htmx:history_restore]` | Whether the current request is for HTMX history restoration ("1" or "0"). |
| `[htmx:target]` | The value of the HX-Target request header. |
| `[htmx:trigger]` | The value of the HX-Trigger request header. |
| `[htmx:trigger_name]` | The value of the HX-Trigger-Name request header. |
| `[htmx:current_url]` | The value of the HX-Current-URL request header. |
| `[htmx:prompt]` | The value of the HX-Prompt request header. |
| `[config]` | The configuration with all current values. |
| `[config_original]` | The configuration with all original values plus overrides. |
| `[config_name]` | The name of the configuration. |
| `[event]` | The event. |
| `[event:machine_name]` | The machine name of the ECA event. |
| `[session_user]` | The user account that dispatched the event, regardless if ECA is processing models under a different account. This is only available if ECA is configured to always run under a specific account. |
An import of a Drupal configuration was validated.
# Rename config
Available since: 1.0.0
Provided tokens
| Token | Description |
| ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `[user]` | The current user. Alias: `current_user` |
| `[htmx]` | Information about the current HTMX request. |
| `[htmx:is_request]` | Whether the current request was sent by HTMX ("1" or "0"). |
| `[htmx:boosted]` | Whether the current request was boosted by HTMX ("1" or "0"). |
| `[htmx:history_restore]` | Whether the current request is for HTMX history restoration ("1" or "0"). |
| `[htmx:target]` | The value of the HX-Target request header. |
| `[htmx:trigger]` | The value of the HX-Trigger request header. |
| `[htmx:trigger_name]` | The value of the HX-Trigger-Name request header. |
| `[htmx:current_url]` | The value of the HX-Current-URL request header. |
| `[htmx:prompt]` | The value of the HX-Prompt request header. |
| `[config]` | The configuration with all current values. |
| `[config_original]` | The configuration with all original values plus overrides. |
| `[config_name]` | The name of the configuration. |
| `[event]` | The event. |
| `[event:machine_name]` | The machine name of the ECA event. |
| `[session_user]` | The user account that dispatched the event, regardless if ECA is processing models under a different account. This is only available if ECA is configured to always run under a specific account. |
A Drupal configuration was renamed.
# Save config
Available since: 1.0.0
Provided tokens
| Token | Description |
| ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `[user]` | The current user. Alias: `current_user` |
| `[htmx]` | Information about the current HTMX request. |
| `[htmx:is_request]` | Whether the current request was sent by HTMX ("1" or "0"). |
| `[htmx:boosted]` | Whether the current request was boosted by HTMX ("1" or "0"). |
| `[htmx:history_restore]` | Whether the current request is for HTMX history restoration ("1" or "0"). |
| `[htmx:target]` | The value of the HX-Target request header. |
| `[htmx:trigger]` | The value of the HX-Trigger request header. |
| `[htmx:trigger_name]` | The value of the HX-Trigger-Name request header. |
| `[htmx:current_url]` | The value of the HX-Current-URL request header. |
| `[htmx:prompt]` | The value of the HX-Prompt request header. |
| `[config]` | The configuration with all current values. |
| `[config_original]` | The configuration with all original values plus overrides. |
| `[config_name]` | The name of the configuration. |
| `[event]` | The event. |
| `[event:machine_name]` | The machine name of the ECA event. |
| `[session_user]` | The user account that dispatched the event, regardless if ECA is processing models under a different account. This is only available if ECA is configured to always run under a specific account. |
A Drupal configuration was saved.
## Fields
`Config name` : Leave empty to apply to all events. To limit this event to just specific events, provide the machine name of the configuration, e.g. "system.site". This can also be limited to a range of configuration, e.g. with "node.type.", this applies to all configurations with a name starting with this pattern.
```
The configuration name to filter on (e.g., `system.site`). Supports prefix matching: entering `node.type.` matches all content type configurations. Leave empty to react to all configuration saves.
```
`Sync Mode` : During config sync, the Drupal site is in sync mode.
```
Filter by sync mode: 'Always' fires regardless, 'Yes' only during config sync (e.g., `drush config:import`), 'No' only for runtime changes outside of sync.
```
`Write Mode` : Filter by write mode: 'Always' fires for both, 'New' only when configuration is created for the first time, 'Update' only when existing configuration is modified.
# ECA Content
This module provides lots of actions, conditions and events for content entities. Almost all interactions with content entities are covered.
## Installation
```
composer require drupal/eca
drush pm:install eca_content
```
Instead of using Drush to enable the module, you can also go to "Administration / Extend" (`/admin/modules`) and enable the module **ECA Content** from there.
# Entity: clone existing
Available since: 2.1.0
Clone an existing content entity without saving it.
## Fields
`Name of token` : Provide the name of a token that holds the new entity. Please provide the token name only, without brackets.
`Entity label` : The label of the new entity.
`Published` : Whether the entity should be published or not.
`Owner UID` : The owner UID of the new entity.
`Entity` : Provide the token name of the *entity* that this action should operate with. Please provide the token name only, without brackets.
# Entity: set form display
Available since: 1.0.0
Change to a specific form display mode. Only works when reacting upon the event *Prepare content entity form*.
## Fields
`Form display mode` : The machine name of the display mode. Please note: This action only works when reacting upon the event "Prepare content entity form".
# Entity: Set validation error
Available since: 2.1.0
Only works when reacting upon *Validate content entity* events.
## Fields
`Error message` : This field supports tokens.
`Property path` : The optional property path on the entity, where to set the validation error. This may be the machine name of a field (e.g. *body*) or a property of a field. Example: *body.0.value*\
This field supports tokens.
`Entity` : Provide the token name of the *entity* that this action should operate with. Please provide the token name only, without brackets.
# Entity: delete
Available since: 1.0.0
Deletes an existing content entity.
## Fields
`Entity` : Provide the token name of the *entity* that this action should operate with. Please provide the token name only, without brackets.
```
See also: [Tokens being forwarded](https://ecaguide.org/eca/concepts/tokens/#tokens-being-forwarded)
```
# Entity: compare
Available since: 2.0.0
Compare 2 entities and return a list of fields that differ
## Fields
`Return values` : If checked, the list will return values. If unchecked, it will only return the machine names of changed fields.
`Name of token` : Provide the name of a token that holds the result list. Please provide the token name only, without brackets.
`Second entity` : Provide the name of a token that holds the original entity. Please provide the token name only, without brackets.
`Exclude fields` : List machine names of fields that should be ignored. Provide 1 name per line.
`Include fields` : List machine names of fields that should be considered for comparison. Provide 1 name per line. Leave this field empty to include all fields, except for the excluded fields.
`Entity` : Provide the token name of the *entity* that this action should operate with. Please provide the token name only, without brackets.
# Entity: get list of bundles
Available since: 2.1.0
Gets the list of bundles for a given entity type.
## Fields
`Name of token` : Provide the name of a token that holds the bundle list. Please provide the token name only, without brackets.
`Type` : The entity type for which to receive the list of bundles.\
When using the "Defined by token" option, make sure there is a token with this name: *eca_get_bundle_list_type*
`Mode` : This either returns a list of bundle IDs or of their labels.
# Entity: get default field value
Available since: 2.0.0
Get the default value of any field in an entity and store it as a token.
## Fields
`Field name` : The machine name of the field, that holds the value.\
This field supports tokens.
`Name of token` : The field value will be loaded into this specified token. Please provide the token name only, without brackets.
`Entity` : Provide the token name of the *entity* that this action should operate with. Please provide the token name only, without brackets.
# Entity: get list of entity types
Available since: 2.1.0
Gets the list of entity types.
## Fields
`Name of token` : Provide the name of a token that holds the entity types list. Please provide the token name only, without brackets.
`Mode` : This either returns a list of entity type IDs or of their labels.
# Entity: get field value
Available since: 1.0.0
Get the value of any field in an entity and store it as a token.
## Fields
`Field name` : The machine name of the field, that holds the value.\
This field supports tokens.
`Name of token` : The field value will be loaded into this specified token. Please provide the token name only, without brackets.
`Entity` : Provide the token name of the *entity* that this action should operate with. Please provide the token name only, without brackets.
```
See also: [Tokens being forwarded](https://ecaguide.org/eca/concepts/tokens/#tokens-being-forwarded)
```
# List: add entity
Available since: 1.1.0
Add a specified entity to a list.
## Fields
`Method` : Options of the specific method to add a value to a list.\
When using the "Defined by token" option, make sure there is a token with this name: *eca_list_add_entity_method*
`Index key` : When using the method *Set by specified index*, then an index key must be specified here.\
This field supports tokens.
`Token containing the list` : Provide the name of the token that contains a list of items. If the list does not exist yet, a new one will be created. Please provide the token name only, without brackets.
`Entity` : Provide the token name of the *entity* that this action should operate with. Please provide the token name only, without brackets.
# List: remove entity
Available since: 1.1.0
Remove an entity from a list and optionally store the removed entity as a token.
## Fields
`Name of token` : Provide the name of a token that holds the removed entity. Please provide the token name only, without brackets.
`Method` : When using the "Defined by token" option, make sure there is a token with this name: *eca_list_remove_entity_method*
`Index key` : When using the method *Drop by specified index key*, then an index key must be specified here.\
This field supports tokens.
`Token containing the list` : Provide the name of the token that contains a list of items. If the list does not exist yet, a new one will be created. Please provide the token name only, without brackets.
`Entity` : Provide the token name of the *entity* that this action should operate with. Please provide the token name only, without brackets.
# Entity: create new
Available since: 1.0.0
Create a new content entity without saving it.
## Fields
`Name of token` : Provide the name of a token that holds the new entity. Please provide the token name only, without brackets.
`Type` : The type of the new entity.\
Note: to create a new user entity, enable the eca_user module and use the "User: create new" action from there.\
When using the "Defined by token" option, make sure there is a token with this name: *eca_new_entity_type*
`Language` : The language code of the new entity.\
When using the "Defined by token" option, make sure there is a token with this name: *eca_new_entity_langcode*
`Entity label` : The label of the new entity.
`Published` : Whether the entity should be published or not.
`Owner UID` : The owner UID of the new entity.
# Entity: save
Available since: 1.0.0
Saves a new or updates an existing content entity.
## Fields
`Entity` : Provide the token name of the *entity* that this action should operate with. Please provide the token name only, without brackets.
```
See also: [Tokens being forwarded](https://ecaguide.org/eca/concepts/tokens/#tokens-being-forwarded)
```
# Entity: set field value
Available since: 1.0.0
Allows to set, unset or change the value(s) of any field in an entity.
How to empty an entity reference field
Leaving the **Field value** empty and using the "Set and clear previous value" method does not work for entity reference fields. Instead, use the **Remove value instead of adding it** method and provide the current value of the field (e.g. the entity ID) as the **Field value**. If the removed value is the only value in the field, the field will be empty afterwards.
For an example of this, see [this issue](https://www.drupal.org/project/eca/issues/3504388).
## Fields
`Field name` : The machine name of the field, that should be changed. Example: *body.value*\
This field supports tokens.
`Field value` : The new field value.\
This field supports tokens.
`Method` : The method to set an entity, like cleaning the old one, etc..\
When using the "Defined by token" option, make sure there is a token with this name: *eca_set_field_value_method*
```
Available options:
```
- **Set and clear previous value**: Sets the field to the given value. If the field contained any value before, that will be lost, even if this is a multi-value field.
- **Set only when empty**: Sets the field to the given value, but only if the field has been empty before. Otherwise, the previous value will be kept and nothing will be set.
- **Append when not full yet**: If the field allows multiple values, the given value will be appended as a new item, if the max. cardinality is not reached yet. Otherwise, this action will be skipped.
- **Append and drop first when full**: Same as above, but if the max cardinality has been reached, the first value will be removed and the new value appended to the list.
- **Append and drop last when full**: Same as above, but instead of dropping the first, this will drop the last item before appending the new value to the list.
- **Prepend when not full yet**: If the field allows multiple values, the given value will be inserted as the first item, if the max. cardinality is not reached yet. Otherwise, this action will be skipped.
- **Prepend and drop first when full**: Same as above, but if the max cardinality has been reached, the first value will be removed and the new value inserted at the beginning to the list.
- **Prepend and drop last when full**: Same as above, but instead of dropping the first, this will drop the last item before inserting the new value at the beginning of the list.
- **Remove value instead of adding it**: Remove the value from a field, if it's contained in the list of existing items. If this removes the last remaining value of the field, then the field will be empty (for an example of this, see [this issue](https://www.drupal.org/project/eca/issues/3504388)).
`Strip tags` : Remove the tags or not.
`Trim` : Trims the field value or not.
`Save entity` : Saves the entity or not after setting the value.
`Entity` : Provide the token name of the *entity* that this action should operate with. Please provide the token name only, without brackets.
```
See also: [Tokens being forwarded](https://ecaguide.org/eca/concepts/tokens/#tokens-being-forwarded)
```
# Entity: set new revision
Available since: 1.0.0
Flags the entity so that a new revision will be created on the next save.
## Fields
`Create new revision` : Whether to create a new revision or not
`Revision Log` : The optional revision log message.\
This field supports tokens.
`Revision user` : The optional revision user. Leave empty to set the current user.\
This field supports tokens.
`Entity` : Provide the token name of the *entity* that this action should operate with. Please provide the token name only, without brackets.
```
See also: [Tokens being forwarded](https://ecaguide.org/eca/concepts/tokens/#tokens-being-forwarded)
```
# Entity: set view mode
Available since: 2.0.0
Changes the view mode of the entity. Only work after the event "Alter entity view mode".
## Fields
`View mode` : The machine name of the view mode.\
This field supports tokens.
# Entity: load
Available since: 1.0.0
Load a single entity from current scope or by certain properties, and store it as a token.
## Fields
`Name of token` : Provide the name of a token that holds the loaded entity. Please provide the token name only, without brackets.
`Load entity from` : Loads the entity from the given list and/or properties below.\
When using the "Defined by token" option, make sure there is a token with this name: *eca_token_load_entity_from*
`Entity type` : The type of the entity to be loaded.\
When using the "Defined by token" option, make sure there is a token with this name: *eca_token_load_entity_entity_type*
`Entity ID` : The ID of the entity to be loaded.
`Revision ID` : The revision ID of the entity to be loaded.
`Property values` : A key-value list of raw field values of the entity to load. This will only be used when loading by properties is selected above. Supports YAML format. Example: *field_mynumber: 1*. When using tokens and YAML altogether, make sure that tokens are wrapped as a string. Example: *title: "[node:title]"*\
This field supports tokens.
`Language` : The language code of the entity to be loaded.\
When using the "Defined by token" option, make sure there is a token with this name: *eca_token_load_entity_langcode*
`Load latest revision` : Whether loading the latest revision or not.
`Load unchanged values` : Whether loading the unchanged values of fields or not.
`Entity` : Provide the token name of the *entity* that this action should operate with. Please provide the token name only, without brackets.
```
See also: [Tokens being forwarded](https://ecaguide.org/eca/concepts/tokens/#tokens-being-forwarded)
```
# Entity: load via reference
Available since: 1.0.0
Load a single entity that is referenced by an entity from the current scope or by certain properties, and store it as a token.
## Fields
`Field name entity reference` : The field name of the entity reference.\
This field supports tokens.
`Name of token` : Provide the name of a token that holds the loaded entity. Please provide the token name only, without brackets.
`Load entity from` : Loads the entity from the given list and/or properties below.\
When using the "Defined by token" option, make sure there is a token with this name: *eca_token_load_entity_ref_from*
`Entity type` : The type of the entity to be loaded.\
When using the "Defined by token" option, make sure there is a token with this name: *eca_token_load_entity_ref_entity_type*
`Entity ID` : The ID of the entity to be loaded.
`Revision ID` : The revision ID of the entity to be loaded.
`Property values` : A key-value list of raw field values of the entity to load. This will only be used when loading by properties is selected above. Supports YAML format. Example: *field_mynumber: 1*. When using tokens and YAML altogether, make sure that tokens are wrapped as a string. Example: *title: "[node:title]"*\
This field supports tokens.
`Language` : The language code of the entity to be loaded.\
When using the "Defined by token" option, make sure there is a token with this name: *eca_token_load_entity_ref_langcode*
`Load latest revision` : Whether loading the latest revision or not.
`Load unchanged values` : Whether loading the unchanged values of fields or not.
`Entity` : Provide the token name of the *entity* that this action should operate with. Please provide the token name only, without brackets.
```
See also: [Tokens being forwarded](https://ecaguide.org/eca/concepts/tokens/#tokens-being-forwarded)
```
# Trigger a custom event (entity-aware)
Available since: 1.0.0
Triggers a custom event which is entity aware.
See [Custom Event Entity Aware](https://ecaguide.org/plugins/eca/content/events/content_entity_custom/index.md), which is listening to this action here.
## Fields
`Event ID` : The ID of the event. Leave empty to trigger all custom events.
```
The id of the custom event (entity aware).
```
`Tokens to forward` : Comma separated list of token names from the current context, that will be forwarded to the triggered event. These tokens are then also available for subsequent conditions and actions within the current process.
`Entity` : Provide the token name of the *entity* that this action should operate with. Please provide the token name only, without brackets.
```
See also: [Tokens being forwarded](https://ecaguide.org/eca/concepts/tokens/#tokens-being-forwarded)
```
# Entity form: compare display mode
Available since: 1.0.0
Evaluates against the machine name of the entity form display mode.
## Fields
`Form display mode` : The machine name of the display mode. Example: *default*
`Comparison operator` : The available comparison operators like *equals* or *less than*.\
When using the "Defined by token" option, make sure there is a token with this name: *eca_content_form_display_mode_operator*
```
Comparison operator. Possible values:
```
- equal
- greater than
- less than
- starts with
- ends with
- contains
- at least
- at most
`Comparison type` : The type of the comparison.\
When using the "Defined by token" option, make sure there is a token with this name: *eca_content_form_display_mode_type*
`Case sensitive comparison` : Compare the values based on case sensitivity.
`Negate the condition` : Negates the condition. Makes TRUE to FALSE and vice versa.
# Entity: compare
Available since: 2.0.0
Compares two entities.
## Fields
`Name of token` : Provide the name of a token that holds the result list. Please provide the token name only, without brackets.
`Second entity` : Provide the name of a token that holds the original entity. Please provide the token name only, without brackets.
`Exclude fields` : List machine names of fields that should be ignored. Provide 1 name per line.
`Include fields` : List machine names of fields that should be considered for comparison. Provide 1 name per line. Leave this field empty to include all fields, except for the excluded fields.
`Negate the condition` : Negates the condition. Makes TRUE to FALSE and vice versa.
`Entity` : Provide the token name of the *entity* that this condition should operate with. Please provide the token name only, without brackets.
# Entity: exists
Available since: 1.0.0
Performs a lookup whether an entity exists and is accessible.
## Fields
`Load entity from` : Loads the entity from the given list and/or properties below.\
When using the "Defined by token" option, make sure there is a token with this name: *eca_entity_exists_from*
`Entity type` : The type of the entity to be loaded.\
When using the "Defined by token" option, make sure there is a token with this name: *eca_entity_exists_entity_type*
`Entity ID` : The ID of the entity to be loaded.
`Revision ID` : The revision ID of the entity to be loaded.
`Property values` : A key-value list of raw field values of the entity to load. This will only be used when loading by properties is selected above. Supports YAML format. Example: *field_mynumber: 1*. When using tokens and YAML altogether, make sure that tokens are wrapped as a string. Example: *title: "[node:title]"*\
This field supports tokens.
`Language` : The language code of the entity to be loaded.\
When using the "Defined by token" option, make sure there is a token with this name: *eca_entity_exists_langcode*
`Load latest revision` : Whether loading the latest revision or not.
`Load unchanged values` : Whether loading the unchanged values of fields or not.
`Negate the condition` : Negates the condition. Makes TRUE to FALSE and vice versa.
`Entity` : Provide the token name of the *entity* that this condition should operate with. Please provide the token name only, without brackets.
```
See also: [Tokens being forwarded](https://ecaguide.org/eca/concepts/tokens/#tokens-being-forwarded)
```
# Entity: field is accessible
Available since: 1.0.0
Evaluates whether the current user has operational access on an entity field.
## Fields
`Field machine name` : The machine name of the field to check.
`Operation` : The operation, like view, edit or delete to check accessibility.\
When using the "Defined by token" option, make sure there is a token with this name: *eca_entity_field_is_accessible_operation*
`Negate the condition` : Negates the condition. Makes TRUE to FALSE and vice versa.
`Entity` : Provide the token name of the *entity* that this condition should operate with. Please provide the token name only, without brackets.
```
See also: [Tokens being forwarded](https://ecaguide.org/eca/concepts/tokens/#tokens-being-forwarded)
```
# Entity: compare field value
Available since: 1.0.0
Compares a field value with an expected custom value.
## Fields
`Field name` : The name of the field whose value to compare.
`Expected field value` : The expected value.
`Comparison operator` : The available comparison operators like *equals* or *less than*.\
When using the "Defined by token" option, make sure there is a token with this name: *eca_entity_field_value_operator*
```
Comparison operator. Possible values:
```
- equal
- greater than
- less than
- starts with
- ends with
- contains
- at least
- at most
`Comparison type` : The type of the comparison.\
When using the "Defined by token" option, make sure there is a token with this name: *eca_entity_field_value_type*
`Case sensitive comparison` : Compare the values based on case sensitivity.
`Negate the condition` : Negates the condition. Makes TRUE to FALSE and vice versa.
`Entity` : Provide the token name of the *entity* that this condition should operate with. Please provide the token name only, without brackets.
```
See also: [Tokens being forwarded](https://ecaguide.org/eca/concepts/tokens/#tokens-being-forwarded)
```
# Entity: field value changed
Available since: 1.0.0
Evaluates against the change of a value field of an entity.
## Fields
`Field name` : The field name of the entity to check, if its value has changed.
`Negate the condition` : Negates the condition. Makes TRUE to FALSE and vice versa.
`Entity` : Provide the token name of the *entity* that this condition should operate with. Please provide the token name only, without brackets.
```
See also: [Tokens being forwarded](https://ecaguide.org/eca/concepts/tokens/#tokens-being-forwarded)
```
# Entity: field value is empty
Available since:
Evaluates if a value field of an entity is empty.
## Fields
`Field name` : The field name of the entity to check, if its value is empty.
`Negate the condition` : Negates the condition. Makes TRUE to FALSE and vice versa.
`Entity` : Provide the token name of the *entity* that this condition should operate with. Please provide the token name only, without brackets.
```
See also: [Tokens being forwarded](https://ecaguide.org/eca/concepts/tokens/#tokens-being-forwarded)
```
# Entity: is accessible
Available since: 1.0.0
Evaluates whether the current user has operational access on an entity.
## Fields
`Operation` : The operation, like view, edit or delete to check accessibility.\
When using the "Defined by token" option, make sure there is a token with this name: *eca_entity_is_accessible_operation*
`Negate the condition` : Negates the condition. Makes TRUE to FALSE and vice versa.
`Entity` : Provide the token name of the *entity* that this condition should operate with. Please provide the token name only, without brackets.
```
See also: [Tokens being forwarded](https://ecaguide.org/eca/concepts/tokens/#tokens-being-forwarded)
```
# Entity: is new
Available since: 1.0.0
Evaluates if an entity is new.
## Fields
`Negate the condition` : Negates the condition. Makes TRUE to FALSE and vice versa.
`Entity` : Provide the token name of the *entity* that this condition should operate with. Please provide the token name only, without brackets.
```
See also: [Tokens being forwarded](https://ecaguide.org/eca/concepts/tokens/#tokens-being-forwarded)
```
# Entity: original has field value
Available since: 1.0.0
Compares a field value of an entities *original* by specific properties.
## Fields
`Field name` : The name of the field whose value to compare.
`Expected field value` : The expected value.
`Comparison operator` : The available comparison operators like *equals* or *less than*.\
When using the "Defined by token" option, make sure there is a token with this name: *eca_entity_original_field_value_operator*
```
Comparison operator. Possible values:
```
- equal
- greater than
- less than
- starts with
- ends with
- contains
- at least
- at most
`Comparison type` : The type of the comparison.\
When using the "Defined by token" option, make sure there is a token with this name: *eca_entity_original_field_value_type*
`Case sensitive comparison` : Compare the values based on case sensitivity.
`Negate the condition` : Negates the condition. Makes TRUE to FALSE and vice versa.
`Entity` : Provide the token name of the *entity* that this condition should operate with. Please provide the token name only, without brackets.
```
See also: [Tokens being forwarded](https://ecaguide.org/eca/concepts/tokens/#tokens-being-forwarded)
```
# Entity type and bundle
Available since: 1.0.0
Evaluates against the entity type and bundle.
## Fields
`Entity type (and bundle)` : When using the "Defined by token" option, make sure there is a token with this name: *eca_entity_type_bundle_type*
```
This field provides a list with all created content entities and its bundles. You can also select **-any-**, if you
want to react to all types or bundles.
```
`Negate the condition` : Negates the condition. Makes TRUE to FALSE and vice versa.
`Entity` : Provide the token name of the *entity* that this condition should operate with. Please provide the token name only, without brackets.
# Initialize content entity bundle
Available since: 1.0.0
An entity bundle object is being created (instantiated) on runtime, without being saved.
Provided tokens
| Token | Description |
| ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `[user]` | The current user. Alias: `current_user` |
| `[htmx]` | Information about the current HTMX request. |
| `[htmx:is_request]` | Whether the current request was sent by HTMX ("1" or "0"). |
| `[htmx:boosted]` | Whether the current request was boosted by HTMX ("1" or "0"). |
| `[htmx:history_restore]` | Whether the current request is for HTMX history restoration ("1" or "0"). |
| `[htmx:target]` | The value of the HX-Target request header. |
| `[htmx:trigger]` | The value of the HX-Trigger request header. |
| `[htmx:trigger_name]` | The value of the HX-Trigger-Name request header. |
| `[htmx:current_url]` | The value of the HX-Current-URL request header. |
| `[htmx:prompt]` | The value of the HX-Prompt request header. |
| `[event]` | The event. |
| `[event:entity_type_id]` | The entity type. |
| `[event:bundle]` | The bundle machine name. |
| `[event]` | The event. |
| `[event:machine_name]` | The machine name of the ECA event. |
| `[session_user]` | The user account that dispatched the event, regardless if ECA is processing models under a different account. This is only available if ECA is configured to always run under a specific account. |
Reacts when specific content entity bundles are created.
## Fields
`Type (and bundle)` : This field provides a list with all created content entities and its bundles. You can also select **-any-**, if you want to react to all types or bundles.
# Delete content entity bundle
Available since: 1.0.0
Provided tokens
| Token | Description |
| ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `[user]` | The current user. Alias: `current_user` |
| `[htmx]` | Information about the current HTMX request. |
| `[htmx:is_request]` | Whether the current request was sent by HTMX ("1" or "0"). |
| `[htmx:boosted]` | Whether the current request was boosted by HTMX ("1" or "0"). |
| `[htmx:history_restore]` | Whether the current request is for HTMX history restoration ("1" or "0"). |
| `[htmx:target]` | The value of the HX-Target request header. |
| `[htmx:trigger]` | The value of the HX-Trigger request header. |
| `[htmx:trigger_name]` | The value of the HX-Trigger-Name request header. |
| `[htmx:current_url]` | The value of the HX-Current-URL request header. |
| `[htmx:prompt]` | The value of the HX-Prompt request header. |
| `[event]` | The event. |
| `[event:entity_type_id]` | The entity type. |
| `[event:bundle]` | The bundle machine name. |
| `[event]` | The event. |
| `[event:machine_name]` | The machine name of the ECA event. |
| `[session_user]` | The user account that dispatched the event, regardless if ECA is processing models under a different account. This is only available if ECA is configured to always run under a specific account. |
Reacts when specific content entity bundles are deleted.
## Fields
`Type (and bundle)` : This field provides a list with all created content entities and its bundles. You can also select **-any-**, if you want to react to all types or bundles.
# Initialize content entity
Available since: 1.0.0
An entity object is being created (instantiated) on runtime, without being saved.
Provided tokens
| Token | Description |
| ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `[user]` | The current user. Alias: `current_user` |
| `[htmx]` | Information about the current HTMX request. |
| `[htmx:is_request]` | Whether the current request was sent by HTMX ("1" or "0"). |
| `[htmx:boosted]` | Whether the current request was boosted by HTMX ("1" or "0"). |
| `[htmx:history_restore]` | Whether the current request is for HTMX history restoration ("1" or "0"). |
| `[htmx:target]` | The value of the HX-Target request header. |
| `[htmx:trigger]` | The value of the HX-Trigger request header. |
| `[htmx:trigger_name]` | The value of the HX-Trigger-Name request header. |
| `[htmx:current_url]` | The value of the HX-Current-URL request header. |
| `[htmx:prompt]` | The value of the HX-Prompt request header. |
| `[event]` | The event. |
| `[event:machine_name]` | The machine name of the ECA event. |
| `[session_user]` | The user account that dispatched the event, regardless if ECA is processing models under a different account. This is only available if ECA is configured to always run under a specific account. |
| `[entity]` | The entity of the event. |
| `[ENTITY_TYPE]` | The entity of the event under the name of its entity type. |
Reacts when specific content entities are created.
## Fields
`Type (and bundle)` : This field provides a list with all created content entities and its bundles. You can also select **-any-**, if you want to react to all types or bundles.
# ECA custom event (entity-aware)
Available since: 1.0.0
Provided tokens
| Token | Description |
| ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `[user]` | The current user. Alias: `current_user` |
| `[htmx]` | Information about the current HTMX request. |
| `[htmx:is_request]` | Whether the current request was sent by HTMX ("1" or "0"). |
| `[htmx:boosted]` | Whether the current request was boosted by HTMX ("1" or "0"). |
| `[htmx:history_restore]` | Whether the current request is for HTMX history restoration ("1" or "0"). |
| `[htmx:target]` | The value of the HX-Target request header. |
| `[htmx:trigger]` | The value of the HX-Trigger request header. |
| `[htmx:trigger_name]` | The value of the HX-Trigger-Name request header. |
| `[htmx:current_url]` | The value of the HX-Current-URL request header. |
| `[htmx:prompt]` | The value of the HX-Prompt request header. |
| `[event]` | The event. |
| `[event:machine_name]` | The machine name of the ECA event. |
| `[VARIOUS]` | All tokens forwarded by the dispatcher. |
| `[session_user]` | The user account that dispatched the event, regardless if ECA is processing models under a different account. This is only available if ECA is configured to always run under a specific account. |
| `[entity]` | The entity of the event. |
| `[ENTITY_TYPE]` | The entity of the event under the name of its entity type. |
Reacts on an event with a specific ID, when an entity is involved.
## Fields
`Event ID` : The id of the custom event (entity aware). Leave empty to trigger all entity aware events.
```
The id of the custom event (entity aware).
```
# Delete content entity
Available since: 1.0.0
Provided tokens
| Token | Description |
| ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `[user]` | The current user. Alias: `current_user` |
| `[htmx]` | Information about the current HTMX request. |
| `[htmx:is_request]` | Whether the current request was sent by HTMX ("1" or "0"). |
| `[htmx:boosted]` | Whether the current request was boosted by HTMX ("1" or "0"). |
| `[htmx:history_restore]` | Whether the current request is for HTMX history restoration ("1" or "0"). |
| `[htmx:target]` | The value of the HX-Target request header. |
| `[htmx:trigger]` | The value of the HX-Trigger request header. |
| `[htmx:trigger_name]` | The value of the HX-Trigger-Name request header. |
| `[htmx:current_url]` | The value of the HX-Current-URL request header. |
| `[htmx:prompt]` | The value of the HX-Prompt request header. |
| `[event]` | The event. |
| `[event:machine_name]` | The machine name of the ECA event. |
| `[session_user]` | The user account that dispatched the event, regardless if ECA is processing models under a different account. This is only available if ECA is configured to always run under a specific account. |
| `[entity]` | The entity of the event. |
| `[ENTITY_TYPE]` | The entity of the event under the name of its entity type. |
Reacts when specific content entities are deleted.
## Fields
`Type (and bundle)` : This field provides a list with all created content entities and its bundles. You can also select **-any-**, if you want to react to all types or bundles.
# Init content entity field values
Available since: 1.0.0
Provided tokens
| Token | Description |
| ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `[user]` | The current user. Alias: `current_user` |
| `[htmx]` | Information about the current HTMX request. |
| `[htmx:is_request]` | Whether the current request was sent by HTMX ("1" or "0"). |
| `[htmx:boosted]` | Whether the current request was boosted by HTMX ("1" or "0"). |
| `[htmx:history_restore]` | Whether the current request is for HTMX history restoration ("1" or "0"). |
| `[htmx:target]` | The value of the HX-Target request header. |
| `[htmx:trigger]` | The value of the HX-Trigger request header. |
| `[htmx:trigger_name]` | The value of the HX-Trigger-Name request header. |
| `[htmx:current_url]` | The value of the HX-Current-URL request header. |
| `[htmx:prompt]` | The value of the HX-Prompt request header. |
| `[event]` | The event. |
| `[event:machine_name]` | The machine name of the ECA event. |
| `[session_user]` | The user account that dispatched the event, regardless if ECA is processing models under a different account. This is only available if ECA is configured to always run under a specific account. |
| `[entity]` | The entity of the event. |
| `[ENTITY_TYPE]` | The entity of the event under the name of its entity type. |
Reacts when field values of specific content entities are initialized.
## Fields
`Type (and bundle)` : This field provides a list with all created content entities and its bundles. You can also select **-any-**, if you want to react to all types or bundles.
# Insert content entity
Available since: 1.0.0
After a new entity got saved (persistently created).
Provided tokens
| Token | Description |
| ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `[user]` | The current user. Alias: `current_user` |
| `[htmx]` | Information about the current HTMX request. |
| `[htmx:is_request]` | Whether the current request was sent by HTMX ("1" or "0"). |
| `[htmx:boosted]` | Whether the current request was boosted by HTMX ("1" or "0"). |
| `[htmx:history_restore]` | Whether the current request is for HTMX history restoration ("1" or "0"). |
| `[htmx:target]` | The value of the HX-Target request header. |
| `[htmx:trigger]` | The value of the HX-Trigger request header. |
| `[htmx:trigger_name]` | The value of the HX-Trigger-Name request header. |
| `[htmx:current_url]` | The value of the HX-Current-URL request header. |
| `[htmx:prompt]` | The value of the HX-Prompt request header. |
| `[event]` | The event. |
| `[event:machine_name]` | The machine name of the ECA event. |
| `[session_user]` | The user account that dispatched the event, regardless if ECA is processing models under a different account. This is only available if ECA is configured to always run under a specific account. |
| `[entity]` | The entity of the event. |
| `[ENTITY_TYPE]` | The entity of the event under the name of its entity type. |
Reacts when specific content entities are inserted.
## Fields
`Type (and bundle)` : This field provides a list with all created content entities and its bundles. You can also select **-any-**, if you want to react to all types or bundles.
# Load content entity
Available since: 1.0.0
Provided tokens
| Token | Description |
| ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `[user]` | The current user. Alias: `current_user` |
| `[htmx]` | Information about the current HTMX request. |
| `[htmx:is_request]` | Whether the current request was sent by HTMX ("1" or "0"). |
| `[htmx:boosted]` | Whether the current request was boosted by HTMX ("1" or "0"). |
| `[htmx:history_restore]` | Whether the current request is for HTMX history restoration ("1" or "0"). |
| `[htmx:target]` | The value of the HX-Target request header. |
| `[htmx:trigger]` | The value of the HX-Trigger request header. |
| `[htmx:trigger_name]` | The value of the HX-Trigger-Name request header. |
| `[htmx:current_url]` | The value of the HX-Current-URL request header. |
| `[htmx:prompt]` | The value of the HX-Prompt request header. |
| `[event]` | The event. |
| `[event:machine_name]` | The machine name of the ECA event. |
| `[session_user]` | The user account that dispatched the event, regardless if ECA is processing models under a different account. This is only available if ECA is configured to always run under a specific account. |
| `[entity]` | The entity of the event. |
| `[ENTITY_TYPE]` | The entity of the event under the name of its entity type. |
Reacts when specific content entities are loaded.
## Fields
`Type (and bundle)` : This field provides a list with all created content entities and its bundles. You can also select **-any-**, if you want to react to all types or bundles.
# Options field selection
Available since: 1.0.0
Provided tokens
| Token | Description |
| ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `[user]` | The current user. Alias: `current_user` |
| `[htmx]` | Information about the current HTMX request. |
| `[htmx:is_request]` | Whether the current request was sent by HTMX ("1" or "0"). |
| `[htmx:boosted]` | Whether the current request was boosted by HTMX ("1" or "0"). |
| `[htmx:history_restore]` | Whether the current request is for HTMX history restoration ("1" or "0"). |
| `[htmx:target]` | The value of the HX-Target request header. |
| `[htmx:trigger]` | The value of the HX-Trigger request header. |
| `[htmx:trigger_name]` | The value of the HX-Trigger-Name request header. |
| `[htmx:current_url]` | The value of the HX-Current-URL request header. |
| `[htmx:prompt]` | The value of the HX-Prompt request header. |
| `[event]` | The event. |
| `[event:machine_name]` | The machine name of the ECA event. |
| `[session_user]` | The user account that dispatched the event, regardless if ECA is processing models under a different account. This is only available if ECA is configured to always run under a specific account. |
| `[entity]` | The entity of the event. |
| `[ENTITY_TYPE]` | The entity of the event under the name of its entity type. |
Reacts when entity options of specific content entities are selected.
## Fields
`Type (and bundle)` : This field provides a list with all created content entities and its bundles. You can also select **-any-**, if you want to react to all types or bundles.
`Restrict by field (machine name)` : The machine name of the field to restrict the event trigger.
```
The machine name of the options field to react to. Leave empty to react to all options fields using the ECA handler.
```
`Token name holding the selection` : The name of the token to hold the selection. Please provide the token name only, without brackets.
```
The token name to hold the selection results. After ECA actions run, the values in this token become the allowed options (key-value pairs where keys are option values and values are labels).
```
# Predelete content entity
Available since: 1.0.0
Provided tokens
| Token | Description |
| ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `[user]` | The current user. Alias: `current_user` |
| `[htmx]` | Information about the current HTMX request. |
| `[htmx:is_request]` | Whether the current request was sent by HTMX ("1" or "0"). |
| `[htmx:boosted]` | Whether the current request was boosted by HTMX ("1" or "0"). |
| `[htmx:history_restore]` | Whether the current request is for HTMX history restoration ("1" or "0"). |
| `[htmx:target]` | The value of the HX-Target request header. |
| `[htmx:trigger]` | The value of the HX-Trigger request header. |
| `[htmx:trigger_name]` | The value of the HX-Trigger-Name request header. |
| `[htmx:current_url]` | The value of the HX-Current-URL request header. |
| `[htmx:prompt]` | The value of the HX-Prompt request header. |
| `[event]` | The event. |
| `[event:machine_name]` | The machine name of the ECA event. |
| `[session_user]` | The user account that dispatched the event, regardless if ECA is processing models under a different account. This is only available if ECA is configured to always run under a specific account. |
| `[entity]` | The entity of the event. |
| `[ENTITY_TYPE]` | The entity of the event under the name of its entity type. |
Reacts before specific content entities are deleted.
## Fields
`Type (and bundle)` : This field provides a list with all created content entities and its bundles. You can also select **-any-**, if you want to react to all types or bundles.
# Preload content entity
Available since: 1.0.0
Provided tokens
| Token | Description |
| ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `[user]` | The current user. Alias: `current_user` |
| `[htmx]` | Information about the current HTMX request. |
| `[htmx:is_request]` | Whether the current request was sent by HTMX ("1" or "0"). |
| `[htmx:boosted]` | Whether the current request was boosted by HTMX ("1" or "0"). |
| `[htmx:history_restore]` | Whether the current request is for HTMX history restoration ("1" or "0"). |
| `[htmx:target]` | The value of the HX-Target request header. |
| `[htmx:trigger]` | The value of the HX-Trigger request header. |
| `[htmx:trigger_name]` | The value of the HX-Trigger-Name request header. |
| `[htmx:current_url]` | The value of the HX-Current-URL request header. |
| `[htmx:prompt]` | The value of the HX-Prompt request header. |
| `[event]` | The event. |
| `[event:entity_type_id]` | The entity type. |
| `[event:ids]` | The IDs of the entities. |
| `[event]` | The event. |
| `[event:machine_name]` | The machine name of the ECA event. |
| `[session_user]` | The user account that dispatched the event, regardless if ECA is processing models under a different account. This is only available if ECA is configured to always run under a specific account. |
Reacts before specific content entities are loaded.
## Fields
`Type (and bundle)` : This field provides a list with all created content entities and its bundles. You can also select **-any-**, if you want to react to all types or bundles.
# Prepare content entity form
Available since: 1.0.0
Provided tokens
| Token | Description |
| -------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `[user]` | The current user. Alias: `current_user` |
| `[form]` | The current form. Alias: `current_form` |
| `[form:id]` | The form ID. |
| `[form:base_id]` | The form base ID. |
| `[form:operation]` | The form operation. |
| `[form:mode]` | The form mode. |
| `[form:triggered]` | The form field name that triggered the form event. |
| `[form:values]` | |
| `[form:values:FIELD_NAME]` | The field value for each of the named fields. |
| `[form:num_errors]` | The number of form errors. |
| `[htmx]` | Information about the current HTMX request. |
| `[htmx:is_request]` | Whether the current request was sent by HTMX ("1" or "0"). |
| `[htmx:boosted]` | Whether the current request was boosted by HTMX ("1" or "0"). |
| `[htmx:history_restore]` | Whether the current request is for HTMX history restoration ("1" or "0"). |
| `[htmx:target]` | The value of the HX-Target request header. |
| `[htmx:trigger]` | The value of the HX-Trigger request header. |
| `[htmx:trigger_name]` | The value of the HX-Trigger-Name request header. |
| `[htmx:current_url]` | The value of the HX-Current-URL request header. |
| `[htmx:prompt]` | The value of the HX-Prompt request header. |
| `[event]` | The event. |
| `[event:machine_name]` | The machine name of the ECA event. |
| `[session_user]` | The user account that dispatched the event, regardless if ECA is processing models under a different account. This is only available if ECA is configured to always run under a specific account. |
| `[entity]` | The entity of the event. |
| `[ENTITY_TYPE]` | The entity of the event under the name of its entity type. |
Reacts when forms of specific content entities are prepared.
## Fields
`Type (and bundle)` : This field provides a list with all created content entities and its bundles. You can also select **-any-**, if you want to react to all types or bundles.
# Prepare content entity view
Available since: 1.0.0
Provided tokens
| Token | Description |
| ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `[user]` | The current user. Alias: `current_user` |
| `[htmx]` | Information about the current HTMX request. |
| `[htmx:is_request]` | Whether the current request was sent by HTMX ("1" or "0"). |
| `[htmx:boosted]` | Whether the current request was boosted by HTMX ("1" or "0"). |
| `[htmx:history_restore]` | Whether the current request is for HTMX history restoration ("1" or "0"). |
| `[htmx:target]` | The value of the HX-Target request header. |
| `[htmx:trigger]` | The value of the HX-Trigger request header. |
| `[htmx:trigger_name]` | The value of the HX-Trigger-Name request header. |
| `[htmx:current_url]` | The value of the HX-Current-URL request header. |
| `[htmx:prompt]` | The value of the HX-Prompt request header. |
| `[entity_view_mode]` | The entity view mode. |
| `[event]` | The event. |
| `[event:machine_name]` | The machine name of the ECA event. |
| `[session_user]` | The user account that dispatched the event, regardless if ECA is processing models under a different account. This is only available if ECA is configured to always run under a specific account. |
| `[entity]` | The entity of the event. |
| `[ENTITY_TYPE]` | The entity of the event under the name of its entity type. |
Reacts when views of specific content entities are prepared.
## Fields
`Type (and bundle)` : This field provides a list with all created content entities and its bundles. You can also select **-any-**, if you want to react to all types or bundles.
# Presave content entity
Available since: 1.0.0
Before a new or existing entity gets saved (persistently created or changed).
Provided tokens
| Token | Description |
| ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `[user]` | The current user. Alias: `current_user` |
| `[htmx]` | Information about the current HTMX request. |
| `[htmx:is_request]` | Whether the current request was sent by HTMX ("1" or "0"). |
| `[htmx:boosted]` | Whether the current request was boosted by HTMX ("1" or "0"). |
| `[htmx:history_restore]` | Whether the current request is for HTMX history restoration ("1" or "0"). |
| `[htmx:target]` | The value of the HX-Target request header. |
| `[htmx:trigger]` | The value of the HX-Trigger request header. |
| `[htmx:trigger_name]` | The value of the HX-Trigger-Name request header. |
| `[htmx:current_url]` | The value of the HX-Current-URL request header. |
| `[htmx:prompt]` | The value of the HX-Prompt request header. |
| `[event]` | The event. |
| `[event:machine_name]` | The machine name of the ECA event. |
| `[session_user]` | The user account that dispatched the event, regardless if ECA is processing models under a different account. This is only available if ECA is configured to always run under a specific account. |
| `[entity]` | The entity of the event. |
| `[ENTITY_TYPE]` | The entity of the event under the name of its entity type. |
Reacts before specific content entities are saved.
## Fields
`Type (and bundle)` : This field provides a list with all created content entities and its bundles. You can also select **-any-**, if you want to react to all types or bundles.
# Entity reference field selection
Available since: 1.0.0
Provided tokens
| Token | Description |
| ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `[user]` | The current user. Alias: `current_user` |
| `[htmx]` | Information about the current HTMX request. |
| `[htmx:is_request]` | Whether the current request was sent by HTMX ("1" or "0"). |
| `[htmx:boosted]` | Whether the current request was boosted by HTMX ("1" or "0"). |
| `[htmx:history_restore]` | Whether the current request is for HTMX history restoration ("1" or "0"). |
| `[htmx:target]` | The value of the HX-Target request header. |
| `[htmx:trigger]` | The value of the HX-Trigger request header. |
| `[htmx:trigger_name]` | The value of the HX-Trigger-Name request header. |
| `[htmx:current_url]` | The value of the HX-Current-URL request header. |
| `[htmx:prompt]` | The value of the HX-Prompt request header. |
| `[event]` | The event. |
| `[event:machine_name]` | The machine name of the ECA event. |
| `[session_user]` | The user account that dispatched the event, regardless if ECA is processing models under a different account. This is only available if ECA is configured to always run under a specific account. |
| `[entity]` | The entity of the event. |
| `[ENTITY_TYPE]` | The entity of the event under the name of its entity type. |
Reacts when entity reference field of specific content entities are selected.
## Fields
`Type (and bundle)` : This field provides a list with all created content entities and its bundles. You can also select **-any-**, if you want to react to all types or bundles.
`Restrict by field (machine name)` : The machine name of the field to restrict the event trigger.
```
The machine name of the entity reference field to react to. Leave empty to react to all entity reference fields using the ECA handler.
```
`Token name holding the selection` : The name of the token to hold the selection. Please provide the token name only, without brackets.
```
The token name to hold the selection results. After ECA actions run, the entities in this token become the referenceable entities.
```
# Initialize content entity revision
Available since: 1.0.0
An entity revision object is being created (instantiated) on runtime, without being saved.
Provided tokens
| Token | Description |
| ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `[user]` | The current user. Alias: `current_user` |
| `[htmx]` | Information about the current HTMX request. |
| `[htmx:is_request]` | Whether the current request was sent by HTMX ("1" or "0"). |
| `[htmx:boosted]` | Whether the current request was boosted by HTMX ("1" or "0"). |
| `[htmx:history_restore]` | Whether the current request is for HTMX history restoration ("1" or "0"). |
| `[htmx:target]` | The value of the HX-Target request header. |
| `[htmx:trigger]` | The value of the HX-Trigger request header. |
| `[htmx:trigger_name]` | The value of the HX-Trigger-Name request header. |
| `[htmx:current_url]` | The value of the HX-Current-URL request header. |
| `[htmx:prompt]` | The value of the HX-Prompt request header. |
| `[event]` | The event. |
| `[event:machine_name]` | The machine name of the ECA event. |
| `[session_user]` | The user account that dispatched the event, regardless if ECA is processing models under a different account. This is only available if ECA is configured to always run under a specific account. |
| `[entity]` | The entity of the event. |
| `[ENTITY_TYPE]` | The entity of the event under the name of its entity type. |
Reacts when revisions of specific content entities are created.
## Fields
`Type (and bundle)` : This field provides a list with all created content entities and its bundles. You can also select **-any-**, if you want to react to all types or bundles.
# Delete content entity revision
Available since: 1.0.0
Provided tokens
| Token | Description |
| ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `[user]` | The current user. Alias: `current_user` |
| `[htmx]` | Information about the current HTMX request. |
| `[htmx:is_request]` | Whether the current request was sent by HTMX ("1" or "0"). |
| `[htmx:boosted]` | Whether the current request was boosted by HTMX ("1" or "0"). |
| `[htmx:history_restore]` | Whether the current request is for HTMX history restoration ("1" or "0"). |
| `[htmx:target]` | The value of the HX-Target request header. |
| `[htmx:trigger]` | The value of the HX-Trigger request header. |
| `[htmx:trigger_name]` | The value of the HX-Trigger-Name request header. |
| `[htmx:current_url]` | The value of the HX-Current-URL request header. |
| `[htmx:prompt]` | The value of the HX-Prompt request header. |
| `[event]` | The event. |
| `[event:machine_name]` | The machine name of the ECA event. |
| `[session_user]` | The user account that dispatched the event, regardless if ECA is processing models under a different account. This is only available if ECA is configured to always run under a specific account. |
| `[entity]` | The entity of the event. |
| `[ENTITY_TYPE]` | The entity of the event under the name of its entity type. |
Reacts when revisions of specific content entities are deleted.
## Fields
`Type (and bundle)` : This field provides a list with all created content entities and its bundles. You can also select **-any-**, if you want to react to all types or bundles.
# Load content entity from storage
Available since: 1.0.0
Provided tokens
| Token | Description |
| ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `[user]` | The current user. Alias: `current_user` |
| `[htmx]` | Information about the current HTMX request. |
| `[htmx:is_request]` | Whether the current request was sent by HTMX ("1" or "0"). |
| `[htmx:boosted]` | Whether the current request was boosted by HTMX ("1" or "0"). |
| `[htmx:history_restore]` | Whether the current request is for HTMX history restoration ("1" or "0"). |
| `[htmx:target]` | The value of the HX-Target request header. |
| `[htmx:trigger]` | The value of the HX-Trigger request header. |
| `[htmx:trigger_name]` | The value of the HX-Trigger-Name request header. |
| `[htmx:current_url]` | The value of the HX-Current-URL request header. |
| `[htmx:prompt]` | The value of the HX-Prompt request header. |
| `[event]` | The event. |
| `[event:machine_name]` | The machine name of the ECA event. |
| `[session_user]` | The user account that dispatched the event, regardless if ECA is processing models under a different account. This is only available if ECA is configured to always run under a specific account. |
| `[entity]` | The entity of the event. |
| `[ENTITY_TYPE]` | The entity of the event under the name of its entity type. |
Reacts when specific content entities are loaded from storage.
## Fields
`Type (and bundle)` : This field provides a list with all created content entities and its bundles. You can also select **-any-**, if you want to react to all types or bundles.
# Initialize content entity translation
Available since: 1.0.0
An entity translation object is being created (instantiated) on runtime, without being saved.
Provided tokens
| Token | Description |
| ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `[user]` | The current user. Alias: `current_user` |
| `[htmx]` | Information about the current HTMX request. |
| `[htmx:is_request]` | Whether the current request was sent by HTMX ("1" or "0"). |
| `[htmx:boosted]` | Whether the current request was boosted by HTMX ("1" or "0"). |
| `[htmx:history_restore]` | Whether the current request is for HTMX history restoration ("1" or "0"). |
| `[htmx:target]` | The value of the HX-Target request header. |
| `[htmx:trigger]` | The value of the HX-Trigger request header. |
| `[htmx:trigger_name]` | The value of the HX-Trigger-Name request header. |
| `[htmx:current_url]` | The value of the HX-Current-URL request header. |
| `[htmx:prompt]` | The value of the HX-Prompt request header. |
| `[event]` | The event. |
| `[event:machine_name]` | The machine name of the ECA event. |
| `[session_user]` | The user account that dispatched the event, regardless if ECA is processing models under a different account. This is only available if ECA is configured to always run under a specific account. |
| `[entity]` | The entity of the event. |
| `[ENTITY_TYPE]` | The entity of the event under the name of its entity type. |
Reacts when translations of specific content entities are created.
## Fields
`Type (and bundle)` : This field provides a list with all created content entities and its bundles. You can also select **-any-**, if you want to react to all types or bundles.
# Delete content entity translation
Available since: 1.0.0
Provided tokens
| Token | Description |
| ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `[user]` | The current user. Alias: `current_user` |
| `[htmx]` | Information about the current HTMX request. |
| `[htmx:is_request]` | Whether the current request was sent by HTMX ("1" or "0"). |
| `[htmx:boosted]` | Whether the current request was boosted by HTMX ("1" or "0"). |
| `[htmx:history_restore]` | Whether the current request is for HTMX history restoration ("1" or "0"). |
| `[htmx:target]` | The value of the HX-Target request header. |
| `[htmx:trigger]` | The value of the HX-Trigger request header. |
| `[htmx:trigger_name]` | The value of the HX-Trigger-Name request header. |
| `[htmx:current_url]` | The value of the HX-Current-URL request header. |
| `[htmx:prompt]` | The value of the HX-Prompt request header. |
| `[event]` | The event. |
| `[event:machine_name]` | The machine name of the ECA event. |
| `[session_user]` | The user account that dispatched the event, regardless if ECA is processing models under a different account. This is only available if ECA is configured to always run under a specific account. |
| `[entity]` | The entity of the event. |
| `[ENTITY_TYPE]` | The entity of the event under the name of its entity type. |
Reacts when translations of specific content entities are deleted.
## Fields
`Type (and bundle)` : This field provides a list with all created content entities and its bundles. You can also select **-any-**, if you want to react to all types or bundles.
# Insert content entity translation
Available since: 1.0.0
After a new entity translation got saved (persistently created).
Provided tokens
| Token | Description |
| ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `[user]` | The current user. Alias: `current_user` |
| `[htmx]` | Information about the current HTMX request. |
| `[htmx:is_request]` | Whether the current request was sent by HTMX ("1" or "0"). |
| `[htmx:boosted]` | Whether the current request was boosted by HTMX ("1" or "0"). |
| `[htmx:history_restore]` | Whether the current request is for HTMX history restoration ("1" or "0"). |
| `[htmx:target]` | The value of the HX-Target request header. |
| `[htmx:trigger]` | The value of the HX-Trigger request header. |
| `[htmx:trigger_name]` | The value of the HX-Trigger-Name request header. |
| `[htmx:current_url]` | The value of the HX-Current-URL request header. |
| `[htmx:prompt]` | The value of the HX-Prompt request header. |
| `[event]` | The event. |
| `[event:machine_name]` | The machine name of the ECA event. |
| `[session_user]` | The user account that dispatched the event, regardless if ECA is processing models under a different account. This is only available if ECA is configured to always run under a specific account. |
| `[entity]` | The entity of the event. |
| `[ENTITY_TYPE]` | The entity of the event under the name of its entity type. |
Reacts when translations of specific content entities are inserted.
## Fields
`Type (and bundle)` : This field provides a list with all created content entities and its bundles. You can also select **-any-**, if you want to react to all types or bundles.
# Update content entity
Available since: 1.0.0
After an existing entity got saved (persistently changed).
Provided tokens
| Token | Description |
| ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `[user]` | The current user. Alias: `current_user` |
| `[htmx]` | Information about the current HTMX request. |
| `[htmx:is_request]` | Whether the current request was sent by HTMX ("1" or "0"). |
| `[htmx:boosted]` | Whether the current request was boosted by HTMX ("1" or "0"). |
| `[htmx:history_restore]` | Whether the current request is for HTMX history restoration ("1" or "0"). |
| `[htmx:target]` | The value of the HX-Target request header. |
| `[htmx:trigger]` | The value of the HX-Trigger request header. |
| `[htmx:trigger_name]` | The value of the HX-Trigger-Name request header. |
| `[htmx:current_url]` | The value of the HX-Current-URL request header. |
| `[htmx:prompt]` | The value of the HX-Prompt request header. |
| `[event]` | The event. |
| `[event:machine_name]` | The machine name of the ECA event. |
| `[session_user]` | The user account that dispatched the event, regardless if ECA is processing models under a different account. This is only available if ECA is configured to always run under a specific account. |
| `[entity]` | The entity of the event. |
| `[ENTITY_TYPE]` | The entity of the event under the name of its entity type. |
Reacts when specific content entities are updated.
## Fields
`Type (and bundle)` : This field provides a list with all created content entities and its bundles. You can also select **-any-**, if you want to react to all types or bundles.
# Validate content entity
Available since: 2.1.0
When an entity is undergoing validation.
Provided tokens
| Token | Description |
| ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `[user]` | The current user. Alias: `current_user` |
| `[htmx]` | Information about the current HTMX request. |
| `[htmx:is_request]` | Whether the current request was sent by HTMX ("1" or "0"). |
| `[htmx:boosted]` | Whether the current request was boosted by HTMX ("1" or "0"). |
| `[htmx:history_restore]` | Whether the current request is for HTMX history restoration ("1" or "0"). |
| `[htmx:target]` | The value of the HX-Target request header. |
| `[htmx:trigger]` | The value of the HX-Trigger request header. |
| `[htmx:trigger_name]` | The value of the HX-Trigger-Name request header. |
| `[htmx:current_url]` | The value of the HX-Current-URL request header. |
| `[htmx:prompt]` | The value of the HX-Prompt request header. |
| `[event]` | The event. |
| `[event:machine_name]` | The machine name of the ECA event. |
| `[session_user]` | The user account that dispatched the event, regardless if ECA is processing models under a different account. This is only available if ECA is configured to always run under a specific account. |
| `[entity]` | The entity of the event. |
| `[ENTITY_TYPE]` | The entity of the event under the name of its entity type. |
Fires when a content entity undergoes validation (e.g., during form submission or REST API operations). Allows adding custom validation rules without writing PHP code.
Use conditions and the appropriate validation actions to add violation messages that will prevent the entity from being saved.
## Fields
`Type (and bundle)` : This field provides a list with all created content entities and its bundles. You can also select **-any-**, if you want to react to all types or bundles.
# View content entity
Available since: 1.0.0
Provided tokens
| Token | Description |
| ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `[user]` | The current user. Alias: `current_user` |
| `[htmx]` | Information about the current HTMX request. |
| `[htmx:is_request]` | Whether the current request was sent by HTMX ("1" or "0"). |
| `[htmx:boosted]` | Whether the current request was boosted by HTMX ("1" or "0"). |
| `[htmx:history_restore]` | Whether the current request is for HTMX history restoration ("1" or "0"). |
| `[htmx:target]` | The value of the HX-Target request header. |
| `[htmx:trigger]` | The value of the HX-Trigger request header. |
| `[htmx:trigger_name]` | The value of the HX-Trigger-Name request header. |
| `[htmx:current_url]` | The value of the HX-Current-URL request header. |
| `[htmx:prompt]` | The value of the HX-Prompt request header. |
| `[entity_view_mode]` | The entity view mode. |
| `[event]` | The event. |
| `[event:machine_name]` | The machine name of the ECA event. |
| `[session_user]` | The user account that dispatched the event, regardless if ECA is processing models under a different account. This is only available if ECA is configured to always run under a specific account. |
| `[entity]` | The entity of the event. |
| `[ENTITY_TYPE]` | The entity of the event under the name of its entity type. |
Reacts when specific content entities are viewed.
## Fields
`Type (and bundle)` : This field provides a list with all created content entities and its bundles. You can also select **-any-**, if you want to react to all types or bundles.
# Alter entity view mode
Available since: 2.0.0
Provided tokens
| Token | Description |
| ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `[user]` | The current user. Alias: `current_user` |
| `[htmx]` | Information about the current HTMX request. |
| `[htmx:is_request]` | Whether the current request was sent by HTMX ("1" or "0"). |
| `[htmx:boosted]` | Whether the current request was boosted by HTMX ("1" or "0"). |
| `[htmx:history_restore]` | Whether the current request is for HTMX history restoration ("1" or "0"). |
| `[htmx:target]` | The value of the HX-Target request header. |
| `[htmx:trigger]` | The value of the HX-Trigger request header. |
| `[htmx:trigger_name]` | The value of the HX-Trigger-Name request header. |
| `[htmx:current_url]` | The value of the HX-Current-URL request header. |
| `[htmx:prompt]` | The value of the HX-Prompt request header. |
| `[event]` | The event. |
| `[event:view_mode]` | The view mode machine name. |
| `[event]` | The event. |
| `[event:machine_name]` | The machine name of the ECA event. |
| `[session_user]` | The user account that dispatched the event, regardless if ECA is processing models under a different account. This is only available if ECA is configured to always run under a specific account. |
| `[entity]` | The entity of the event. |
| `[ENTITY_TYPE]` | The entity of the event under the name of its entity type. |
Fires before an entity is rendered, allowing the view mode to be changed dynamically. Useful for switching between view modes based on user roles, entity field values, or other runtime conditions.
## Fields
`Type (and bundle)` : This field provides a list with all created content entities and its bundles. You can also select **-any-**, if you want to react to all types or bundles.
# ECA Content Access
## Installation
```
composer require drupal/eca_content_access
drush pm:install eca_content_access
```
Instead of using Drush to enable the module, you can also go to "Administration / Extend" (`/admin/modules`) and enable the module **ECA Content Access** from there.
# Content access: grant access
Available since: 1.0.0
Grants access to an individual node for a role to overwrite settings of the content type.
## Fields
`Operation`
`User role`
`Follow up`
`Clear cache`
`Entity` : Provide the token name of the *node* that this action should operate with. Please provide the token name only, without brackets.
# Content access: revoke access
Available since: 1.0.0
Revoke access from an individual node for a role to overwrite settings of the content type.
## Fields
`Operation`
`User role`
`Follow up`
`Clear cache`
`Entity` : Provide the token name of the *node* that this action should operate with. Please provide the token name only, without brackets.
# ECA CrowdSec
## Installation
```
composer require drupal/crowdsec
drush pm:install eca_crowdsec
```
Instead of using Drush to enable the module, you can also go to "Administration / Extend" (`/admin/modules`) and enable the module **ECA CrowdSec** from there.
# IP banned
Available since: 1.0.0
Provided tokens
| Token | Description |
| ---------------------- | ------------------------------------------------------------------------------------------------------------- |
| `[event]` | The event. |
| `[event:crowdsec_ip]` | The ip address. |
| `[event]` | The event. |
| `[event:machine_name]` | The machine name of the ECA event. |
| `[session_user]` | The user account that dispatched the event, regardless if ECA is processing models under a different account. |
Fires when an IP address is banned by CrowdSec.
The `[event:crowdsec_ip]` token contains the banned IP address.
# IP blocked
Available since: 1.0.0
Provided tokens
| Token | Description |
| ---------------------- | ------------------------------------------------------------------------------------------------------------- |
| `[event]` | The event. |
| `[event:crowdsec_ip]` | The ip address. |
| `[event]` | The event. |
| `[event:machine_name]` | The machine name of the ECA event. |
| `[session_user]` | The user account that dispatched the event, regardless if ECA is processing models under a different account. |
Fires when a request from an IP address is blocked by CrowdSec.
The `[event:crowdsec_ip]` token contains the blocked IP address.
# Scenario list
Available since: 1.0.0
Provided tokens
| Token | Description |
| -------------------------------- | ------------------------------------------------------------------------------------------------------------- |
| `[event]` | The event. |
| `[event:crowdsec_scenario_list]` | The CrowdSec object. |
| `[event]` | The event. |
| `[event:machine_name]` | The machine name of the ECA event. |
| `[session_user]` | The user account that dispatched the event, regardless if ECA is processing models under a different account. |
Fires when CrowdSec builds the list of scenarios to monitor. Use this to dynamically add or modify scenarios in the list.
The `[event:crowdsec_scenario_list]` token provides the current array of scenarios.
# IP signalled
Available since: 1.0.0
Provided tokens
| Token | Description |
| --------------------------- | ------------------------------------------------------------------------------------------------------------- |
| `[event]` | The event. |
| `[event:crowdsec_ip]` | The ip address. |
| `[event:crowdsec_scenario]` | The CrowdSec scenario. |
| `[event]` | The event. |
| `[event:machine_name]` | The machine name of the ECA event. |
| `[session_user]` | The user account that dispatched the event, regardless if ECA is processing models under a different account. |
Fires when an IP address is signalled upstream to the CrowdSec API.
The `[event:crowdsec_ip]` token contains the signalled IP address and `[event:crowdsec_scenario]` identifies the triggered scenario.
# Signal scenario list
Available since: 1.0.0
Provided tokens
| Token | Description |
| -------------------------------- | ------------------------------------------------------------------------------------------------------------- |
| `[event]` | The event. |
| `[event:crowdsec_scenario_list]` | The CrowdSec object. |
| `[event]` | The event. |
| `[event:machine_name]` | The machine name of the ECA event. |
| `[session_user]` | The user account that dispatched the event, regardless if ECA is processing models under a different account. |
Fires when CrowdSec builds the list of scenarios for upstream signaling. Use this to control which scenarios are reported to the CrowdSec API.
The `[event:crowdsec_scenario_list]` token provides the current array of scenarios.
# IP unbanned
Available since: 1.0.0
Provided tokens
| Token | Description |
| ---------------------- | ------------------------------------------------------------------------------------------------------------- |
| `[event]` | The event. |
| `[event:crowdsec_ip]` | The ip address. |
| `[event]` | The event. |
| `[event:machine_name]` | The machine name of the ECA event. |
| `[session_user]` | The user account that dispatched the event, regardless if ECA is processing models under a different account. |
Fires when a previously banned IP address is unbanned by CrowdSec.
The `[event:crowdsec_ip]` token contains the unbanned IP address.
# ECA: DANSE integration
## Installation
```
composer require drupal/danse
drush pm:install eca_danse
```
Instead of using Drush to enable the module, you can also go to "Administration / Extend" (`/admin/modules`) and enable the module **ECA: DANSE integration** from there.
# Recipient Selection
Available since: 2.3.0
Provided tokens
| Token | Description |
| ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `[user]` | The current user. Alias: `current_user` |
| `[htmx]` | Information about the current HTMX request. |
| `[htmx:is_request]` | Whether the current request was sent by HTMX ("1" or "0"). |
| `[htmx:boosted]` | Whether the current request was boosted by HTMX ("1" or "0"). |
| `[htmx:history_restore]` | Whether the current request is for HTMX history restoration ("1" or "0"). |
| `[htmx:target]` | The value of the HX-Target request header. |
| `[htmx:trigger]` | The value of the HX-Trigger request header. |
| `[htmx:trigger_name]` | The value of the HX-Trigger-Name request header. |
| `[htmx:current_url]` | The value of the HX-Current-URL request header. |
| `[htmx:prompt]` | The value of the HX-Prompt request header. |
| `[event]` | The event. |
| `[event:machine_name]` | The machine name of the ECA event. |
| `[session_user]` | The user account that dispatched the event, regardless if ECA is processing models under a different account. This is only available if ECA is configured to always run under a specific account. |
Fires when the DANSE (Drupal Advanced Notification System for Entities) module requests recipient selection through ECA. Use this to dynamically determine which users should receive a notification.
The `[danse_recipients]` token provides a list to which you can add user entities or user IDs. Each ECA model using this event automatically registers as a DANSE recipient selection plugin.
## Fields
`Event ID` : The event ID to trigger this event directly.
```
The event ID to trigger this event directly. Must match the ID used by the DANSE recipient selection plugin dispatching the event.
```
# ECA Endpoint
## Installation
```
composer require drupal/eca
drush pm:install eca_endpoint
```
Instead of using Drush to enable the module, you can also go to "Administration / Extend" (`/admin/modules`) and enable the module **ECA Endpoint** from there.
# Request: Get client IP
Available since: 1.1.0
## Fields
`Name of token` : Please provide the token name only, without brackets.
# Request: Get path argument
Available since: 1.1.0
## Fields
`Index position` : The index position of the path argument, starting at 0. Example: When index position is set to *2* and the path is */node/1/edit*, then the returned value is *edit*. When empty, the whole requested path will be returned.
`Name of token` : Please provide the token name only, without brackets.
# Request: Get URL query parameter
Available since: 1.1.0
## Fields
`Query parameter name` : The name of the URL query parameter. Example: To get the parameter of the destination parameter *?destination=...*, then set *destination* as the parameter name. When this field is empty, then all parameters will be returned as a list, keyed by parameter name.\
This field supports tokens.
`Name of token` : Please provide the token name only, without brackets.
# Request: Get content
Available since: 1.1.0
## Fields
`Name of token` : Please provide the token name only, without brackets.
# Request: Get content type
Available since: 1.1.0
## Fields
`Name of token` : Please provide the token name only, without brackets.
# Request: Get header
Available since: 1.1.0
## Fields
`Header name` : The name / key of the request header. Example: *Content-Type*. When this field is empty, then all headers will be returned as a list, keyed by header name.\
This field supports tokens.
`Name of token` : Please provide the token name only, without brackets.
# Request: Get method
Available since: 1.1.0
## Fields
`Name of token` : Please provide the token name only, without brackets.
# Request: Get uri
Available since: 1.1.0
## Fields
`Name of token` : Please provide the token name only, without brackets.
# Ajax Response: add css
Available since: 2.0.0
## Fields
`CSS file` : The href attribute for the styles, e.g. "/my_styles/special.css".\
This field supports tokens.
`Media` : This attribute defines which media the style should be applied to.\
This field supports tokens.
# Ajax Response: add js
Available since: 2.0.0
## Fields
`URL` : The src attribute for the script.\
This field supports tokens.
`Clear previous messages` : If TRUE, loading of the script will be deferred.
`CSS Selector` : A CSS selector of the element where the script tags will be appended.\
This field supports tokens.
# Ajax Response: insert content after
Available since: 2.0.0
## Fields
`CSS Selector` : A CSS selector of the element where the content will be inserted.\
This field supports tokens.
`Content` : The content to be inserted.\
This field supports tokens.
`Settings` : An array of JavaScript settings in YAML format to be passed to any attached behaviors.\
This field supports tokens.
# Ajax Response: set alert
Available since: 2.0.0
## Fields
`Message` : The alert message returned to the ajax response.\
This field supports tokens.
# Ajax Response: set announcement
Available since: 2.0.0
## Fields
`Message` : The message returned to the ajax response.\
This field supports tokens.
`Announce priority` : The priority that will be used for the announcement. Defaults to empty/unset which will not set a priority in the response sent to the client and therefore the default of polite will be used for the message.\
This field supports tokens.
# Ajax Response: append content
Available since: 2.0.0
## Fields
`CSS Selector` : A CSS selector of the element where the content will be inserted.\
This field supports tokens.
`Content` : The content to be inserted.\
This field supports tokens.
`Settings` : An array of JavaScript settings in YAML format to be passed to any attached behaviors.\
This field supports tokens.
# Ajax Response: insert before content
Available since: 2.0.0
## Fields
`CSS Selector` : A CSS selector of the element where the content will be inserted.\
This field supports tokens.
`Content` : The content to be inserted.\
This field supports tokens.
`Settings` : An array of JavaScript settings in YAML format to be passed to any attached behaviors.\
This field supports tokens.
# Ajax Response: set changed
Available since: 2.0.0
## Fields
`CSS Selector` : CSS selector for elements to be marked as changed.\
This field supports tokens.
`Asterisk` : CSS selector for elements to which an asterisk will be appended.\
This field supports tokens.
# Ajax Response: close dialog
Available since: 2.0.0
## Fields
`CSS Selector` : CSS selector string of the dialog to close; leave empty for the default.\
This field supports tokens.
`Persist dialog in DOM` : Whether to persist the dialog in the DOM or not.
# Ajax Response: close modal dialog
Available since: 2.0.0
## Fields
`Persist dialog in DOM` : Whether to persist the dialog in the DOM or not.
# Ajax Response: css
Available since: 2.0.0
## Fields
`CSS Selector` : A CSS selector of the element where the CSS should be applied.\
This field supports tokens.
`CSS` : An array of CSS properties and values in YAML format.\
This field supports tokens.
# Ajax Response: set data attribute
Available since: 2.0.0
## Fields
`Selector` : A CSS selector for the elements to which the data will be attached.\
This field supports tokens.
`Name` : The key of the data to be attached to elements matched by the selector.\
This field supports tokens.
`Value` : The value of the data to be attached to elements matched by the selector.\
This field supports tokens.
# Ajax Response: set focus
Available since: 2.0.0
## Fields
`Selector` : A CSS selector of the container with tabbable elements.\
This field supports tokens.
# Ajax Response: apply html content
Available since: 2.0.0
## Fields
`CSS Selector` : A CSS selector of the element where the content will be inserted.\
This field supports tokens.
`Content` : The content to be inserted.\
This field supports tokens.
`Settings` : An array of JavaScript settings in YAML format to be passed to any attached behaviors.\
This field supports tokens.
# Ajax Response: insert content
Available since: 2.0.0
## Fields
`CSS Selector` : A CSS selector of the element where the content will be inserted.\
This field supports tokens.
`Content` : The content to be inserted.\
This field supports tokens.
`Settings` : An array of JavaScript settings in YAML format to be passed to any attached behaviors.\
This field supports tokens.
# Ajax Response: invoke
Available since: 2.0.0
## Fields
`CSS Selector` : A jQuery selector.\
This field supports tokens.
`Method` : The name of a jQuery method to invoke.\
This field supports tokens.
`Arguments` : An array of arguments in YAML format that will be passed to the method.\
This field supports tokens.
# Ajax Response: set message
Available since: 2.0.0
## Fields
`Type` : The message type.\
When using the "Defined by token" option, make sure there is a token with this name: *eca_endpoint_set_ajax_response_message_type*
`Message` : The message returned to the ajax response.\
This field supports tokens.
`Wrapper Selector` : The CSS selector for the wrapper for messages. Leave empty to use defaults.\
This field supports tokens.
`ID` : The message ID, it can be a simple value or several values separated by a space which can be used as an explicit selector for a message.\
This field supports tokens.
`Announce` : The CSS selector for the wrapper for messages. Leave empty to use defaults.\
This field supports tokens.
`Announce priority` : The priority that will be used for the announcement. Defaults to empty/unset which will not set a priority in the response sent to the client and therefore the default of polite will be used for the message.\
This field supports tokens.
`Clear previous messages` : If TRUE, previous messages will be cleared first.
# Ajax Response: open dialog
Available since: 2.0.0
## Fields
`CSS Selector` : The selector of the dialog.\
This field supports tokens.
`Title` : The title of the dialog.\
This field supports tokens.
`Content` : The content to be inserted.\
This field supports tokens.
`Dialog options` : Options in YAML format to be passed to the dialog implementation. Any jQuery UI option can be used. See https://api.jqueryui.com/dialog.\
This field supports tokens.
`Settings` : Custom settings in YAML format that will be passed to the Drupal behaviors on the content of the dialog. If left empty, the settings will be populated automatically from the current request.\
This field supports tokens.
# Ajax Response: open modal dialog
Available since: 2.0.0
## Fields
`Title` : The title of the dialog.\
This field supports tokens.
`Content` : The content to be inserted.\
This field supports tokens.
`Dialog options` : Options in YAML format to be passed to the dialog implementation. Any jQuery UI option can be used. See https://api.jqueryui.com/dialog.\
This field supports tokens.
`Settings` : Custom settings in YAML format that will be passed to the Drupal behaviors on the content of the dialog. If left empty, the settings will be populated automatically from the current request.\
This field supports tokens.
# Ajax Response: open off-canvas dialog
Available since: 2.0.0
## Fields
`Position` : The position to render the off-canvas dialog.\
When using the "Defined by token" option, make sure there is a token with this name: *eca_endpoint_set_ajax_response_open_off_canvas_dialog_position*
`Title` : The title of the dialog.\
This field supports tokens.
`Content` : The content to be inserted.\
This field supports tokens.
`Dialog options` : Options in YAML format to be passed to the dialog implementation. Any jQuery UI option can be used. See https://api.jqueryui.com/dialog.\
This field supports tokens.
`Settings` : Custom settings in YAML format that will be passed to the Drupal behaviors on the content of the dialog. If left empty, the settings will be populated automatically from the current request.\
This field supports tokens.
# Ajax Response: prepend content
Available since: 2.0.0
## Fields
`CSS Selector` : A CSS selector of the element where the content will be inserted.\
This field supports tokens.
`Content` : The content to be inserted.\
This field supports tokens.
`Settings` : An array of JavaScript settings in YAML format to be passed to any attached behaviors.\
This field supports tokens.
# Ajax Response: redirect
Available since: 2.0.0
## Fields
`URL` : The URL that will be loaded into window.location. This should be a full URL.\
This field supports tokens.
# Ajax Response: replace content
Available since: 2.0.0
## Fields
`CSS Selector` : A CSS selector of the element where the content will be inserted.\
This field supports tokens.
`Content` : The content to be inserted.\
This field supports tokens.
`Settings` : An array of JavaScript settings in YAML format to be passed to any attached behaviors.\
This field supports tokens.
# Ajax Response: reset table striping
Available since: 2.0.0
## Fields
`CSS Selector` : A CSS selector for the table to be restriped.\
This field supports tokens.
# Ajax Response: scroll to top
Available since: 2.0.0
## Fields
`CSS Selector` : A CSS selector.\
This field supports tokens.
# Ajax Response: set dialog option
Available since: 2.0.0
## Fields
`Selector` : The selector of the dialog whose title will be set. If set to an empty value, the default modal dialog will be selected.\
This field supports tokens.
`Name` : The name of the option to set. May be any jQuery UI dialog option. See https://api.jqueryui.com/dialog.\
This field supports tokens.
`Value` : The value of the option to be passed to the dialog.\
This field supports tokens.
# Ajax Response: set dialog title
Available since: 2.0.0
## Fields
`Selector` : The selector of the dialog whose title will be set. If set to an empty value, the default modal dialog will be selected.\
This field supports tokens.
`Title` : The title that will be set on the dialog.\
This field supports tokens.
# Ajax Response: settings
Available since: 2.0.0
## Fields
`Settings` : An array of key/value pairs of JavaScript settings in YAML format.\
This field supports tokens.
`Merge` : Whether the settings should be merged into the global drupalSettings.
# Ajax Response: tabledrag warning
Available since: 2.0.0
## Fields
`ID` : The id of the changed row.\
This field supports tokens.
`Table instance` : The identifier of the tabledrag instance.\
This field supports tokens.
# Ajax Response: update build id
Available since: 2.0.0
## Fields
`Old ID` : The old build ID.\
This field supports tokens.
`New ID` : The new build ID.\
This field supports tokens.
# Response: set content
Available since: 1.1.0
## Fields
`Content` : The response content to set.\
This field supports tokens.
# Response: set content type
Available since: 1.1.0
## Fields
`content_type` : This field supports tokens.
# Response: set expires
Available since: 1.1.0
## Fields
`Expires` : The date of expiry, either formatted as a date time string, or a UNIX timestamp.\
This field supports tokens.
# Response: set headers
Available since: 1.1.0
## Fields
`Headers` : The headers to set. If you can also use YAML syntax by enabling it below.\
This field supports tokens.
`Interpret above value as YAML format` : Nested data can be set using YAML format, for example *Content-Type: "text/html; charset=UTF-8"*. When using this format, this option needs to be enabled. When using tokens and YAML altogether, make sure that tokens are wrapped as a string. Example: *Content-Type: "[content_type]"*
`Validate YAML to prevent this from being executed when invalid`
# Response: set max age
Available since: 1.1.0
## Fields
`Client value (max-age)` : The number of seconds for the client-side max age.\
This field supports tokens.
`Shared value (s-max-age)` : The number of seconds for the shared max age.\
This field supports tokens.
`Set public` : Cacheability is usually only working when the response is set to be public. When not enabled, the response will be set as private.
`Set expires` : When enabled, the expires header is automatically derived from the defined max age.
# Response: set status code
Available since: 1.1.0
## Fields
`Status code` : Must be a valid HTTP status code.\
This field supports tokens.
# ECA Endpoint access
Available since: 1.1.0
Provided tokens
| Token | Description |
| ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `[user]` | The current user. Alias: `current_user` |
| `[htmx]` | Information about the current HTMX request. |
| `[htmx:is_request]` | Whether the current request was sent by HTMX ("1" or "0"). |
| `[htmx:boosted]` | Whether the current request was boosted by HTMX ("1" or "0"). |
| `[htmx:history_restore]` | Whether the current request is for HTMX history restoration ("1" or "0"). |
| `[htmx:target]` | The value of the HX-Target request header. |
| `[htmx:trigger]` | The value of the HX-Trigger request header. |
| `[htmx:trigger_name]` | The value of the HX-Trigger-Name request header. |
| `[htmx:current_url]` | The value of the HX-Current-URL request header. |
| `[htmx:prompt]` | The value of the HX-Prompt request header. |
| `[event]` | The event. |
| `[event:arguments]` | The arguments of the request path. |
| `[event:uid]` | The ID of the user account of the request. |
| `[event]` | The event. |
| `[event:machine_name]` | The machine name of the ECA event. |
| `[session_user]` | The user account that dispatched the event, regardless if ECA is processing models under a different account. This is only available if ECA is configured to always run under a specific account. |
| `[account]` | The user account of the event. |
Fires when Drupal checks access to an ECA endpoint URL.
Access defaults to denied
If no ECA model grants access, the endpoint returns 403 Forbidden. You must explicitly set the access result using the [Set access result](https://ecaguide.org/plugins/eca/access/actions/eca_access_set_result/index.md) action.
## Fields
`First path argument` : The **first** path argument to match up. This argument will be resolved from the URL path */eca/**{first}**/{second}*.
```
The first path argument in the endpoint URL (`/eca/{first}/{second}`). This is the primary discriminator for routing to the correct ECA model.
```
`Second path argument` : Optionally specify a second path argument to match up. This argument will be resolved from the URL path */eca/{first}/**{second}***.
```
The optional second path argument in the endpoint URL (`/eca/{first}/{second}`). Leave empty to match any second argument.
```
# ECA Endpoint response
Available since: 1.1.0
Provided tokens
| Token | Description |
| ------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `[user]` | The current user. Alias: `current_user` |
| `[htmx]` | Information about the current HTMX request. |
| `[htmx:is_request]` | Whether the current request was sent by HTMX ("1" or "0"). |
| `[htmx:boosted]` | Whether the current request was boosted by HTMX ("1" or "0"). |
| `[htmx:history_restore]` | Whether the current request is for HTMX history restoration ("1" or "0"). |
| `[htmx:target]` | The value of the HX-Target request header. |
| `[htmx:trigger]` | The value of the HX-Trigger request header. |
| `[htmx:trigger_name]` | The value of the HX-Trigger-Name request header. |
| `[htmx:current_url]` | The value of the HX-Current-URL request header. |
| `[htmx:prompt]` | The value of the HX-Prompt request header. |
| `[event]` | The event. |
| `[event:path_arguments]` | The arguments of the request path. |
| `[event:request]` | The request. |
| `[event:request:method]` | The request method, e.g. "GET" or "POST". |
| `[event:request:path]` | The requested path. |
| `[event:request:query]` | The query arguments of the request. |
| `[event:request:headers]` | The request headers. |
| `[event:request:content_type]` | The content type of the request. |
| `[event:request:content]` | The content of the POST request. |
| `[event:request:ip]` | The client IP. |
| `[event:uid]` | The ID of the user account of the request. |
| `[event]` | The event. |
| `[event:machine_name]` | The machine name of the ECA event. |
| `[session_user]` | The user account that dispatched the event, regardless if ECA is processing models under a different account. This is only available if ECA is configured to always run under a specific account. |
Attention
Don't forget to also add an [ECA Endpoint access](https://ecaguide.org/plugins/eca/endpoint/events/eca_endpoint_access/index.md) to your model, so that you can allow this endpoint to be requested, unless you determine that by some other means.
It's likely that you will need the [Set access result](https://ecaguide.org/plugins/eca/access/actions/eca_access_set_result/index.md) for this, which is available from the ECA Access sub-module, that needs to be enabled separately.
## Fields
`First path argument` : The **first** path argument to match up. This argument will be resolved from the URL path */eca/**{first}**/{second}*.
```
The first path argument in the endpoint URL (`/eca/{first}/{second}`). This is the primary discriminator for routing to the correct ECA model.
```
`Second path argument` : Optionally specify a second path argument to match up. This argument will be resolved from the URL path */eca/{first}/**{second}***.
```
The optional second path argument in the endpoint URL (`/eca/{first}/{second}`). Leave empty to match any second argument.
```
# ECA Entity Print
## Installation
```
composer require drupal/eca_entity_print
drush pm:install eca_entity_print
```
Instead of using Drush to enable the module, you can also go to "Administration / Extend" (`/admin/modules`) and enable the module **ECA Entity Print** from there.
# Print file document from entity
Available since: 1.0.0
Print file document from a chosen entity and saves it as a file entity.
## Fields
`Name of token` : Name of the token available after file document from view render is generated. Contains the file entity. Please provide the token name only, without brackets.
`The exported file name` : Provide a name for the generated file.\
This field supports tokens.
`Export type`
`Entity` : Provide the token name of the *entity* that this action should operate with. Please provide the token name only, without brackets.
# Print file document from views output
Available since: 1.0.0
Print file document from a chosen view output and saves it as a file entity.
## Fields
`View` : Select the view from the list. The view will always return a list of complete entities.
`Display` : Write the view `display id` to execute. Set as default to use the default view configuration.
`Arguments` : Provide the Contextual filters of the view in order and one by line. This property supports tokens.
`Name of token` : Name of the token available after file document from view render is generated. Contains the file entity. Please provide the token name only, without brackets.
`The exported file name` : Provide a name for the generated file.\
This field supports tokens.
`Export type`
# ECA Entity Share Client
## Installation
```
composer require drupal/eca_entity_share_client
drush pm:install eca_entity_share_client
```
Instead of using Drush to enable the module, you can also go to "Administration / Extend" (`/admin/modules`) and enable the module **ECA Entity Share Client** from there.
# Entity share: Relationship Field Value
Available since: 1.0.0
Provided tokens
| Token | Description |
| ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `[event]` | The event. |
| `[event:machine_name]` | The machine name of the ECA event. |
| `[session_user]` | The user account that dispatched the event, regardless if ECA is processing models under a different account. This is only available if ECA is configured to always run under a specific account. |
Fires when the Entity Share client module processes a relationship field value during entity import. Use this to alter how referenced entities are resolved or mapped on the client side.
# ECA Entity Share Server
## Installation
```
composer require drupal/eca_entity_share_server
drush pm:install eca_entity_share_server
```
Instead of using Drush to enable the module, you can also go to "Administration / Extend" (`/admin/modules`) and enable the module **ECA Entity Share Server** from there.
# Entity share: Channel list prepared
Available since: 1.0.0
Provided tokens
| Token | Description |
| ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `[event]` | The event. |
| `[event:machine_name]` | The machine name of the ECA event. |
| `[session_user]` | The user account that dispatched the event, regardless if ECA is processing models under a different account. This is only available if ECA is configured to always run under a specific account. |
Fires when the Entity Share server module prepares the channel list for sharing. Use this to dynamically alter which channels or entities are available for sharing with remote clients.
# ECA File
## Installation
```
composer require drupal/eca
drush pm:install eca_file
```
Instead of using Drush to enable the module, you can also go to "Administration / Extend" (`/admin/modules`) and enable the module **ECA File** from there.
# File: rename
Available since: 2.1.0
Rename the physical file of a file entity.
## Fields
`New file name` : The new file name without schema or path, but with the file extension.\
This field supports tokens.
`Behavior` : The behavior when dealing with existing files.\
When using the "Defined by token" option, make sure there is a token with this name: *eca_file_rename_exists_behavior*
`Entity` : Provide the token name of the *entity* that this action should operate with. Please provide the token name only, without brackets.
# File: write
Available since: 2.1.0
Write data to a physical file of a file entity.
## Fields
`File content` : The content to write to the given file.\
This field supports tokens.
`Entity` : Provide the token name of the *entity* that this action should operate with. Please provide the token name only, without brackets.
# ECA Flag
## Installation
```
composer require drupal/eca_flag
drush pm:install eca_flag
```
Instead of using Drush to enable the module, you can also go to "Administration / Extend" (`/admin/modules`) and enable the module **ECA Flag** from there.
# Flag: get flagging for entity
Available since: 1.0.0
Get a flagging for a content entity.
## Fields
`Name of flag` : Provide the name of a flag.\
When using the "Defined by token" option, make sure there is a token with this name: *eca_flag_get_flagging_flag_name*
`Name of token` : Provide the name of a token that holds the loaded entity.
`Entity` : Provide the token name of the *entity* that this action should operate with. Please provide the token name only, without brackets.
# Flag: entity flagged
Available since: 1.0.0
Performs a lookup whether an entity is flagged.
## Fields
`Name of flag` : Provide the name of a flag.
`Negate the condition` : Negates the condition. Makes TRUE to FALSE and vice versa.
`Entity` : Provide the token name of the *entity* that this condition should operate with. Please provide the token name only, without brackets.
# Delete flagging
Available since: 1.0.0
Provided tokens
| Token | Description |
| ------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `[flagging]` | The flagging entity. |
| `[flag]` | The flag entity. |
| `[entity]` | The flagged entity |
| `[event]` | The event. |
| `[event:flag]` | The flag entity (if there is only 1 item). |
| `[event:flagging]` | The flagging entity (if there is only 1 item). |
| `[event:entity]` | The flagged entity (if there is only 1 item). |
| `[event:flaggings]` | The flaggings. |
| `[event:flaggings:#]` | The index in the list beginning with 0. |
| `[event:flaggings:#:flag]` | The flag entity. |
| `[event:flaggings:#:flagging]` | The flagging entity. |
| `[event:flaggings:#:entity]` | The flagged entity. |
| `[event]` | The event. |
| `[event:machine_name]` | The machine name of the ECA event. |
| `[session_user]` | The user account that dispatched the event, regardless if ECA is processing models under a different account. This is only available if ECA is configured to always run under a specific account. |
Fires when a flagging entity is deleted from the database. This is triggered via Drupal's entity hook system and runs alongside (but independently from) the Unflag event.
Tokens `[flagging]`, `[flag]`, and `[entity]` are available when there is exactly one flagging. Use `[event:flaggings:#:flagging]` to iterate when multiple flaggings are involved.
# Flag
Available since: 1.0.0
Provided tokens
| Token | Description |
| ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `[flagging]` | The flagging entity. |
| `[flag]` | The flag entity. |
| `[entity]` | The flagged entity |
| `[event]` | The event. |
| `[event:flag]` | The flag entity. |
| `[event:flagging]` | The flagging entity. |
| `[event:entity]` | The flagged entity. |
| `[event]` | The event. |
| `[event:machine_name]` | The machine name of the ECA event. |
| `[session_user]` | The user account that dispatched the event, regardless if ECA is processing models under a different account. This is only available if ECA is configured to always run under a specific account. |
Fires when an entity is flagged. This event originates from the Flag module and provides the flagging entity.
Tokens `[flagging]`, `[flag]`, and `[entity]` give access to the flagging record, the flag configuration, and the flagged entity respectively.
# Insert flagging
Available since: 1.0.0
Provided tokens
| Token | Description |
| ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `[flagging]` | The flagging entity. |
| `[flag]` | The flag entity. |
| `[entity]` | The flagged entity |
| `[event]` | The event. |
| `[event:flag]` | The flag entity. |
| `[event:flagging]` | The flagging entity. |
| `[event:entity]` | The flagged entity. |
| `[event]` | The event. |
| `[event:machine_name]` | The machine name of the ECA event. |
| `[session_user]` | The user account that dispatched the event, regardless if ECA is processing models under a different account. This is only available if ECA is configured to always run under a specific account. |
Fires when a flagging entity is inserted into the database. This is triggered via Drupal's entity hook system and runs alongside (but independently from) the Flag event.
Tokens `[flagging]`, `[flag]`, and `[entity]` provide access to the flagging record, the flag configuration, and the flagged entity.
# Unflag
Available since: 1.0.0
Provided tokens
| Token | Description |
| ------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `[flagging]` | The flagging entity. |
| `[flag]` | The flag entity. |
| `[entity]` | The flagged entity |
| `[event]` | The event. |
| `[event:flag]` | The flag entity (if there is only 1 item). |
| `[event:flagging]` | The flagging entity (if there is only 1 item). |
| `[event:entity]` | The flagged entity (if there is only 1 item). |
| `[event:flaggings]` | The flaggings. |
| `[event:flaggings:#]` | The index in the list beginning with 0. |
| `[event:flaggings:#:flag]` | The flag entity. |
| `[event:flaggings:#:flagging]` | The flagging entity. |
| `[event:flaggings:#:entity]` | The flagged entity. |
| `[event]` | The event. |
| `[event:machine_name]` | The machine name of the ECA event. |
| `[session_user]` | The user account that dispatched the event, regardless if ECA is processing models under a different account. This is only available if ECA is configured to always run under a specific account. |
Fires when a previously flagged entity is unflagged. This event originates from the Flag module and may contain multiple flagging records.
Tokens `[flagging]`, `[flag]`, and `[entity]` are available when there is exactly one flagging. Use `[event:flaggings:#:flagging]` to iterate over all flaggings when multiple are removed at once.
# ECA Form
This module allows to change and extend any existing form. Examples:
- Disable or hide existing fields
- Set fields as required
- Add or change available options
- Set and change default values
- Add new form fields:
- Textfield
- Checkboxes
- Select lists
- etc.
- Group field elements together
- Collapsible HTML details and containers
- Validate user input
- Set granular form field errors
- Execute custom logic on form submissions
- Check for any submitted user input and react accordingly
- Redirect to a certain page
- Make the form a multistep worflow
- Optionally with ajax
- Build up custom forms from scratch
- This ability requires the [ECA Render](https://ecaguide.org/plugins/eca/render/index.md) module to be installed.
## Usage
Everything about forms within ECA will most likely be found within this module. As always in ECA, form-related workflows always start with a certain event. This module covers all main form events that occur when Drupal is processing a form.
The following events take place, listed ascending in their time occurring order:
- **Build form**: This is the earliest form event, also to be seen as the first layer of **form building** events. This type of event happens, when the render array of the form is initially being build up.
- **Process form**: This is the second layer of **form building** events. This can and will be used by components that may rely on logic that happened before within the **Build form** event layer.
- **After build form**: The third layer of **form building** events. This can be used for internal follow-up actions,\
that don't affect the final result of the form display anymore.
All three events listed above are parts of the **form building** events. These types of events are being triggered in order to display a form to a user.
Form building events are being triggered at least once when a form is being rendered on a page. It may be called multiple times on the same page, when a component instructs to rebuild a form.
Note
**One note about fields of content entities:**
The processing of content entity fields is usually happening within the second layer of form events, **Process form**. In case you want to change or extend those fields, the earliest event to react upon is a **Process form** event. Reacting upon **Build form** event to work with entity fields will most likely not work.
When submitting a form, Drupal always involves two layers: **Validation** and **Submission**. Drupal always first executes the **validation layer**. When the validation layer results with no validation error, then Drupal will execute the **submission layer**.
That means, when at least one validation error was found, then Drupal won't execute the submission layer at all. Derived from that core mechanic, ECA offers two according types of events:
- **Validate form**: This happens when Drupal executes the validation layer.
- **Submit form**: This happens when Drupal executes the submission layer - but only when no validation errors occurred.
Typical triggers of form validation and submission are
- The user submitted a form by clicking on a submit button (for example "Save" on a content form).
- Ajax (JavaScript) that is changing the output of a form without reloading the whole page. Ajax triggering can (but is not forced to) partially or completely suppress validation errors. However, not all Ajax handlers submit a form, and therefore they won't trigger validation and submission. But many ajax handlers do so, because they can instruct the form to completely rebuild, and they do so by submitting the form.
- Programmatic client instructions: For example, automated frontend tests that may fake a form submission by sending form input as a POST request to the web server.
### When to use form events
Form events are mostly useful to change the output, and to extend validation and submission behavior of an existing form. Whenever data is coming from form input that needs to be processed, form events may help to achieve this.
Furthermore, new custom forms can be created by using actions of the [ECA Render](https://ecaguide.org/plugins/eca/render/index.md) module, specifically the action "Render: custom form" is meant for such use case.
When it's about adding or changing data of a content entity in general, events of the [ECA Content](https://ecaguide.org/plugins/eca/content/index.md) may be more suitable and preferred over form events. Specifically events such as [Presave content entity](https://ecaguide.org/plugins/eca/content/events/content_entity_presave/index.md), [Insert content entity](https://ecaguide.org/plugins/eca/content/events/content_entity_insert/index.md) and [Update content entity](https://ecaguide.org/plugins/eca/content/events/content_entity_update/index.md) may be considered.
### How to configure a form event
Any sort of form event always has the following restriction options, in order to specify for which form(s) the event should apply:
- **Restrict by form ID**: At most forms, the element ID of the rendered `