core/includes/common.inc | 34 ++++++++++++++-------- .../Core/Page/DefaultHtmlFragmentRenderer.php | 8 +++-- core/modules/block/block.install | 19 ------------ .../content_translation/content_translation.module | 4 +-- .../src/Tests/System/MainContentFallbackTest.php | 8 +---- core/modules/system/system.module | 15 ---------- .../tests/modules/system_test/system_test.module | 17 +++++------ .../modules/system_test/system_test.routing.yml | 8 ----- .../tests/modules/theme_test/theme_test.module | 6 ++-- core/modules/system/theme.api.php | 20 +++++++++---- core/modules/views/views.module | 20 ++++++++++--- 11 files changed, 73 insertions(+), 86 deletions(-) diff --git a/core/includes/common.inc b/core/includes/common.inc index eb11364..16b1194 100644 --- a/core/includes/common.inc +++ b/core/includes/common.inc @@ -945,8 +945,7 @@ function drupal_add_html_head_link($attributes, $header = FALSE) { * $options['preprocess'] should be only set to TRUE when a file is required for * all typical visitors and most pages of a site. It is critical that all * preprocessed files are added unconditionally on every page, even if the - * files do not happen to be needed on a page. This is normally done by calling - * _drupal_add_css() in a hook_page_build() implementation. + * files do not happen to be needed on a page. * * Non-preprocessed files should only be added to the page when they are * actually needed. @@ -994,8 +993,8 @@ function drupal_add_html_head_link($attributes, $header = FALSE) { * page of the website for users for whom it is present at all. This * defaults to FALSE. It is set to TRUE for stylesheets added via module and * theme .info.yml files. Modules that add stylesheets within - * hook_page_build() implementations, or from other code that ensures that - * the stylesheet is added to all website pages, should also set this flag + * hook_page_attachments() implementations, or from other code that ensures + * that the stylesheet is added to all website pages, should also set this flag * to TRUE. All stylesheets within the same group that have the 'every_page' * flag set to TRUE and do not have 'preprocess' set to FALSE are aggregated * together into a single aggregate file, and that aggregate file can be @@ -1395,8 +1394,7 @@ function drupal_clean_id_identifier($id) { * $options['preprocess'] should be only set to TRUE when a file is required for * all typical visitors and most pages of a site. It is critical that all * preprocessed files are added unconditionally on every page, even if the - * files are not needed on a page. This is normally done by calling - * _drupal_add_js() in a hook_page_build() implementation. + * files are not needed on a page. * * Non-preprocessed files should only be added to the page when they are * actually needed. @@ -1439,9 +1437,9 @@ function drupal_clean_id_identifier($id) { * page of the website for users for whom it is present at all. This * defaults to FALSE. It is set to TRUE for JavaScript files that are added * via module and theme .info.yml files. Modules that add JavaScript within - * hook_page_build() implementations, or from other code that ensures that - * the JavaScript is added to all website pages, should also set this flag - * to TRUE. All JavaScript files within the same group and that have the + * hook_page_attachments() implementations, or from other code that ensures + * that the JavaScript is added to all website pages, should also set this + * flag to TRUE. All JavaScript files within the same group and that have the * 'every_page' flag set to TRUE and do not have 'preprocess' set to FALSE * are aggregated together into a single aggregate file, and that aggregate * file can be reused across a user's entire site visit, leading to faster @@ -1740,13 +1738,13 @@ function drupal_get_js($scope = 'header', $javascript = NULL, $skip_alter = FALS * * Example: * @code - * function module1_page_build(&$page) { + * function module1_page_attachments(&$page) { * $page['#attached']['js'][] = array( * 'type' => 'setting', * 'data' => array('foo' => array('a', 'b', 'c')), * ); * } - * function module2_page_build(&$page) { + * function module2_page_attachments(&$page) { * $page['#attached']['js'][] = array( * 'type' => 'setting', * 'data' => array('foo' => array('d')), @@ -2452,7 +2450,10 @@ function drupal_pre_render_links($element) { * @return array * The processed render array for the page. * - * @see hook_page_alter() + * @see hook_page_attachments() + * @see hook_page_attachments_alter() + * @see hook_page_top() + * @see hook_page_bottom() * @see element_info() */ function drupal_prepare_page($page) { @@ -2508,6 +2509,15 @@ function drupal_prepare_page($page) { // @todo Clean this up as part of https://www.drupal.org/node/2352155. if (\Drupal::moduleHandler()->moduleExists('block')) { _block_page_build($page); + // Find all non-empty page regions, and add a theme wrapper function that + // allows them to be consistently themed. + $regions = system_region_list(\Drupal::theme()->getActiveTheme()->getName()); + foreach (array_keys($regions) as $region) { + if (!empty($page[$region])) { + $page[$region]['#theme_wrappers'][] = 'region'; + $page[$region]['#region'] = $region; + } + } } // If no module has taken care of the main content, add it to the page now. diff --git a/core/lib/Drupal/Core/Page/DefaultHtmlFragmentRenderer.php b/core/lib/Drupal/Core/Page/DefaultHtmlFragmentRenderer.php index d4f4970..7139ec4 100644 --- a/core/lib/Drupal/Core/Page/DefaultHtmlFragmentRenderer.php +++ b/core/lib/Drupal/Core/Page/DefaultHtmlFragmentRenderer.php @@ -72,8 +72,12 @@ public function render(HtmlFragmentInterface $fragment, $status_code = 200) { // Persist cache tags associated with this page. Also associate the // "rendered" cache tag. This allows us to invalidate the entire render // cache, regardless of the cache bin. - $cache_tags = $page_array['#cache']['tags']; - $cache_tags[] = 'rendered'; + $cache_tags = Cache::mergeTags( + isset($page_array['page_top']) ? $page_array['page_top']['#cache']['tags'] : [], + $page_array['#cache']['tags'], + isset($page_array['page_bottom']) ? $page_array['page_bottom']['#cache']['tags'] : [], + ['rendered'] + ); // Only keep unique cache tags. We need to prevent duplicates here already // rather than only in the cache layer, because they are also used by // reverse proxies (like Varnish), not only by Drupal's page cache. diff --git a/core/modules/block/block.install b/core/modules/block/block.install deleted file mode 100644 index a53a120..0000000 --- a/core/modules/block/block.install +++ /dev/null @@ -1,19 +0,0 @@ -drupalGet('system-test/main-content-handling'); $this->assertRaw('id="system-test-content"', 'Content handled by another module'); - $this->assertText(t('Content to test main content fallback'), 'Main content still displayed.'); + $this->assertNoText(t('Content to test main content fallback'), 'Main content not displayed.'); // Fallback should trigger when another module // indicates that it is not handling the content. $this->drupalGet('system-test/main-content-fallback'); $this->assertText(t('Content to test main content fallback'), 'Main content fallback properly triggers.'); - // Fallback should not trigger when another module is handling content. - // Note that this test ensures that no duplicate - // content gets created by the fallback. - $this->drupalGet('system-test/main-content-duplication'); - $this->assertNoText(t('Content to test main content fallback'), 'Main content not duplicated.'); - // Request a user* page and see if it is displayed. $this->drupalLogin($this->web_user); $this->drupalGet('user/' . $this->web_user->id() . '/edit'); diff --git a/core/modules/system/system.module b/core/modules/system/system.module index ee21a5e..41ad2c0 100644 --- a/core/modules/system/system.module +++ b/core/modules/system/system.module @@ -1268,21 +1268,6 @@ function system_entity_type_build(array &$entity_types) { } /** - * Implements hook_page_alter(). - */ -function system_page_alter(&$page) { - // Find all non-empty page regions, and add a theme wrapper function that - // allows them to be consistently themed. - $regions = system_region_list(\Drupal::theme()->getActiveTheme()->getName()); - foreach (array_keys($regions) as $region) { - if (!empty($page[$region])) { - $page[$region]['#theme_wrappers'][] = 'region'; - $page[$region]['#region'] = $region; - } - } -} - -/** * Implements hook_block_view_BASE_BLOCK_ID_alter(). */ function system_block_view_system_main_block_alter(array &$build, BlockPluginInterface $block) { diff --git a/core/modules/system/tests/modules/system_test/system_test.module b/core/modules/system/tests/modules/system_test/system_test.module index 40da5d2..78c1504 100644 --- a/core/modules/system/tests/modules/system_test/system_test.module +++ b/core/modules/system/tests/modules/system_test/system_test.module @@ -96,22 +96,21 @@ function system_test_lock_exit() { } /** - * Implements hook_page_build(). + * Implements hook_page_attachments(). */ -function system_test_page_build(&$page) { +function system_test_page_attachments(array &$page) { $menu_item['path'] = current_path(); $main_content_display = &drupal_static('system_main_content_added', FALSE); - if ($menu_item['path'] == 'system-test/main-content-handling') { - $page['footer'] = drupal_set_page_content(); - $page['footer']['main']['#markup'] = '
' . $page['footer']['main']['#markup'] . '
'; - } - elseif ($menu_item['path'] == 'system-test/main-content-fallback') { + if ($menu_item['path'] == 'system-test/main-content-fallback') { + // Get the main content, to e.g. dynamically attach an asset. drupal_set_page_content(); + // Indicate we don't want to override the main content. $main_content_display = FALSE; } - elseif ($menu_item['path'] == 'system-test/main-content-duplication') { - drupal_set_page_content(); + elseif ($menu_item['path'] == 'system-test/main-content-handling') { + // Set the main content. + drupal_set_page_content('
Overridden!
'); } // Used by FrontPageTestCase to get the results of drupal_is_front_page(). $frontpage = \Drupal::state()->get('system_test.front_page_output') ?: 0; diff --git a/core/modules/system/tests/modules/system_test/system_test.routing.yml b/core/modules/system/tests/modules/system_test/system_test.routing.yml index 32d235b..9d257fb 100644 --- a/core/modules/system/tests/modules/system_test/system_test.routing.yml +++ b/core/modules/system/tests/modules/system_test/system_test.routing.yml @@ -29,14 +29,6 @@ system_test.main_content_fallback: requirements: _access: 'TRUE' -system_test.main_content_duplication: - path: '/system-test/main-content-duplication' - defaults: - _title: 'Test main content duplication' - _content: '\Drupal\system_test\Controller\SystemTestController::mainContentFallback' - requirements: - _access: 'TRUE' - system_test.lock_acquire: path: '/system-test/lock-acquire' defaults: 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 8616f02..271b01d 100644 --- a/core/modules/system/tests/modules/theme_test/theme_test.module +++ b/core/modules/system/tests/modules/theme_test/theme_test.module @@ -74,10 +74,10 @@ function theme_test_preprocess_html(&$variables) { } /** - * Implements hook_page_alter(). + * Implements hook_page_bottom(). */ -function theme_test_page_alter(&$page) { - $page['page_bottom']['theme_test_page_bottom'] = array('#markup' => 'theme test page bottom markup'); +function theme_test_page_bottom(array &$page_bottom) { + $page_bottom['theme_test_page_bottom'] = array('#markup' => 'theme test page bottom markup'); } /** diff --git a/core/modules/system/theme.api.php b/core/modules/system/theme.api.php index 26c998a..d0d9749 100644 --- a/core/modules/system/theme.api.php +++ b/core/modules/system/theme.api.php @@ -161,14 +161,24 @@ * * @section Assets * - * We can distinguish between two types of assets: - * 1. global assets (loaded on all pages where the theme is in use): these are - * defined in the theme's *.info.yml file. - * 2. template-specific assets (loaded on all pages where a specific template is + * We can distinguish between threetypes of assets: + * 1. unconditional page-level assets (loaded on all pages where the theme is in + * use): these are defined in the theme's *.info.yml file. + * 2. conditional page-level assets (loaded on all pages where the theme is in + * use and a certain condition is met): these are attached in + * hook_page_attachments_alter(), e.g.: + * @code + * function THEME_page_attachments_alter(array &$page) { + * if ($some_condition) { + * $page['#attached']['library'][] = 'mytheme/something'; + * } + * } + * @endcode + * 3. template-specific assets (loaded on all pages where a specific template is * in use): these can be added by in preprocessing functions, using @code * $variables['#attached'] @endcode, e.g.: * @code - * function seven_preprocess_menu_local_action(array &$variables) { + * function THEME_preprocess_menu_local_action(array &$variables) { * // We require Modernizr's touch test for button styling. * $variables['#attached']['library'][] = 'core/modernizr'; * } diff --git a/core/modules/views/views.module b/core/modules/views/views.module index a41e73e..5d0c6d6 100644 --- a/core/modules/views/views.module +++ b/core/modules/views/views.module @@ -285,15 +285,26 @@ function views_theme_suggestions_comment_alter(array &$suggestions, array $varia } /** - * Implements hook_page_alter(). + * Implements hook_element_info_alter(). + * + * @see views_page_display_pre_render() + * @see views_preprocess_page() + */ +function views_element_info_alter(&$types) { + $types['page']['#pre_render'][] = 'views_page_display_pre_render'; +} + +/** + * #pre_render callback to set contextual links for views using a Page display. */ -function views_page_alter(&$page) { +function views_page_display_pre_render(array $element) { // If the main content of this page contains a view, attach its contextual // links to the overall page array. This allows them to be rendered directly // next to the page title. if ($view = views_get_page_view()) { - views_add_contextual_links($page, 'page', $view, $view->current_display); + views_add_contextual_links($element, 'page', $view, $view->current_display); } + return $element; } /** @@ -306,7 +317,8 @@ function views_preprocess_page(&$variables) { } // If the page contains a view as its main content, contextual links may have - // been attached to the page as a whole; for example, by views_page_alter(). + // been attached to the page as a whole; for example, by + // views_page_display_pre_render(). // This allows them to be associated with the page and rendered by default // next to the page title (which we want). However, it also causes the // Contextual Links module to treat the wrapper for the entire page (i.e.,