gatsby-8.x-1.x-dev/gatsby.module

gatsby.module
<?php

/**
 * @file
 * Primary hook implementations for the Gatsby module.
 */

use Drupal\Core\Config\Entity\ConfigEntityInterface;
use Drupal\Core\Datetime\DrupalDateTime;
use Drupal\Core\Entity\EntityInterface;
use Drupal\node\NodeInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Routing\RouteMatchInterface;
use Drupal\Core\Url;
use Drupal\node\Entity\NodeType;
use Drupal\Core\Entity\Display\EntityViewDisplayInterface;
use Drupal\Core\Entity\RevisionableInterface;

/**
 * Implements hook_help().
 */
function gatsby_help($route_name, RouteMatchInterface $route_match) {
  switch ($route_name) {
    // Main module help for the gatsby module.
    case 'help.page.gatsby':
      $output = '';
      $output .= '<h3>' . t('About') . '</h3>';
      $output .= '<p>' . t('Enables Live Preview for Gatsby') . '</p>';
      return $output;

    default:
  }
}

/**
 * Implements hook_form_alter().
 */
function gatsby_form_alter(&$form, FormStateInterface $form_state, $form_id): void {
  // Add Gatsby Preview options to the content type form.
  if ($form_id == 'node_type_add_form' || $form_id == 'node_type_edit_form') {
    $entity = $form_state->getFormObject()->getEntity();

    // Make sure the node integration is actually enabled before doing anything.
    $entity_types = \Drupal::config('gatsby.settings')
      ->get('supported_entity_types');
    if (empty($entity_types) || !in_array('node', $entity_types)) {
      return;
    }

    // Get settings.
    $preview_settings = $entity->getThirdPartySetting('gatsby', 'preview');

    $form['gatsby'] = [
      '#title' => t('Gatsby Preview'),
      '#type' => 'details',
      '#group' => 'additional_settings',
    ];

    $form['gatsby']['gatsby_preview'] = [
      '#type' => 'checkbox',
      '#title' => t('Enable Gatsby Preview Button'),
      '#default_value' => !empty($preview_settings),
      '#description' => t('This will add a Gatsby Preview button to node pages.'),
    ];

    $form['#entity_builders'][] = 'gatsby_preview_node_entity_builder';
  }
  // Override node edit form.
  elseif (preg_match('/node_(\w*)_edit_form/', $form_id, $matches)) {
    $moduleHandler = \Drupal::service('module_handler');
    if ($moduleHandler->moduleExists('gatsby_endpoints')) {
      return;
    }

    $settings = \Drupal::config('gatsby.settings');
    $entity_types = $settings->get('supported_entity_types');
    $preview_url = $settings->get('preview_callback_url');

    // Make sure the node integration is actually enabled before doing anything.
    if (!$preview_url || empty($entity_types) || !in_array('node', $entity_types)) {
      return;
    }

    // Load settings.
    /** @var \Drupal\Core\Entity\EntityInterface $entity */
    $entity = $form_state->getFormObject()->getEntity();
    $entity_type = NodeType::load($entity->bundle());
    $preview_settings = $entity_type->getThirdPartySetting('gatsby', 'preview');
    $server_urls = array_map('trim', explode(',', \Drupal::config('gatsby.settings')->get('server_url')));
    $server_url = reset($server_urls);
    if (!empty($preview_settings) && !empty($server_url)) {
      $moderation_info = \Drupal::service('content_moderation.moderation_information');
      $is_moderated = $moderation_info->isModeratedEntity($entity);
      // Alert the user that content moderation is disabled for the node type.
      if (!$is_moderated) {
        \Drupal::messenger()->addWarning('Gatsby Preview requires content moderation enabled for this node type.');
      }

      /** @var \Drupal\Core\TempStore\PrivateTempStore $tempstore */
      $tempstore = \Drupal::service('tempstore.private')->get('gatsby');
      $preview_url = $tempstore->get('preview_url');
      // Add Gatsby Preview button.
      $form['actions']['gatsby_preview'] = [
        '#type' => 'submit',
        '#weight' => 5,
        '#value' => 'Open Gatsby Preview',
        '#name' => 'gatsby_preview',
        '#attributes' => [
          'class' => [
            'gatsby-preview',
          ],
          'disabled' => !$is_moderated,
        ],
        '#submit' => [
          '::submitForm',
          '::save',
          'gatsby_module_preview_submit_handler',
        ],
        '#attached' => [
          'library' => [
            'gatsby/open_preview_window',
          ],
          'drupalSettings' => [
            'gatsby' => [
              'entity_type' => 'node',
              'entity_id' => $entity->id(),
            ],
          ],
        ],
      ];

      if ($preview_url) {
        $form['actions']['gatsby_preview']['#attached']['drupalSettings']['gatsby']['preview_url'] = $preview_url;
        // Prevent temporary storage key from persisting.
        try {
          $tempstore->delete('preview_url');
        }
        catch (\Exception $error) {

        }
      }
      array_unshift($form['#validate'], 'gatsby_node_validate');
    }
  }
}

