foldershare-8.x-1.2/src/Form/CommandFormWrapper.php

src/Form/CommandFormWrapper.php
<?php

namespace Drupal\foldershare\Form;

use Drupal\Core\Url;
use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Ajax\AjaxResponse;
use Drupal\Core\Ajax\CloseModalDialogCommand;
use Drupal\Core\Ajax\InvokeCommand;
use Drupal\Core\Ajax\RedirectCommand;

use Symfony\Component\DependencyInjection\ContainerInterface;

use Drupal\foldershare\Constants;
use Drupal\foldershare\Utilities\FormatUtilities;
use Drupal\foldershare\Plugin\FolderShareCommandManager;
use Drupal\foldershare\Plugin\FolderShareCommand\FolderShareCommandInterface;
use Drupal\foldershare\Ajax\OpenErrorDialogCommand;
use Drupal\foldershare\Entity\Exception\RuntimeExceptionWithMarkup;

/**
 * Creates a form that wraps a command plugin to prompt for its parameters.
 *
 * This form is invoked to prompt the user for additional plugin
 * command-specific parameters, beyond primary parameters for the parent
 * folder (if any), destination folder (if any), and selection (if any).
 * Each plugin has its own (optional) form fragment that prompts for its
 * parameters. This form page incorporates that fragment and adds a
 * page title and submit button. It further orchestrates creation of
 * the plugin and invokation of its functions.
 *
 * The form page accepts a URL with a single required 'encoded' parameter
 * that includes a base64-encoded JSON-encoded associative array that
 * has the plugin ID, configuration (parent, destination, selection),
 * and URL to get back to the invoking page.
 *
 * <B>Warning:</B> This class is strictly internal to the FolderShare
 * module. The class's existance, name, and content may change from
 * release to release without any promise of backwards compatability.
 *
 * @ingroup foldershare
 */
class CommandFormWrapper extends FormBase {

  /*--------------------------------------------------------------------
   *
   * Fields - cached from dependency injection.
   *
   *--------------------------------------------------------------------*/

  /**
   * The plugin manager for folder shared commands.
   *
   * @var \Drupal\foldershare\FolderShareCommand\FolderShareCommandManager
   */
  protected $commandPluginManager;

  /**
   * The command's plugin ID from command parameters.
   *
   * The plugin ID is a unique string that identifies the plugin within
   * the set of loaded plugins in the plugin manager.
   *
   * @var string
   */
  protected $pluginId;

  /**
   * An instance of the command plugin.
   *
   * The command instance is created using the plugin ID from the
   * URL parameters.
   *
   * @var \Drupal\foldershare\FolderShareCommand\FolderShareCommandInterface
   */
  protected $command;

  /**
   * The URL to redirect to (or back to) after form submission.
   *
   * The URL comes from the command parameters and indicates the original
   * page to return to after the command executes.
   *
   * @var string
   */
  protected $url;

  /**
   * The parent form ID that triggered the command.
   *
   * The parent form ID comes from the command parameters and indicates the
   * parent form ID that triggered the command. This is used to direct an
   * AJAX refresh back to that form when the command finishes.
   *
   * @var string
   */
  protected $parentFormId;

  /**
   * Whether to enable AJAX.
   *
   * The flag comes from the command parameters given to buildForm().
   * By default, this is FALSE, but it can be set TRUE if the caller
   * sets the flag in the parameters. This would be the case if the
   * caller has embedded the form within an AJAX dialog and therefore
   * requires that this form continue to use AJAX as well.
   *
   * @var bool
   */
  protected $enableAjax;

  /*--------------------------------------------------------------------
   *
   * Construction.
   *
   *--------------------------------------------------------------------*/

  /**
   * Constructs a new form.
   *
   * @param \Drupal\foldershare\FolderShareCommand\FolderShareCommandManager $commandPluginManager
   *   The command plugin manager.
   */
  public function __construct(FolderShareCommandManager $commandPluginManager) {
    // Save the plugin manager, which we'll need to create a plugin
    // instance based upon a parameter on the form's URL.
    $this->commandPluginManager = $commandPluginManager;
    $this->enableAjax = TRUE;
  }

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

  /*--------------------------------------------------------------------
   *
   * Form setup.
   *
   *--------------------------------------------------------------------*/

  /**
   * {@inheritdoc}
   */
  public function getFormId() {
    return str_replace('\\', '_', get_class($this));
  }

  /*--------------------------------------------------------------------
   *
   * Form build.
   *
   *--------------------------------------------------------------------*/

