semantic_connector-8.x-1.1/src/SemanticConnector.php

src/SemanticConnector.php
<?php

/**
 * @file
 * The main class of the Semantic Connector.
 */

namespace Drupal\semantic_connector;

use Drupal\Core\Language\LanguageInterface;
use Drupal\Core\Link;
use Drupal\Core\Url;
use Drupal\powertagging\Entity\PowerTaggingConfig;
use Drupal\pp_graphsearch\Entity\PPGraphSearchConfig;
use Drupal\pp_graphsearch\PPGraphSearch;
use Drupal\pp_taxonomy_manager\Entity\PPTaxonomyManagerConfig;
use Drupal\semantic_connector\Entity\SemanticConnectorConnection;
use Drupal\semantic_connector\Entity\SemanticConnectorPPServerConnection;
use Drupal\semantic_connector\Entity\SemanticConnectorSparqlEndpointConnection;
use Drupal\smart_glossary\Entity\SmartGlossaryConfig;


/**
 * A collection of static functions offered by the PoolParty Semantic Connector.
 */
class SemanticConnector {

  /**
   * Get a connection of the PoolParty Semantic Connector by its ID.
   *
   * @param string $type
   *   The type of the connection to receive. Possible values: 'pp_server',
   *   and 'sparql_endpoint'.
   * @param int $connection_id
   *   The ID of the Semantic Connector Connection.
   *
   * @return SemanticConnectorConnection
   *   The connection object, depending on the provided $type.
   */
  public static function getConnection($type, $connection_id = 0) {
    switch ($type) {
      case 'pp_server':
        if (empty($connection_id)) {
          return SemanticConnectorPPServerConnection::create();
        }
        else {
          return SemanticConnectorPPServerConnection::load($connection_id);
        }

      case 'sparql_endpoint':
        if (empty($connection_id)) {
          return SemanticConnectorSparqlEndpointConnection::create();
        }
        else {
          return SemanticConnectorSparqlEndpointConnection::load($connection_id);
        }

      default:
        return NULL;
    }
  }

  /**
   * Get all connection of the PoolParty Semantic Connector by connection-type.g
   *
   * @param string $type
   *   The type of the connections to receive. Possible values: 'pp_server'
   *   and 'sparql_endpoint'.
   *
   * @return array
   *   Array of SemanticConnectorConnection-objects of the give type.
   */
  public static function getConnectionsByType($type) {
    $controller = \Drupal::entityTypeManager()
      ->getStorage($type . '_connection');
    return $controller->loadMultiple();
  }

  /**
   * Search for connections matching a set of search filters.
   *
   * @param string $type
   *   The type of the connections to receive. Possible values: 'pp_server'
   *   and 'sparql_endpoint'.
   * @param array $search_filters
   *   An associative array of search filters, where the key is the name of the
   *   database field to search in and the value is a string, which will
   *   filtered for on "Exact match"-basis.
   *   Possible keys are: 'url', 'title', 'username', 'password' and
   *   'config' (config needs to be a serialized array)
   *
   * @return array
   *   Array of SemanticConnectorConnection-objects matching the search filters.
   */
  public static function searchConnections($type, array $search_filters) {
    $connections_query = \Drupal::entityQuery($type . '_connection');

    $allowed_filter_keys = ['url', 'title', 'username', 'password', 'config'];
    foreach ($search_filters as $search_filter_key => $search_filter_value) {
      if (in_array($search_filter_key, $allowed_filter_keys) && (is_string($search_filter_value) || is_array($search_filter_value))) {
        $connections_query->condition($search_filter_key, $search_filter_value);
      }
    }

    $connections_found = $connections_query->execute();
    $connections = [];
    if (!empty($connections_query)) {
      $controller = \Drupal::entityTypeManager()
        ->getStorage($type . '_connection');
      $connections = $controller->loadMultiple($connections_found);
    }

    return $connections;
  }