/**
 * Custom validation callback.
 */
function gatsby_node_validate(array &$form, FormStateInterface $form_state): void {
  $trigger = $form_state->getTriggeringElement();
  if ($trigger && $trigger['#name'] === 'gatsby_preview') {
    $form_state->setValue(['moderation_state', '0', 'value'], 'draft');
  }
}

/**
 * Custom submit handler to handle Content Sync preview actions.
 *
 * This is needed because Content Sync needs to wait until the page is fully
 * saved before opening the preview window.
 */
function gatsby_module_preview_submit_handler($form, FormStateInterface $form_state): void {
  /** @var \Drupal\Core\TempStore\PrivateTempStore $tempstore */
  $tempstore = \Drupal::service('tempstore.private')->get('gatsby');
  /** @var \Drupal\node\NodeInterface $node */
  $node = $form_state->getFormObject()->getEntity();
  $server_urls = array_map('trim', explode(',', \Drupal::config('gatsby.settings')->get('server_url')));
  $server_url = reset($server_urls);
  $path = \Drupal::service('gatsby.path_mapping')->getPath($node);
  $preview_url = rtrim($server_url, '/') . $path;
  $contentsync_url = \Drupal::config('gatsby.settings')->get('contentsync_url');
  $redirect_url = Url::fromRoute('<current>');
  $url_params = [];
  if ($destination = \Drupal::request()->query->get('destination')) {
    $url_params['destination'] = $destination;
  }
  \Drupal::request()->query->remove('destination');
  $redirect_url->setOptions(['query' => $url_params]);
  if ($contentsync_url) {
    $date = DrupalDateTime::createFromTimestamp($node->getRevisionCreationTime());
    $date->setTimezone(new \DateTimeZone('UTC'));
    // Generate the correct content sync url.
    $preview_url = $contentsync_url . '/gatsby-source-drupal/' . $node->id() . '-' . $date->format(\DateTime::RFC3339) . '-' . $node->language()->getId();
  }
  // Set $preview_url to temporary storage for form to use after redirect.
  try {
    $tempstore->set('preview_url', $preview_url);
  }
  catch (\Exception $error) {

  }

  $form_state->setRedirectUrl($redirect_url);
}

/**
 * Custom handler for Gatsby preview option.
 */
function gatsby_preview_node_entity_builder($entity_type, ConfigEntityInterface $config_entity, &$form, FormStateInterface $form_state): void {
  // Save Preview setting.
  $config_entity->setThirdPartySetting('gatsby', 'preview', $form_state->getValue('gatsby_preview'));
}

/**
 * Implements hook_entity_update().
 */
