rules-8.x-3.x-dev/src/Drush/Commands/RulesDrushCommands.php

src/Drush/Commands/RulesDrushCommands.php
<?php

declare(strict_types=1);

namespace Drupal\rules\Drush\Commands;

use Consolidation\AnnotatedCommand\Hooks\HookManager;
use Consolidation\OutputFormatters\StructuredData\RowsOfFields;
use Drupal\Component\Serialization\Yaml;
use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Config\StorageCacheInterface;
use Drupal\rules\Core\RulesEventManagerInterface;
use Drush\Attributes as CLI;
use Drush\Commands\AutowireTrait;
use Drush\Commands\DrushCommands;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;

// cspell:ignore rlst renb rdis rdel rexp rrev

/**
 * Drush 12+ commands for the Rules module.
 */
final class RulesDrushCommands extends DrushCommands {
  use AutowireTrait;

  /**
   * RulesDrushCommands constructor.
   *
   * @param \Drupal\Core\Config\ConfigFactoryInterface $configFactory
   *   The config factory service.
   * @param \Drupal\Core\Config\StorageCacheInterface $configStorage
   *   The config storage service.
   * @param \Drupal\rules\Core\RulesEventManagerInterface $rulesEventManager
   *   The rules event manager.
   */
  public function __construct(
    protected ConfigFactoryInterface $configFactory,
    protected StorageCacheInterface $configStorage,
    protected RulesEventManagerInterface $rulesEventManager,
  ) {
    parent::__construct();
  }

  /**
   * Lists all the active and inactive rules for your site.
   *
   * @param string $type
   *   (optional) Either 'rule' or 'component'. Any other value (or no value)
   *   will list both Reaction Rules and Rules Components.
   * @param array $options
   *   (optional) The options.
   *
   * @command rules:list
   * @aliases rlst,rules-list
   *
   * @usage drush rules:list
   *   Lists both Reaction Rules and Rules Components.
   * @usage drush rules:list component
   *   Lists only Rules Components.
   * @usage drush rules:list --fields=machine-name
   *   Lists just the machine names.
   * @usage drush rules:list --fields=machine-name --pipe
   *   Outputs machine names in a format suitable for piping.
   *
   * @table-style default
   * @field-labels
   *   machine-name: Rule
   *   label: Label
   *   event: Event
   *   active: Active
   * @default-fields machine-name,label,event,active
   *
   * @return \Consolidation\OutputFormatters\StructuredData\RowsOfFields
   *   The data.
   *
   * @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException
   */
  #[CLI\Command(name: 'rules:list', aliases: ['rules-list', 'rlst'])]
  #[CLI\Help(description: 'Lists all the active and inactive rules for your site.')]
  #[CLI\Argument(name: 'type', description: "Type of Rule. Either 'rule' or 'component'.")]
  #[CLI\Usage(name: 'drush rules:list', description: 'Lists both Reaction Rules and Rules Components.')]
  #[CLI\Usage(name: 'drush rules:list component', description: 'Lists only Rules Components.')]
  #[CLI\Usage(name: 'drush rules:list --fields=machine-name', description: 'Lists just the machine names.')]
  #[CLI\Usage(name: 'drush rules:list --fields=machine-name --pipe', description: 'Outputs machine names in a format suitable for piping.')]
  #[CLI\FieldLabels(labels: ['machine-name' => 'Rule', 'label' => 'Label', 'event' => 'Event', 'active' => 'Active'])]
  #[CLI\DefaultFields(fields: ['machine-name', 'label', 'event', 'active'])]
  public function listAll(string $type = '', array $options = ['format' => 'table', 'fields' => '']): RowsOfFields {
    // Type is 'rule', or 'component'. Any other value (or no value) will
    // list both Reaction Rules and Rules Components.
    switch ($type) {
      case 'rule':
        $types = ['reaction'];
        break;

      case 'component':
        $types = ['component'];
        break;

      default:
        $types = ['reaction', 'component'];
        break;
    }

    // Loop over type parameter.
    $rows = [];
    foreach ($types as $item) {
      $rules = $this->configFactory->listAll('rules.' . $item);
      // Loop over configuration entities for this $item.
      foreach ($rules as $config) {
        $rule = $this->configFactory->get($config);
        if (!empty($rule->get('id')) && !empty($rule->get('label'))) {
          $events = [];
          $active = '';
          // Components don't have events and can't be enabled/disabled.
          if ($item == 'reaction') {
            foreach ($rule->get('events') as $event) {
              $plugin = $this->rulesEventManager->getDefinition($event['event_name']);
              $events[] = (string) $plugin['label'];
            }
            $active = $rule->get('status') ? dt('Enabled') : dt('Disabled');
          }
          $rows[(string) $rule->get('id')] = [
            'machine-name' => (string) $rule->get('id'),
            'label' => (string) $rule->get('label'),
            'event' => implode(', ', $events),
            'active' => (string) $active,
          ];
        }
      }
    }

    return new RowsOfFields($rows);
  }

