View source  
  <?php
namespace Drupal\acquia_lift\Service\Context;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\Routing\Route;
use Drupal\Core\Controller\TitleResolverInterface;
use Drupal\Core\Language\LanguageManagerInterface;
use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Routing\RouteMatchInterface;
use Drupal\node\NodeInterface;
use Drupal\acquia_lift\Service\Helper\SettingsHelper;
class PageContext extends BaseContext {
  
  const ENGAGEMENT_SCORE_DEFAULT = 1;
  
  const LIFT_JS_FILENAME = 'lift.js';
  
  private $fieldMappings;
  
  private $udfPersonMappings;
  
  private $udfEventMappings;
  
  private $udfTouchMappings;
  
  private $taxonomyTermStorage;
  
  protected $htmlHeadContexts = [
    'content_title' => 'Untitled',
    'content_type' => 'page',
    'page_type' => 'content page',
    'context_language' => '',
    'content_section' => '',
    'content_keywords' => '',
    'post_id' => '',
    'content_uuid' => '',
    'published_date' => '',
    'persona' => '',
    'engagement_score' => SELF::ENGAGEMENT_SCORE_DEFAULT,
  ];
  
  private $assetsUrl;
  
  private static $CREDENTIAL_MAPPING = [
    'account_id' => 'account_id',
    'site_id' => 'site_id',
    'assets_url' => 'liftAssetsURL',
    'decision_api_url' => 'liftDecisionAPIURL',
  ];
  
  public function __construct(ConfigFactoryInterface $config_factory, EntityTypeManagerInterface $entity_type_manager, RequestStack $request_stack, RouteMatchInterface $route_match, TitleResolverInterface $title_resolver, LanguageManagerInterface $language_manager) {
    
    $settings = $config_factory
      ->get('acquia_lift.settings');
    
    $credential_settings = $settings
      ->get('credential');
    $this->assetsUrl = $credential_settings['assets_url'];
    $this
      ->setContextCredential($credential_settings);
    
    $this->fieldMappings = $settings
      ->get('field_mappings');
    $this->udfPersonMappings = $settings
      ->get('udf_person_mappings') ?: [];
    $this->udfEventMappings = $settings
      ->get('udf_touch_mappings') ?: [];
    $this->udfTouchMappings = $settings
      ->get('udf_event_mappings') ?: [];
    
    $this
      ->setContextAdvanced($settings
      ->get('advanced'));
    
    $this->taxonomyTermStorage = $entity_type_manager
      ->getStorage('taxonomy_term');
    
    $request = $request_stack
      ->getCurrentRequest();
    $route = $route_match
      ->getRouteObject();
    
    $this
      ->setContextByNode($request);
    
    $this
      ->setBaseData($request, $route, $title_resolver, $language_manager);
  }
  
  private function setContextByNode(Request $request) {
    
    if (!$request->attributes
      ->has('node')) {
      return;
    }
    $node = $request->attributes
      ->get('node');
    if (empty($node) || !$node instanceof NodeInterface) {
      return;
    }
    
    $this
      ->setNodeData($node);
    $this
      ->setFields($node);
  }
  
  private function setBaseData(Request $request, Route $route, TitleResolverInterface $titleResolver, LanguageManagerInterface $languageManager) {
    
    $this->htmlHeadContexts['context_language'] = $languageManager
      ->getCurrentLanguage()
      ->getId();
    
    $title = $titleResolver
      ->getTitle($request, $route);
    
    if (is_array($title) && isset($title['#markup']) && isset($title['#allowed_tags'])) {
      $allowed_tags = empty($title['#allowed_tags']) ? '' : '<' . implode('><', $title['#allowed_tags']) . '>';
      $title = strip_tags($title['#markup'], $allowed_tags);
    }
    
    if (is_array($title) || empty($title)) {
      return;
    }
    
    $this->htmlHeadContexts['content_title'] = $title;
  }
  
  private function setContextCredential($credential_settings) {
    foreach (SELF::$CREDENTIAL_MAPPING as $credential_key => $tag_name) {
      if (isset($credential_settings[$credential_key])) {
        $this->htmlHeadContexts[$tag_name] = $credential_settings[$credential_key];
      }
    }
  }
  
  private function setContextAdvanced($advanced_settings) {
    $bootstrap_mode = isset($advanced_settings['bootstrap_mode']) ? $advanced_settings['bootstrap_mode'] : 'auto';
    $replacement_mode = $advanced_settings['content_replacement_mode'];
    $cdf_version = isset($advanced_settings['cdf_version']) ? $advanced_settings['cdf_version'] : SettingsHelper::CDF_VERSION_DEFAULT;
    if (SettingsHelper::isValidBootstrapMode($bootstrap_mode)) {
      $this->htmlHeadContexts['bootstrapMode'] = $bootstrap_mode;
    }
    if (SettingsHelper::isValidContentReplacementMode($replacement_mode)) {
      $this->htmlHeadContexts['contentReplacementMode'] = $replacement_mode;
    }
    if (SettingsHelper::isValidCdfVersion($cdf_version)) {
      $this->htmlHeadContexts['cdfVersion'] = $cdf_version;
    }
    if (!empty($advanced_settings['content_origins'])) {
      $content_origins = array_map('trim', explode("\n", trim($advanced_settings['content_origins'])));
      $content_origins = implode(',', $content_origins);
      if (!empty($content_origins)) {
        $this->htmlHeadContexts['content_origins'] = $content_origins;
      }
    }
  }
  
