automatic_updates-8.x-2.x-dev/automatic_updates_extensions/src/Form/UpdaterForm.php

automatic_updates_extensions/src/Form/UpdaterForm.php
<?php

declare(strict_types=1);

namespace Drupal\automatic_updates_extensions\Form;

use Drupal\automatic_updates\Form\UpdateFormBase;
use Drupal\automatic_updates_extensions\BatchProcessor;
use Drupal\automatic_updates_extensions\ExtensionUpdateSandboxManager;
use Drupal\Core\Batch\BatchBuilder;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\State\StateInterface;
use Drupal\Core\Messenger\MessengerInterface;
use Drupal\Core\Render\RendererInterface;
use Drupal\Core\Url;
use Drupal\package_manager\ComposerInspector;
use Drupal\package_manager\Exception\FailureMarkerExistsException;
use Drupal\package_manager\FailureMarker;
use Drupal\package_manager\PathLocator;
use Drupal\package_manager\ProjectInfo;
use Drupal\package_manager\ValidationResult;
use Drupal\system\SystemManager;
use Drupal\update\ProjectRelease;
use Drupal\update\UpdateManagerInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;

/**
 * A form for selecting extension updates.
 *
 * @internal
 *   Form classes are internal and should not be used by external code.
 */
final class UpdaterForm extends UpdateFormBase {

  /**
   * {@inheritdoc}
   */
  public static function create(ContainerInterface $container) {
    return new static(
      $container->get(ExtensionUpdateSandboxManager::class),
      $container->get('event_dispatcher'),
      $container->get('renderer'),
      $container->get('state'),
      $container->get(FailureMarker::class),
      $container->get(ComposerInspector::class),
      $container->get(PathLocator::class),
    );
  }

  public function __construct(
    private readonly ExtensionUpdateSandboxManager $sandboxManager,
    private readonly EventDispatcherInterface $eventDispatcher,
    private readonly RendererInterface $renderer,
    private readonly StateInterface $state,
    private readonly FailureMarker $failureMarker,
    private readonly ComposerInspector $composerInspector,
    private readonly PathLocator $pathLocator,
  ) {}

  /**
   * {@inheritdoc}
   */
  public function getFormId() {
    return 'automatic_updates_extensions_updater_form';
  }

  /**
   * {@inheritdoc}
   */
  public function buildForm(array $form, FormStateInterface $form_state) {
    try {
      $this->failureMarker->assertNotExists();
    }
    catch (FailureMarkerExistsException $e) {
      $this->messenger()->addError($e->getMessage());
      return $form;
    }
    $update_projects = $this->getRecommendedModuleUpdates();
    $options = [];
    $recommended_versions = [];
    foreach ($update_projects as $project_name => $update_project) {
      switch ($update_project['status']) {
        case UpdateManagerInterface::NOT_SECURE:
        case UpdateManagerInterface::REVOKED:
          $status_message = $this->t('(Security update)');
          break;

        case UpdateManagerInterface::NOT_SUPPORTED:
          $status_message = $this->t('(Unsupported)');
          break;

        default:
          $status_message = '';
      }
      $project_release = ProjectRelease::createFromArray($update_project['releases'][$update_project['recommended']]);
      $options[$project_name] = [
        $update_project['title'] . $status_message,
        $update_project['existing_version'],
        $this->t(
          '@version (<a href=":url">Release notes</a>)',
          [
            '@version' => $project_release->getVersion(),
            ':url' => $project_release->getReleaseUrl(),
          ]),
      ];
      $recommended_versions[$project_name] = $update_project['recommended'];
    }
    $form['recommended_versions'] = [
      '#type' => 'value',
      '#value' => $recommended_versions,
    ];
    $form['projects'] = [
      '#type' => 'tableselect',
      '#header' => [
        $this->t('Project:'),
        $this->t('Current Version:'),
        $this->t('Update Version:'),
      ],
      '#options' => $options,
      '#empty' => $this->t('There are no available updates.'),
      '#attributes' => ['class' => ['update-recommended']],
      '#required' => TRUE,
      '#required_error' => t('Select one or more projects.'),
    ];

    if ($form_state->getUserInput()) {
      $results = [];
    }
    else {
      $results = $this->runStatusCheck($this->sandboxManager, $this->eventDispatcher);
    }
    $this->displayResults($results, $this->renderer);
    $security_level = ValidationResult::getOverallSeverity($results);

    if ($update_projects && $security_level !== SystemManager::REQUIREMENT_ERROR) {
      $form['actions'] = $this->actions($form_state);
    }

    $form['last_check'] = [
      '#theme' => 'update_last_check',
      '#last' => $this->state->get('update.last_check', 0),
    ];
    return $form;
  }

