Rules Engine¶
GLPI provide a set of tools to implements a rule engine which take criteria in input and output actions. criteria and actions are defined by the user (and/or predefined at the GLPI installation).
Here is the list of base rules set provided in a staple GLPI:
ruleimportentity: rules for assigning an item to an entity,
ruleimportcomputer: rules for import and link computers,
rulemailcollector: rules for assigning a ticket created through a mails receiver,
ruleright: authorizations assignment rules,
rulesoftwarecategory: rules for assigning a category to software,
ruleticket: business rules for ticket.
Plugin could add their own set of rules.
Classes¶
A rules system is represented by these base classes:
RuleclassParent class for all Rule* classes. This class represents a single rule (matching a line in
glpi_rulestable) and include test, process, display for an instance.RuleCollectionclassParent class for all
Rule*Collectionclasses.This class represents the whole collection of rules for a
sub_type(matching all line inglpi_rulestable for thissub_type) and includes some method to process, duplicate, test and display the full collection.RuleCriteriaclassThis class permits to manipulate a single criteria (matching a line in
glpi_rulecriteriastable) and include methods to display and match input values.RuleActionclassThis class permits to manipulate a single action (matching a line in
glpi_ruleactionstable) and include methods to display and process output values.
And for each sub_type of rule:
RuleSubtypeclassDefine the specificity of the
sub_typerule like list of criteria and actions or how to display specific parts.RuleSubtypeCollectionclassDefine the specificity of the
sub_typerule collection like the preparation of input and the tests results.
Database Model¶
Here is the list of important tables / fields for rules:
glpi_rules:All rules for all
sub_typesare inserted here.sub_type: the type of the rule (ruleticket, ruleright, etc),
ranking: the order of execution in the collection,
match: define the link between the rule’s criteria. Can be AND or OR,
uuid: unique id for the rule, useful for import/export in xml,
condition: addition condition for the
sub_type(only used by ruleticket for defining the trigger of the collection on add and/or update of a ticket).
glpi_rulecriterias:Store all criteria for all rules.
rules_id: the foreign key for glpi_rules,
criteria: one of the key defined in the
RuleSubtype::getCriterias()method,condition: an integer matching the constant set in
Ruleclass constants,pattern: the direct value or regex to compare to the criteria.
glpi_ruleactions:Store all actions for all rules.
rules_id: the foreign key for glpi_rules,
action_type: the type of action to apply on the input. See
RuleAction::getActions(),field: the field to alter by the current action. See keys definition in
RuleSubtype::getActions(),value: the value to apply in the field.
Add a new Rule class¶
Here is the minimal setup to have a working set.
You need to add the following classes for describing you new sub_type.
src/RuleMytype.php
<?php
class RuleMytype extends Rule {
// optional right to apply to this rule type (default: 'config'), see Rights management.
static $rightname = 'rule_mytype';
// define a label to display in interface titles
function getTitle() {
return __('My rule type name');
}
// return an array of criteria
// default type can be found under Rule::getCriteriaDisplayPattern
function getCriterias() {
$criterias = [
'_users_id_requester' => [
'field' => 'name',
'name' => __('Requester'),
'table' => 'glpi_users',
'type' => 'dropdown',
],
'GROUPS' => [
'table' => 'glpi_groups',
'field' => 'completename',
'name' => sprintf(__('%1$s: %2$s'), __('User'),
__('Group'));
'linkfield' => '',
'type' => 'dropdown',
'virtual' => true,
'id' => 'groups',
],
...
];
return $criterias;
}
// return an array of actions
function getActions() {
$actions = [
'entities_id' => [
'name' => __('Entity'),
'type' => 'dropdown',
'table' => 'glpi_entities',
],
...
];
return $actions;
}
}
A separator can be added in the criteria or actions lists by adding an entry with text contents. It render the following criteria/actions in an HTML fieldset with the provided text as legend.
<?php
class RuleMytype extends Rule {
function getCriterias() {
return [
'_users_id_requester' => [...],
'separator' => __('Additional criteria'), // can be any string, not only 'separator'
'_users_id_observer' => [...],
];
}
function getActions() {
return [
'separator' => __('Observers'),
'_users_id_observer' => [...],
];
}
src/RuleMytypeCollection.php
<?php
class RuleMytypeCollection extends RuleCollection {
// a rule collection can process all rules for the input or stop
//after a single match with its criteria (default false)
public $stop_on_first_match = true;
// optional right to apply to this rule type (default: 'config'),
//see Rights management.
static $rightname = 'rule_mytype';
// menu key to use with Html::header in front page.
public $menu_option = 'myruletype';
// define a label to display in interface titles
function getTitle() {
return return __('My rule type name');
}
// if we need to change the input of the object before passing
//it to the criteria.
// Example if the input couldn't directly contains a criteria
//and we need to compute it before (GROUP)
function prepareInputDataForProcess($input, $params) {
$input['_users_id_requester'] = $params['_users_id_requester'];
$fields = $this->getFieldsToLookFor();
//Add all user's groups
if (in_array('groups', $fields)) {
foreach (Group_User::getUserGroups($input['_users_id_requester']) as $group) {
$input['GROUPS'][] = $group['id'];
}
}
}
...
return $input;
}
}
You need to also add the following php files for list and form:
front/rulemytype.php
<?php
include ('../inc/includes.php');
$rulecollection = new RuleMytypeCollection($_SESSION['glpiactive_entity']);
include (GLPI_ROOT . "/front/rule.common.php");
front/rulemytype.form.php
<?php
include ('../inc/includes.php');
$rulecollection = new RuleMytypeCollection($_SESSION['glpiactive_entity']);
include (GLPI_ROOT . "/front/rule.common.form.php");
And add the rulecollection in $CFG_GLPI (Only for Core rules):
inc/define.php
<?php
...
$CFG_GLPI["rulecollections_types"] = [
'RuleImportEntityCollection',
'RuleImportComputerCollection',
'RuleMailCollectorCollection',
'RuleRightCollection',
'RuleSoftwareCategoryCollection',
'RuleTicketCollection',
'RuleMytypeCollection' // <-- My type is added here
];
Plugin instead must declare it in their init function:
plugin/myplugin/setup.php
<?php
function plugin_init_myplugin() {
...
$Plugin->registerClass(
'PluginMypluginRuleMytypeCollection',
['rulecollections_types' => true]
);
...
}
Apply a rule collection¶
To call your rules collection and alter the data:
<?php
...
$rules = new PluginMypluginRuleMytypeCollection();
// data send by a form (which will be compared to criteria)
$input = [...];
// usually = $input, but it could differ if you want to avoid comparison of
//some fields with the criteria.
$output = [...];
// array passed to the prepareInputDataForProcess function of the collection
//class (if you need to add conditions)
$params = [];
$output = $rules->processAllRules(
$input,
$output,
$params
);
Test for rule collection¶
Changed in version 11.0.5: plugin and core RuleCollection can change the test path by overriding the RuleCollection::getRulesTestURL function.
For plugins, there is currently no GenericController so you must implement it.
Here is the minimal setup:
<?php
namespace GlpiPlugin\MyPlugin\Controller;
use Glpi\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;
final class RuleTestController extends AbstractController
{
#[Route(
"/rules/test", // /front/rulesengine.test.php for version previous to 11.0.5
name: "rule_myplugin_test",
methods: ["GET"],
)]
public function __invoke(Request $request): Response
{
// No generic RuleTestController controller for now
include(GLPI_ROOT . "/front/rulesengine.test.php");
return new Response();
}
}
Dictionaries¶
They inherits Rule* classes but have some specificities.
A dictionary aims to modify on the fly data coming from an external source (CSV file, inventory tools, etc.). It applies on an itemtype, as defined in the sub_type field of the glpi_rules table.
As the classic rules aim to apply additional and multiple data to input, dictionaries generally used to alter a single field (relative to the their sub_type). Ex, RuleDictionnaryComputerModel alters model field of glpi_computers.
Some exceptions exists and provide multiple actions (Ex: RuleDictionnarySoftware).
As they are shown in a separate menu, you should define they in a separate $CFG_GLPI entry in inc/define.php:
<?php
...
$CFG_GLPI["dictionnary_types"] = array('ComputerModel', 'ComputerType', 'Manufacturer',
'MonitorModel', 'MonitorType',
'NetworkEquipmentModel', 'NetworkEquipmentType',
'OperatingSystem', 'OperatingSystemServicePack',
'OperatingSystemVersion', 'PeripheralModel',
'PeripheralType', 'PhoneModel', 'PhoneType',
'Printer', 'PrinterModel', 'PrinterType',
'Software', 'OperatingSystemArchitecture',
'RuleMytypeCollection' // <-- My type is added here
);
Example: Adding Custom Actions with Customized display¶
In this example, we will add a _send_message action that:
Uses a textarea for input (instead of a standard text field).
Forces the action type to “Send”.
Limits input to 255 characters.
1. Define the Action¶
In your Rule class (e.g., TicketRule), override the getActions() method. Define your custom action and use force_actions to associate it with a specific action operator (like send).
<?php
...
public function getActions()
{
$actions = parent::getActions();
$actions['_send_message'] = [
'type' => 'textarea', // Custom type we will handle manually
'name' => __('Send a short text message', 'myplugin'),
'force_actions' => ['send'], // Force the 'Send' action operator; name can be found in RuleAction::getActions
];
return $actions;
}
2. Customize the Display¶
Override displayAdditionalRuleAction() to render your custom input field. This method allows you to output raw HTML (or use GLPI helpers) when your custom type is detected.
<?php
...
#[Override]
public function displayAdditionalRuleAction(array $action, $value = '')
{
if ($action['type'] === 'textarea') {
// Render a textarea with a character limit
echo "<textarea class='form-control' name='value' rows='4' maxlength='255'>" . htmlescape($value) . "</textarea>";
// Use the following if you don't need to limit the field maxlength
// Html::textarea(['name' => 'value', 'value' => $value, 'display' => true, 'rows' => 4]);
return true;
}
return false;
}
3. Handle the Execution Logic¶
By default, GLPI might not handle your custom field or the “Send” action type for assignment. Override executeActions() to manually handle the value.
<?php
...
#[Override]
public function executeActions($output, $params, array $input = [])
{
if (count($this->actions)) {
foreach ($this->actions as $action) {
// Intercept our specific field and action type
if ($action->fields["field"] == '_send_message' && $action->fields["action_type"] == 'send') {
// Manually assign the value to the output
$output[$action->fields["field"]] = $action->fields["value"];
}
}
}
return parent::executeActions($output, $params, $input);
}
