Automation Plugin
Jira-style automation engine for Festi projects. Admins manage rules in a
DGS grid at /automation/workspace/ (search, enable/disable toggle,
pagination) and compose each Trigger → Conditions → Actions rule on a
Drawflow canvas at /automation/workspace/rules/<id>/ (or rules/create/
for a new one). Triggers and actions are added through a searchable picker
dialog grouped by owning plugin; the registry is fetched via AJAX, so the
builder scales to hundreds of registry entries. The available building
blocks are discovered by scanning PHP attributes declared in the plugins
that own them, so there is no central registry to maintain. The read-only
trigger registry lives at /automation/workspace/triggers/.
Requirements
- The Festi Theme package (
Festi\Theme\Plugin\DisplayPlugin). - The Modules plugin. The plugin class implements
plugin\Modules\domain\scope\ISystemSpaceModule. - The Queues plugin. Each trigger event goes into the queue
Automation.
Installation
Use the Festi CLI to add the plugin to the project. Run the command from the project root:
./vendor/bin/festi-plugin --mode install --path ./src/dashboard/ --dump ./dumps/ \
--plugin Automation --repository [email protected]:FestiPlugins/php_festi_plugin_automation.git
This example was not run for this page.
The command adds the repository to the plugins directory as a git submodule and adds the plugin to festi_plugins.
The repository has no install folder. Thus the command does not create the automation_* tables, the queue table,
the URL rules or the sections. Add them with a migration of the project and apply it with festi-migrate.
The plugin uses these sections (AutomationSections):
| Section | Constant | Function |
|---|---|---|
automation_settings |
SETTINGS |
The rule list, the rule builder and the rule log. |
automation_manage_triggers |
MANAGE_TRIGGERS |
The trigger registry page. |
automation_user |
USER |
The user scope of the rules. |
Declaring a trigger
A trigger is a FestiEvent subclass tagged with #[AutomationTrigger].
It must define a non-empty EVENT_TYPE constant — that constant is the
registry ident and the event type the engine subscribes to. Place trigger
classes in plugins/<Name>/domain/automation/trigger/ (or
Domain/Automation/Trigger/ for PSR-4 plugins) so the scanner finds them.
#[AutomationTrigger(
caption: 'User Created',
description: 'Fires when a new employee account is created.'
)]
class UserCreatedEvent extends FestiEvent
{
public const EVENT_TYPE = 'user_created_event';
#[AutomationConditionEntity(caption: 'User')]
protected UserValuesObject $user;
}
#[AutomationConditionEntity]marks an event property holding a values object the rules can inspect and pass to actions.#[AutomationConditionField]marks a scalar public event property as a testable field.
Declaring condition fields on a values object
Tag getters of the values object with #[AutomationConditionField]. The
field ident defaults to the getter name (getTypeID → type_id); the
getter must declare a scalar return type.
#[AutomationConditionField(name: 'email', caption: 'Email')]
public function getEmail(): ?string
Declaring an action
Tag a public plugin method with #[AutomationAction]. Scalar parameters
tagged #[AutomationActionOption] are filled by the rule author in the
builder; values-object parameters tagged #[AutomationActionEntity] are
bound to a trigger entity at run time. Pass multiline: true on an option
to render it as a textarea instead of a single-line input.
#[AutomationAction(caption: 'Notify User')]
public function sendUserNotification(
#[AutomationActionEntity(caption: 'User')]
UserValuesObject $userValuesObject,
#[AutomationActionOption(caption: 'Title')]
string $title,
#[AutomationActionOption(caption: 'Message', multiline: true)]
string $message
): void
Action options can also use a typed dynamic options provider. Providers
implement IActionOptionsProvider and return stored values mapped to their
user-facing captions. Set multiple: true on an array parameter to render a
multi-select and persist all selected values.
#[AutomationActionOption(
caption: 'Roles',
optionsProvider: UserTypeOptionsProvider::class,
multiple: true
)]
array $userTypeIDs
Notification recipients
The Notification plugin exposes three dashboard-notification actions:
- Notify User sends to the user entity emitted by the trigger.
- Notify User's Mentors resolves that user's active mentors when the queued action runs.
- Notify Users by Role resolves all active users of one or more selected company roles when the queued action runs.
Recipient groups are resolved at action execution time. Empty groups are a successful no-op, and recipients are deduplicated within each action.
Registry synchronization
The scanner (Domain/Scanner/AutomationScanner) reflects the tagged
classes and Domain/Scanner/DefinitionSynchronizer upserts them into the
automation_* tables. The method AutomationPlugin::onCronSyncPlugins() does
the scan. Start it from a cron script of the host project or with the Festi CLI:
./vendor/bin/festi-plugin --mode exec --plugin Automation --method onCronSyncPlugins
This example was not run for this page.
Broken classes are skipped; one bad file never aborts the sync. When the scan has errors, the synchronizer does not remove registry rows.
Runtime
AutomationPlugin::onLoadSystemSpaceModule() subscribes to every trigger
ident that has an active rule. The Modules plugin calls this method when it
loads the module. The plugin has no init.php. A fired event is serialized
(Domain/Engine/TriggerContextSerializer) and enqueued through the Queues
plugin (queue_automation table). The queue worker rehydrates the
context, evaluates each active rule's conditions
(Domain/Engine/ConditionEvaluator, AND semantics, missing data = no
match) and executes matching actions (Domain/Engine/ActionInvoker) with
per-action error isolation.
Run the worker: php plugins/Queues/queues.php --name Automation.
Known Spike limitations
- Condition operands are typed literals; enum operands (e.g. picking a user type by name instead of its numeric id) are a follow-up.
- Rules are linear: one trigger, N AND-joined conditions, M actions.
- Removing a trigger/action from code deletes its registry rows on the next sync; dependent rules are cascade-deleted rather than soft-flagged.