Upload
srijan-technologies
View
514
Download
1
Embed Size (px)
Citation preview
• Facet API Pretty Paths
• Geocluster
• Drupal Centroamerica, Austria, Switzerland, ++
• Deputy Head Technology @ Amazee Labs
twitter.com/dasjo
• fago (Rules creator)• klausi (co-maintainer)• dasjo (communication)• fubhy (developer)• nico grienauer (design)• steve purkiss
(developer)
#d8rules team
Rules• Build flexible workflows
using events, condition & actions
• Send customized mails to notify your users
• Create custom redirections, system messages, breadcrumbs
Rules• Build flexible workflows
using events, condition & actions
• Send customized mails to notify your users
• Create custom redirections, system messages, breadcrumbs
• 300.000+ reported installs(30% of all Drupal 7 sites)
• Hundreds of integration modules
• Entity API, Fields, Views, Webform, Context, Features, Search API, Tokens, Paths, Menus, Queue, Field formatter conditions, ...
Drupal 8 wins
• OOP, Dependency Injection
• APIs• PHPUnit• Symfony2
• Removed legacy modules
• Web services built in
• Front-end, responsive, ...
Drupal 8 for Rules
• Plug-in API• Entity & Typed Data API• Conditions API• (Actions API -> forked)• Context API shared w/ Core, Page Manager• Configuration Management (CMI, YAML)
Reusable components (“rtools”)
• Tokens (automatic, based on typed data)• Typed data widgets & formatters• Extended Context API• Embeddable Rules UI components
• Actions & Conditions• Rules data selector for tokens, contexts
Site building
• Admin UI usability improvements• (Simple Views Bulk operations in core)• “Inline Rules” instead of Rule sets / Rules
conditional• Deployable configuration
#d8rules goals• Accelerate Drupal 8 uptake by ensuring that
Rules as a key contributed module is ready, early enough.
• Enable flexible workflows in Drupal 8 sites that are easily configurable & reusable.
• Make Drupal contributions sustainable by funding contributed code used on hundreds of thousands of sites.
Rules 8.x M1Rules core API fundamentals
✓ Rules core engine, plugin types✓ Align Rules condition & action APIs with core✓ Parameter configuration & Context mapping
Rules 8.x M2Rules core completion with basic UI
✓ Completed Rules engine features✓ Completed Rules plugins (Events, Loops, API)✓ Configuration entity, CMI and integrity checks✓ Basic UI and autocomplete functionality✓ API for embedding the Rules UI✓ Twig-style token replacements
Rules 8.x M3Rules release
❏ Stable Rules 8.x-3.0 Release❏ Rules Engine follow-ups❏ Complete Rules UI❏ Rules scheduler port❏ Port existing integrations (almost done!)
Rules 8.xRoadmap
https://www.drupal.org/node/2245015
Sprints, trainings & sessionsSzeged, Portoroz, Austin, Drupalaton, Amsterdam, Zurich, Bogotá, Vienna, Milano, Montpellier, Bratislava, Sunderland, Barcelona, Mumbai, Heidelberg, Granada, ..
http://d8rules.org/events
Contribution
• 114 forks, 40 contributors• paranoik, stevepurkiss, scuts, jibran, chindris,
omissis, ndewhurst, jzavrl, MegaChriz, bbujisic, dawehner, torenware, bartfeenstra, M1r1k, rinasek, joashuataylor, lokapujya, icanblink, bojanz, a.milkovsky, mariancalinro, pjezek,czigor, mikl, nlisgo, nielsdefeyter, ...
• 427 closed pull requests, Thank you all!
Getting started
• Setup Drupal 8• Fork Rules 8.x on github:
• https://github.com/fago/rules
/** * Provides a 'Node is sticky' condition. * * @Condition( * id = "rules_node_is_sticky", * label = @Translation("Node is sticky"), * category = @Translation("Node"), * context = {…} * ) */class NodeIsSticky extends RulesConditionBase {
/** * {@inheritdoc} */ public function evaluate() { $node = $this->getContextValue('node'); return $node->isSticky(); }
}
/** * Provides a 'Delete entity' action. * * @Action( * id = "rules_entity_delete", * label = @Translation("Delete entity"), * category = @Translation("Entity"), * context = {…} * ) */class EntityDelete extends RulesActionBase {
/** * {@inheritdoc} */ public function execute() { $entity = $this->getContextValue('entity'); $entity->delete(); }
}
rules_user_login:
label: 'User has logged in'
category: 'User'
context:
account:
type: 'entity:user'
label: 'Logged in user'
Event example from rules.rules.events.yml
Event invocation
/** * Implements hook_user_login(). */function rules_user_login($account) { // Set the account twice on the event: as the main subject but also in the // list of arguments. $event = new UserLoginEvent($account, ['account' => $account]); $event_dispatcher = Drupal::service('event_dispatcher'); $event_dispatcher->dispatch(UserLoginEvent::EVENT_NAME, $event);}
Event invocation
/** * Implements hook_user_login(). */function rules_user_login($account) { // Set the account twice on the event: as the main subject but also in the // list of arguments. $event = new UserLoginEvent($account, ['account' => $account]); $event_dispatcher = Drupal::service('event_dispatcher'); $event_dispatcher->dispatch(UserLoginEvent::EVENT_NAME, $event);}
/** * Provides a 'Delete entity' action. * * @Action( * id = "rules_entity_delete", * label = @Translation("Delete entity"), * category = @Translation("Entity"), * context = {…} * ) */class EntityDelete extends RulesActionBase {
/** * {@inheritdoc} */ public function execute() { $entity = $this->getContextValue('entity'); $entity->delete(); }
}
/** * Provides a 'Delete entity' action. * * @Action( * id = "rules_entity_delete", * label = @Translation("Delete entity"), * category = @Translation("Entity"), * context = { * "entity" = @ContextDefinition("entity", * label = @Translation("Entity"), * description = @Translation("Specifies the entity, which should be deleted permanently.") * ) * } * ) */class EntityDelete extends RulesActionBase {...}
class EntityDelete extends RulesActionBase {
/** * Executes the action with the given context. */ public function doExecute(EntityInterface $entity) { $entity->delete(); }
}
class EntityDelete extends RulesActionBase {
/** * Executes the action with the given context. */ public function doExecute(EntityInterface $entity) { $entity->delete(); }
}
class FetchEntityById extends RulesActionBase implements ContainerFactoryPluginInterface {
/** * Executes the action with the given context. */ public function doExecute($entity_type, $entity_id) { $storage = $this->entityManager->getStorage($entity_type); $entity = $storage->load($entity_id); $this->setProvidedValue('entity', $entity); }}
class FetchEntityById extends RulesActionBase implements ContainerFactoryPluginInterface {
/** * Executes the action with the given context. */ public function doExecute($entity_type, $entity_id) { $storage = $this->entityManager->getStorage($entity_type); $entity = $storage->load($entity_id); $this->setProvidedValue('entity', $entity); }}
Context
● Defining context for a plug-in● Using context within a plug-in● Passing context to the plug-in
class ListCountIsTest extends RulesIntegrationTestBase {
/** * Tests evaluating the condition. * * @covers ::evaluate() */ public function testConditionEvaluation() { // Test that the list count is greater than 2. $this->condition ->setContextValue('list', [1, 2, 3, 4]) ->setContextValue('operator', '>') ->setContextValue('value', '2'); $this->assertTrue($condition->evaluate()); }
}
class ListCountIsTest extends RulesIntegrationTestBase {
/** * Tests evaluating the condition. * * @covers ::evaluate() */ public function testConditionEvaluation() { // Test that the list count is greater than 2. $this->condition ->setContextValue('list', [1, 2, 3, 4]) ->setContextValue('operator', '>') ->setContextValue('value', '2'); $this->assertTrue($condition->evaluate()); }
}
Storing configuration
• D7: Entity exportables
• Rules:• RulesPlugin->export()
-> ?• RulesEntityController->import()
-> ?
Storing configuration
• D7: Entity exportables
• Rules:• RulesPlugin->export()
-> RulesExpression::getConfiguration()• RulesEntityController->import()
-> RulesExpression::setConfiguration()
Drupal 8: CMI● Rules leverages the Config System● Two types of config entities:
○ Reaction Rules (WIP)○ Components
● Inherit features from CMI○ Config deployment / import&export / sync○ Config Translation (instead of Entity i18n)○ Default config
Provide default rule configs
• hook_default_rules_configuration()-> rules.reaction.*.yml-> rules.component.*.yml
langcode: enstatus: truedependencies: {}id: rules_test_default_componentlabel: Rules test default componentmodule: rulesdescription: 'Tests adding Rules component by default.'tag: 'test'core: 8.xexpression_id: rules_ruleconfiguration:
...
configuration: id: rules_rule context: user: type: 'entity:user' label: User conditions: id: rules_and conditions: {} actions: id: rules_action_set actions: - id: rules_action action_id: rules_system_message context_mapping: message: 'user:mail:value'
Executing the component$config_entity = RulesComponent::load('rules_test_default_component');
$expression = $config_entity->getExpression();
$expression ->setContextValue('user', \Drupal::currentUser()) ->execute();
Typed Data API
• Consistent way of interacting with any data based on metadata
• Part of Drupal 8 & Entity Fields• Defines a type system for PHP:
○ Primitive types (integer, float, string, dates, ..)○ Complex types○ Lists (with items of a specified type)
Data types● any● string, integer,
uri, float, ...○ email○ timestamp,
datetime_iso8601○ timespan,
duration_iso8601
● entity○ entity:node○ entity:comment
● field_item○ field_item:string○ field_item:text○ field_item:image
/** * The float data type. * * The plain value of a float is a regular PHP float. For setting the value * any PHP variable that casts to a float may be passed. * * @DataType( * id = "float", * label = @Translation("Float") * ) */class FloatData extends PrimitiveBase implements FloatInterface {
/** * {@inheritdoc} */ public function getCastedValue() { return (float) $this->value; }}
/** * The float data type. * * The plain value of a float is a regular PHP float. For setting the value * any PHP variable that casts to a float may be passed. * * @DataType( * id = "float", * label = @Translation("Float") * ) */class FloatData extends PrimitiveBase implements FloatInterface {
/** * {@inheritdoc} */ public function getCastedValue() { return (float) $this->value; }}
/** * Plugin implementation of the 'link' field type. * * @FieldType( * id = "link", * label = @Translation("Link"), * description = @Translation(“..."), * default_widget = "link_default", * default_formatter = "link", * constraints = {"LinkType" = {}} * ) */class LinkItem extends FieldItemBase implements LinkItemInterface {
public static function propertyDefinitions( FieldStorageDefinitionInterface $field_definition) {
$properties['url'] = DataDefinition::create('string') ->setLabel(t('URL'));… return $properties; }
/** * Plugin implementation of the 'link' field type. * * @FieldType( * id = "link", * label = @Translation("Link"), * description = @Translation(“..."), * default_widget = "link_default", * default_formatter = "link", * constraints = {"LinkType" = {}} * ) */class LinkItem extends FieldItemBase implements LinkItemInterface {
public static function propertyDefinitions( FieldStorageDefinitionInterface $field_definition) {
$properties['url'] = DataDefinition::create('string') ->setLabel(t('URL'));… return $properties; }
Describe data to rules
• hook_rules_data_info()-> Data type plugins, Typed Data
• hook_entity_property_info_alter-> hook_data_type_info_alter()-> hook_entity_base_field_info/alter()-> hook_entity_bundle_field_info/alter()-> FieldItem::propertyDefintions()
Lists & Multiple values● list<foo> notation is gone● “list” data type & per data type class● Separate data definitions for lists vs. list
items● Mark context as “multiple”
$rule = $this->expressionManager->createRule([ 'context_definitions' => [ 'test' => [ 'type' => 'string', 'label' => 'Test string', ], ], ]);
$rule = $this->expressionManager->createRule([ 'context_definitions' => [ 'test' => [ 'type' => 'string', 'label' => 'Test string', ], ], ]);
$rule->addCondition('rules_test_string_condition', ContextConfig::create()->map('text', 'test')
);
$rule = $this->expressionManager->createRule([ 'context_definitions' => [ 'test' => [ 'type' => 'string', 'label' => 'Test string', ], ], ]);
$rule->addCondition('rules_test_string_condition', ContextConfig::create()->map('text', 'test')
);
$rule->addAction('rules_test_log');
$rule = $this->expressionManager->createRule([ 'context_definitions' => [ 'test' => [ 'type' => 'string', 'label' => 'Test string', ], ], ]);
$rule->addCondition('rules_test_string_condition', ContextConfig::create()->map('text', 'test')
);
$rule->addAction('rules_test_log'); $rule->setContextValue('test', 'test value');
$rule = $this->expressionManager->createRule([ 'context_definitions' => [ 'test' => [ 'type' => 'string', 'label' => 'Test string', ], ], ]);
$rule->addCondition('rules_test_string_condition', ContextConfig::create()->map('text', 'test')
);
$rule->addAction('rules_test_log'); $rule->setContextValue('test', 'test value'); $rule->execute();
$rule = $this->expressionManager->createRule(); // Condition provides a "provided_text" variable. $rule->addCondition('rules_test_provider');
$rule = $this->expressionManager->createRule(); // Condition provides a "provided_text" variable. $rule->addCondition('rules_test_provider'); // Action provides a "concatenated" variable. $rule->addAction('rules_test_string',
ContextConfig::create()->provideAs('text', 'provided_text')
);
$rule = $this->expressionManager->createRule(); // Condition provides a "provided_text" variable. $rule->addCondition('rules_test_provider'); // Action provides a "concatenated" variable. $rule->addAction('rules_test_string',
ContextConfig::create()->provideAs('text', 'provided_text')
); // Same action again now provides "concatenated2" $rule->addAction('rules_test_string',
ContextConfig::create()->map(text, 'concatenated') ->provideAs('concatenated', 'concatenated2')
);
$rule = $this->expressionManager->createRule(); // Condition provides a "provided_text" variable. $rule->addCondition('rules_test_provider'); // Action provides a "concatenated" variable. $rule->addAction('rules_test_string',
ContextConfig::create()->provideAs('text', 'provided_text')
); // Same action again now provides "concatenated2" $rule->addAction('rules_test_string',
ContextConfig::create()->map(text, 'concatenated') ->provideAs('concatenated', 'concatenated2')
); $rule->execute();
/** * A data processor for applying numerical offsets. * * The plugin configuration must contain the following entry: * - offset: the value that should be added. * * @RulesDataProcessor( * id = "rules_numeric_offset", * label = @Translation("Apply numeric offset") * ) */class NumericOffset extends PluginBase implements RulesDataProcessorInterface {
/** * {@inheritdoc} */ public function process($value) { return $value + $this->configuration['offset']; }
}
Automated testing
• RulesUnitTestBase extends UnitTestCase• Internal unit tests such as RuleTest, RulesAndTest,
RulesContextTraitTest, …
Automated testing
• RulesUnitTestBase extends UnitTestCase• Internal unit tests such as RuleTest, RulesAndTest,
RulesContextTraitTest, …• RulesIntegrationTestBase
• use ActionManager, ConditionManager, TypedDataManager, …
class DataListCountIs extends RulesConditionBase {
/** * {@inheritdoc} */ public function evaluate() { $list = $this->getContextValue('list'); $operator = $this->getContextValue('operator'); $value = $this->getContextValue('value');
switch ($operator) { case '==': return count($list) == $value;
case '<'; return count($list) < $value;
case '>'; return count($list) > $value;
} }
}
class ListCountIsTest extends RulesIntegrationTestBase {
/** * Tests evaluating the condition. * * @covers ::evaluate() */ public function testConditionEvaluation() { // Test that the list count is greater than 2. $condition = $this->condition ->setContextValue('list', [1, 2, 3, 4]) ->setContextValue('operator', '>') ->setContextValue('value', '2'); $this->assertTrue($condition->evaluate());
// Test that the list count is not equal to 0. $condition = $this->condition ->setContextValue('list', [1, 2, 3]) ->setContextValue('operator', '==') ->setContextValue('value', '0'); $this->assertFalse($condition->evaluate()); }
}
Automated testing
• RulesUnitTestBase extends UnitTestCase• Internal unit tests such as RuleTest, RulesAndTest,
RulesContextTraitTest, …• RulesIntegrationTestBase
• use ActionManager, ConditionManager, TypedDataManager, …
Automated testing
• RulesUnitTestBase extends UnitTestCase• Internal unit tests such as RuleTest, RulesAndTest,
RulesContextTraitTest, …• RulesIntegrationTestBase
• use ActionManager, ConditionManager, TypedDataManager, …
• RulesEntityIntegrationTestBase
/** * @Condition( * id = "rules_entity_is_of_type", * label = @Translation("Entity is of type"), * category = @Translation("Entity"), * context = { * "entity" = @ContextDefinition("entity", * label = @Translation("Entity"), * ), * "type" = @ContextDefinition("string", * label = @Translation("Type"), * ) * } * ) */class EntityIsOfType extends RulesConditionBase {
public function evaluate() { $provided_entity = $this->getContextValue('entity'); $specified_type = $this->getContextValue('type'); $entity_type = $provided_entity->getEntityTypeId();
// Check whether the entity type matches. return $entity_type == $specified_type; }}
class EntityIsOfTypeTest extends RulesEntityIntegrationTestBase {
/** * Tests evaluating the condition. * * @covers ::evaluate() */ public function testConditionEvaluation() { $entity = $this->getMock('Drupal\Core\Entity\EntityInterface'); $entity->expects($this->exactly(2)) ->method('getEntityTypeId') ->will($this->returnValue('node'));
// Add the test node to our context as the evaluated entity. // First, test with a value that should evaluate TRUE. $this->condition->setContextValue('entity', $entity) ->setContextValue('type', 'node'); $this->assertTrue($this->condition->evaluate());
// Then test with values that should evaluate FALSE. $this->condition->setContextValue('type', 'taxonomy_term'); $this->assertFalse($this->condition->evaluate()); }}
class EntityIsOfTypeTest extends RulesEntityIntegrationTestBase {
/** * Tests evaluating the condition. * * @covers ::evaluate() */ public function testConditionEvaluation() { $entity = $this->getMock('Drupal\Core\Entity\EntityInterface'); $entity->expects($this->exactly(2)) ->method('getEntityTypeId') ->will($this->returnValue('node'));
// Add the test node to our context as the evaluated entity. // First, test with a value that should evaluate TRUE. $this->condition->setContextValue('entity', $entity) ->setContextValue('type', 'node'); $this->assertTrue($this->condition->evaluate());
// Then test with values that should evaluate FALSE. $this->condition->setContextValue('type', 'taxonomy_term'); $this->assertFalse($this->condition->evaluate()); }}
class EntityIsOfTypeTest extends RulesEntityIntegrationTestBase {
/** * Tests evaluating the condition. * * @covers ::evaluate() */ public function testConditionEvaluation() { $entity = $this->getMock('Drupal\Core\Entity\EntityInterface'); $entity->expects($this->exactly(2)) ->method('getEntityTypeId') ->will($this->returnValue('node'));
// Add the test node to our context as the evaluated entity. // First, test with a value that should evaluate TRUE. $this->condition->setContextValue('entity', $entity) ->setContextValue('type', 'node'); $this->assertTrue($this->condition->evaluate());
// Then test with values that should evaluate FALSE. $this->condition->setContextValue('type', 'taxonomy_term'); $this->assertFalse($this->condition->evaluate()); }}
class EntityIsOfTypeTest extends RulesEntityIntegrationTestBase {
/** * Tests evaluating the condition. * * @covers ::evaluate() */ public function testConditionEvaluation() { $entity = $this->getMock('Drupal\Core\Entity\EntityInterface'); $entity->expects($this->exactly(2)) ->method('getEntityTypeId') ->will($this->returnValue('node'));
// Add the test node to our context as the evaluated entity. // First, test with a value that should evaluate TRUE. $this->condition->setContextValue('entity', $entity) ->setContextValue('type', 'node'); $this->assertTrue($this->condition->evaluate());
// Then test with values that should evaluate FALSE. $this->condition->setContextValue('type', 'taxonomy_term'); $this->assertFalse($this->condition->evaluate()); }}
trait StringTranslationTrait {
/** * The string translation service. * * @var \Drupal\Core\StringTranslation\TranslationInterface */ protected $stringTranslation;
/** * Translates a string to the current language or to a given language. * * See the t() documentation for details. */ protected function t($string, …) { return $this->getStringTranslation()->translate($string, …); }
}
class MyClass {
use StringTranslationTrait;
public function __construct( TranslationInterface $string_translation) { $this->stringTranslation = $string_translation; }
/** * Does something. */ public function doSth() { // ... $string = $this->t('Something'); // ... }
}
Traits
• RulesContextTrait• in addition to ContextAwarePluginBase• used in RulesActionBase and RulesConditionBase
Sprint with us!
• Port actions & conditions• https://www.drupal.org/node/2245015
Sprint with us!
• Port actions & conditions• https://www.drupal.org/node/2245015
https://www.youtube.com/watch?v=gEH291mq48Y
Rules Transformers
• https://www.drupal.org/node/2251267