  /**
   * Enables a Reaction Rule on your site.
   *
   * @param string|null $rule
   *   (optional) Reaction rule name (machine name) to enable.
   *
   * @command rules:enable
   * @interact-disabled-rules
   * @aliases renb,rules-enable
   *
   * @usage drush rules:enable
   *   Displays all disabled rules and allows you to select one to enable.
   * @usage drush rules:enable test_rule
   *   Enables the rule with machine name 'test_rule'.
   *
   * @throws \Exception
   */
  #[CLI\Command(name: 'rules:enable', aliases: ['rules-enable', 'renb'])]
  #[CLI\Help(description: 'Enables a Reaction Rule on your site.')]
  #[CLI\Argument(name: 'rule', description: 'Reaction Rule name (machine name) to enable.')]
  #[CLI\Usage(name: 'drush rules:enable', description: 'Displays all disabled rules and allows you to select one to enable.')]
  #[CLI\Usage(name: 'drush rules:enable test_rule', description: "Enables the rule with machine name 'test_rule'.")]
  public function enable(?string $rule = NULL): void {
    if ($rule === NULL) {
      // There are no Reaction Rules to enable.
      return;
    }
    // The $rule argument must be a Reaction Rule.
    if ($this->configStorage->exists('rules.reaction.' . $rule)) {
      $config = $this->configFactory->getEditable('rules.reaction.' . $rule);
    }
    // The @interact-disabled-rules hook returns fully-qualified names.
    elseif ($this->configStorage->exists($rule)) {
      $config = $this->configFactory->getEditable($rule);
    }
    else {
      throw new \Exception(dt('Could not find a Reaction Rule named @name', ['@name' => $rule]));
    }

    if (!$config->get('status')) {
      $config->set('status', TRUE);
      $config->save();
      $this->logger->success(dt('The rule @name has been enabled.', ['@name' => $rule]));
    }
    else {
      $this->logger->warning(dt('The rule @name is already enabled', ['@name' => $rule]));
    }
  }

  /**
   * Disables a Reaction Rule on your site.
   *
   * @param string|null $rule
   *   (optional) Reaction rule name (machine name) to disable.
   *
   * @command rules:disable
   * @interact-enabled-rules
   * @aliases rdis,rules-disable
   *
   * @usage drush rules:disable
   *   Displays all enabled rules and allows you to select one to disable.
   * @usage drush rules:disable test_rule
   *   Disables the rule with machine name 'test_rule'.
   *
   * @throws \Exception
   */
  #[CLI\Command(name: 'rules:disable', aliases: ['rules-disable', 'rdis'])]
  #[CLI\Help(description: 'Disables a Reaction Rule on your site.')]
  #[CLI\Argument(name: 'rule', description: 'Reaction Rule name (machine name) to disable.')]
  #[CLI\Usage(name: 'drush rules:disable', description: 'Displays all enabled rules and allows you to select one to disable.')]
  #[CLI\Usage(name: 'drush rules:disable test_rule', description: "Disables the rule with machine name 'test_rule'.")]
  public function disable(?string $rule = NULL): void {
    if ($rule === NULL) {
      // There are no Reaction Rules to disable.
      return;
    }
    // The $rule argument must be a Reaction Rule.
    if ($this->configStorage->exists('rules.reaction.' . $rule)) {
      $config = $this->configFactory->getEditable('rules.reaction.' . $rule);
    }
    // The @interact-enabled-rules hook returns fully-qualified names.
    elseif ($this->configStorage->exists($rule)) {
      $config = $this->configFactory->getEditable($rule);
    }
    else {
      throw new \Exception(dt('Could not find a Reaction Rule named @name', ['@name' => $rule]));
    }

    if ($config->get('status')) {
      $config->set('status', FALSE);
      $config->save();
      $this->logger->success(dt('The rule @name has been disabled.', ['@name' => $rule]));
    }
    else {
      $this->logger->warning(dt('The rule @name is already disabled', ['@name' => $rule]));
    }
  }

