ifthenelse-8.x-1.x-dev-no-core/modules/contrib/if_then_else/src/IfthenelseRuleForm.php

modules/contrib/if_then_else/src/IfthenelseRuleForm.php
<?php

namespace Drupal\if_then_else;

use Drupal\Core\Url;
use Drupal\Core\Entity\EntityForm;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Messenger\MessengerInterface;
use Drupal\if_then_else\Event\EventConditionEvent;
use Drupal\if_then_else\Event\GraphValidationEvent;
use Drupal\if_then_else\Event\NodeValidationEvent;
use Drupal\if_then_else\Event\SocketSubscriptionEvent;
use Symfony\Component\DependencyInjection\ContainerInterface;
use function GuzzleHttp\json_decode;
use Drupal\if_then_else\Event\NodeSubscriptionEvent;

/**
 * Defines a class to build a ifthenelseRule entity form.
 *
 * @see \Drupal\if_then_else
 */
class IfthenelseRuleForm extends EntityForm {

  /**
   * Constructs an ExampleForm object.
   *
   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entityTypeManager
   *   The entityTypeManager.
   */
  public function __construct(EntityTypeManagerInterface $entityTypeManager) {
    $this->entityTypeManager = $entityTypeManager;
  }

  /**
   * {@inheritdoc}
   */
  public static function create(ContainerInterface $container) {
    return new static(
      $container->get('entity_type.manager')
    );
  }

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

    $form = parent::form($form, $form_state);

    $form['#prefix'] = '<div id="ifthenelse_form_wrapper">';
    $form['#suffix'] = '</div>';

    $socket_subscription_event = new SocketSubscriptionEvent();
    \Drupal::service('event_dispatcher')->dispatch(SocketSubscriptionEvent::EVENT_NAME, $socket_subscription_event);
    $form['#attached']['drupalSettings']['if_then_else']['sockets'] = $socket_subscription_event->sockets;

    // Current entity object.
    $entity = $this->entity;

    // Instantiate our event.
    $event = new NodeSubscriptionEvent();

    // Get the event_dispatcher server and dispatch the event.
    $event_dispatcher = \Drupal::service('event_dispatcher');
    $event_dispatcher->dispatch(NodeSubscriptionEvent::EVENT_NAME, $event);

    // Adding retejs intialization library.
    $form['#attached']['library'][] = 'if_then_else/initialize_retejs';
    $form['#attached']['library'][] = 'if_then_else/if_then_else';

    // Attach all libraries defined by all modules which are subscribing
    // above event.
    foreach ($event->nodes as $node_name => $node) {
      $node['name'] = $node_name;

      if (isset($node['library'])) {
        $form['#attached']['library'][] = $node['library'];
        unset($node['library']);
      }

      if ($node['type'] != 'event') {
        // Check if 'execute' input is defined.
        if (array_key_exists('inputs', $node) && array_key_exists('execute', $node['inputs'])) {
          // Make 'execute' key the first one.
          $execute['execute'] = $node['inputs']['execute'];
          unset($node['inputs']['execute']);
        }
        else {
          $execute['execute'] = [
            'label' => t('Execute'),
            'description' => t('Should the @type be executed?', ['@type' => $node['type']]),
            'sockets' => ['bool'],
          ];
        }

        if (array_key_exists('inputs', $node)) {
          $node['inputs'] = $execute + $node['inputs'];
        }
        else {
          $node['inputs'] = $execute;
        }
      }

      if ($node['type'] == 'action' || $node['type'] == 'event') {
        if (array_key_exists('outputs', $node) && array_key_exists('success', $node['outputs'])) {
          // Make 'success' key the first one.
          $success['success'] = $node['outputs']['success'];
          unset($node['outputs']['success']);
        }
        else {
          $success['success'] = [
            'label' => t('Success'),
            'description' => ($node['type'] == 'action') ? t("Did the action execute without any error? This socket can be used to chain actions by connecting to the next action's 'Execute' socket") : t("This socket will always be TRUE for an event and can be used to connect to a condition or action's 'Execute' socket."),
            'socket' => 'bool',
          ];

          if (!array_key_exists('outputs', $node)) {
            $node['outputs'] = [];
          }
        }

        if (array_key_exists('outputs', $node)) {
          $node['outputs'] = $success + $node['outputs'];
        }
        else {
          $node['outputs'] = $success;
        }
      }

      $form['#attached']['drupalSettings']['if_then_else']['nodes'][$node_name] = $node;
    }

