diff --git a/.drone.yml b/.drone.yml index 0d832f6d3ba63954d9d6efd27c22237f9dd6df47..7621a466dda7d6614039cbfec4e0472157c165ca 100644 --- a/.drone.yml +++ b/.drone.yml @@ -43,6 +43,7 @@ pipeline: volumes: - /cache:/cache commands: + - composer self-update --1 - composer install --ansi --no-suggest --no-progress composer-update-lowest: @@ -51,6 +52,7 @@ pipeline: volumes: - /cache:/cache commands: + - composer self-update --1 - composer update --prefer-lowest --ansi --no-suggest --no-progress when: event: @@ -105,4 +107,4 @@ matrix: - lowest - highest PHP_VERSION: - - 7.3 + - 7.4 diff --git a/composer.json b/composer.json index 2f2f955525757a42862a62f9dca496110206915e..52522f0b2a5b9c4941085aa990ee338cae50eb6e 100644 --- a/composer.json +++ b/composer.json @@ -6,29 +6,34 @@ "minimum-stability": "dev", "prefer-stable": true, "require": { - "php": ">=7.3", + "php": ">=7.4", "cweagans/composer-patches": "^1.7", - "drupal/core": "^8.9 || ^9.1", + "drupal/core": "^8.9 || ^9.2", "openeuropa/oe_bootstrap_theme": "0.1.202202072010" }, "require-dev": { "composer/installers": "^1.11", - "drupal/core-composer-scaffold": "^8.9 || ^9.1", - "drupal/config_devel": "~1.2", "drupal/better_exposed_filters": "^5.0", + "drupal/config_devel": "~1.2", + "drupal/core-composer-scaffold": "^8.9 || ^9.2", + "drupal/ctools": "^3.7", "drupal/drupal-extension": "~4.1", + "drupal/file_link": "^2.0.4", + "drupal/pathauto": "^1.8", + "drupal/search_api": "^1.21", + "drupal/search_api_autocomplete": "^1.5", "drush/drush": "^10.3", - "openeuropa/code-review": "1.7", + "easyrdf/easyrdf": "1.0.0 as 0.9.1", + "egulias/email-validator": "^2.1.22 || ^3.0", + "openeuropa/code-review": "^1.7 || ^2.0", "openeuropa/composer-artifacts": "~0.1", - "openeuropa/drupal-core-require-dev": "^8.9 || ^9.1", + "openeuropa/drupal-core-require-dev": "^8.9 || ^9.2", "openeuropa/oe_authentication": "^1.4", "openeuropa/oe_corporate_blocks": "^4.4", "openeuropa/oe_multilingual": "^1.9", + "openeuropa/oe_starter_content": "1.x-dev", "openeuropa/task-runner-drupal-project-symlink": "^1.0", - "phpspec/prophecy-phpunit": "^1 || ^2", - "easyrdf/easyrdf": "1.0.0 as 0.9.1", - "drupal/search_api": "^1.21", - "drupal/search_api_autocomplete": "^1.5" + "phpspec/prophecy-phpunit": "^1 || ^2" }, "scripts": { "post-install-cmd": "./vendor/bin/run drupal:site-setup", @@ -38,6 +43,10 @@ "drupal":{ "type": "composer", "url": "https://packages.drupal.org/8" + }, + "openeuropa/oe_starter_content": { + "type": "git", + "url": "https://github.com/openeuropa/oe_starter_content" } }, "extra": { @@ -56,6 +65,12 @@ "web-root": "./build" } }, + "_readme": [ + "Explicit minimum version requirement of drupal/ctools module due to D9.2 compatability.", + "Explicit requirement for drupal/file_link due to https://www.drupal.org/project/file_link/issues/3147517. It can be removed when oe_media requires version 2.0.4 or above.", + "Explicit requirement for drupal/pathauto due to D9.2 compatability according to https://www.drupal.org/node/2979476.", + "Explicit requirement for egulias/email-validator due to https://www.drupal.org/project/drupal/issues/3061074#comment-14300579. It can be removed when Drupal core 9.2 support is droppped." + ], "installer-paths": { "build/core": [ "type:drupal-core" diff --git a/docker-compose.yml b/docker-compose.yml index 053e590a5d3a650befc9d2db5258bb32ffa54ae2..71e0089fbf3a4ed409399c44500b63631a36c894 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,7 +1,7 @@ version: '2' services: web: - image: fpfis/httpd-php-dev:7.3 + image: fpfis/httpd-php-dev:7.4 working_dir: /var/www/html ports: - 8080:8080 diff --git a/modules/oe_whitelabel_news/README.md b/modules/oe_whitelabel_news/README.md new file mode 100644 index 0000000000000000000000000000000000000000..8affd80eaed4d22f800f1d246265e03fa64e004c --- /dev/null +++ b/modules/oe_whitelabel_news/README.md @@ -0,0 +1 @@ +# OpenEuropa Whitelabel News \ No newline at end of file diff --git a/modules/oe_whitelabel_news/config/install/core.date_format.oe_whitelabel_news_date.yml b/modules/oe_whitelabel_news/config/install/core.date_format.oe_whitelabel_news_date.yml new file mode 100644 index 0000000000000000000000000000000000000000..373d4583e625c58c1069b48cd9a7e05a1aed54d2 --- /dev/null +++ b/modules/oe_whitelabel_news/config/install/core.date_format.oe_whitelabel_news_date.yml @@ -0,0 +1,7 @@ +langcode: en +status: true +dependencies: { } +id: oe_whitelabel_news_date +label: 'OE Whitelabel News date' +locked: false +pattern: 'd F Y' diff --git a/modules/oe_whitelabel_news/config/install/core.entity_view_display.node.oe_news.full.yml b/modules/oe_whitelabel_news/config/install/core.entity_view_display.node.oe_news.full.yml new file mode 100644 index 0000000000000000000000000000000000000000..c7bd5898a2850b54fd0877192a1368ad13bf8cc2 --- /dev/null +++ b/modules/oe_whitelabel_news/config/install/core.entity_view_display.node.oe_news.full.yml @@ -0,0 +1,59 @@ +langcode: en +status: true +dependencies: + config: + - field.field.node.oe_news.body + - field.field.node.oe_news.oe_featured_media + - field.field.node.oe_news.oe_publication_date + - field.field.node.oe_news.oe_summary + - node.type.oe_news + module: + - datetime + - text + - user + +id: node.oe_news.full +targetEntityType: node +bundle: oe_news +mode: full +content: + body: + type: text_default + label: hidden + settings: { } + third_party_settings: { } + weight: 12 + region: content + links: + settings: { } + third_party_settings: { } + weight: 100 + region: content + oe_featured_media: + type: entity_reference_entity_view + label: hidden + settings: + view_mode: default + link: false + third_party_settings: { } + weight: 10 + region: content + oe_publication_date: + type: datetime_default + label: hidden + settings: + timezone_override: '' + format_type: oe_whitelabel_news_date + third_party_settings: { } + weight: 13 + region: content + oe_summary: + type: basic_string + label: hidden + settings: { } + third_party_settings: { } + weight: 11 + region: content +hidden: + langcode: true + search_api_excerpt: true diff --git a/modules/oe_whitelabel_news/config/install/core.entity_view_display.node.oe_news.teaser.yml b/modules/oe_whitelabel_news/config/install/core.entity_view_display.node.oe_news.teaser.yml new file mode 100644 index 0000000000000000000000000000000000000000..b385718c7d92f746abfb503105c28ca6180bad7e --- /dev/null +++ b/modules/oe_whitelabel_news/config/install/core.entity_view_display.node.oe_news.teaser.yml @@ -0,0 +1,60 @@ +langcode: en +status: true +dependencies: + config: + - core.entity_view_mode.node.teaser + - field.field.node.oe_news.body + - field.field.node.oe_news.oe_featured_media + - field.field.node.oe_news.oe_publication_date + - field.field.node.oe_news.oe_summary + - node.type.oe_news + module: + - datetime + - text + - user + +id: node.oe_news.teaser +targetEntityType: node +bundle: oe_news +mode: teaser +content: + body: + type: text_default + label: hidden + settings: { } + third_party_settings: { } + weight: 12 + region: content + links: + settings: { } + third_party_settings: { } + weight: 100 + region: content + oe_featured_media: + type: entity_reference_entity_view + label: hidden + settings: + view_mode: default + link: false + third_party_settings: { } + weight: 10 + region: content + oe_publication_date: + type: datetime_default + label: hidden + settings: + timezone_override: '' + format_type: oe_whitelabel_news_date + third_party_settings: { } + weight: 13 + region: content + oe_summary: + type: basic_string + label: hidden + settings: { } + third_party_settings: { } + weight: 11 + region: content +hidden: + langcode: true + search_api_excerpt: true diff --git a/modules/oe_whitelabel_news/oe_whitelabel_news.info.yml b/modules/oe_whitelabel_news/oe_whitelabel_news.info.yml new file mode 100644 index 0000000000000000000000000000000000000000..ba0c28c9cd82fa4fa348591b485216cc587e5a17 --- /dev/null +++ b/modules/oe_whitelabel_news/oe_whitelabel_news.info.yml @@ -0,0 +1,14 @@ +name: OpenEuropa Whitelabel News +type: module +description: Companion module to OE News providing styling to nodes. +package: OpenEuropa Whitelabel Theme +core_version_requirement: ^8.9 || ^9.2 +dependencies: + - oe_whitelabel:oe_whitelabel_helper + - oe_starter_content:oe_starter_content_news + +config_devel: + install: + - core.date_format.oel_whitelabel_news_date.yml + - core.entity_view_display.node.oe_news.full.yml + - core.entity_view_display.node.oe_news.teaser.yml diff --git a/modules/oe_whitelabel_news/oe_whitelabel_news.module b/modules/oe_whitelabel_news/oe_whitelabel_news.module new file mode 100644 index 0000000000000000000000000000000000000000..fc89a07f215d9c95d1c225cd5cad86f3cab484cf --- /dev/null +++ b/modules/oe_whitelabel_news/oe_whitelabel_news.module @@ -0,0 +1,76 @@ +<?php + +/** + * @file + * OE Whitelabel theme News. + */ + +declare(strict_types = 1); + +use Drupal\Core\Cache\CacheableMetadata; +use Drupal\media\MediaInterface; +use Drupal\media\Plugin\media\Source\Image; +use Drupal\media_avportal\Plugin\media\Source\MediaAvPortalPhotoSource; +use Drupal\oe_bootstrap_theme\ValueObject\ImageValueObject; + +/** + * Implements template_preprocess_node() for the News node type. + */ +function oe_whitelabel_news_preprocess_node__oe_news(&$variables) { + if ($variables['view_mode'] !== 'full' && $variables['view_mode'] !== 'teaser') { + return; + } + + /** @var \Drupal\node\NodeInterface $node */ + $node = $variables['node']; + + // Bail out if there is no media present. + if ($node->get('oe_featured_media')->isEmpty()) { + return; + } + + /** @var \Drupal\media\Entity\Media $media */ + $media = $node->get('oe_featured_media')->entity; + if (!$media instanceof MediaInterface) { + // The media entity is not available anymore, bail out. + return; + } + + // Retrieve the correct media translation. + /** @var \Drupal\media\Entity\Media $media */ + $media = \Drupal::service('entity.repository')->getTranslationFromContext($media, $node->language()->getId()); + + // Caches are handled by the formatter usually. Since we are not rendering + // the original render arrays, we need to propagate our caches to the + // paragraph template. + $cacheability = CacheableMetadata::createFromRenderArray($variables); + $cacheability->addCacheableDependency($media); + + // Run access checks on the media entity. + $access = $media->access('view', $variables['user'], TRUE); + $cacheability->addCacheableDependency($access); + if (!$access->isAllowed()) { + $cacheability->applyTo($variables); + return; + } + + // Get the media source. + $source = $media->getSource(); + + $is_image = $source instanceof MediaAvPortalPhotoSource || $source instanceof Image; + + // If it's not an image and not a video, bail out. + if (!$is_image) { + $cacheability->applyTo($variables); + return; + } + + $thumbnail = $media->get('thumbnail')->first(); + $variables['image'] = ImageValueObject::fromImageItem($thumbnail); + + if ($variables['view_mode'] == 'teaser') { + $variables['image'] = ['#markup' => $variables['image']->getSource()]; + } + + $cacheability->applyTo($variables); +} diff --git a/modules/oe_whitelabel_news/templates/field--node--oe-publication-date--oe-news.html.twig b/modules/oe_whitelabel_news/templates/field--node--oe-publication-date--oe-news.html.twig new file mode 100644 index 0000000000000000000000000000000000000000..c1d5573167b19e22028950682f7880352c2e2654 --- /dev/null +++ b/modules/oe_whitelabel_news/templates/field--node--oe-publication-date--oe-news.html.twig @@ -0,0 +1,16 @@ +{# +/** + * @file + * Theme override for field oe-publication-date. + */ +#} +{% if label_hidden %} + {% for item in items %} + {{ item.content }} + {% endfor %} +{% else %} + {{ label }} + {% for item in items %} + {{ item.content }} + {% endfor %} +{% endif %} \ No newline at end of file diff --git a/modules/oe_whitelabel_news/templates/node--oe-news--full.html.twig b/modules/oe_whitelabel_news/templates/node--oe-news--full.html.twig new file mode 100644 index 0000000000000000000000000000000000000000..26d8251d95a59f20b5add6a0d75c5a3c9d848d8d --- /dev/null +++ b/modules/oe_whitelabel_news/templates/node--oe-news--full.html.twig @@ -0,0 +1,25 @@ +{# +/** + * @file + * News full display. + */ +#} +<article{{attributes}}> + {{ pattern('content_banner', { + background: 'gray', + title: label, + content: content.oe_summary, + image: image, + meta: [ + content.oe_publication_date, + ] + }) }} + + <div class="container mt-md-4-75 mt-4"> + <div class="row"> + <div class="col-12 col-lg-10 col-xl-9 col-xxl-8 mb-4"> + {{ content.body }} + </div> + </div> + </div> +</article> diff --git a/modules/oe_whitelabel_news/templates/node--oe-news--teaser.html.twig b/modules/oe_whitelabel_news/templates/node--oe-news--teaser.html.twig new file mode 100644 index 0000000000000000000000000000000000000000..51cb95a61f580d497f9630740b58843e7fcca018 --- /dev/null +++ b/modules/oe_whitelabel_news/templates/node--oe-news--teaser.html.twig @@ -0,0 +1,21 @@ +{# +/** + * @file + * Search result template. + */ +#} +{% set _title %} + <a class="text-underline-hover" href="{{ url }}">{{ label }}</a> +{% endset %} +{% set _content %} + <span class="text-muted text-nowrap me-4-5">{{ content.oe_publication_date }}</span> +{% endset %} +<article{{attributes}}> + {{ pattern('card', { + variant: 'search', + title: _title, + text: content.oe_summary, + image: image, + content: _content + }) }} +</article> diff --git a/tests/src/Functional/ContentNewsRenderTest.php b/tests/src/Functional/ContentNewsRenderTest.php new file mode 100644 index 0000000000000000000000000000000000000000..7681b1c751758b6ddf3cfaa19efd3e915131ee0a --- /dev/null +++ b/tests/src/Functional/ContentNewsRenderTest.php @@ -0,0 +1,157 @@ +<?php + +declare(strict_types = 1); + +namespace Drupal\Tests\oe_whitelabel\Functional; + +use Symfony\Component\DomCrawler\Crawler; +use Drupal\Tests\media\Traits\MediaTypeCreationTrait; +use Drupal\media\Entity\Media; +use Drupal\file\Entity\File; +use Drupal\Tests\TestFileCreationTrait; + +/** + * Tests that the News content type renders correctly. + */ +class ContentNewsRenderTest extends WhitelabelBrowserTestBase { + + use MediaTypeCreationTrait; + use TestFileCreationTrait; + + /** + * {@inheritdoc} + */ + protected static $modules = [ + 'oe_whitelabel_news', + ]; + + /** + * A node to be rendered in different display views. + * + * @var \Drupal\node\NodeInterface + */ + protected $node; + + /** + * {@inheritdoc} + */ + protected function setUp(): void { + parent::setUp(); + + // Create a sample image media entity to be embedded. + File::create([ + 'uri' => $this->getTestFiles('image')[0]->uri, + ])->save(); + $media_image = Media::create([ + 'bundle' => 'image', + 'name' => 'Starter Image test', + 'oe_media_image' => [ + [ + 'target_id' => 1, + 'alt' => 'Starter Image test alt', + 'title' => 'Starter Image test title', + ], + ], + ]); + $media_image->save(); + + // Create a News node. + /** @var \Drupal\node\Entity\Node $node */ + $node = \Drupal::entityTypeManager() + ->getStorage('node') + ->create([ + 'type' => 'oe_news', + 'title' => 'Test news node', + 'oe_summary' => 'http://www.example.org is a web page', + 'body' => 'News body', + 'oe_publication_date' => [ + 'value' => '2022-02-09T20:00:00', + ], + 'uid' => 1, + 'status' => 1, + ]); + $node->set('oe_featured_media', [$media_image]); + $node->save(); + $this->node = $node; + } + + /** + * Tests that the News page renders correctly in full display. + */ + public function testNewsRenderingFull(): void { + // Build node full view. + $builder = \Drupal::entityTypeManager()->getViewBuilder('node'); + $build = $builder->view($this->node, 'full'); + $render = $this->container->get('renderer')->renderRoot($build); + $crawler = new Crawler((string) $render); + + // Assert content banner title. + $content_banner = $crawler->filter('.bcl-content-banner'); + $this->assertEquals( + 'Test news node', + trim($content_banner->filter('.card-title')->text()) + ); + // Assert content banner image. + $image = $content_banner->filter('img'); + $this->assertCount(1, $image); + $this->assertCount(1, $image->filter('.card-img-top')); + $this->assertStringContainsString( + 'image-test.png', + trim($image->attr('src')) + ); + $this->assertEquals('Starter Image test alt', + $image->attr('alt') + ); + // Assert content banner publication date. + $this->assertEquals( + '10 February 2022', + trim($content_banner->filter('.card-body > div.my-4')->text()) + ); + // Assert content banner summary. + $this->assertEquals( + 'http://www.example.org is a web page', + trim($content_banner->filter('.oe-news__oe-summary')->text()) + ); + // Assert the news content. + $this->assertEquals( + 'News body', + trim($crawler->filter('.oe-news__body')->text()) + ); + } + + /** + * Tests that the News page renders correctly in teaser display. + */ + public function testNewsRenderingTeaser(): void { + // Build node teaser view. + $builder = \Drupal::entityTypeManager()->getViewBuilder('node'); + $build = $builder->view($this->node, 'teaser'); + $render = $this->container->get('renderer')->renderRoot($build); + $crawler = new Crawler((string) $render); + + // Assert content banner title. + $this->assertEquals( + 'Test news node', + trim($crawler->filter('h5.card-title')->text()) + ); + // Assert content banner image. + $image = $crawler->filter('img'); + $this->assertCount(1, $image); + $this->assertCount(1, $image->filter('.card-img-top')); + $this->assertStringContainsString( + 'image-test.png', + trim($image->attr('src')) + ); + // Assert content banner content. + $this->assertEquals( + 'http://www.example.org is a web page', + trim($crawler->filter('p.card-text')->text()) + ); + // Assert content banner publication date. + $this->assertEquals( + '10 February 2022', + trim($crawler->filter('div.card-body > span.text-muted')->text()) + ); + } + +} diff --git a/tests/src/Functional/WhitelabelBrowserTestBase.php b/tests/src/Functional/WhitelabelBrowserTestBase.php new file mode 100644 index 0000000000000000000000000000000000000000..7aae37c3cffb7b8ac893d6a143b962312db4472d --- /dev/null +++ b/tests/src/Functional/WhitelabelBrowserTestBase.php @@ -0,0 +1,37 @@ +<?php + +declare(strict_types = 1); + +namespace Drupal\Tests\oe_whitelabel\Functional; + +use Drupal\Tests\BrowserTestBase; + +/** + * Base class for testing content types. + */ +abstract class WhitelabelBrowserTestBase extends BrowserTestBase { + + /** + * {@inheritdoc} + */ + protected $defaultTheme = 'stark'; + + /** + * {@inheritdoc} + */ + protected function setUp(): void { + parent::setUp(); + + // Enable and set OpenEuropa Theme as default. + \Drupal::service('theme_installer')->install(['oe_whitelabel']); + \Drupal::configFactory() + ->getEditable('system.theme') + ->set('default', 'oe_whitelabel') + ->save(); + + // Rebuild the ui_pattern definitions to collect the ones provided by + // oe_whitelabel itself. + \Drupal::service('plugin.manager.ui_patterns')->clearCachedDefinitions(); + } + +}