function gatsby_entity_update(EntityInterface $entity): void {
  /** @var \Drupal\gatsby\GatsbyPreview $gatsbyPreview */
  $gatsbyPreview = \Drupal::service('gatsby.preview');
  $settings = \Drupal::config('gatsby.settings');
  $build_published = $settings->get('build_published');
  $special_types = ['redirect'];

  // Verify this is a supported entity type.
  if (!$gatsbyPreview->isSupportedEntity($entity)) {
    return;
  }

  // Return early if we are only acting on nodes.
  if ($build_published && !$entity instanceof NodeInterface) {
    return;
  }

  // Extra checks for files, which have their own peculiarities.
  if (!_gatsby_will_process_file($entity)) {
    return;
  }

  $published = method_exists($entity, 'isPublished') && $entity->isPublished();

  // Log preview update.
  $gatsbyPreview->gatsbyPreparePreviewData($entity, 'update');

  // Always send an update for special types.
  if (in_array($entity->getEntityTypeId(), $special_types)) {
    $gatsbyPreview->gatsbyPrepareBuildData($entity, 'update');
  }

  // We're only concerned with logging builds for published entities.
  if ($published) {
    if ($entity instanceof RevisionableInterface) {
      // If entity is revisionable, only trigger a build if we are saving a
      // published version.
      if ($entity->isDefaultRevision()) {
        $gatsbyPreview->gatsbyPrepareBuildData($entity, 'update');
      }
    }
    // Assume anything else not handled through content moderation that's
    // published triggers a build.
    else {
      $gatsbyPreview->gatsbyPrepareBuildData($entity, 'update');
    }
  }
  else {
    if ($entity instanceof RevisionableInterface && method_exists($entity, 'isPublished') && $entity->original->isPublished()) {
      // If this is not a draft version, trigger a delete log.
      if ($entity->isDefaultRevision()) {
        $gatsbyPreview->gatsbyPrepareBuildData($entity, 'delete');
      }
    }
  }

  drupal_register_shutdown_function('_gatsby_update');
}

/**
 * Implements hook_entity_insert().
 */
function gatsby_entity_insert(EntityInterface $entity): void {
  /** @var \Drupal\gatsby\GatsbyPreview $gatsbyPreview */
  $gatsbyPreview = \Drupal::service('gatsby.preview');
  $settings = \Drupal::config('gatsby.settings');
  $build_published = $settings->get('build_published');
  $special_types = ['redirect'];

  // Verify this is a supported entity type.
  if (!$gatsbyPreview->isSupportedEntity($entity)) {
    return;
  }

  // Return early if we are only acting on nodes.
  if ($build_published && !$entity instanceof NodeInterface) {
    return;
  }

  // Extra checks for files, which have their own peculiarities.
  if (!_gatsby_will_process_file($entity)) {
    return;
  }

  // Always send content to preview server.
  $gatsbyPreview->gatsbyPreparePreviewData($entity, 'insert');

  $published = method_exists($entity, 'isPublished') && $entity->isPublished();

  // Check to see if we are only triggering builds for published content.
  if ($published || in_array($entity->getEntityTypeId(), $special_types)) {
    $gatsbyPreview->gatsbyPrepareBuildData($entity, 'insert');
  }
  drupal_register_shutdown_function('_gatsby_update');
}

/**
 * Implements hook_entity_delete().
 */
function gatsby_entity_delete(EntityInterface $entity): void {
  /** @var \Drupal\gatsby\GatsbyPreview $gatsbyPreview */
  $gatsbyPreview = \Drupal::service('gatsby.preview');
  $settings = \Drupal::config('gatsby.settings');
  $build_published = $settings->get('build_published');

  // Verify this is a supported entity type.
  if (!$gatsbyPreview->isSupportedEntity($entity)) {
    return;
  }

  // Return early if we are only acting on nodes.
  if ($build_published && !$entity instanceof NodeInterface) {
    return;
  }

  // Extra checks for files, which have their own peculiarities.
  if (!_gatsby_will_process_file($entity)) {
    return;
  }

  // Notify Gatsby of the change.
  $gatsbyPreview->gatsbyPreparePreviewData($entity, 'delete');
  $gatsbyPreview->gatsbyPrepareBuildData($entity, 'delete');
  drupal_register_shutdown_function('_gatsby_update');
}

/**
 * Implements hook_cron().
 */
function gatsby_cron(): void {
  // Do not delete entities if delete setting is not enabled.
  if (!\Drupal::config('gatsby.settings')->get('delete_log_entities')) {
    return;
  }

  // Make sure a valid expiration setting is set.
  $expiration = \Drupal::config('gatsby.settings')->get('log_expiration');
  if (!$expiration) {
    return;
  }

  \Drupal::service('gatsby.logger')->deleteExpiredLoggedEntities(time() - $expiration);

  $last_logtime = \Drupal::service('gatsby.logger')->getOldestLoggedEntityTimestamp();

  // Set last logtime as current time if there are no log entries.
  if (!$last_logtime) {
    $last_logtime = time();
  }

  // Store the log time of the last log entry in order to validate future syncs.
  \Drupal::state()->set('gatsby.last_logtime', $last_logtime);
}

