You are here

MaestroSpawnSubFlowTask.php in Maestro 3.x

Same filename and directory in other branches
  1. 8.2 src/Plugin/EngineTasks/MaestroSpawnSubFlowTask.php

File

src/Plugin/EngineTasks/MaestroSpawnSubFlowTask.php
View source
<?php

namespace Drupal\maestro\Plugin\EngineTasks;

use Drupal\maestro\Engine\Exception\MaestroSaveEntityException;
use Drupal\Core\Url;
use Drupal\Core\Plugin\PluginBase;
use Drupal\maestro\MaestroEngineTaskInterface;
use Drupal\maestro\Engine\MaestroEngine;
use Drupal\maestro\MaestroTaskTrait;
use Drupal\Core\Form\FormStateInterface;
use Drupal\maestro\Form\MaestroExecuteInteractive;

/**
 * Maestro Spawn Sub Flow Task Plugin.
 *
 * The plugin annotations below should include:
 * id: The task type ID for this task.  For Maestro tasks, this is Maestro[TaskType].
 *     So for example, the start task shipped by Maestro is MaestroStart.
 *     The Maestro End task has an id of MaestroEnd
 *     Those task IDs are what's used in the engine when a task is injected into the queue.
 *
 * @Plugin(
 *   id = "MaestroSpawnSubFlow",
 *   task_description = @Translation("The Maestro Engine's Spawn Sub Flow Task."),
 * )
 */
class MaestroSpawnSubFlowTask extends PluginBase implements MaestroEngineTaskInterface {
  use MaestroTaskTrait;

  /**
   * Constructor.
   */
  public function __construct($configuration = NULL) {
    if (is_array($configuration)) {
      $this->processID = $configuration[0];
      $this->queueID = $configuration[1];
    }
  }

  /**
   * {@inheritDoc}
   */
  public function isInteractive() {
    return FALSE;
  }

  /**
   * {@inheritDoc}
   */
  public function shortDescription() {
    return t('Spawn Sub Flow');
  }

  /**
   * {@inheritDoc}
   */
  public function description() {
    return $this
      ->t('Spawn Sub Flow.');
  }

  /**
   * {@inheritDoc}
   *
   * @see \Drupal\Component\Plugin\PluginBase::getPluginId()
   */
  public function getPluginId() {
    return 'MaestroSpawnSubFlow';
  }

  /**
   * {@inheritDoc}
   */
  public function getTaskColours() {
    return '#707070';
  }

  /**
   * {@inheritdoc}
   */
  public function getExecutableForm($modal, MaestroExecuteInteractive $parent) {
  }

  /**
   * {@inheritdoc}
   */
  public function handleExecuteSubmit(array &$form, FormStateInterface $form_state) {
  }

  /**
   * Part of the ExecutableInterface
   * Execution of the Sub Flow task will create a new process and push all selected parent variables to the newly spawned
   * sub process.  The variables pushed to the sub process will be prefixed with "maestro_parent_" and will also include a new
   * variable named "parent_process_id" which will store the process ID of the parent.
   * {@inheritdoc}.
   */
  public function execute() {
    $templateMachineName = MaestroEngine::getTemplateIdFromProcessId($this->processID);
    $taskMachineName = MaestroEngine::getTaskIdFromQueueId($this->queueID);
    $task = MaestroEngine::getTemplateTaskByID($templateMachineName, $taskMachineName);
    $spawnTemplate = $task['data']['maestro_template'];
    $variables = [];
    if (isset($task['data']['variables'])) {
      $variables = $task['data']['variables'];
    }

    // Let's first start by adding in the new process ID.
    $maestro = new MaestroEngine();
    $newProcessID = $maestro
      ->newProcess($spawnTemplate);
    if ($newProcessID !== FALSE) {

      // first, create the parent process ID variable.
      $values = [
        'process_id' => $newProcessID,
        'variable_name' => 'maestro_parent_process_id',
        'variable_value' => $this->processID,
      ];
      $new_var = \Drupal::entityTypeManager()
        ->getStorage('maestro_process_variables')
        ->create($values);
      $new_var
        ->save();
      if (!$new_var
        ->id()) {

        // Throw a maestro exception
        // completion should technically end here for this initiation.
        throw new MaestroSaveEntityException('maestro_process_variable', $values['variable_name'] . ' failed saving during new process creation.');
      }
      foreach ($variables as $machine_name => $checked_value) {
        if ($machine_name != '') {

          // We now populate the new process with variables.
          $parent_value = MaestroEngine::getProcessVariable($machine_name, $this->processID);
          $values = [
            'process_id' => $newProcessID,
            'variable_name' => 'maestro_parent_' . $machine_name,
            'variable_value' => $parent_value,
          ];
          $new_var = \Drupal::entityTypeManager()
            ->getStorage('maestro_process_variables')
            ->create($values);
          $new_var
            ->save();
          if (!$new_var
            ->id()) {

            // Throw a maestro exception
            // completion should technically end here for this initiation.
            throw new MaestroSaveEntityException('maestro_process_variable', $values['variable_name'] . ' failed saving during new process creation.');
          }
          $parent_value = '';
        }
      }
      return TRUE;
    }
    else {
      \Drupal::logger('maestro')
        ->error('Unable to spawn sub process.  Process spawn returned an error.');
      return FALSE;
    }
  }