  /**
   * Create a new connection for the PoolParty Semantic Connector.
   *
   * @param string $type
   *   The type of the connection to receive. Possible values: 'pp_server',
   *   'sonr_server' and 'sparql_endpoint'.
   * @param string $url
   *   The URL of the connection.
   * @param string $title
   *   The title of the connection.
   * @param array $credentials
   *   The credentials required for the connection in the format
   *   "username:password" if required.
   * @param array $config
   *   The config of the Semantic Connector Connection as an array.
   * @param int $fixed_id
   *   A fixed connection id to use instead of the one with the given $type and
   *   $url.
   *   WARNING: Using an ID that does not exist will result in an error.
   *
   * @return SemanticConnectorConnection
   *   The connection object, depending on the provided $type.
   */
  public static function createConnection($type, $url, $title, array $credentials = [
    'username' => '',
    'password' => '',
  ], array                                $config = [], $fixed_id = 0) {
    $connection = NULL;
    $allowed_types = ['pp_server', 'sparql_endpoint'];

    if (!in_array($type, $allowed_types)) {
      \Drupal::messenger()
        ->addMessage(t('The type (%type) of the connection %title is wrong.', [
          '%type' => $type,
          '%title' => $title,
        ]), 'error');
      return NULL;
    }

    // Remove trailing slashes from the URL.
    $url = rtrim($url, "/");

    if ($fixed_id <= 0) {
      $query = \Drupal::entityQuery($type . '_connection');
      $old_connection_ids = $query->condition('url', $url)->execute();
      $old_connection_id = reset($old_connection_ids);
    }
    else {
      $old_connection_id = $fixed_id;
    }

    // If there is a connection available with the url, load it.
    if ($old_connection_id !== FALSE) {
      switch ($type) {
        case 'pp_server':
          $connection = SemanticConnectorPPServerConnection::load($old_connection_id);
          break;

        case 'sparql_endpoint':
          $connection = SemanticConnectorSparqlEndpointConnection::load($old_connection_id);
          break;
      }

      // If there already is a connection available, change if data has changed.
      $has_changed = FALSE;
      /** @var SemanticConnectorConnection $connection */
      if ($connection->getTitle() != $title) {
        $connection->setTitle($title);
        $has_changed = TRUE;
      }
      if ($connection->getUrl() != $url) {
        $connection->setUrl($url);
        $has_changed = TRUE;
      }
      if ($connection->getCredentials() != $credentials) {
        $connection->setCredentials($credentials);
        $has_changed = TRUE;
      }
      if (!empty($config) && $connection->getConfig() != $config) {
        $connection->setConfig(array_merge($connection->getConfig(), $config));
        $has_changed = TRUE;
      }

      // Save the connection if its data has changed.
      if ($has_changed) {
        $connection->save();
      }
    }
    // Data was not found in the DB --> Really create a new Connection.
    else {
      switch ($type) {
        case 'pp_server':
          $connection = SemanticConnectorPPServerConnection::create();
          break;

        case 'sparql_endpoint':
          $connection = SemanticConnectorSparqlEndpointConnection::create();
          break;
      }

      // Set the ID.
      $connection->set('id', self::createUniqueEntityMachineName($type . '_connection', $title));

      // Set all the required variables and save the connection.
      $connection->setTitle($title);
      $connection->setUrl($url);
      $connection->setCredentials($credentials);
      $connection->setConfig(array_merge($connection->getDefaultConfig(), $config));
      $connection->save();
    }

    return $connection;
  }

  /**
   * Delete one or multiple Semantic Connector connections.
   *
   * TODO: Remove all the sparql endpoints related to the Semantic Connector
   * connections.
   *
   * @param string $type
   *   The type of the connections to receive. Possible values: 'pp_server'
   *   and 'sparql_endpoint'.
   * @param array $connection_ids
   *   A single connection_id or an array of connection_ids to remove.
   */
  public static function deleteConnections($type, array $connection_ids) {
    $controller = \Drupal::entityTypeManager()
      ->getStorage($type . '_connection');
    $entities = $controller->loadMultiple($connection_ids);
    $controller->delete($entities);
  }

