contextly-8.x-2.1/src/ContextlyBaseService.php

src/ContextlyBaseService.php
<?php

namespace Drupal\contextly;

use Drupal\Core\StringTranslation\StringTranslationTrait;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Drupal\Core\Url;
use Drupal\contextly\Contextly\ContextlyDrupalKit;
use Drupal\Core\Routing\TrustedRedirectResponse;
use Drupal\Core\Entity\EntityInterface;
use Drupal\field\Entity\FieldConfig;
use Drupal\Core\Entity\Display\EntityViewDisplayInterface;
use Drupal\node\NodeInterface;
use Drupal\node\Entity\NodeType;

/**
 * The ContextlyBaseService class.
 */
class ContextlyBaseService implements ContextlyBaseServiceInterface {

  use StringTranslationTrait;

  public function __construct(
    protected readonly ContainerInterface $container,
  ) {
  }

  /**
   * {@inheritdoc}
   */
  public function settingsValidateApiKey(string $key): array {
    // Parse string key into an array.
    $key = explode('-', $key, 2);
    if (count($key) !== 2) {
      return [FALSE, $this->t('API key is incorrect.')];
    }

    // Convert it into associative array for later use.
    $key = array_combine(['appID', 'appSecret'], $key);

    try {
      // Check API key. Create API client with isolated session.
      $settings = ContextlyDrupalKit::getDefaultSettings();
      foreach ($key as $name => $value) {
        $settings->{$name} = $value;
      }
      /** @var \Drupal\contextly\Contextly\ContextlyDrupalKit $kit */
      $kit = new ContextlyDrupalKit($settings);
      //$session = $kit->newApiSessionIsolated();
      $api = $kit->newApi();

      // Failed authorization should throw an exception at this point.
      $api->testCredentials();

      return [TRUE, $key];
    }
    catch (\Exception $e) {
      return [FALSE, $this->t('Test API request failed.')];
    }
  }

  /**
   * {@inheritdoc}
   */
  public function settingsCpTokenValue(string $type): string {
    return 'contextly/cp/' . $type;
  }

  /**
   * {@inheritdoc}
   */
  public function settingsSetApiKeyTokenValue() {
    return "contextly/set-api-key";
  }

  /**
   * {@inheritdoc}
   */
  public function settingsCpTourRedirect() {
    /** @var \Symfony\Component\HttpFoundation\Request $request */
    $request = $this->container->get('request_stack')->getMainRequest();
    if ($request) {
      // Cleanup destination in any case.
      $request->query->remove('destination');

      $token = $request->query->get('token');
      if (empty($token)) {
        throw new NotFoundHttpException();
      }

      /** @var \Drupal\Core\Access\CsrfTokenGenerator $token_generator */
      $token_generator = $this->container->get('csrf_token');
      $token_value = $this->settingsCpTokenValue('tour');
      if (!$token_generator->validate($token, $token_value)) {
        $this->container->get('messenger')
          ->addWarning($this->t('Something went wrong. Please try again.'));
        return new RedirectResponse(Url::fromUri('/admin/config/content/contextly'));
      }

      return $this->settingsUrl('/tour');
    }
  }

  /**
   * {@inheritdoc}
   */
  public function settingsSetApiKeyRedirect() {
    /** @var \Symfony\Component\HttpFoundation\Request $request */
    $request = $this->container->get('request_stack')->getMainRequest();
    if ($request) {
      // Cleanup destination in any case.
      $request->query->remove('destination');

      $token = $request->query->get('token');
      $api_key = $request->query->get('api_key');
      if (empty($token) || empty($api_key)) {
        throw new NotFoundHttpException();
      }

      /** @var \Drupal\Core\Access\CsrfTokenGenerator $token_generator */
      $token_generator = $this->container->get('csrf_token');
      $token_value = $this->settingsSetApiKeyTokenValue();
      if (!$token_generator->validate($token, $token_value)) {
        $this->container->get('messenger')
          ->addWarning($this->t('Something went wrong. Please try again.'));
        return new RedirectResponse(Url::fromRoute('contextly.contextly_admin_form'));
      }

      [$success, $result] = $this->settingsValidateApiKey($api_key);
      if ($success) {
        $this->container->get('state')->set('api_key', $result);
        $this->settingsResetSharedToken();
        $this->container->get('messenger')
          ->addStatus($this->t('Your API key has been successfully updated.'));
      }
      else {
        $this->container->get('messenger')->addError($result);
      }

      return new RedirectResponse(Url::fromRoute('contextly.contextly_admin_form'));
    }
  }

