diff --git a/modules/oe_theme_content_event/js/event_livestream.js b/modules/oe_theme_content_event/js/event_livestream.js new file mode 100644 index 000000000..f636d6ee8 --- /dev/null +++ b/modules/oe_theme_content_event/js/event_livestream.js @@ -0,0 +1,23 @@ +/** + * @file + * Attaches behaviors for Event Livestreams. + */ +(function (Drupal, drupalSettings) { + /** + * Shows related description and link when livestream is active. + * + * @type {Drupal~behavior} + * + * @prop {Drupal~behaviorAttach} attach + * Attaches the Livestream behaviors. + */ + Drupal.behaviors.liveStreamDiscloser = { + attach: function attach(context) { + setTimeout(function () { + Array.prototype.forEach.call(document.querySelectorAll('[data-livestream-element]'), function (element) { + element.classList.remove('ecl-u-d-none'); + }); + }, drupalSettings.oe_theme_content_event.livestream_starttime_timestamp - Date.now()) + }, + }; +})(Drupal, drupalSettings); diff --git a/modules/oe_theme_content_event/oe_theme_content_event.libraries.yml b/modules/oe_theme_content_event/oe_theme_content_event.libraries.yml new file mode 100644 index 000000000..27096bb4b --- /dev/null +++ b/modules/oe_theme_content_event/oe_theme_content_event.libraries.yml @@ -0,0 +1,6 @@ +livestream_link_disclosure: + js: + js/event_livestream.js: {} + dependencies: + - core/drupal + - core/jquery diff --git a/modules/oe_theme_content_event/oe_theme_content_event.module b/modules/oe_theme_content_event/oe_theme_content_event.module index 848c622be..695a34627 100644 --- a/modules/oe_theme_content_event/oe_theme_content_event.module +++ b/modules/oe_theme_content_event/oe_theme_content_event.module @@ -48,6 +48,7 @@ function oe_theme_content_event_theme() { ], 'oe_theme_content_event_online_description' => [ 'variables' => [ + 'hidden' => FALSE, 'label' => '', 'url' => '', 'description' => '', @@ -58,6 +59,7 @@ function oe_theme_content_event_theme() { 'label' => '', 'url' => '', 'date' => '', + 'hide_link' => FALSE, ], ], 'oe_theme_content_event_status_message' => [ diff --git a/modules/oe_theme_content_event/src/Plugin/ExtraField/Display/InfoDisclosureExtraFieldBase.php b/modules/oe_theme_content_event/src/Plugin/ExtraField/Display/InfoDisclosureExtraFieldBase.php new file mode 100644 index 000000000..f41bcd0fc --- /dev/null +++ b/modules/oe_theme_content_event/src/Plugin/ExtraField/Display/InfoDisclosureExtraFieldBase.php @@ -0,0 +1,97 @@ +dateFormatter = $date_formatter; + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) { + return new static( + $configuration, + $plugin_id, + $plugin_definition, + $container->get('entity_type.manager'), + $container->get('datetime.time'), + $container->get('oe_time_caching.time_based_cache_tag_generator'), + $container->get('date.formatter') + ); + } + + /** + * Is current day. + * + * @param int $timestamp + * The timestamp of start date. + * + * @return bool + * True if timestamp is within current day. + */ + protected function isCurrentDay(int $timestamp): bool { + $current_date = $this->dateFormatter->format($this->requestDateTime->getTimestamp(), 'custom', 'Ymd'); + $start_day = $this->dateFormatter->format($timestamp, 'custom', 'Ymd'); + return $current_date === $start_day; + } + + /** + * Add livestream information disclosure. + * + * @param array $build + * The render array. + * @param int $timestamp + * The timestamp of start date. + */ + protected function attachLivestreamDisclosure(array &$build, int $timestamp): void { + $build['#attached'] = [ + 'library' => 'oe_theme_content_event/livestream_link_disclosure', + 'drupalSettings' => [ + 'oe_theme_content_event' => [ + 'livestream_starttime_timestamp' => $timestamp * 1000, + ], + ], + ]; + } + +} diff --git a/modules/oe_theme_content_event/src/Plugin/ExtraField/Display/LivestreamExtraField.php b/modules/oe_theme_content_event/src/Plugin/ExtraField/Display/LivestreamExtraField.php index 5a7f9eb93..ff944a6af 100644 --- a/modules/oe_theme_content_event/src/Plugin/ExtraField/Display/LivestreamExtraField.php +++ b/modules/oe_theme_content_event/src/Plugin/ExtraField/Display/LivestreamExtraField.php @@ -4,13 +4,8 @@ namespace Drupal\oe_theme_content_event\Plugin\ExtraField\Display; -use Drupal\Component\Datetime\TimeInterface; -use Drupal\Core\Datetime\DateFormatterInterface; use Drupal\Core\Entity\ContentEntityInterface; -use Drupal\Core\Entity\EntityTypeManagerInterface; use Drupal\oe_content_event\EventNodeWrapper; -use Drupal\oe_time_caching\Cache\TimeBasedCacheTagGeneratorInterface; -use Symfony\Component\DependencyInjection\ContainerInterface; /** * Extra field displaying livestream information on events. @@ -24,52 +19,7 @@ * visible = true * ) */ -class LivestreamExtraField extends DateAwareExtraFieldBase { - - /** - * Date formatter service. - * - * @var \Drupal\Core\Datetime\DateFormatterInterface - */ - protected $dateFormatter; - - /** - * LivestreamExtraField constructor. - * - * @param array $configuration - * A configuration array containing information about the plugin instance. - * @param string $plugin_id - * The plugin_id for the plugin instance. - * @param mixed $plugin_definition - * The plugin implementation definition. - * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager - * The entity type manager. - * @param \Drupal\Component\Datetime\TimeInterface $time - * The time service. - * @param \Drupal\oe_time_caching\Cache\TimeBasedCacheTagGeneratorInterface $cache_tag_generator - * Time based cache tag generator service. - * @param \Drupal\Core\Datetime\DateFormatterInterface $date_formatter - * The date formatter. - */ - public function __construct(array $configuration, $plugin_id, $plugin_definition, EntityTypeManagerInterface $entity_type_manager, TimeInterface $time, TimeBasedCacheTagGeneratorInterface $cache_tag_generator, DateFormatterInterface $date_formatter) { - parent::__construct($configuration, $plugin_id, $plugin_definition, $entity_type_manager, $time, $cache_tag_generator); - $this->dateFormatter = $date_formatter; - } - - /** - * {@inheritdoc} - */ - public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) { - return new static( - $configuration, - $plugin_id, - $plugin_definition, - $container->get('entity_type.manager'), - $container->get('datetime.time'), - $container->get('oe_time_caching.time_based_cache_tag_generator'), - $container->get('date.formatter') - ); - } +class LivestreamExtraField extends InfoDisclosureExtraFieldBase { /** * {@inheritdoc} @@ -95,10 +45,21 @@ public function viewElements(ContentEntityInterface $entity) { $this->isEmpty = TRUE; return $build; } + $link = $entity->get('oe_event_online_link')->first(); + $value = $link->getValue(); + $link = [ + '#url' => $link->getUrl(), + '#label' => $value['title'], + ]; // If the livestream didn't start yet, we cache it by its start date and // render the date only. if ($event->isOnlinePeriodYetToCome($this->requestDateTime)) { $this->applyHourTag($build, $event->getOnlineStartDate()); + if ($this->isCurrentDay($event->getOnlineStartDate()->getTimestamp())) { + $build += $link; + $build['#hide_link'] = TRUE; + $this->attachLivestreamDisclosure($build, $event->getOnlineStartDate()->getTimestamp()); + } } $build['#date'] = $this->t('Starts on @date', [ '@date' => $this->dateFormatter->format($event->getOnlineStartDate()->getTimestamp(), 'oe_event_long_date_hour'), @@ -107,12 +68,7 @@ public function viewElements(ContentEntityInterface $entity) { if ($event->isOnlinePeriodActive($this->requestDateTime)) { // Cache it by its end date. $this->applyHourTag($build, $event->getOnlineEndDate()); - $link = $entity->get('oe_event_online_link')->first(); - $value = $link->getValue(); - $build += [ - '#url' => $link->getUrl(), - '#label' => $value['title'], - ]; + $build += $link; } return $build; diff --git a/modules/oe_theme_content_event/src/Plugin/ExtraField/Display/OnlineDescriptionExtraField.php b/modules/oe_theme_content_event/src/Plugin/ExtraField/Display/OnlineDescriptionExtraField.php index 488d978e8..f4d2c2984 100644 --- a/modules/oe_theme_content_event/src/Plugin/ExtraField/Display/OnlineDescriptionExtraField.php +++ b/modules/oe_theme_content_event/src/Plugin/ExtraField/Display/OnlineDescriptionExtraField.php @@ -19,7 +19,7 @@ * visible = true * ) */ -class OnlineDescriptionExtraField extends DateAwareExtraFieldBase { +class OnlineDescriptionExtraField extends InfoDisclosureExtraFieldBase { /** * {@inheritdoc} @@ -43,24 +43,31 @@ public function viewElements(ContentEntityInterface $entity) { // If the livestream didn't start yet, we cache it by its start date. if ($event->isOnlinePeriodYetToCome($this->requestDateTime)) { $this->applyHourTag($build, $event->getOnlineStartDate()); - $this->isEmpty = TRUE; - return $build; + // Do not send field value to browser if it is not yet day online + // livestreaming should be started. + if (!$this->isCurrentDay($event->getOnlineStartDate()->getTimestamp())) { + $this->isEmpty = TRUE; + return $build; + } + // But anyway keep information hidden from users till the time + // of online streaming has started. + $build['#hidden'] = TRUE; + $this->attachLivestreamDisclosure($build, $event->getOnlineStartDate()->getTimestamp()); } if ($event->isOnlinePeriodActive($this->requestDateTime)) { // Cache it by the livestream end date. $this->applyHourTag($build, $event->getOnlineEndDate()); - $view_builder = $this->entityTypeManager->getViewBuilder('node'); - $build['#description'] = $view_builder->viewField($entity->get('oe_event_online_description'), [ - 'label' => 'hidden', - ]); - - /** @var \Drupal\link\Plugin\Field\FieldType\LinkItem $link */ - $link = $entity->get('oe_event_online_link')->first(); - $value = $link->getValue(); - $build['#url'] = $link->getUrl(); - $build['#label'] = $value['title']; } + $view_builder = $this->entityTypeManager->getViewBuilder('node'); + $build['#description'] = $view_builder->viewField($entity->get('oe_event_online_description'), [ + 'label' => 'hidden', + ]); + /** @var \Drupal\link\Plugin\Field\FieldType\LinkItem $link */ + $link = $entity->get('oe_event_online_link')->first(); + $value = $link->getValue(); + $build['#url'] = $link->getUrl(); + $build['#label'] = $value['title']; return $build; } diff --git a/modules/oe_theme_content_event/templates/oe-theme-content-event-livestream.html.twig b/modules/oe_theme_content_event/templates/oe-theme-content-event-livestream.html.twig index af16e46d4..4aa0a182f 100644 --- a/modules/oe_theme_content_event/templates/oe-theme-content-event-livestream.html.twig +++ b/modules/oe_theme_content_event/templates/oe-theme-content-event-livestream.html.twig @@ -19,6 +19,12 @@ name: 'livestreaming', size: 's', path: ecl_icon_path - } + }, + extra_attributes: [ + { + name: 'data-livestream-element' + }, + ], + extra_classes: hide_link ? 'ecl-u-d-none', } %} {% endif %} diff --git a/modules/oe_theme_content_event/templates/oe-theme-content-event-online-description.html.twig b/modules/oe_theme_content_event/templates/oe-theme-content-event-online-description.html.twig index feccfbe4f..7376f7938 100644 --- a/modules/oe_theme_content_event/templates/oe-theme-content-event-online-description.html.twig +++ b/modules/oe_theme_content_event/templates/oe-theme-content-event-online-description.html.twig @@ -5,7 +5,9 @@ */ #} {% if description is not empty %} -