  /**
   * Check what Semantic Connector connections are used by which module.
   *
   * @param array $modules_to_check
   *   An array of module keys to check for connections.
   *
   * @return array
   *   Associative array of connections usages, categorized by connection_id and
   *   then by module_key.
   */
  public static function checkConnectionUsage(array $modules_to_check = [
    'pp_taxonomy_manager',
    'powertagging',
    'smart_glossary',
    'pp_graphsearch',
  ]) {
    $connections_used = [];

    foreach ($modules_to_check as $module_key) {
      if (\Drupal::moduleHandler()->moduleExists($module_key)) {
        switch ($module_key) {
          case 'powertagging':
            /** @var PowerTaggingConfig $config */ foreach (PowerTaggingConfig::loadMultiple() as $config) {
            if (!isset($connections_used[$config->getConnectionId()])) {
              $connections_used[$config->getConnectionId()] = [];;
            }
            if (!isset($connections_used[$config->getConnectionId()][$module_key])) {
              $connections_used[$config->getConnectionId()][$module_key] = [];
            }
            $connections_used[$config->getConnectionId()][$module_key][] = [
              'id' => $config->id(),
              'title' => $config->getTitle(),
              'project_id' => $config->getProjectId(),
            ];
          }
            break;

          case 'smart_glossary':
            /** @var SmartGlossaryConfig $config */ foreach (SmartGlossaryConfig::loadMultiple() as $config) {
            if (!isset($connections_used[$config->getConnectionID()])) {
              $connections_used[$config->getConnectionID()] = [];;
            }
            if (!isset($connections_used[$config->getConnectionID()][$module_key])) {
              $connections_used[$config->getConnectionID()][$module_key] = [];
            }
            $connections_used[$config->getConnectionID()][$module_key][] = [
              'id' => $config->id(),
              'title' => $config->getTitle(),
            ];
          }
            break;

          case 'pp_graphsearch':
            /** @var PPGraphSearchConfig $config */ foreach (PPGraphSearchConfig::loadMultiple() as $config) {
            $connection_id = $config->getConnectionId();
            if (!isset($connections_used[$connection_id])) {
              $connections_used[$connection_id] = [];;
            }
            if (!isset($connections_used[$connection_id][$module_key])) {
              $connections_used[$connection_id][$module_key] = [];
            }
            $connections_used[$connection_id][$module_key][] = [
              'id' => $config->id(),
              'title' => $config->getTitle(),
              'project_id' => $config->getSearchSpaceId(),
            ];
          }
            break;

          case 'pp_taxonomy_manager':
            /** @var PPTaxonomyManagerConfig $config */ foreach (PPTaxonomyManagerConfig::loadMultiple() as $config) {
            $settings = $config->getConfig();
            $connection_id = $config->getConnectionId();
            if (!isset($connections_used[$connection_id])) {
              $connections_used[$connection_id][$module_key] = [];
            }
            $connections_used[$connection_id][$module_key][] = [
              'id' => $config->id(),
              'title' => $config->getTitle(),
              'root_level' => $settings['root_level'],
              'project_id' => $config->getProjectId(),
              'project_ids' => $settings['taxonomies'],
            ];
          }
            break;
        }
      }
    }

    return $connections_used;
  }

  /**
   * Theme buttons to edit or delete a Semantic Connector connection.
   *
   * @param SemanticConnectorConnection $connection
   *   The Semantic Connector connection to theme the buttons for.
   * @param bool $can_be_deleted
   *   Whether a delete-button should be added or not.
   *
   * @return string
   *   The rendered HTML.
   */
  public static function themeConnectionButtons(SemanticConnectorConnection $connection, $can_be_deleted = FALSE) {
    $type = $connection->getType();
    $output = '<div class="semantic-connector-connection-buttons">';

    // Edit-button.
    $output .= Link::fromTextAndUrl(t('Edit'), Url::fromRoute('entity.' . $type . '_connection.edit_form', [$type . '_connection' => $connection->getId()], ['attributes' => ['class' => ['semantic-connector-connection-buttons-edit']]]))
      ->toString();

    // Delete button.
    if ($can_be_deleted) {
      $output .= '|' . Link::fromTextAndUrl(t('Delete'), Url::fromRoute('entity.' . $type . '_connection.delete_form', [$type . '_connection' => $connection->getId()], ['attributes' => ['class' => ['semantic-connector-connection-buttons-delete']]]))
          ->toString();
    }

    // Refresh projects button.
    if ($type == 'pp_server') {
      $output .= '|' . Link::fromTextAndUrl(t('Refresh server details'), Url::fromRoute('entity.pp_server_connection.refresh', ['connection' => $connection->getId()], ['attributes' => ['class' => ['semantic-connector-connection-buttons-refresh']]]))
          ->toString();
    }

    $server_config = $connection->getConfig();
    if (isset($server_config['version'])) {
      $output .= '|<span class="semantic-connector-connection-version">Version: ' . $server_config['version'] . '</span>';
    }

    // Get license information.
    if ($connection->getType() == 'pp_server') {
      $license_information = $connection->getApi('PPT')->getLicense();
      $license_classes = self::checkPoolpartyLicenses($connection, $license_information, TRUE);
      $output .= '|<span class="semantic-connector-connection-license ' . implode(' ', $license_classes) . '">License valid until: ' . (isset($license_information['expiryDateInMillis']) ? date('j. M Y', $license_information['expiryDateInMillis'] / 1000) : '-') . '</span>';
    }

    $output .= '</div>';
    return $output;
  }

