Index: modules/system/system.module =================================================================== RCS file: /cvs/drupal/drupal/modules/system/system.module,v retrieving revision 1.594 diff -u -r1.594 system.module --- modules/system/system.module 1 Apr 2008 20:05:15 -0000 1.594 +++ modules/system/system.module 1 Apr 2008 22:41:25 -0000 @@ -2004,6 +2004,63 @@ } /** + * Implements hook_theme_modes. + * + * Provide a rendering mode for JSON based popup dialogs. + */ +function system_theme_modes() { + return array( + 'json/popup' => 'system_json_popup_render' + ); +} + +/** + * Render the page as JSON, for consumption by AJAX calls. + * Optimized for AJAX popups. + * + * @return + * JSON object with metadata and themed page content. + */ +function system_json_popup_render() { + $args = func_get_args(); + $hook = array_shift($args); + switch ($hook) { + case 'page': + return drupal_json(array( + 'status' => 'ok', + 'title' => drupal_get_title(), + 'messages' => theme('status_messages'), + 'path' => $_GET['q'], + 'content' => $args[0], + )); + default: + array_unshift($args, $hook); + return call_user_func_array('theme_render', $args); + } +} + +/** + * Implements hook_after_process_form + */ +function system_after_process_form($request_mode, $form, $form_state) { + switch ($request_mode) { + case 'XMLHttpRequest': + // Request comes from jQuery AJAX call. + // Return status, next page url and form state as JSON. + $url = drupal_redirect_form($form, $form_state['redirect'], FALSE); + print drupal_json(array( + 'status' => 'redirect', + 'path' => $url, + 'form_state' => $form_state, + )); + exit; + case 'standard': + // Default Drupal behavior, redirect to the next page. + drupal_redirect_form($form, $form_state['redirect']); + } +} + +/** * Format the Powered by Drupal text. * * @ingroup themeable Index: includes/bootstrap.inc =================================================================== RCS file: /cvs/drupal/drupal/includes/bootstrap.inc,v retrieving revision 1.206 diff -u -r1.206 bootstrap.inc --- includes/bootstrap.inc 10 Jan 2008 22:47:17 -0000 1.206 +++ includes/bootstrap.inc 1 Apr 2008 22:41:18 -0000 @@ -271,7 +271,7 @@ * session name correctly. */ function conf_init() { - global $base_url, $base_path, $base_root; + global $base_url, $base_path, $base_root, $render_mode, $request_mode; // Export the following settings.php variables to the global namespace global $db_url, $db_prefix, $cookie_domain, $conf, $installed_profile, $update_free_access; @@ -337,6 +337,10 @@ ini_set('session.cookie_domain', $cookie_domain); } session_name('SESS'. md5($session_name)); + + // TODO - add comments to describe what these globals control. + $render_mode = isset($_SERVER['HTTP_X_DRUPAL_RENDER_MODE']) ? $_SERVER['HTTP_X_DRUPAL_RENDER_MODE'] : 'xhtml/plain'; + $request_mode = isset($_SERVER['HTTP_X_REQUESTED_WITH']) ? $_SERVER['HTTP_X_REQUESTED_WITH'] : 'standard'; } /** Index: includes/form.inc =================================================================== RCS file: /cvs/drupal/drupal/includes/form.inc,v retrieving revision 1.268 diff -u -r1.268 form.inc --- includes/form.inc 15 Mar 2008 11:02:47 -0000 1.268 +++ includes/form.inc 1 Apr 2008 22:41:21 -0000 @@ -433,7 +433,9 @@ // however, we'll skip this and let the calling function examine // the resulting $form_state bundle itself. if (!$form['#programmed'] && empty($form_state['rebuild']) && empty($form_state['storage'])) { - drupal_redirect_form($form, $form_state['redirect']); + global $request_mode; + // TODO - more comments here, and update the above comments. + module_invoke_all('after_process_form', $request_mode, $form, $form_state); } } } @@ -606,8 +608,13 @@ * @param $redirect * An optional value containing the destination path to redirect * to if none is specified by the form. + * @param $do_goto + * Boolean flag. + * If true, call to drupal_goto to do the redirect. + * If false, return the url of the redirect, but don't go there. This allows + * for alternal flows of control, such as AJAX or webservices. */ -function drupal_redirect_form($form, $redirect = NULL) { +function drupal_redirect_form($form, $redirect = NULL, $do_goto = TRUE) { $goto = NULL; if (isset($redirect)) { $goto = $redirect; @@ -615,17 +622,31 @@ if ($goto !== FALSE && isset($form['#redirect'])) { $goto = $form['#redirect']; } - if (!isset($goto) || ($goto !== FALSE)) { - if (isset($goto)) { - if (is_array($goto)) { - call_user_func_array('drupal_goto', $goto); + if (!isset($goto) || ($goto !== FALSE)) { + // If $do_goto is set, call drupal_goto and do the redirect & exit. + if ($do_goto) { + if (isset($goto)) { + if (is_array($goto)) { + call_user_func_array('drupal_goto', $goto); + } + else { + drupal_goto($goto); + } } - else { - drupal_goto($goto); + drupal_goto($_GET['q']); + } + else { // If $do_goto is false, just calculate and return the next url. + if (isset($goto)) { + if (is_array($goto)) { + return call_user_func_array('drupal_get_goto_url', $goto); + } + else { + return drupal_get_goto_url($goto); + } } + return drupal_get_goto_url($_GET['q']); } - drupal_goto($_GET['q']); - } + } } /** Index: includes/theme.inc =================================================================== RCS file: /cvs/drupal/drupal/includes/theme.inc,v retrieving revision 1.418 diff -u -r1.418 theme.inc --- includes/theme.inc 25 Mar 2008 14:10:01 -0000 1.418 +++ includes/theme.inc 1 Apr 2008 22:41:22 -0000 @@ -463,7 +463,7 @@ } /** - * Generate the themed output. + * Generate themed output for the current rendering mode. * * All requests for theme hooks must go through this function. It examines * the request and routes it to the appropriate theme function. The theme @@ -547,9 +547,41 @@ * @param ... * Additional arguments to pass along to the theme function. * @return - * An HTML string that generates the themed output. + * An string that generates the themed output according to the current render + * mode. HTML is the default output. */ function theme() { + global $render_mode; + $args = func_get_args(); + + // Check if we're using a custom render mode. + if ($render_mode != 'xhtml/plain') { + static $theme_renderers; + if (!isset($theme_renderers)) { + $theme_renderers = module_invoke_all('theme_modes'); + } + $theme_renderer = isset($theme_renderers[$render_mode]) ? $theme_renderers[$render_mode] : NULL; + + // If a different theme rendering system is available, call it instead of the default. + if (function_exists($theme_renderer)) { + return call_user_func_array($theme_renderer, $args); + } + } + + // If not using a custom render mode, use the default renderer. + return call_user_func_array('theme_render', $args); +} + +/** + * The default theme mode renderer. Generates the HTML output for most requests. + * + * Invokes the theme system and generates the page layout when making a normal + * page request. This function is called by theme() when using the default + * rendering mode. + * + * @see theme() + */ +function theme_render() { $args = func_get_args(); $hook = array_shift($args); Index: includes/common.inc =================================================================== RCS file: /cvs/drupal/drupal/includes/common.inc,v retrieving revision 1.762 diff -u -r1.762 common.inc --- includes/common.inc 31 Mar 2008 20:50:05 -0000 1.762 +++ includes/common.inc 1 Apr 2008 22:41:20 -0000 @@ -293,14 +293,14 @@ * supported. * @see drupal_get_destination() */ -function drupal_goto($path = '', $query = NULL, $fragment = NULL, $http_response_code = 302) { - +// TODO - update comments to reflect breaking drupal_goto into two functions. +function drupal_get_goto_url($path = '', $query = NULL, $fragment = NULL) { if (isset($_REQUEST['destination'])) { extract(parse_url(urldecode($_REQUEST['destination']))); } else if (isset($_REQUEST['edit']['destination'])) { extract(parse_url(urldecode($_REQUEST['edit']['destination']))); - } + } $url = url($path, array('query' => $query, 'fragment' => $fragment, 'absolute' => TRUE)); // Remove newlines from the URL to avoid header injection attacks. @@ -311,19 +311,25 @@ if (!defined('MAINTENANCE_MODE') || MAINTENANCE_MODE != 'update') { module_invoke_all('exit', $url); } + return $url; +} +function drupal_goto($path = '', $query = NULL, $fragment = NULL, $http_response_code = 302) { + $url = drupal_get_goto_url($path, $query, $fragment); + // Even though session_write_close() is registered as a shutdown function, we // need all session data written to the database before redirecting. session_write_close(); - + header('Location: '. $url, TRUE, $http_response_code); - + // The "Location" header sends a redirect status code to the HTTP daemon. In // some cases this can be wrong, so we make sure none of the code below the // drupal_goto() call gets executed upon redirection. exit(); } + /** * Generates a site off-line message. */