From 2e8ca690ff471b1d6604226e8153f401b1827204 Mon Sep 17 00:00:00 2001 From: Dries Buytaert <dries@buytaert.net> Date: Tue, 27 Jan 2009 00:22:27 +0000 Subject: [PATCH] - Patch #351235 by dmitrig01, webchick, frando, moshe weitzman, et al: hook_page_alter. Oh, behave. --- includes/common.inc | 78 ++++++++++++++++++++---- includes/theme.inc | 92 +++++++++++++---------------- index.php | 6 +- modules/block/block.admin.inc | 2 - modules/block/block.module | 55 +++++++++++++++++ modules/blog/blog.pages.inc | 58 +++++++++--------- modules/comment/comment.module | 2 +- modules/comment/comment.pages.inc | 2 +- modules/node/node.api.php | 4 +- modules/node/node.module | 81 +++++++++++++++++-------- modules/node/node.pages.inc | 6 +- modules/system/system.admin.inc | 5 +- modules/system/system.api.php | 57 ++++++++++++++++++ modules/system/system.module | 13 ++++ modules/taxonomy/taxonomy.module | 41 +++---------- modules/taxonomy/taxonomy.pages.inc | 77 +++++++++++------------- modules/upload/upload.test | 2 +- 17 files changed, 380 insertions(+), 201 deletions(-) diff --git a/includes/common.inc b/includes/common.inc index e33a5c187c03..dd7e58c3582a 100644 --- a/includes/common.inc +++ b/includes/common.inc @@ -301,8 +301,7 @@ function drupal_get_destination() { * Drupal will ensure that messages set by drupal_set_message() and other * session data are written to the database before the user is redirected. * - * This function ends the request; use it rather than a print theme('page') - * statement in your menu callback. + * This function ends the request; use it instead of a return in your menu callback. * * @param $path * A Drupal path or a full URL. @@ -379,19 +378,23 @@ function drupal_not_found() { $path = drupal_get_normal_path(variable_get('site_404', '')); if ($path && $path != $_GET['q']) { - // Set the active item in case there are tabs to display, or other - // dependencies on the path. + // Custom 404 handler. Set the active item in case there are tabs to + // display, or other dependencies on the path. menu_set_active_item($path); $return = menu_execute_active_handler($path); } if (empty($return) || $return == MENU_NOT_FOUND || $return == MENU_ACCESS_DENIED) { + // Standard 404 handler. drupal_set_title(t('Page not found')); $return = t('The requested page could not be found.'); } + $page = drupal_get_page($return); // To conserve CPU and bandwidth, omit the blocks. - print theme('page', $return, FALSE); + $page['#show_blocks'] = FALSE; + + print drupal_render_page($page); } /** @@ -408,17 +411,19 @@ function drupal_access_denied() { $path = drupal_get_normal_path(variable_get('site_403', '')); if ($path && $path != $_GET['q']) { - // Set the active item in case there are tabs to display or other - // dependencies on the path. + // Custom 403 handler. Set the active item in case there are tabs to + // display or other dependencies on the path. menu_set_active_item($path); $return = menu_execute_active_handler($path); } if (empty($return) || $return == MENU_NOT_FOUND || $return == MENU_ACCESS_DENIED) { + // Standard 403 handler. drupal_set_title(t('Access denied')); $return = t('You are not authorized to access this page.'); } - print theme('page', $return); + + print drupal_render_page($return); } /** @@ -816,7 +821,10 @@ function _drupal_log_error($error, $fatal = FALSE) { drupal_set_header($_SERVER['SERVER_PROTOCOL'] . ' Service unavailable'); drupal_set_title(t('Error')); if (!defined('MAINTENANCE_MODE') && drupal_get_bootstrap_phase() == DRUPAL_BOOTSTRAP_FULL) { - print theme('page', t('The website encountered an unexpected error. Please try again later.'), FALSE); + // To conserve CPU and bandwidth, omit the blocks. + $page = drupal_get_page(t('The website encountered an unexpected error. Please try again later.')); + $page['#show_blocks'] = FALSE; + print drupal_render_page($page); } else { print theme('maintenance_page', t('The website encountered an unexpected error. Please try again later.'), FALSE); @@ -3194,6 +3202,53 @@ function drupal_alter($type, &$data) { } } +/** + * Retrieve a $page element that is ready for decorating. + * + * Used by menu callbacks in order to populate the page with content + * and behavior (e.g. #show_blocks). + * + * @param $content + * A string or renderable array representing the body of the page. + * @return + * A $page element that should be decorated and then passed to drupal_render_page(). + * + * @see drupal_render_page(). + */ +function drupal_get_page($content = NULL) { + // Initialize page array with defaults. @see hook_elements() - 'page' element. + $page = _element_info('page'); + $page['content'] = is_array($content) ? $content : array('main' => array('#markup' => $content)); + + return $page; +} + +/** + * Renders the page, including all theming. + * + * @param $page + * A string or array representing the content of a page. The array consists of + * the following keys: + * - #type: Value is always 'page'. This pushes the theming through page.tpl.php (required). + * - content: A renderable array as built by the menu callback (required). + * - #show_blocks: A marker which suppresses left/right regions if FALSE (optional). + * - #show_messages: Suppress drupal_get_message() items. Used by Batch API (optional). + * + * @see hook_page_alter() + * @see drupal_get_page() + */ +function drupal_render_page($page) { + // Allow menu callbacks to return strings. + if (is_string($page)) { + $page = drupal_get_page($page); + } + // Modules alter the $page as needed. Blocks are populated into regions like + // 'left', 'footer', etc. + drupal_alter('page', $page); + + return drupal_render($page); +} + /** * Renders HTML given a structured array tree. * @@ -3366,7 +3421,7 @@ function drupal_common_theme() { 'arguments' => array('text' => NULL) ), 'page' => array( - 'arguments' => array('content' => NULL, 'show_blocks' => TRUE, 'show_messages' => TRUE), + 'arguments' => array('page' => NULL), 'template' => 'page', ), 'maintenance_page' => array( @@ -3425,6 +3480,9 @@ function drupal_common_theme() { 'item_list' => array( 'arguments' => array('items' => array(), 'title' => NULL, 'type' => 'ul', 'attributes' => NULL), ), + 'list' => array( + 'arguments' => array('elements' => NULL), + ), 'more_help_link' => array( 'arguments' => array('url' => NULL), ), diff --git a/includes/theme.inc b/includes/theme.inc index b71da908bffe..b77e35854245 100644 --- a/includes/theme.inc +++ b/includes/theme.inc @@ -1568,6 +1568,25 @@ function theme_item_list($items = array(), $title = NULL, $type = 'ul', $attribu return $output; } +/** + * Return a themed list of items from a drupal_render() style array. + * + * @param $elements + * An array consisting of the following keys: + * - #items: an array of items as expected by theme('item_list'). + * - #title: a title which prints above the list. + * - #list_type: the type of list to return. Defaults to "ul". + * - #attributes: an array of attributes as expected by theme('item_list'). + * @return + * A string containing the list output. + */ +function theme_list($elements) { + // Populate any missing array elements with their defaults. + $elements += _element_info('list'); + + return theme('item_list', $elements['#items'], $elements['#title'], $elements['#list_type'], $elements['#attributes']); +} + /** * Returns code that emits the 'more help'-link. */ @@ -1630,30 +1649,6 @@ function theme_closure($main = 0) { return implode("\n", $footer) . drupal_get_js('footer'); } -/** - * Return a set of blocks available for the current user. - * - * @param $region - * Which set of blocks to retrieve. - * @return - * A string containing the themed blocks for this region. - */ -function theme_blocks($region) { - $output = ''; - - if ($list = block_list($region)) { - foreach ($list as $key => $block) { - // $key == <i>module</i>_<i>delta</i> - $output .= theme('block', $block); - } - } - - // Add any content assigned to this region through drupal_set_content() calls. - $output .= drupal_get_content($region); - - return $output; -} - /** * Format a username. * @@ -1816,32 +1811,26 @@ function template_preprocess(&$variables, $hook) { * Any changes to variables in this preprocessor should also be changed inside * template_preprocess_maintenance_page() to keep all of them consistent. * - * The $variables array contains the following arguments: - * - $content - * - $show_blocks + * The $variables array contains two keys: + * - 'page': the fully decorated page. + * - 'content': the content of the page, already rendered. * + * @see drupal_render_page * @see page.tpl.php */ function template_preprocess_page(&$variables) { - // Add favicon - if (theme_get_setting('toggle_favicon')) { - drupal_set_html_head('<link rel="shortcut icon" href="' . check_url(theme_get_setting('favicon')) . '" type="image/x-icon" />'); + // Move some variables to the top level for themer convenience and template cleanliness. + $variables['show_blocks'] = $variables['page']['#show_blocks']; + $variables['show_messages'] = $variables['page']['#show_messages']; + + // Render each region into top level variables. + foreach (system_region_list($GLOBALS['theme']) as $region_key => $region_name) { + $variables[$region_key] = empty($variables['page'][$region_key]) ? '' : drupal_render($variables['page'][$region_key]); } - global $theme; - // Populate all block regions. - $regions = system_region_list($theme); - // Load all region content assigned via blocks. - foreach (array_keys($regions) as $region) { - // Prevent left and right regions from rendering blocks when 'show_blocks' == FALSE. - if ($variables['show_blocks'] || ($region != 'left' && $region != 'right')) { - $blocks = theme('blocks', $region); - } - else { - $blocks = ''; - } - // Assign region to a region variable. - isset($variables[$region]) ? $variables[$region] .= $blocks : $variables[$region] = $blocks; + // Add favicon. + if (theme_get_setting('toggle_favicon')) { + drupal_set_html_head('<link rel="shortcut icon" href="' . check_url(theme_get_setting('favicon')) . '" type="image/x-icon" />'); } // Set up layout variable. @@ -1881,8 +1870,8 @@ function template_preprocess_page(&$variables) { $variables['logo'] = theme_get_setting('logo'); $variables['messages'] = $variables['show_messages'] ? theme('status_messages') : ''; $variables['mission'] = isset($mission) ? $mission : ''; - $variables['main_menu'] = theme_get_setting('toggle_main_menu') ? menu_main_menu() : array(); - $variables['secondary_menu'] = theme_get_setting('toggle_secondary_menu') ? menu_secondary_menu() : array(); + $variables['main_menu'] = theme_get_setting('toggle_main_menu') ? menu_main_menu() : array(); + $variables['secondary_menu'] = theme_get_setting('toggle_secondary_menu') ? menu_secondary_menu() : array(); $variables['search_box'] = (theme_get_setting('toggle_search') ? drupal_get_form('search_theme_form') : ''); $variables['site_name'] = (theme_get_setting('toggle_name') ? variable_get('site_name', 'Drupal') : ''); $variables['site_slogan'] = (theme_get_setting('toggle_slogan') ? variable_get('site_slogan', '') : ''); @@ -1975,6 +1964,8 @@ function template_preprocess_page(&$variables) { * @see node.tpl.php */ function template_preprocess_node(&$variables) { + $variables['teaser'] = $variables['elements']['#teaser']; + $variables['node'] = $variables['elements']['#node']; $node = $variables['node']; $variables['date'] = format_date($node->created); @@ -1982,17 +1973,17 @@ function template_preprocess_node(&$variables) { $variables['node_url'] = url('node/' . $node->nid); $variables['title'] = check_plain($node->title); $variables['page'] = (bool)menu_get_object(); - + if ($node->build_mode == NODE_BUILD_PREVIEW) { unset($node->content['links']); } - + // Render taxonomy links separately. $variables['terms'] = !empty($node->content['links']['terms']) ? drupal_render($node->content['links']['terms']) : ''; - + // Render all remaining node links. $variables['links'] = !empty($node->content['links']) ? drupal_render($node->content['links']) : ''; - + // Render any comments. $variables['comments'] = !empty($node->content['comments']) ? drupal_render($node->content['comments']) : ''; @@ -2035,6 +2026,7 @@ function template_preprocess_node(&$variables) { */ function template_preprocess_block(&$variables) { static $block_counter = array(); + $variables['block'] = $variables['block']['#block']; // All blocks get an independent counter for each region. if (!isset($block_counter[$variables['block']->region])) { $block_counter[$variables['block']->region] = 1; diff --git a/index.php b/index.php index dba9f1123c9b..d776d767a757 100644 --- a/index.php +++ b/index.php @@ -21,7 +21,7 @@ drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL); $return = menu_execute_active_handler(); -// Menu status constants are integers; page content is a string. +// Menu status constants are integers; page content is a string or array. if (is_int($return)) { switch ($return) { case MENU_NOT_FOUND: @@ -36,8 +36,8 @@ } } elseif (isset($return)) { - // Print any value (including an empty string) except NULL or undefined: - print theme('page', $return); + // Print anything besides a menu constant, assuming it's not NULL or undefined. + print drupal_render_page($return); } drupal_page_footer(); diff --git a/modules/block/block.admin.inc b/modules/block/block.admin.inc index ead47fba0c88..00afc9bd82a0 100644 --- a/modules/block/block.admin.inc +++ b/modules/block/block.admin.inc @@ -368,8 +368,6 @@ function template_preprocess_block_admin_display_form(&$variables) { $variables['block_regions'] = $block_regions + array(BLOCK_REGION_NONE => t('Disabled')); foreach ($block_regions as $key => $value) { - // Highlight regions on page to provide visual reference. - drupal_set_content($key, '<div class="block-region">' . $value . '</div>'); // Initialize an empty array for the region. $variables['block_listing'][$key] = array(); } diff --git a/modules/block/block.module b/modules/block/block.module index 318617e8bfe4..c08bac949874 100644 --- a/modules/block/block.module +++ b/modules/block/block.module @@ -225,6 +225,61 @@ function block_block_view($delta = 0, $edit = array()) { return $data; } +/** + * Implementation of hook_page_alter(). + * + * Render blocks into their regions. + */ +function block_page_alter($page) { + global $theme; + + // The theme system might not yet be initialized. We need $theme. + init_theme(); + + // Populate all block regions + $regions = system_region_list($theme); + + // Load all region content assigned via blocks. + foreach (array_keys($regions) as $region) { + // Prevent left and right regions from rendering blocks when 'show_blocks' == FALSE. + if ($page['#show_blocks'] || ($region != 'left' && $region != 'right')) { + // Assign blocks to region. + if ($blocks = block_get_blocks_by_region($region)) { + $page[$region]['blocks'] = $blocks; + } + + // Append region description if we are rendering the block admin page. + $item = menu_get_item(); + if ($item['path'] == 'admin/build/block') { + $description = '<div class="block-region">' . $regions[$region] . '</div>'; + $page[$region]['blocks']['block_description'] = array('#markup' => $description); + } + } + } +} + +/** + * Get a renderable array of a region containing all enabled blocks. + * + * @param $region + * The requested region. + */ +function block_get_blocks_by_region($region) { + $weight = 0; + $build = array(); + if ($list = block_list($region)) { + foreach ($list as $key => $block) { + $build[$key] = array( + '#theme' => 'block', + '#block' => $block, + '#weight' => ++$weight, + ); + } + $build['#sorted'] = TRUE; + } + return $build; +} + /** * Update the 'block' DB table with the blocks currently exported by modules. * diff --git a/modules/blog/blog.pages.inc b/modules/blog/blog.pages.inc index d44bbf5fa42b..889bf47a166c 100644 --- a/modules/blog/blog.pages.inc +++ b/modules/blog/blog.pages.inc @@ -23,18 +23,20 @@ function blog_page_user($account) { $items[] = t('You are not allowed to post a new blog entry.'); } - $output = theme('item_list', $items); - - $result = pager_query(db_rewrite_sql("SELECT n.nid, n.sticky, n.created FROM {node} n WHERE n.type = 'blog' AND n.uid = %d AND n.status = 1 ORDER BY n.sticky DESC, n.created DESC"), variable_get('default_nodes_main', 10), 0, NULL, $account->uid); - $has_posts = FALSE; - - while ($node = db_fetch_object($result)) { - $output .= node_view(node_load($node->nid), 1); - $has_posts = TRUE; - } - - if ($has_posts) { - $output .= theme('pager', NULL, variable_get('default_nodes_main', 10)); + $build['blog_actions'] = array( + '#items' => $items, + '#theme' => 'list', + '#weight' => -1, + ); + + $nids = pager_query(db_rewrite_sql("SELECT n.nid, n.sticky, n.created FROM {node} n WHERE n.type = 'blog' AND n.uid = %d AND n.status = 1 ORDER BY n.sticky DESC, n.created DESC"), variable_get('default_nodes_main', 10), 0, NULL, $account->uid)->fetchCol(); + if (!empty($nids)) { + $nodes = node_load_multiple($nids); + $build += node_build_multiple($nodes); + $build['pager'] = array( + '#markup' => theme('pager', NULL, variable_get('default_nodes_main', 10)), + '#weight' => 5, + ); } else { if ($account->uid == $user->uid) { @@ -46,7 +48,7 @@ function blog_page_user($account) { } drupal_add_feed(url('blog/' . $account->uid . '/feed'), t('RSS - !title', array('!title' => $title))); - return $output; + return drupal_get_page($build); } /** @@ -54,33 +56,33 @@ function blog_page_user($account) { */ function blog_page_last() { global $user; - - $output = ''; - $items = array(); + $build = array(); if (user_access('edit own blog')) { $items[] = l(t('Create new blog entry.'), "node/add/blog"); + $build['blog_actions'] = array( + '#items' => $items, + '#theme' => 'list', + '#weight' => -1, + ); } - $output = theme('item_list', $items); - - $result = pager_query(db_rewrite_sql("SELECT n.nid, n.created FROM {node} n WHERE n.type = 'blog' AND n.status = 1 ORDER BY n.sticky DESC, n.created DESC"), variable_get('default_nodes_main', 10)); - $has_posts = FALSE; - - while ($node = db_fetch_object($result)) { - $output .= node_view(node_load($node->nid), 1); - $has_posts = TRUE; - } + $nids = pager_query(db_rewrite_sql("SELECT n.nid, n.sticky, n.created FROM {node} n WHERE n.type = 'blog' AND n.status = 1 ORDER BY n.sticky DESC, n.created DESC"), variable_get('default_nodes_main', 10))->fetchCol(); - if ($has_posts) { - $output .= theme('pager', NULL, variable_get('default_nodes_main', 10)); + if (!empty($nids)) { + $nodes = node_load_multiple($nids); + $build += node_build_multiple($nodes); + $build['pager'] = array( + '#markup' => theme('pager', NULL, variable_get('default_nodes_main', 10)), + '#weight' => 5, + ); } else { drupal_set_message(t('No blog entries have been created.')); } drupal_add_feed(url('blog/feed'), t('RSS - blogs')); - return $output; + return drupal_get_page($build); } /** diff --git a/modules/comment/comment.module b/modules/comment/comment.module index b1e48ed3ac9d..a92b96a455a1 100644 --- a/modules/comment/comment.module +++ b/modules/comment/comment.module @@ -1649,7 +1649,7 @@ function comment_form_add_preview($form, &$form_state) { } else { $suffix = empty($form['#suffix']) ? '' : $form['#suffix']; - $form['#suffix'] = $suffix . node_view($node); + $form['#suffix'] = $suffix . drupal_render(node_build($node)); $edit['pid'] = 0; } diff --git a/modules/comment/comment.pages.inc b/modules/comment/comment.pages.inc index a4c94e22ae1a..12d30cec8fcf 100644 --- a/modules/comment/comment.pages.inc +++ b/modules/comment/comment.pages.inc @@ -92,7 +92,7 @@ function comment_reply($node, $pid = NULL) { } // This is the case where the comment is in response to a node. Display the node. elseif (user_access('access content')) { - $output .= node_view($node); + $output .= drupal_render(node_build($node)); } // Should we show the reply box? diff --git a/modules/node/node.api.php b/modules/node/node.api.php index 68050065d123..8b8343d2ed0f 100644 --- a/modules/node/node.api.php +++ b/modules/node/node.api.php @@ -81,7 +81,7 @@ function hook_node_access_records($node) { return; } - // We only care about the node if it's been marked private. If not, it is + // We only care about the node if it has been marked private. If not, it is // treated just like any other node and we completely ignore it. if ($node->private) { $grants = array(); @@ -169,7 +169,7 @@ function hook_node_operations() { * @return * None. */ -function hook_nodeapi_alter($node, $teaser, $page) { +function hook_nodeapi_alter($node, $teaser) { } /** diff --git a/modules/node/node.module b/modules/node/node.module index 3936359ade75..3629e2209c63 100644 --- a/modules/node/node.module +++ b/modules/node/node.module @@ -96,7 +96,7 @@ function node_help($path, $arg) { function node_theme() { return array( 'node' => array( - 'arguments' => array('node' => NULL, 'teaser' => FALSE, 'page' => FALSE), + 'arguments' => array('elements' => NULL), 'template' => 'node', ), 'node_list' => array( @@ -1126,24 +1126,28 @@ function node_delete($nid) { } /** - * Generate a display of the given node. + * Generate an array for rendering the given node. * * @param $node * A node array or node object. * @param $teaser * Whether to display the teaser only or the full form. - * @param $links - * Whether or not to display node links. Links are omitted for node previews. * * @return - * An HTML representation of the themed node. + * An array as expected by drupal_render(). */ -function node_view($node, $teaser = FALSE) { +function node_build($node, $teaser = FALSE) { $node = (object)$node; $node = node_build_content($node, $teaser); - return theme('node', $node, $teaser); + $build = $node->content; + $build += array( + '#theme' => 'node', + '#node' => $node, + '#teaser' => $teaser, + ); + return $build; } /** @@ -1205,25 +1209,31 @@ function node_build_content($node, $teaser = FALSE) { node_invoke_nodeapi($node, 'view', $teaser); // Allow modules to modify the structured node. - drupal_alter('node_view', $node, $teaser); + drupal_alter('node_build', $node, $teaser); return $node; } /** - * Generate a page displaying a single node. + * Generate an array which displays a node detail page. + * + * @param $node + * A node object. + * @param $message + * A flag which sets a page title relevant to the revision being viewed. + * @return + * A $page element suitable for use by drupal_page_render(). */ function node_show($node, $message = FALSE) { if ($message) { drupal_set_title(t('Revision of %title from %date', array('%title' => $node->title, '%date' => format_date($node->revision_timestamp))), PASS_THROUGH); } - $output = node_view($node, FALSE, TRUE); - // Update the history table, stating that this user viewed this node. node_tag_new($node->nid); - return $output; + // For markup consistency with other pages, use node_build_multiple() rather than node_build(). + return drupal_get_page(node_build_multiple(array($node), FALSE)); } /** @@ -1904,21 +1914,44 @@ function node_feed($nids = FALSE, $channel = array()) { print $output; } +/** + * Construct a drupal_render() style array from an array of loaded nodes. + * + * @param $nodes + * An array of nodes as returned by node_load_multiple(). + * @param $teaser + * Display nodes into teaser view or full view. + * @param $weight + * An integer representing the weight of the first node in the list. + * @return + * An array in the format expected by drupal_render(). + */ +function node_build_multiple($nodes, $teaser = TRUE, $weight = 0) { + $build = array(); + foreach ($nodes as $node) { + $build['nodes'][$node->nid] = node_build($node, $teaser); + $build['nodes'][$node->nid]['#weight'] = $weight; + $weight++; + } + $build['nodes']['#sorted'] = TRUE; + return $build; +} + /** * Menu callback; Generate a listing of promoted nodes. */ function node_page_default() { - $nids = pager_query(db_rewrite_sql('SELECT n.nid FROM {node} n WHERE n.promote = 1 AND n.status = 1 ORDER BY n.sticky DESC, n.created DESC'), variable_get('default_nodes_main', 10))->fetchCol(); + $nids = pager_query(db_rewrite_sql('SELECT n.nid, n.sticky, n.created FROM {node} n WHERE n.promote = 1 AND n.status = 1 ORDER BY n.sticky DESC, n.created DESC'), variable_get('default_nodes_main', 10))->fetchCol(); if (!empty($nids)) { $nodes = node_load_multiple($nids); - $output = ''; - foreach ($nodes as $node) { - $output .= node_view($node, TRUE); - } + $build = node_build_multiple($nodes); $feed_url = url('rss.xml', array('absolute' => TRUE)); drupal_add_feed($feed_url, variable_get('site_name', 'Drupal') . ' ' . t('RSS')); - $output .= theme('pager', NULL, variable_get('default_nodes_main', 10)); + $build['pager'] = array( + '#markup' => theme('pager', NULL, variable_get('default_nodes_main', 10)), + '#weight' => 5, + ); } else { $default_message = '<h1 class="title">' . t('Welcome to your new Drupal website!') . '</h1>'; @@ -1930,12 +1963,14 @@ function node_page_default() { $default_message .= '<li>' . t('<strong>Start posting content</strong> Finally, you can <a href="@content">create content</a> for your website. This message will disappear once you have promoted a post to the front page.', array('@content' => url('node/add'))) . '</li>'; $default_message .= '</ol>'; $default_message .= '<p>' . t('For more information, please refer to the <a href="@help">help section</a>, or the <a href="@handbook">online Drupal handbooks</a>. You may also post at the <a href="@forum">Drupal forum</a>, or view the wide range of <a href="@support">other support options</a> available.', array('@help' => url('admin/help'), '@handbook' => 'http://drupal.org/handbooks', '@forum' => 'http://drupal.org/forum', '@support' => 'http://drupal.org/support')) . '</p>'; - - $output = '<div id="first-time">' . $default_message . '</div>'; + $build['default_message'] = array( + '#markup' => $default_message, + '#prefix' => '<div id="first-time">', + '#suffix' => '</div>', + ); } drupal_set_title(''); - - return $output; + return drupal_get_page($build); } /** @@ -2969,7 +3004,7 @@ function node_unpublish_by_keyword_action_submit($form, $form_state) { */ function node_unpublish_by_keyword_action($node, $context) { foreach ($context['keywords'] as $keyword) { - if (strstr(node_view(clone $node), $keyword) || strstr($node->title, $keyword)) { + if (strstr(drupal_render(node_build(clone $node)), $keyword) || strstr($node->title, $keyword)) { $node->status = 0; watchdog('action', 'Set @type %title to unpublished.', array('@type' => node_get_types('name', $node), '%title' => $node->title)); break; diff --git a/modules/node/node.pages.inc b/modules/node/node.pages.inc index 28aff234a930..a6e5d34bfc28 100644 --- a/modules/node/node.pages.inc +++ b/modules/node/node.pages.inc @@ -415,12 +415,12 @@ function theme_node_preview($node) { if ($preview_trimmed_version) { drupal_set_message(t('The trimmed version of your post shows what your post looks like when promoted to the main page or when exported for syndication.<span class="no-js"> You can insert the delimiter "<!--break-->" (without the quotes) to fine-tune where your post gets split.</span>')); $output .= '<h3>' . t('Preview trimmed version') . '</h3>'; - $output .= node_view(clone $node, 1, FALSE); + $output .= drupal_render(node_build(clone $node, TRUE)); $output .= '<h3>' . t('Preview full version') . '</h3>'; - $output .= node_view($node, 0, FALSE); + $output .= drupal_render(node_build($node, FALSE)); } else { - $output .= node_view($node, 0, FALSE); + $output .= drupal_render(node_build($node, FALSE)); } $output .= "</div>\n"; diff --git a/modules/system/system.admin.inc b/modules/system/system.admin.inc index 703de2d0127c..be461d727749 100644 --- a/modules/system/system.admin.inc +++ b/modules/system/system.admin.inc @@ -1888,7 +1888,10 @@ function system_batch_page() { elseif (isset($output)) { // Force a page without blocks or messages to // display a list of collected messages later. - print theme('page', $output, FALSE, FALSE); + $page = drupal_get_page($output); + $page['#show_blocks'] = FALSE; + $page['#show_messages'] = FALSE; + return $page; } } diff --git a/modules/system/system.api.php b/modules/system/system.api.php index 3c74f48db4b8..cd98c508150f 100644 --- a/modules/system/system.api.php +++ b/modules/system/system.api.php @@ -179,6 +179,63 @@ function hook_js_alter(&$javascript) { $javascript['misc/jquery.js']['data'] = drupal_get_path('module', 'jquery_update') . '/jquery.js'; } +/** + * Perform alterations before a page is rendered. + * + * Use this hook when you want to add, remove, or alter elements at the page + * level. If you are making changes to entities such as forms, menus, or user + * profiles, use those objects' native alter hooks instead (hook_form_alter(), + * for example). + * + * The $page array contains top level elements for each block region: + * @code + * $page['header'] + * $page['left'] + * $page['content'] + * $page['right'] + * $page['footer'] + * @endcode + * + * The 'content' element contains the main content of the current page, and its + * structure will vary depending on what module is responsible for building the + * page. Some legacy modules may not return structured content at all: their + * pre-rendered markup will be located in $page['content']['main']['#markup']. + * + * Pages built by Drupal's core Node and Blog modules use a standard structure: + * + * @code + * // Node body. + * $page['content']['nodes'][$nid]['body'] + * // Array of links attached to the node (add comments, read more). + * $page['content']['nodes'][$nid]['links'] + * // The node object itself. + * $page['content']['nodes'][$nid]['#node'] + * // The results pager. + * $page['content']['pager'] + * @code + * + * Blocks may be referenced by their module/delta pair within a region: + * @code + * // The login block in the left sidebar region. + * $page['left']['user-login']['#block']; + * @endcode + * + * @param $page + * Nested array of renderable elements that make up the page. + * + * @see drupal_render_page() + */ +function hook_page_alter($page) { + if (menu_get_object('node', 1)) { + // We are on a node detail page. Append a standard disclaimer to the + // content region. + $page['content']['disclaimer'] = array( + '#markup' => t('Acme, Inc. is not responsible for the contents of this sample code.'), + '#weight' => 25, + ); + } +} + /** * Perform alterations before a form is rendered. * diff --git a/modules/system/system.module b/modules/system/system.module index f50318254806..581ae0aab185 100644 --- a/modules/system/system.module +++ b/modules/system/system.module @@ -237,6 +237,19 @@ function system_elements() { '#action' => request_uri(), ); + $type['page'] = array( + '#show_messages' => TRUE, + '#show_blocks' => TRUE, + '#theme' => 'page', + ); + + $type['list'] = array( + '#title' => '', + '#list_type' => 'ul', + '#attributes' => array(), + '#items' => array(), + ); + /** * Input elements. */ diff --git a/modules/taxonomy/taxonomy.module b/modules/taxonomy/taxonomy.module index 109397f3c16f..4e25c3bd8aac 100644 --- a/modules/taxonomy/taxonomy.module +++ b/modules/taxonomy/taxonomy.module @@ -26,9 +26,6 @@ function taxonomy_theme() { 'taxonomy_term_select' => array( 'arguments' => array('element' => NULL), ), - 'taxonomy_term_page' => array( - 'arguments' => array('tids' => array(), 'result' => NULL), - ), 'taxonomy_overview_vocabularies' => array( 'arguments' => array('form' => array()), ), @@ -73,7 +70,7 @@ function taxonomy_nodeapi_view($node) { } } } - + $node->content['links']['terms'] = array( '#type' => 'node_links', '#value' => $links, @@ -323,7 +320,7 @@ function taxonomy_term_save($term) { $status = drupal_write_record('taxonomy_term_data', $term); module_invoke_all('taxonomy_term_update', $term); } - + $or = db_or()->condition('tid1', $term->tid)->condition('tid2', $term->tid); db_delete('taxonomy_term_relation')->condition($or)->execute(); @@ -1224,7 +1221,7 @@ function theme_taxonomy_term_select($element) { * @param $order * The order clause for the query that retrieve the nodes. * @return - * A resource identifier pointing to the query results. + * An array of node IDs. */ function taxonomy_select_nodes($tids = array(), $operator = 'or', $depth = 0, $pager = TRUE, $order = 'n.sticky DESC, n.created DESC') { if (count($tids) > 0) { @@ -1254,44 +1251,20 @@ function taxonomy_select_nodes($tids = array(), $operator = 'or', $depth = 0, $p $wheres .= ' AND tn' . $index . '.tid IN (' . db_placeholders($tids, 'int') . ')'; $args = array_merge($args, $tids); } - $sql = 'SELECT DISTINCT(n.nid), n.sticky, n.title, n.created FROM {node} n ' . $joins . ' WHERE n.status = 1 ' . $wheres . ' ORDER BY ' . $order; + $sql = 'SELECT DISTINCT(n.nid) AS nid, n.sticky, n.created FROM {node} n ' . $joins . ' WHERE n.status = 1 ' . $wheres . ' ORDER BY ' . $order; $sql_count = 'SELECT COUNT(DISTINCT(n.nid)) FROM {node} n ' . $joins . ' WHERE n.status = 1 ' . $wheres; } $sql = db_rewrite_sql($sql); $sql_count = db_rewrite_sql($sql_count); if ($pager) { - $result = pager_query($sql, variable_get('default_nodes_main', 10), 0, $sql_count, $args); + $nids = pager_query($sql, variable_get('default_nodes_main', 10), 0, $sql_count, $args)->fetchCol(); } else { - $result = db_query_range($sql, $args, 0, variable_get('feed_default_items', 10)); + $nids = db_query_range($sql, $args, 0, variable_get('feed_default_items', 10))->fetchCol(); } } - return $result; -} - -/** - * Accepts the result of a pager_query() call, such as that performed by - * taxonomy_select_nodes(), and formats each node along with a pager. - */ -function taxonomy_render_nodes($result) { - $output = ''; - $nids = array(); - foreach ($result as $record) { - $nids[] = $record->nid; - } - if (!empty($nids)) { - $nodes = node_load_multiple($nids); - - foreach ($nodes as $node) { - $output .= node_view($node, 1); - } - $output .= theme('pager', NULL, variable_get('default_nodes_main', 10), 0); - } - else { - $output .= '<p>' . t('There are currently no posts in this category.') . '</p>'; - } - return $output; + return $nids; } /** diff --git a/modules/taxonomy/taxonomy.pages.inc b/modules/taxonomy/taxonomy.pages.inc index 84ea1d3ca210..16013aa1b408 100644 --- a/modules/taxonomy/taxonomy.pages.inc +++ b/modules/taxonomy/taxonomy.pages.inc @@ -42,11 +42,40 @@ function taxonomy_term_page($terms, $depth = 0, $op = 'page') { $breadcrumb[] = l(t('Home'), NULL); $breadcrumb = array_reverse($breadcrumb); drupal_set_breadcrumb($breadcrumb); - - $output = theme('taxonomy_term_page', $tids, taxonomy_select_nodes($tids, $terms['operator'], $depth, TRUE)); drupal_add_feed(url('taxonomy/term/' . $str_tids . '/' . $depth . '/feed'), 'RSS - ' . $title); - return $output; - break; + drupal_add_css(drupal_get_path('module', 'taxonomy') . '/taxonomy.css'); + + $build = array(); + // Only display the description if we have a single term, to avoid clutter and confusion. + if (count($tids) == 1) { + $term = taxonomy_term_load($tids[0]); + if (!empty($term->description)) { + $build['term_description'] = array( + '#markup' => filter_xss_admin($term->description), + '#weight' => -1, + '#prefix' => '<div class="taxonomy-term-description">', + '#suffix' => '</div>', + ); + } + } + + if ($nids = taxonomy_select_nodes($tids, $terms['operator'], $depth, TRUE)) { + $nodes = node_load_multiple($nids); + $build += node_build_multiple($nodes); + $build['pager'] = array( + '#markup' => theme('pager', NULL, variable_get('default_nodes_main', 10)), + '#weight' => 5, + ); + } + else { + $build['no_content'] = array( + '#prefix' => '<p>', + '#markup' => t('There are currently no posts in this category.'), + '#suffix' => '</p>', + ); + } + + return drupal_get_page($build); case 'feed': $channel['link'] = url('taxonomy/term/' . $str_tids . '/' . $depth, array('absolute' => TRUE)); @@ -58,13 +87,9 @@ function taxonomy_term_page($terms, $depth = 0, $op = 'page') { $channel['description'] = $term->description; } - $result = taxonomy_select_nodes($tids, $terms['operator'], $depth, FALSE); - $items = array(); - while ($row = db_fetch_object($result)) { - $items[] = $row->nid; - } + $nids = taxonomy_select_nodes($tids, $terms['operator'], $depth, FALSE); - node_feed($items, $channel); + node_feed($nids, $channel); break; default: @@ -77,38 +102,6 @@ function taxonomy_term_page($terms, $depth = 0, $op = 'page') { } } -/** - * Render a taxonomy term page HTML output. - * - * @param $tids - * An array of term ids. - * @param $result - * A pager_query() result, such as that performed by taxonomy_select_nodes(). - * - * @ingroup themeable - */ -function theme_taxonomy_term_page($tids, $result) { - drupal_add_css(drupal_get_path('module', 'taxonomy') . '/taxonomy.css'); - $output = ''; - - // Only display the description if we have a single term, to avoid clutter and confusion. - if (count($tids) == 1) { - $term = taxonomy_term_load($tids[0]); - $description = $term->description; - - // Check that a description is set. - if (!empty($description)) { - $output .= '<div class="taxonomy-term-description">'; - $output .= filter_xss_admin($description); - $output .= '</div>'; - } - } - - $output .= taxonomy_render_nodes($result); - - return $output; -} - /** * Page to edit a vocabulary term. */ diff --git a/modules/upload/upload.test b/modules/upload/upload.test index 79358225b9d4..bd4bfbdc96a9 100644 --- a/modules/upload/upload.test +++ b/modules/upload/upload.test @@ -53,7 +53,7 @@ class UploadTestCase extends DrupalWebTestCase { // Assure that the attachment link appears on teaser view and has correct count. $node = node_load($node->nid); - $teaser = node_view($node, TRUE); + $teaser = drupal_render(node_build($node, TRUE)); $this->assertTrue(strpos($teaser, format_plural(2, '1 attachment', '@count attachments')), 'Attachments link found on node teaser.'); // Fetch db record and use fid to rename and delete file. -- GitLab