maestro-3.0.1-rc2/src/Plugin/EngineTasks/MaestroSpawnSubFlowTask.php
src/Plugin/EngineTasks/MaestroSpawnSubFlowTask.php
<?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 $this->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) {
$default_value = FALSE;
if(is_array($var_name) && array_key_exists($var_name, $task['data']['variables'])) {
$default_value = TRUE;
}
$form['maestro_sub_flow_settings']['variables']['variable_' . $var_name] = [
'#type' => 'checkbox',
'#title' => $var_name,
'#default_value' => $default_value,
'#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'];
}
}
