cms_content_sync-3.0.x-dev/modules/cms_content_sync_health/src/Controller/SyncHealth.php

modules/cms_content_sync_health/src/Controller/SyncHealth.php
<?php

namespace Drupal\cms_content_sync_health\Controller;

use Drupal\cms_content_sync\Entity\EntityStatus;
use Drupal\cms_content_sync\Entity\Flow;
use Drupal\cms_content_sync\Entity\Pool;
use Drupal\cms_content_sync\PushIntent;
use Drupal\cms_content_sync\SyncCoreInterface\DrupalApplication;
use Drupal\cms_content_sync\SyncCoreInterface\SyncCoreFactory;
use Drupal\Component\Utility\Xss;
use Drupal\Core\Config\ConfigFactory;
use Drupal\Core\Controller\ControllerBase;
use Drupal\Core\Database\Connection;
use Drupal\Core\Datetime\DateFormatter;
use Drupal\Core\Entity\EntityTypeManager;
use Drupal\Core\Extension\ModuleHandler;
use Drupal\Core\Logger\RfcLogLevel;
use Drupal\Core\Messenger\MessengerInterface;
use Drupal\update\UpdateFetcher;
use EdgeBox\SyncCore\Interfaces\IReportingService;
use GuzzleHttp\Client;
use Symfony\Component\DependencyInjection\ContainerInterface;
use function t;
use Drupal\Core\Extension\ModuleExtensionList;
use Drupal\Core\Site\Settings;
use EdgeBox\SyncCore\V2\Helper;

/**
 * Provides a listing of Flow.
 */
class SyncHealth extends ControllerBase {
  /**
   * The Drupal core database connection.
   *
   * @var \Drupal\Core\Database\Database
   */
  protected $database;

  /**
   * The Drupal Core module handler.
   *
   * @var \Drupal\Core\Extension\ModuleHandler
   */
  protected $moduleHandler;

  /**
   * {@inheritdoc}
   *
   * @var \Drupal\Core\Config\ConfigFactory
   */
  protected $configFactory;

  /**
   * {@inheritdoc}
   *
   * @var \Drupal\Core\Datetime\DateFormatter
   */
  protected $dateFormatter;

  /**
   * {@inheritdoc}
   *
   * @var \GuzzleHttp\Client
   */
  protected $httpClient;

  /**
   * @var \Drupal\Core\Site\Settings
   */
  protected $settings;

  /**
   * {@inheritdoc}
   *
   * @var \Drupal\Core\Messenger\MessengerInterface
   */
  protected $messenger;

  /**
   * {@inheritdoc}
   *
   * @var \Drupal\Core\Entity\EntityTypeManager
   */
  protected $entityTypeManager;

  /**
   * {@inheritdoc}
   *
   * @var \Drupal\Core\Extension\ModuleExtensionList
   */
  protected $moduleExtensionList;

  /**
   * Constructs a \Drupal\cms_content_sync_health\Controller\SyncHealth object.
   */
  public function __construct(Connection $database, ModuleHandler $moduleHandler, ConfigFactory $configFactory, DateFormatter $dateFormatter, Client $httpClient, MessengerInterface $messenger, EntityTypeManager $entityTypeManager, ModuleExtensionList $moduleExtensionList, Settings $settings) {
    $this->database = $database;
    $this->moduleHandler = $moduleHandler;
    $this->configFactory = $configFactory;
    $this->dateFormatter = $dateFormatter;
    $this->httpClient = $httpClient;
    $this->messenger = $messenger;
    $this->entityTypeManager = $entityTypeManager;
    $this->moduleExtensionList = $moduleExtensionList;
    $this->settings = $settings;
  }

  /**
   * {@inheritdoc}
   */
  public static function create(ContainerInterface $container) {
    return new static(
    $container->get('database'),
    $container->get('module_handler'),
    $container->get('config.factory'),
    $container->get('date.formatter'),
    $container->get('http_client'),
    $container->get('messenger'),
    $container->get('entity_type.manager'),
    $container->get('extension.list.module'),
    $container->get('settings')
    );
  }