  /**
   * Theme concepts with all their possible destinations.
   *
   * @param array $concepts
   *   An associative array containing following keys:
   *   - "html" --> The HTML of a concept, that will be used as the link text
   *   - "uri" --> The URI of the concept; if the URI is left empty, this item
   *     will be handled as a free term (no linking, but still added to the
   *   list)
   *   - "alt_labels" (optional) --> The alt labels to be added into the hidden
   *   box
   *   - "hidden_labels" (optional) --> The hidden labels to be added into the
   *   hidden box
   * @param int $connection_id
   *   The ID of the Semantic Connector connection.
   * @param string $project_id
   *   The ID of the project this concept is from.
   * @param string $separator
   *   If more than one concept is given, the list of concepts will will be
   *   separated with this string.
   * @param array $ignore_destinations
   *   An array of destination IDs, which should not be displayed.
   *
   * @return string
   *   The themed list of concepts.
   */
  public static function themeConcepts(array $concepts, $connection_id, $project_id, $separator = ', ', array $ignore_destinations = []) {
    global $base_path;
    $themed_items = [];
    $destinations = self::getDestinations();

    if (!empty($concepts)) {
      // Get all URI --> tid connections to avoid lots of database requests.
      $uri_tid_mapping = [];
      if ($destinations['taxonomy_term_detail_page']['use'] && !in_array('taxonomy_term_detail_page', $ignore_destinations)) {
        $query = \Drupal::database()->select('taxonomy_term__field_uri', 'u');
        $query->fields('u', ['field_uri_uri', 'entity_id']);
        $uri_tid_mapping = $query->execute()->fetchAllKeyed();
      }
      $pp_server_connection = SemanticConnector::getConnection('pp_server', $connection_id);

      $smart_glossary_destinations = [];
      if (isset($destinations['smart_glossary_detail_page']) && $destinations['smart_glossary_detail_page']['use'] && !in_array('smart_glossary_detail_page', $ignore_destinations)) {

        $server_config = $pp_server_connection->getConfig();
        if (isset($server_config['projects']) && !empty($server_config['projects'])) {
          foreach ($server_config['projects'] as $project) {
            if ($project['id'] == $project_id) {
              if (isset($project['sparql_endpoint_url'])) {
                // Get all IDs of SPARQL endpoint connections matching the
                // project's sparql endpoints url.
                $connection_query = \Drupal::entityQuery('sparql_endpoint_connection');
                $connection_query->condition('url', $project['sparql_endpoint_url']);
                $sparql_endpoint_connection_ids = $connection_query->execute();
                if (!empty($sparql_endpoint_connection_ids)) {
                  // Load all Smart Glossary configurations for the connection.
                  $smart_glossary_configs = \Drupal::entityTypeManager()
                    ->getStorage('smart_glossary')
                    ->loadByProperties(['connection_id' => array_keys($sparql_endpoint_connection_ids)]);

                  /** @var SmartGlossaryConfig $smart_glossary_config */
                  foreach ($smart_glossary_configs as $smart_glossary_config) {
                    $language_mapping = $smart_glossary_config->getLanguageMapping();
                    $advanced_settings = $smart_glossary_config->getAdvancedSettings();
                    //@todo: add multilanguage support.
                    $default_language = \Drupal::languageManager()
                      ->getDefaultLanguage()
                      ->getId();
                    if (isset($language_mapping[$default_language]) && !empty($language_mapping[$default_language]['glossary_languages'][0]) && (!isset($advanced_settings['semantic_connection']['show_in_destinations']) || $advanced_settings['semantic_connection']['show_in_destinations'])) {
                      $smart_glossary_destinations[$smart_glossary_config->getBasePath() . '/' . $language_mapping[$default_language]['glossary_languages'][0]] = $smart_glossary_config->getTitle();
                    }
                  }
                }
              }
              break;
            }
          }
        }
      }

      $pp_graphsearch_destinations = [];
      if (isset($destinations['pp_graphsearch']) && $destinations['pp_graphsearch']['use'] && !in_array('pp_graphsearch', $ignore_destinations)) {

        $connection_config = $pp_server_connection->getConfig();
        if (isset($connection_config["graphsearch_configuration"]) && !empty($connection_config["graphsearch_configuration"])) {
          if (isset($connection_config["graphsearch_configuration"]['projects'][$project_id])) {
            $search_space_ids = array_keys($connection_config["graphsearch_configuration"]['projects'][$project_id]['search_spaces']);

            // Get all block paths of sOnr webmining blocks, which use the given
            // connection ID and project ID.
            $pp_graphsearch_configs = \Drupal::entityTypeManager()
              ->getStorage('pp_graphsearch')
              ->loadByProperties([
                'connection_id' => $connection_id,
                'search_space_id' => $search_space_ids,
              ]);

            if (!empty($pp_graphsearch_configs)) {
              /** @var PPGraphSearchConfig $pp_graphsearch_config */
              foreach ($pp_graphsearch_configs as $pp_graphsearch_config) {
                $advanced_settings = $pp_graphsearch_config->getConfig();
                if (!isset($advanced_settings['semantic_connection']['show_in_destinations']) || $advanced_settings['semantic_connection']['show_in_destinations']) {
                  // Use the first concrete path of the block.
                  $pp_graphsearch = new PPGraphSearch($pp_graphsearch_config);
                  $pp_graphsearch_block_path = $pp_graphsearch->getBlockPath();
                  if (!empty($pp_graphsearch_block_path)) {
                    $pp_graphsearch_destinations[($pp_graphsearch_block_path == '<front>' ? '' : $pp_graphsearch_block_path)] = $pp_graphsearch_config->getTitle();
                  }
                }
              }
            }
          }
        }
      }

      foreach ($concepts as $concept) {
        if (!isset($concept['uri']) | !isset($concept['html'])) {
          continue;
        }

        // Free terms.
        if (empty($concept['uri'])) {
          $themed_items[] = $concept['html'];
        }
        // Real concepts.
        else {
          $destination_links = [];
          // Destinations are ordered by weight already, so we don't have to check
          // this property here.
          foreach ($destinations as $destination_id => $destination) {
            if ($destination['use']) {
              switch ($destination_id) {
                case 'taxonomy_term_detail_page':
                  if (isset($uri_tid_mapping[$concept['uri']])) {
                    $destination_links['taxonomy/term/' . $uri_tid_mapping[$concept['uri']]] = $destination['list_title'];
                  }
                  break;
                case 'smart_glossary_detail_page':
                  foreach ($smart_glossary_destinations as $smart_glossary_path => $smart_glossary_title) {
                    $destination_links[$smart_glossary_path . '/concept?uri=' . $concept['uri']] = $destination['list_title'] . (count($smart_glossary_destinations) > 1 ? ' (' . $smart_glossary_title . ')' : '');
                  }
                  break;
                case 'pp_graphsearch':
                  foreach ($pp_graphsearch_destinations as $pp_graphsearch_path => $pp_graphsearch_title) {
                    $destination_links[$pp_graphsearch_path . '?uri=' . $concept['uri']] = $destination['list_title'] . (count($pp_graphsearch_destinations) > 1 ? ' (' . $pp_graphsearch_title . ')' : '');
                  }
                  break;
              }
            }
          }

          // Theme the item.
          $themed_item_content = '';
          if (empty($destination_links)) {
            $themed_item_content .= $concept['html'];
          }
          else {
            $themed_item_content .= '<div class="semantic-connector-concept"><ul class="semantic-connector-concept-menu"><li><a class="semantic-connector-concept-link" href="' . $base_path . key($destination_links) . '">' . $concept['html'] . '</a>';
            if (count($destination_links) > 1) {
              $themed_item_content .= '<ul class="semantic-connector-concept-destination-links">';
              foreach ($destination_links as $destination_link_path => $destination_link_label) {
                // Remove initial slash in the path if available.
                if (substr($destination_link_path, 0, 1) == '/') {
                  $destination_link_path = substr($destination_link_path, 1);
                }
                $themed_item_content .= '<li class="semantic-connector-concept-destination-link"><a href="' . $base_path . $destination_link_path . '">' . $destination_link_label . '</a></li>';
              }
              $themed_item_content .= '</ul>';
            }
            $hidden_box_content = '';
            if (isset($concept['alt_labels']) && !empty($concept['alt_labels'])) {
              $hidden_box_content .= implode(', ', $concept['alt_labels']);
            }
            if (isset($concept['hidden_labels']) && !empty($concept['hidden_labels'])) {
              $hidden_box_content .= (!empty($hidden_box_content) ? ',' : '') . implode(', ', $concept['hidden_labels']);
            }
            $themed_item_content .= '</li></ul>' . (!empty($hidden_box_content) ? '<div class="semantic-connector-concept-hidden-box">' . $hidden_box_content . '</div>' : '') . '</div>';
          }
          $themed_items[] = $themed_item_content;
        }
      }
    }

    return implode($separator, $themed_items);
  }

