You are here

class DebugForm in CMS Content Sync 8

Same name and namespace in other branches
  1. 2.1.x src/Form/DebugForm.php \Drupal\cms_content_sync\Form\DebugForm
  2. 2.0.x src/Form/DebugForm.php \Drupal\cms_content_sync\Form\DebugForm

Content Sync advanced debug form.

Hierarchy

Expanded class hierarchy of DebugForm

1 string reference to 'DebugForm'
cms_content_sync.routing.yml in ./cms_content_sync.routing.yml
cms_content_sync.routing.yml

File

src/Form/DebugForm.php, line 26

Namespace

Drupal\cms_content_sync\Form
View source
class DebugForm extends ConfigFormBase {

  /**
   * @var \Drupal\Core\Entity\EntityTypeManagerInterface
   */
  protected $entityTypeManager;

  /**
   * @var \Drupal\Core\Entity\EntityTypeBundleInfoInterface
   */
  protected $bundleInfoService;

  /**
   * @var \Drupal\Core\Entity\EntityFieldManager
   */
  protected $entityFieldManager;

  /**
   * Constructs an object.
   *
   * @param \Drupal\Core\Config\ConfigFactoryInterface        $config_factory
   *                                                                                The factory for configuration objects
   * @param \Drupal\Core\Entity\EntityTypeManagerInterface    $entity_type_manager
   *                                                                                The entity query
   * @param \Drupal\Core\Entity\EntityTypeBundleInfoInterface $bundle_info_service
   *                                                                                The bundle info service
   * @param \Drupal\Core\Entity\EntityFieldManager            $entity_field_manager
   *                                                                                The entity field manager
   */
  public function __construct(ConfigFactoryInterface $config_factory, EntityTypeManagerInterface $entity_type_manager, EntityTypeBundleInfoInterface $bundle_info_service, EntityFieldManager $entity_field_manager) {
    parent::__construct($config_factory);
    $this->entityTypeManager = $entity_type_manager;
    $this->bundleInfoService = $bundle_info_service;
    $this->entityFieldManager = $entity_field_manager;
  }

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

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

  /**
   * Display debug output for a given entity to analyze it's sync structure.
   *
   * @param array $form
   *
   * @return AjaxResponse|array
   *                            The debug output table
   */
  public function inspectEntity($form, FormStateInterface $form_state) {
    $entity_type = $form_state
      ->getValue([
      'cms_content_sync_inspect_entity',
      'entity_type',
    ]);
    $entity_id = $form_state
      ->getValue([
      'cms_content_sync_inspect_entity',
      'entity_id',
    ]);
    $result = [];
    if ($entity_type && $entity_id) {
      $storage = $this->entityTypeManager
        ->getStorage($entity_type);
      $entity = $storage
        ->load($entity_id);
      if (!$entity) {
        $form_state
          ->setError($form['cms_content_sync_inspect_entity']['entity_id'], 'This entity doesn\'t exist.');
        return $result;
      }
    }
    $ajax_response = new AjaxResponse();
    if ($entity) {
      $result = [
        '#type' => 'table',
        '#sticky' => true,
        '#header' => array_merge([
          $this
            ->t('Label'),
          $this
            ->t('Entity Type'),
          $this
            ->t('Bundle'),
          $this
            ->t('ID'),
          $this
            ->t('UUID'),
          $this
            ->t('Entity Status ID'),
          $this
            ->t('Flow'),
          $this
            ->t('Pool'),
          $this
            ->t('Flags'),
          $this
            ->t('Last push / edit date'),
          $this
            ->t('Last pull'),
          $this
            ->t('Latest log messages'),
        ]),
      ];
      $this
        ->debugEntity($result, $entity);
      $ajax_response
        ->addCommand(new HtmlCommand('.cms_content_sync-inspect-entity-output', $result));
      $serializer = \Drupal::service('serializer');
      $data = $serializer
        ->serialize($entity, 'json', [
        'plugin_id' => 'entity',
      ]);
      $entity_data = '<b>Entity Data:</b><br>';
      $entity_data .= '<br><pre><code>' . $data . '</code></pre>';
      $ajax_response
        ->addCommand(new HtmlCommand('.cms_content_sync-inspect-entity-data-output', $entity_data));
    }
    return $ajax_response;
  }