  /**
   * Render the overview page.
   *
   * @return array
   *   Returns the overview page render array.
   */
  public function overview() {
    $sync_cores = [];
    if (DrupalApplication::get()->getSiteUuid()) {
      foreach (SyncCoreFactory::getAllSyncCores() as $host => $core) {
        $status = $core->getReportingService()->getStatus();
        $reporting = $core->getReportingService();
        $status['error_log'] = $this->filterSyncCoreLogMessages($reporting->getLog(IReportingService::LOG_LEVEL_ERROR));
        $status['warning_log'] = $this->filterSyncCoreLogMessages($reporting->getLog(IReportingService::LOG_LEVEL_WARNING));
        $sync_cores[$host] = $status;
      }
    }
    else {
      \Drupal::messenger()->addWarning($this->t('Site is not registered.'));
    }

    $module_info = $this->moduleExtensionList->getExtensionInfo('cms_content_sync');
    $moduleHandler = $this->moduleHandler;
    if ($moduleHandler->moduleExists('update')) {
      $updates = new UpdateFetcher($this->configFactory, $this->httpClient, $this->settings);
      $available = $updates->fetchProjectData([
        'name' => 'cms_content_sync',
        'info' => $module_info,
        'includes' => [],
        'project_type' => 'module',
        'project_status' => TRUE,
      ]);
      if ($available) {
        preg_match_all('@<version>\s*([0-9]+)\.([0-9]+)\.([0-9]+)\s*</version>@i', $available, $versions, PREG_SET_ORDER);
      }
      else {
        $versions = [];
      }
      $newest_major = 0;
      $newest_minor = 0;
      $newest_build = 0;
      foreach ($versions as $version) {
        if ($version[1] > $newest_major) {
          $newest_major = $version[1];
          $newest_minor = $version[2];
          $newest_build = $version[3];
        }
        elseif ($version[1] == $newest_major) {
          if ($version[2] > $newest_minor) {
            $newest_minor = $version[2];
            $newest_build = $version[3];
          }
          elseif ($version[2] == $newest_minor) {
            if ($version[3] > $newest_build) {
              $newest_build = $version[3];
            }
          }
        }
      }
      $newest_version = $newest_major . '.' . $newest_minor . '.' . $newest_build;
    }
    else {
      $newest_version = NULL;
    }

    if (isset($module_info['version'])) {
      $module_version = $module_info['version'];
      $module_version = preg_replace('@^\d\.x-(.*)$@', '$1', $module_version);
      if ($module_version != $newest_version) {
        if ($newest_version) {
          $this->messenger->addMessage($this->t("There's an update available! The newest module version is @newest, yours is @current.", [
            '@newest' => $newest_version,
            '@current' => $module_version,
          ]));
        }
        else {
          $this->messenger->addMessage($this->t("Please enable the 'update' module to see if you're running the latest Content Sync version."));
        }
      }
    }
    else {
      $module_version = NULL;
      if ($newest_version) {
        $this->messenger->addWarning($this->t("You're running a dev release. The newest module version is @newest.", [
          '@newest' => $newest_version,
        ]));
      }
    }

    $push_failures_hard = $this->countStatusEntitiesWithFlag(EntityStatus::FLAG_PUSH_FAILED);

    $push_failures_soft = $this->countStatusEntitiesWithFlag(EntityStatus::FLAG_PUSH_FAILED_SOFT);

    $pull_failures_hard = $this->countStatusEntitiesWithFlag(EntityStatus::FLAG_PULL_FAILED);

    $pull_failures_soft = $this->countStatusEntitiesWithFlag(EntityStatus::FLAG_PULL_FAILED_SOFT);

    $version_differences['local'] = $this->getLocalVersionDifferences();

    $moduleHandler = $this->moduleHandler;
    $dblog_enabled = $moduleHandler->moduleExists('dblog');
    if ($dblog_enabled) {
      $site_log_disabled = FALSE;
      $error_log = $this->getLocalLogMessages([
        RfcLogLevel::EMERGENCY,
        RfcLogLevel::ALERT,
        RfcLogLevel::CRITICAL,
        RfcLogLevel::ERROR,
      ]);
      $warning_log = $this->getLocalLogMessages([
        RfcLogLevel::WARNING,
      ]);
    }
    else {
      $site_log_disabled = TRUE;
      $error_log = NULL;
      $warning_log = NULL;
    }

    return [
      '#theme' => 'cms_content_sync_sync_health_overview',
      '#sync_cores' => $sync_cores,
      '#module_version' => $module_version,
      '#newest_version' => $newest_version,
      '#push_failures_hard' => $push_failures_hard,
      '#push_failures_soft' => $push_failures_soft,
      '#pull_failures_hard' => $pull_failures_hard,
      '#pull_failures_soft' => $pull_failures_soft,
      '#version_differences' => $version_differences,
      '#error_log' => $error_log,
      '#warning_log' => $warning_log,
      '#site_log_disabled' => $site_log_disabled,
    ];
  }

