diff --git a/composer.json b/composer.json index 8d3b0471324dcc9865d77002068fca22fa6f00d6..66f1aff5c8868b9e4dd2e40212f693453eeecfdf 100644 --- a/composer.json +++ b/composer.json @@ -10,8 +10,7 @@ "drupal/core": "^8.9 || ^9.1", "drupal/ui_patterns": "^1.0", "drupal/ui_patterns_settings": "^1.0", - "openeuropa/oe_bootstrap_theme": "0.100920211846", - "openeuropa/oe_authentication": "^1.4", + "openeuropa/oe_bootstrap_theme": "0.140920211750", "php": ">=7.3" }, "require-dev": { @@ -23,6 +22,8 @@ "openeuropa/code-review": "1.6", "openeuropa/composer-artifacts": "~0.1", "openeuropa/drupal-core-require-dev": "^8.9 || ^9.1", + "openeuropa/oe_authentication": "^1.4", + "openeuropa/oe_multilingual": "^1.8", "openeuropa/task-runner-drupal-project-symlink": "^1.0", "phpspec/prophecy-phpunit": "^1 || ^2" }, diff --git a/modules/oe_whitelabel_helper/README.md b/modules/oe_whitelabel_helper/README.md new file mode 100644 index 0000000000000000000000000000000000000000..8d1b136b08933d3dac94226408c725226a72327a --- /dev/null +++ b/modules/oe_whitelabel_helper/README.md @@ -0,0 +1,28 @@ +# OpenEuropa Whitelabel Helper + +This module offers some additional functionality that might come in useful when +theming an OpenEuropa website. + +Here is an overview of the features it offers: + +### Corporate Logo Blocks + +Two blocks, one for EC one for EU displaying the European Commission and European Union logos linked. + +### Enables OE Authentication + +Enables the [OpenEuropa Authentication](https://github.com/openeuropa/oe_authentication) module so the themed login block can be picked up by Drupal. + +### Enables OE Multilingual + +Enables the [OpenEuropa Multilingual](https://github.com/openeuropa/oe_multilingual) module. +The language switcher block is themed out of the box. + +## Requirements + +To be able to enable this module you will have to provide the dependent modules in your projects composer.json + +``` +composer require openeuropa/oe_authentication +composer require openeuropa/oe_multilingual +``` diff --git a/modules/oe_whitelabel_helper/oe_whitelabel_helper.info.yml b/modules/oe_whitelabel_helper/oe_whitelabel_helper.info.yml index bcbfac2536f13724fcc3ecb8aa266e595282144f..07351195e1816361ef2a8a13e5a4ba670f9e0223 100644 --- a/modules/oe_whitelabel_helper/oe_whitelabel_helper.info.yml +++ b/modules/oe_whitelabel_helper/oe_whitelabel_helper.info.yml @@ -5,3 +5,4 @@ package: OpenEuropa Whitelabel Theme core_version_requirement: ^8.9 || ^9.1 dependencies: - openeuropa:oe_authentication + - openeuropa:oe_multilingual diff --git a/oe_whitelabel.theme b/oe_whitelabel.theme new file mode 100644 index 0000000000000000000000000000000000000000..87447e810ff5da233bb7b1b63d11f9af26497714 --- /dev/null +++ b/oe_whitelabel.theme @@ -0,0 +1,66 @@ +<?php + +/** + * @file + * Functions to support theming. + */ + +declare(strict_types = 1); + +/** + * Implements hook__preprocess_links__language_block(). + */ +function oe_whitelabel_preprocess_links__language_block(&$variables) { + $currentLanguage = \Drupal::languageManager()->getCurrentLanguage(); + $current_language_id = $currentLanguage->getId(); + $language_config_storage = \Drupal::entityTypeManager()->getStorage('configurable_language'); + $eu_links = []; + $non_eu_links = []; + + foreach ($variables['links'] as $language_code => $link) { + /** @var \Drupal\Core\Url $url */ + $url = $link['link']['#url']; + $href = $url + ->setOptions($link['link']['#options']) + ->setAbsolute(TRUE) + ->toString(); + $label = $link['link']['#title']; + + $link = [ + 'href' => $href, + 'name' => $label, + 'id' => 'link_' . $language_code, + 'hreflang' => $language_code, + ]; + + if (!empty($current_language_id) && $language_code === $current_language_id) { + $variables['language']['link'] = $link; + $variables['language']['link']['target'] = 'languageModal'; + $link['active'] = TRUE; + } + + $language_config = $language_config_storage->load($language_code); + $category = $language_config->getThirdPartySetting('oe_multilingual', 'category'); + + if ($category === 'eu') { + $eu_links[$language_code] = $link; + } + else { + $non_eu_links[$language_code] = $link; + } + } + + $variables['language']['modal'] = [ + 'id' => 'languageModal', + 'size' => 'fullscreen', + 'header' => [ + 'title' => t('Select your language'), + 'label' => t('Close'), + ], + 'eu_links' => $eu_links, + 'non_eu_links' => $non_eu_links, + 'footer' => [ + 'label' => t('Close'), + ], + ]; +} diff --git a/templates/overrides/navigation/block--oe-authentication-login_block.html.twig b/templates/overrides/navigation/block--oe-authentication-login_block.html.twig index 021a33f06f0f1562f02a5a05f41badd991b2150b..a38878e8b88b54fbfb69d0c66276c21e797f1092 100644 --- a/templates/overrides/navigation/block--oe-authentication-login_block.html.twig +++ b/templates/overrides/navigation/block--oe-authentication-login_block.html.twig @@ -4,8 +4,20 @@ * icon. */ #} -{% include '@oe-bcl/bcl-icon/icon.html.twig' with { - 'name': 'person-fill', - 'path': bcl_icon_path, - 'size': 'm', -} %} {{ content }} +{% set extra_attributes = create_attribute() %} +{% set extra_attributes = extra_attributes.addClass('nav-link') %} + +<ul class="nav oe-authentication"> + <li class="nav-item"> + {{ pattern('link', { + label: content['#title']|render, + path: content['#url']|render, + icon: { + name: 'person-fill', + size: 's' + }, + icon_position: 'before', + attributes: extra_attributes + }) }} + </li> +</ul> diff --git a/templates/overrides/navigation/links--language-block.html.twig b/templates/overrides/navigation/links--language-block.html.twig new file mode 100644 index 0000000000000000000000000000000000000000..0231b11a163ad9da9e1c1a89497272f0c80349d4 --- /dev/null +++ b/templates/overrides/navigation/links--language-block.html.twig @@ -0,0 +1,42 @@ +{% spaceless %} + +{# Parameters: + - language + - link + - label + - href + - target + - modal +#} + +{% set _language = { + link: { + label: language.link.name|default(''), + href: language.link.href|default('#'), + target: language.link.target|default(language.modal.id|default('')), + }, + modal: language.modal, +} %} +{% set extra_attributes = create_attribute() %} +{% set extra_attributes = extra_attributes.setAttribute('data-bs-toggle', 'modal') %} +{% set extra_attributes = extra_attributes.setAttribute('data-bs-target', '#' ~ _language.link.target) %} +{% set extra_attributes = extra_attributes.addClass('nav-link') %} + +<ul class="nav"> + <li class="nav-item"> + {{ pattern('link', { + label: _language.link.label, + path: _language.link.href, + icon: { + name: 'chat-left-dots-fill', + size: 's' + }, + icon_position: 'before', + attributes: extra_attributes + }) }} + </li> +</ul> + +{% include '@oe_whitelabel/patterns/modal/modal-language.html.twig' with _language.modal only %} + +{% endspaceless %} diff --git a/templates/patterns/modal/modal-language.html.twig b/templates/patterns/modal/modal-language.html.twig new file mode 100644 index 0000000000000000000000000000000000000000..9744103f9f39e13897687529fa5800728f18bd7a --- /dev/null +++ b/templates/patterns/modal/modal-language.html.twig @@ -0,0 +1,82 @@ +{% spaceless %} + +{# Parameters: + - modal + - id + - size + - header + - title + - label + - body + - links + - footer + - label +#} + +{% if header %} + {% set _header %} + <h5 class="modal-title" id="languageeModalLabel">{{ header.title|default('Select your language'|t) }}</h5> + <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="{{ header.label|default('Close'|t) }}"></button> + {% endset %} +{% endif %} + +{% if eu_links or non_eu_links %} + {% set _body %} + <div class="container"> + <div class="row"> + {% set _number_items = (eu_links|length / 2) %} + {% for link in eu_links|batch(_number_items) %} + {% if loop.index % 2 == 0 %} + <div class="col col-lg-4 offset-lg-2"> + {% else %} + <div class="col col-lg-4"> + {% endif %} + <div class="oe-language__list"> + {% for id, data in link %} + <a id="{{ data.id }}" href="{{ data.href|default('#') }}" class="oe-language__item">{{ data.name }}</a> + {% endfor %} + </div> + </div> + {% endfor %} + </div> + {% if non_eu_links %} + <div class="row"> + {% set _number_items = (non_eu_links|length / 2) %} + {% for link in non_eu_links|batch(_number_items) %} + {% if loop.index % 2 == 0 %} + <div class="col col-lg-4 offset-lg-2"> + {% else %} + <div class="col col-lg-4"> + {% endif %} + <div class="oe-language__list"> + {% for id, data in link %} + <a id="{{ data.id }}" href="{{ data.href|default('#') }}" class="oe-language__item">{{ data.name }}</a> + {% endfor %} + </div> + </div> + {% endfor %} + </div> + {% endif %} + </div> + {% endset %} +{% endif %} + +{% if footer %} + {% set _footer %} + <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">{{ header.label|default('Close'|t) }}</button> + {% endset %} +{% endif %} + +{% set _data = { + id: id, + size: size, + header: _header, + body: _body, + footer: _footer, +} %} + +<div class="oe-language"> + {% include '@oe-bcl/bcl-modal/modal.html.twig' with _data only %} +</div> + +{% endspaceless %} diff --git a/tests/src/Kernel/AuthenticationBlockTest.php b/tests/src/Kernel/AuthenticationBlockTest.php index c53680766cd17d485c86eb80e7bd04cf0397db87..52c6ebab3bf34f91d29887bad1a37d9110b0473d 100644 --- a/tests/src/Kernel/AuthenticationBlockTest.php +++ b/tests/src/Kernel/AuthenticationBlockTest.php @@ -68,10 +68,10 @@ class AuthenticationBlockTest extends KernelTestBase { $render = $this->container->get('renderer')->renderRoot($build); $crawler = new Crawler($render->__toString()); - $actual = $crawler->filter('#block-euloginlinkblock'); + $actual = $crawler->filter('.oe-authentication'); $this->assertCount(1, $actual); $icon = $actual->filter('svg'); - $this->assertSame('bi icon--m', $icon->attr('class')); + $this->assertSame('bi icon--s', $icon->attr('class')); $use = $icon->filter('use'); $expected = '/themes/contrib/oe_bootstrap_theme/assets/icons/bootstrap-icons.svg#person-fill'; $this->assertSame($expected, $use->attr('xlink:href')); diff --git a/tests/src/Kernel/MultilingualBlockTest.php b/tests/src/Kernel/MultilingualBlockTest.php new file mode 100644 index 0000000000000000000000000000000000000000..656b518dc268d352183fbb070d5f383ac39a23b9 --- /dev/null +++ b/tests/src/Kernel/MultilingualBlockTest.php @@ -0,0 +1,184 @@ +<?php + +declare(strict_types = 1); + +namespace Drupal\Tests\oe_whitelabel\Kernel; + +use Drupal\KernelTests\KernelTestBase; +use Symfony\Component\DomCrawler\Crawler; + +/** + * Tests the OE Multilingual Block rendering. + */ +class MultilingualBlockTest extends KernelTestBase { + + /** + * {@inheritdoc} + */ + protected static $modules = [ + 'block', + 'components', + 'content_translation', + 'ctools', + 'language', + 'locale', + 'oe_multilingual', + 'path', + 'pathauto', + 'path_alias', + 'system', + 'token', + 'ui_patterns', + 'ui_patterns_library', + 'user', + ]; + + /** + * {@inheritdoc} + */ + protected function setUp(): void { + parent::setUp(); + /** @var \Drupal\Core\Extension\ThemeInstallerInterface $theme_installer */ + \Drupal::service('theme_installer')->install(['oe_whitelabel']); + + \Drupal::configFactory() + ->getEditable('system.theme') + ->set('default', 'oe_whitelabel') + ->save(); + + $this->container->set('theme.registry', NULL); + $this->container->get('cache.render')->deleteAll(); + + $this->installSchema('locale', [ + 'locales_location', + 'locales_target', + 'locales_source', + 'locale_file', + ]); + + $this->installSchema('user', ['users_data']); + + $this->installConfig([ + 'locale', + 'language', + 'content_translation', + 'oe_multilingual', + ]); + $this->container->get('module_handler')->loadInclude('oe_multilingual', 'install'); + oe_multilingual_install(FALSE); + + \Drupal::service('kernel')->rebuildContainer(); + } + + /** + * Tests the rendering of blocks. + */ + public function testBlockRendering(): void { + $entity_type_manager = $this->container + ->get('entity_type.manager') + ->getStorage('block'); + $entity = $entity_type_manager->create([ + 'id' => 'languageswitcherinterfacetext', + 'theme' => 'oe_whitelabel', + 'plugin' => 'language_block:language_interface', + 'region' => 'header', + 'settings' => [ + 'id' => 'language_block:language_interface', + 'label' => 'Language switcher (Interface text)', + 'provider' => 'language', + 'label_display' => '0', + ], + ]); + $entity->save(); + $builder = \Drupal::entityTypeManager()->getViewBuilder('block'); + $build = $builder->view($entity, 'block'); + $render = $this->container->get('renderer')->renderRoot($build); + $crawler = new Crawler($render->__toString()); + + $block = $crawler->filter('#block-languageswitcherinterfacetext'); + $this->assertCount(1, $block); + $link = $crawler->filter('a.nav-link'); + $this->assertSame('English', trim($link->text())); + $this->assertSame('http://localhost/en/%3Cnone%3E', $link->attr('href')); + $title = $crawler->filter('h5#languageeModalLabel'); + $this->assertSame('Select your language', $title->text()); + $button_header = $crawler->filter('button.btn-close'); + $this->assertSame('Close', $button_header->attr('aria-label')); + $button_close = $crawler->filter('button.btn.btn-secondary'); + $this->assertSame('Close', $button_close->text()); + $link_language = $crawler->filter('a#link_bg'); + $this->assertSame('http://localhost/bg/%3Cnone%3E', $link_language->attr('href')); + $this->assertSame('българÑки', $link_language->text()); + $link_language = $crawler->filter('a#link_cs'); + $this->assertSame('http://localhost/cs/%3Cnone%3E', $link_language->attr('href')); + $this->assertSame('ÄeÅ¡tina', $link_language->text()); + $link_language = $crawler->filter('a#link_da'); + $this->assertSame('http://localhost/da/%3Cnone%3E', $link_language->attr('href')); + $this->assertSame('dansk', $link_language->text()); + $link_language = $crawler->filter('a#link_de'); + $this->assertSame('http://localhost/de/%3Cnone%3E', $link_language->attr('href')); + $this->assertSame('Deutsch', $link_language->text()); + $link_language = $crawler->filter('a#link_et'); + $this->assertSame('http://localhost/et/%3Cnone%3E', $link_language->attr('href')); + $this->assertSame('eesti', $link_language->text()); + $link_language = $crawler->filter('a#link_el'); + $this->assertSame('http://localhost/el/%3Cnone%3E', $link_language->attr('href')); + $this->assertSame('ελληνικά', $link_language->text()); + $link_language = $crawler->filter('a#link_en'); + $this->assertSame('http://localhost/en/%3Cnone%3E', $link_language->attr('href')); + $this->assertSame('English', $link_language->text()); + $link_language = $crawler->filter('a#link_es'); + $this->assertSame('http://localhost/es/%3Cnone%3E', $link_language->attr('href')); + $this->assertSame('español', $link_language->text()); + $link_language = $crawler->filter('a#link_fr'); + $this->assertSame('http://localhost/fr/%3Cnone%3E', $link_language->attr('href')); + $this->assertSame('français', $link_language->text()); + $link_language = $crawler->filter('a#link_ga'); + $this->assertSame('http://localhost/ga/%3Cnone%3E', $link_language->attr('href')); + $this->assertSame('Gaeilge', $link_language->text()); + $link_language = $crawler->filter('a#link_hr'); + $this->assertSame('http://localhost/hr/%3Cnone%3E', $link_language->attr('href')); + $this->assertSame('hrvatski', $link_language->text()); + $link_language = $crawler->filter('a#link_it'); + $this->assertSame('http://localhost/it/%3Cnone%3E', $link_language->attr('href')); + $this->assertSame('italiano', $link_language->text()); + $link_language = $crawler->filter('a#link_lt'); + $this->assertSame('http://localhost/lt/%3Cnone%3E', $link_language->attr('href')); + $this->assertSame('lietuvių', $link_language->text()); + $link_language = $crawler->filter('a#link_lv'); + $this->assertSame('http://localhost/lv/%3Cnone%3E', $link_language->attr('href')); + $this->assertSame('latvieÅ¡u', $link_language->text()); + $link_language = $crawler->filter('a#link_hu'); + $this->assertSame('http://localhost/hu/%3Cnone%3E', $link_language->attr('href')); + $this->assertSame('magyar', $link_language->text()); + $link_language = $crawler->filter('a#link_mt'); + $this->assertSame('http://localhost/mt/%3Cnone%3E', $link_language->attr('href')); + $this->assertSame('Malti', $link_language->text()); + $link_language = $crawler->filter('a#link_nl'); + $this->assertSame('http://localhost/nl/%3Cnone%3E', $link_language->attr('href')); + $this->assertSame('Nederlands', $link_language->text()); + $link_language = $crawler->filter('a#link_pl'); + $this->assertSame('http://localhost/pl/%3Cnone%3E', $link_language->attr('href')); + $this->assertSame('polski', $link_language->text()); + $link_language = $crawler->filter('a#link_pt-pt'); + $this->assertSame('http://localhost/pt/%3Cnone%3E', $link_language->attr('href')); + $this->assertSame('português', $link_language->text()); + $link_language = $crawler->filter('a#link_ro'); + $this->assertSame('http://localhost/ro/%3Cnone%3E', $link_language->attr('href')); + $this->assertSame('română', $link_language->text()); + $link_language = $crawler->filter('a#link_sk'); + $this->assertSame('http://localhost/sk/%3Cnone%3E', $link_language->attr('href')); + $this->assertSame('slovenÄina', $link_language->text()); + $link_language = $crawler->filter('a#link_sl'); + $this->assertSame('http://localhost/sl/%3Cnone%3E', $link_language->attr('href')); + $this->assertSame('slovenÅ¡Äina', $link_language->text()); + $link_language = $crawler->filter('a#link_fi'); + $this->assertSame('http://localhost/fi/%3Cnone%3E', $link_language->attr('href')); + $this->assertSame('suomi', $link_language->text()); + $link_language = $crawler->filter('a#link_sv'); + $this->assertSame('http://localhost/sv/%3Cnone%3E', $link_language->attr('href')); + $this->assertSame('svenska', $link_language->text()); + + } + +}