  /**
   * Get an array of available destinations to go to from a concept link.
   *
   * @return array
   *   The array of destinations keyed by the destination-id, each one is an
   *   array with following keys:
   *   - "weight" --> The weight that defines the order of this destination in
   *   the list of available destinations.
   *   - "label" --> A label describing this destination.
   *   - "list_title" --> The title of the destination for the users in the
   *   list
   *     of available destinations.
   *   - "use" --> TRUE if this destination has to be used, FALSE if not.
   */
  public static function getDestinations() {
    // An array of available destinations with their default values.
    $available_destinations = [
      'taxonomy_term_detail_page' => [
        'weight' => 1,
        'label' => t('Taxonomy Term Detail Page'),
        'list_title' => 'Taxonomy Term Detail Page',
        'use' => FALSE,
      ],
    ];
    if (\Drupal::moduleHandler()->moduleExists('smart_glossary')) {
      $available_destinations['smart_glossary_detail_page'] = [
        'weight' => 0,
        'label' => t('Smart Glossary Detail Page'),
        'list_title' => 'Smart Glossary Detail Page',
        'use' => FALSE,
      ];
    }
    if (\Drupal::moduleHandler()->moduleExists('pp_graphsearch')) {
      $available_destinations['pp_graphsearch'] = [
        'weight' => 2,
        'label' => t('PoolParty GraphSearch Page'),
        'list_title' => 'PoolParty GraphSearch Page',
        'use' => FALSE,
      ];
    }

    // Replace the default values with actual saved values.
    $term_destination_options = \Drupal::config('semantic_connector.settings')
      ->get('term_click_destinations');
    if (!is_null($term_destination_options)) {
      foreach ($term_destination_options as $destination_id => $destination) {
        if (isset($available_destinations[$destination_id])) {
          foreach (array_keys($available_destinations[$destination_id]) as $destination_property) {
            if (isset($destination[$destination_property])) {
              $available_destinations[$destination_id][$destination_property] = $destination[$destination_property];
            }
          }
        }
      }
    }

    // Order the destinations by weight.
    uasort($available_destinations, '\Drupal\Component\Utility\SortArray::sortByWeightElement');

    return $available_destinations;
  }

