Index: includes/batch.inc =================================================================== RCS file: /cvs/drupal/drupal/includes/batch.inc,v retrieving revision 1.19 diff -u -p -r1.19 batch.inc --- includes/batch.inc 21 Jun 2008 18:24:20 -0000 1.19 +++ includes/batch.inc 24 Jun 2008 18:36:31 -0000 @@ -133,7 +133,7 @@ function _batch_progress_page_nojs() { // followed by the error message. ob_start(); $fallback = $current_set['error_message'] . '
' . $batch['error_message']; - $fallback = theme('maintenance_page', $fallback, FALSE, FALSE); + $fallback = theme('maintenance_page', $fallback, FALSE, FALSE, FALSE); // We strip the end of the page using a marker in the template, so any // additional HTML output by PHP shows up inside the page rather than Index: includes/common.inc =================================================================== RCS file: /cvs/drupal/drupal/includes/common.inc,v retrieving revision 1.772 diff -u -p -r1.772 common.inc --- includes/common.inc 18 Jun 2008 03:36:23 -0000 1.772 +++ includes/common.inc 24 Jun 2008 18:36:32 -0000 @@ -332,7 +332,8 @@ function drupal_site_offline() { drupal_set_header('HTTP/1.1 503 Service unavailable'); drupal_set_title(t('Site off-line')); print theme('maintenance_page', filter_xss_admin(variable_get('site_offline_message', - t('@site is currently under maintenance. We should be back shortly. Thank you for your patience.', array('@site' => variable_get('site_name', 'Drupal')))))); + t('@site is currently under maintenance. We should be back shortly. Thank you for your patience.', array('@site' => variable_get('site_name', 'Drupal'))))), TRUE, TRUE, FALSE + ); } /** @@ -361,8 +362,8 @@ function drupal_not_found() { $return = t('The requested page could not be found.'); } - // To conserve CPU and bandwidth, omit the blocks. - print theme('page', $return, FALSE); + // To conserve CPU and bandwidth, omit the blocks; but keep messages. + print theme('page', $return, FALSE, TRUE, FALSE); } /** @@ -389,7 +390,9 @@ function drupal_access_denied() { drupal_set_title(t('Access denied')); $return = t('You are not authorized to access this page.'); } - print theme('page', $return); + + // Display blocks, messages and set success to FALSE. + print theme('page', $return, TRUE, TRUE, FALSE); } /** @@ -2832,11 +2835,11 @@ function drupal_common_theme() { 'arguments' => array('text' => NULL) ), 'page' => array( - 'arguments' => array('content' => NULL, 'show_blocks' => TRUE, 'show_messages' => TRUE), + 'arguments' => array('content' => NULL, 'show_blocks' => TRUE, 'show_messages' => TRUE, 'success' => TRUE), 'template' => 'page', ), 'maintenance_page' => array( - 'arguments' => array('content' => NULL, 'show_blocks' => TRUE, 'show_messages' => TRUE), + 'arguments' => array('content' => NULL, 'show_blocks' => TRUE, 'show_messages' => TRUE, 'success' => TRUE), 'template' => 'maintenance-page', ), 'update_page' => array( Index: includes/database.inc =================================================================== RCS file: /cvs/drupal/drupal/includes/database.inc,v retrieving revision 1.94 diff -u -p -r1.94 database.inc --- includes/database.inc 20 Apr 2008 18:23:21 -0000 1.94 +++ includes/database.inc 24 Jun 2008 18:36:32 -0000 @@ -189,7 +189,7 @@ function _db_error_page($error = '') { $message .= '

The ' . theme('placeholder', $db_type) . ' error was: ' . theme('placeholder', $error) . '.

'; } - print theme('maintenance_page', $message); + print theme('maintenance_page', $message, TRUE, TRUE, FALSE); exit; } Index: includes/theme.inc =================================================================== RCS file: /cvs/drupal/drupal/includes/theme.inc,v retrieving revision 1.427 diff -u -p -r1.427 theme.inc --- includes/theme.inc 18 Jun 2008 16:22:13 -0000 1.427 +++ includes/theme.inc 24 Jun 2008 18:36:35 -0000 @@ -602,6 +602,9 @@ function theme() { include_once($include_file); } if (isset($info['function'])) { + // Enable the redirection of output into formats other than XHTML, + // e.g. XML, JSON. + drupal_set_renderer($info['function'], $hook, $args); // The theme call is a function. $output = call_user_func_array($info['function'], $args); } @@ -672,6 +675,9 @@ function theme() { $template_file = $info['path'] . '/' . $template_file; } } + // Enable the redirection of output into formats other than XHTML, + // e.g. XML, JSON. + drupal_set_renderer($render_function, $hook, $variables); $output = $render_function($template_file, $variables); } // restore path_to_theme() @@ -2001,3 +2007,39 @@ function template_preprocess_block(&$var $variables['template_files'][] = 'block-' . $variables['block']->module . '-' . $variables['block']->delta; } +/** + * Enable modules to override the standard theme renderer for a theme call. + * + * Modules invoking hook_renderer() are called in order of weight, + * starting with the highest, and only the first response is accepted. + */ +function drupal_set_renderer(&$render_function, $hook, &$variables) { + foreach (array_reverse(module_implements('renderer')) as $module) { + // Construction needed to pass by reference. + $args = array($hook, &$variables); + if ($function = call_user_func_array($module . '_renderer', $args)) { + $render_function = $function; + return; + } + } +} + +/** + * Render function to output themed data in JSON format. + * + * The theme system passes two arguments, a template file path and the + * content to be rendered. Here we need only the second. + */ +function drupal_render_json() { + $args = func_get_args(); + drupal_json(array_pop($args)); +} + +/** + * Render function to output an XML-RPC defined structure based on data. + */ +function drupal_render_xml() { + $args = func_get_args(); + include_once 'includes/xmlrpc.inc'; + print xmlrpc_value_get_xml(xmlrpc_value(array_pop($args))); +} Index: modules/system/system.module =================================================================== RCS file: /cvs/drupal/drupal/modules/system/system.module,v retrieving revision 1.602 diff -u -p -r1.602 system.module --- modules/system/system.module 7 May 2008 19:17:50 -0000 1.602 +++ modules/system/system.module 24 Jun 2008 18:36:45 -0000 @@ -668,6 +668,50 @@ function _system_themes_access($theme) { } /** + * Implementation of hook_renderer(). + * + * If a page or item has been requested via jQuery, return it in JSON format. + */ +function system_renderer($hook, &$variables) { + global $theme_key; + $render_type = null; + if (isset($_GET['render'])) { + $render_type = $_GET['render']; + } + // jQuery sets a HTTP_X_REQUESTED_WITH header of 'XMLHttpRequest'. + if (!$render_type) { + // Set the render type based on the request header. + if (isset($_SERVER['HTTP_X_REQUESTED_WITH']) && $_SERVER['HTTP_X_REQUESTED_WITH'] == 'XMLHttpRequest') { + $render_type = 'json'; + } + } + if ($render_type) { + // If this is a full page request that has reached the rendering stage, + // return it in JSON or XML. + if ($hook == 'page') { + switch ($render_type) { + case 'json': + case 'xml': + // Strip out all but a subset of page properties. + // This prevents security issues involved with passing unthemed + // data to the client and minimizes transfer bandwidth. + $use_variables = array('content', 'tabs', 'title', 'show_blocks', + 'show_messages', 'success', 'menu_result', 'sidebar_left', + 'sidebar_right', 'help', 'messages', 'mission', 'primary_links', + 'secondary_links'); + $use_variables = array_merge($use_variables, array_keys(system_region_list($theme_key))); + foreach (array_keys($variables) as $key) { + if (!in_array($key, $use_variables)) { + unset($variables[$key]); + } + } + return 'drupal_render_' . $render_type; + } + } + } +} + +/** * Implementation of hook_init(). */ function system_init() {