  /**
   * Builds the form actions.
   *
   * @param \Drupal\Core\Form\FormStateInterface $form_state
   *   The form state.
   *
   * @return mixed[][]
   *   The form's actions elements.
   */
  private function actions(FormStateInterface $form_state): array {
    $actions = ['#type' => 'actions'];
    if (!$this->sandboxManager->isAvailable()) {
      // If the form has been submitted do not display this error message
      // because ::deleteExistingUpdate() may run on submit. The message will
      // still be displayed on form build if needed.
      if (!$form_state->getUserInput()) {
        $this->messenger()->addError($this->t('Cannot begin an update because another Composer operation is currently in progress.'));
      }
      $actions['delete'] = [
        '#type' => 'submit',
        '#value' => $this->t('Delete existing update'),
        '#submit' => ['::deleteExistingUpdate'],
      ];
    }
    else {
      $actions['submit'] = [
        '#type' => 'submit',
        '#value' => $this->t('Update'),
      ];
    }
    return $actions;
  }

  /**
   * Submit function to delete an existing in-progress update.
   */
  public function deleteExistingUpdate(): void {
    $this->sandboxManager->destroy(TRUE);
    $this->messenger()->addMessage($this->t("Staged update deleted"));
  }

  /**
   * {@inheritdoc}
   */
  public function submitForm(array &$form, FormStateInterface $form_state) {
    $projects = $form_state->getValue('projects');
    $selected_projects = array_filter($projects);
    $recommended_versions = $form_state->getValue('recommended_versions');
    $selected_versions = array_intersect_key($recommended_versions, $selected_projects);
    $batch = (new BatchBuilder())
      ->setTitle($this->t('Downloading updates'))
      ->setInitMessage($this->t('Preparing to download updates'))
      ->addOperation(
        [BatchProcessor::class, 'begin'],
        [$selected_versions]
      )
      ->addOperation([BatchProcessor::class, 'stage'])
      ->setFinishCallback([BatchProcessor::class, 'finishStage'])
      ->toArray();

    batch_set($batch);
  }

  /**
   * Gets the modules that require updates.
   *
   * @return mixed[]
   *   Modules that require updates.
   */
  private function getRecommendedModuleUpdates(): array {
    $supported_project_types = [
      "module", "module-disabled", "theme", "theme-disabled",
    ];
    $available_updates = update_get_available(TRUE);
    if (empty($available_updates)) {
      $this->messenger()->addError('There was a problem getting update information. Try again later.');
      return [];
    }

    $all_projects_data = update_calculate_project_data($available_updates);
    $outdated_modules = [];
    $installed_packages = $this->composerInspector->getInstalledPackagesList($this->pathLocator->getProjectRoot());
    $non_supported_update_statuses = [];
    foreach ($all_projects_data as $project_name => $project_data) {
      if (in_array($project_data['project_type'], $supported_project_types, TRUE)) {
        if ($project_data['status'] !== UpdateManagerInterface::CURRENT) {
          if ($installed_packages->getPackageByDrupalProjectName($project_name) === NULL) {
            $non_supported_update_statuses[] = $project_data['status'];
            continue;
          }
          $project_information = new ProjectInfo($project_name);
          $installable_versions = array_keys($project_information->getInstallableReleases());
          if (!empty($project_data['recommended']) && in_array($project_data['recommended'], $installable_versions, TRUE)) {
            $outdated_modules[$project_name] = $project_data;
          }
          else {
            $non_supported_update_statuses[] = $project_data['status'];
          }
        }
      }
    }
    if ($non_supported_update_statuses) {
      $message_status = array_intersect([UpdateManagerInterface::NOT_SECURE, UpdateManagerInterface::NOT_SUPPORTED, UpdateManagerInterface::REVOKED], $non_supported_update_statuses) ?
        MessengerInterface::TYPE_ERROR :
        MessengerInterface::TYPE_STATUS;
      if ($outdated_modules) {
        $this->messenger()->addMessage(
          $this->t(
            'Other updates were found, but they must be performed manually. See <a href=":url">the list of available updates</a> for more information.',
            [
              ':url' => Url::fromRoute('update.status')->toString(),
            ]
          ),
          $message_status
        );
      }
      else {
        $this->messenger()->addMessage(
          $this->t(
            'Updates were found, but they must be performed manually. See <a href=":url">the list of available updates</a> for more information.',
            [
              ':url' => Url::fromRoute('update.status')->toString(),
            ]
          ),
          $message_status
        );
      }
    }
    return $outdated_modules;
  }

}

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

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