  /**
   * Deletes a rule on your site.
   *
   * @param string $rule
   *   Rule name (machine id) to delete.
   *
   * @command rules:delete
   * @interact-rule-names
   * @aliases rdel,rules-delete
   *
   * @usage drush rules:delete
   *   Displays all rules and allows you to select one to delete.
   * @usage drush rules:delete test_rule
   *   Permanently deletes the rule with machine name 'test_rule'.
   *
   * @throws \Exception
   */
  #[CLI\Command(name: 'rules:delete', aliases: ['rules-delete', 'rdel'])]
  #[CLI\Help(description: 'Deletes a rule on your site.')]
  #[CLI\Argument(name: 'rule', description: 'Rule name (machine name) to delete.')]
  #[CLI\Usage(name: 'drush rules:delete', description: 'Displays all rules and allows you to select one to delete.')]
  #[CLI\Usage(name: 'drush rules:delete test_rule', description: "Permanently deletes the rule with machine name 'test_rule'.")]
  #[CLI\HookSelector(name: 'interact-rule-names')]
  public function delete(string $rule): void {
    // The $rule argument could refer to a Reaction Rule or a Rules Component.
    if ($this->configStorage->exists('rules.reaction.' . $rule)) {
      $config = $this->configFactory->getEditable('rules.reaction.' . $rule);
    }
    elseif ($this->configStorage->exists('rules.component.' . $rule)) {
      $config = $this->configFactory->getEditable('rules.component.' . $rule);
    }
    // The @interact-rule-names hook returns fully-qualified names.
    elseif ($this->configStorage->exists($rule)) {
      $config = $this->configFactory->getEditable($rule);
    }
    else {
      throw new \Exception(dt('Could not find a Reaction Rule or a Rules Component named @name', ['@name' => $rule]));
    }

    if ($this->confirm(dt('Are you sure you want to delete the rule named "@name"? This action cannot be undone.', ['@name' => $rule]))) {
      $config->delete();
      $this->logger->success(dt('The rule @name has been deleted.', ['@name' => $rule]));
    }

  }

  /**
   * Exports a single rule configuration, in YAML format.
   *
   * @param string $rule
   *   Rule name (machine id) to export.
   *
   * @command rules:export
   * @interact-rule-names
   * @aliases rexp,rules-export
   *
   * @usage drush rules:export
   *   Displays all rules and allows you to select one to export.
   * @usage drush rules:export test_rule > rules.reaction.test_rule.yml
   *   Exports the Rule named 'test_rule' and saves it in a .yml file.
   * @usage drush rules:list rule --fields=machine-name --pipe | xargs -I{}  sh -c "drush rules:export '{}' > 'rules.reaction.{}.yml'"
   *   Exports all Reaction Rules into individual YAML files.
   *
   * @throws \Exception
   */
  #[CLI\Command(name: 'rules:export', aliases: ['rules-export', 'rexp'])]
  #[CLI\Help(description: 'Exports a single rule configuration, in YAML format.')]
  #[CLI\Argument(name: 'rule', description: 'Rule name (machine name) to export.')]
  #[CLI\Usage(name: 'drush rules:export', description: 'Displays all rules and allows you to select one to export.')]
  #[CLI\Usage(name: 'drush rules:export test_rule > rules.reaction.test_rule.yml', description: "Exports the Rule with machine name 'test_rule' and saves it in a .yml file.")]
  #[CLI\Usage(name: "drush rules:list rule --fields=machine-name --pipe | xargs -I{}  sh -c \"drush rules:export '{}' > 'rules.reaction.{}.yml'\"", description: 'Exports all Reaction Rules into individual YAML files.')]
  #[CLI\HookSelector(name: 'interact-rule-names')]
  public function export(string $rule): void {
    // The $rule argument could refer to a Reaction Rule or a Rules Component.
    $config = $this->configStorage->read('rules.reaction.' . $rule);
    if (empty($config)) {
      $config = $this->configStorage->read('rules.component.' . $rule);
      if (empty($config)) {
        // The @interact-rule-names hook returns fully-qualified names.
        $config = $this->configStorage->read($rule);
        if (empty($config)) {
          throw new \Exception(dt('Could not find a Reaction Rule or a Rules Component named @name', ['@name' => $rule]));
        }
      }
    }

    $this->output->write(Yaml::encode($config), FALSE);
    $this->logger->success(dt('The rule @name has been exported.', ['@name' => $rule]));
  }