  /**
   * {@inheritdoc}
   */
  public function buildForm(array $form, FormStateInterface $form_state) {
    $form['#tree'] = true;
    $settings = ContentSyncSettings::getInstance();
    $form['cms_content_sync_login'] = [
      '#type' => 'submit',
      '#value' => $this
        ->t('Login at Sync Core'),
    ];

    // The extended entity export/import logging is stored within the key value table
    // since we do not want this stored within the Drupal configuration.
    $form['cms_content_sync_extended_entity_export_logging'] = [
      '#type' => 'checkbox',
      '#title' => $this
        ->t('Extended Entity Export logging'),
      '#default_value' => $settings
        ->getExtendedEntityExportLogging() ?? 0,
      '#description' => $this
        ->t('When the "Extended Entity Export logging" is enabled, Content Sync is going to add a log entry to Watchdog
                                        showing all entity values processed by content sync after the <b><u>export</u></b>. This is helpful to debug outgoing entities.<br>
                                        <b>This will create many large log messages, so only use this for a short period of time and disable it immediately after your debugging session.</b>'),
    ];
    $form['cms_content_sync_extended_entity_import_logging'] = [
      '#type' => 'checkbox',
      '#title' => $this
        ->t('Extended Entity Import logging'),
      '#default_value' => $settings
        ->getExtendedEntityImportLogging() ?? 0,
      '#description' => $this
        ->t('When the "Extended Entity Import logging" is enabled, Content Sync is going to add a log entry to Watchdog
                                        showing all entity values processed by content sync after the <b><u>import</u></b>. This is helpful to identify if an entity
                                        has changed after content sync has processed it.<br>
                                        <b>This will create many large log messages, so only use this for a short period of time and disable it immediately after your debugging session.</b>'),
    ];
    $form['cms_content_sync_inspect_entity'] = [
      '#type' => 'fieldset',
      '#title' => $this
        ->t('Inspect entity'),
    ];
    $entity_types = $this->entityTypeManager
      ->getDefinitions();
    $field_map = $this->entityFieldManager
      ->getFieldMap();
    $entity_types_names = [];
    foreach ($entity_types as $type_key => $entity_type) {

      // This entity type doesn't contain any fields.
      if (!isset($field_map[$type_key])) {
        continue;
      }
      if ('cms_content_sync' == $entity_type
        ->getProvider()) {
        continue;
      }
      $entity_types_names[$type_key] = $type_key;
    }
    $form['cms_content_sync_inspect_entity']['entity_type'] = [
      '#type' => 'select',
      '#title' => $this
        ->t('Entity type'),
      '#options' => $entity_types_names,
      '#default_value' => 'node',
    ];
    $form['cms_content_sync_inspect_entity']['entity_id'] = [
      '#type' => 'textfield',
      '#title' => $this
        ->t('Entity ID'),
    ];
    $form['cms_content_sync_inspect_entity']['submit'] = [
      '#type' => 'button',
      '#value' => $this
        ->t('Check'),
      '#ajax' => [
        'callback' => '::inspectEntity',
        'wrapper' => 'cms_content_sync-inspect-entity-output',
      ],
    ];
    $form['cms_content_sync_inspect_entity']['entity'] = [
      '#prefix' => '<div class="cms_content_sync-inspect-entity-output">',
      '#suffix' => '</div>',
    ];
    $form['cms_content_sync_inspect_entity']['entity_data'] = [
      '#prefix' => '<div class="cms_content_sync-inspect-entity-data-output">',
      '#suffix' => '</div>',
    ];
    return parent::buildForm($form, $form_state);
  }

  /**
   * {@inheritdoc}
   */
  public function validateForm(array &$form, FormStateInterface $form_state) {
    parent::validateForm($form, $form_state);
  }

  /**
   * {@inheritdoc}
   */
  public function submitForm(array &$form, FormStateInterface $form_state) {
    parent::submitForm($form, $form_state);
    $settings = ContentSyncSettings::getInstance();
    $settings
      ->setExtendedEntityExportLogging($form_state
      ->getValue('cms_content_sync_extended_entity_export_logging'));
    $settings
      ->setExtendedEntityImportLogging($form_state
      ->getValue('cms_content_sync_extended_entity_import_logging'));
    if ('Login at Sync Core' == $form_state
      ->getValue('op')) {
      $messenger = \Drupal::messenger();
      foreach (SyncCoreFactory::getAllSyncCores() as $host => $core) {
        if ($core
          ->getSyndicationService()
          ->refreshAuthentication()) {
          $messenger
            ->addStatus('SUCCESS login from Sync Core at ' . $host);
        }
        else {
          $messenger
            ->addError('FAILED to login from Sync Core at ' . $host);
        }
      }
    }
  }

  /**
   * @param array                               $result
   *                                                             The table render array
   * @param \Drupal\Core\Entity\EntityInterface $entity
   * @param array                               $parent_entities
   *                                                             Entities that have already been processed up the ladder
   */
  protected function debugEntity(&$result, $entity, $parent_entities = []) {
    $label = str_repeat('+', count($parent_entities)) . ' ';

    /*foreach($parent_entities as $parent_entity) {
      $label .= $parent_entity->label().' => ';
      }*/
    $label .= $entity
      ->label();
    $moduleHandler = \Drupal::service('module_handler');
    $dblog_enabled = $moduleHandler
      ->moduleExists('dblog');
    if ($dblog_enabled) {
      $connection = \Drupal::database();
      $log_levels = RfcLogLevel::getLevels();
    }
    else {
      \Drupal::messenger()
        ->addMessage('dblog is disabled, so no log messages will be displayed.');
    }
    $children = [];
    $infos = EntityStatus::getInfosForEntity($entity
      ->getEntityTypeId(), $entity
      ->uuid());
    if (!count($infos)) {
      return;
    }
    foreach ($infos as $index => $info) {
      $current_row = [];
      if (0 == $index) {
        $current_row['label'] = [
          '#markup' => $label,
        ];
        $current_row['entity_type'] = [
          '#markup' => $entity
            ->getEntityTypeId(),
        ];
        $current_row['bundle'] = [
          '#markup' => $entity
            ->bundle(),
        ];
        $current_row['id'] = [
          '#markup' => $entity
            ->id(),
        ];
        $current_row['uuid'] = [
          '#markup' => $entity
            ->uuid(),
        ];
      }
      else {
        $current_row['label'] = [
          '#markup' => '',
        ];
        $current_row['entity_type'] = [
          '#markup' => '',
        ];
        $current_row['bundle'] = [
          '#markup' => '',
        ];
        $current_row['id'] = [
          '#markup' => '',
        ];
        $current_row['uuid'] = [
          '#markup' => '',
        ];
      }
      $flow = $info
        ->getFlow();
      $pool = $info
        ->getPool();
      $current_row['entity_status_id'] = [
        '#markup' => $info->id->value,
      ];
      $current_row['flow'] = [
        '#markup' => $flow ? $flow
          ->label() : $info
          ->get('flow')->value,
      ];
      $current_row['pool'] = [
        '#markup' => $pool ? $pool
          ->label() : $info
          ->get('pool')->value,
      ];
      $current_row['flags'] = [
        '#theme' => 'item_list',
      ];
      if ($info
        ->isSourceEntity()) {
        $current_row['flags']['#items'][]['#markup'] = 'Source';
      }
      if ($info
        ->isManualPushEnabled()) {
        $current_row['flags']['#items'][]['#markup'] = 'Pushing enabled';
      }
      if ($info
        ->didUserEnablePush()) {
        $current_row['flags']['#items'][]['#markup'] = 'Pushed by user';
      }
      if ($info
        ->isPushedAsDependency()) {
        $current_row['flags']['#items'][]['#markup'] = 'Pushed as dependency';
      }
      if ($info
        ->isOverriddenLocally()) {
        $current_row['flags']['#items'][]['#markup'] = 'Overridden locally';
      }
      $timestamp = $info
        ->getLastPush();
      $current_row['last_export'] = [
        '#markup' => $timestamp ? \Drupal::service('date.formatter')
          ->format($timestamp, 'long') : 'NEVER',
      ];
      $timestamp = $info
        ->getLastPull();
      $current_row['last_import'] = [
        '#markup' => $timestamp ? \Drupal::service('date.formatter')
          ->format($timestamp, 'long') : 'NEVER',
      ];
      $current_row['log_messages'] = [
        '#theme' => 'item_list',
        '#items' => [],
      ];
      $query = $connection
        ->select('watchdog', 'w')
        ->fields('w', [
        'timestamp',
        'severity',
        'message',
        'variables',
      ])
        ->orderBy('timestamp', 'DESC')
        ->range(0, 3)
        ->condition('type', 'cms_content_sync')
        ->condition('variables', '%' . $connection
        ->escapeLike($entity
        ->uuid()) . '%', 'LIKE');
      $query = $query
        ->execute();
      $rows = $query
        ->fetchAll();
      foreach ($rows as $res) {
        $message = '<strong>' . $log_levels[$res->severity] . '</strong> <em>' . \Drupal::service('date.formatter')
          ->format($res->timestamp, 'long') . '</em> ' . $this
          ->formatMessage($res)
          ->render();
        $current_row['log_messages']['#items'][]['#markup'] = $message;
      }
      $intent = new PushIntent($info
        ->getFlow(), $info
        ->getPool(), PushIntent::PUSH_FORCED, SyncIntent::ACTION_CREATE, $entity);
      $intent
        ->execute(true);
      $serialized = $intent
        ->getOperation()
        ->getData();
      foreach ($serialized['embed_entities'] as $child) {
        $id = $child[Entity::ENTITY_TYPE_KEY] . $child[Entity::UUID_KEY];
        if (isset($children[$id])) {
          continue;
        }
        $children[$id] = $child;
      }
      $result[] = $current_row;
    }
    $result[0]['label']['#attributes']['rowspan'] = $index + 1;
    $result[0]['entity_type']['#attributes']['rowspan'] = $index + 1;
    $result[0]['bundle']['#attributes']['rowspan'] = $index + 1;
    $result[0]['id']['#attributes']['rowspan'] = $index + 1;
    $result[0]['uuid']['#attributes']['rowspan'] = $index + 1;

    /** @var \Drupal\Core\Entity\EntityRepositoryInterface $entity_repository */
    $entity_repository = \Drupal::service('entity.repository');
    $parent_entities[] = $entity;
    foreach ($children as $child) {
      $child_entity = $entity_repository
        ->loadEntityByUuid($child[Entity::ENTITY_TYPE_KEY], $child[Entity::UUID_KEY]);
      $this
        ->debugEntity($result, $child_entity, $parent_entities);
    }
  }

  /**
   * 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 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 = $this
          ->t('Log data is corrupted and cannot be unserialized: @message', [
          '@message' => Xss::filterAdmin($row->message),
        ]);
      }
      else {
        $message = $this
          ->t(Xss::filterAdmin($row->message), $variables);
      }
    }
    else {
      $message = false;
    }
    return $message;
  }

  /**
   * {@inheritdoc}
   */
  protected function getEditableConfigNames() {
    return [
      'cms_content_sync.debug',
    ];
  }

}

Members

Namesort descending Modifiers Type Description Overrides
ConfigFormBaseTrait::config protected function Retrieves a configuration object.
DebugForm::$bundleInfoService protected property
DebugForm::$entityFieldManager protected property
DebugForm::$entityTypeManager protected property
DebugForm::buildForm public function Form constructor. Overrides ConfigFormBase::buildForm
DebugForm::create public static function Instantiates a new instance of this class. Overrides ConfigFormBase::create
DebugForm::debugEntity protected function
DebugForm::formatMessage protected function Formats a database log message.
DebugForm::getEditableConfigNames protected function Gets the configuration names that will be editable. Overrides ConfigFormBaseTrait::getEditableConfigNames
DebugForm::getFormId public function Returns a unique string identifying the form. Overrides FormInterface::getFormId
DebugForm::inspectEntity public function Display debug output for a given entity to analyze it's sync structure.
DebugForm::submitForm public function Form submission handler. Overrides ConfigFormBase::submitForm
DebugForm::validateForm public function Form validation handler. Overrides FormBase::validateForm
DebugForm::__construct public function Constructs an object. Overrides ConfigFormBase::__construct
DependencySerializationTrait::$_entityStorages protected property An array of entity type IDs keyed by the property name of their storages.
DependencySerializationTrait::$_serviceIds protected property An array of service IDs keyed by property name used for serialization.
DependencySerializationTrait::__sleep public function 1
DependencySerializationTrait::__wakeup public function 2
FormBase::$configFactory protected property The config factory. 1
FormBase::$requestStack protected property The request stack. 1
FormBase::$routeMatch protected property The route match.
FormBase::configFactory protected function Gets the config factory for this form. 1
FormBase::container private function Returns the service container.
FormBase::currentUser protected function Gets the current user.
FormBase::getRequest protected function Gets the request object.
FormBase::getRouteMatch protected function Gets the route match.
FormBase::logger protected function Gets the logger for a specific channel.
FormBase::redirect protected function Returns a redirect response object for the specified route. Overrides UrlGeneratorTrait::redirect
FormBase::resetConfigFactory public function Resets the configuration factory.
FormBase::setConfigFactory public function Sets the config factory for this form.
FormBase::setRequestStack public function Sets the request stack object to use.
LinkGeneratorTrait::$linkGenerator protected property The link generator. 1
LinkGeneratorTrait::getLinkGenerator Deprecated protected function Returns the link generator.
LinkGeneratorTrait::l Deprecated protected function Renders a link to a route given a route name and its parameters.
LinkGeneratorTrait::setLinkGenerator Deprecated public function Sets the link generator service.
LoggerChannelTrait::$loggerFactory protected property The logger channel factory service.
LoggerChannelTrait::getLogger protected function Gets the logger for a specific channel.
LoggerChannelTrait::setLoggerFactory public function Injects the logger channel factory.
MessengerTrait::$messenger protected property The messenger. 29
MessengerTrait::messenger public function Gets the messenger. 29
MessengerTrait::setMessenger public function Sets the messenger.
RedirectDestinationTrait::$redirectDestination protected property The redirect destination service. 1
RedirectDestinationTrait::getDestinationArray protected function Prepares a 'destination' URL query parameter for use with \Drupal\Core\Url.
RedirectDestinationTrait::getRedirectDestination protected function Returns the redirect destination service.
RedirectDestinationTrait::setRedirectDestination public function Sets the redirect destination service.
StringTranslationTrait::$stringTranslation protected property The string translation service. 1
StringTranslationTrait::formatPlural protected function Formats a string containing a count of items.
StringTranslationTrait::getNumberOfPlurals protected function Returns the number of plurals supported by a given language.
StringTranslationTrait::getStringTranslation protected function Gets the string translation service.
StringTranslationTrait::setStringTranslation public function Sets the string translation service to use. 2
StringTranslationTrait::t protected function Translates a string to the current language or to a given language.
UrlGeneratorTrait::$urlGenerator protected property The url generator.
UrlGeneratorTrait::getUrlGenerator Deprecated protected function Returns the URL generator service.
UrlGeneratorTrait::setUrlGenerator Deprecated public function Sets the URL generator service.
UrlGeneratorTrait::url Deprecated protected function Generates a URL or path for a specific route based on the given parameters.