diff --git a/includes/common.inc b/includes/common.inc index e33a5c187c0..dd7e58c3582 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 b71da908bff..b77e3585424 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 == module_delta - $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(''); + // 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(''); } // 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 dba9f1123c9..d776d767a75 100644 --- a/index.php +++ b/index.php @@ -21,7 +21,7 @@ require_once DRUPAL_ROOT . '/includes/bootstrap.inc'; 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 @@ if (is_int($return)) { } } 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 ead47fba0c8..00afc9bd82a 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, '
' . $value . '
'); // 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 318617e8bfe..c08bac94987 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 = '
' . $regions[$region] . '
'; + $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 d44bbf5fa42..889bf47a166 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); + $build['blog_actions'] = array( + '#items' => $items, + '#theme' => 'list', + '#weight' => -1, + ); - $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)); + $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); + $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(); - $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; - } - - 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 b1e48ed3ac9..a92b96a455a 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 a4c94e22ae1..12d30cec8fc 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 68050065d12..8b8343d2ed0 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 3936359ade7..3629e2209c6 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 = '

' . t('Welcome to your new Drupal website!') . '

'; @@ -1930,12 +1963,14 @@ function node_page_default() { $default_message .= '
  • ' . t('Start posting content Finally, you can create content for your website. This message will disappear once you have promoted a post to the front page.', array('@content' => url('node/add'))) . '
  • '; $default_message .= ''; $default_message .= '

    ' . t('For more information, please refer to the help section, or the online Drupal handbooks. You may also post at the Drupal forum, or view the wide range of other support options available.', array('@help' => url('admin/help'), '@handbook' => 'http://drupal.org/handbooks', '@forum' => 'http://drupal.org/forum', '@support' => 'http://drupal.org/support')) . '

    '; - - $output = '
    ' . $default_message . '
    '; + $build['default_message'] = array( + '#markup' => $default_message, + '#prefix' => '
    ', + '#suffix' => '
    ', + ); } 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 28aff234a93..a6e5d34bfc2 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. You can insert the delimiter "<!--break-->" (without the quotes) to fine-tune where your post gets split.')); $output .= '

    ' . t('Preview trimmed version') . '

    '; - $output .= node_view(clone $node, 1, FALSE); + $output .= drupal_render(node_build(clone $node, TRUE)); $output .= '

    ' . t('Preview full version') . '

    '; - $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 .= "\n"; diff --git a/modules/system/system.admin.inc b/modules/system/system.admin.inc index 703de2d0127..be461d72774 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 3c74f48db4b..cd98c508150 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 f5031825480..581ae0aab18 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 109397f3c16..4e25c3bd8aa 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 .= '

    ' . t('There are currently no posts in this category.') . '

    '; - } - return $output; + return $nids; } /** diff --git a/modules/taxonomy/taxonomy.pages.inc b/modules/taxonomy/taxonomy.pages.inc index 84ea1d3ca21..16013aa1b40 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' => '
    ', + '#suffix' => '
    ', + ); + } + } + + 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' => '

    ', + '#markup' => t('There are currently no posts in this category.'), + '#suffix' => '

    ', + ); + } + + 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 .= '
    '; - $output .= filter_xss_admin($description); - $output .= '
    '; - } - } - - $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 79358225b9d..bd4bfbdc96a 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.