editoria11y-1.0.0-alpha8/modules/editoria11y_csa/src/Form/DashboardActions.php

modules/editoria11y_csa/src/Form/DashboardActions.php
<?php

declare(strict_types=1);

namespace Drupal\editoria11y_csa\Form;

use Drupal\Core\Batch\BatchBuilder;
use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Link;
use Drupal\Core\Url;
use Drupal\views\Views;

/**
 * Provides a Editoria11y form.
 */
final class DashboardActions extends FormBase {

  /**
   * {@inheritdoc}
   */
  public function getFormId(): string {
    return 'editoria11y_csa_dashboard_actions';
  }

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

    $crawler = Views::getView('editoria11y_crawler');
    if ($crawler) {
      $crawler->setDisplay('crawl');
      $url = $crawler->getUrl();
      $form['Manage results'] = [
        '#type' => 'fieldset',
        '#title' => t('Refresh results'),
        [
          '#theme' => 'item_list',
          '#list_type' => 'ul',
          '#items' => [
            [
              '#type' => 'link',
      // @todo query param to not load images and drop stale results.
              '#title' => t('Set up a site-wide recheck'),
              '#url' => $url,
            ],
          ],
        ],
      ];
    }

    $exporter = Views::getView('editoria11y_export');
    if ($exporter) {
      $exporter->setDisplay('pages');
      $pageUrl = $exporter->getUrl();
      $exporter->setDisplay('dismissals');
      $dismissUrl = $exporter->getUrl();
      $exporter->setDisplay('results');
      $alertUrl = $exporter->getUrl();

      $form['export'] = [
        '#type' => 'fieldset',
        '#title' => t('Export'),
        [
          '#theme' => 'item_list',
          '#list_type' => 'ul',
          '#items' => [
            [
              '#type' => 'link',
              '#title' => t('Export pages'),
              '#url' => $pageUrl,
            ],
            [
              '#type' => 'link',
              '#title' => t('Export dismissals'),
              '#url' => $dismissUrl,
            ],
            [
              '#type' => 'link',
              '#title' => t('Export alerts'),
              '#url' => $alertUrl,
            ],
          ],
        ],
      ];

    }

    $pageView = Link::createFromRoute(
      $this->t('Paths with parameters'),
      'view.editoria11y_pages.page_1',
      ['page_path' => '?']
    )->toString();

    $form['maintenance'] = [
      '#type' => 'fieldset',
      '#title' => 'Routine maintenance',
    ];

    $form['maintenance']['check_deleted'] = [
      '#title' => $this->t("Check for deleted pages"),
      '#type' => 'checkbox',
      '#default_value' => TRUE,
      '#description' => $this->t('Removes results for pages with invalid paths or entity references.'),
    ];

    // @todo remove stale dismissals.
    /*
    $form['maintenance']['remove_stale'] = [
    '#title' => $this->t("Check for stale dismissals"),
    '#type' => 'checkbox',
    '#default_value' => TRUE,
    '#description' => $this->t('Discards dismissals for alerts where the element disappeared or was fixed more than a year ago.'),
    ];
     */

    $form['maintenance']['update_titles'] = [
      '#title' => $this->t("Update recorded titles"),
      '#type' => 'checkbox',
      '#default_value' => FALSE,
      '#description' => $this->t('Replaces recorded titles for Nodes, Terms and Users with the value on their edit page.'),
    ];

    $form['maintenance']['update_paths'] = [
      '#title' => $this->t("Update recorded paths"),
      '#type' => 'checkbox',
      '#default_value' => FALSE,
      '#description' => $this->t('Replaces recorded URLs for Nodes, Terms and Users with their current canonical path. @ReviewPaths (node/1?node=2) will not be updated.', ['@ReviewPaths' => $pageView]),
    ];

    $form['maintenance']['danger'] = [
      '#title' => $this->t('Risky deletions'),
      '#type' => 'details',
    ];