  /**
   * Get detailed information about SPARQL endpoints from a PoolParty server.
   *
   * @param string $connection_id
   *   The ID of the SPARQL endpoint connection
   *
   * @return array|bool
   *   Array of information found found for this SPARQL endpoint containing
   *   following keys:
   *   - "pp_connection_id" --> The ID of the corresponding PoolParty server
   *     connection containing the SPARQL endpoint.
   *   - "project_id" --> The ID of the project using the SPARQL endpoint.
   *   or FALSE if no information was found or if this connection does not
   *   exist.
   */
  public static function getSparqlConnectionDetails($connection_id) {
    $sparql_connection = SemanticConnector::getConnection('sparql_endpoint', $connection_id);
    if (!is_null($sparql_connection)) {
      $pp_server_connections = SemanticConnector::getConnectionsByType('pp_server');
      foreach ($pp_server_connections as $pp_server_connection) {
        $server_config = $pp_server_connection->getConfig();
        if (isset($server_config['projects']) && !empty($server_config['projects'])) {
          foreach ($server_config['projects'] as $project) {
            if (isset($project->sparql_endpoint_url) && $project->sparql_endpoint_url == $sparql_connection->getUrl()) {
              return [
                'pp_connection_id' => $pp_server_connection->getId(),
                'project_id' => $project->id,
              ];
            }
          }
        }
      }
    }

    return FALSE;
  }

  /**
   * Create a unique machine name for an entity based on a title.
   *
   * @param string $entity_id
   *   The ID of the entity to get the machine name for
   * @param string $title
   *   The title to build the machine name with.
   *
   * @return string
   *   The machine name
   */
  public static function createUniqueEntityMachineName($entity_id, $title) {
    // Title and entity ID may not be empty.
    if (empty($entity_id) || empty($title)) {
      return NULL;
    }

    $machine_name = \Drupal::transliteration()
      ->transliterate($title, LanguageInterface::LANGCODE_DEFAULT, '_');
    $machine_name = str_replace(' ', '_', mb_strtolower($machine_name));

    $entity_ids = \Drupal::entityQuery($entity_id)
      ->condition('id', $machine_name, 'STARTS_WITH')
      ->execute();

    // The machine name is already in use, check for a new one.
    if (!empty($entity_ids) && in_array($machine_name, $entity_ids)) {
      $machine_name_count = 1;
      while (TRUE) {
        $new_machine_name = $machine_name . '_' . $machine_name_count;
        if (!in_array($new_machine_name, $entity_ids)) {
          return $new_machine_name;
        }
        $machine_name_count++;
      }
    }

    return $machine_name;
  }

  /**
   * Get the configuration for the global notifications.
   *
   * @return array
   *   An associative array of notification settings.
   */
  public static function getGlobalNotificationConfig() {
    return \Drupal::config('semantic_connector.settings')->get('notifications');
  }