  /**
   * {@inheritdoc}
   */
  public function buildForm(
    array $form,
    FormStateInterface $formState = NULL,
    string $encoded = NULL) {

    //
    // Decode parameters.
    // ------------------
    // Decode the incoming URL's parameters, which include:
    // - The command plugin's ID.
    // - The configuration for that command, including a selection, parent,
    //   destination, etc.
    // - The URL of the page to return to.
    // - Whethe AJAX is enabled.
    $parameters = $this->decodeParameters($encoded);
    if (empty($parameters) === TRUE) {
      return $form;
    }

    $this->pluginId     = $parameters['pluginId'];
    $configuration      = $parameters['configuration'];
    $this->url          = $parameters['url'];
    $this->parentFormId = $parameters['parentFormId'];

    if (isset($parameters['enableAjax']) === TRUE) {
      $this->enableAjax = $parameters['enableAjax'];
    }
    else {
      $this->enableAjax = FALSE;
    }

    $forPage = ($this->enableAjax === FALSE);

    //
    // Instantiate plugin.
    // -------------------
    // The plugin ID gives us the name of the plugin. Create one and pass
    // in the configuration. This initializes the plugin's internal
    // structure, but does not validate or execute it. The plugin's form
    // below may prompt the user for additional configuration values.
    $this->command = $this->commandPluginManager->createInstance(
      $this->pluginId,
      $configuration);
    $def = $this->command->getPluginDefinition();

    //
    // Pre-validate.
    // -------------
    // The invoker of this form should already have checked that the
    // parent and selection are valid for this command. But validate
    // again now to insure that internal cached values from validation
    // are set before we continue. If validation fails, there is no
    // point in building much of a form.
    try {
      $this->command->validateParentConstraints();
      $this->command->validateSelectionConstraints();
    }
    catch (RuntimeExceptionWithMarkup $e) {
      $this->messenger()->addMessage($e->getMarkup(), 'error');
      return NULL;
    }
    catch (\Exception $e) {
      $this->messenger()->addMessage($e->getMessage(), 'error');
      return NULL;
    }

    //
    // Set up form.
    // ------------
    // The command provides its own form fragment to prompt for the
    // values it needs to complete its configuration.
    $commandFormWrapper = Constants::MODULE . '_command_form_wrapper';
    $form['#prefix'] = '<div id="' . $commandFormWrapper . '">';
    $form['#suffix'] = '</div>';

    // Attach libraries.
    $form['#attached']['library'][] = Constants::LIBRARY_MODULE;
    $form['#attributes']['class'][] = $def['id'];
    $form['#attributes']['class'][] = 'foldershare-dialog';

    // Mark form as tree-structured and non-collapsable.
    $form['#tree'] = TRUE;

    //
    // Set the page title, if any.
    // ---------------------------
    // Get the command's page title. This may vary based on whether the
    // form is in a stand-alone page or in a dialog. The title may be empty.
    $form['#title'] = $this->command->getTitle($forPage);

    //
    // Set the description, if any.
    // ----------------------------
    // Get the command's description. This may vary based on whether the
    // form is in a stand-alone page or in a dialog.
    $description = $this->command->getDescription($forPage);
    if (empty($description) === FALSE) {
      if (is_array($description) === FALSE) {
        $primaryDescription = $description;
        $secondaryDescription = '';
      }
      elseif (count($description) === 1) {
        $primaryDescription = $description[0];
        $secondaryDescription = '';
      }
      else {
        $primaryDescription = $description[0];
        $secondaryDescription = $description[1];
      }

      if (empty($primaryDescription) === FALSE) {
        $form['primary-description'] = [
          '#type'   => 'html_tag',
          '#name'   => 'primary-description',
          '#weight' => -100,
          '#tag'    => 'p',
          '#value'  => $primaryDescription,
          '#attributes' => [
            'class' => [
              'foldershare-primary-description',
            ],
          ],
        ];
      }

      if (empty($secondaryDescription) === FALSE) {
        $form['secondary-description'] = [
          '#type'   => 'html_tag',
          '#name'   => 'secondary-description',
          '#weight' => -99,
          '#tag'    => 'p',
          '#value'  => $secondaryDescription,
          '#attributes' => [
            'class' => [
              'foldershare-secondary-description',
            ],
          ],
        ];
      }
    }

    //
    // Add actions.
    // ------------
    // Add the submit button.
    $form['actions'] = [
      '#type'          => 'actions',
      '#weight'        => 1000,
      'submit'         => [
        '#type'        => 'submit',
        '#name'        => 'submit',
        '#value'       => $this->command->getSubmitButtonName(),
        '#button_type' => 'primary',
      ],
    ];

    // When AJAX is enabled, add an AJAX callback to the submit button
    // and a cancel button.
    if ($this->enableAjax === TRUE) {
      $form['actions']['cancel'] = [
        '#type'  => 'button',
        '#name'  => 'cancel',
        '#value' => $this->t('Cancel'),
        '#attributes' => [
          'class' => [
              // Marking the button with "dialog-cancel" marks the button as
              // a cancel button that simply closes the dialog, without sending
              // anything to the server.
            'dialog-cancel',
          ],
        ],
      ];

      $form['actions']['submit']['#ajax'] = [
        'callback'   => [$this, 'submitFormAjax'],
        'event'      => 'click',
        'wrapper'    => $commandFormWrapper,
        'disable-refocus' => TRUE,
        'url'        => Url::fromRoute(
          'entity.foldersharecommand.plugin',
          [
            'encoded' => base64_encode(json_encode($parameters)),
          ]),
        'options'    => [
          'query'    => [
            'ajax_form' => 1,
          ],
        ],
        'progress'   => [
          'type'     => 'none',
        ],
      ];
    }

    //
    // Add command form.
    // -----------------
    // Add the command's form.
    $form = $this->command->buildConfigurationForm($form, $formState);

    return $form;
  }