    $form['maintenance']['danger']['remove_pages_with_params'] = [
      '#title' => $this->t("Delete all alerts for paths with parameters"),
      '#type' => 'select',
      '#options' => [
        'none' => $this->t('None'),
        'entities' => $this->t('Only for nodes, terms & users'),
        'all' => $this->t('All, including search and views'),
      ],
      '#default_value' => 'none',
      '#description' => $this->t('<strong>There is no undo</strong>. @ReviewPaths should be reviewed before proceeding.', ['@ReviewPaths' => $pageView]),
    ];

    $form['maintenance']['actions'] = [
      '#type' => 'actions',
      'submit' => [
        '#type' => 'submit',
        '#value' => $this->t('Start'),
      ],
    ];

    return $form;
  }

  /**
   * {@inheritdoc}
   */
  public function validateForm(array &$form, FormStateInterface $form_state): void {
    // @todo Validate the form here.
    // Example:
    // @code
    //   if (mb_strlen($form_state->getValue('message')) < 10) {
    //     $form_state->setErrorByName(
    //       'message',
    //       $this->t('Message should be at least 10 characters.'),
    //     );
    //   }
    // @endcode
  }

  /**
   * Compares Editoria11y DB tables to Drupal tables and routes.
   *
   * @param int $batch_id
   *   Incremented integer.
   * @param int $batch_size
   *   Records to process.
   * @param int $max
   *   The last batch_id.
   * @param array $form_values
   *   User choices for the batch process.
   * @param array $context
   *   The Drupal batch environment variables.
   */
  public static function batchProcess(int $batch_id, int $batch_size, int $max, array $form_values, array &$context): void {
    if (!isset($context['results']['progress'])) {
      // First round; initialize variables.
      $context['results']['last_record'] = 0;
      $context['results']['max'] = $max;
      $context['results']['updated'] = 0;
      $context['results']['deleted'] = 0;
      $context['results']['progress'] = 0;
      $context['results']['process'] = 'Form batch completed';
    }

    // Message above progress bar.
    $context['message'] = t('Processing batch #@batch_id of @count.', [
      '@batch_id' => number_format($batch_id),
      '@count' => number_format($context['results']['max']),
    ]);

    // @phpstan-ignore-next-line
    $database = \Drupal::database();
    $query = $database->select('editoria11y_pages');
    $query->fields(
      'editoria11y_pages',
      [
        'pid',
        'page_path',
        'page_language',
        'entity_id',
        'entity_type',
        'page_title',
        'route_name',
      ],
    );
    $query->leftJoin(
      'node_field_data',
      'node_field_data',
      "editoria11y_pages.route_name = 'entity.node.canonical' AND node_field_data.nid = editoria11y_pages.entity_id AND node_field_data.langcode = editoria11y_pages.page_language"
    );
    $query->fields('node_field_data',
      [
        'nid',
        'title',
      ],
    );
    $query->leftJoin(
      'users_field_data',
      'users_field_data',
      "editoria11y_pages.route_name = 'entity.user.canonical' AND users_field_data.uid = editoria11y_pages.entity_id AND users_field_data.langcode = editoria11y_pages.page_language"
    );
    $query->fields('users_field_data',
      [
        'uid',
        'name',
      ],
    );
    $query->leftJoin(
      'taxonomy_term_field_data',
      'taxonomy_term_field_data',
      "editoria11y_pages.route_name = 'entity.taxonomy_term.canonical' AND taxonomy_term_field_data.tid = editoria11y_pages.entity_id AND taxonomy_term_field_data.langcode = editoria11y_pages.page_language"
    );
    $query->fields('taxonomy_term_field_data',
      [
        'tid',
        'name',
      ],
    );
    $query->orderBy('pid');
    $query->condition('pid', $context['results']['last_record'], '>');
    $query->range(0, $batch_size);
    $results = $query->execute()->fetchAll();

    $pages_to_delete = [];
    $paths_to_update = [];
    $titles_to_update = [];
    $counter = 0;
    $count = count($results);

    $path_validator = \Drupal::service('path.validator');

    foreach ($results as $record) {
      $counter++;
      $path_params = FALSE;
      if (str_contains($record->page_path, '?')) {
        $path_params = explode("?", $record->page_path, 2)[1];
      }

      switch ($record->route_name) {
        case 'entity.node.canonical':
          if (empty($record->nid) && $form_values['check_deleted']
          ) {
            $pages_to_delete[] = $record->pid;
            break;
          }
          elseif (
            $path_params &&
            $form_values['remove_pages_with_params'] !== 'none'
            ) {
            $pages_to_delete[] = $record->pid;
            break;
          }

          if ($form_values['update_titles'] === 1 &&
            $record->page_title !== $record->title
          ) {
            $titles_to_update[$record->pid] = $record->title;
          }

          if ($form_values['update_paths'] === 1) {
            $internal_path = '/node/' . $record->entity_id;
            $alias = \Drupal::service('path_alias.manager')->getAliasByPath($internal_path, $record->page_language);
            if ($path_params) {
              $alias = $alias . '?' . $path_params;
            }
            if (
              $alias !== $record->page_path &&
              $alias !== str_replace('/' . $record->page_language, '', $record->page_path)
            ) {
              $paths_to_update[$record->pid] = $alias;
            }
          }

          break;

        case 'entity.taxonomy_term.canonical':
          if (empty($record->tid) && $form_values['check_deleted']
          ) {
            $pages_to_delete[] = $record->pid;
            break;
          }
          elseif (
            $path_params &&
            $form_values['remove_pages_with_params'] !== 'none'
          ) {
            $pages_to_delete[] = $record->pid;
            break;
          }

          if ($form_values['update_titles'] === 1 &&
            $record->page_title !== $record->taxonomy_term_field_data_name
          ) {
            $titles_to_update[$record->pid] = $record->taxonomy_term_field_data_name;
          }

          if ($form_values['update_paths'] === 1) {
            // Replace 123 with your entity's ID.
            $internal_path = '/term/' . $record->entity_id;
            $alias = \Drupal::service('path_alias.manager')->getAliasByPath($internal_path, $record->page_language);
            if ($path_params) {
              $alias = $alias . '?' . $path_params;
            }
            if (
              $alias !== $record->page_path &&
              $alias !== str_replace('/' . $record->page_language, '', $record->page_path)
            ) {
              $paths_to_update[$record->pid] = $alias;
            }
          }

          break;

        case 'entity.user.canonical':
          if (empty($record->uid) && $form_values['check_deleted']
          ) {
            $pages_to_delete[] = $record->pid;
            break;
          }
          elseif (
            $path_params &&
            $form_values['remove_pages_with_params'] !== 'none'
          ) {
            $pages_to_delete[] = $record->pid;
            break;
          }

          if ($form_values['update_titles'] === 1 &&
            $record->page_title !== $record->name
          ) {
            $titles_to_update[$record->pid] = $record->name;
          }

          if ($form_values['update_paths'] === 1) {
            // Replace 123 with your entity's ID.
            $internal_path = '/term/' . $record->entity_id;
            $alias = \Drupal::service('path_alias.manager')->getAliasByPath($internal_path, $record->page_language);
            if ($path_params) {
              $alias = $alias . '?' . $path_params;
            }
            if (
              $alias !== $record->page_path &&
              $alias !== str_replace('/' . $record->page_language, '', $record->page_path)
            ) {
              $paths_to_update[$record->pid] = $alias;
            }
          }

          break;

        default:
          // Views and other entity types.
          // Delete if it has parameters we want to drop.
          if ($form_values['remove_pages_with_params'] === 'all' &&
              $path_params
            ) {
            $pages_to_delete[] = $record->pid;
            break;
          }

          $url_object = $path_validator->getUrlIfValid($record->page_path);
          // Delete if the path is gone.
          if (!$url_object instanceof Url) {
            if ($form_values['check_deleted']) {
              $pages_to_delete[] = $record->pid;
            }
            break;
          }
          if (!$path_params && $form_values['update_paths'] === 1) {
            $alias = $url_object->toString();
            if (
              $alias !== $record->page_path &&
              $alias !== str_replace('/' . $record->page_language, '', $record->page_path)
            ) {
              $paths_to_update[$record->pid] = $alias;
            }
          }

          break;

      }

      if ($counter === $count) {
        $context['results']['last_record'] = $record->pid;
      }
    }

    if (count($pages_to_delete) > 0) {
      $delete = $database->delete('editoria11y_results');
      $delete->condition('pid', $pages_to_delete, 'IN');
      $delete->execute();
      $delete = $database->delete('editoria11y_dismissals');
      $delete->condition('pid', $pages_to_delete, 'IN');
      $delete->execute();
      $delete = $database->delete('editoria11y_pages');
      $delete->condition('pid', $pages_to_delete, 'IN');
      $delete->execute();
      $context['results']['deleted']++;
    }

    if ($form_values['update_paths'] === 1 && count($paths_to_update) > 0) {
      foreach ($paths_to_update as $key => $path) {
        $paths = $database->update('editoria11y_pages');
        $paths->condition('pid', $key, '=');
        $paths->fields([
          'page_path' => $path,
        ]);
        $paths->execute();
        $context['results']['updated']++;
      }
    }

    if ($form_values['update_titles'] === 1 && count($titles_to_update) > 0) {
      foreach ($titles_to_update as $key => $title) {
        $titles = $database->update('editoria11y_pages');
        $titles->condition('pid', $key, '=');
        $titles->fields([
          'page_title' => $title,
        ]);
        $titles->execute();
        $context['results']['updated']++;
      }
    }

    // Keep track of progress.
    $context['results']['progress'] += $batch_size;
  }

  /**
   * Sends messages to UI and logs on complete.
   *
   * @param bool $success
   *   Did it work?
   * @param array $results
   *   Values forwarded from the batch process.
   * @param array $operations
   *   Where we were when things blew up.
   * @param string $elapsed
   *   How long the batch took.
   */
  public static function batchFinished(bool $success, array $results, array $operations, string $elapsed): void {
    // Grab the messenger service, this will be needed if the batch was a
    // success or a failure.
    $messenger = \Drupal::messenger();
    if ($success) {
      // The success variable was true, which indicates that the batch process
      // was successful (i.e. no errors occurred).
      // Show success message to the user.
      $messenger->addMessage(t('@process processed @count, deleted @deleted, updated @updated.', [
        '@process' => $results['process'],
        '@count' => $results['progress'],
        '@deleted' => $results['deleted'],
        '@updated' => $results['updated'],
        '@elapsed' => $elapsed,
      ]));
      // Log the batch success.
      \Drupal::logger('Editoria11y database maintenance')->info(
        '@process processed @count, deleted @deleted, updated @updated in @elapsed.', [
          '@process' => $results['process'],
          '@count' => $results['progress'],
          '@deleted' => $results['deleted'],
          '@updated' => $results['updated'],
          '@elapsed' => $elapsed,
        ]);
    }
    else {
      // An error occurred. $operations contains the operations that remained
      // unprocessed. Pick the last operation and report on what happened.
      $error_operation = reset($operations);
      if ($error_operation) {
        $message = t('An error occurred while processing %error_operation with arguments: @arguments', [
          '%error_operation' => print_r($error_operation[0], TRUE),
          '@arguments' => print_r($error_operation[1], TRUE),
        ]);
        $messenger->addError($message);
      }
    }
  }

  /**
   * {@inheritdoc}
   */
  public function submitForm(array &$form, FormStateInterface $form_state): void {
    $batch = new BatchBuilder();
    $batch->setTitle('Running batch process.')
      ->setFinishCallback([self::class, 'batchFinished'])
      ->setInitMessage('Commencing')
      ->setProgressMessage('Processing...')
      ->setErrorMessage('An error occurred during processing.');

    // Create 10 chunks of 100 items.
    // @phpstan-ignore-next-line
    $database = \Drupal::database();
    $batch_size = 100;
    $results_query = $database->select('editoria11y_pages');
    $result_count = (int) $results_query->countQuery()->execute()->fetchField();
    $batches = (int) ceil((int) $result_count / $batch_size);

    // @todo Stop if there is 0;
    for ($i = 0; $i < $batches; $i++) {
      $args = [
        $i,
        $batch_size,
        $result_count,
        $form_state->getValues(),
      ];
      $batch->addOperation([self::class, 'batchProcess'], $args);
    }
    batch_set($batch->toArray());

    // Set the redirect for the form submission back to the form itself.
    $form_state->setRedirectUrl(new Url('editoria11y_csa.dashboard_actions'));
  }

}

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

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