    $form['#attached']['library'][] = 'if_then_else/render_retejs_canvas';

    $form['label'] = [
      '#type' => 'textfield',
      '#title' => t('Label'),
      '#required' => TRUE,
      '#default_value' => $entity->label(),
      '#weight' => 10,
      '#prefix' => '<div class="container-inline label_checkbox_section">',
    ];

    $form['id'] = [
      '#type' => 'machine_name',
      '#default_value' => $entity->id(),
      '#machine_name' => [
        'exists' => [$this, 'exist'],
      ],
      '#disabled' => !$entity->isNew(),
      '#weight' => 15,
    ];

    $form['active'] = [
      '#type' => 'checkbox',
      '#title' => t('Active'),
      '#default_value' => isset($entity->active) ? $entity->active : '',
      '#weight' => 20,
      '#suffix' => '</div>',
    ];
    $full_screen_img_path = drupal_get_path('module', 'if_then_else').'/css/images/full_screen.png';
    $form['full_screen_button'] = [
      '#type' => 'image_button',
      '#title' => t('Enable Full Screen'),
      '#src' => $full_screen_img_path,
      '#weight' => -1,
    ];
    // Container to create retejs editor and nodes.
    $form['rete-container'] = [
      '#type' => 'item',
      '#markup' => '<div class="editor"><div id="dock"></div><div class="container"><div id="rete-editor"></div></div></div>',
      '#weight' => -1,
    ];

    // Get values of usre input. It will be empty if form is not
    // submitted.
    $form_inputs = $form_state->getUserInput();
    if (!empty($form_inputs)) {
      $form['#attached']['drupalSettings']['if_then_else']['data'] = $form_inputs['data'];
    }
    else {
      $form['#attached']['drupalSettings']['if_then_else']['data'] = isset($entity->data) ? $entity->data : FALSE;
    }

    $form['module'] = [
      '#type' => 'hidden',
      '#title' => t('Module'),
      '#value' => 'ifthenelse',
      '#default_value' => isset($entity->module) ? $entity->module : '',
    ];

    // @todo: For now the event is hardcoded using javascript to form_alter
    // Need to make that dynamic based on what event node is added on retejs
    $form['event'] = [
      '#type' => 'hidden',
      '#title' => t('Module'),
      '#attributes' => [
        'id' => 'ifthenelse-event',
      ],
      '#default_value' => isset($entity->event) ? $entity->event : '',
    ];

    $form['data'] = [
      '#type' => 'hidden',
      '#title' => t('Module'),
      '#attributes' => [
        'id' => 'ifthenelse-data',
      ],
      '#default_value' => isset($entity->data) ? $entity->data : '',
    ];