  /**
   * Reverts a rule to its original state on your site.
   *
   * @param string $rule
   *   Rule name (machine id) to revert.
   *
   * @command rules:revert
   * @interact-rule-names
   * @aliases rrev,rules-revert
   *
   * @usage drush rules:revert test_rule
   *   Restores the module-provided Rule with machine id 'test_rule' to its
   *   original state. If the Rule hasn't been customized on the site, this has
   *   no effect.
   *
   * @throws \Exception
   */
  #[CLI\Command(name: 'rules:revert', aliases: ['rules-revert', 'rrev'])]
  #[CLI\Help(description: 'Reverts a rule to its original state on your site.')]
  #[CLI\Argument(name: 'rule', description: 'Rule name (machine name) to revert.')]
  #[CLI\Usage(name: 'drush rules:revert test_rule', description: "Restores the module-provided Rule with machine id 'test_rule' to its original state. If the Rule hasn't been customized on the site, this has no effect.")]
  #[CLI\HookSelector(name: 'interact-rule-names')]
  public function revert(string $rule): void {
    // @todo Implement this function.

    // The $rule argument could refer to a Reaction Rule or a Rules Component.
    $config = $this->configStorage->read('rules.reaction.' . $rule);
    if (empty($config)) {
      $config = $this->configStorage->read('rules.component.' . $rule);
      if (empty($config)) {
        // The @interact-rule-names hook returns fully-qualified names.
        $config = $this->configStorage->read($rule);
        if (empty($config)) {
          throw new \Exception(dt('Could not find a Reaction Rule or a Rules Component named @name', ['@name' => $rule]));
        }
      }
    }

    if (($rule->status & ENTITY_OVERRIDDEN) == ENTITY_OVERRIDDEN) {
      if ($this->confirm(dt('Are you sure you want to revert the rule named "@name"? This action cannot be undone.', ['@name' => $rule]))) {
        // $config->delete();
        $this->logger->success(dt('The rule @name has been reverted to its default state.', ['@name' => $rule]));
      }
    }
    else {
      $this->logger->warning(dt('The rule "@name" has not been overridden and can\'t be reverted.', ['@name' => $rule]));
    }
  }

  /**
   * Shows a list of Rules events.
   *
   * @command rules:events
   * @aliases rules-events
   */
  #[CLI\Command(name: 'rules:events', aliases: ['rules-events'])]
  #[CLI\Help(description: 'Shows a list of Rules events.')]
  public function listEvents(): void {
    $this->formatOutput('plugin.manager.rules_event', 'Available Rules Events:');
  }

  /**
   * Shows a list of Rules conditions.
   *
   * @command rules:conditions
   * @aliases rules-conditions
   */
  #[CLI\Command(name: 'rules:conditions', aliases: ['rules-conditions'])]
  #[CLI\Help(description: 'Shows a list of Rules conditions.')]
  public function listConditions(): void {
    $this->formatOutput('plugin.manager.condition', 'Available Rules Conditions:');
  }

  /**
   * Shows a list of Rules actions.
   *
   * @command rules:actions
   * @aliases rules-actions
   */
  #[CLI\Command(name: 'rules:actions', aliases: ['rules-actions'])]
  #[CLI\Help(description: 'Shows a list of Rules actions.')]
  public function listActions(): void {
    $this->formatOutput('plugin.manager.rules_action', 'Available Rules Actions:');
  }

  /**
   * Shows a list of Rules expressions.
   *
   * @command rules:expressions
   * @aliases rules-expressions
   */
  #[CLI\Command(name: 'rules:expressions', aliases: ['rules-expressions'])]
  #[CLI\Help(description: 'Shows a list of Rules expressions.')]
  public function listExpressions(): void {
    $this->formatOutput('plugin.manager.rules_expression', 'Available Rules Expressions:');
  }

