diff --git a/core/includes/form.inc b/core/includes/form.inc index b5a22a4..baafecf 100644 --- a/core/includes/form.inc +++ b/core/includes/form.inc @@ -2217,6 +2217,7 @@ function form_pre_render_vertical_tabs($element) { function template_preprocess_vertical_tabs(&$variables) { $element = $variables['element']; $variables['children'] = (!empty($element['#children'])) ? $element['#children'] : ''; + $variables['attributes'] = new Attribute($element['#attributes']); } /** diff --git a/core/includes/theme.inc b/core/includes/theme.inc index 2e7d41e..7d23dfe 100644 --- a/core/includes/theme.inc +++ b/core/includes/theme.inc @@ -725,20 +725,6 @@ function _theme($hook, $variables = array()) { template_preprocess($default_template_variables, $hook, $info); $variables += $default_template_variables; } - if (!isset($default_attributes)) { - $default_attributes = new Attribute(); - } - foreach (array('attributes', 'title_attributes', 'content_attributes') as $key) { - if (isset($variables[$key]) && !($variables[$key] instanceof Attribute)) { - if ($variables[$key]) { - $variables[$key] = new Attribute($variables[$key]); - } - else { - // Create empty attributes. - $variables[$key] = clone $default_attributes; - } - } - } // Render the output using the template file. $template_file = $info['template'] . $extension; @@ -1859,6 +1845,7 @@ function template_preprocess_feed_icon(&$variables) { $variables['attributes']['class'] = array('feed-icon'); // Stripping tags because that's what l() used to do. $variables['attributes']['title'] = strip_tags($text); + $variables['attributes'] = new Attribute($variables['attributes']); } /** @@ -2010,8 +1997,6 @@ function _template_preprocess_default_variables() { // Variables that don't depend on a database connection. $variables = array( 'attributes' => array(), - 'title_attributes' => array(), - 'content_attributes' => array(), 'title_prefix' => array(), 'title_suffix' => array(), 'db_is_active' => !defined('MAINTENANCE_MODE'), @@ -2077,6 +2062,8 @@ function template_preprocess_html(&$variables) { } } + $variables['attributes'] = new Attribute($variables['attributes']); + // Initializes attributes which are specific to the html and body elements. $variables['html_attributes'] = new Attribute; @@ -2473,6 +2460,8 @@ function template_preprocess_maintenance_page(&$variables) { $variables['attributes']['class'][] = 'sidebar-' . $variables['layout']; } + $variables['attributes'] = new Attribute($variables['attributes']); + $variables['head'] = drupal_get_html_head(); // While this code is used in the installer, the language module may not be @@ -2537,6 +2526,8 @@ function template_preprocess_region(&$variables) { $variables['attributes']['class'][] = 'region'; $variables['attributes']['class'][] = drupal_html_class('region-' . $variables['region']); + + $variables['attributes'] = new Attribute($variables['attributes']); } /** diff --git a/core/lib/Drupal/Core/Template/Attribute.php b/core/lib/Drupal/Core/Template/Attribute.php index 473b31a..1ebdd9f 100644 --- a/core/lib/Drupal/Core/Template/Attribute.php +++ b/core/lib/Drupal/Core/Template/Attribute.php @@ -83,6 +83,9 @@ protected function createAttributeValue($name, $value) { if (is_array($value)) { $value = new AttributeArray($name, $value); } + elseif ($name == 'class') { + $value = new AttributeArray($name, array($value)); + } elseif (is_bool($value)) { $value = new AttributeBoolean($name, $value); } diff --git a/core/modules/aggregator/aggregator.theme.inc b/core/modules/aggregator/aggregator.theme.inc index 84e0d1e..48fb92d 100644 --- a/core/modules/aggregator/aggregator.theme.inc +++ b/core/modules/aggregator/aggregator.theme.inc @@ -40,6 +40,7 @@ function template_preprocess_aggregator_item(&$variables) { } $variables['attributes']['class'][] = 'feed-item'; + $variables['attributes'] = new Attribute($variables['attributes']); } /** @@ -172,6 +173,7 @@ function template_preprocess_aggregator_feed_source(&$variables) { } $variables['attributes']['class'][] = 'feed-source'; + $variables['attributes'] = new Attribute($variables['attributes']); } /** diff --git a/core/modules/block/block.module b/core/modules/block/block.module index 2ee5f48..02cf60f 100644 --- a/core/modules/block/block.module +++ b/core/modules/block/block.module @@ -7,6 +7,7 @@ use Drupal\block\BlockInterface; use Drupal\Component\Plugin\Exception\PluginException; +use Drupal\Core\Template\Attribute; use Symfony\Cmf\Component\Routing\RouteObjectInterface; /** @@ -483,6 +484,19 @@ function template_preprocess_block(&$variables) { $variables['attributes']['class'][] = 'block'; $variables['attributes']['class'][] = drupal_html_class('block-' . $variables['configuration']['module']); + // The block template provides a wrapping element for the content. Render the + // #attributes of the content on this wrapping element rather than passing + // them through to the content's #theme function/template. This allows the + // content to not require a function/template at all, or if it does use one, + // to not require it to output an extra wrapping element. + if(!isset($variables['content_attributes'])) { + $variables['content_attributes'] = array(); + } + if (isset($variables['content']['#attributes'])) { + $variables['content_attributes'] = NestedArray::mergeDeep($variables['content_attributes'], $variables['content']['#attributes']); + unset($variables['content']['#attributes']); + } + // Add default class for block content. $variables['content_attributes']['class'][] = 'content'; @@ -490,6 +504,16 @@ function template_preprocess_block(&$variables) { if ($id = $variables['elements']['#block']->id()) { $variables['attributes']['id'] = drupal_html_id('block-' . $id); } + + static $default_attributes; + if (!isset($default_attributes)) { + $default_attributes = new Attribute; + } + + // For best performance, we only instantiate Attribute objects when needed. + $variables['attributes'] = isset($variables['attributes']) ? new Attribute($variables['attributes']) : clone $default_attributes; + $variables['title_attributes'] = isset($variables['title_attributes']) ? new Attribute($variables['title_attributes']) : clone $default_attributes; + $variables['content_attributes'] = isset($variables['content_attributes']) ? new Attribute($variables['content_attributes']) : clone $default_attributes; } /** diff --git a/core/modules/comment/comment.module b/core/modules/comment/comment.module index 0cbe24c..c3ec3ea 100644 --- a/core/modules/comment/comment.module +++ b/core/modules/comment/comment.module @@ -12,6 +12,7 @@ use Drupal\Core\Entity\EntityInterface; use Drupal\Core\Entity\EntityChangedInterface; +use Drupal\Core\Template\Attribute; use Drupal\comment\CommentInterface; use Drupal\Core\Entity\Display\EntityViewDisplayInterface; use Drupal\field\FieldInstanceConfigInterface; @@ -1517,6 +1518,16 @@ function template_preprocess_comment(&$variables) { $variables['attributes']['data-comment-user-id'] = $comment->getOwnerId(); $variables['content_attributes']['class'][] = 'content'; + + static $default_attributes; + if (!isset($default_attributes)) { + $default_attributes = new Attribute; + } + + // For best performance, we only instantiate Attribute objects when needed. + $variables['attributes'] = isset($variables['attributes']) ? new Attribute($variables['attributes']) : clone $default_attributes; + $variables['title_attributes'] = isset($variables['title_attributes']) ? new Attribute($variables['title_attributes']) : clone $default_attributes; + $variables['content_attributes'] = isset($variables['content_attributes']) ? new Attribute($variables['content_attributes']) : clone $default_attributes; } /** @@ -1541,6 +1552,7 @@ function template_preprocess_comment_wrapper(&$variables) { // Add a comment wrapper class. $variables['attributes']['class'][] = 'comment-wrapper'; + $variables['attributes'] = new Attribute($variables['attributes']); // Create separate variables for the comments and comment form. $variables['comments'] = $variables['content']['comments']; diff --git a/core/modules/contextual/contextual.module b/core/modules/contextual/contextual.module index 3513f56..8b85996 100644 --- a/core/modules/contextual/contextual.module +++ b/core/modules/contextual/contextual.module @@ -145,6 +145,8 @@ function contextual_preprocess(&$variables, $hook, $info) { '#id' => _contextual_links_to_id($element['#contextual_links']), ); } + + $variables['attributes'] = new Attribute($variables['attributes']); } /** diff --git a/core/modules/datetime/datetime.module b/core/modules/datetime/datetime.module index 27b52e4..eb8f5a3 100644 --- a/core/modules/datetime/datetime.module +++ b/core/modules/datetime/datetime.module @@ -201,7 +201,6 @@ function datetime_date_default_time($date) { function template_preprocess_datetime_form(&$variables) { $element = $variables['element']; - $variables['attributes'] = array(); if (isset($element['#id'])) { $variables['attributes']['id'] = $element['#id']; } @@ -210,6 +209,7 @@ function template_preprocess_datetime_form(&$variables) { } $variables['attributes']['class'][] = 'container-inline'; + $variables['attributes'] = new Attribute($variables['attributes']); $variables['content'] = $element; } diff --git a/core/modules/field/field.module b/core/modules/field/field.module index 6bd2c6d..393adad 100644 --- a/core/modules/field/field.module +++ b/core/modules/field/field.module @@ -544,16 +544,11 @@ function template_preprocess_field(&$variables, $hook) { if (!isset($default_attributes)) { $default_attributes = new Attribute; } - // The default theme implementation for fields is a function. - // template_preprocess() (which initializes the attributes, title_attributes, - // and content_attributes arrays) does not run for theme function - // implementations. Additionally, Attribute objects for the three variables - // below only get instantiated for template file implementations, and we need - // Attribute objects for printing in both theme functions and template files. + // For best performance, we only instantiate Attribute objects when needed. $variables['attributes'] = isset($variables['attributes']) ? new Attribute($variables['attributes']) : clone $default_attributes; - $variables['title_attributes'] = isset($variables['title_attributes']) ? new Attribute($variables['title_attributes']) : clone($default_attributes); - $variables['content_attributes'] = isset($variables['content_attributes']) ? new Attribute($variables['content_attributes']) : clone($default_attributes); + $variables['title_attributes'] = isset($variables['title_attributes']) ? new Attribute($variables['title_attributes']) : clone $default_attributes; + $variables['content_attributes'] = isset($variables['content_attributes']) ? new Attribute($variables['content_attributes']) : clone $default_attributes; // Modules (e.g., rdf.module) can add field item attributes (to // $item->_attributes) within hook_entity_prepare_view(). Some field @@ -563,7 +558,7 @@ function template_preprocess_field(&$variables, $hook) { // formatters leave them within $element['#items'][$delta]['_attributes'] to // be rendered on the item wrappers provided by theme_field(). foreach ($variables['items'] as $delta => $item) { - $variables['item_attributes'][$delta] = !empty($element['#items'][$delta]->_attributes) ? new Attribute($element['#items'][$delta]->_attributes) : clone($default_attributes); + $variables['item_attributes'][$delta] = !empty($element['#items'][$delta]->_attributes) ? new Attribute($element['#items'][$delta]->_attributes) : clone $default_attributes; } } diff --git a/core/modules/filter/filter.module b/core/modules/filter/filter.module index ca50030..f5a1e34 100644 --- a/core/modules/filter/filter.module +++ b/core/modules/filter/filter.module @@ -712,6 +712,8 @@ function template_preprocess_filter_guidelines(&$variables) { '#theme' => 'filter_tips', '#tips' => _filter_tips($format->format, FALSE), ); + + $variables['attributes'] = new Attribute($variables['attributes']); } /** diff --git a/core/modules/forum/forum.module b/core/modules/forum/forum.module index 2843782..c8d4765 100644 --- a/core/modules/forum/forum.module +++ b/core/modules/forum/forum.module @@ -852,6 +852,7 @@ function template_preprocess_forum_icon(&$variables) { $variables['attributes']['class'][] = 'icon'; $variables['attributes']['class'][] = 'topic-status-' . $icon_status_class; $variables['attributes']['title'] = $variables['icon_title']; + $variables['attributes'] = new Attribute($variables['attributes']); } /** diff --git a/core/modules/node/node.module b/core/modules/node/node.module index 5bef612..d5d6f86 100644 --- a/core/modules/node/node.module +++ b/core/modules/node/node.module @@ -713,6 +713,17 @@ function template_preprocess_node(&$variables) { $variables['attributes']['class'][] = 'preview'; } $variables['content_attributes']['class'][] = 'content'; + + static $default_attributes; + if (!isset($default_attributes)) { + $default_attributes = new Attribute; + } + + // For best performance, we only instantiate Attribute objects when needed. + $variables['attributes'] = isset($variables['attributes']) ? new Attribute($variables['attributes']) : clone $default_attributes; + $variables['title_attributes'] = isset($variables['title_attributes']) ? new Attribute($variables['title_attributes']) : clone $default_attributes; + $variables['content_attributes'] = isset($variables['content_attributes']) ? new Attribute($variables['content_attributes']) : clone $default_attributes; + } /** diff --git a/core/modules/rdf/rdf.module b/core/modules/rdf/rdf.module index 01cb430..c24b331 100644 --- a/core/modules/rdf/rdf.module +++ b/core/modules/rdf/rdf.module @@ -276,6 +276,7 @@ function rdf_preprocess_node(&$variables) { $bundle_mapping = $mapping->getPreparedBundleMapping('node', $bundle); $variables['attributes']['about'] = empty($variables['node_url']) ? NULL: $variables['node_url']; $variables['attributes']['typeof'] = empty($bundle_mapping['types']) ? NULL : $bundle_mapping['types']; + $variables['attributes'] = new Attribute($variables['attributes']); // Adds RDFa markup for the node title as metadata because wrapping the title // with markup is not reliable and the title output is different depdending on @@ -344,6 +345,7 @@ function rdf_preprocess_user(&$variables) { if (!empty($bundle_mapping['types'])) { $variables['attributes']['typeof'] = $bundle_mapping['types']; $variables['attributes']['about'] = $account->url(); + $variables['attributes'] = new Attribute($variables['attributes']); } // If we are on the user account page, add the relationship between the // sioc:UserAccount and the foaf:Person who holds the account. @@ -421,6 +423,7 @@ function rdf_preprocess_username(&$variables) { } else { $variables['attributes'] = array_merge_recursive($variables['attributes'], $attributes); + $variables['attributes'] = new Attribute($variables['attributes']); } } diff --git a/core/modules/search/search.pages.inc b/core/modules/search/search.pages.inc index ec71855..7598cec 100644 --- a/core/modules/search/search.pages.inc +++ b/core/modules/search/search.pages.inc @@ -59,5 +59,15 @@ function template_preprocess_search_result(&$variables) { // Provide separated and grouped meta information.. $variables['info_split'] = $info; $variables['info'] = implode(' - ', $info); + + static $default_attributes; + if (!isset($default_attributes)) { + $default_attributes = new Attribute; + } + + // For best performance, we only instantiate Attribute objects when needed. + $variables['attributes'] = isset($variables['attributes']) ? new Attribute($variables['attributes']) : clone $default_attributes; + $variables['title_attributes'] = isset($variables['title_attributes']) ? new Attribute($variables['title_attributes']) : clone $default_attributes; + $variables['content_attributes'] = isset($variables['content_attributes']) ? new Attribute($variables['content_attributes']) : clone $default_attributes; } diff --git a/core/modules/system/tests/modules/theme_test/theme_test.module b/core/modules/system/tests/modules/theme_test/theme_test.module index 4e1fc13..b9b5472 100644 --- a/core/modules/system/tests/modules/theme_test/theme_test.module +++ b/core/modules/system/tests/modules/theme_test/theme_test.module @@ -126,6 +126,7 @@ function theme_theme_test_function_template_override($variables) { */ function template_preprocess_theme_test_render_element(&$variables) { $variables['attributes']['data-variables-are-preprocessed'] = TRUE; + $variables['attributes'] = new Attribute($variables['attributes']); } /** diff --git a/core/modules/taxonomy/taxonomy.module b/core/modules/taxonomy/taxonomy.module index 3124f63..9c87960 100644 --- a/core/modules/taxonomy/taxonomy.module +++ b/core/modules/taxonomy/taxonomy.module @@ -417,6 +417,7 @@ function template_preprocess_taxonomy_term(&$variables) { $variables['attributes']['class'][] = 'taxonomy-term'; $vocabulary_name_css = str_replace('_', '-', $term->bundle()); $variables['attributes']['class'][] = 'vocabulary-' . $vocabulary_name_css; + $variables['attributes'] = new Attribute($variables['attributes']); } /** diff --git a/core/modules/toolbar/toolbar.module b/core/modules/toolbar/toolbar.module index 7217986..ca337fc 100644 --- a/core/modules/toolbar/toolbar.module +++ b/core/modules/toolbar/toolbar.module @@ -221,7 +221,7 @@ function template_preprocess_toolbar(&$variables) { $element = $variables['element']; // Prepare the toolbar attributes. - $variables['attributes'] = $element['#attributes']; + $variables['attributes'] = new Attribute($element['#attributes']); $variables['toolbar_attributes'] = new Attribute($element['#bar']['#attributes']); $variables['toolbar_heading'] = $element['#bar']['#heading']; diff --git a/core/modules/user/user.pages.inc b/core/modules/user/user.pages.inc index e093781..1d92b1a 100644 --- a/core/modules/user/user.pages.inc +++ b/core/modules/user/user.pages.inc @@ -10,6 +10,7 @@ use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException; use Symfony\Component\HttpKernel\HttpKernelInterface; use Drupal\Component\Utility\Crypt; +use Drupal\Core\Template\Attribute; /** * Menu callback; process one time login link and redirects to the user page on success. @@ -116,6 +117,7 @@ function template_preprocess_user(&$variables) { // Set up attributes. $variables['attributes']['class'][] = 'profile'; + $variables['attributes'] = new Attribute($variables['attributes']); } /** diff --git a/core/modules/views/views.theme.inc b/core/modules/views/views.theme.inc index 400fcf2..b80f86a 100644 --- a/core/modules/views/views.theme.inc +++ b/core/modules/views/views.theme.inc @@ -159,6 +159,8 @@ function template_preprocess_views_view(&$variables) { } $variables['rows'] = $form; } + + $variables['attributes'] = new Attribute($variables['attributes']); } /** @@ -758,6 +760,8 @@ function template_preprocess_views_view_table(&$variables) { // This is needed to target tables constructed by this function. $variables['attributes']['class'][] = 'responsive-enabled'; } + + $variables['attributes'] = new Attribute($variables['attributes']); } /** @@ -881,6 +885,7 @@ function template_preprocess_views_view_grid(&$variables) { // Add items to the variables array. $variables['items'] = $items; + $variables['attributes'] = new Attribute($variables['attributes']); } /** @@ -927,6 +932,7 @@ function template_preprocess_views_view_unformatted(&$variables) { } $variables['rows'][$id]['attributes'] = new Attribute($variables['rows'][$id]['attributes']); } + $variables['attributes'] = new Attribute($variables['attributes']); } /** diff --git a/core/modules/views_ui/views_ui.theme.inc b/core/modules/views_ui/views_ui.theme.inc index db4f3e8..776e49f 100644 --- a/core/modules/views_ui/views_ui.theme.inc +++ b/core/modules/views_ui/views_ui.theme.inc @@ -46,6 +46,8 @@ function template_preprocess_views_ui_display_tab_setting(&$variables) { if ($variables['description'] && $variables['description_separator']) { $variables['description'] .= t(':'); } + + $variables['attributes'] = new Attribute($variables['attributes']); } /** @@ -74,6 +76,8 @@ function template_preprocess_views_ui_display_tab_bucket(&$variables) { $variables['content'] = $element['#children']; $variables['title'] = $element['#title']; $variables['actions'] = !empty($element['#actions']) ? $element['#actions'] : array(); + + $variables['attributes'] = new Attribute($variables['attributes']); } /**