    return $form;
  }

  /**
   * {@inheritdoc}
   */
  protected function actions(array $form, FormStateInterface $form_state) {
    $actions = parent::actions($form, $form_state);

    if (!$this->entity->isNew()) {
      $actions['clone'] = [
        '#type' => 'link',
        '#title' => t('Clone'),
        '#url' => $this->entity->toUrl('clone'),
        '#attributes' => ['class' => 'button button--danger', '#id'=>'test'],
      ];
    }

    return $actions;
  }

  /**
   * Validate form for ifthenelse rule entity.
   */
  public function validateForm(array &$form, FormStateInterface $form_state) {
    // If the flow is not active, there is no need to validate.
    if (!$form_state->getValue('active')) {
      $form_state->setValue('processed_data', '');
      return;
    }

    // Instantiate our event.
    $event = new NodeSubscriptionEvent();

    // Get the event_dispatcher server and dispatch the event.
    $event_dispatcher = \Drupal::service('event_dispatcher');
    $event_dispatcher->dispatch(NodeSubscriptionEvent::EVENT_NAME, $event);

    $data = json_decode($form_state->getValue('data'));

    $event_name = '';

    // Make sure that the graph has only one event node.
    foreach ($data->nodes as $nid => $node) {
      if ($node->data->type == 'event') {
        if (empty($event_name)) {
          $event_name = $node->data->name;
        }
        else {
          // There are two events in this graph. Set an error.
          $form_state->setErrorByName('rete-container', t('The graph has at least two event nodes. A graph should have exactly one event node.'));
          return;
        }
      }
    }

    if (empty($event_name)) {
      $form_state->setErrorByName('rete-container', t('The graph has no event node. Add one event node to indicate when the graph should execute.'));
      return;
    }

    $has_error = FALSE;

    // Validate each node.
    $node_errors = [];
    foreach ($data->nodes as $nid => $node) {
      $node_validation_event = new NodeValidationEvent($nid, $node);
      $event_dispatcher->dispatch('if_then_else_' . $node->data->name . '_node_validation_event', $node_validation_event);
      if (!empty($node_validation_event->errors)) {
        $node_errors = array_merge($node_errors, $node_validation_event->errors);
      }
    }

    if (!empty($node_errors)) {
      $content = [
        '#theme' => 'item_list',
        '#list_type' => 'ul',
        '#items' => $node_errors,
        '#attributes' => ['class' => 'error-list ifthenelse error'],
        '#wrapper_attributes' => ['class' => 'container'],
      ];
      $form_state->setErrorByName('rete-container', $content);
      return;
    }

    $precedent_nids = [];
    $dependent_nids = [];
    $execution_order = [];

    $event_condition = '';

    $has_error = FALSE;

    // Get execution order of precedent and dependent nodes and event condition.
    // Show an error if a required input is not connected.
    foreach ($data->nodes as $nid => $node) {
      $inputs = $node->inputs;
      $outputs = $node->outputs;

      if ($node->data->type == 'event') {
        $event_condition_event = new EventConditionEvent($node->data);
        $event_dispatcher->dispatch('if_then_else_' . $node->data->name . '_event_condition_event', $event_condition_event);
        $event_condition = implode(',', $event_condition_event->conditions);
      }

      $defined_inputs = array_key_exists('inputs', $event->nodes[$node->data->name]) ? $event->nodes[$node->data->name]['inputs'] : [];
      foreach ($defined_inputs as $input_name => $defined_input) {
        if (in_array('required', $defined_input) && $defined_input['required'] && !count($node->inputs->{$input_name}->connections)) {
          // A required input is not connected.
          $form_state->setErrorByName('rete-container', t('Required input "@input_name" of "@node_name" is not connected.', ['@input_name' => $defined_input['label'], '@node_name' => $node->name]));
        }
      }

      $dependent_nids[$nid] = [];
      $precedent_nids[$nid] = [];

      if (!empty((array) $inputs)) {
        foreach ($inputs as $socket_name => $input) {
          foreach ($input->connections as $precedent_node_output_object) {
            if (!in_array($precedent_node_output_object->node, $precedent_nids[$nid])) {
              $precedent_nids[$nid][] = $precedent_node_output_object->node;
            }
          }
        }
      }

      if (!empty((array) $outputs)) {
        foreach ($outputs as $socket_name => $output) {
          foreach ($output->connections as $dependent_node_input_object) {
            if (!in_array($dependent_node_input_object->node, $dependent_nids[$nid])) {
              $dependent_nids[$nid][] = $dependent_node_input_object->node;
            }
          }
        }
      }
    }

    if ($has_error) {
      return;
    }

    $form_state->setValue('event', $event_name);
    $form_state->setValue('condition', $event_condition);

    // Validate the full graph.
    $graph_errors = [];
    foreach ($data->nodes as $nid => $node) {
      $graph_validation_event = new GraphValidationEvent($data);
      $event_dispatcher->dispatch('if_then_else_' . $node->data->name . '_graph_validation_event', $graph_validation_event);
      if (!empty($graph_validation_event->errors)) {
        $graph_errors = array_merge($graph_errors, $graph_validation_event->errors);
      }
    }

    if (!empty($graph_errors)) {
      $content = [
        '#theme' => 'item_list',
        '#list_type' => 'ul',
        '#items' => $graph_errors,
        '#attributes' => ['class' => 'error-list ifthenelse error'],
        '#wrapper_attributes' => ['class' => 'container'],
      ];
      $form_state->setErrorByName('rete-container', $content);
      return;
    }

    // Compute execution order of nodes.
    while (count($execution_order) < count((array) $data->nodes)) {
      $node_processed = FALSE;

      foreach ($data->nodes as $nid => $node) {
        if (in_array($nid, $execution_order)) {
          continue;
        }

        $all_inputs_available = TRUE;
        foreach ($precedent_nids[$nid] as $precedent_nid) {
          if (!in_array($precedent_nid, $execution_order)) {
            $all_inputs_available = FALSE;
            break;
          }
        }

        if (!$all_inputs_available) {
          continue;
        }

        // Found a node which could be processed now.
        if (isset($node->data->class)) {
          $class = str_replace('//', '/', $node->data->class);

          if (class_exists($class)) {
            $execution_order[] = $nid;
            $node_processed = TRUE;
          }
        }
      }

      if (!$node_processed) {
        // There seems to be circular dependency.
        $form_state->setErrorByName('rete-container', t('There seems to be circular dependency in the flow.'));
        return;
      }
    }

    // Iterate over all the nodes in execution order and fill precedent nodes.
    foreach ($execution_order as $nid) {
      $current_precedent_nids = [];
      foreach ($precedent_nids[$nid] as $precedent_nid) {
        $current_precedent_nids = array_merge($current_precedent_nids, $precedent_nids[$precedent_nid]);
      }
      $precedent_nids[$nid] = array_unique(array_merge($precedent_nids[$nid], $current_precedent_nids));
    }

    // Iterate over all the nodes in reverse execution order and fill dependent
    // nodes.
    foreach (array_reverse($execution_order) as $nid) {
      $current_dependent_nids = [];
      foreach ($dependent_nids[$nid] as $dependent_nid) {
        $current_dependent_nids = array_merge($current_dependent_nids, $dependent_nids[$dependent_nid]);
      }
      $dependent_nids[$nid] = array_unique(array_merge($dependent_nids[$nid], $current_dependent_nids));
    }

    $processed_data = [
      'execution_order' => $execution_order,
      'precedent_nids' => $precedent_nids,
      'dependent_nids' => $dependent_nids,
    ];

    $form_state->setValue('processed_data', serialize($processed_data));
  }

  /**
   * Save function for saving ifthenelse rule entity.
   */
  public function save(array $form, FormStateInterface $form_state) {
    $entity = $this->entity;
    $status = $entity->save();

    if ($status) {
      $this->messenger()->addMessage($this->t('Saved the %label Example.', [
        '%label' => $entity->label(),
      ]));
    }
    else {
      $this->messenger()->addMessage($this->t('The %label Example was not saved.', [
        '%label' => $entity->label(),
      ]), MessengerInterface::TYPE_ERROR);
    }

    // Redirect to Rule list page.
    $form_state->setRedirectUrl($this->entity->toUrl('collection'));
  }

  /**
   * Helper function to check whether an Example configuration entity exists.
   */
  public function exist($id) {
    $entity = $this->entityTypeManager->getStorage('ifthenelserule')->getQuery()
      ->condition('id', $id)
      ->execute();
    return (bool) $entity;
  }

}

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

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