  /*--------------------------------------------------------------------
   *
   * Form validate.
   *
   *--------------------------------------------------------------------*/

  /**
   * {@inheritdoc}
   */
  public function validateForm(array &$form, FormStateInterface $formState) {
    // Validate the user's input for the form by passing them to the
    // command to validate.
    if ($this->command === NULL) {
      // Fail. No command?
      return;
    }

    //
    // Validate additions
    // ------------------
    // Let the command's form validate the user's input.
    $this->command->validateConfigurationForm($form, $formState);
    if ($formState->hasAnyErrors() === TRUE) {
      // The command's validation failed. There is no point in continuing.
      return;
    }

    if ($formState->isRebuilding() === TRUE) {
      // The command directs that the form should be rebuilt and form use
      // should continue.
      return;
    }

    //
    // Validate rest
    // -------------
    // The command's configuration form should have validated whatever
    // additional parameters it has added. And earlier we validated the
    // parent and selection. But just to be sure, validate everything
    // one last time. Since validation skips work if it has already
    // been done, this will be fast if everything is validated fine already.
    try {
      $this->command->validateConfiguration();
    }
    catch (RuntimeExceptionWithMarkup $e) {
      $this->messenger()->addMessage($e->getMarkup(), 'error');
    }
    catch (\Exception $e) {
      // Validation failed. This should not be possible if all prior
      // validation checks occurred as they were supposed to.
      $this->messenger()->addMessage($e->getMessage(), 'error');
    }
  }

  /*--------------------------------------------------------------------
   *
   * Form submit.
   *
   *--------------------------------------------------------------------*/

  /**
   * {@inheritdoc}
   */
  public function submitForm(array &$form, FormStateInterface $formState) {
    if ($this->enableAjax === TRUE) {
      return;
    }

    switch ($formState->getTriggeringElement()['#name']) {
      default:
      case 'cancel':
        // On cancel, return to the starting page. On anything else (there
        // shouldn't be anything), update the page as well.
        //
        // This chould never occur. The dialogs are marked so that cancel
        // automatically takes down the dialog.
        if ($this->url !== NULL) {
          $formState->setRedirectUrl(Url::fromUri($this->url));
        }
        break;

      case 'submit':
        // On submit, execute command, then return to the starting page.
        if ($this->command !== NULL) {
          $this->command->submitConfigurationForm($form, $formState);
        }

        if ($this->url !== NULL) {
          $formState->setRedirectUrl(Url::fromUri($this->url));
        }
        break;
    }
  }