{{ 'Livestream'|t }}

- {{ description }} - {{ label }} + {% endif %} diff --git a/modules/oe_theme_content_event/tests/src/FunctionalJavascript/LivestreamDisplayDisclosingTest.php b/modules/oe_theme_content_event/tests/src/FunctionalJavascript/LivestreamDisplayDisclosingTest.php new file mode 100644 index 000000000..65066b120 --- /dev/null +++ b/modules/oe_theme_content_event/tests/src/FunctionalJavascript/LivestreamDisplayDisclosingTest.php @@ -0,0 +1,175 @@ +container->get('theme_installer')->install(['oe_theme']); + $this->config('system.theme')->set('default', 'oe_theme')->save(); + $this->container->set('theme.registry', NULL); + + // Rebuild the ui_pattern definitions to collect the ones provided by + // oe_theme itself. + \Drupal::service('plugin.manager.ui_patterns')->clearCachedDefinitions(); + + FilterFormat::create([ + 'format' => 'full_html', + 'name' => 'Full HTML', + ])->save(); + } + + /** + * Tests that Event livestreaming link will be visible exactly on time. + */ + public function testLivestreamDisplayDisclosing(): void { + $static_time = new DrupalDateTime('now', DateTimeItemInterface::STORAGE_TIMEZONE); + $start_date = (clone $static_time)->modify('+10 days'); + // Create an Event node with required fields only. + /** @var \Drupal\node\Entity\Node $node */ + $node = $this->container->get('entity_type.manager') + ->getStorage('node') + ->create([ + 'type' => 'oe_event', + 'title' => 'Test event node', + 'oe_event_type' => 'http://publications.europa.eu/resource/authority/public-event-type/COMPETITION_AWARD_CEREMONY', + 'oe_teaser' => 'Event teaser', + 'oe_subject' => 'http://data.europa.eu/uxp/1000', + 'oe_event_status' => 'as_planned', + 'oe_event_dates' => [ + 'value' => $start_date->format(DateTimeItemInterface::DATETIME_STORAGE_FORMAT), + 'end_value' => $start_date->format(DateTimeItemInterface::DATETIME_STORAGE_FORMAT), + ], + 'oe_event_online_dates' => [ + 'value' => $start_date->format(DateTimeItemInterface::DATETIME_STORAGE_FORMAT), + 'end_value' => $start_date->modify('+3 hours')->format(DateTimeItemInterface::DATETIME_STORAGE_FORMAT), + ], + 'oe_event_languages' => [ + ['target_id' => 'http://publications.europa.eu/resource/authority/language/EST'], + ['target_id' => 'http://publications.europa.eu/resource/authority/language/FRA'], + ], + 'oe_author' => 'http://publications.europa.eu/resource/authority/corporate-body/ACJHR', + 'oe_content_content_owner' => 'http://publications.europa.eu/resource/authority/corporate-body/COMMU', + 'uid' => 0, + 'status' => 1, + ]); + $node->save(); + $this->drupalGet($node->toUrl()); + $livetime_elements = $this->getSession()->getPage()->findAll('css', '[data-livestream-element]'); + $this->assertCount(0, $livetime_elements); + $livestream_js = $this->xpath("//script[contains(@src, 'js/event_livestream.js')]"); + $this->assertCount(0, $livestream_js); + + // Set livestream start date in 10 minutes later. + $start_date = (clone $static_time)->modify('+10 minutes'); + $node->set('oe_event_online_type', 'livestream'); + $node->set('oe_event_online_link', [ + 'uri' => 'http://www.example.com/online_link', + 'title' => 'Link to online event', + ]); + $node->set('oe_event_online_description', 'Online event description'); + $node->set('oe_event_online_dates', [ + 'value' => $start_date->format(DateTimeItemInterface::DATETIME_STORAGE_FORMAT), + 'end_value' => $start_date->modify('+3 hours')->format(DateTimeItemInterface::DATETIME_STORAGE_FORMAT), + ]); + $node->save(); + $this->drupalGet($node->toUrl()); + $livestream_js = $this->xpath("//script[contains(@src, 'js/event_livestream.js')]"); + $this->assertCount(1, $livestream_js); + $livetime_elements = $this->getSession()->getPage()->findAll('css', '[data-livestream-element]'); + $this->assertCount(2, $livetime_elements); + foreach ($livetime_elements as $livetime_element) { + $this->assertFalse($livetime_element->isVisible()); + } + + // Set livestream start date in 10 seconds later. + $start_date = (clone $static_time)->modify('+10 seconds'); + $node->set('oe_event_online_dates', [ + 'value' => $start_date->format(DateTimeItemInterface::DATETIME_STORAGE_FORMAT), + 'end_value' => $start_date->modify('+3 hours')->format(DateTimeItemInterface::DATETIME_STORAGE_FORMAT), + ]); + $node->save(); + $this->drupalGet($node->toUrl()); + $livestream_js = $this->xpath("//script[contains(@src, 'js/event_livestream.js')]"); + $this->assertCount(1, $livestream_js); + $livetime_elements = $this->getSession()->getPage()->findAll('css', '[data-livestream-element]'); + $this->assertCount(2, $livetime_elements); + foreach ($livetime_elements as $livetime_element) { + $this->assertFalse($livetime_element->isVisible()); + } + $this->getSession()->wait(10000); + foreach ($livetime_elements as $livetime_element) { + $this->assertTrue($livetime_element->isVisible()); + } + + // During livestream active period and cache tag invalidation, + // we as expected should see livestream information but don't have anymore + // javascript for disclosing. + $node->set('oe_event_online_dates', [ + 'value' => (clone $static_time)->modify('-1 hour')->format(DateTimeItemInterface::DATETIME_STORAGE_FORMAT), + 'end_value' => (clone $static_time)->modify('+3 hours')->format(DateTimeItemInterface::DATETIME_STORAGE_FORMAT), + ]); + $node->save(); + $this->drupalGet($node->toUrl()); + $livestream_js = $this->xpath("//script[contains(@src, 'js/event_livestream.js')]"); + $this->assertCount(0, $livestream_js); + $livetime_elements = $this->getSession()->getPage()->findAll('css', '[data-livestream-element]'); + $this->assertCount(2, $livetime_elements); + foreach ($livetime_elements as $livetime_element) { + $this->assertTrue($livetime_element->isVisible()); + } + + // When livestream is over and after cache tag invalidation, + // we should not see livestream information and don't have + // javascript for disclosing. + $node->set('oe_event_online_dates', [ + 'value' => (clone $static_time)->modify('-4 hours')->format(DateTimeItemInterface::DATETIME_STORAGE_FORMAT), + 'end_value' => (clone $static_time)->modify('-1 hour')->format(DateTimeItemInterface::DATETIME_STORAGE_FORMAT), + ]); + $node->save(); + $this->drupalGet($node->toUrl()); + $this->drupalGet($node->toUrl()); + $livestream_js = $this->xpath("//script[contains(@src, 'js/event_livestream.js')]"); + $this->assertCount(0, $livestream_js); + $livetime_elements = $this->getSession()->getPage()->findAll('css', '[data-livestream-element]'); + $this->assertCount(0, $livetime_elements); + } + +}