Skip to content

Commit

Permalink
Merge branch 'hotfix/1.4.5'
Browse files Browse the repository at this point in the history
  • Loading branch information
AlexSkrypnyk committed Apr 6, 2023
2 parents bdba98d + c99520f commit db2f1fa
Show file tree
Hide file tree
Showing 17 changed files with 572 additions and 18 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/storybook-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v2
- uses: actions/setup-node@v3
with:
node-version: '16.x'
- name: Install dependencies
Expand Down
339 changes: 339 additions & 0 deletions LICENSE.txt

Large diffs are not rendered by default.

1 change: 0 additions & 1 deletion behat.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ default:
browser_name: chrome
selenium2:
wd_host: "http://chrome:4444/wd/hub"
capabilities: { "browser": "chrome", "version": "*", "marionette": true, "extra_capabilities": { "chromeOptions": { "w3c": false } } }
javascript_session: selenium2
# Provides integration with Drupal APIs.
Drupal\DrupalExtension:
Expand Down
2 changes: 1 addition & 1 deletion docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ services:

# Chrome container, used for browser testing.
chrome:
image: selenium/standalone-chrome:4.5.2-20221021
image: selenium/standalone-chrome
shm_size: '1gb' # Increase '/dev/shm' partition size to avoid browser crashing.
<<: *default-volumes # Use default volumes to provide access to test fixtures.
environment:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
{% set collapsible_panel = 'data-collapsible-panel' %}
{% set aria_hidden = item.in_active_trail ? 'aria-hidden="false"' : 'aria-hidden="true"' %}
{% endif %}
<div class="ct-menu__sub-menu__wrapper ct-menu__sub-menu__wrapper--level-{{ menu_level }}" {{ collapsible_panel }} {{ aria_hidden }}>
<div class="ct-menu__sub-menu__wrapper ct-menu__sub-menu__wrapper--level-{{ menu_level }}" {{ collapsible_panel }} {{ aria_hidden|raw }}>
<ul class="ct-menu ct-menu__sub-menu ct-menu--level-{{ menu_level }}">
{% endif %}

Expand All @@ -49,10 +49,10 @@
{% set aria_expanded = item.in_active_trail and item.is_expanded ? 'aria-expanded="true"' : 'aria-expanded="false"' %}
{% if is_collapsible %}
{% set collapsible_collapsed = item.in_active_trail and item.is_expanded ? '' : 'data-collapsible-collapsed' %}
{% set item_attributes = [item_attributes, 'data-collapsible', 'data-collapsible-duration=0', collapsible_collapsed, aria_expanded]|join(' ') %}
{% set item_attributes = [item_attributes, 'data-collapsible', 'data-collapsible-duration="0"', collapsible_collapsed, aria_expanded]|join(' ') %}
{% endif %}

<li class="{{ item_classes|join(' ') }}" {{ item_attributes }}>
<li class="{{ item_classes|join(' ') }}" {{ item_attributes|raw }}>

{% include '@atoms/link/link.twig' with {
theme: theme,
Expand All @@ -66,7 +66,7 @@
} only %}

{% if is_collapsible and item.below %}
<a href="#" class="ct-menu__item__link-trigger" data-collapsible-trigger {{ aria_expanded }} title="Expand {{ item.title }} menu"></a>
<a href="#" class="ct-menu__item__link-trigger" data-collapsible-trigger {{ aria_expanded|raw }} title="Expand {{ item.title }} menu"></a>
{% endif %}

{% if item.below %}
Expand Down
49 changes: 47 additions & 2 deletions docroot/themes/contrib/civictheme/includes/link.inc
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,15 @@ use Drupal\Component\Utility\Html;
* @SuppressWarnings(PHPMD.BooleanArgumentFlag)
*/
function _civictheme_process_html_content_links($html, $base_url, $new_window = FALSE, $external_new_window = FALSE, array $override_domains = []) {
if (_civictheme_feature_is_optedout('process', 'components.link')) {
return $html;
}

$class_prefix = 'ct-';
$link_class = 'ct-content-link ct-theme-light';
$link_external_class = 'ct-content-link--external';

$email_pattern = '~(^|[\s\.,;\n\(])([a-zA-Z0-9._+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,4})~';
$html = preg_replace($email_pattern, '$1<a href="mailto:$2">$2</a>', $html);
$html = _civictheme_process_html_content_links_emails($html);

$dom = Html::load($html);

Expand Down Expand Up @@ -98,3 +101,45 @@ function _civictheme_process_html_content_links($html, $base_url, $new_window =

return Html::serialize($dom);
}

/**
* Process HTML content to replace emails with mailto links.
*
* @SuppressWarnings(PHPMD.StaticAccess)
* @SuppressWarnings(PHPMD.MissingImport)
*/
function _civictheme_process_html_content_links_emails($html) {
if (_civictheme_feature_is_optedout('process', 'components.link.email')) {
return $html;
}

$dom = Html::load($html);

if (!$dom) {
return $html;
}

$xpath = new \DOMXPath($dom);

$text_nodes = $xpath->query('//text()[not(ancestor::a)]');

foreach ($text_nodes as $text_node) {
if (preg_match_all(_civictheme_process_html_content_links_get_email_regex(), $text_node->nodeValue, $matches)) {
$emails = $matches[0];
foreach ($emails as $email) {
$replacement = $dom->createDocumentFragment();
$replacement->appendXML(str_replace($email, "<a href=\"mailto:$email\">$email</a>", $text_node->textContent));
$text_node->replaceWith($replacement);
}
}
}

return Html::serialize($dom);
}

/**
* Email pattern as a regex string.
*/
function _civictheme_process_html_content_links_get_email_regex() {
return '/\b[A-Za-z0-9._%+-]+@(?:(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\.)+(?:[a-zA-Z]{2,})\b)(?!\.[a-zA-Z0-9]{1,}\b)/';
}
23 changes: 23 additions & 0 deletions docroot/themes/contrib/civictheme/includes/utilities.inc
Original file line number Diff line number Diff line change
Expand Up @@ -726,7 +726,30 @@ function _civictheme_feature_is_optedout($type, $name, $context = NULL) {
}
}
break;