  /**
   * Helper function to format command output.
   *
   * @param string $plugin_manager_service
   *   The service name to use.
   * @param string $title
   *   The output title.
   * @param bool $categories
   *   Whether to group output into categories or not.
   * @param bool $short
   *   TRUE to display short form of output.
   */
  protected function formatOutput(string $plugin_manager_service, string $title, bool $categories = TRUE, bool $short = FALSE): void {
    // Dependency injection deliberately not used because we don't know
    // a priori what service will be needed. So ignore the phpcs message.
    // phpcs:ignore DrupalPractice.Objects.GlobalDrupal.GlobalDrupal
    $definitions = \Drupal::service($plugin_manager_service)->getDefinitions();
    $plugins = [];
    foreach ($definitions as $plugin) {
      if ($categories) {
        if ($short) {
          $plugins[(string) $plugin['category']][] = $plugin['id'];
        }
        else {
          $plugins[(string) $plugin['category']][] = $plugin['label'] . '   (' . $plugin['id'] . ')';
        }
      }
      else {
        if ($short) {
          $plugins[] = $plugin['id'];
        }
        else {
          $plugins[] = $plugin['label'] . '   (' . $plugin['id'] . ')';
        }
      }
    }

    $this->output()->writeln(dt($title));
    if ($categories) {
      ksort($plugins);
      foreach ($plugins as $category => $plugin_list) {
        $this->output()->writeln('  ' . $category);
        sort($plugin_list);
        $this->output()->writeln('    ' . implode(PHP_EOL . '    ', $plugin_list));
        $this->output()->writeln('');
      }
    }
    else {
      $unique = array_unique($plugins);
      sort($unique);
      $this->output()->writeln('  ' . implode(PHP_EOL . '  ', $unique) . PHP_EOL);
    }
  }

  /**
   * Drush hook to prompt user to select from a list of enabled Rules.
   */
  #[CLI\Hook(type: HookManager::INTERACT, target: 'rules:disable')]
  public function interactEnabledRules(InputInterface $input, OutputInterface $output): void {
    if (empty($input->getArgument('rule'))) {
      $rules = $this->configFactory->listAll('rules.reaction');
      // Loop over configuration entities for this $item.
      foreach ($rules as $index => $config) {
        $rule = $this->configFactory->get($config);
        if ($rule->get('status') === FALSE) {
          unset($rules[$index]);
        }
      }
      if (empty($rules)) {
        $this->io()->info('There are no enabled Reaction Rules to choose from.');
        return;
      }
      $choice = $this->io()->choice('Choose a Reaction Rule to disable', array_combine($rules, $rules));
      $input->setArgument('rule', $choice);
    }
  }

  /**
   * Drush hook to prompt user to select from a list of disabled Rules.
   */
  #[CLI\Hook(type: HookManager::INTERACT, target: 'rules:enable')]
  public function interactDisabledRules(InputInterface $input, OutputInterface $output): void {
    if (empty($input->getArgument('rule'))) {
      $rules = $this->configFactory->listAll('rules.reaction');
      // Loop over configuration entities for this $item.
      foreach ($rules as $index => $config) {
        $rule = $this->configFactory->get($config);
        if ($rule->get('status') === TRUE) {
          unset($rules[$index]);
        }
      }
      if (empty($rules)) {
        $this->io()->info('There are no disabled Reaction Rules to choose from.');
        return;
      }
      $choice = $this->io()->choice('Choose a Reaction Rule to enable', array_combine($rules, $rules));
      $input->setArgument('rule', $choice);
    }
  }

  /**
   * Drush hook to prompt user to select from a list of Rule names.
   */
  #[CLI\Hook(type: HookManager::INTERACT, selector: 'interact-rule-names')]
  public function interactRuleNames(InputInterface $input, OutputInterface $output): void {
    if (empty($input->getArgument('rule'))) {
      $rule_names = $this->configFactory->listAll('rules.reaction');
      $component_names = $this->configFactory->listAll('rules.component');
      $config_names = array_merge($rule_names, $component_names);
      $choice = $this->io()->choice('Choose a Rule', array_combine($config_names, $config_names));
      $input->setArgument('rule', $choice);
    }
  }

}

Главная | Обратная связь

drupal hosting | друпал хостинг | it patrol .inc