  /**
   * Get the configuration for the global notifications.
   *
   * @return array
   *   An array of associative action arrays.
   *   Every action has following properties:
   *   - id: string that gets used as the key of the action
   *   - title
   *   - description
   *   - default_value: boolean default value
   *   - callback: the function to call to check for the notification
   */
  public static function getGlobalNotificationActions() {
    $actions = [];

    $default_action = [
      'id' => '',
      'title' => '',
      'description' => '',
      'default_value' => TRUE,
      'callback' => '',
    ];

    \Drupal::moduleHandler()
      ->invokeAllWith('semantic_connector_global_notification_actions', function (callable $hook, string $module) use ($default_action, &$actions) {
        $module_actions = $hook();
        foreach ($module_actions as $module_action) {
          $module_action = $module_action + $default_action;
          // At least ID, title and callback have to be given.
          if (!empty($module_action['id']) && !empty($module_action['title']) && !empty($module_action['callback'])) {
            $actions[] = $module_action;
          }
        }
      });

    return $actions;
  }

  /**
   * Retrieve the timestamp of the last notification check.
   *
   * @return string
   *   The timestamp of the last notification check.
   */
  public static function getLastNotificationCheckTime() {
    return \Drupal::state()
      ->get('semantic_connector.global_notification_last_check');
  }

  /**
   * Set the last notification check timestamp.
   *
   * @param string $timestamp
   *   The timestamp.
   */
  public static function setLastNotificationCheckTime(string $timestamp) {
    \Drupal::state()
      ->set('semantic_connector.global_notification_last_check', $timestamp);
  }

  /**
   * Get the global notifications.
   *
   * @return array
   *   The global notifications.
   */
  public static function getGlobalNotifications() {
    \Drupal::state()->get('semantic_connector.global_notifications', []);
  }

  /**
   * Set the global notifications.
   *
   * @param array $notifications
   *   The notifications.
   */
  public static function setGlobalNotifications(array $notifications) {
    \Drupal::state()
      ->set('semantic_connector.global_notifications', $notifications);
  }

  /**
   * Check all global notifications.
   *
   * @param bool $force_check
   *   Check for notifications, even if interval is not yet over.
   * @param bool $send_mail
   *   TRUE if mails should be sent out (if mail addresses are provided in the
   *   notification settings.
   *
   * @return string[]
   *   Array of notification strings.
   */
  public static function checkGlobalNotifications($force_check = FALSE, $send_mail = FALSE) {
    $notifications = [];
    $notification_config = self::getGlobalNotificationConfig();
    $notification_config_update_required = FALSE;

    if ($notification_config['enabled']) {
      $settings = \Drupal::configFactory()
        ->getEditable('semantic_connector.settings');
      $last_notification_check = static::getLastNotificationCheckTime();
      // Find out if a check is already required.
      if ((time() - $last_notification_check) >= $notification_config['interval'] || $force_check) {
        $actions = self::getGlobalNotificationActions();
        foreach ($actions as $action) {
          // Use the default value in case there is no value yet.
          if (!isset($notification_config['actions'][$action['id']])) {
            $notification_config['actions'][$action['id']] = $action['default_value'];
            $notification_config_update_required = TRUE;
          }

          // Find out if this specfic check has to be done.
          if ($notification_config['actions'][$action['id']]) {
            $action_notifications = call_user_func($action['callback']);
            $notifications = array_merge($notifications, $action_notifications);
          }
        }

        // Update the notifications and update the notificiation config if required.
        static::setGlobalNotifications($notifications);
        if ($notification_config_update_required) {
          $settings->set('notifications', $notification_config);
        }

        // Send mails if required.
        if (!empty($notifications) && $send_mail && !empty($notification_config['mail_to'])) {
          $params = [
            'notifications' => $notifications,
          ];
          $mailManager = \Drupal::service('plugin.manager.mail');
          $mailManager->mail('semantic_connector', 'global_notifications', $notification_config['mail_to'], \Drupal::languageManager()
            ->getDefaultLanguage()
            ->getId(), $params, NULL, TRUE);
        }

        // Set the current timestamp as last check and update the notifications.
        static::setLastNotificationCheckTime(time());
        $settings->save();
      }
      // If no check is required use the existing notifications.
      else {
        $notifications = static::getGlobalNotifications();
      }
    }

    return $notifications;
  }

