it would be great to do the CRUD operations for content entities in general, so we could act on terms, media, users, etc.
We've done just that in the Drupal piece in ActivePieces, maybe you can borrow the implementation from there? It has nothing hard-coded in there, it receives the list of entity types, their bundles, and their edit forms from the Drupal site with plain JSON:API, so that it's supposed to work with any Drupal site, regardless of their configuration.
I'm also interested in receiving and acting on webform submissions for one of my use cases.
The idea here is that ECA would be in the driver seat, as it is designed to respond to events that happen in Drupal (e.g. the webform submission), and then acts upon that. One action could then be to dispatch that event together with relevant data to external platforms like e.g. n8n. For AP we've done that through webhooks that AP can register on a Drupal site, and then ECA can leverage them for certain scenarios.
There may be other ways of doing this, but that's what we have so far, and it seems to be providing everything needed, and is easily customizable.
Thanks for testing this in #15 @svendecabooter, that makes sense, and I'M glad we're not in a catch-22.
I'm I wrong, or does this mean that there can't be a webform 6 release be available that works with Drupal 11.2.6 and also with earlier versions? Because there, we would most likely get the same exception.
This is looking great, thank you so much @rclemings for your contribution. I've just made a couple of minor corrections to fix code style tests. The 1 remaining test that fails is a Symfony deprecation in the Drupal 11.3.x context which we can ignore, i.e. I'll be fixing that by settings in the phpstan config.
That sounds wonderful, great progress.
Next steps will involve adding full CRUD operations for nodes (create, update, delete) and entity listing, aligning with the Drupal JSON:API approach.
Have you considered doing that part for content entities in general, not just nodes?
Once I get basic functions working, if anyone on this project would like an n8n account on my server for testing I can set them up.
I'm certainly interested to test this, let me know when it's time, but no rush.
I'm pretty sure a cache rebuild will resolve this.
@mandclu could you please give the dev release a try? I'd like to publish patch release with this fix and a few others later today, if possible.
I can't reproduce this. The user entity is just loading fine with search by name or email. It sounds like you probably run into a permission issue that the current user is not allowed to load user entities?
If that's not it, please create a simple ECA model to reproduce this and upload it here for review.
Oh, that's an interesting one. Surprising that this never happened before. Here is what's going on:
When installing a new module, the route provider from Drupal core rebuilds all routes. That triggers all sorts of hooks and events, and eventually, it calls the system_info_alter hook of the modeler api. I suspect this is because that Drupal site uses a view with a rest export, which collects all routes from views that also leverage some node access control plugins. That will call Drupal\Core\Extension\ExtensionList->getAllInstalledInfo() at some point, which then also dispatches the system info alter hook when building the module list.
The Modeler API implements that hook to alter the module info for ECA and other model owner like AI Agents so that their configuration path can be defined, as they don't own that route themselves, it's provided by the Modeler API. To alter those module infos, the access to the list of routes is necessary. And that's where the route building gets called recursively.
Now, to resolve this, we just remove that system info alter hook. Those configuration links only have one usage: they provide that link in the module list (/admin/modules). But it's not worth it to find a workaround for this issue only to have those links available.
Still, it's confusing that this hasn't shown up ever before. Anyway, on my way to fix this now.
Thanks a lot for all the testing. Glad we sorted this out together.
This is ready for being tested. Please update to ECA 3.0.7 and use the 2.0.x release of the ECA Tamper module. Then, rebuild the cache and afterwards this should be solved.
You asked the same question on Slack before opening this issue, and my response was this:
Custom events are not supposed to be dispatched by php code, they are ECA internal and should be dispatched by the action for it.
If you're writing custom code in php, then you can implement your own event.
The List: contains item condition requires a token name in the "Name of token containing the list" field, see description below the field. That token needs to be created first and it needs to contain the list with all the items you want to check against.
However, there might be a simpler way of achieving something similar. If you use the Compare two scalar values condition, your first value could be something like ,26122,26119,26567, and the second value would be ,[node:id],. The comparison operator would be contains. Note that in this example, both value to compare have a leading and trailing comma, that way you can avoid false positives, e.g. if your second value would just be 261.
You can use the Determining entity create access event and allow access to create any type of entity type. Just tried it with files successfully.
OK, we're not alone with this. Drupal Canvas had similar issues and solved it with new data types and config schema altering. We can implement something like that in ECA, and I've opened an issue at 📌 Add new data types and config schema alters to support tokens in more fields Active for working on it.
Leaving this one open until we can verify this against the solution provided over there.
Now I found the reason for this: the config schema.
The schema defines that the config field named value has the type float. Because of that, if we provide a token as the value, when Drupal core stores that config entity, it casts the value to a float. And casting e.g. [number] to a float results in 0.0.
So, the fact that ECA now supports config schema, causes that issue which is caused by a feature outside of ECA.
I wonder how to resolve this. We could either alter the config schema and turn all numeric values into string types. Or, we stop supporting tokens for numeric fields. As for the math plugin in ECA, you could actually turn things around by using the token in the "Data to be tampered" field and the numeric value in the "Value" field. This works until both values are represented by tokens.
The attachment is just the raw bpmn data, but not the eca model.
However, from the stack trace it looks like the ECA model has the issue that there is a form process event followed by a condition that wants to check if an entity field value has changed. In that context, there is no entity, so that's why that fails.
Why the drush command also executes that event is unclear to me, but without a stack trace for that one, it's hard to tell.
If your site doesn't allow you to either delete or edit and fix that ECA model due to that error, you may want to go for the kill switch that's described in the Troubleshooting section of the ECA Guide. That disables the ECA processor entirely and you can get into the UI and fix that model, or delete it.
The crowdsec module subscribes to the flood control event from Drupal core already, and if core dispatches that event, crowdsec takes that as a signal already.
If this module doesn't have additional functionality that would provide extra signals, then an integration wouldn't be helpful. From the name of the module I expected something extra, that's why I opened the issue. Please, feel free to close it if that's not the case.
Thank you @smulvih2 for investigating this. I'd like to suggest some of the implementation items you listed above, as the design of the orchestration module is to be agnostic of the remote platforms. If it requires additional features to make the n8n integration possible, then we should define and implement them in a generic way. The idea is to only have n8n specific code on the n8n side, not in Drupal.
I admit, this viewpoint got influenced by the fact that all of that was possible with the initial ActivePieces integration, and I don't know if and how something similar can be done on the n8n side, or for any of the other external platforms for that matter. But we should at least try.
Happy to get involved in that process, either by discussing it here in this issue, or in the Slack channel or even on another video call. Which ever you prefer is fine with me.
It's been a deliberate decision to only support Drupal 11.2 and later. Reason being that this is a project to showcase what can turn into something significant for Drupal and Drupal agencies in the future. And as Drupal 10 is already EOL as soon as next year, it didn't feel like supporting lower quality PHP code for something that just gets started would make a lot of sense. If we keep supporting old code for too long, it will hardly ever happen that we improve.
There has been a similar discussion on Slack in the #orchestration channel:
Therefore, I'd really prefer to keep the constraints and the code as is. But if there are compelling business reasons to reevaluate that, we'll certainly do that.
What's the message when the saving fails? I suspect this is about an invalid value in the operation field which is because the tamper plugin doesn't define a default value for it. To resolve this you need to change the operation to something else than addition and then back to addition, then the field gets a real value.
Similar comments as in the related issues apply here. I'd suggest that we finish them off in ECA 2.1.x first, and once that's being resolved, we can then replicate that same logic here.
This is looking like a good start but can be simplified. Also, I've changed the MR destination branch to 2.1.x, but still tests are failing. However, most if not all of those test failures will disappear if the comments I've left in the MR will have been incorporated. So, I'd start with them, and then see if all tests are green before the next review.
Hmm, that raises several questions:
- The
EntityFieldValueChanged->getOriginal()call is in line 43, how come that the exception is reporting line 38. Is there a chance that the code is not up to date? Maybe there is a patch somewhere that alters that code? - Any chance of getting a full stack trace when this happens?
- Is there an ECA model that takes action and fails while something gets disabled?
- The drush command
eca:disabledoesn't exist anymore in ECA 3. That disabling has moved to modeler API, so I'm wondering what's going wrong here?
This looks like what's being reported at 🐛 Can't make calculations, tokens are not properly resolved Active .
Not sure what the fix for the other issue is, but for now I needed to revert the change to that libraries get loaded again.
I ran into this as well, not related to mobile or desktop, though. I just figured that no libraries get called anymore. And when I revert that change from render to renderRoot, then the problem is fixed. This was introduced in
🐛
Fix empty render context
Needs work
but that doesn't seem to be the correct solution. I'm creating an MR to revert this.
OK, I can reproduce this. It's so that storing the math tamper config casts the value to a number. Let's see how we can avoid that.
Well, even if it feels cumbersome to reproduce what I've described in #7, there is a clear issue in the PHP code that is described like this:
In \Drupal\Core\Field\WidgetBase::extractFormValues there is this line:
$values = NestedArray::getValue($form_state->getValues(), $path, $key_exists);
As a result, that can return _none. This is acceptable according to the method declaration.
Further down, we then find this line:
$values = $this->massageFormValues($values, $form, $form_state);
In the case we received _none in the first line, we run into this issue because $values needs to be an array.
Even if that should never happen, it's statically not correct that we allow to pass in a string instead of an array.
I'm not sure I can follow. Are you saying you put in [b], and save the model, and the yaml still has 0.0 as the value?
When you import the model with the token, are you certain that the token really contains a number?
Regarding block visibility rules, there is an issue at ✨ provide a block condition plugin Active and the is also an ECA Condition → module which seems to be doing that, but I'm not sure how solid that module is.
@bigtomfelix I can't tel, those 2 plugins are provided by the user module in Drupal core and should always be there.
That sounds similar to what has been reported and fixed in 💬 Tamper math and token Active . But that would be part of bpmn_io 3.0.2, so I wonder why that should still be a problem. Maybe you can find something in that other issue that may help better understanding this?
Sounds great, so we don't have to change the format at our end?
Well, isn't the Remove a role from the selected users action working for you?
Also, side note, you're currently sending this in the version csphpcapi_drupal/v0.0.0 I guess it's in test more, but in case it's not :D
The version is now set to the module version, unless it's a dev release, then it uses the fallback 0.0.0. This is because your library validates the version string and only allows the format x.y.z, so I can't use anything outside of that pattern.
Well, that's not working that way. I am currently using that first format which is the proper one, but only the last dataset is being displayed in the dashboard.
Here is the payload of what was just pushed at 14:15 CET today:
{"scenario":"drupal\/4xx-scan","scenario_hash":"","scenario_version":"","scenario_trust":"manual","created_at":"2025-10-30T12:41:13.000000Z","machine_id":"v9hpas6u4i2d51nd3tyemjqa55df4hf7uhsljgp3dguontey","message":"","start_at":"2025-10-30T12:41:13.000000Z","stop_at":"2025-10-30T12:41:13.000000Z","uuid":"770ff947-138b-4273-a92a-3d9ad71c00c9","context":[{"key":"target_uri","value":"\/phpinfo.php"},{"key":"user_agent","value":"Mozilla\/5.0 (Linux x86_64; X11) Gecko\/20043002 Firefox\/16.0"},{"key":"method","value":"GET"},{"key":"status","value":404},{"key":"target_uri","value":"\/info.php"},{"key":"user_agent","value":"Mozilla\/5.0 (Linux x86_64; X11) Gecko\/20043002 Firefox\/16.0"},{"key":"method","value":"GET"},{"key":"status","value":404},{"key":"target_uri","value":"\/php_info.php"},{"key":"user_agent","value":"Mozilla\/5.0 (Linux x86_64; X11) Gecko\/20043002 Firefox\/16.0"},{"key":"method","value":"GET"},{"key":"status","value":404},{"key":"target_uri","value":"\/phpinfo"},{"key":"user_agent","value":"Mozilla\/5.0 (Linux x86_64; X11) Gecko\/20043002 Firefox\/16.0"},{"key":"method","value":"GET"},{"key":"status","value":404},{"key":"target_uri","value":"\/test.php"},{"key":"user_agent","value":"Mozilla\/5.0 (Linux x86_64; X11) Gecko\/20043002 Firefox\/16.0"},{"key":"method","value":"GET"},{"key":"status","value":404},{"key":"target_uri","value":"\/info1.php"},{"key":"user_agent","value":"Mozilla\/5.0 (Linux x86_64; X11) Gecko\/20043002 Firefox\/16.0"},{"key":"method","value":"GET"},{"key":"status","value":404},{"key":"target_uri","value":"\/info2.php"},{"key":"user_agent","value":"Mozilla\/5.0 (Linux x86_64; X11) Gecko\/20043002 Firefox\/16.0"},{"key":"method","value":"GET"},{"key":"status","value":404}],"decisions":[{"id":0,"uuid":"770ff947-138b-4273-a92a-3d9ad71c00c9","duration":"1h0m0s","scenario":"drupal\/4xx-scan","origin":"crowdsec","scope":"ip","value":"13.229.146.132","type":"ban","simulated":false}],"source":{"scope":"ip","value":"13.229.146.132"}}
The dashboard only shows the last of the 5 target_uris.
Thank you @sunlix, this made me eventually discover where the problem is coming from. It's a misconception in the hook that dynamically provides the third party settings: it goes through the available model owners and applies its third-party settings to their model config entities. Well, the problem is, that list of owner only contains the eca model owner when eca_ui is enabled.
So, the idea of dependencies was quite correct, but the hook implementation was wrong. I've now found a much better approach to define those third party settings without even requiring a hook, and therefore we don't even require the content rebuild after installation, which is even better.
For the next test, please use modeler_api 1.1.x-dev and eca 3.0.x-dev - this should now be working.
The way I tested this to reproduce the issue and to now confirm that it's fixed:
drush si
drush cr
drush recipe ../recipes/eca_lib_0007
That recipe I used comes from the library in the ECA Guide and can be made available with composer require drupal-eca-recipe/eca_lib_0007
If you can confirm that this is now working, I'll make this available for modeler_api 1.0 as well and will tag new releases for it and for ECA.
There is the eca_access submodule that allows you to implement access control with ECA.
I'm still not sure this is a bug in ECA, setting back the category.
Here is what I can find when debugging this:
The ECA entity looks perfectly OK, and the config storage handler is calling the save method of that config entity. The config validator then complains about the dot being part of the key of the action plugin being used. That must be a new thing in the config validator and not in ECA. Or it may be caused by the fact that ECA 3 now comes with validatable configuration which wasn't the case before.
In any event, those preconfigured action plugins come from Drupal core, and we've seen variations of issues with them in the past. Why don't you just use the Add a role to the selected users action plugin which can be configured like this:
That's exactly what I thought, yes. It's just failing tests. I think the PHPCS failure can be resolved by $value = $value ?? default, and regarding the PHPStan errors, it's that not each plugin is configurable and therefore doesn't always have that method to receive default configuration.
When this is done for 2.1.x we will then also have to migrate to the bpmn_ui 3.0.x branch, because that code segment was moved out of ECA in the new release.
Sorry, I was AFK, but have now added the summary from slack above.
Merged this as the final showstopper with a preprocessing hook got resolved with the help of @nicxvan
I am unsure what would be the next recommendation. I think ECA UI should be a dependency of ECA?
No, the fact that enabling eca_ui after the installation doesn't mean that enabling it during installation would have the same effect. Our assumption is that the issue comes from the installation process. Because even without enabling eca_ui, when modeler_api is enabled and cache is cleared, you won't get schema errors.
You mention in #11 that you're using mpodeler_api 1.0.4, so you haven't applied the suggested fix from #10? If so, would you mind applying that and then trying it again?
@smustgrave yes, we can do that. But first we need to determine what's the best way to accomplish what needs to be done. It feels like there will be quite a bit of back and forth before we get sign-off on this. And then, it can only be tested once Gin will be merged into core as well.
The MR from the referenced issue in the user module of Drupal core is required in order to test this.
No relevant legacy code found any more, therefore marking as fixed.
There is nothing to be done here any more, as we decided to continue with Gin 5.x for Drupal 11.3 and beyond. Therefore, Gin Toolbar can still be used together with that version of Gin.
The merged version of Gin in core will have a different name (see 📌 Rename Gin Active ) and Gin Toolbar will not support that.
For system.mail there is no UI to configure it, at least not in Drupal core. So, it appears that it's the responsibility of the mailer system to set the default interface correctly. And the SMTP module is already doing it in 2 scenarios: when the module gets enabled, and if the SMTP gets enabled or disabled through the form.
What I'm proposing is to do the same thing also when those settings get changed via config set and config import. An event subscriber should be the right way of doing this.
There is detailed logging available. Going to /admin/config/workflow/eca/settings you can configure the level of what should be logged. Changing that to "Debugging" will give you all background information for each step in each model. Is that what you're looking for?
Moving forward, we're currently planning a lot of improvements that may also influence this area. For further details, you may want to join the #eca-next-gen channel on Drupal Slack.
Thank you @nisith for providing a sample. Unfortunately, I don't see how that would improve what's already possible with ECA.
@bluegeek9 the problem is that the form submit is doing more than just saving configuration values from the form into smtp.settings. It also updates config in system.mail.
That leads to the problem, that if SMTP config is changed through the form, the end result is different from other was of changing the same config without the form, e.g. config import or drush config set.
This is not how that should work. When SMTP config changes such that this should lead to changes in system.mail as well, then that logic should be always applied when those conditions arise.
Lot's of changes again, time to test this with the Gin 6.x again. Good news, this is still working with any issues. And we've also replaced the call to the deprecated theme_get_setting method successfully.
Looks like this should be NR according to the last 2 comments. My tests most likely don't qualify for RTBC yet.
@anybody the ban dependency is now removed for version 1.2.x and CrowdSec now uses either the ban or the advban module, which ever is available.
Thank you @antonín slejška for reviewing and testing this, and also for the great feedback from your IT team. I've just merged this now.
Currently, we send signals in this structure:
"context": [
{
"key": "target_uri",
"value": "\/app\/"
},
{
"key": "user_agent",
"value": "Mozilla\/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit\/537.36 (KHTML, like Gecko) Chrome\/135.0.0.0 Safari\/537.36"
},
{
"key": "method",
"value": "GET"
},
{
"key": "status",
"value": 403
},
{
"key": "target_uri",
"value": "\/login\/"
},
{
"key": "user_agent",
"value": "Mozilla\/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit\/537.36 (KHTML, like Gecko) Chrome\/135.0.0.0 Safari\/537.36"
},
{
"key": "method",
"value": "GET"
},
{
"key": "status",
"value": 404
},
{
"key": "target_uri",
"value": "\/matomo.js"
},
{
"key": "user_agent",
"value": "Mozilla\/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit\/537.36 (KHTML, like Gecko) Chrome\/135.0.0.0 Safari\/537.36"
},
{
"key": "method",
"value": "GET"
},
{
"key": "status",
"value": 404
},
{
"key": "target_uri",
"value": "\/login\/matomo.js"
},
{
"key": "user_agent",
"value": "Mozilla\/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit\/537.36 (KHTML, like Gecko) Chrome\/135.0.0.0 Safari\/537.36"
},
{
"key": "method",
"value": "GET"
},
{
"key": "status",
"value": 404
},
{
"key": "target_uri",
"value": "\/app\/matomo.js"
},
{
"key": "user_agent",
"value": "Mozilla\/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit\/537.36 (KHTML, like Gecko) Chrome\/135.0.0.0 Safari\/537.36"
},
{
"key": "method",
"value": "GET"
},
{
"key": "status",
"value": 404
},
{
"key": "target_uri",
"value": "\/app\/"
},
{
"key": "user_agent",
"value": "Mozilla\/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit\/537.36 (KHTML, like Gecko) Chrome\/135.0.0.0 Safari\/537.36"
},
{
"key": "method",
"value": "GET"
},
{
"key": "status",
"value": 403
}
],
That doesn't work, it only shows each key (e.g. target_uri) once in the dashboard.
I've tried changing this into this:
"context": [
[
{
"key": "target_uri",
"value": "\/app\/"
},
{
"key": "user_agent",
"value": "Mozilla\/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit\/537.36 (KHTML, like Gecko) Chrome\/135.0.0.0 Safari\/537.36"
},
{
"key": "method",
"value": "GET"
},
{
"key": "status",
"value": 403
}
],
[
{
"key": "target_uri",
"value": "\/login\/"
},
{
"key": "user_agent",
"value": "Mozilla\/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit\/537.36 (KHTML, like Gecko) Chrome\/135.0.0.0 Safari\/537.36"
},
{
"key": "method",
"value": "GET"
},
{
"key": "status",
"value": 404
}
],
[
{
"key": "target_uri",
"value": "\/matomo.js"
},
{
"key": "user_agent",
"value": "Mozilla\/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit\/537.36 (KHTML, like Gecko) Chrome\/135.0.0.0 Safari\/537.36"
},
{
"key": "method",
"value": "GET"
},
{
"key": "status",
"value": 404
}
],
[
{
"key": "target_uri",
"value": "\/login\/matomo.js"
},
{
"key": "user_agent",
"value": "Mozilla\/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit\/537.36 (KHTML, like Gecko) Chrome\/135.0.0.0 Safari\/537.36"
},
{
"key": "method",
"value": "GET"
},
{
"key": "status",
"value": 404
}
],
[
{
"key": "target_uri",
"value": "\/app\/matomo.js"
},
{
"key": "user_agent",
"value": "Mozilla\/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit\/537.36 (KHTML, like Gecko) Chrome\/135.0.0.0 Safari\/537.36"
},
{
"key": "method",
"value": "GET"
},
{
"key": "status",
"value": 404
}
],
[
{
"key": "target_uri",
"value": "\/app\/"
},
{
"key": "user_agent",
"value": "Mozilla\/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit\/537.36 (KHTML, like Gecko) Chrome\/135.0.0.0 Safari\/537.36"
},
{
"key": "method",
"value": "GET"
},
{
"key": "status",
"value": 403
}
]
]
So, it's an array of arrays that each contains the target_uri and the other keys. But that fail with the following exception when calling $watcher->buildSignal():
Can not create signal on scenario drupal/4xx-scan for IP 34.1.22.188: Something went wrong while creating signal: Unrecognized options "0, 1, 2, 3" under "signalConfig.context.0". Available options are "key", "value".
@rr404 how are we supposed to send multiple context records with one signal?