  /**
   * Formats a database log message.
   *
   * @param object $row
   *   The record from the watchdog table. The object properties are: wid, uid,
   *   severity, type, timestamp, message, variables, link, name.
   *
   * @return false|string|TranslatableMarkup
   *   The formatted log message or FALSE if the message or variables properties
   *   are not set
   */
  protected static function formatMessage($row) {
    // Check for required properties.
    if (isset($row->message, $row->variables)) {
      $variables = @unserialize($row->variables);
      // Messages without variables or user specified text.
      if (NULL === $variables) {
        $message = Xss::filterAdmin($row->message);
      }
      elseif (!is_array($variables)) {
        $message = t('Log data is corrupted and cannot be unserialized: @message', ['@message' => Xss::filterAdmin($row->message)]);
      }
      // Message to translate with injected variables.
      else {
        // @codingStandardsIgnoreStart
        $message = t(Xss::filterAdmin($row->message), $variables);
        // @codingStandardsIgnoreEnd
      }
    }
    else {
      $message = FALSE;
    }

    return $message;
  }

  /**
   * Count status entities with the given flag.
   *
   * @param int $flag
   *   See EntityStatus::FLAG_*.
   * @param array $details
   *   Search the 'data' column to contain the given $value and save it in the
   *   result array at $key.
   *
   * @return array
   *   The counts, always having 'total'=>... and optionally the counts given
   *   by $details.
   */
  protected function countStatusEntitiesWithFlag($flag, array $details = []) {
    $result['total'] = $this->database->select('cms_content_sync_entity_status')
      ->where('flags&:flag=:flag', [':flag' => $flag])
      ->countQuery()
      ->execute()
      ->fetchField();

    if ($result['total']) {
      foreach ($details as $name => $search) {
        $search = '%' . $this->database->escapeLike($search) . '%';
        $result[$name] = $this->database->select('cms_content_sync_entity_status')
          ->where('flags&:flag=:flag', [':flag' => $flag])
          ->condition('data', $search, 'LIKE')
          ->countQuery()
          ->execute()
          ->fetchField();
      }
    }

    return $result;
  }

  /**
   * Get content sync related log messages.
   *
   * @param mixed $levels
   *   The depth to be returned.
   * @param mixed $count
   *   The amount of log messages to be returned.
   */
  protected function getLocalLogMessages($levels, $count = 10) {
    $result = [];

    $connection = $this->database;

    $query = $connection
      ->select('watchdog', 'w')
      ->fields('w', ['timestamp', 'severity', 'message', 'variables'])
      ->orderBy('timestamp', 'DESC')
      ->range(0, $count)
      ->condition('type', 'cms_content_sync')
      ->condition('severity', $levels, 'IN');
    $query = $query->execute();
    $rows = $query->fetchAll();
    foreach ($rows as $res) {
      $message =
        '<em>' .
        $this->dateFormatter->format($res->timestamp, 'long') .
        '</em> ' .
        self::formatMessage($res)->render();

      $result[] = $message;
    }

    return Helper::obfuscateCredentials($result);
  }

