diff --git a/.travis.yml b/.travis.yml index 294823f742ccf1a95bcf32a4ad6081cb7ddb754f..26918036c9a7cf2d98c1cda4a211ef32b4c9767d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -15,6 +15,10 @@ env: matrix: allow_failures: + # There are weird segmentation faults in the UI with PHP 5.6.5 (which travis + # uses). As everything works with other versions and 5.6.18 we allow 5.6 to + # fail for now. See https://www.drupal.org/node/2659028. + - php: 5.6 # We cannot use hhvm-nightly since that does not work in Travis CI's old # Ubuntu 12.04. #- php: hhvm diff --git a/phpcs.xml.dist b/phpcs.xml.dist index 93f5e611f63d5e5db60834a372e3762b12de9666..a99a187f760b30d4a6e5a2b358509bca839a4686 100644 --- a/phpcs.xml.dist +++ b/phpcs.xml.dist @@ -3,9 +3,6 @@ <description>Default PHP CodeSniffer configuration for Rules.</description> <file>.</file> - <!-- Exclude generated code files. --> - <exclude-pattern>src/ProxyClass/ParamConverter/RulesTempConverter.php</exclude-pattern> - <arg name="extensions" value="inc,install,module,php,profile,test,theme,yml"/> <rule ref="Drupal.NamingConventions.ValidVariableName.LowerCamelName"> diff --git a/rules.routing.yml b/rules.routing.yml index 069a5e1664d5069707bae67cec76302098912f46..5fd854c09447ceacd76ad0943b295d7602cd7c4f 100755 --- a/rules.routing.yml +++ b/rules.routing.yml @@ -25,6 +25,8 @@ entity.rules_reaction_rule.add_form: defaults: _entity_form: 'rules_reaction_rule.add' _title: 'Add reaction rule' + options: + _rules_ui: rules.reaction_rules requirements: _permission: 'administer rules+administer rules reactions' @@ -38,6 +40,7 @@ entity.rules_reaction_rule.edit_form: rules_reaction_rule: tempstore: TRUE type: entity:rules_reaction_rule + _rules_ui: rules.reaction_rules requirements: _permission: 'administer rules+administer rules reactions' @@ -45,14 +48,8 @@ entity.rules_reaction_rule.delete_form: path: '/admin/config/workflow/rules/reactions/delete/{rules_reaction_rule}' defaults: _entity_form: 'rules_reaction_rule.delete' - requirements: - _permission: 'administer rules+administer rules reactions' - -entity.rules_reaction_rule.break_lock_form: - path: '/admin/config/workflow/rules/reactions/break-lock/{rules_reaction_rule}' - defaults: - _entity_form: 'rules_reaction_rule.break_lock' - _title: 'Break lock' + options: + _rules_ui: rules.reaction_rules requirements: _permission: 'administer rules+administer rules reactions' diff --git a/rules.rules_ui.yml b/rules.rules_ui.yml index eb16c4a9f46ad227adcac98286ac83617d173d55..ffcdfb509e88f01a7e7709289b48fb2b690e1bc3 100644 --- a/rules.rules_ui.yml +++ b/rules.rules_ui.yml @@ -1,3 +1,6 @@ rules.reaction_rules: label: 'Manage reaction rules.' base_route: entity.rules_reaction_rule.edit_form + component_type_label: rule + settings: + config_parameter: rules_reaction_rule diff --git a/rules.services.yml b/rules.services.yml index a57b4b3b9697e02ad5ae8b848ede07ec51f46f17..053638c9034c783986d1e2dcb6ef6b6e20a2a17d 100644 --- a/rules.services.yml +++ b/rules.services.yml @@ -34,8 +34,13 @@ services: class: Drupal\rules\EventSubscriber\RedirectEventSubscriber tags: - { name: 'event_subscriber' } + rules.ui_route_enhancer: + class: Drupal\rules\Routing\RulesUiRouteEnhancer + arguments: ['@plugin.manager.rules_ui'] + tags: + - { name: route_enhancer } rules.ui_route_subscriber: - class: Drupal\rules\Routing\UiRouteSubscriber + class: Drupal\rules\Routing\RulesUiRouteSubscriber arguments: ['@plugin.manager.rules_ui'] tags: - { name: 'event_subscriber' } @@ -44,9 +49,3 @@ services: typed_data.placeholder_resolver: class: Drupal\rules\TypedData\PlaceholderResolver arguments: ['@typed_data.data_fetcher', '@plugin.manager.typed_data_filter'] - paramconverter.rules: - class: Drupal\rules\ParamConverter\RulesTempConverter - arguments: ['@entity.manager', '@user.shared_tempstore'] - tags: - - { name: paramconverter, priority: 10 } - lazy: true diff --git a/src/Core/RulesUiConfigHandler.php b/src/Core/RulesUiConfigHandler.php new file mode 100644 index 0000000000000000000000000000000000000000..6d50f2661892d712a24cb9ac9471eb97c4e215b4 --- /dev/null +++ b/src/Core/RulesUiConfigHandler.php @@ -0,0 +1,117 @@ +<?php + +/** + * @file + * Contains Drupal\rules\Core\RulesUiDefaultHandler. + */ + +namespace Drupal\rules\Core; + +use Drupal\Core\Entity\EntityInterface; +use Drupal\Core\Plugin\ContainerFactoryPluginInterface; +use Drupal\Core\Plugin\PluginBase; +use Drupal\Core\Routing\RouteMatchInterface; +use Drupal\Core\Url; +use Drupal\rules\Engine\RulesComponent; +use Drupal\rules\Form\TempStoreTrait; +use Symfony\Component\DependencyInjection\ContainerInterface; + +/** + * The default handler for RulesUi plugins that store to config. + * + * It follows a list of supported settings. Note that settings that are not + * marked as optional are required. + * - config_parameter: The name of the routing parameter holding the config. + * + * @see RulesUiDefinition::settings + */ +class RulesUiConfigHandler extends PluginBase implements RulesUiHandlerInterface, ContainerFactoryPluginInterface { + + use TempStoreTrait; + + /** + * The rules UI (plugin) definition. + * + * @var \Drupal\rules\Core\RulesUiDefinition + */ + protected $pluginDefinition; + + /** + * The current route match. + * + * @var \Drupal\Core\Routing\RouteMatchInterface + */ + protected $currentRouteMatch; + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) { + return new static($configuration, $plugin_id, $plugin_definition, $container->get('current_route_match')); + } + + /** + * {@inheritdoc} + * + * @param \Drupal\Core\Routing\RouteMatchInterface $route_match + * The current route match. + */ + public function __construct(array $configuration, $plugin_id, $plugin_definition, RouteMatchInterface $route_match) { + parent::__construct($configuration, $plugin_id, $plugin_definition); + $this->currentRouteMatch = $route_match; + } + + /** + * Gets the edited config object. + * + * @return \Drupal\Core\Config\Entity\ConfigEntityBase|\Drupal\Core\Config\Config + * The config entity or config object. + */ + public function getConfig() { + $config = $this->fetchFromTempStore(); + if (!$config) { + $config = $this->currentRouteMatch->getParameter($this->pluginDefinition->settings['config_parameter']); + } + return $config; + } + + /** + * {@inheritdoc} + */ + public function getComponentLabel() { + if (isset($this->pluginDefinition->component_label)) { + return $this->pluginDefinition->component_label; + } + elseif ($this->getConfig() instanceof EntityInterface) { + return $this->getConfig()->label(); + } + else { + return $this->pluginDefinition->component_type_label; + } + } + + /** + * {@inheritdoc} + */ + public function getComponent() { + return $this->getConfig()->getComponent(); + } + + /** + * {@inheritdoc} + */ + public function updateComponent(RulesComponent $component) { + $config = $this->getConfig(); + $config->updateFromComponent($component); + $this->storeToTempStore($config); + } + + /** + * {@inheritdoc} + */ + public function getBaseRouteUrl() { + // See Url::fromRouteMatch() + return Url::fromRoute($this->pluginDefinition->base_route, $this->currentRouteMatch->getRawParameters()->all()); + } + +} diff --git a/src/Core/RulesUiDefaultHandler.php b/src/Core/RulesUiDefaultHandler.php deleted file mode 100644 index 5b170ff36b6894173177bcbe3b7ca322d0aa3be5..0000000000000000000000000000000000000000 --- a/src/Core/RulesUiDefaultHandler.php +++ /dev/null @@ -1,70 +0,0 @@ -<?php - -/** - * @file - * Contains Drupal\rules\Core\RulesUiDefaultHandler. - */ - -namespace Drupal\rules\Core; - -use Drupal\Core\Plugin\PluginBase; -use Symfony\Component\Routing\Route; -use Symfony\Component\Routing\RouteCollection; - -/** - * The default handler for RulesUi plugins. - * - * @todo: Complete implementation. - */ -class RulesUiDefaultHandler extends PluginBase implements RulesUiHandlerInterface { - - /** - * The rules UI (plugin) definition. - * - * @var \Drupal\rules\Core\RulesUiDefinition - */ - protected $pluginDefinition; - - /** - * {@inheritdoc} - */ - public function registerRoutes(RouteCollection $collection) { - $base_route = $collection->get($this->pluginDefinition->base_route); - - $options = [ - 'parameters' => ($base_route->getOption('parameters') ?: []), - '_admin_route' => $base_route->getOption('_admin_route') ?: FALSE, - ]; - $requirements = [ - '_permission' => $this->pluginDefinition->permissions ?: $base_route->getRequirement('_permission'), - ]; - - $route = (new Route($base_route->getPath() . '/add/{expression_id}')) - ->addDefaults([ - '_form' => '\Drupal\rules\Form\AddExpressionForm', - '_title_callback' => '\Drupal\rules\Form\AddExpressionForm::getTitle', - ]) - ->addOptions($options) - ->addRequirements($requirements); - $collection->add($this->pluginDefinition->base_route . '.expression.add', $route); - - $route = (new Route($base_route->getPath() . '/edit/{uuid}')) - ->addDefaults([ - '_form' => '\Drupal\rules\Form\EditExpressionForm', - '_title_callback' => '\Drupal\rules\Form\EditExpressionForm::getTitle', - ]) - ->addOptions($options) - ->addRequirements($requirements); - $collection->add($this->pluginDefinition->base_route . '.expression.edit', $route); - - $route = (new Route($base_route->getPath() . '/delete/{uuid}')) - ->addDefaults([ - '_form' => '\Drupal\rules\Form\DeleteExpressionForm', - '_title' => 'Delete expression', - ]) - ->addOptions($options) - ->addRequirements($requirements); - $collection->add($this->pluginDefinition->base_route . '.expression.delete', $route); - } - -} diff --git a/src/Core/RulesUiDefinition.php b/src/Core/RulesUiDefinition.php index 90b1aa5a148e80c33813744bfd5c8549ce4a62a2..8c339b6e9ebcf79ca5c90a0cf6ab4c605bea78ab 100644 --- a/src/Core/RulesUiDefinition.php +++ b/src/Core/RulesUiDefinition.php @@ -54,7 +54,16 @@ class RulesUiDefinition implements PluginDefinitionInterface { * * @var string */ - public $class = RulesUiDefaultHandler::class; + public $class = RulesUiConfigHandler::class; + + /** + * Array of handler-specific settings. + * + * Check the documentation of the ui handler for further details. + * + * @var array + */ + public $settings = []; /** * The plugin provider; e.g., the module. @@ -81,6 +90,28 @@ class RulesUiDefinition implements PluginDefinitionInterface { */ public $permissions; + /** + * The label used for referring to the component (optional). + * + * If omitted, a handler-specific fallback logic is applied. For example, + * the RulesUiConfigHandler assumes a config entity and uses its label() + * method. + * + * @ingroup plugin_translatable + * + * @var string|null + */ + public $component_label; + + /** + * The label used for referring to the component type. + * + * @ingroup plugin_translatable + * + * @var string|null + */ + public $component_type_label = 'component'; + /** * {@inheritdoc} */ diff --git a/src/Core/RulesUiHandlerInterface.php b/src/Core/RulesUiHandlerInterface.php index 1b04e096071977f3560f8a4a7fb25d240d44422a..37f44d6aaffab4f166df473621754b61b4471238 100644 --- a/src/Core/RulesUiHandlerInterface.php +++ b/src/Core/RulesUiHandlerInterface.php @@ -7,24 +7,109 @@ namespace Drupal\rules\Core; -use Symfony\Component\Routing\RouteCollection; +use Drupal\Component\Plugin\PluginInspectionInterface; +use Drupal\Core\Form\FormStateInterface; +use Drupal\rules\Engine\RulesComponent; /** * Interface for Rules UI handlers. * * Rules UI handlers define where RulesUI instances are embedded and are * responsible for generating the appropriate routes. - * - * @todo: Implement. */ -interface RulesUiHandlerInterface { +interface RulesUiHandlerInterface extends PluginInspectionInterface { + + /** + * {@inheritdoc} + * + * @return \Drupal\rules\Core\RulesUiDefinition + * The rules_ui plugin definition. + */ + public function getPluginDefinition(); + + /** + * Gets the human-readable label of the component. + * + * The human-readable label used when referring to the whole component. This + * can be a fixed string, or the label of a config entity. + * + * @return string + * The label. + */ + public function getComponentLabel(); + + /** + * Gets the currently edited component. + * + * @return \Drupal\rules\Engine\RulesComponent + * The edited component. + */ + public function getComponent(); + + /** + * Updates the edited component. + * + * @param \Drupal\rules\Engine\RulesComponent $component + * The updated, edited component. + */ + public function updateComponent(RulesComponent $component); + + /** + * Clears any temporary storage. + * + * Note that after clearing the temporary storage any unsaved changes are + * lost. + */ + public function clearTemporaryStorage(); + + /** + * Returns the URL of the base route, based on the current URL. + * + * @return \Drupal\Core\Url + * The url of the base route. + */ + public function getBaseRouteUrl(); + + /** + * Determines if the component is locked for the current user. + * + * @return bool + * TRUE if locked, FALSE otherwise. + */ + public function isLocked(); + + /** + * Checks if the rule has been modified and is present in the storage. + * + * @return bool + * TRUE if the rule has been modified, FALSE otherwise. + */ + public function isEdited(); + + /** + * Provides information which user at which time locked the rule for editing. + * + * @return object + * StdClass object as provided by \Drupal\user\SharedTempStore. + */ + public function getLockMetaData(); + + /** + * Renders a message if the rule component is locked/modified. + * + * @return array + * The render array, showing the message when applicable. + */ + public function addLockInformation(); /** - * Registers the routes as need for the UI. + * Validation callback that prevents editing locked rule components. * - * @param \Symfony\Component\Routing\RouteCollection $collection - * The route collection to which to add the routes. + * @param array $form + * An associative array containing the structure of the form. + * @param \Drupal\Core\Form\FormStateInterface $form_state + * The current state of the form. */ - public function registerRoutes(RouteCollection $collection); + public function validateLock(array &$form, FormStateInterface $form_state); } diff --git a/src/Core/RulesUiManagerInterface.php b/src/Core/RulesUiManagerInterface.php index ad3b12c96887db58b10e532e9dcedf14d41370ec..53e590c27544828a728fa2002df0b8870425bda7 100644 --- a/src/Core/RulesUiManagerInterface.php +++ b/src/Core/RulesUiManagerInterface.php @@ -2,7 +2,7 @@ /** * @file - * Contains Drupal\rules\Core\RulesUiManagerInterface. + * Contains \Drupal\rules\Core\RulesUiManagerInterface. */ namespace Drupal\rules\Core; diff --git a/src/Engine/ActionExpressionContainer.php b/src/Engine/ActionExpressionContainer.php index 08493b3adec8e14fc4b04f0f5d10e4f74bdf26db..c2677d9005b2c81c591d15232675873cbb52fbc3 100644 --- a/src/Engine/ActionExpressionContainer.php +++ b/src/Engine/ActionExpressionContainer.php @@ -114,6 +114,16 @@ abstract class ActionExpressionContainer extends ExpressionBase implements Actio return new \ArrayIterator($this->actions); } + /** + * PHP magic __clone function. + */ + public function __clone() { + // Implement a deep clone. + foreach ($this->actions as &$action) { + $action = clone $action; + } + } + /** * {@inheritdoc} */ diff --git a/src/Engine/ConditionExpressionContainer.php b/src/Engine/ConditionExpressionContainer.php index e53a0fe4dfe6b53d8b625f449220e8a109df27fb..104521873868d23862de06ef94f8d527fda969ce 100644 --- a/src/Engine/ConditionExpressionContainer.php +++ b/src/Engine/ConditionExpressionContainer.php @@ -142,6 +142,16 @@ abstract class ConditionExpressionContainer extends ExpressionBase implements Co return new \ArrayIterator($this->conditions); } + /** + * PHP magic __clone function. + */ + public function __clone() { + // Implement a deep clone. + foreach ($this->conditions as &$condition) { + $condition = clone $condition; + } + } + /** * {@inheritdoc} */ diff --git a/src/Engine/ExpressionBase.php b/src/Engine/ExpressionBase.php index bac649c66c35209c99c089961075cb4de44b404c..a12cc10ac30c85be5c7cd58763551e97011e3a34 100644 --- a/src/Engine/ExpressionBase.php +++ b/src/Engine/ExpressionBase.php @@ -119,6 +119,7 @@ abstract class ExpressionBase extends PluginBase implements ExpressionInterface */ public function getRoot() { if (isset($this->root)) { + // @todo: This seems to be the parent, not root. return $this->root->getRoot(); } return $this; diff --git a/src/Engine/ExpressionInterface.php b/src/Engine/ExpressionInterface.php index ebc32b97ebdae5aa2de30de0f1772f65ec6cdc3b..98cde6bd777b7da728091df9ace3dc95a8b89c4c 100644 --- a/src/Engine/ExpressionInterface.php +++ b/src/Engine/ExpressionInterface.php @@ -8,6 +8,7 @@ namespace Drupal\rules\Engine; use Drupal\Component\Plugin\ConfigurablePluginInterface; +use Drupal\Component\Plugin\PluginInspectionInterface; use Drupal\Core\Executable\ExecutableInterface; /** @@ -15,7 +16,7 @@ use Drupal\Core\Executable\ExecutableInterface; * * @see \Drupal\rules\Engine\ExpressionManager */ -interface ExpressionInterface extends ExecutableInterface, ConfigurablePluginInterface { +interface ExpressionInterface extends ExecutableInterface, ConfigurablePluginInterface, PluginInspectionInterface { /** * Execute the expression with a given Rules state. diff --git a/src/Engine/ExpressionManagerInterface.php b/src/Engine/ExpressionManagerInterface.php index 7b7680bba83df171dc091d6669845ddaf708854e..cc7fb7ae320fa4d0715d68b34e06da75936f193d 100644 --- a/src/Engine/ExpressionManagerInterface.php +++ b/src/Engine/ExpressionManagerInterface.php @@ -14,6 +14,14 @@ use Drupal\Component\Plugin\PluginManagerInterface; */ interface ExpressionManagerInterface extends PluginManagerInterface { + /** + * {@inheritdoc} + * + * @return \Drupal\rules\Engine\ExpressionInterface + * A fully configured plugin instance. + */ + public function createInstance($plugin_id, array $configuration = []); + /** * Creates a new rule. * diff --git a/src/Engine/RulesComponent.php b/src/Engine/RulesComponent.php index 2788ab5990cc74819262edd9f3bed487c87954d2..b08bd0a94985f3aef3190c6c19cd964e02ff6839 100644 --- a/src/Engine/RulesComponent.php +++ b/src/Engine/RulesComponent.php @@ -7,9 +7,7 @@ namespace Drupal\rules\Engine; -use Drupal\Core\Config\Entity\ConfigEntityInterface; use Drupal\rules\Context\ContextDefinitionInterface; -use Drupal\rules\Entity\ReactionRuleConfig; /** * Handles executable Rules components. @@ -113,19 +111,16 @@ class RulesComponent { } /** - * Adds the configured context definitions from the config entity. + * Adds the available event context for the given events. * - * Example: for a reaction rule config all context definitions of the event - * will be added. - * - * @param \Drupal\Core\Config\Entity\ConfigEntityInterface $rules_config - * The config entity. + * @param string[] $event_names + * The event names; e.g., as configured for a reaction rule. * * @return $this */ - public function addContextDefinitionsFrom(ConfigEntityInterface $rules_config) { - if ($rules_config instanceof ReactionRuleConfig) { - $event_name = $rules_config->getEvent(); + public function addContextDefinitionsForEvents(array $event_names) { + foreach ($event_names as $event_name) { + // @todo: Correctly handle multiple events to intersect available context. // @todo Use setter injection for the service. $event_definition = \Drupal::service('plugin.manager.rules_event')->getDefinition($event_name); foreach ($event_definition['context'] as $context_name => $context_definition) { @@ -250,4 +245,13 @@ class RulesComponent { return ExecutionMetadataState::create($data_definitions); } + /** + * PHP magic __clone function. + */ + public function __clone() { + // Implement a deep clone. + $this->state = clone $this->state; + $this->expression = clone $this->expression; + } + } diff --git a/src/Entity/ReactionRuleConfig.php b/src/Entity/ReactionRuleConfig.php index d86c8a0a77b11b9793da4010185be1948cf0973a..b811ad79e480639caf61a71f16d2475d6ce86dc7 100644 --- a/src/Entity/ReactionRuleConfig.php +++ b/src/Entity/ReactionRuleConfig.php @@ -9,6 +9,7 @@ namespace Drupal\rules\Entity; use Drupal\Core\Config\Entity\ConfigEntityBase; use Drupal\rules\Engine\ExpressionInterface; +use Drupal\rules\Engine\RulesComponent; /** * Reaction rule configuration entity to persistently store configuration. @@ -22,8 +23,7 @@ use Drupal\rules\Engine\ExpressionInterface; * "form" = { * "add" = "\Drupal\rules\Form\ReactionRuleAddForm", * "edit" = "\Drupal\rules\Form\ReactionRuleEditForm", - * "delete" = "\Drupal\Core\Entity\EntityDeleteForm", - * "break_lock" = "\Drupal\rules\Form\BreakLockForm" + * "delete" = "\Drupal\Core\Entity\EntityDeleteForm" * } * }, * admin_permission = "administer rules", @@ -48,7 +48,7 @@ use Drupal\rules\Engine\ExpressionInterface; * "collection" = "/admin/config/workflow/rules", * "edit-form" = "/admin/config/workflow/rules/reactions/edit/{rules_reaction_rule}", * "delete-form" = "/admin/config/workflow/rules/reactions/delete/{rules_reaction_rule}", - * "break-lock-form" = "/admin/config/workflow/rules/reactions/break-lock/{rules_reaction_rule}" + * "break-lock-form" = "/admin/config/workflow/rules/reactions/edit/break-lock/{rules_reaction_rule}" * } * ) */ @@ -142,7 +142,6 @@ class ReactionRuleConfig extends ConfigEntityBase { return $this; } - /** * Gets a Rules expression instance for this Reaction rule. * @@ -159,6 +158,36 @@ class ReactionRuleConfig extends ConfigEntityBase { return $this->expression; } + /** + * Gets the Rules component that is invoked when the events are dispatched. + * + * The returned component has the definitions of the available event context + * set. + * + * @return \Drupal\rules\Engine\RulesComponent + * The Rules component. + */ + public function getComponent() { + $component = RulesComponent::create($this->getExpression()); + $component->addContextDefinitionsForEvents([$this->getEvent()]); + return $component; + } + + /** + * Updates the configuration based upon the given component. + * + * @param \Drupal\rules\Engine\RulesComponent $component + * The component containing the configuration to set. + * + * @return $this + */ + public function updateFromComponent(RulesComponent $component) { + // Note that the available context definitions stem from the configured + // events, which are handled separately. + $this->setExpression($component->getExpression()); + return $this; + } + /** * Returns the Rules expression manager. * diff --git a/src/Entity/RulesComponentConfig.php b/src/Entity/RulesComponentConfig.php index 8057d12bc22685641305ed35c7fc988828a4dac0..f8a8255a069e28189769da3ca2d1f458d4225393 100644 --- a/src/Entity/RulesComponentConfig.php +++ b/src/Entity/RulesComponentConfig.php @@ -10,6 +10,7 @@ namespace Drupal\rules\Entity; use Drupal\Core\Config\Entity\ConfigEntityBase; use Drupal\rules\Context\ContextDefinition; use Drupal\rules\Engine\ExpressionInterface; +use Drupal\rules\Engine\RulesComponent; /** * Rules component configuration entity to persistently store configuration. @@ -170,7 +171,7 @@ class RulesComponentConfig extends ConfigEntityBase { * The component. */ public function getComponent() { - $component = \Drupal\rules\Engine\RulesComponent::create($this->getExpression()); + $component = RulesComponent::create($this->getExpression()); foreach ($this->context_definitions as $name => $definition) { $component->addContextDefinition($name, ContextDefinition::createFromArray($definition)); } @@ -188,7 +189,7 @@ class RulesComponentConfig extends ConfigEntityBase { * * @return $this */ - public function setComponent(\Drupal\rules\Engine\RulesComponent $component) { + public function setComponent(RulesComponent $component) { $this->setExpression($component->getExpression()); $this->setContextDefinitions($component->getContextDefinitions()); return $this; diff --git a/src/Form/AddExpressionForm.php b/src/Form/AddExpressionForm.php index d6b43c6a21fb2beb22bccfa2f58d77f268cb4d38..112233c5c65c54c93c6d0b47052b74b6c4ad3f82 100644 --- a/src/Form/AddExpressionForm.php +++ b/src/Form/AddExpressionForm.php @@ -7,21 +7,17 @@ namespace Drupal\rules\Form; -use Drupal\Core\Form\FormBase; use Drupal\Core\Form\FormStateInterface; +use Drupal\rules\Core\RulesUiHandlerInterface; +use Drupal\rules\Engine\ExpressionContainerInterface; use Drupal\rules\Engine\ExpressionManagerInterface; use Drupal\rules\Engine\RulesComponent; -use Drupal\rules\Entity\ReactionRuleConfig; use Symfony\Component\DependencyInjection\ContainerInterface; /** * UI form to add an expression like a condition or action to a rule. */ -class AddExpressionForm extends FormBase { - - use TempStoreTrait { - validateForm as lockValidateForm; - } +class AddExpressionForm extends EditExpressionForm { /** * The Rules expression manager to get expression plugins. @@ -30,13 +26,6 @@ class AddExpressionForm extends FormBase { */ protected $expressionManager; - /** - * The reaction rule config the expression is added to. - * - * @var \Drupal\rules\Entity\ReactionRuleConfig - */ - protected $ruleConfig; - /** * The expression ID that is added, example: 'rules_action'. * @@ -61,44 +50,41 @@ class AddExpressionForm extends FormBase { /** * {@inheritdoc} */ - public function buildForm(array $form, FormStateInterface $form_state, ReactionRuleConfig $rules_reaction_rule = NULL, $expression_id = NULL) { - $this->ruleConfig = $rules_reaction_rule; + public function buildForm(array $form, FormStateInterface $form_state, RulesUiHandlerInterface $rules_ui_handler = NULL, $uuid = NULL, $expression_id = NULL) { $this->expressionId = $expression_id; + $this->uuid = $uuid; + + // When initially adding the expression, we have to initialize the object + // and add the expression. + if (!$this->uuid) { + // Before we add our edited expression to the component's expression, + // we clone it such that we do not change the source component until + // the form has been successfully submitted. + $component = clone $rules_ui_handler->getComponent(); + $this->uuid = $this->getEditedExpression($component)->getUuid(); + $form_state->set('component', $component); + $form_state->set('uuid', $this->uuid); + } - $expression = $this->expressionManager->createInstance($expression_id); - $form_handler = $expression->getFormHandler(); - $form = $form_handler->form($form, $form_state); - return $form; - } - - /** - * {@inheritdoc} - */ - public function getFormId() { - return 'rules_expression_add'; + return parent::buildForm($form, $form_state, $rules_ui_handler, $this->uuid); } /** * {@inheritdoc} */ - public function validateForm(array &$form, FormStateInterface $form_state) { - $this->lockValidateForm($form, $form_state); - - $expression = $this->expressionManager->createInstance($this->expressionId); - $form_handler = $expression->getFormHandler(); - $form_handler->validateForm($form, $form_state); - - $validation_config = clone $this->ruleConfig; - $rule_expression = $validation_config->getExpression(); - $rule_expression->addExpressionObject($expression); - - $all_violations = RulesComponent::create($rule_expression) - ->addContextDefinitionsFrom($validation_config) - ->checkIntegrity(); - $local_violations = $all_violations->getFor($expression->getUuid()); - - foreach ($local_violations as $violation) { - $form_state->setError($form, $violation->getMessage()); + protected function getEditedExpression(RulesComponent $component) { + $component_expression = $component->getExpression(); + if (!$component_expression instanceof ExpressionContainerInterface) { + throw new \LogicException('Cannot add expression to expression of type ' . $component_expression->getPluginId()); + } + if ($this->uuid && $expression = $component_expression->getExpression($this->uuid)) { + return $expression; + } + else { + $expression = $this->expressionManager->createInstance($this->expressionId); + $rule_expression = $component->getExpression(); + $rule_expression->addExpressionObject($expression); + return $expression; } } @@ -106,28 +92,16 @@ class AddExpressionForm extends FormBase { * {@inheritdoc} */ public function submitForm(array &$form, FormStateInterface $form_state) { - $expression = $this->expressionManager->createInstance($this->expressionId); - $form_handler = $expression->getFormHandler(); - $form_handler->submitForm($form, $form_state); - - $rule_expression = $this->ruleConfig->getExpression(); - $rule_expression->addExpressionObject($expression); - // Set the expression again so that the config is copied over to the - // config entity. - $this->ruleConfig->setExpression($rule_expression); - - $this->saveToTempStore(); - - $form_state->setRedirect('entity.rules_reaction_rule.edit_form', [ - 'rules_reaction_rule' => $this->ruleConfig->id(), - ]); + parent::submitForm($form, $form_state); + $form_state->setRedirect('entity.rules_reaction_rule.edit_form', $this->getRouteMatch()->getRawParameters()->all()); } /** * Provides the page title on the form. */ - public function getTitle(ReactionRuleConfig $rules_reaction_rule, $expression_id) { - $expression = $this->expressionManager->createInstance($expression_id); + public function getTitle(RulesUiHandlerInterface $rules_ui_handler, $expression_id) { + $this->expressionId = $expression_id; + $expression = $this->expressionManager->createInstance($this->expressionId); return $this->t('Add @expression', ['@expression' => $expression->getLabel()]); } diff --git a/src/Form/BreakLockForm.php b/src/Form/BreakLockForm.php index 79db983c6f92a48c03194e3c7cfe5986bd41ccf2..559ec0e9e4d0020ade5daf61bf25977d87e1abe2 100644 --- a/src/Form/BreakLockForm.php +++ b/src/Form/BreakLockForm.php @@ -7,17 +7,17 @@ namespace Drupal\rules\Form; -use Drupal\Core\Entity\EntityConfirmFormBase; use Drupal\Core\Entity\EntityTypeManagerInterface; +use Drupal\Core\Form\ConfirmFormBase; use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Render\RendererInterface; -use Drupal\user\SharedTempStoreFactory; +use Drupal\rules\Core\RulesUiHandlerInterface; use Symfony\Component\DependencyInjection\ContainerInterface; /** * Builds the form to break the lock of an edited rule. */ -class BreakLockForm extends EntityConfirmFormBase { +class BreakLockForm extends ConfirmFormBase { /** * The entity type manager. @@ -27,25 +27,24 @@ class BreakLockForm extends EntityConfirmFormBase { protected $entityTypeManager; /** - * The temporary storage factory. + * The rendering service. * - * @var \Drupal\user\SharedTempStoreFactory + * @var \Drupal\Core\Render\RendererInterface */ - protected $tempStoreFactory; + protected $renderer; /** - * The rendering service. + * The RulesUI handler of the currently active UI. * - * @var \Drupal\Core\Render\RendererInterface + * @var \Drupal\rules\Core\RulesUiHandlerInterface */ - protected $renderer; + protected $rulesUiHandler; /** * Constructor. */ - public function __construct(EntityTypeManagerInterface $entity_type_manager, SharedTempStoreFactory $temp_store_factory, RendererInterface $renderer) { + public function __construct(EntityTypeManagerInterface $entity_type_manager, RendererInterface $renderer) { $this->entityTypeManager = $entity_type_manager; - $this->tempStoreFactory = $temp_store_factory; $this->renderer = $renderer; } @@ -55,7 +54,6 @@ class BreakLockForm extends EntityConfirmFormBase { public static function create(ContainerInterface $container) { return new static( $container->get('entity_type.manager'), - $container->get('user.shared_tempstore'), $container->get('renderer') ); } @@ -71,15 +69,14 @@ class BreakLockForm extends EntityConfirmFormBase { * {@inheritdoc} */ public function getQuestion() { - return $this->t('Do you want to break the lock on rule %name?', ['%name' => $this->entity->id()]); + return $this->t('Do you want to break the lock on %label?', ['%label' => $this->rulesUiHandler->getComponentLabel()]); } /** * {@inheritdoc} */ public function getDescription() { - $store = $this->tempStoreFactory->get($this->entity->getEntityTypeId()); - $locked = $store->getMetadata($this->entity->id()); + $locked = $this->rulesUiHandler->getLockMetaData(); $account = $this->entityTypeManager->getStorage('user')->load($locked->owner); $username = [ '#theme' => 'username', @@ -94,7 +91,7 @@ class BreakLockForm extends EntityConfirmFormBase { * {@inheritdoc} */ public function getCancelUrl() { - return $this->entity->urlInfo('edit-form'); + return $this->rulesUiHandler->getBaseRouteUrl(); } /** @@ -107,10 +104,10 @@ class BreakLockForm extends EntityConfirmFormBase { /** * {@inheritdoc} */ - public function buildForm(array $form, FormStateInterface $form_state) { - $store = $this->tempStoreFactory->get($this->entity->getEntityTypeId()); - if (!$store->getMetadata($this->entity->id())) { - $form['message']['#markup'] = $this->t('There is no lock on rule %name to break.', ['%name' => $this->entity->id()]); + public function buildForm(array $form, FormStateInterface $form_state, RulesUiHandlerInterface $rules_ui_handler = NULL) { + $this->rulesUiHandler = $rules_ui_handler; + if (!$rules_ui_handler->isLocked()) { + $form['message']['#markup'] = $this->t('There is no lock on %label to break.', ['%label' => $rules_ui_handler->getComponentLabel()]); return $form; } return parent::buildForm($form, $form_state); @@ -120,10 +117,11 @@ class BreakLockForm extends EntityConfirmFormBase { * {@inheritdoc} */ public function submitForm(array &$form, FormStateInterface $form_state) { - $store = $this->tempStoreFactory->get($this->entity->getEntityTypeId()); - $store->delete($this->entity->id()); - $form_state->setRedirectUrl($this->entity->urlInfo('edit-form')); - drupal_set_message($this->t('The lock has been broken and you may now edit this rule.')); + $this->rulesUiHandler->clearTemporaryStorage(); + $form_state->setRedirectUrl($this->rulesUiHandler->getBaseRouteUrl()); + drupal_set_message($this->t('The lock has been broken and you may now edit this @component_type.', [ + '@component_type' => $this->rulesUiHandler->getPluginDefinition()->component_type_label, + ])); } } diff --git a/src/Form/DeleteExpressionForm.php b/src/Form/DeleteExpressionForm.php index 53766b724269ad4c3a1ecf53cd699d800a6baf3d..7b02c49fb178d147ba704d71007f4db551575630 100644 --- a/src/Form/DeleteExpressionForm.php +++ b/src/Form/DeleteExpressionForm.php @@ -9,7 +9,7 @@ namespace Drupal\rules\Form; use Drupal\Core\Form\ConfirmFormBase; use Drupal\Core\Form\FormStateInterface; -use Drupal\rules\Entity\ReactionRuleConfig; +use Drupal\rules\Core\RulesUiHandlerInterface; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; /** @@ -17,21 +17,19 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; */ class DeleteExpressionForm extends ConfirmFormBase { - use TempStoreTrait; - /** - * The reaction rule config the expression is deleted from. + * The UUID of the expression in the rule. * - * @var \Drupal\rules\Entity\ReactionRuleConfig + * @var string */ - protected $ruleConfig; + protected $uuid; /** - * The UUID of the expression in the rule. + * The RulesUI handler of the currently active UI. * - * @var string + * @var \Drupal\rules\Core\RulesUiHandlerInterface */ - protected $uuid; + protected $rulesUiHandler; /** * {@inheritdoc} @@ -43,8 +41,8 @@ class DeleteExpressionForm extends ConfirmFormBase { /** * {@inheritdoc} */ - public function buildForm(array $form, FormStateInterface $form_state, ReactionRuleConfig $rules_reaction_rule = NULL, $uuid = NULL) { - $this->ruleConfig = $rules_reaction_rule; + public function buildForm(array $form, FormStateInterface $form_state, RulesUiHandlerInterface $rules_ui_handler = NULL, $uuid = NULL) { + $this->rulesUiHandler = $rules_ui_handler; $this->uuid = $uuid; return parent::buildForm($form, $form_state); } @@ -60,7 +58,7 @@ class DeleteExpressionForm extends ConfirmFormBase { * {@inheritdoc} */ public function getQuestion() { - $rule_expression = $this->ruleConfig->getExpression(); + $rule_expression = $this->rulesUiHandler->getComponent()->getExpression(); $expression_inside = $rule_expression->getExpression($this->uuid); if (!$expression_inside) { throw new NotFoundHttpException(); @@ -68,7 +66,7 @@ class DeleteExpressionForm extends ConfirmFormBase { return $this->t('Are you sure you want to delete %title from %rule?', [ '%title' => $expression_inside->getLabel(), - '%rule' => $this->ruleConfig->label(), + '%rule' => $this->rulesUiHandler->getComponentLabel(), ]); } @@ -76,21 +74,16 @@ class DeleteExpressionForm extends ConfirmFormBase { * {@inheritdoc} */ public function getCancelUrl() { - return $this->ruleConfig->urlInfo(); + return $this->rulesUiHandler->getBaseRouteUrl(); } /** * {@inheritdoc} */ public function submitForm(array &$form, FormStateInterface $form_state) { - $expression = $this->ruleConfig->getExpression(); - $expression->deleteExpression($this->uuid); - // Set the expression again so that the config is copied over to the - // config entity. - $this->ruleConfig->setExpression($expression); - - $this->saveToTempStore(); - + $component = $this->rulesUiHandler->getComponent(); + $component->getExpression()->deleteExpression($this->uuid); + $this->rulesUiHandler->updateComponent($component); $form_state->setRedirectUrl($this->getCancelUrl()); } diff --git a/src/Form/EditExpressionForm.php b/src/Form/EditExpressionForm.php index 6786e06776698fb6c3b56aac4ff425403252a75f..5f45a19684647d28bd7e6fc9fd78bbe49421cebf 100644 --- a/src/Form/EditExpressionForm.php +++ b/src/Form/EditExpressionForm.php @@ -9,8 +9,8 @@ namespace Drupal\rules\Form; use Drupal\Core\Form\FormBase; use Drupal\Core\Form\FormStateInterface; +use Drupal\rules\Core\RulesUiHandlerInterface; use Drupal\rules\Engine\RulesComponent; -use Drupal\rules\Entity\ReactionRuleConfig; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; /** @@ -18,37 +18,60 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; */ class EditExpressionForm extends FormBase { - use TempStoreTrait { - validateForm as lockValidateForm; - } + /** + * The edited component. + * + * @var \Drupal\rules\Engine\RulesComponent + */ + protected $component; /** - * The reaction rule config the expression is edited on. + * The RulesUI handler of the currently active UI. * - * @var \Drupal\rules\Entity\ReactionRuleConfig + * @var \Drupal\rules\Core\RulesUiHandlerInterface */ - protected $ruleConfig; + protected $rulesUiHandler; /** - * The UUID of the expression in the rule. + * The UUID of the edited expression in the rule. * * @var string */ protected $uuid; + /** + * Gets the currently edited expression from the given component. + * + * @param \Drupal\rules\Engine\RulesComponent $component + * The component from which to get the expression. + * + * @return \Drupal\rules\Engine\ExpressionInterface|null + * The expression object. + */ + protected function getEditedExpression(RulesComponent $component) { + $rule_expression = $component->getExpression(); + return $rule_expression->getExpression($this->uuid); + } + /** * {@inheritdoc} */ - public function buildForm(array $form, FormStateInterface $form_state, ReactionRuleConfig $rules_reaction_rule = NULL, $uuid = NULL) { - $this->ruleConfig = $rules_reaction_rule; - $this->uuid = $uuid; + public function buildForm(array $form, FormStateInterface $form_state, RulesUiHandlerInterface $rules_ui_handler = NULL, $uuid = NULL) { + $this->rulesUiHandler = $rules_ui_handler; + $this->component = is_object($form_state->get('component')) ? $form_state->get('component') : $this->rulesUiHandler->getComponent(); + $this->uuid = $form_state->get('uuid') ?: $uuid; + + // During form rebuilds, keep track of changes using form state. + $form_state->set('rules_ui_handler', $this->rulesUiHandler); + $form_state->set('component', $this->component); + $form_state->set('uuid', $this->uuid); + + $expression = $this->getEditedExpression($this->component, $form_state); - $rule_expression = $rules_reaction_rule->getExpression(); - $expression_inside = $rule_expression->getExpression($uuid); - if (!$expression_inside) { + if (!$expression) { throw new NotFoundHttpException(); } - $form_handler = $expression_inside->getFormHandler(); + $form_handler = $expression->getFormHandler(); $form = $form_handler->form($form, $form_state); return $form; } @@ -61,26 +84,47 @@ class EditExpressionForm extends FormBase { } /** - * {@inheritdoc} + * Builds an updated component object based upon the submitted form values. + * + * @param array $form + * An associative array containing the structure of the form. + * @param \Drupal\Core\Form\FormStateInterface $form_state + * The current state of the form. + * + * @return \Drupal\rules\Engine\RulesComponent + * The updated component. */ - public function validateForm(array &$form, FormStateInterface $form_state) { - $this->lockValidateForm($form, $form_state); - - // In order to validdate the whole rule we need to invoke the submission - // handler of the expression form. That way the expression is changed and we - // can validate the change for integrity afterwards. - $validation_config = clone $this->ruleConfig; - $rule_expression = $validation_config->getExpression(); - $expression = $rule_expression->getExpression($this->uuid); + protected function buildComponent(array $form, FormStateInterface $form_state) { + $component = clone $this->component; + + // In order to update the whole component we need to invoke the submission + // handler of the expression form. That way the expression gets changed + // accordingly. + $expression = $this->getEditedExpression($component); $form_handler = $expression->getFormHandler(); $form_handler->submitForm($form, $form_state); + return $component; + } + + /** + * {@inheritdoc} + */ + public function validateForm(array &$form, FormStateInterface $form_state) { + // Ensure the object properties are initialized, see + // https://www.drupal.org/node/2669032. + $this->rulesUiHandler = $form_state->get('rules_ui_handler'); + $this->component = is_object($form_state->get('component')) ? $form_state->get('component') : $this->rulesUiHandler->getComponent(); + $this->uuid = $form_state->get('uuid'); - $all_violations = RulesComponent::create($rule_expression) - ->addContextDefinitionsFrom($validation_config) - ->checkIntegrity(); - $local_violations = $all_violations->getFor($this->uuid); + $this->rulesUiHandler->validateLock($form, $form_state); - foreach ($local_violations as $violation) { + // @todo: This ignores ExpressionFormInterface::validateForm(). + + $component = $this->buildComponent($form, $form_state); + $violations = $component->checkIntegrity(); + + // Only show the violations caused by the edited expression. + foreach ($violations->getFor($this->uuid) as $violation) { $form_state->setError($form, $violation->getMessage()); } } @@ -89,29 +133,18 @@ class EditExpressionForm extends FormBase { * {@inheritdoc} */ public function submitForm(array &$form, FormStateInterface $form_state) { - $rule_expression = $this->ruleConfig->getExpression(); - $expression = $rule_expression->getExpression($this->uuid); - $form_handler = $expression->getFormHandler(); - $form_handler->submitForm($form, $form_state); - - // Set the expression again so that the config is copied over to the - // config entity. - $this->ruleConfig->setExpression($rule_expression); - - $this->saveToTempStore(); - - $form_state->setRedirect('entity.rules_reaction_rule.edit_form', [ - 'rules_reaction_rule' => $this->ruleConfig->id(), - ]); + $this->component = $this->buildComponent($form, $form_state); + $this->rulesUiHandler->updateComponent($this->component); + $form_state->setRedirectUrl($this->rulesUiHandler->getBaseRouteUrl()); } /** * Provides the page title on the form. */ - public function getTitle(ReactionRuleConfig $rules_reaction_rule, $uuid) { - $rule_expression = $rules_reaction_rule->getExpression(); - $expression_inside = $rule_expression->getExpression($uuid); - return $this->t('Edit @expression', ['@expression' => $expression_inside->getLabel()]); + public function getTitle(RulesUiHandlerInterface $rules_ui_handler, $uuid) { + $this->uuid = $uuid; + $expression = $this->getEditedExpression($rules_ui_handler->getComponent()); + return $this->t('Edit @expression', ['@expression' => $expression->getLabel()]); } } diff --git a/src/Form/Expression/ActionForm.php b/src/Form/Expression/ActionForm.php index ca37bb357af2370b769b883a8c503f723f2a5675..acc170c87bfa76264d7b0d3d1bd11754e8787177 100644 --- a/src/Form/Expression/ActionForm.php +++ b/src/Form/Expression/ActionForm.php @@ -47,14 +47,15 @@ class ActionForm implements ExpressionFormInterface { * {@inheritdoc} */ public function form(array $form, FormStateInterface $form_state) { - $action_name = $form_state->get('action'); + $action_id = $form_state->get('action_id'); $configuration = $this->actionExpression->getConfiguration(); - if (empty($action_name) && !empty($configuration['action_id'])) { - $action_name = $configuration['action_id']; + if (empty($action_id) && !empty($configuration['action_id'])) { + $action_id = $configuration['action_id']; + $form_state->set('action_id', $action_id); } // Step 1 of the multistep form. - if (!$action_name) { + if (!$action_id) { $action_definitions = $this->actionManager->getGroupedDefinitions(); $options = []; foreach ($action_definitions as $group => $definitions) { @@ -63,7 +64,7 @@ class ActionForm implements ExpressionFormInterface { } } - $form['action'] = [ + $form['action_id'] = [ '#type' => 'select', '#title' => $this->t('Action'), '#options' => $options, @@ -74,7 +75,7 @@ class ActionForm implements ExpressionFormInterface { '#value' => $this->t('Continue'), '#name' => 'continue', // Only validate the selected action in the first step. - '#limit_validation_errors' => [['action']], + '#limit_validation_errors' => [['action_id']], '#submit' => [static::class . '::submitFirstStep'], ]; @@ -82,15 +83,11 @@ class ActionForm implements ExpressionFormInterface { } // Step 2 of the form. - $action = $this->actionManager->createInstance($action_name); + $action = $this->actionManager->createInstance($action_id); $form['summary'] = [ '#markup' => $action->summary(), ]; - $form['action'] = [ - '#type' => 'value', - '#value' => $action_name, - ]; $context_definitions = $action->getContextDefinitions(); @@ -113,7 +110,7 @@ class ActionForm implements ExpressionFormInterface { */ public function validateForm(array $form, FormStateInterface $form_state) { // Only if there is an action selected already we can validate something. - if ($form_state->get('action')) { + if ($form_state->get('action_id')) { // Invoke the submission handler which will setup the expression being // edited in the form. That way the expression is ready for other // validation handlers. @@ -125,7 +122,7 @@ class ActionForm implements ExpressionFormInterface { * Submit callback: save the selected action in the first step. */ public static function submitFirstStep(array &$form, FormStateInterface $form_state) { - $form_state->set('action', $form_state->getValue('action')); + $form_state->set('action_id', $form_state->getValue('action_id')); $form_state->setRebuild(); } @@ -133,6 +130,10 @@ class ActionForm implements ExpressionFormInterface { * {@inheritdoc} */ public function submitForm(array &$form, FormStateInterface $form_state) { + // Nothing todo as long as the first step is not completed. + if (!$form_state->get('action_id')) { + return; + } $context_config = ContextConfig::create(); foreach ($form_state->getValue('context') as $context_name => $value) { if ($form_state->get("context_$context_name") == 'selector') { @@ -143,7 +144,7 @@ class ActionForm implements ExpressionFormInterface { } } $configuration = $context_config->toArray(); - $configuration['action_id'] = $form_state->getValue('action'); + $configuration['action_id'] = $form_state->get('action_id'); $this->actionExpression->setConfiguration($configuration); } diff --git a/src/Form/Expression/ConditionForm.php b/src/Form/Expression/ConditionForm.php index da0d9508a91b6ae2b7560861f3158d925914d632..d15cb0cbe484d7b890e25a5e264130b7e08f395a 100644 --- a/src/Form/Expression/ConditionForm.php +++ b/src/Form/Expression/ConditionForm.php @@ -12,7 +12,6 @@ use Drupal\Core\StringTranslation\StringTranslationTrait; use Drupal\rules\Core\ConditionManager; use Drupal\rules\Context\ContextConfig; use Drupal\rules\Engine\ConditionExpressionInterface; -use Drupal\rules\Form\Expression\ExpressionFormInterface; /** * UI form for adding/editing a Rules condition. @@ -48,14 +47,16 @@ class ConditionForm implements ExpressionFormInterface { * {@inheritdoc} */ public function form(array $form, FormStateInterface $form_state) { - $condition_name = $form_state->get('condition'); + $condition_id = $form_state->get('condition_id'); + $configuration = $this->conditionExpression->getConfiguration(); - if (empty($condition_name) && !empty($configuration['condition_id'])) { - $condition_name = $configuration['condition_id']; + if (empty($condition_id) && !empty($configuration['condition_id'])) { + $condition_id = $configuration['condition_id']; + $form_state->set('condition_id', $condition_id); } // Step 1 of the multistep form. - if (!$condition_name) { + if (!$condition_id) { $condition_definitions = $this->conditionManager->getGroupedDefinitions(); $options = []; foreach ($condition_definitions as $group => $definitions) { @@ -64,7 +65,7 @@ class ConditionForm implements ExpressionFormInterface { } } - $form['condition'] = [ + $form['condition_id'] = [ '#type' => 'select', '#title' => $this->t('Condition'), '#options' => $options, @@ -75,7 +76,7 @@ class ConditionForm implements ExpressionFormInterface { '#value' => $this->t('Continue'), '#name' => 'continue', // Only validate the selected condition in the first step. - '#limit_validation_errors' => [['condition']], + '#limit_validation_errors' => [['condition_id']], '#submit' => [static::class . '::submitFirstStep'], ]; @@ -84,15 +85,11 @@ class ConditionForm implements ExpressionFormInterface { // Step 2 of the form. /** @var \Drupal\rules\Core\RulesConditionInterface $condition */ - $condition = $this->conditionManager->createInstance($condition_name); + $condition = $this->conditionManager->createInstance($condition_id); $form['summary'] = [ '#markup' => $condition->summary(), ]; - $form['condition'] = [ - '#type' => 'value', - '#value' => $condition_name, - ]; $context_definitions = $condition->getContextDefinitions(); @@ -114,8 +111,8 @@ class ConditionForm implements ExpressionFormInterface { * {@inheritdoc} */ public function validateForm(array $form, FormStateInterface $form_state) { - // Only if there is a conditoon selected already we can validate something. - if ($form_state->get('condition')) { + // Only if there is a condition selected already we can validate something. + if ($form_state->get('condition_id')) { // Invoke the submission handler which will setup the expression being // edited in the form. That way the expression is ready for other // validation handlers. @@ -123,12 +120,11 @@ class ConditionForm implements ExpressionFormInterface { } } - /** * Submit callback: save the selected condition in the first step. */ public static function submitFirstStep(array &$form, FormStateInterface $form_state) { - $form_state->set('condition', $form_state->getValue('condition')); + $form_state->set('condition_id', $form_state->getValue('condition_id')); $form_state->setRebuild(); } @@ -136,6 +132,10 @@ class ConditionForm implements ExpressionFormInterface { * {@inheritdoc} */ public function submitForm(array &$form, FormStateInterface $form_state) { + // Nothing todo as long as the first step is not completed. + if (!$form_state->get('condition_id')) { + return; + } $context_config = ContextConfig::create(); foreach ($form_state->getValue('context') as $context_name => $value) { if ($form_state->get("context_$context_name") == 'selector') { @@ -147,7 +147,7 @@ class ConditionForm implements ExpressionFormInterface { } $configuration = $context_config->toArray(); - $configuration['condition_id'] = $form_state->getValue('condition'); + $configuration['condition_id'] = $form_state->get('condition_id'); $this->conditionExpression->setConfiguration($configuration); } diff --git a/src/Form/ReactionRuleEditForm.php b/src/Form/ReactionRuleEditForm.php index 8e47d49678e655b9faaf29f0393f6475fd9f2933..5c0f54ae1364d1d07268542da2865fbdd2f769a2 100644 --- a/src/Form/ReactionRuleEditForm.php +++ b/src/Form/ReactionRuleEditForm.php @@ -9,6 +9,7 @@ namespace Drupal\rules\Form; use Drupal\Core\Form\FormStateInterface; use Drupal\rules\Core\RulesEventManager; +use Drupal\rules\Core\RulesUiConfigHandler; use Symfony\Component\DependencyInjection\ContainerInterface; /** @@ -16,8 +17,6 @@ use Symfony\Component\DependencyInjection\ContainerInterface; */ class ReactionRuleEditForm extends RulesComponentFormBase { - use TempStoreTrait; - /** * The event plugin manager. * @@ -25,6 +24,13 @@ class ReactionRuleEditForm extends RulesComponentFormBase { */ protected $eventManager; + /** + * The RulesUI handler of the currently active UI. + * + * @var \Drupal\rules\Core\RulesUiConfigHandler + */ + protected $rulesUiHandler; + /** * Constructs a new object of this class. * @@ -42,11 +48,30 @@ class ReactionRuleEditForm extends RulesComponentFormBase { return new static($container->get('plugin.manager.rules_event')); } + /** + * {@inheritdoc} + */ + public function buildForm(array $form, FormStateInterface $form_state, RulesUiConfigHandler $rules_ui_handler = NULL) { + // Overridden such we can receive further route parameters. + $this->rulesUiHandler = $rules_ui_handler; + return parent::buildForm($form, $form_state); + } + + /** + * {@inheritdoc} + */ + protected function prepareEntity() { + parent::prepareEntity(); + // Replace the config entity with the latest entity from temp store, so any + // interim changes are picked up. + $this->entity = $this->rulesUiHandler->getConfig(); + } + /** * {@inheritdoc} */ public function form(array $form, FormStateInterface $form_state) { - $this->addLockInformation($form); + $form['locked'] = $this->rulesUiHandler->addLockInformation(); $event_name = $this->entity->getEvent(); $event_definition = $this->eventManager->getDefinition($event_name); @@ -54,11 +79,20 @@ class ReactionRuleEditForm extends RulesComponentFormBase { '@label' => $event_definition['label'], '@name' => $event_name, ]); - $form_handler = $this->entity->getExpression()->getFormHandler(); + $form_handler = $this->rulesUiHandler->getComponent() + ->getExpression()->getFormHandler(); $form = $form_handler->form($form, $form_state); return parent::form($form, $form_state); } + /** + * {@inheritdoc} + */ + public function validateForm(array &$form, FormStateInterface $form_state) { + parent::validateForm($form, $form_state); + $this->rulesUiHandler->validateLock($form, $form_state); + } + /** * {@inheritdoc} */ @@ -78,8 +112,9 @@ class ReactionRuleEditForm extends RulesComponentFormBase { */ public function save(array $form, FormStateInterface $form_state) { parent::save($form, $form_state); + // Also remove the temporarily stored rule, it has been persisted now. - $this->deleteFromTempStore(); + $this->rulesUiHandler->clearTemporaryStorage(); drupal_set_message($this->t('Reaction rule %label has been updated.', ['%label' => $this->entity->label()])); } @@ -88,7 +123,7 @@ class ReactionRuleEditForm extends RulesComponentFormBase { * Form submission handler for the 'cancel' action. */ public function cancel(array $form, FormStateInterface $form_state) { - $this->deleteFromTempStore(); + $this->rulesUiHandler->clearTemporaryStorage(); drupal_set_message($this->t('Canceled.')); $form_state->setRedirect('entity.rules_reaction_rule.collection'); } @@ -100,13 +135,4 @@ class ReactionRuleEditForm extends RulesComponentFormBase { return $this->t('Edit reaction rule "@label"', ['@label' => $rules_reaction_rule->label()]); } - /** - * Returns the entity object, which is the rules config on this class. - * - * @see \Drupal\rules\Form\TempStoreTrait - */ - protected function getRuleConfig() { - return $this->entity; - } - } diff --git a/src/Form/TempStoreTrait.php b/src/Form/TempStoreTrait.php index 940022a3b021373c3aa4e2f1667b966cc44ae4ae..43e168cdeee220a0952c59188a871e4e7079f3b4 100644 --- a/src/Form/TempStoreTrait.php +++ b/src/Form/TempStoreTrait.php @@ -13,7 +13,13 @@ use Drupal\Core\Url; use Drupal\user\SharedTempStoreFactory; /** - * Provides methods for modified rules configurations in temporary storage. + * Provides methods for modified rules components in temporary storage. + * + * Note that this implements the lock-related methods of + * \Drupal\rules\Core\RulesUiHandlerInterface. + * + * @see \Drupal\rules\Core\RulesUiHandlerInterface + * @see \Drupal\rules\Core\RulesUiConfigHandler */ trait TempStoreTrait { @@ -25,12 +31,19 @@ trait TempStoreTrait { protected $tempStoreFactory; /** - * The temporary store for the rules configuration. + * The temporary store for the rules component. * * @var \Drupal\user\SharedTempStore */ protected $tempStore; + /** + * The currently active rules UI handler. + * + * @var \Drupal\rules\Core\RulesUiHandlerInterface + */ + protected $rulesUiHandler; + /** * The date formatter service. * @@ -77,7 +90,7 @@ trait TempStoreTrait { /** * Setter injection for the date formatter service. * - * @param \Drupal\rules\Form\DateFormatterInterface $date_formatter + * @param \Drupal\Core\Datetime\DateFormatterInterface $date_formatter * The service. */ public function setDateFormatter(DateFormatterInterface $date_formatter) { @@ -98,36 +111,50 @@ trait TempStoreTrait { } /** - * Gets the temporary storage repository from the factory. + * Gets the currently active RulesUI's handler. * - * @return \Drupal\user\SharedTempStore - * The shareds storage. + * @return \Drupal\rules\Core\RulesUiHandlerInterface + * The RulesUI handler. */ - protected function getTempStore() { - if (!isset($this->tempStore)) { - $this->tempStore = $this->getTempStoreFactory()->get($this->getRuleConfig()->getEntityTypeId()); - } - return $this->tempStore; + protected function getRulesUiHandler() { + // Usually the trait is used on the RulesUI handler. + return $this; } /** - * Saves the rule configuration to the temporary storage. + * Fetches the stored data from the temporary storage. + * + * @return mixed|null + * The stored data or NULL if the temp store is empty. */ - protected function saveToTempStore() { - $this->getTempStore()->set($this->getRuleConfig()->id(), $this->getRuleConfig()); + protected function fetchFromTempStore() { + return $this->getTempStore()->get($this->getTempStoreItemId()); } /** - * Determines if the rule coniguration is locked for the current user. + * Stores some data in the temporary storage. * - * @return bool - * TRUE if locked, FALSE otherwise. + * @param mixed $data + * The data to store. + */ + protected function storeToTempStore($data) { + $this->getTempStore()->set($this->getTempStoreItemId(), $data); + } + + /** + * @see \Drupal\rules\Core\RulesUiHandlerInterface::clearTemporaryStorage() */ - protected function isLocked() { + public function clearTemporaryStorage() { + $this->getTempStore()->delete($this->getTempStoreItemId()); + } + + /** + * @see \Drupal\rules\Core\RulesUiHandlerInterface::isLocked() + */ + public function isLocked() { // If there is an object in the temporary storage from another user then - // this configuration is locked. - if ($this->getTempStore()->get($this->getRuleConfig()->id()) - && !$this->getTempStore()->getIfOwner($this->getRuleConfig()->id()) + // this component is locked. + if ($this->getTempStore()->get($this->getTempStoreItemId()) && !$this->getTempStore()->getIfOwner($this->getTempStoreItemId()) ) { return TRUE; } @@ -135,54 +162,53 @@ trait TempStoreTrait { } /** - * Provides information which user at which time locked the rule for editing. + * Generates the temp store item's ID to use for the edited component. * - * @return object - * StdClass object as provided by \Drupal\user\SharedTempStore. + * @return string + * The temp store ID. */ - protected function getLockMetaData() { - return $this->getTempStore()->getMetadata($this->getRuleConfig()->id()); + private function getTempStoreItemId() { + // The internal path is unique for the currently edited component. + return $this->getRulesUiHandler()->getBaseRouteUrl()->getInternalPath(); } /** - * Checks if the rule has been modified and is present in the storage. + * Gets the temporary storage repository from the factory. * - * @return bool - * TRUE if the rule has been modified, FALSE otherwise. + * @return \Drupal\user\SharedTempStore + * The shareds storage. */ - protected function isEdited() { - if ($this->getTempStore()->get($this->getRuleConfig()->id())) { - return TRUE; + private function getTempStore() { + if (!isset($this->tempStore)) { + $this->tempStore = $this->getTempStoreFactory()->get($this->getRulesUiHandler()->getPluginId()); } - return FALSE; + return $this->tempStore; } /** - * Removed the current rule configuration from the temporary storage. + * @see \Drupal\rules\Core\RulesUiHandlerInterface::getLockMetaData() */ - protected function deleteFromTempStore() { - $this->getTempStore()->delete($this->getRuleConfig()->id()); + public function getLockMetaData() { + return $this->getTempStore()->getMetadata($this->getTempStoreItemId()); } /** - * Provides the config entity object that is dealt with in the temp store. - * - * @return \Drupal\Core\Config\Entity\ConfigEntityInterface - * The rules config entity. + * @see \Drupal\rules\Core\RulesUiHandlerInterface::isEdited() */ - protected function getRuleConfig() { - return $this->ruleConfig; + public function isEdited() { + if ($this->getTempStore()->get($this->getTempStoreItemId())) { + return TRUE; + } + return FALSE; } /** - * Adds a message to the form if the rule configuration is locked/modified. - * - * @param array $form - * The form render array. + * @see \Drupal\rules\Core\RulesUiHandlerInterface::addLockInformation() */ - protected function addLockInformation(array &$form) { + public function addLockInformation() { + $build = []; if ($this->isLocked()) { - $form['locked'] = [ + $build['locked'] = [ '#type' => 'container', '#attributes' => [ 'class' => ['rules-locked', 'messages', 'messages--warning'], @@ -192,7 +218,7 @@ trait TempStoreTrait { ]; } else { - $form['changed'] = [ + $build['changed'] = [ '#type' => 'container', '#attributes' => [ 'class' => ['rules-changed', 'messages', 'messages--warning'], @@ -201,15 +227,16 @@ trait TempStoreTrait { '#weight' => -10, ]; if (!$this->isEdited()) { - $form['changed']['#attributes']['class'][] = 'js-hide'; + $build['changed']['#attributes']['class'][] = 'js-hide'; } } + return $build; } /** - * Validation callback that prevents editing locked rule configs. + * @see \Drupal\rules\Core\RulesUiHandlerInterface::validateLock() */ - public function validateForm(array &$form, FormStateInterface $form_state) { + public function validateLock(array &$form, FormStateInterface $form_state) { if ($this->isLocked()) { $form_state->setError($form, $this->lockInformationMessage()); } @@ -221,7 +248,7 @@ trait TempStoreTrait { * @return \Drupal\Core\StringTranslation\TranslatableMarkup * The message suitable to be shown in the UI. */ - protected function lockInformationMessage() { + private function lockInformationMessage() { $lock = $this->getLockMetaData(); $username = [ '#theme' => 'username', @@ -230,11 +257,10 @@ trait TempStoreTrait { $lock_message_substitutions = [ '@user' => drupal_render($username), '@age' => $this->getDateFormatter()->formatTimeDiffSince($lock->updated), - ':url' => Url::fromRoute('entity.rules_reaction_rule.break_lock_form', [ - 'rules_reaction_rule' => $this->getRuleConfig()->id(), - ])->toString(), + '@component_type' => $this->getRulesUiHandler()->getPluginDefinition()->component_type_label, + ':url' => Url::fromRoute($this->getRulesUiHandler()->getPluginDefinition()->base_route . '.break_lock', \Drupal::routeMatch()->getRawParameters()->all())->toString(), ]; - return $this->t('This rule is being edited by user @user, and is therefore locked from editing by others. This lock is @age old. Click here to <a href=":url">break this lock</a>.', $lock_message_substitutions); + return $this->t('This @component_type is being edited by user @user, and is therefore locked from editing by others. This lock is @age old. Click here to <a href=":url">break this lock</a>.', $lock_message_substitutions); } } diff --git a/src/ParamConverter/RulesTempConverter.php b/src/ParamConverter/RulesTempConverter.php deleted file mode 100644 index 8b58a696de3e51e31d8bb8ed135f09b0cfe18f0e..0000000000000000000000000000000000000000 --- a/src/ParamConverter/RulesTempConverter.php +++ /dev/null @@ -1,76 +0,0 @@ -<?php - -/** - * @file - * Contains \Drupal\rules\ParamConverter\RulesTempConverter. - */ - -namespace Drupal\rules\ParamConverter; - -use Drupal\Core\Entity\EntityManagerInterface; -use Drupal\Core\ParamConverter\EntityConverter; -use Symfony\Component\Routing\Route; -use Drupal\user\SharedTempStoreFactory; - -/** - * Provides upcasting for a rules entity to be used in the UI. - * - * Either loads the rule from the temporary storage if it is currently being - * edited or from the canonical entity storage otherwise. - * - * Largely copied from \Drupal\views_ui\ParamConverter\ViewsUIConverter. - */ -class RulesTempConverter extends EntityConverter { - - /** - * Stores the tempstore factory. - * - * @var \Drupal\user\SharedTempStoreFactory - */ - protected $tempStoreFactory; - - /** - * Constructor. - * - * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager - * The entity manager. - * @param \Drupal\user\SharedTempStoreFactory $temp_store_factory - * The factory for the temp store object. - */ - public function __construct(EntityManagerInterface $entity_manager, SharedTempStoreFactory $temp_store_factory) { - parent::__construct($entity_manager); - - $this->tempStoreFactory = $temp_store_factory; - } - - /** - * {@inheritdoc} - */ - public function convert($value, $definition, $name, array $defaults) { - // Standard upcasting: check if the config entity exists at all in the - // storage. - if (!$entity = parent::convert($value, $definition, $name, $defaults)) { - return; - } - - // Now check if there is also a version being edited and return that. - $store = $this->tempStoreFactory->get($entity->getEntityTypeId()); - $edited_entity = $store->get($value); - if ($edited_entity) { - return $edited_entity; - } - - return $entity; - } - - /** - * {@inheritdoc} - */ - public function applies($definition, $name, Route $route) { - if (parent::applies($definition, $name, $route)) { - return !empty($definition['tempstore']) && $definition['type'] === 'entity:rules_reaction_rule'; - } - return FALSE; - } - -} diff --git a/src/Plugin/RulesExpression/Rule.php b/src/Plugin/RulesExpression/Rule.php index 07c66a94c168a21bbf38348a2d28ff0ed1ccb467..ec7b0759464c1ee84bdae898faef09f8dd4d38e0 100644 --- a/src/Plugin/RulesExpression/Rule.php +++ b/src/Plugin/RulesExpression/Rule.php @@ -28,11 +28,9 @@ use Symfony\Component\DependencyInjection\ContainerInterface; * nest several rules into one rule. This is the functionality of so called * "rule sets" in Drupal 7. * - * @todo rename the form class to just RuleForm. - * * @RulesExpression( * id = "rules_rule", - * label = @Translation("A rule, executing actions when conditions are met."), + * label = @Translation("Rule"), * form_class = "\Drupal\rules\Form\Expression\ReactionRuleForm" * ) */ @@ -239,4 +237,14 @@ class Rule extends ExpressionBase implements RuleInterface, ContainerFactoryPlug return TRUE; } + /** + * PHP magic __clone function. + */ + public function __clone() { + $this->actions = clone $this->actions; + $this->actions->setRoot($this->getRoot()); + $this->conditions = clone $this->conditions; + $this->conditions->setRoot($this->getRoot()); + } + } diff --git a/src/ProxyClass/ParamConverter/RulesTempConverter.php b/src/ProxyClass/ParamConverter/RulesTempConverter.php deleted file mode 100644 index 70b5a18f1d5fb1bc5789a3bf3925191dfe3191ba..0000000000000000000000000000000000000000 --- a/src/ProxyClass/ParamConverter/RulesTempConverter.php +++ /dev/null @@ -1,92 +0,0 @@ -<?php - -/** - * @file - * Contains \Drupal\rules\ProxyClass\ParamConverter\RulesTempConverter. - */ - -/** - * This file was generated via php core/scripts/generate-proxy-class.php 'Drupal\rules\ParamConverter\RulesTempConverter' "modules/rules/src". - */ - -namespace Drupal\rules\ProxyClass\ParamConverter { - - /** - * Provides a proxy class for \Drupal\rules\ParamConverter\RulesTempConverter. - * - * @see \Drupal\Component\ProxyBuilder - */ - class RulesTempConverter implements \Drupal\Core\ParamConverter\ParamConverterInterface - { - - use \Drupal\Core\DependencyInjection\DependencySerializationTrait; - - /** - * The id of the original proxied service. - * - * @var string - */ - protected $drupalProxyOriginalServiceId; - - /** - * The real proxied service, after it was lazy loaded. - * - * @var \Drupal\rules\ParamConverter\RulesTempConverter - */ - protected $service; - - /** - * The service container. - * - * @var \Symfony\Component\DependencyInjection\ContainerInterface - */ - protected $container; - - /** - * Constructs a ProxyClass Drupal proxy object. - * - * @param \Symfony\Component\DependencyInjection\ContainerInterface $container - * The container. - * @param string $drupal_proxy_original_service_id - * The service ID of the original service. - */ - public function __construct(\Symfony\Component\DependencyInjection\ContainerInterface $container, $drupal_proxy_original_service_id) - { - $this->container = $container; - $this->drupalProxyOriginalServiceId = $drupal_proxy_original_service_id; - } - - /** - * Lazy loads the real service from the container. - * - * @return object - * Returns the constructed real service. - */ - protected function lazyLoadItself() - { - if (!isset($this->service)) { - $this->service = $this->container->get($this->drupalProxyOriginalServiceId); - } - - return $this->service; - } - - /** - * {@inheritdoc} - */ - public function convert($value, $definition, $name, array $defaults) - { - return $this->lazyLoadItself()->convert($value, $definition, $name, $defaults); - } - - /** - * {@inheritdoc} - */ - public function applies($definition, $name, \Symfony\Component\Routing\Route $route) - { - return $this->lazyLoadItself()->applies($definition, $name, $route); - } - - } - -} diff --git a/src/Routing/RulesUiRouteEnhancer.php b/src/Routing/RulesUiRouteEnhancer.php new file mode 100644 index 0000000000000000000000000000000000000000..43782bff4214c2ae80ab6e5687292a8a50d75d12 --- /dev/null +++ b/src/Routing/RulesUiRouteEnhancer.php @@ -0,0 +1,63 @@ +<?php + +/** + * @file + * Contains \Drupal\rules\Routing\RulesUiRouteEnhancer. + */ + +namespace Drupal\rules\Routing; + +use Drupal\Core\Routing\Enhancer\RouteEnhancerInterface; +use Drupal\rules\Core\RulesUiManagerInterface; +use Symfony\Cmf\Component\Routing\RouteObjectInterface; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\Routing\Route; + +/** + * Enhances routes with the specified RulesUI. + * + * Routes have the plugin ID of the active RulesUI instance set on the _rules_ui + * option. Based upon that information, this enhances adds the following + * parameters to the routes: + * - rules_ui_handler: The RulesUI handler, as specified by the plugin. + * - rules_component: The rules component being edited, as provided by the + * handler. + */ +class RulesUiRouteEnhancer implements RouteEnhancerInterface { + + /** + * The rules_ui plugin manager. + * + * @var \Drupal\rules\Core\RulesUiManagerInterface + */ + protected $rulesUiManager; + + /** + * Constructs the object. + * + * @param \Drupal\rules\Core\RulesUiManagerInterface $rules_ui_manager + * The rules_ui plugin manager. + */ + public function __construct(RulesUiManagerInterface $rules_ui_manager) { + $this->rulesUiManager = $rules_ui_manager; + } + + /** + * {@inheritdoc} + */ + public function enhance(array $defaults, Request $request) { + // @var $route \Symfony\Component\Routing\Route + $route = $defaults[RouteObjectInterface::ROUTE_OBJECT]; + $plugin_id = $route->getOption('_rules_ui'); + $defaults['rules_ui_handler'] = $this->rulesUiManager->createInstance($plugin_id); + return $defaults; + } + + /** + * {@inheritdoc} + */ + public function applies(Route $route) { + return ($route->hasOption('_rules_ui')); + } + +} diff --git a/src/Routing/RulesUiRouteSubscriber.php b/src/Routing/RulesUiRouteSubscriber.php new file mode 100644 index 0000000000000000000000000000000000000000..ddfa86c4585c91213f4eb9682e2d83537e29719b --- /dev/null +++ b/src/Routing/RulesUiRouteSubscriber.php @@ -0,0 +1,117 @@ +<?php + +/** + * @file + * Contains \Drupal\rules\Routing\RulesUiRouteSubscriber. + */ + +namespace Drupal\rules\Routing; + +use Drupal\Core\Routing\RouteSubscriberBase; +use Drupal\Core\Routing\RoutingEvents; +use Drupal\rules\Core\RulesUiDefinition; +use Drupal\rules\Core\RulesUiManagerInterface; +use Symfony\Component\Routing\Route; +use Symfony\Component\Routing\RouteCollection; + +/** + * Adds routes generated by the rules UI handlers. + */ +class RulesUiRouteSubscriber extends RouteSubscriberBase { + + /** + * The rules UI manager. + * + * @var \Drupal\rules\Core\RulesUiManagerInterface + */ + protected $rulesUiManager; + + /** + * Constructs the object. + * + * @param \Drupal\rules\Core\RulesUiManagerInterface $rules_ui_manager + * The rules UI manager. + */ + public function __construct(RulesUiManagerInterface $rules_ui_manager) { + $this->rulesUiManager = $rules_ui_manager; + } + + /** + * {@inheritdoc} + */ + protected function alterRoutes(RouteCollection $collection) { + foreach ($this->rulesUiManager->getDefinitions() as $name => $definition) { + $ui_definition = $this->rulesUiManager->getDefinition($name); + $this->registerRoutes($ui_definition, $collection); + } + } + + /** + * {@inheritdoc} + */ + public static function getSubscribedEvents() { + $events = parent::getSubscribedEvents(); + // Should run after AdminRouteSubscriber so the routes can inherit admin + // status of the edit routes on entities. Therefore priority -210. + $events[RoutingEvents::ALTER] = ['onAlterRoutes', -210]; + return $events; + } + + /** + * Registers the routes as needed for the UI. + * + * @param \Drupal\rules\Core\RulesUiDefinition $ui_definition + * The definition of the RulesUI for which to register the routes. + * @param \Symfony\Component\Routing\RouteCollection $collection + * The route collection to which to add the routes. + */ + protected function registerRoutes(RulesUiDefinition $ui_definition, RouteCollection $collection) { + $base_route = $collection->get($ui_definition->base_route); + + $options = [ + 'parameters' => ($base_route->getOption('parameters') ?: []), + '_admin_route' => $base_route->getOption('_admin_route') ?: FALSE, + '_rules_ui' => $ui_definition->id, + ]; + $requirements = [ + '_permission' => $ui_definition->permissions ?: $base_route->getRequirement('_permission'), + ]; + + $route = (new Route($base_route->getPath() . '/add/{expression_id}')) + ->addDefaults([ + '_form' => '\Drupal\rules\Form\AddExpressionForm', + '_title_callback' => '\Drupal\rules\Form\AddExpressionForm::getTitle', + ]) + ->addOptions($options) + ->addRequirements($requirements); + $collection->add($ui_definition->base_route . '.expression.add', $route); + + $route = (new Route($base_route->getPath() . '/edit/{uuid}')) + ->addDefaults([ + '_form' => '\Drupal\rules\Form\EditExpressionForm', + '_title_callback' => '\Drupal\rules\Form\EditExpressionForm::getTitle', + ]) + ->addOptions($options) + ->addRequirements($requirements); + $collection->add($ui_definition->base_route . '.expression.edit', $route); + + $route = (new Route($base_route->getPath() . '/delete/{uuid}')) + ->addDefaults([ + '_form' => '\Drupal\rules\Form\DeleteExpressionForm', + '_title' => 'Delete expression', + ]) + ->addOptions($options) + ->addRequirements($requirements); + $collection->add($ui_definition->base_route . '.expression.delete', $route); + + $route = (new Route($base_route->getPath() . '/break-lock')) + ->addDefaults([ + '_form' => '\Drupal\rules\Form\\BreakLockForm', + '_title' => 'Break lock', + ]) + ->addOptions($options) + ->addRequirements($requirements); + $collection->add($ui_definition->base_route . '.break_lock', $route); + } + +} diff --git a/src/Routing/UiRouteSubscriber.php b/src/Routing/UiRouteSubscriber.php deleted file mode 100644 index 087e80c19dc64affe430b40549c2a595013dd70b..0000000000000000000000000000000000000000 --- a/src/Routing/UiRouteSubscriber.php +++ /dev/null @@ -1,58 +0,0 @@ -<?php - -/** - * @file - * Contains \Drupal\rules\Routing\UiRouteSubscriber. - */ - -namespace Drupal\rules\Routing; - -use Drupal\Core\Routing\RouteSubscriberBase; -use Drupal\Core\Routing\RoutingEvents; -use Drupal\rules\Core\RulesUiManagerInterface; -use Symfony\Component\Routing\RouteCollection; - -/** - * Adds routes generated by the rules UI handlers. - */ -class UiRouteSubscriber extends RouteSubscriberBase { - - /** - * The rules UI manager. - * - * @var \Drupal\rules\Core\RulesUiManagerInterface - */ - protected $rulesUiManager; - - /** - * Constructs the object. - * - * @param \Drupal\rules\Core\RulesUiManagerInterface $rules_ui_manager - * The rules UI manager. - */ - public function __construct(RulesUiManagerInterface $rules_ui_manager) { - $this->rulesUiManager = $rules_ui_manager; - } - - /** - * {@inheritdoc} - */ - protected function alterRoutes(RouteCollection $collection) { - foreach ($this->rulesUiManager->getDefinitions() as $name => $definition) { - $ui_handler = $this->rulesUiManager->createInstance($name); - $ui_handler->registerRoutes($collection); - } - } - - /** - * {@inheritdoc} - */ - public static function getSubscribedEvents() { - $events = parent::getSubscribedEvents(); - // Should run after AdminRouteSubscriber so the routes can inherit admin - // status of the edit routes on entities. Therefore priority -210. - $events[RoutingEvents::ALTER] = ['onAlterRoutes', -210]; - return $events; - } - -} diff --git a/tests/src/Kernel/RulesUiEmbedTest.php b/tests/src/Kernel/RulesUiEmbedTest.php index 44374bffcf4bd2cacf25aaed21dcf01882203ff6..b0c1bce442ec6603179760499737a04e540e5261 100644 --- a/tests/src/Kernel/RulesUiEmbedTest.php +++ b/tests/src/Kernel/RulesUiEmbedTest.php @@ -7,7 +7,7 @@ namespace Drupal\Tests\rules\Kernel; -use Drupal\rules\Core\RulesUiDefaultHandler; +use Drupal\rules\Core\RulesUiConfigHandler; use Drupal\rules\Core\RulesUiDefinition; /** @@ -55,7 +55,7 @@ class RulesUiEmbedTest extends RulesDrupalTestBase { $this->assertTrue(isset($definition['rules_test_ui_embed.settings_conditions'])); $this->assertInstanceOf(RulesUiDefinition::class, $definition['rules_test_ui_embed.settings_conditions']); $this->assertTrue(!empty($definition['rules_test_ui_embed.settings_conditions']->label)); - $this->assertEquals(RulesUiDefaultHandler::class, $definition['rules_test_ui_embed.settings_conditions']->getClass()); + $this->assertEquals(RulesUiConfigHandler::class, $definition['rules_test_ui_embed.settings_conditions']->getClass()); } }