default:
try {
$optouts = civictheme_get_theme_config_manager()->load('optouts', []);
// Additionally filter using available flags to ensure that only
// correctly mapped flags are used.
if (array_key_exists($name, _civictheme_feature_optout_flags())) {
$optedout = in_array($name, $optouts);
}
}
catch (\Exception $e) {
$optedout = FALSE;
}
}

return $optedout;
}

/**
* Map of available opt-out flags.
*/
function _civictheme_feature_optout_flags() {
return [
'components.link' => t('Links processing'),
'components.link.email' => t('Email links processing'),
];
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ class CivicthemeSettingsFormSectionColors extends CivicthemeSettingsFormSectionB
* {@inheritdoc}
*/
public function weight() {
return 2;
return 20;
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ class CivicthemeSettingsFormSectionComponents extends CivicthemeSettingsFormSect
* {@inheritdoc}
*/
public function weight() {
return 3;
return 30;
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ class CivicthemeSettingsFormSectionInformation extends CivicthemeSettingsFormSec
* {@inheritdoc}
*/
public function weight() {
return 2;
return 20;
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ class CivicthemeSettingsFormSectionMigration extends CivicthemeSettingsFormSecti
* {@inheritdoc}
*/
public function weight() {
return 4;
return 40;
}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
<?php

namespace Drupal\civictheme\Settings;

use Drupal\Component\Render\FormattableMarkup;
use Drupal\Core\Form\FormStateInterface;

/**
* CivicTheme settings section to opt-out from features.
*/
class CivicthemeSettingsFormSectionOptout extends CivicthemeSettingsFormSectionBase {

/**
* {@inheritdoc}
*/
public function weight() {
return 35;
}

/**
* {@inheritdoc}
*
* @SuppressWarnings(PHPMD.StaticAccess)
*/
public function form(&$form, FormStateInterface &$form_state) {
$form['optout_details'] = [
'#type' => 'details',
'#title' => $this->t('Opt-out from features'),
'#open' => FALSE,
'#weight' => 60,
];

$form['optout_details']['optouts'] = [
'#type' => 'textarea',
'#title' => $this->t('Opt-out flags'),
'#rows' => 3,
'#description' => $this->t('Add opt-out feature flags to disable CivicTheme features. One per line. Expand the fieldset below to get a list of available flags.'),
'#element_validate' => [[self::class, 'multilineToArray']],
'#default_value' => implode("\n", $this->themeConfigManager->load('optouts', [])),
];

$form['optout_details']['mapping'] = [
'#type' => 'details',
'#title' => $this->t('Available flags'),
'#open' => FALSE,
'#weight' => 60,
];

$form['optout_details']['mapping']['content'] = [
'#theme' => 'item_list',
'#items' => array_map(function ($key, $description) {
return new FormattableMarkup('<code>@key</code>: @description', [
'@key' => $key,
'@description' => $description,
]);
}, array_keys(_civictheme_feature_optout_flags()), _civictheme_feature_optout_flags()),
];
}

/**
* Convert element value from multiline string to an array.
*/
public static function multilineToArray(array $element, &$form_state) {
$lines = is_array($element['#value']) ? $element['#value'] : explode("\n", str_replace("\r\n", "\n", $element['#value']));
$form_state->setValueForElement($element, array_values(array_filter(array_map('trim', $lines))));
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ class CivicthemeSettingsFormSectionProvision extends CivicthemeSettingsFormSecti
* {@inheritdoc}
*/
public function weight() {
return 5;
return 50;
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ class CivicthemeSettingsFormSectionStorybook extends CivicthemeSettingsFormSecti
* {@inheritdoc}
*/
public function weight() {
return 6;
return 60;
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ public static function create(ContainerInterface $container) {
* {@inheritdoc}
*/
public function weight() {
return 1;
return 10;
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -242,7 +242,8 @@ public function dataProviderProcessHtmlLinks() {
'<p>Word3 <a href="http://example2.com/path">Link</a> word4</p>' .
'<p>Word5 <a href="http://example2.com" class="someclass">Link</a> word6</p>' .
'<p>Word7 <a href="http://example2.com/path" class="someclass">Link</a> word8</p>' .
'<p>Word9 <a href="mailto:example@example.com" class="someclass">example@example.com</a> word10</p>',
'<p>Word9 <a href="mailto:example@example.com" class="someclass">example@example.com</a> word10</p>' .
'<p>Word11 <a href="mailto:example@example.com" class="someclass">example@example.com</a> word12 example2@example.com word13</p>',
'example2.com',
FALSE,
FALSE,
Expand All @@ -255,7 +256,8 @@ public function dataProviderProcessHtmlLinks() {
'<p>Word3 <a href="http://example2.com/path" class="ct-content-link ct-theme-light">Link</a> word4</p>' .
'<p>Word5 <a href="http://example2.com" class="someclass ct-content-link ct-theme-light">Link</a> word6</p>' .
'<p>Word7 <a href="http://example2.com/path" class="someclass ct-content-link ct-theme-light">Link</a> word8</p>' .
'<p>Word9 <a href="mailto:example@example.com" class="someclass ct-content-link ct-theme-light">example@example.com</a> word10</p>',
'<p>Word9 <a href="mailto:example@example.com" class="someclass ct-content-link ct-theme-light">example@example.com</a> word10</p>' .
'<p>Word11 <a href="mailto:example@example.com" class="someclass ct-content-link ct-theme-light">example@example.com</a> word12 <a href="mailto:example2@example.com" class="ct-content-link ct-theme-light">example2@example.com</a> word13</p>',
],

// Existing classes.
Expand All @@ -267,4 +269,63 @@ public function dataProviderProcessHtmlLinks() {
];
}

/**
* Test for _civictheme_process_html_content_links_get_email_regex().
*
* @dataProvider dataProviderEmailRegex
*/
public function testEmail($string, $match) {
preg_match(_civictheme_process_html_content_links_get_email_regex(), $string, $matches);

if ($match) {
$this->assertEquals(1, count($matches));
$this->assertEquals($match, $matches[0]);
}
else {
$this->assertEquals(0, count($matches));
}
}

/**
* Data provider for testEmail().
*
* @SuppressWarnings(PHPMD.ExcessiveMethodLength)
*/
public function dataProviderEmailRegex() {
return [
['a@example.com', 'a@example.com'],
['a@example2.com', 'a@example2.com'],
['a@e2xample2.com', 'a@e2xample2.com'],
['a.a@e2xample2.com', 'a.a@e2xample2.com'],
['a.a1@e2xample2.com', 'a.a1@e2xample2.com'],
['a@e2xample2.com.au', 'a@e2xample2.com.au'],
['a@e2xample2.digital', 'a@e2xample2.digital'],
['a+b_c.d+e@e2xample2.digital', 'a+b_c.d+e@e2xample2.digital'],
['a+b_c.d+e-f--g@e2xample2.digital', 'a+b_c.d+e-f--g@e2xample2.digital'],

[' a+b_c.d+e@e2xample2.digital', 'a+b_c.d+e@e2xample2.digital'],
["\ta+b_c.d+e@e2xample2.digital", 'a+b_c.d+e@e2xample2.digital'],
["\na+b_c.d+e@e2xample2.digital", 'a+b_c.d+e@e2xample2.digital'],

['', FALSE],
['a@b.c', FALSE],
['a.a@b.c', FALSE],
['a.1@b.c', FALSE],
['a1@b.c', FALSE],
['a1.2@b.c', FALSE],

['a@example.com.a', FALSE],
['a@example.com.a.b', FALSE],
['a@example.com.a.b.c', FALSE],

['a@example.com.', 'a@example.com'],
['a@example.com,', 'a@example.com'],
['a@e2xample2.com.', 'a@e2xample2.com'],
['1a@e2xample2.com.', '1a@e2xample2.com'],
['1a@3e2xample2.com.', '1a@3e2xample2.com'],

['<p> a+b_c.d+e@e2xample2.digital.</p>', 'a+b_c.d+e@e2xample2.digital'],
];
}

}
19 changes: 19 additions & 0 deletions tests/behat/features/theme.settings.optout.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
@p0 @civictheme @civictheme_theme_settings @civictheme_theme_optout
Feature: Opt-out form is available in settings.

@api
Scenario: Opt-out flags can be set
Given I am logged in as a user with the "Site Administrator" role
And I visit current theme settings page

When I fill in "Opt-out flags" with:
"""
test.flag1
test.flag2
test.flag3
"""
And I press "Save configuration"
Then I should see the text "The configuration options have been saved."
And the response should contain "test.flag1"
And the response should contain "test.flag2"
And the response should contain "test.flag3"

0 comments on commit db2f1fa

Please sign in to comment.