From b73f6ef4bccc0437b90f097a50b2c93f7f145071 Mon Sep 17 00:00:00 2001 From: Timo Welde Date: Mon, 5 Sep 2016 12:58:50 +0200 Subject: [PATCH] Integrate fb instant articles (#9) * FIA wrap in an iframe * FIA: Add referrer, aplus_data, javascript via the _attach_orbyd method and the adHelper.js file. * FIA: correct width for Ads * FIA: make ad-tag configurable * Refactor token lookup into service (According to https://github.com/NoDiskInDriveA/module-ivw_integration/tree/8.x-1.x-fia-must-go-somewhere) --- ad_integration.module | 49 +++ ad_integration.services.yml | 5 +- ad_integration.tokens.inc | 123 ++---- config/install/ad_integration.settings.yml | 14 +- config/schema/ad_integration.schema.yml | 11 +- src/AdIntegration.php | 64 +--- src/AdIntegrationInterface.php | 42 +- src/AdIntegrationLookup.php | 223 +++++++++++ src/AdIntegrationLookupInterface.php | 71 ++++ src/Form/SettingsForm.php | 13 + templates/ad-slot-fia.html.twig | 19 + tests/src/Kernel/AdIntegrationTest.php | 421 +++++++++++++++++++++ 12 files changed, 889 insertions(+), 166 deletions(-) create mode 100644 src/AdIntegrationLookup.php create mode 100644 src/AdIntegrationLookupInterface.php create mode 100644 templates/ad-slot-fia.html.twig create mode 100644 tests/src/Kernel/AdIntegrationTest.php diff --git a/ad_integration.module b/ad_integration.module index 93ff87b..332ed41 100644 --- a/ad_integration.module +++ b/ad_integration.module @@ -27,6 +27,15 @@ function ad_integration_theme() { 'ad_tag' => NULL, ), ), + 'ad_slot_fia' => array( + 'template' => 'ad-slot-fia', + 'variables' => array( + 'referrer' => NULL, + 'ad_tag' => NULL, + 'data' => NULL, + 'adengine' => NULL, + ), + ), ); } @@ -101,6 +110,46 @@ function ad_integration_page_attachments(array &$page) { } } +/** + * Adds the ad template for Facebook Instant Articles + * + * Implements hook_preprocess_views_view_row_fia(). + */ +function ad_integration_preprocess_views_view_row_fia(&$variables) { + /** @var \Drupal\ad_integration\AdIntegrationInterface $advertisingService */ + $advertisingService = \Drupal::service('ad_integration'); + $ad_provider = $advertisingService->getAdProvider(); + + $config = Drupal::config('ad_integration.settings'); + $ad_tag = $config->get('adsc_fia_tag'); + + $node = $variables['row']['#node']; + $data = array('entity' => $node); + + if ($ad_provider == 'orbyd' && !empty($ad_tag)) { + $adRender = array( + '#theme' => 'ad_slot_fia', + '#referrer' => $GLOBALS['base_url'], + '#ad_tag' => $ad_tag, + '#data' => array( + 'adunit1' => $advertisingService->getAdUnit1($data), + 'adunit2' => $advertisingService->getAdUnit2($data), + 'adunit3' => $advertisingService->getAdUnit3($data), + 'keyword' => $advertisingService->getKeyword($data), + 'admode' => $advertisingService->getAdMode($data), + ), + ); + $adengine = $advertisingService->getAdEngine(); + if ($adengine) { + $adRender += array( + '#adengine' => $adengine, + ); + } + $adHtml = render($adRender); + $variables['options']['automatic_ad'] = $adHtml; + } +} + /** * @param array $page * @param $advertisingService diff --git a/ad_integration.services.yml b/ad_integration.services.yml index 1ef1802..46e5b20 100644 --- a/ad_integration.services.yml +++ b/ad_integration.services.yml @@ -1,4 +1,7 @@ services: ad_integration: class: Drupal\ad_integration\AdIntegration - arguments: ['@entity_type.manager', '@entity.query', '@config.factory', '@path.matcher', '@current_route_match', '@token'] + arguments: ['@config.factory', '@token'] + ad_integration.lookup: + class: Drupal\ad_integration\AdIntegrationLookup + arguments: ['@current_route_match', '@config.factory', '@entity_type.manager'] \ No newline at end of file diff --git a/ad_integration.tokens.inc b/ad_integration.tokens.inc index 8f504d1..d488486 100644 --- a/ad_integration.tokens.inc +++ b/ad_integration.tokens.inc @@ -5,10 +5,10 @@ * Builds placeholder replacement tokens for ad-related data. */ -use Drupal\node\Entity\Node; +use Drupal\Core\Entity\ContentEntityInterface; use Drupal\Component\Utility\Xss; -use Drupal\taxonomy\Entity\Term; use Drupal\Core\Render\BubbleableMetadata; +use Drupal\taxonomy\TermInterface; /** * Implements hook_token_info(). @@ -57,103 +57,38 @@ function ad_integration_tokens($type, $tokens, array $data, array $options, Bubb $replacements = array(); if ($type == 'advertising') { - foreach ($tokens as $name => $original) { - $replacement = ad_integration_get_setting($name); - $replacements[$original] = $sanitize ? Xss::filter($replacement) : $replacement; - } - } - - return $replacements; -} - -function ad_integration_get_setting($name) { - /* @var CurrentRouteMatch $currentRoutematch */ - $currentRoutematch = \Drupal::service('current_route_match'); - - $parameters = ['node', 'taxonomy_term']; - $entity = NULL; - foreach ($parameters as $parameter) { - /* @var ContentEntityInterface $entity */ - if ($entity = $currentRoutematch->getParameter($parameter)) { - - // The Entity might not be loaded so we need to check if we only - // got the entity id. - if (is_numeric($entity)) { - switch ($parameter) { - case "node": - $entity = Node::load($entity); - break; - - case "taxonomy_term": - $entity = Term::load($entity); - break; - - default: - break; - } - } - - // Search for ad_integration_settings field. - foreach ($entity->getFieldDefinitions() as $fieldDefinition) { - $fieldType = $fieldDefinition->getType(); - - // If settings are found, check if an overridden value for the - // given setting is found and return that. - $overiddenSetting = get_overridden_ad_setting($name, $fieldDefinition, $entity); - if (isset($overiddenSetting)) { - return $overiddenSetting; - } - - // Check for fallback categories if no ad_integration_setting is found. - if (!isset($termOverride) && $fieldType === 'entity_reference' && $fieldDefinition->getSetting('target_type') === 'taxonomy_term') { - $fieldName = $fieldDefinition->getName(); - if ($tid = $entity->$fieldName->target_id) { - if ($term = Term::load($tid)) { - $termOverride = get_overridden_ad_setting_from_term($name, $term); - } - } - } - } - - // If we not returned before, it is possible, - // that we found a termOverride. - if (isset($termOverride)) { - return $termOverride; - } + $lookupFrom = 'currentRoute'; + if (isset($data['entity']) && $data['entity'] instanceof ContentEntityInterface) { + $lookupFrom = 'entity'; } - } - - $default_setting_key = $name . '_default'; - return \Drupal::config('ad_integration.settings')->get($default_setting_key); -} - -/** - * @param $name - * @param $fieldDefinition - * @param $entity - */ -function get_overridden_ad_setting($name, $fieldDefinition, $entity) { - if ($fieldDefinition->getType() === 'ad_integration_settings') { - $fieldName = $fieldDefinition->getName(); - if (!empty($entity->$fieldName->get(0)->$name)) { - return $entity->$fieldName->get(0)->$name; + elseif (isset($data['term']) && $data['term'] instanceof TermInterface) { + $lookupFrom = 'term'; } - } -} -function get_overridden_ad_setting_from_term($name, Term $term) { - foreach ($term->getFieldDefinitions() as $fieldDefinition) { - $override = get_overridden_ad_setting($name, $fieldDefinition, $term); - if (isset($override)) { - return $override; + /** @var \Drupal\ad_integration\AdIntegrationLookupInterface $lookup */ + $lookup = \Drupal::service('ad_integration.lookup'); + foreach ($tokens as $name => $original) { + switch ($lookupFrom) { + case 'currentRoute': + $replacement = $lookup->byCurrentRoute($name); + break; + + case 'entity': + $replacement = $lookup->byEntity($name, $data['entity']); + break; + + case 'term': + $replacement = $lookup->byTerm($name, $data['term']); + break; + + default: + $replacement = NULL; + break; + } + $replacements[$original] = $sanitize ? Xss::filter($replacement) : $replacement; } } - foreach (\Drupal::entityTypeManager()->getStorage('taxonomy_term')->loadParents($term->id()) as $parent) { - $override = get_overridden_ad_setting_from_term($name, $parent); - if (isset($override)) { - return $override; - } - } + return $replacements; } diff --git a/config/install/ad_integration.settings.yml b/config/install/ad_integration.settings.yml index b65cad5..42c80ef 100644 --- a/config/install/ad_integration.settings.yml +++ b/config/install/ad_integration.settings.yml @@ -7,14 +7,14 @@ adsc_unit1_default: '' adsc_unit2_default: '' adsc_unit3_default: '' -adsc_unit2_values: '' -adsc_unit3_values: '' +adsc_unit2_values: [] +adsc_unit3_values: [] -adsc_unit1_overridable: 0 -adsc_unit2_overridable: 1 -adsc_unit3_overridable: 1 +adsc_unit1_overridable: false +adsc_unit2_overridable: true +adsc_unit3_overridable: true adsc_mode_default: '' -adsc_mode_overridable: 0 +adsc_mode_overridable: false -adsc_keyword: '' +adsc_keyword: '' \ No newline at end of file diff --git a/config/schema/ad_integration.schema.yml b/config/schema/ad_integration.schema.yml index b36d339..9d37821 100644 --- a/config/schema/ad_integration.schema.yml +++ b/config/schema/ad_integration.schema.yml @@ -20,23 +20,26 @@ ad_integration.settings: adsc_unit1_default: type: string label: 'Ad unit 1' + adsc_unit1_overridable: + type: boolean + label: 'Ad unit 1 is overridable' adsc_unit2_default: type: string label: 'Ad unit 2' adsc_unit2_overridable: - type: bool + type: boolean label: 'Ad unit 2 is overridable' adsc_unit3_default: type: string label: 'Ad unit 3' adsc_unit3_overridable: - type: bool + type: boolean label: 'Ad unit 3 is overridable' adsc_mode_default: type: string label: 'Adsc mode' adsc_mode_overridable: - type: bool + type: boolean label: 'Adsc mode is overridable' adsc_unit2_values: type: sequence @@ -49,4 +52,4 @@ ad_integration.settings: label: 'Possible Ad unit 3 values' sequence: type: string - label: 'Value' + label: 'Value' \ No newline at end of file diff --git a/src/AdIntegration.php b/src/AdIntegration.php index 1a25870..7ba1aba 100644 --- a/src/AdIntegration.php +++ b/src/AdIntegration.php @@ -15,20 +15,6 @@ * @package Drupal\ad_integration */ class AdIntegration implements AdIntegrationInterface { - /** - * The entity storage object for taxonomy terms. - * - * @var TermStorageInterface - */ - protected $termStorage; - - /** - * The entity query object for nodes. - * - * @var \Drupal\Core\Entity\Query\Sql\Query - */ - protected $nodeQuery; - /** * The config factory. * @@ -36,20 +22,6 @@ class AdIntegration implements AdIntegrationInterface { */ protected $settings; - /** - * The current path match. - * - * @var PathMatcher - */ - protected $pathMatch; - - /** - * The current route match. - * - * @var CurrentRouteMatch - */ - protected $currentRouteMatch; - /** * The token object. * @@ -60,68 +32,52 @@ class AdIntegration implements AdIntegrationInterface { /** * Generates Advertising information. * - * @param EntityTypeManagerInterface $entity_manager - * The entity query object for taxonomy terms. - * @param QueryFactory $query - * The entity query object for taxonomy terms. * @param ConfigFactoryInterface $config_factory * The config factory service. - * @param PathMatcher $path_match - * The current path match. - * @param CurrentRouteMatch $current_route_match - * The current route match. * @param Token $token * Token service. */ public function __construct( - EntityTypeManagerInterface $entity_manager, - QueryFactory $query, ConfigFactoryInterface $config_factory, - PathMatcher $path_match, - CurrentRouteMatch $current_route_match, Token $token ) { - $this->termStorage = $entity_manager->getStorage('taxonomy_term'); - $this->nodeQuery = $query->get('node'); $this->settings = $config_factory->get('ad_integration.settings'); - $this->pathMatch = $path_match; - $this->currentRouteMatch = $current_route_match; $this->token = $token; } /** * {@inheritdoc} */ - public function getAdUnit1() { - return $this->token->replace('[advertising:adsc_unit1]', array(), array('sanitize' => FALSE)); + public function getAdUnit1($data = array()) { + return $this->token->replace('[advertising:adsc_unit1]', $data, array('sanitize' => FALSE)); } /** * {@inheritdoc} */ - public function getAdUnit2() { - return $this->token->replace('[advertising:adsc_unit2]', array(), array('sanitize' => FALSE)); + public function getAdUnit2($data = array()) { + return $this->token->replace('[advertising:adsc_unit2]', $data, array('sanitize' => FALSE)); } /** * {@inheritdoc} */ - public function getAdUnit3() { - return $this->token->replace('[advertising:adsc_unit3]', array(), array('sanitize' => FALSE)); + public function getAdUnit3($data = array()) { + return $this->token->replace('[advertising:adsc_unit3]', $data, array('sanitize' => FALSE)); } /** * {@inheritdoc} */ - public function getKeyword() { - return $this->token->replace('[advertising:adsc_keyword]', array(), array('sanitize' => FALSE)); + public function getKeyword($data = array()) { + return $this->token->replace('[advertising:adsc_keyword]', $data, array('sanitize' => FALSE)); } /** * {@inheritdoc} */ - public function getAdMode() { - return $this->token->replace('[advertising:adsc_mode]', array(), array('sanitize' => FALSE)); + public function getAdMode($data = array()) { + return $this->token->replace('[advertising:adsc_mode]', $data, array('sanitize' => FALSE)); } /** diff --git a/src/AdIntegrationInterface.php b/src/AdIntegrationInterface.php index 4203d3c..d36c83f 100644 --- a/src/AdIntegrationInterface.php +++ b/src/AdIntegrationInterface.php @@ -38,41 +38,71 @@ public function getAdEngine(); /** * Gets Ad unit 1. * - * @return string + * @param array $data + * Specify an entity or a term, which should be looked up on + * Leave empty, to look up accorsing to current route. + * ['entity'] ContentEntityInterface - instance of entity. + * ['term'] TermInterface - instance of term. + * + * @return string The ad unit 1. * The ad unit 1. */ - public function getAdUnit1(); + public function getAdUnit1($data = array()); /** * Gets Ad unit 2. * + * @param array $data + * Specify an entity or a term, which should be looked up on + * Leave empty, to look up accorsing to current route. + * ['entity'] ContentEntityInterface - instance of entity. + * ['term'] TermInterface - instance of term. + * * @return string * The ad unit 2. */ - public function getAdUnit2(); + public function getAdUnit2($data = array()); /** * Gets Ad unit 3. * + * @param array $data + * Specify an entity or a term, which should be looked up on + * Leave empty, to look up accorsing to current route. + * ['entity'] ContentEntityInterface - instance of entity. + * ['term'] TermInterface - instance of term. + * * @return string * The ad unit 3. */ - public function getAdUnit3(); + public function getAdUnit3($data = array()); /** * Gets Ad keyword. * + * @param array $data + * Specify an entity or a term, which should be looked up on + * Leave empty, to look up accorsing to current route. + * ['entity'] ContentEntityInterface - instance of entity. + * ['term'] TermInterface - instance of term. + * * @return string * The ad keyword. */ - public function getKeyword(); + public function getKeyword($data = array()); /** * Gets Ad mode. * + * @param array $data + * Specify an entity or a term, which should be looked up on + * Leave empty, to look up accorsing to current route. + * ['entity'] ContentEntityInterface - instance of entity. + * ['term'] TermInterface - instance of term. + * * @return string * The ad unit 3. */ - public function getAdMode(); + public function getAdMode($data = array()); } diff --git a/src/AdIntegrationLookup.php b/src/AdIntegrationLookup.php new file mode 100644 index 0000000..58d7484 --- /dev/null +++ b/src/AdIntegrationLookup.php @@ -0,0 +1,223 @@ +currentRouteMatch = $currentRouteMatch; + $this->config = $configFactory->get('ad_integration.settings'); + $this->entityTypeManager = $entityTypeManager; + } + + /** + * Automatically uses the current route to look up an Ad property. + * + * @param string $name + * The name of the Ad property to look up. + * @param bool $termsOnly + * If set to TRUE, skips lookup on node settings. + * + * @return string + * The property value. + */ + public function byCurrentRoute($name, $termsOnly = FALSE) { + // TODO: Implement byCurrentRoute() method. + return $this->byRoute($name, $this->currentRouteMatch, $termsOnly); + } + + /** + * Get Ad property by providing a route. + * + * @param string $name + * The name of the Ad property to look up. + * @param \Drupal\Core\Routing\RouteMatchInterface $routeMatch + * The route matching the entity (node, term) on which to look up properties. + * @param bool $termsOnly + * If set to TRUE, skips lookup on node settings. + * + * @return string + * The property value. + */ + public function byRoute($name, RouteMatchInterface $routeMatch, $termsOnly = FALSE) { + $entity = NULL; + + foreach (static::supportedEntityParameters as $parameter) { + if ($entity = $routeMatch->getParameter($parameter)) { + if (is_numeric($entity)) { + $entity = Node::load($entity); + } + $setting = $this->searchEntity($name, $entity, $termsOnly); + if ($setting !== NULL) { + return $setting; + } + } + } + + return $this->defaults($name); + } + + /** + * Get Ad property by providing an entity. + * + * @param string $name + * The name of the Ad property to look up. + * @param \Drupal\Core\Entity\ContentEntityInterface $entity + * The content entity (usually node) to look up the property on. + * @param bool $termsOnly + * If set to TRUE, skips lookup on node settings. + * + * @return string + * The property value. + */ + public function byEntity($name, ContentEntityInterface $entity, $termsOnly = FALSE) { + $result = $this->searchEntity($name, $entity, $termsOnly); + return $result !== NULL ? $result : $this->defaults($name); + } + + /** + * Get Ad property by providing a term. + * + * @param string $name + * The name of the Ad property to look up. + * @param \Drupal\taxonomy\TermInterface $term + * The term to look up the property on. + * + * @return string + * The property value. + */ + public function byTerm($name, TermInterface $term) { + $result = $this->searchTerm($name, $term); + return $result !== NULL ? $result : $this->defaults($name); + } + + /** + * Search for an Ad property in an entity. + * + * @param string $name + * The name of the Ad property to look up. + * @param \Drupal\Core\Entity\ContentEntityInterface $entity + * The content entity (usually node) to look up the property on. + * @param bool $termsOnly + * If set to TRUE, skips lookup on node settings. + * + * @return string|null + * The property value or null. + */ + protected function searchEntity($name, ContentEntityInterface $entity, $termsOnly = FALSE) { + // Search for ad_integration_settings field. + foreach ($entity->getFieldDefinitions() as $fieldDefinition) { + $fieldType = $fieldDefinition->getType(); + + if (!$termsOnly) { + // If settings are found, check if an overridden value for the + // given setting is found and return that. + $overiddenSetting = $this->getOverriddenAdSetting($name, $fieldDefinition, $entity); + if (isset($overiddenSetting)) { + return $overiddenSetting; + } + } + + + // Check for fallback categories if no ad_integration_setting is found. + if (!isset($termOverride) && $fieldType === 'entity_reference' && $fieldDefinition->getSetting('target_type') === 'taxonomy_term') { + $fieldName = $fieldDefinition->getName(); + if ($tid = $entity->$fieldName->target_id) { + if ($term = Term::load($tid)) { + $termOverride = $this->searchTerm($name, $term); + } + } + } + } + + // If we not returned before, it is possible, + // that we found a termOverride. + if (isset($termOverride)) { + return $termOverride; + } + + return NULL; + } + + /** + * @param string $name + * @param \Drupal\taxonomy\Entity\TermInterface $term + * @return string|null + */ + protected function searchTerm($name, TermInterface $term) { + foreach ($term->getFieldDefinitions() as $fieldDefinition) { + $override = $this->getOverriddenAdSetting($name, $fieldDefinition, $term); + if (isset($override)) { + return $override; + } + } + + $termStorage = $this->entityTypeManager->getStorage('taxonomy_term'); + + foreach ($termStorage->loadParents($term->id()) as $parent) { + $override = $this->searchTerm($name, $parent); + if (isset($override)) { + return $override; + } + } + + return NULL; + } + + /** + * @param $name + * @param $fieldDefinition + * @param $entity + * + * @return string|null + */ + protected function getOverriddenAdSetting($name, FieldDefinitionInterface $fieldDefinition, ContentEntityInterface $entity) { + if ($fieldDefinition->getType() === 'ad_integration_settings') { + $fieldName = $fieldDefinition->getName(); + if (!empty($entity->$fieldName->get(0)->$name)) { + return $entity->$fieldName->get(0)->$name; + } + } + return NULL; + } + + /** + * Get default value for Ad property. + * + * @param string $name + * The name of the Ad property to look up. + * + * @return string|null + * The default property value or NULL. + */ + private function defaults($name) { + return $this->config->get($name . '_default'); + } +} \ No newline at end of file diff --git a/src/AdIntegrationLookupInterface.php b/src/AdIntegrationLookupInterface.php new file mode 100644 index 0000000..20d6761 --- /dev/null +++ b/src/AdIntegrationLookupInterface.php @@ -0,0 +1,71 @@ + 'textfield', + '#title' => t('FB Instant Articles: Ad-Tag'), + '#default_value' => $settings->get('adsc_fia_tag'), + '#description' => t('The Ad-Tag for Facebook Instant Articles provided by Orbyd'), + '#states' => array( + 'visible' => array( + ':input[name=ad_provider]' => array('value' => 'orbyd'), + ), + ), + ); + $form['site_settings']['adsc_ad_engine'] = array( '#type' => 'textfield', '#title' => t('Ad engine'), @@ -200,6 +212,7 @@ public function submitForm(array &$form, FormStateInterface $form_state) { $values = $form_state->getValues(); $config = $this->configFactory()->getEditable('ad_integration.settings'); $config->set('adsc_container_tag', $values['adsc_container_tag']) + ->set('adsc_fia_tag', $values['adsc_fia_tag']) ->set('adsc_ad_engine', $values['adsc_ad_engine']) ->set('ad_provider', $values['ad_provider']) ->set('adsc_unit1_default', $values['adsc_unit1_default']) diff --git a/templates/ad-slot-fia.html.twig b/templates/ad-slot-fia.html.twig new file mode 100644 index 0000000..3996ea9 --- /dev/null +++ b/templates/ad-slot-fia.html.twig @@ -0,0 +1,19 @@ + \ No newline at end of file diff --git a/tests/src/Kernel/AdIntegrationTest.php b/tests/src/Kernel/AdIntegrationTest.php new file mode 100644 index 0000000..03eb46b --- /dev/null +++ b/tests/src/Kernel/AdIntegrationTest.php @@ -0,0 +1,421 @@ + 'adscunit1', + 'adsc_unit2' => 'adscunit2', + 'adsc_unit3' => 'adscunit3', + 'adsc_mode' => 'adscmode', + ]; + + protected $nodeType; + protected $vocabulary; + protected $term; + + /** + * The current route match. + * + * @var \Drupal\Core\Routing\RouteMatch|\PHPUnit_Framework_MockObject_MockObject + */ + protected $routeMatch; + + /** + * {@inheritdoc} + */ + protected function setUp() { + parent::setUp(); + + $this->installConfig('user'); + $this->installEntitySchema('user'); + $this->installEntitySchema('node'); + $this->installEntitySchema('taxonomy_term'); + $this->installConfig('ad_integration'); + $this->installConfig(array('filter', 'field', 'node')); + + $this->nodeType = NodeType::create(array( + 'type' => 'page', + 'name' => 'page', + )); + $this->nodeType->save(); + + $this->vocabulary = Vocabulary::create([ + 'name' => $this->randomMachineName(), + 'vid' => Unicode::strtolower($this->randomMachineName()), + 'langcode' => LanguageInterface::LANGCODE_NOT_SPECIFIED, + ]); + $this->vocabulary->save(); + + $config = $this->config('ad_integration.settings'); + foreach ($this->configs as $property => $value) { + $config->set($property . '_default', $value . '_default'); + } + $config->save(); + } + + /** + * Tests default configs. + */ + public function testDefaults() { + $this->executeTestsForAllProperties(NULL, '_default', 'Default config'); + } + + /** + * Tests the default config with a route. + */ + public function testDefaultsWithRoute() { + $this->addAdvertisingFieldToNode(); + + $node = $this->drupalCreateNode(); + + $adIntegrationLookup = $this->getAdIntegrationLookupServiceForNode($node); + + foreach ($this->configs as $property => $orignalValue) { + $value = $adIntegrationLookup->byCurrentRoute($property); + self::assertEquals($orignalValue . '_default', $value, 'Default with Route: value for' . $property . 'found'); + } + } + + /** + * Test by node overridden config with route. + * + * Tests if correct ad properties are returned from + * AdIntegrationLookup service, overridden by node and using a route. + */ + public function testOverrideByNodeWithRoute() { + $this->addAdvertisingFieldToNode(); + + $field_advertising = $this->postfixPropertyValues($this->configs, '_overrideRoute'); + + /** @var Node $node */ + $node = $this->drupalCreateNode(array( + 'field_advertising' => $field_advertising, + )); + + $adIntegrationLookup = $this->getAdIntegrationLookupServiceForNode($node); + + foreach ($this->configs as $property => $orignalValue) { + $value = $adIntegrationLookup->byCurrentRoute($property); + self::assertEquals($orignalValue . '_overrideRoute', $value, 'RouteOverride: value for' . $property . 'found'); + } + } + + /** + * Test by term overridden config with a route. + */ + public function testOverrideByTermWithRoute() { + $this->addAdvertisingFieldToNode(); + + // Field to be added to Term. + $this->addField('field_advertising', 'taxonomy_term', $this->vocabulary->id(), 'ad_integration_settings'); + + // Field to be added to Node. + $this->addField('field_channel', 'node', $this->nodeType->id(), 'entity_reference', array('target_type' => 'taxonomy_term')); + + $field_advertising = $this->postfixPropertyValues($this->configs, '_termoverrideRoute'); + + // Create Term. + $term = Term::create([ + 'name' => $this->randomMachineName(), + 'vid' => $this->vocabulary->id(), + 'langcode' => LanguageInterface::LANGCODE_NOT_SPECIFIED, + 'field_advertising' => $field_advertising, + ]); + $term->save(); + + // Create Node. + /** @var Node $node */ + $node = $this->drupalCreateNode(array( + 'field_channel' => array( + 'target_id' => $term->id(), + ), + )); + + $adIntegrationLookup = $this->getAdIntegrationLookupServiceForNode($node); + + foreach ($this->configs as $property => $orignalValue) { + $value = $adIntegrationLookup->byCurrentRoute($property); + self::assertEquals($orignalValue . '_termoverrideRoute', $value, 'RouteOverride by term: value for' . $property . 'found'); + } + + // Add subterm and test it. + $subTerm = Term::create([ + 'name' => $this->randomMachineName(), + 'vid' => $this->vocabulary->id(), + 'langcode' => LanguageInterface::LANGCODE_NOT_SPECIFIED, + 'field_advertising' => array(), + 'parent' => array( + 'target_id' => $term->id(), + ), + ]); + $subTerm->save(); + + /** @var Node $node */ + $nodeWithSubTerm = $this->drupalCreateNode(array( + 'field_channel' => array( + 'target_id' => $subTerm->id(), + ), + )); + + $adIntegrationLookup = $this->getAdIntegrationLookupServiceForNode($nodeWithSubTerm); + + foreach ($this->configs as $property => $orignalValue) { + $value = $adIntegrationLookup->byCurrentRoute($property); + self::assertEquals($orignalValue . '_termoverrideRoute', $value, 'RouteOverride with empty sub term, overridden by parent term: value for' . $property . 'found'); + } + + $field_advertising = $this->postfixPropertyValues($this->configs, '_subtermoverrideRoute'); + + $subTerm->field_advertising = $field_advertising; + $subTerm->save(); + + foreach ($this->configs as $property => $orignalValue) { + $value = $adIntegrationLookup->byCurrentRoute($property); + self::assertEquals($orignalValue . '_subtermoverrideRoute', $value, 'RouteOverride by subterm: value for' . $property . 'found'); + } + } + + /** + * Returns an ad_integration.lookup service, routed to a node. + * + * The service is provided with a mocked CurrentRoute. + * + * @return \Drupal\ad_integration\AdIntegrationLookup + * An ad_integration.lookup service + */ + protected function getAdIntegrationLookupServiceForNode($node) { + /** @var PHPUnit_Framework_MockObject_MockObject $routeMatchMock */ + $routeMatchMock = $this->getMockBuilder('\Drupal\Core\Routing\CurrentRouteMatch') + ->disableOriginalConstructor() + ->setMethods(['getParameter']) + ->getMock(); + $routeMatchMock->expects($this->any()) + ->method('getParameter') + /*->with('node')*/ + ->will($this->returnValueMap(array( + array('node', $node->id()), + ))); + $configFactory = \Drupal::service('config.factory'); + $entityTypeManager = \Drupal::service('entity_type.manager'); + /** @var AdIntegrationLookupInterface $adIntegrationLookup */ + return new AdIntegrationLookup($routeMatchMock, $configFactory, $entityTypeManager); + } + + /** + * Tests if correct ad properties are returned from default config. + */ + public function testDefaultsWithEntity() { + /** @var Node $node */ + $node = $this->drupalCreateNode(); + + $this->executeTestsForAllProperties($node, '_default', 'Default config with entity'); + + } + + /** + * Tests if correct ad properties are returned, when overridden by node. + */ + public function testOverrideByNodeWithEntity() { + $this->addAdvertisingFieldToNode(); + + $field_advertising = $this->postfixPropertyValues($this->configs, '_override'); + + /** @var Node $node */ + $node = $this->drupalCreateNode(array( + 'field_advertising' => $field_advertising, + )); + + /** @var FieldItemListInterface $field_ad */ + /*$field_ad = $node->get('field_advertising');*/ + + $this->executeTestsForAllProperties($node, '_override', 'Overridden by node'); + } + + /** + * Tests if correct ad properties are returned, when overridden by term. + */ + public function testOverrideByTermWithEntity() { + $this->addAdvertisingFieldToNode(); + + // Field to be added to Term. + $this->addField('field_advertising', 'taxonomy_term', $this->vocabulary->id(), 'ad_integration_settings'); + + // Field to be added to Node. + $this->addField('field_channel', 'node', $this->nodeType->id(), 'entity_reference', array('target_type' => 'taxonomy_term')); + + $field_advertising = $this->postfixPropertyValues($this->configs, '_termoverride'); + + // Create Term. + $term = Term::create([ + 'name' => $this->randomMachineName(), + 'vid' => $this->vocabulary->id(), + 'langcode' => LanguageInterface::LANGCODE_NOT_SPECIFIED, + 'field_advertising' => $field_advertising, + ]); + $term->save(); + + // Create Node. + /** @var Node $node */ + $node = $this->drupalCreateNode(array( + 'field_channel' => array( + 'target_id' => $term->id(), + ), + )); + + $this->executeTestsForAllProperties($node, '_termoverride', 'Overridden by term'); + + // Add subterm and test it. + $subTerm = Term::create([ + 'name' => $this->randomMachineName(), + 'vid' => $this->vocabulary->id(), + 'langcode' => LanguageInterface::LANGCODE_NOT_SPECIFIED, + 'field_advertising' => array(), + 'parent' => array( + 'target_id' => $term->id(), + ), + ]); + $subTerm->save(); + + /** @var Node $node */ + $nodeWithSubTerm = $this->drupalCreateNode(array( + 'field_channel' => array( + 'target_id' => $subTerm->id(), + ), + )); + + $this->executeTestsForAllProperties($nodeWithSubTerm, '_termoverride', 'With empty sub term, overriden by parent term'); + + $field_advertising = $this->postfixPropertyValues($this->configs, '_subtermoverride'); + + $subTerm->field_advertising = $field_advertising; + $subTerm->save(); + + $this->executeTestsForAllProperties($nodeWithSubTerm, '_subtermoverride', 'Overridden by sub term'); + } + + /** + * Returns new array, where values of an array are postfixed. + * + * @param array $arr + * The array, which should be postfixed. + * @param string $postfix + * The postfix to be used. + * + * @return array + * The postfixed array. + */ + protected function postfixPropertyValues($arr, $postfix) { + $field_advertising = array(); + foreach ($arr as $property => $value) { + $field_advertising[$property] = $value . $postfix; + } + return $field_advertising; + } + + /** + * Execute tests for all properties. + * + * @param \Drupal\Core\Entity\ContentEntityInterface $node + * The entity, which should be used for the lookup. + * @param string $postfix + * A postfix for the expected value. + * @param string $prefixMessage + * A prefix for the assertion message. + */ + protected function executeTestsForAllProperties(ContentEntityInterface $node = NULL, $postfix = '', $prefixMessage = '') { + /** @var AdIntegrationInterface $adIntegration */ + $adIntegration = \Drupal::service('ad_integration'); + + $data = array(); + if (isset($node)) { + $data = array('entity' => $node); + } + + $value = $adIntegration->getAdUnit1($data); + self::assertEquals($this->configs['adsc_unit1'] . $postfix, $value, $prefixMessage . ': value for adsc_unit1 found'); + + $value = $adIntegration->getAdUnit2($data); + self::assertEquals($this->configs['adsc_unit2'] . $postfix, $value, $prefixMessage . ': value for adsc_unit2 found'); + + $value = $adIntegration->getAdUnit3($data); + self::assertEquals($this->configs['adsc_unit3'] . $postfix, $value, $prefixMessage . ': value for adsc_unit3 found'); + + $value = $adIntegration->getAdMode($data); + self::assertEquals($this->configs['adsc_mode'] . $postfix, $value, $prefixMessage . ': value for adsc_mode found'); + } + + /** + * Adds an advertising field to Node. + */ + protected function addAdvertisingFieldToNode() { + $field_name = 'field_advertising'; + $this->addField($field_name, 'node', 'page', 'ad_integration_settings'); + + $node = $this->drupalCreateNode(); + self::assertTrue($node->hasField($field_name), 'Node has field advertising'); + } + + /** + * Helper method, to add a field to an entity type and bundle. + * + * @param string $field_name + * The field name. + * @param string $entityType + * The entity type, the field should be added to. + * @param string $bundle + * The bundle , the field should be added to. + * @param string $fieldType + * The type of field. + * @param array $settings + * Optional field type specific settings. + */ + protected function addField($field_name, $entityType, $bundle, $fieldType, $settings = array()) { + $field_storage = FieldStorageConfig::create(array( + 'field_name' => $field_name, + 'entity_type' => $entityType, + 'type' => $fieldType, + 'settings' => $settings, + )); + $field_storage->save(); + + $field = FieldConfig::create(array( + 'field_storage' => $field_storage, + 'bundle' => $bundle, + )); + $field->save(); + } + +} \ No newline at end of file