  /**
   * {@inheritdoc}
   */
  public function settingsUrl($type) {
    global $base_url;
    $site_name = $this->container->get('config.factory')
      ->get('system.site')->get('name');
    $token_value = $this->settingsSetApiKeyTokenValue();
    /** @var \Drupal\Core\Access\CsrfTokenGenerator $token_generator */
    $token_generator = $this->container->get('csrf_token');
    $token = $token_generator->get($token_value);
    $options = [
      'query' => [
        'token' => $token,
      ],
      'absolute' => TRUE,
    ];
    $url = Url::fromRoute('contextly.admin_controller_set_api_key', [], $options);
    $query = [
      'type' => $type,
      'blog_url' => $base_url,
      'blog_title' => $site_name,
      'cms_settings_page' => $url->toString(),
    ];

    /** @var \Drupal\contextly\Contextly\ContextlyDrupalKit $kit */
    $kit = ContextlyDrupalKit::getInstance();
    // Try to get the auth token and use direct login.
    try {
      $token = $kit->newApi()->getAccessToken();
      $query += [
        'contextly_access_token' => $token,
      ];
    }
    catch (\Exception $e) {
      // Just silently fail.
    }

    $path = $this->contextlyServerUrl('cp') . 'cms-redirect';
    $url = Url::fromUri($path, ['query' => $query])->toString();
    $redirect = new TrustedRedirectResponse($url);
    return $redirect->send();
  }

  /**
   * {@inheritdoc}
   */
  public function contextlyServerUrl($server_type,
    $secure = TRUE): string {
    $scheme = $secure ? 'https:' : 'http:';
    $server_mode = $this->container->get('config.factory')
      ->get('contextly.settings')->get('server_mode') ?: 'dev';
    $servers = $this->contextlyServers($scheme);
    if (!empty($servers[$server_mode][$server_type])) {
      return $servers[$server_mode][$server_type];
    }

    return '';
  }

  /**
   * {@inheritdoc}
   */
  public function contextlyServers(string $scheme): array {
    return [
      'dev' => [
        'main' => "$scheme//dev.contextly.com/",
        'cp' => "$scheme//dev.contextly.com/",
        'api' => "$scheme//devrest.contextly.com/",
      ],
      'live' => [
        'main' => "$scheme//contextly.com/",
        'cp' => "$scheme//contextly.com/",
        'api' => "$scheme//rest.contextly.com/",
      ],
    ];
  }

  /**
   * {@inheritdoc}
   */
  public function settingsResetSharedToken() {
    /** @var \Drupal\contextly\Contextly\ContextlyDrupalKit $kit */
    $kit = ContextlyDrupalKit::getInstance();
    $kit->newApiSession()->removeSharedToken();
  }

  /**
   * {@inheritdoc}
   */
  public function nodeContextlyIsDisabled(NodeInterface $node): bool {
    if ($this->isNodeTypeEnabled($node->bundle())) {
      return !empty($node->contextly_disabled->value);
    }

    return TRUE;
  }

  /**
   * {@inheritdoc}
   */
  public function getApiKey(): array {
    $config = $this->container->get('config.factory')
      ->get('contextly.settings');
    $key = explode('-', $config->get('api_key'));
    if (count($key) !== 2) {
      return [];
    }
    // Convert it into associative array for later use.
    return array_combine(['appID', 'appSecret'], $key);
  }

  /**
   * {@inheritdoc}
   */
  public function isApiKeySet(): bool {
    return !empty($this->getApiKey());
  }

  /**
   * {@inheritdoc}
   */
  public function isNodeTypeEnabled(string $type_name): bool {
    $config = $this->container->get('config.factory')
      ->get('contextly.contextly_node_types_admin');

    if ($config->get('contextly_all_node_types')) {
      return TRUE;
    }

    $enabled_types = $config->get('contextly_node_types');
    if (!is_array($enabled_types)) {
      return FALSE;
    }

    return in_array($type_name, $enabled_types, TRUE);
  }

  /**
   * {@inheritdoc}
   */
  public function saveNodeRevisionSettings(NodeInterface $node) {
    $fields = [
      'disabled' => (int) $this->nodeContextlyIsDisabled($node),
    ];
    $key = [
      'nid' => $node->id(),
      'vid' => $node->getRevisionId(),
    ];

    $this->container->get('database')->merge('contextly_node_settings')
      ->key($key)
      ->fields($fields)
      ->execute();
  }

  /**
   * {@inheritdoc}
   */
  public function watchdogException(\Exception $exception,
    string $message = NULL) {
    $details = (string) $exception;
    if (isset($message)) {
      $details = "{$message}\n\n{$details}";
    }
    $details = nl2br($details);
    $this->container->get('logger.factory')->get('contextly')->error($details);
  }

  /**
   * {@inheritdoc}
   */
  public function nodeChanged(NodeInterface $node) {
    if ($this->isApiKeySet() && $this->isNodeTypeEnabled($node->bundle())) {
      /** @var \Drupal\contextly\Contextly\ContextlyDrupalKit $kit */
      $kit = ContextlyDrupalKit::getInstance();
      try {
        $kit->newDrupalNodeEditor()->putNode($node);
      }
      catch (\ContextlyKitException $e) {
        $this->watchdogException($e, 'Unable to send the node to the Contextly');
        if ($this->container->get('current_user')->hasPermission('manage contextly links')) {
          $this->container->get('messenger')
            ->addError('Unable to send the node to the Contextly. See log for details.');
        }
      }
    }
  }

  /**
   * {@inheritdoc}
   */
  public function removeNodeRevisionSettings(NodeInterface $node) {
    $this->container->get('database')->delete('contextly_node_settings')
      ->condition('vid', $node->vid)
      ->execute();
  }