  /**
   * {@inheritdoc}
   */
  public function getTaskEditForm(array $task, $templateMachineName) {
    $form = [
      '#markup' => t('Spawn Sub Flow Edit'),
    ];
    $maestro_templates = MaestroEngine::getTemplates();
    $templates = [];
    $templates['none'] = $this
      ->t('Please Select Template');
    foreach ($maestro_templates as $machine_name => $template) {
      $templates[$machine_name] = $template
        ->label();
    }
    $form['maestro_task_machine_name'] = [
      '#type' => 'hidden',
      '#value' => $task['id'],
    ];
    $form['maestro_template_machine_name'] = [
      '#type' => 'hidden',
      '#value' => $templateMachineName,
    ];
    $selected_template = '';
    if (isset($task['data']['maestro_template'])) {
      $selected_template = $task['data']['maestro_template'];
    }
    $form['maestro_template'] = [
      '#type' => 'select',
      '#options' => $templates,
      '#title' => $this
        ->t('Choose the Maestro Template'),
      '#default_value' => $selected_template,
      '#required' => TRUE,
      '#ajax' => [
        'callback' => [
          $this,
          'subFlowChoiceHandlerCallback',
        ],
        'event' => 'change',
        'wrapper' => 'handler-ajax-refresh-wrapper',
        'progress' => [
          'type' => 'throbber',
          'message' => NULL,
        ],
      ],
    ];
    $template_machine_name = $selected_template;
    $form_state_template = $task['form_state']
      ->getValue('maestro_template');
    if (isset($form_state_template)) {
      $template_machine_name = $form_state_template;
    }
    if ($template_machine_name != 'none' && $template_machine_name != '') {
      $template = MaestroEngine::getTemplate($template_machine_name);
      $form['maestro_sub_flow_label'] = [
        '#type' => 'link',
        '#title' => $this
          ->t('Chosen Template') . ': ' . $template
          ->label(),
        '#url' => Url::fromRoute('maestro_template_builder', [
          'templateMachineName' => $template_machine_name,
        ]),
        '#attributes' => [
          'class' => [
            'handler-help-message',
          ],
          'target' => '_new',
          'id' => [
            'handler-ajax-refresh-wrapper',
          ],
        ],
      ];
    }
    else {
      $form['maestro_sub_flow_label'] = [
        '#type' => 'html_tag',
        '#tag' => 'div',
        '#value' => $this
          ->t('Please choose a template.'),
        '#attributes' => [
          'class' => [
            'handler-help-message',
          ],
          'id' => [
            'handler-ajax-refresh-wrapper',
          ],
        ],
      ];
    }
    $form['maestro_sub_flow_settings'] = [
      '#type' => 'details',
      '#open' => TRUE,
      '#description' => $this
        ->t('Choose the variables you wish to send from the parent to the child. Variables will be prefixed with "maestro_parent_" when injected into the sub-process.'),
      '#title' => $this
        ->t('Variable Selection'),
    ];
    $template = MaestroEngine::getTemplate($templateMachineName);
    $form['maestro_sub_flow_settings']['variables'] = [];

    // Get all variables other than some of the exclusive Maestro variables.
    foreach ($template->variables as $var_name => $var_definition) {
      $form['maestro_sub_flow_settings']['variables']['variable_' . $var_name] = [
        '#type' => 'checkbox',
        '#title' => $var_name,
        '#default_value' => @array_key_exists($var_name, $task['data']['variables']) ? TRUE : FALSE,
        '#attributes' => [
          'autocomplete' => 'off',
        ],
      ];
    }
    $form['#cache'] = [
      'max-age' => 0,
    ];

    // Hi Firefox, I see you caching.
    $form['#attributes']['autocomplete'] = 'off';
    return $form;
  }

  /**
   * Implements callback for Ajax event on objective selection.
   *
   * @param array $form
   *   From render array.
   * @param \Drupal\Core\Form\FormStateInterface $form_state
   *   Current state of form.
   *
   * @return array
   *   Objective selection section of the form.
   */
  public function subFlowChoiceHandlerCallback(array $form, FormStateInterface &$form_state) {
    return $form['maestro_sub_flow_label'];
  }

  /**
   * {@inheritDoc}
   */
  public function validateTaskEditForm(array &$form, FormStateInterface $form_state) {
    $template = $form_state
      ->getValue('maestro_template');

    // Let's validate the handler here to ensure that it actually exists.
    if ($template == 'none') {
      $form_state
        ->setErrorByName('maestro_template', $this
        ->t('You must choose a template.'));
    }
  }

  /**
   * {@inheritDoc}
   */
  public function prepareTaskForSave(array &$form, FormStateInterface $form_state, array &$task) {
    $task['data']['maestro_template'] = $form_state
      ->getValue('maestro_template');

    // Now handle the variables.
    unset($task['data']['variables']);
    $all_values = $form_state
      ->getValues();
    foreach ($all_values as $key => $var) {
      if (strpos($key, 'variable_') === 0) {

        // Starts with 'variable_',so we know this is our variables.
        $is_checked = $form_state
          ->getValue($key);
        if ($is_checked) {

          // Strip of "variable_".
          $varname = substr($key, 9);

          // Signal that it's checked.
          $task['data']['variables'][$varname] = 1;
        }
      }
    }
  }

  /**
   * {@inheritDoc}
   */
  public function performValidityCheck(array &$validation_failure_tasks, array &$validation_information_tasks, array $task) {

    // So we know that we need a few keys in this $task array to even have a batch function run properly.
    // namely the handler.
    if (array_key_exists('maestro_template', $task['data']) && $task['data']['maestro_template'] == '' || !array_key_exists('maestro_template', $task['data'])) {
      $validation_failure_tasks[] = [
        'taskID' => $task['id'],
        'taskLabel' => $task['label'],
        'reason' => t('This task requires a Maestro Template to be chosen.'),
      ];
    }
  }

  /**
   * {@inheritDoc}
   */
  public function getTemplateBuilderCapabilities() {
    return [
      'edit',
      'drawlineto',
      'removelines',
      'remove',
    ];
  }

}

Classes

Namesort descending Description
MaestroSpawnSubFlowTask Maestro Spawn Sub Flow Task Plugin.