  /**
   * Filter the given messages to only display those related to this site.
   *
   * @param array[] $messages
   *   The messages to filtered.
   *
   * @return array[]
   *   Returns the filtered log messages.
   */
  protected function filterSyncCoreLogMessages(array $messages) {
    $result = [];

    $allowed_prefixes = [];
    foreach (Pool::getAll() as $pool) {
      $allowed_prefixes[] = 'drupal-' . $pool->id() . '-' . DrupalApplication::get()->getSiteMachineName() . '-';
    }

    foreach ($messages as $msg) {
      if (!isset($msg['connection_id'])) {
        continue;
      }

      $keep = FALSE;

      foreach ($allowed_prefixes as $allowed) {
        if (substr($msg['connection_id'], 0, strlen($allowed)) == $allowed) {
          $keep = TRUE;

          break;
        }
      }

      if ($keep) {
        $result[] = $msg;
      }
    }

    return array_slice($result, -20);
  }

  /**
   * Returns count of stale entities.
   */
  protected function countStaleEntities() {
    $checked = [];
    $count = 0;

    foreach (Flow::getAll() as $flow) {
      foreach ($flow->getController()->getEntityTypeConfig(NULL, NULL, TRUE) as $type_name => $bundles) {
        foreach ($bundles as $bundle_name => $config) {
          $id = $type_name . "\n" . $bundle_name;
          if (in_array($id, $checked)) {
            continue;
          }

          if (PushIntent::PUSH_AUTOMATICALLY != $config['export']) {
            continue;
          }

          if (!in_array(Pool::POOL_USAGE_FORCE, array_values($config['export_pools']))) {
            continue;
          }

          $checked[] = $id;

          /**
           * @var \Drupal\Core\Entity\EntityTypeManager $entityTypeManager
           */
          $entityTypeManager = $this->entityTypeManager;
          $type = $entityTypeManager->getDefinition($type_name);

          $query = $this->database->select($type->getBaseTable(), 'e');

          $query
            ->leftJoin('cms_content_sync_entity_status', 's', 'e.uuid=s.entity_uuid AND s.entity_type=:type', [':type' => $type_name]);

          $query = $query
            ->isNull('s.id');

          // Some entity types don't store their bundle information in their
          // table if they don't actually have multiple bundles.
          if (!in_array($type_name, ['bibcite_contributor', 'bibcite_keyword'])) {
            $query = $query
              ->condition('e.' . $type->getKey('bundle'), $bundle_name);
          }

          $result = $query
            ->countQuery()
            ->execute();

          $count += (int) $result
            ->fetchField();
        }
      }
    }

    return $count;
  }

  /**
   * Returns the local version differences.
   */
  protected function getLocalVersionDifferences() {
    $result = [];

    foreach (Flow::getAll() as $flow) {
      foreach ($flow->getController()->getEntityTypeConfig(NULL, NULL, TRUE) as $type_name => $bundles) {
        foreach ($bundles as $bundle_name => $config) {
          $version = $config['version'];

          $current = Flow::getEntityTypeVersion($type_name, $bundle_name);

          if ($version == $current) {
            continue;
          }

          $result[] = $flow->label() . ' uses entity type  ' . $type_name . '.' . $bundle_name . ' with version ' . $version . '. Current version is ' . $current . '. Please update the Flow.';
        }
      }
    }

    return $result;
  }

  /**
   * Returns the count of entities with a changed version for the push.
   */
  protected function countEntitiesWithChangedVersionForPush() {
    $checked = [];
    $versions = [];
    $types = [];

    foreach (Flow::getAll() as $flow) {
      foreach ($flow->getController()->getEntityTypeConfig(NULL, NULL, TRUE) as $type_name => $bundles) {
        foreach ($bundles as $bundle_name => $config) {
          $id = $type_name . "\n" . $bundle_name;
          if (in_array($id, $checked)) {
            continue;
          }

          $checked[] = $id;

          $version = $config['version'];
          if (!in_array($type_name, $types)) {
            $types[] = $type_name;
          }
          $versions[] = $version;
        }
      }
    }

    return $this->database->select('cms_content_sync_entity_status')
      ->condition('entity_type', $types, 'IN')
      ->condition('entity_type_version', $versions, 'NOT IN')
      ->where('flags&:flag=:flag', [':flag' => EntityStatus::FLAG_IS_SOURCE_ENTITY])
      ->countQuery()
      ->execute()
      ->fetchField();
  }

}

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

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