  /**
   * {@inheritdoc}
   */
  public function getEnabledTypes() {
    $config = $this->container->get('config.factory')
      ->get('contextly.contextly_node_types_admin');
    // $all = $config->get('contextly_all_node_types');
    if ($config->get('contextly_all_node_types')) {
      return array_keys(NodeType::loadMultiple());
    }
    else {
      return $config->get('contextly_node_types');
    }
  }

  /**
   * {@inheritdoc}
   */
  public function getNodeTypeFields(string $node_type,
    array $field_types) {
    $fields = [];
    $instances = $this->container->get('entity_field.manager')
      ->getFieldDefinitions('node', $node_type);
    foreach ($instances as $field_definition) {
      if ($field_definition instanceof FieldConfig &&
        (in_array($field_definition->getType(), $field_types, TRUE) ||
        in_array($field_definition->getSetting('handler'), $field_types, TRUE))) {
        $fields[$field_definition->getName()] = $field_definition->getLabel();
      }
    }
    return $fields;
  }

  /**
   * {@inheritdoc}
   */
  public function settingsGetAvailableFields(array $field_types) {
    $enabled_types = $this->getEnabledTypes();
    $available_fields = [];
    foreach ($enabled_types as $node_type) {
      $node_type_fields = $this->getNodeTypeFields($node_type, $field_types);
      if (!empty($node_type_fields)) {
        $available_fields[$node_type] = $node_type_fields;
      }
    }
    return $available_fields;
  }

  /**
   * {@inheritdoc}
   */
  public function nodeWidgetsSettings(NodeInterface $node): array {
    $api_key = $this->getApiKey();
    $app_id = '';
    if (isset($api_key['appID'])) {
      $app_id = $api_key['appID'];
    }

    /** @var \Drupal\contextly\Contextly\ContextlyDrupalKit $kit */
    $kit = ContextlyDrupalKit::getInstance();
    $isHttps = $kit->isHttps();

    return [
      'contextlyWidgets' => [
        'version' => CONTEXTLY_CLIENT_VERSION,
        'appId' => $app_id,
        'apiServerURL' => $this->contextlyServerUrl('api', $isHttps),
        'mainServerURL' => $this->contextlyServerUrl('main', $isHttps),
        'renderLinkWidgets' => empty($node->contextly_disabled),
      ],
    ];
  }

  /**
   * {@inheritdoc}
   */
  public function contextlyNodeView(array &$build,
    EntityInterface $entity,
    EntityViewDisplayInterface $display,
    $view_mode) {

    if (!$this->isApiKeySet()) {
      // API key is not set.
      return;
    }

    if (!$this->isNodeTypeEnabled($entity->bundle())) {
      // Node bundle is not enable to contextly view.
      return;
    }

    // Show widget on the node view page only.
    if ($view_mode !== 'full' || !node_is_page($entity)) {
      return;
    }

    $build['contextly_widget'] = [
      '#weight' => 0,
    ];
    $build['contextly_widget']['#attached'] = [
      'library' => [
        'contextly/widget',
        'contextly/node-view',
      ],
      'drupalSettings' => $this->nodeWidgetsSettings($entity),
    ];
    $build['storyline_subscribe'] = [
      '#theme_wrappers' => ['container'],
      '#attributes' => [
        'id' => 'ctx-sl-subscribe',
        'class' => ['ctx-clearfix'],
      ],
    ];
    $build['module'] = [
      '#theme_wrappers' => ['container'],
      '#attributes' => [
        'id' => 'ctx-module',
        'class' => ['ctx-module-container', 'ctx-clearfix'],
      ],
    ];

    /** @var \Drupal\contextly\Contextly\ContextlyDrupalKit $kit */
    $kit = ContextlyDrupalKit::getInstance();
    // Add Contextly meta-data to the post at this point.
    $metadata = $kit->newDrupalNodeData()->getMetadata();
    $metadata_tag = [
      '#type' => 'html_tag',
      '#tag' => 'meta',
      '#attributes' => [
        'name' => 'contextly-page',
        'id' => 'contextly-page',
        'content' => json_encode($metadata),
      ],
    ];
    $build['#attached']['html_head'][] = [$metadata_tag, 'contextly_metadata'];
  }

  /**
   * {@inheritdoc}
   */
  public function formatDate($timestamp): string {
    $system_date = $this->container->get('config.factory')->get('system.date');
    $timezone = $system_date
      ->get('timezone_default') ?: date_default_timezone_get();
    return \Drupal::service('date.formatter')
      ->format($timestamp, 'custom', 'Y-m-d H:i:s', $timezone);
  }

  /**
   * {@inheritdoc}
   */
  public function getSettings(NodeInterface $node): array {
    global $base_url;
    return [
      'contextlyEditor' => [
        'token' => $this->container->get('csrf_token')
          ->get('contextly/node-edit/' . $node->id()),
        'nid' => $node->id(),
        'baseUrl' => $base_url . '/',
      ],
    ];
  }

}

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

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