diff --git includes/common.inc includes/common.inc index 58b0845..82da890 100644 --- includes/common.inc +++ 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 rather than returning an array in your menu callback. * * @param $path * A Drupal path or a full URL. @@ -389,7 +388,10 @@ function drupal_not_found() { } // To conserve CPU and bandwidth, omit the blocks. - print theme('page', $return, FALSE); + print drupal_render_page(array( + '#markup' => $return, + '#page_properties' => array('#show_blocks' => FALSE), + )); } /** @@ -416,7 +418,12 @@ function drupal_access_denied() { drupal_set_title(t('Access denied')); $return = t('You are not authorized to access this page.'); } - print theme('page', $return); + + // To conserve CPU and bandwidth, omit the blocks. + print drupal_render_page(array( + '#markup' => $return, + '#page_properties' => array('#show_blocks' => FALSE), + )); } /** @@ -821,7 +828,11 @@ 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. + print drupal_render_page(array( + '#markup' => t('The website encountered an unexpected error. Please try again later.'), + '#page_properties' => array('#show_blocks' => FALSE), + )); } else { print theme('maintenance_page', t('The website encountered an unexpected error. Please try again later.'), FALSE); @@ -3176,6 +3187,92 @@ function drupal_alter($type, &$data) { } /** + * Renders the page and adds all theming. + * + * @param $content + * A string or array representing the content of the page. + */ +function drupal_render_page($content) { + $page['#type'] = 'page'; + // Merge in default values. + $page = array_merge($page, _element_info('page')); + // Merge in properties passed by the caller. + if (isset($content['#page_properties']) && is_array($content['#page_properties'])) { + $page = array_merge($page, $content['#page_properties']); + } + // Set the main content. + if (is_array($content)) { + $page['content'] = $content; + } + else { + $page['content'] = array('#markup' => $content); + } + + // Load the blocks and merge the regions into the page array. + $regions = drupal_get_blocks($page['#show_blocks']); + foreach ($regions as $region => $blocks) { + $page[$region]['blocks'] = $blocks; + } + drupal_alter('page', $page); + print drupal_render($page); +} + +/** + * Get a renderable array of all blocks for the current page, keyed by region. + * + * @param $show_blocks + * If FALSE, the left and right regions are prevented from rendering blocks. + */ +function drupal_get_blocks($show_blocks = TRUE) { + // The theme system might not yet be initialized. + init_theme(); + global $theme; + + $output = array(); + // 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 ($show_blocks || ($region != 'left' && $region != 'right')) { + $output[$region] = drupal_get_blocks_by_region($region); + } + else { + $output[$region] = array('#markup' => ''); + } + } + return $output; +} + +/** + * Get a renderable array of a region containing all enabled blocks in the + * region plus additional content set via drupal_set_content(). + * + * @param $region + * The requested region. + */ +function drupal_get_blocks_by_region($region) { + $weight = 0; + if ($list = block_list($region)) { + foreach ($list as $key => $block) { + $output[$key] = array( + '#theme' => 'block', + '#block' => $block, + '#weight' => $weight + ); + $weight++; + } + } + + // Add any content assigned to this region through drupal_set_content() calls. + $output['extra_content'] = array( + '#markup' => drupal_get_content($region), + '#weight' => $weight, + ); + return $output; +} + +/** * Renders HTML given a structured array tree. * * Recursively iterates over each of the array elements, generating HTML code. @@ -3246,7 +3343,17 @@ function drupal_render(&$elements) { // Render each of the children using drupal_render and concatenate them. if (!isset($content) || $content === '') { foreach ($children as $key) { - $content .= drupal_render($elements[$key]); + // If #concatenate_children is set to FALSE, the rendered children of the + // element are not concatenated into a single string, but instead kept as + // a keyed array of rendered string in the #children property of the parent + // element. The parent's element theming function is then responsible for + // printing out all elements in the #children array. + if (isset($elements['#concatenate_children']) && $elements['#concatenate_children'] == FALSE) { + $content[$key] = drupal_render($elements[$key]); + } + else { + $content .= drupal_render($elements[$key]); + } } } } @@ -3339,7 +3446,7 @@ function drupal_common_theme() { 'arguments' => array('text' => NULL) ), 'page' => array( - 'arguments' => array('content' => NULL, 'show_blocks' => TRUE, 'show_messages' => TRUE), + 'arguments' => array('content' => NULL), 'template' => 'page', ), 'maintenance_page' => array( @@ -3398,6 +3505,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 includes/theme.inc includes/theme.inc index 37d8652..2173dbe 100644 --- includes/theme.inc +++ includes/theme.inc @@ -1571,6 +1571,28 @@ function theme_item_list($items = array(), $title = NULL, $type = 'ul', $attribu } /** + * 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'). Mandatory. + * - #title. A title which prints above the list. Optional. + * - #list_type. The type of list to return. Defaults to "ul". + * - #attributes. An array of attributes as expected by theme('item_list'). Optional. + * @return + * A string containing the list output. + */ +function theme_list($elements) { + // Populate any missing array elements with their defaults. + $elements += array( + '#title' => '', + '#list_type' => 'ul', + '#attributes' => array(), + '#items' => array(), + ); + return theme('item_list', $elements['#items'], $elements['#title'], $elements['#list_type'], $elements['#attributes']); +} +/** * Returns code that emits the 'more help'-link. */ function theme_more_help_link($url) { @@ -1633,30 +1655,6 @@ function theme_closure($main = 0) { } /** - * 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. * * @param $object @@ -1825,27 +1823,23 @@ function template_preprocess(&$variables, $hook) { * @see page.tpl.php */ function template_preprocess_page(&$variables) { + // Move some variables to the top level for themer convenience and template cleanliness. + $names = array('show_blocks', 'show_messages'); + foreach ($names as $name) { + $variables[$name] = $variables['content']['#'. $name]; + } + + // Import children of the content array as variables. + // This contains the main 'content' and the regions (with rendered blocks). + $content = $variables['content']['#children']; + foreach ($content as $key => $child) { + $variables[$key] = $child; + } // Add favicon if (theme_get_setting('toggle_favicon')) { drupal_set_html_head(''); } - 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; - } - // Set up layout variable. $variables['layout'] = 'none'; if (!empty($variables['left'])) { @@ -1977,6 +1971,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); @@ -2042,6 +2038,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 index.php index.php index e9cdef1..0eeb3aa 100644 --- index.php +++ 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. + drupal_render_page($return); } drupal_page_footer(); diff --git modules/blog/blog.pages.inc modules/blog/blog.pages.inc index a48c432..4308ae5 100644 --- modules/blog/blog.pages.inc +++ 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)); + $page['blog_actions'] = array( + '#items' => $items, + '#theme' => 'list', + '#weight' => -1, + ); + + $nids = pager_query(db_rewrite_sql("SELECT n.nid 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); + $page += node_build_multiple($nodes); + $page['pager'] = array( + '#markup' => theme('pager', NULL, variable_get('default_nodes_main', 10)), + '#weight' => count($nodes), + ); } 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 $page; } /** @@ -56,31 +58,33 @@ function blog_page_last() { global $user; $output = ''; - $items = array(); + $page = array(); if (user_access('edit own blog')) { $items[] = l(t('Create new blog entry.'), "node/add/blog"); + $page['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 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); + $page += node_build_multiple($nodes); + $page['pager'] = array( + '#markup' => theme('pager', NULL, variable_get('default_nodes_main', 10)), + '#weight' => count($nodes), + ); } else { drupal_set_message(t('No blog entries have been created.')); } drupal_add_feed(url('blog/feed'), t('RSS - blogs')); - return $output; + return $page; } /** diff --git modules/comment/comment.module modules/comment/comment.module index 58e8315..f196ee4 100644 --- modules/comment/comment.module +++ modules/comment/comment.module @@ -1628,7 +1628,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_view($node)); $edit['pid'] = 0; } diff --git modules/comment/comment.pages.inc modules/comment/comment.pages.inc index d36d04f..a656835 100644 --- modules/comment/comment.pages.inc +++ 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_view($node)); } // Should we show the reply box? diff --git modules/node/node.api.php modules/node/node.api.php index 4a8636a..75937df 100644 --- modules/node/node.api.php +++ modules/node/node.api.php @@ -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 modules/node/node.module modules/node/node.module index d2d3771..8cec3c7 100644 --- modules/node/node.module +++ 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( @@ -1123,24 +1123,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) { $node = (object)$node; $node = node_build_content($node, $teaser); - - return theme('node', $node, $teaser); + + $render = $node->content; + $render += array( + '#theme' => 'node', + 'node' => $node, + 'teaser' => $teaser, + ); + return $render; } /** @@ -1208,19 +1212,17 @@ function node_build_content($node, $teaser = FALSE) { } /** - * Generate a page displaying a single node. + * Generate an array which displays a single node. */ 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; + return node_view($node, FALSE); } /** @@ -1872,20 +1874,42 @@ function node_feed($nids = FALSE, $channel = array()) { } /** + * Construct a drupal_render() style array from an array of loaded nodes. + * + * @param array $nodes + * An array of nodes as returned by node_load_multiple(). + * @param boolean $teaser + * Display nodes into teaser view or full view. + * @param integer $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) { + $render = array(); + foreach ($nodes as $node) { + $render["nid_$node->nid"] = node_view($node, $teaser); + $render["nid_$node->nid"]['#weight'] = $weight; + $weight++; + } + return $render; +} + +/** * 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(); if (!empty($nids)) { $nodes = node_load_multiple($nids); - $output = ''; - foreach ($nodes as $node) { - $output .= node_view($node, TRUE); - } + $page = 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)); + $page['pager'] = array( + '#markup' => theme('pager', NULL, variable_get('default_nodes_main', 10)), + '#weight' => count($nodes), + ); } else { $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 = '' . t('There are currently no posts in this category.') . '
'; - } - return $output; + return $nids; } /** diff --git modules/taxonomy/taxonomy.pages.inc modules/taxonomy/taxonomy.pages.inc index 1e448d0..0c5144e 100644 --- modules/taxonomy/taxonomy.pages.inc +++ modules/taxonomy/taxonomy.pages.inc @@ -42,12 +42,44 @@ 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; + + if ($nids = taxonomy_select_nodes($tids, $terms['operator'], $depth, TRUE)) { + drupal_add_css(drupal_get_path('module', 'taxonomy') . '/taxonomy.css'); + $page = array(); + + $nodes = node_load_multiple($nids); + $page += node_build_multiple($nodes); + $page['pager'] = array( + '#markup' => theme('pager', NULL, variable_get('default_nodes_main', 10)), + '#weight' => count($nodes), + ); + } + else { + $page['no_posts'] = array( + '#prefix' => '', + '#markup' => t('There are currently no posts in this category.'), + '#suffix' => '
', + ); + } + // 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)) { + $page['term_description'] = array( + '#markup' => filter_xss_admin($description), + '#weight' => -1, + '#prefix' => '