  /**
   * Get the search spaces of a GraphSearch config.
   *
   * @param array $graphsearch_config
   *   The GraphSearch configuration including the search space data.
   * @param string $search_space_id_filter
   *   Optional; The ID of the search space.
   * @param string $project_id_filter
   *   Optional; The ID of the search PoolParty project used in the search
   *   space.
   * @param string $language_filter
   *   Optional; The language of the search space.
   *
   * @return array
   *   An array of search spaces by their ID, each item contains following
   *   keys:
   *   - "id": The ID of the search space
   *   - "name": The name of the search space
   *   - "language": The language used in the search space
   *   - "project_ids": An array of project ID string the search space contains
   */
  public static function getGraphSearchSearchSpaces(array $graphsearch_config, $search_space_id_filter = '', $project_id_filter = '', $language_filter = '') {
    $search_spaces = [];

    if (isset($graphsearch_config['projects'])) {
      // Create a list of all search spaces first.
      foreach ($graphsearch_config['projects'] as $graphsearch_project) {
        foreach ($graphsearch_project['search_spaces'] as $search_space) {
          if (!isset($search_spaces[$search_space['id']])) {
            $search_spaces[$search_space['id']] = $search_space;
            $search_spaces[$search_space['id']]['project_ids'] = [];
          }
          $search_spaces[$search_space['id']]['project_ids'][] = $graphsearch_project['id'];
        }
      }

      // Remove wrong search spaces then.
      foreach ($search_spaces as $search_space_id => $search_space) {
        if ((!empty($search_space_id_filter) && $search_space_id_filter != $search_space_id) || (!empty($project_id_filter) && !in_array($project_id_filter, $search_space['project_ids'])) || (!empty($language_filter) && $language_filter != $search_space['language'])) {
          unset($search_spaces[$search_space_id]);
        }
      }
    }

    return $search_spaces;
  }

  /**
   * Check if any extraction model has to be refreshed.
   *
   * @param SemanticConnectorPPServerConnection $connection
   *   Optional; A specific PoolParty Server Connection to check for a valid
   *   license.
   * @param array $license
   *   Optional; A fixed license to check against instead of fetching the actual
   *   license used by the server.
   * @param bool $class_only
   *   If set to TRUE, instead of a message only "warning" or "error" will be
   *   returned, depending on the date of expiration.
   *
   * @return string[]
   *   Array of notification strings.
   */
  public static function checkPoolpartyLicenses($connection = NULL, $license = NULL, $class_only = FALSE) {
    $notifications = [];

    if (!is_null($connection)) {
      $pp_server_connections = [$connection];
    }
    else {
      $pp_server_connections = SemanticConnector::getConnectionsByType('pp_server');
    }

    /** @var SemanticConnectorPPServerConnection $pp_server_connection */
    foreach ($pp_server_connections as $pp_server_connection) {
      // Load the license information if required.
      if (!is_null($license)) {
        $license_information = $license;
      }
      else {
        $license_information = $pp_server_connection->getApi('PPT')
          ->getLicense();
      }

      if (isset($license_information['expiryDateInMillis'])) {
        $current_time = time();
        // License already expired.
        if (($license_information['expiryDateInMillis'] / 1000) <= $current_time) {
          if (!$class_only) {
            // Add the notification.
            $notifications[] = t('The PoolParty license for connection "%connection" is outdated.', ['%connection' => $pp_server_connection->getTitle()]);
          }
          else {
            $notifications[] = 'license-expired';
          }
        }
        // License expires in the next 14 days.
        elseif (($license_information['expiryDateInMillis'] / 1000) <= ($current_time + 1209600)) {
          if (!$class_only) {
            // Add the notification.
            $notifications[] = t('The PoolParty license for connection "%connection" is about to run out on %expiration.', [
              '%connection' => $pp_server_connection->getTitle(),
              '%expiration' => date('j. M Y', $license_information['expiryDateInMillis'] / 1000),
            ]);
          }
          else {
            $notifications[] = 'license-almost-expired';
          }
        }
      }
    }

    return $notifications;
  }

  /**
   * Find out if the Visual Mapper exists.
   *
   * @return bool
   *   TRUE if the Visual Mapper exists, FALSE if not
   *
   * @deprecated No longer used since the VisualMapper library was included in
   *   the Semantic Connector in version 8.x-1.0. Use visualMapperUsable()
   *   instead.
   */
  public static function visualMapperExists() {
    return self::visualMapperUsable();
  }

  /**
   * Find out if the Visual Mapper can be used.
   *
   * @return bool
   *   TRUE if the Visual Mapper can be used, FALSE if not
   */
  public static function visualMapperUsable() {
    return file_exists(\Drupal::service('file_system')
      ->realpath('libraries/d3js/d3.min.js'));
  }
}

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

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