  /**
   * {@inheritdoc}
   */
  public function submitFormAjax(array &$form, FormStateInterface $formState) {
    $response = new AjaxResponse();

    if ($formState->isRebuilding() === TRUE) {
      return $form;
    }

    switch ($formState->getTriggeringElement()['#name']) {
      case 'cancel':
        // On cancel, close the dialog and return to the starting page.
        //
        // This chould never occur. The dialogs are marked so that cancel
        // automatically takes down the dialog.
        $response->addCommand(new CloseModalDialogCommand());
        return $response;

      case 'submit':
        // On submit, execute command, then close dialog and return
        // to the starting page.
        if ($this->command === NULL) {
          // This should not be possible.
          $response->addCommand(new CloseModalDialogCommand());
          return $response;
        }
        break;

      default:
        // On any unrecognized trigger, the best we can do is force a
        // refresh of the page after taking down the dialog.
        // Redirect to a new page.
        $response->addCommand(new CloseModalDialogCommand());
        if ($this->url !== NULL) {
          $response->addCommand(new RedirectCommand($this->url));
        }
        else {
          $url = Url::fromRoute('<current>');
          $response->addCommand(new RedirectCommand($url->toString()));
        }
        return $response;
    }

    //
    // Execute.
    // --------
    // The command doesn't need more operands. Validate, check permissions,
    // then execute. Failures either set form element errors or set page
    // errors. Pull out those errors and report them.
    $this->messenger()->deleteAll();
    try {
      $this->command->validateConfiguration();
      if ($this->command->isValidated() === TRUE) {
        $this->command->submitConfigurationForm($form, $formState);
      }
    }
    catch (AccessDeniedHttpException $e) {
      $this->messenger()->addMessage(FormatUtilities::createFormattedMessage(
        $this->t('You do not have sufficient permissions.'),
        $this->t('The operation could not be completed.')),
        'error');
    }
    catch (NotFoundHttpException $e) {
      $this->messenger()->addMessage(FormatUtilities::createFormattedMessage(
        $this->t('One or more items could not be found.'),
        $this->t('The operation could not be completed.')),
        'error');
    }
    catch (RuntimeExceptionWithMarkup $e) {
      $this->messenger()->addMessage($e->getMarkup(), 'error');
    }
    catch (\Exception $e) {
      $this->messenger()->addMessage($e->getMessage(), 'error');
    }

    $response->addCommand(new CloseModalDialogCommand());

    // If there are any errors, present them in an error dialog.
    $msgs = $this->messenger()->all();
    if (isset($msgs['error']) === TRUE ||
        isset($msgs['warning']) === TRUE) {
      $cmd = new OpenErrorDialogCommand($this->t('Problem'));
      $cmd->setFromPageMessages();
      $response->addCommand($cmd);
    }
    elseif ($formState->hasAnyErrors() === TRUE) {
      $cmd = new OpenErrorDialogCommand($this->t('Problem'));
      $cmd->setFromFormErrors($formState);
      $response->addCommand($cmd);
    }
    else {
      // Otherwise the command executed correctly. Handle the post-execute
      // behaviors.
      $executeBehavior = $this->command->getExecuteBehavior();
      switch ($executeBehavior) {
        default:
        case FolderShareCommandInterface::POST_EXECUTE_PAGE_REFRESH:
        case FolderShareCommandInterface::POST_EXECUTE_PAGE_REDIRECT:
          // Redirect to a new page.
          if ($this->url !== NULL) {
            $response->addCommand(new RedirectCommand($this->url));
          }
          else {
            $url = Url::fromRoute('<current>');
            $response->addCommand(new RedirectCommand($url->toString()));
          }
          break;

        case FolderShareCommandInterface::POST_EXECUTE_VIEW_REFRESH:
          // Refresh the view associated with the menu the command came from.
          $selector = '#' . $this->parentFormId;
          $method   = 'trigger';
          $args     = [
            'FolderShareRefreshView',
          ];

          $response->addCommand(new InvokeCommand($selector, $method, $args));
          break;
      }
    }

    $this->command = NULL;

    return $response;
  }

  /*--------------------------------------------------------------------
   *
   * Utilities.
   *
   *--------------------------------------------------------------------*/

  /**
   * Decodes plugin information passed to the form via its URL.
   *
   * @param string $encoded
   *   The base64 encoded string included as a parameter on the form URL.
   */
  private function decodeParameters(string $encoded) {
    //
    // The incoming parameters are an encoded associative array that provides
    // the plugin ID, configuration, and redirect URL. Encoding has expressed
    // the array as a base64 string so that it could be included as a
    // URL parameter.  Decoding reverses the base64 encode, and a JSON decode
    // to return an object, which we cast to an array as needed.
    //
    // Decode parameters.
    $parameters = (array) json_decode(base64_decode($encoded), TRUE);

    if (empty($parameters) === TRUE) {
      // Fail. No parameters?
      //
      // This should not be possible because the route requires that there
      // be parameters. At a minimum, we need the command plugin ID.
      $this->messenger()->addMessage($this->t(
        "Communications problem.\nThe '@operation' command could not be completed because it is missing one or more required parameters.",
        [
          '@operation' => $this->command->label(),
        ]),
        'error');
      return [];
    }

    // Get plugin ID.
    if (empty($parameters['pluginId']) === TRUE) {
      // Fail. No plugin ID?
      //
      // The parameters are malformed and missing the essential plugin ID.
      $this->messenger()->addMessage($this->t(
        "Communications problem.\nThe '@operation' command could not be completed because it is missing one or more required parameters.",
        [
          '@operation' => $this->command->label(),
        ]),
        'error');
      return [];
    }

    // Get initial configuration.
    if (isset($parameters['configuration']) === FALSE) {
      // Fail. No configuration?
      //
      // The parameters are malformed and missing the essential configuration.
      $this->messenger()->addMessage($this->t(
        "Communications problem.\nThe '@operation' command could not be completed because it is missing one or more required parameters.",
        [
          '@operation' => $this->command->label(),
        ]),
        'error');
      return [];
    }

    // Insure the configuration is an array.
    $parameters['configuration'] = (array) $parameters['configuration'];

    return $parameters;
  }

}

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

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