/**
 * Determine whether the given file entity should be processed.
 *
 * @param \Drupal\Core\Entity\EntityInterface $entity
 *   Whether the entity should be processed.
 *
 * @return bool
 *   Whether the file should be processed.
 */
function _gatsby_will_process_file(EntityInterface $entity): bool {
  // Extra logic for files, which have their own peculiarities.
  if ($entity->getEntityTypeId() == 'file') {
    // Ignore files before they are marked "permanent".
    if ($entity->isTemporary()) {
      return FALSE;
    }

    // Private files are ignored by default, but may be published if an option
    // is enabled.
    if (substr($entity->getFileUri(), 0, 7) == 'private') {
      $gatsby_settings = \Drupal::config('gatsby.settings');
      if (!$gatsby_settings->get('publish_private_files')) {
        return FALSE;
      }
    }
  }

  return TRUE;
}

/**
 * Implements hook_entity_extra_field_info().
 */
function gatsby_entity_extra_field_info(): array {
  $extra = [];
  $entity_types = \Drupal::config('gatsby.settings')->get('supported_entity_types');

  if (!empty($entity_types)) {
    foreach ($entity_types as $entity_type) {
      $bundle_info = \Drupal::service('entity_type.bundle.info')->getBundleInfo($entity_type);
      $entity_type_definition = \Drupal::entityTypeManager()->getDefinition($entity_type);
      if (count($bundle_info) > 1 || $entity_type_definition->getKey('bundle')) {
        foreach ($bundle_info as $bundle => $info) {
          $extra[$entity_type][$bundle]['display']['gatsby_iframe_preview'] = [
            'label' => t('Gatsby iframe preview'),
            'description' => t('A preview of this content using the GatsbyJS system, displayed using an iframe.'),
            'weight' => 100,
            'visible' => FALSE,
          ];
        }
      }
      else {
        $extra[$entity_type][$entity_type_definition->getKey('bundle')]['display']['gatsby_iframe_preview'] = [
          'label' => t('Gatsby iframe preview'),
          'description' => t('A preview of this content using the GatsbyJS system, displayed using an iframe.'),
          'weight' => 100,
          'visible' => FALSE,
        ];
      }
    }
  }

  return $extra;
}

/**
 * Implements hook_entity_view().
 */
function gatsby_entity_view(array &$build, EntityInterface $entity, EntityViewDisplayInterface $display, $view_mode): void {
  if ($display->getComponent('gatsby_iframe_preview')) {
    // Don't run hook_entity_view if gatsby_endpoints is enabled.
    $server_urls = array_map('trim', explode(',', \Drupal::config('gatsby.settings')->get('server_url')));
    $server_url = reset($server_urls);

    // Skip the rest of the logic if no server URL is defined.
    if (empty($server_url)) {
      return;
    }

    // Render the preview for this entity.
    $gatsby_url = preg_replace('/\/$/', '', $server_url) . \Drupal::service('gatsby.path_mapping')->getPath($entity);
    $build['gatsby_iframe_preview'] = [
      '#type' => 'inline_template',
      '#template' => '<div class="gatsby-iframe-container"><iframe class="gatsby-iframe" src="{{ url }}"></iframe></div>',
      '#context' => [
        'url' => $gatsby_url,
      ],
      '#attached' => [
        'library' => [
          'gatsby/iframe_preview',
        ],
      ],
    ];
  }
}

/**
 * Triggers the update to the Gatsby Preview and Incremental Builds servers.
 *
 * @see gatsby_entity_insert()
 * @see gatsby_entity_update()
 * @see gatsby_entity_delete()
 */
function _gatsby_update(): void {
  $gatsbyPreview = \Drupal::service('gatsby.preview');
  $gatsbyPreview->gatsbyUpdate();
}

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

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