  private function setNodeData(NodeInterface $node) {
    $this->htmlHeadContexts['content_type'] = $node
      ->getType();
    $this->htmlHeadContexts['content_title'] = $node
      ->getTitle();
    $this->htmlHeadContexts['published_date'] = $node
      ->getCreatedTime();
    $this->htmlHeadContexts['post_id'] = $node
      ->id();
    $this->htmlHeadContexts['content_uuid'] = $node
      ->uuid();
    $this->htmlHeadContexts['page_type'] = 'node page';
  }
  
  private function setFields(NodeInterface $node) {
    $available_field_vocabulary_names = $this
      ->getAvailableFieldVocabularyNames($node);
    $vocabulary_term_names = $this
      ->getVocabularyTermNames($node);
    
    foreach ($available_field_vocabulary_names as $page_context_name => $vocabulary_names) {
      $field_term_names = $this
        ->getFieldTermNames($vocabulary_names, $vocabulary_term_names);
      
      if (!empty($field_term_names)) {
        $this->htmlHeadContexts[$page_context_name] = implode(',', $field_term_names);
      }
    }
  }
  
  private function getAvailableFieldVocabularyNames(NodeInterface $node) {
    $available_field_vocabulary_names = [];
    $available_field_vocabulary_fields = [];
    
    foreach ($this->fieldMappings as $page_context_name => $field_name) {
      if (!isset($node->{$field_name})) {
        continue;
      }
      
      if (!isset($available_field_vocabulary_fields[$field_name])) {
        $available_field_vocabulary_fields[$field_name] = [];
      }
      $available_field_vocabulary_fields[$field_name][] = $page_context_name;
    }
    
    $udf_mappings = array_merge($this->udfPersonMappings, $this->udfTouchMappings, $this->udfEventMappings);
    foreach ($udf_mappings as $page_context_name => $properties) {
      if (!isset($node->{$properties['value']})) {
        continue;
      }
      
      if (!isset($available_field_vocabulary_fields[$properties['value']])) {
        $available_field_vocabulary_fields[$properties['value']] = [];
      }
      $available_field_vocabulary_fields[$properties['value']][] = $page_context_name;
    }
    foreach ($available_field_vocabulary_fields as $field_name => $page_contexts) {
      $field_handler_settings = $node->{$field_name}
        ->getSetting('handler_settings');
      $vocabulary_names = array_key_exists('target_bundles', $field_handler_settings) ? $field_handler_settings['target_bundles'] : [];
      foreach ($page_contexts as $page_context_name) {
        $available_field_vocabulary_names[$page_context_name] = $vocabulary_names;
      }
    }
    return $available_field_vocabulary_names;
  }
  
  private function getVocabularyTermNames(NodeInterface $node) {
    
    $terms = $this->taxonomyTermStorage
      ->getNodeTerms([
      $node
        ->id(),
    ]);
    $node_terms = isset($terms[$node
      ->id()]) ? $terms[$node
      ->id()] : [];
    
    $vocabulary_term_names = [];
    foreach ($node_terms as $term) {
      $vocabulary_id = $term
        ->bundle();
      $term_name = $term
        ->getName();
      $vocabulary_term_names[$vocabulary_id][] = $term_name;
    }
    return $vocabulary_term_names;
  }
  
  private function getFieldTermNames($vocabulary_names, $vocabulary_term_names) {
    $field_term_names = [];
    foreach ($vocabulary_names as $vocabulary_name) {
      if (!isset($vocabulary_term_names[$vocabulary_name])) {
        continue;
      }
      $field_term_names = array_merge($field_term_names, $vocabulary_term_names[$vocabulary_name]);
    }
    return array_unique($field_term_names);
  }
  
  private function getJavaScriptTagRenderArray() {
    return [
      [
        '#tag' => 'script',
        '#attributes' => [
          'src' => $this->assetsUrl . '/' . SELF::LIFT_JS_FILENAME,
          'async' => true,
        ],
      ],
      'acquia_lift_javascript',
    ];
  }
  
  protected function populateHtmlHead(&$htmlHead) {
    parent::populateHtmlHead($htmlHead);
    
    $htmlHead[] = $this
      ->getJavaScriptTagRenderArray();
  }
}