Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bugfix/#64: Empty regions verification optimization #124

Merged
merged 8 commits into from
Jul 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@
}
},
"require": {
"drupal/core": "^9 || ^10"
"drupal/core": "^9 || ^10",
"drupal/twig_real_content": "^1.0"
},
"require-dev": {},
"minimum-stability": "dev",
Expand Down
25 changes: 25 additions & 0 deletions includes/preprocess.html.inc
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,31 @@
* Implements hook_preprocess_HOOK() for html.html.twig.
*/
function kiso_preprocess_html(&$variables) {

$variables['page_navigation'] = _kiso_render_region($variables['page']['navigation']);
$variables['page_complementary'] = _kiso_render_region($variables['page']['complementary']);
$has_navigation = _kiso_has_region($variables['page_navigation']);
$has_complementary = _kiso_has_region($variables['page_complementary']);

// Added body classes when sidebar(s) has (have) content.
if ($has_navigation && $has_complementary) {
$variables['attributes']['class'][] = 'sidebar';
$variables['attributes']['class'][] = 'two-sidebars';
}
elseif ($has_navigation) {
$variables['attributes']['class'][] = 'sidebar';
$variables['attributes']['class'][] = 'one-sidebar';
$variables['attributes']['class'][] = 'is-visible--navigation';
}
elseif ($has_complementary) {
$variables['attributes']['class'][] = 'sidebar';
$variables['attributes']['class'][] = 'one-sidebar';
$variables['attributes']['class'][] = 'is-visible--complementary';
}
else {
$variables['attributes']['class'][] = 'no-sidebars';
}

// Add body classes related to node content.
$node = \Drupal::routeMatch()->getParameter('node');
if ($node) {
Expand Down
8 changes: 8 additions & 0 deletions includes/preprocess.page.inc
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,14 @@
* Implements hook_preprocess_HOOK() for page.html.twig.
*/
function kiso_preprocess_page(&$variables) {
// Add boolean variables detecting if regions are empty.
$theme = \Drupal::theme()->getActiveTheme()->getName();
$regions = system_region_list($theme);
foreach ($regions as $key => $value) {
$variables['page_'. $key] = _kiso_render_region($variables['page'][$key]);
$variables['has_' . $key] = _kiso_has_region($variables['page_'. $key]);
}

// Create variable for status code.
if ($exception = \Drupal::request()->get('exception')) {
$status_code = $exception->getStatusCode();
Expand Down
86 changes: 83 additions & 3 deletions includes/preprocess.utils.inc
Original file line number Diff line number Diff line change
@@ -1,14 +1,94 @@
<?php

use Twig\TwigFilter;

/**
* Small function to render the regions without passing them by reference.
*
* @param $region
*
* @return mixed
*/
function _kiso_render_region($region) {
return Drupal::service('renderer')->render($region);
}

/**
* @file
* Utilities used by the theme preprocess functions.
*/

/**
* Properly detect if regions are empty.
*
* @param ?string $markup
* The rendered region markup to be tested if empty.
* @param string $allowed_tags
* Allowed tags to be excluded from the strip HTML tags process.
*
* @return
* TRUE if the region exists and is not empty, FALSE otherwise.
*
* @see https://www.drupal.org/node/953034
* @see https://drupal.stackexchange.com/questions/175389/how-do-i-properly-detect-if-region-is-empty
*/
function _kiso_has_region(?string $markup, string $allowed_tags = '') {
$moduleHandler = Drupal::service('module_handler');
if ($moduleHandler->moduleExists('twig_real_content')) {
$filters = Drupal::service('twig_real_content.twig_extension')
->getFilters();
$key = array_search('real_content', array_map(function(TwigFilter $filter) {
return $filter->getName();
}, $filters), TRUE);
$callable = $filters[$key]->getCallable();

$real_content = $callable($markup);

return !empty($real_content);
}
else {
$non_conditional_html_comments_pattern = '/<!--(.|\s)*?-->\s*|\r|\n/';
$cleaned_region_output = preg_replace($non_conditional_html_comments_pattern, '', $markup);

return !empty(_kiso_strip_tags($cleaned_region_output, $allowed_tags));
}
}

/**
* Strips html tags, except allowed, returning a trimmed clean markup.
*
* @param string $markup
* Original markup.
* @param string|array $allowed_tags
* Allowed tags, can be an array of tag of a string.
*
* @return string
* @throws \Twig\Error\RuntimeError
*/
function _kiso_strip_tags(string $markup, string $allowed_tags = '') {
$allowed_tags .= '<drupal-render-placeholder><img>';
return trim(strip_tags($markup, $allowed_tags));
function _kiso_strip_tags(string $markup, string|array $allowed_tags) {
$allowed_tags_base = [
'<drupal-render-placeholder>',
'<embed>',
'<hr>',
'<iframe>',
'<img>',
'<input>',
'<link>',
'<object>',
'<script>',
'<source>',
'<style>',
'<video>',
];

if (is_string($allowed_tags)) {
$allowed_tags .= implode('', $allowed_tags_base);
}
elseif (is_array($allowed_tags)) {
$allowed_tags = array_merge($allowed_tags_base, $allowed_tags);
}

$text = strip_tags($markup, $allowed_tags);
$text = twig_trim_filter($text);
return $text;
}
11 changes: 0 additions & 11 deletions templates/layout/html.html.twig
Original file line number Diff line number Diff line change
Expand Up @@ -27,17 +27,6 @@
db_offline ? 'db-offline',
]
%}
{% set page_navigaton = page.navigation|render %}
{% set page_complementary = page.complementary|render %}
{% if page_navigation is real_content and page_complementary is real_content %}
{% set body_classes = body_classes|merge(['sidebar', 'two-sidebars']) %}
{% elseif page_navigaton is real_content %}
{% set body_classes = body_classes|merge(['sidebar', 'one-sidebar', 'is-visible--navigation']) %}
{% elseif page_complementary is real_content %}
{% set body_classes = body_classes|merge(['sidebar', 'one-sidebar', 'is-visible--complementary']) %}
{% else %}
{% set body_classes = body_classes|merge(['no-sidebars']) %}
{% endif %}
{# Enable the "Back to top" button. #}
{% if backtotop_enable %}
{% set attributes = attributes.setAttribute('id', 'top') %}
Expand Down
46 changes: 25 additions & 21 deletions templates/layout/page.html.twig
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@
*
* You will find same variables as in the core 'page.html.twig' template.
*
* Custom variables:
* - has_tools, has_header, has_header_collapsible, has_highlighted, has_navigation,
* has_complementary, has_postscript, has_footer: Properly detect if regions are empty.
*
* Regions:
* - page.tools: Items for the Toolbar region.
* - page.header: Items for the Header region.
Expand Down Expand Up @@ -36,36 +40,36 @@
{% set container = container_fluid ? 'container-fluid' : 'container' %}

{# Toolbar Area #}
{% if page.tools|render is real_content %}
{% if has_tools %}
{% block tools %}
<div class="page__wrapper page__wrapper--tools">
<div class="page__section page__section--tools {{ container }}">
{{ page.tools }}
{{ page_tools }}
</div>
</div>
{% endblock %}
{% endif %}

{# Banner Landmark #}
{% if page.header|render is real_content or page.header_collapsible|render is real_content %}
{% if has_header or has_header_collapsible %}
{% block header %}
<div class="page__wrapper page__wrapper--header">
<header class="page__section page__section--header {{ container }}">
{{ page.header }}
{{ page_header }}
{# This button is used as the toggle for the Header (Collapsible) #}
<button type="button" class="button button--toggler" aria-controls="headerCollapsible" aria-expanded="false"><span>Menu</span></button>
{{ page.header_collapsible }}
{{ page_header_collapsible }}
</header>
</div>
{% endblock %}
{% endif %}

{# Featured content Area #}
{% if page.highlighted|render is real_content %}
{% if has_highlighted %}
{% block highlighted %}
<div class="page__wrapper page__wrapper--highlighted">
<div class="page__section page__section--highlighted {{ container }}">
{{ page.highlighted }}
{{ page_highlighted }}
</div>
</div>
{% endblock %}
Expand All @@ -77,15 +81,15 @@

{# Dynamic help text #}
{% block help %}
{{ page.help }}
{{ page_help }}
{% endblock %}

{# Breadcrumb #}
{% block breadcrumb %}
{{ page.breadcrumb }}
{{ page_breadcrumb }}
{% endblock %}

{% if page.navigation|render is real_content or page.complementary|render is real_content %}
{% if has_navigation or has_complementary %}
<div class="{{ container }}">
<div class="row">
{% endif %}
Expand All @@ -95,12 +99,12 @@
set content_classes = [
'page__section',
'page__section--content',
page.navigation|render is real_content or page.complementary|render is real_content ? '' : container,
has_navigation or has_complementary ? '' : container,
]
%}
{% block content %}
<main{{ content_attributes.addClass(content_classes).setAttribute('role', 'main') }}>
{{ page.content }}
{{ page_content }}

{# Enable and display the "Back to top" button. #}
{% if backtotop_enable %}
Expand All @@ -110,24 +114,24 @@
{% endblock %}

{# Navigation sidebar (Left) #}
{% if page.navigation|render is real_content %}
{% if has_navigation %}
{% block navigation %}
<div class="page__section page__section--navigation">
{{ page.navigation }}
{{ page_navigation }}
</div>
{% endblock %}
{% endif %}

{# Related content sidebar (Right) #}
{% if page.complementary|render is real_content %}
{% if has_complementary %}
{% block complementary %}
<div class="page__section page__section--complementary">
{{ page.complementary }}
{{ page_complementary }}
</div>
{% endblock %}
{% endif %}

{% if page.navigation|render is real_content or page.complementary|render is real_content %}
{% if has_navigation or has_complementary %}
</div>
</div>
{% endif %}
Expand All @@ -136,22 +140,22 @@
{% endblock %}

{# Footnotes Area #}
{% if page.postscript|render is real_content %}
{% if has_postscript %}
{% block postscript %}
<div class="page__wrapper page__wrapper--postscript">
<div class="page__section page__section--postscript {{ container }}">
{{ page.postscript }}
{{ page_postscript }}
</div>
</div>
{% endblock %}
{% endif %}

{# Contentinfo Landmark #}
{% if page.footer|render is real_content %}
{% if has_footer %}
{% block footer %}
<div class="page__wrapper page__wrapper--footer">
<footer class="page__section page__section--footer {{ container }}">
{{ page.footer }}
{{ page_footer }}
</footer>
</div>
{% endblock %}
Expand Down