diff options
-rw-r--r-- | includes/ajax.inc | 72 | ||||
-rw-r--r-- | includes/common.inc | 254 | ||||
-rw-r--r-- | includes/menu.inc | 68 | ||||
-rw-r--r-- | index.php | 23 | ||||
-rw-r--r-- | modules/book/book.module | 3 | ||||
-rw-r--r-- | modules/book/book.pages.inc | 5 | ||||
-rw-r--r-- | modules/comment/comment.module | 2 | ||||
-rw-r--r-- | modules/file/file.module | 2 | ||||
-rw-r--r-- | modules/menu/menu.admin.inc | 2 | ||||
-rw-r--r-- | modules/menu/menu.api.php | 4 | ||||
-rw-r--r-- | modules/poll/poll.module | 4 | ||||
-rw-r--r-- | modules/system/system.api.php | 74 | ||||
-rw-r--r-- | modules/system/system.install | 15 | ||||
-rw-r--r-- | modules/system/system.module | 12 | ||||
-rw-r--r-- | modules/user/user.pages.inc | 2 |
15 files changed, 416 insertions, 126 deletions
diff --git a/includes/ajax.inc b/includes/ajax.inc index ca198d6fa..0cd6f63e6 100644 --- a/includes/ajax.inc +++ b/includes/ajax.inc @@ -291,30 +291,60 @@ function ajax_form_callback() { $callback = $triggering_element['#ajax']['callback']; } if (!empty($callback) && function_exists($callback)) { - $html = $callback($form, $form_state); - - // If the returned value is a string, assume it is HTML, add the status - // messages, and create a command object to return automatically. We want - // the status messages inside the new wrapper, so that they get replaced - // on subsequent AJAX calls for the same wrapper. - if (is_string($html)) { - $commands = array(); - $commands[] = ajax_command_replace(NULL, $html); - $commands[] = ajax_command_prepend(NULL, theme('status_messages')); - } - // Otherwise, $html is supposed to be an array of commands, suitable for - // Drupal.ajax, so we pass it on as is. In this situation, the callback is - // doing something fancy, so let it decide how to handle status messages - // without second guessing it. - else { - $commands = $html; - } + return $callback($form, $form_state); + } +} - ajax_render($commands); +/** + * Package and send the result of a page callback to the browser as an AJAX response. + * + * @param $page_callback_result + * The result of a page callback. Can be one of: + * - NULL: to indicate no content. + * - An integer menu status constant: to indicate an error condition. + * - A string of HTML content. + * - A renderable array of content. + */ +function ajax_deliver($page_callback_result) { + $commands = array(); + if (!isset($page_callback_result)) { + // Simply delivering an empty commands array is sufficient. This results + // in the AJAX request being completed, but nothing being done to the page. } + elseif (is_int($page_callback_result)) { + switch ($page_callback_result) { + case MENU_NOT_FOUND: + $commands[] = ajax_command_alert(t('The requested page could not be found.')); + break; + + case MENU_ACCESS_DENIED: + $commands[] = ajax_command_alert(t('You are not authorized to access this page.')); + break; - // Return a 'do nothing' command if there was no callback. - ajax_render(array()); + case MENU_SITE_OFFLINE: + $commands[] = ajax_command_alert(filter_xss_admin(variable_get('maintenance_mode_message', + t('@site is currently under maintenance. We should be back shortly. Thank you for your patience.', array('@site' => variable_get('site_name', 'Drupal')))))); + break; + } + } + elseif (is_array($page_callback_result) && isset($page_callback_result['#type']) && ($page_callback_result['#type'] == 'ajax_commands')) { + // Complex AJAX callbacks can return a result that contains a specific + // set of commands to send to the browser. + if (isset($page_callback_result['#ajax_commands'])) { + $commands = $page_callback_result['#ajax_commands']; + } + } + else { + // Like normal page callbacks, simple AJAX callbacks can return html + // content, as a string or renderable array, to replace what was previously + // there in the wrapper. In this case, in addition to the content, we want + // to add the status messages, but inside the new wrapper, so that they get + // replaced on subsequent AJAX calls for the same wrapper. + $html = is_string($page_callback_result) ? $page_callback_result : drupal_render($page_callback_result); + $commands[] = ajax_command_replace(NULL, $html); + $commands[] = ajax_command_prepend(NULL, theme('status_messages')); + } + ajax_render($commands); } /** diff --git a/includes/common.inc b/includes/common.inc index e715010f2..9bc5e335b 100644 --- a/includes/common.inc +++ b/includes/common.inc @@ -665,75 +665,39 @@ function drupal_goto($path = '', array $query = array(), $fragment = NULL, $http } /** - * Generates a site offline message. + * Deliver a "site is under maintenance" message to the browser. + * + * Page callback functions wanting to report a "site offline" message should + * return MENU_SITE_OFFLINE instead of calling drupal_site_offline(). However, + * functions that are invoked in contexts where that return value might not + * bubble up to menu_execute_active_handler() should call drupal_site_offline(). */ function drupal_site_offline() { - drupal_maintenance_theme(); - drupal_add_http_header('503 Service unavailable'); - drupal_set_title(t('Site under maintenance')); - print theme('maintenance_page', array('content' => filter_xss_admin(variable_get('maintenance_mode_message', - t('@site is currently under maintenance. We should be back shortly. Thank you for your patience.', array('@site' => variable_get('site_name', 'Drupal'))))))); + drupal_deliver_page(MENU_SITE_OFFLINE); } /** - * Generates a 404 error if the request can not be handled. + * Deliver a "page not found" error to the browser. + * + * Page callback functions wanting to report a "page not found" message should + * return MENU_NOT_FOUND instead of calling drupal_not_found(). However, + * functions that are invoked in contexts where that return value might not + * bubble up to menu_execute_active_handler() should call drupal_not_found(). */ function drupal_not_found() { - drupal_add_http_header('404 Not Found'); - - watchdog('page not found', check_plain($_GET['q']), NULL, WATCHDOG_WARNING); - - // Keep old path for reference, and to allow forms to redirect to it. - if (!isset($_GET['destination'])) { - $_GET['destination'] = $_GET['q']; - } - - $path = drupal_get_normal_path(variable_get('site_404', '')); - if ($path && $path != $_GET['q']) { - // 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.'); - } - - drupal_set_page_content($return); - $page = element_info('page'); - print drupal_render_page($page); + drupal_deliver_page(MENU_NOT_FOUND); } /** - * Generates a 403 error if the request is not allowed. + * Deliver a "access denied" error to the browser. + * + * Page callback functions wanting to report an "access denied" message should + * return MENU_ACCESS_DENIED instead of calling drupal_access_denied(). However, + * functions that are invoked in contexts where that return value might not + * bubble up to menu_execute_active_handler() should call drupal_access_denied(). */ function drupal_access_denied() { - drupal_add_http_header('403 Forbidden'); - watchdog('access denied', check_plain($_GET['q']), NULL, WATCHDOG_WARNING); - - // Keep old path for reference, and to allow forms to redirect to it. - if (!isset($_GET['destination'])) { - $_GET['destination'] = $_GET['q']; - } - - $path = drupal_get_normal_path(variable_get('site_403', '')); - if ($path && $path != $_GET['q']) { - // 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 drupal_render_page($return); + drupal_deliver_page(MENU_ACCESS_DENIED); } /** @@ -2592,6 +2556,182 @@ function l($text, $path, array $options = array()) { } /** + * Deliver a page callback result to the browser in the format appropriate. + * + * This function is most commonly called by menu_execute_active_handler(), but + * can also be called by error conditions such as drupal_not_found(), + * drupal_access_denied(), and drupal_site_offline(). + * + * When a user requests a page, index.php calls menu_execute_active_handler() + * which calls the 'page callback' function registered in hook_menu(). The page + * callback function can return one of: + * - NULL: to indicate no content + * - an integer menu status constant: to indicate an error condition + * - a string of HTML content + * - a renderable array of content + * Returning a renderable array rather than a string of HTML is preferred, + * because that provides modules with more flexibility in customizing the final + * result. + * + * When the page callback returns its constructed content to + * menu_execute_active_handler(), this functions gets called. The purpose of + * this function is to determine the most appropriate 'delivery callback' + * function to route the content to. The delivery callback function then + * sends the content to the browser in the needed format. The default delivery + * callback is drupal_deliver_html_page() which delivers the content as an HTML + * page, complete with blocks in addition to the content. This default can be + * overridden on a per menu item basis by setting 'delivery callback' in + * hook_menu(), hook_menu_alter(), or hook_menu_active_handler_alter(). + * Additionally, modules may use hook_page_delivery_callback_alter() to specify + * a different delivery callback to use for the page request. + * + * For example, the same page callback function can be used for an HTML + * version of the page and an AJAX version of the page. The page callback + * function just needs to decide what content is to be returned and the + * delivery callback function will send it as an HTML page or an AJAX + * response, as appropriate. + * + * In order for page callbacks to be reusable in different delivery formats, + * they should not issue any "print" or "echo" statements, but instead just + * return content. + * + * @param $page_callback_result + * The result of a page callback. Can be one of: + * - NULL: to indicate no content. + * - An integer menu status constant: to indicate an error condition. + * - A string of HTML content. + * - A renderable array of content. + * @param $default_delivery_callback + * (Optional) If given, it is the name of a delivery function most likely + * to be appropriate for the page request as determined by the calling + * function (e.g., menu_execute_active_handler()). If not given, it is + * determined from the menu router information of the current page. In either + * case, modules have a final chance to alter which function is called. + * + * @see menu_execute_active_handler() + * @see hook_menu() + * @see hook_menu_alter() + * @see hook_menu_active_handler_alter() + * @see hook_page_delivery_callback_alter() + */ +function drupal_deliver_page($page_callback_result, $default_delivery_callback = NULL) { + if (!isset($default_delivery_callback) && ($router_item = menu_get_item())) { + drupal_alter('menu_active_handler', $router_item); + $default_delivery_callback = $router_item['delivery_callback']; + } + $delivery_callback = !empty($default_delivery_callback) ? $default_delivery_callback : 'drupal_deliver_html_page'; + // Give modules a final chance to alter the delivery callback used. This is + // for modules that need to decide which delivery callback to use based on + // information made available during page callback execution and for pages + // without router items. + drupal_alter('page_delivery_callback', $delivery_callback); + if (function_exists($delivery_callback)) { + $delivery_callback($page_callback_result); + } + else { + // If a delivery callback is specified, but doesn't exist as a function, + // something is wrong, but don't print anything, since it's not known + // what format the response needs to be in. + watchdog('delivery callback not found', check_plain($delivery_callback) . ': ' . check_plain($_GET['q']), NULL, WATCHDOG_ERROR); + } +} + +/** + * Package and send the result of a page callback to the browser as a normal + * HTML page. + * + * @param $page_callback_result + * The result of a page callback. Can be one of: + * - NULL: to indicate no content. + * - An integer menu status constant: to indicate an error condition. + * - A string of HTML content. + * - A renderable array of content. + * + * @see drupal_deliver_page + */ +function drupal_deliver_html_page($page_callback_result) { + // Menu status constants are integers; page content is a string or array. + if (is_int($page_callback_result)) { + // @todo: Break these up into separate functions? + switch ($page_callback_result) { + case MENU_NOT_FOUND: + // Print a 404 page. + drupal_add_http_header('404 Not Found'); + + watchdog('page not found', check_plain($_GET['q']), NULL, WATCHDOG_WARNING); + + // Keep old path for reference, and to allow forms to redirect to it. + if (!isset($_GET['destination'])) { + $_GET['destination'] = $_GET['q']; + } + + $path = drupal_get_normal_path(variable_get('site_404', '')); + if ($path && $path != $_GET['q']) { + // 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, FALSE); + } + + 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.'); + } + + drupal_set_page_content($return); + $page = element_info('page'); + print drupal_render_page($page); + break; + + case MENU_ACCESS_DENIED: + // Print a 403 page. + drupal_add_http_header('403 Forbidden'); + watchdog('access denied', check_plain($_GET['q']), NULL, WATCHDOG_WARNING); + + // Keep old path for reference, and to allow forms to redirect to it. + if (!isset($_GET['destination'])) { + $_GET['destination'] = $_GET['q']; + } + + $path = drupal_get_normal_path(variable_get('site_403', '')); + if ($path && $path != $_GET['q']) { + // 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, FALSE); + } + + 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 drupal_render_page($return); + break; + + case MENU_SITE_OFFLINE: + // Print a 503 page. + drupal_maintenance_theme(); + drupal_add_http_header('503 Service unavailable'); + drupal_set_title(t('Site under maintenance')); + print theme('maintenance_page', array('content' => filter_xss_admin(variable_get('maintenance_mode_message', + t('@site is currently under maintenance. We should be back shortly. Thank you for your patience.', array('@site' => variable_get('site_name', 'Drupal'))))))); + break; + } + } + elseif (isset($page_callback_result)) { + // Print anything besides a menu constant, assuming it's not NULL or + // undefined. + print drupal_render_page($page_callback_result); + } + + // Perform end-of-request tasks. + drupal_page_footer(); +} + +/** * Perform end-of-request tasks. * * This function sets the page cache if appropriate, and allows modules to diff --git a/includes/menu.inc b/includes/menu.inc index f14b7aa6b..05cdf7771 100644 --- a/includes/menu.inc +++ b/includes/menu.inc @@ -401,29 +401,56 @@ function menu_get_item($path = NULL, $router_item = NULL) { } /** - * Execute the page callback associated with the current path - */ -function menu_execute_active_handler($path = NULL) { + * Execute the page callback associated with the current path. + * + * @param $path + * The drupal path whose handler is to be be executed. If set to NULL, then + * the current path is used. + * @param $deliver + * (optional) A boolean to indicate whether the content should be sent to the + * browser using the appropriate delivery callback (TRUE) or whether to return + * the result to the caller (FALSE). + */ +function menu_execute_active_handler($path = NULL, $deliver = TRUE) { if (_menu_site_is_offline()) { - return MENU_SITE_OFFLINE; + $page_callback_result = MENU_SITE_OFFLINE; } - // Rebuild if we know it's needed, or if the menu masks are missing which - // occurs rarely, likely due to a race condition of multiple rebuilds. - if (variable_get('menu_rebuild_needed', FALSE) || !variable_get('menu_masks', array())) { - menu_rebuild(); - } - if ($router_item = menu_get_item($path)) { - if ($router_item['access']) { - if ($router_item['file']) { - require_once DRUPAL_ROOT . '/' . $router_item['file']; + else { + // Rebuild if we know it's needed, or if the menu masks are missing which + // occurs rarely, likely due to a race condition of multiple rebuilds. + if (variable_get('menu_rebuild_needed', FALSE) || !variable_get('menu_masks', array())) { + menu_rebuild(); + } + if ($router_item = menu_get_item($path)) { + // hook_menu_alter() lets modules control menu router information that + // doesn't depend on the details of a particular page request. + // Here, we want to give modules a chance to use request-time information + // to make alterations just for this request. + drupal_alter('menu_active_handler', $router_item, $path); + if ($router_item['access']) { + if ($router_item['file']) { + require_once DRUPAL_ROOT . '/' . $router_item['file']; + } + $page_callback_result = call_user_func_array($router_item['page_callback'], $router_item['page_arguments']); + } + else { + $page_callback_result = MENU_ACCESS_DENIED; } - return call_user_func_array($router_item['page_callback'], $router_item['page_arguments']); } else { - return MENU_ACCESS_DENIED; + $page_callback_result = MENU_NOT_FOUND; } } - return MENU_NOT_FOUND; + + // Deliver the result of the page callback to the browser, or if requested, + // return it raw, so calling code can do more processing. + if ($deliver) { + $default_delivery_callback = (isset($router_item) && $router_item) ? $router_item['delivery_callback'] : NULL; + drupal_deliver_page($page_callback_result, $default_delivery_callback); + } + else { + return $page_callback_result; + } } /** @@ -935,6 +962,7 @@ function menu_tree_all_data($menu_name, $link = NULL, $max_depth = NULL) { 'access_arguments', 'page_callback', 'page_arguments', + 'delivery_callback', 'title', 'title_callback', 'title_arguments', @@ -1118,6 +1146,7 @@ function menu_tree_page_data($menu_name, $max_depth = NULL) { 'access_arguments', 'page_callback', 'page_arguments', + 'delivery_callback', 'title', 'title_callback', 'title_arguments', @@ -2833,6 +2862,10 @@ function _menu_router_build($callbacks) { $item['file path'] = $parent['file path']; } } + // Same for delivery callbacks. + if (!isset($item['delivery callback']) && isset($parent['delivery callback'])) { + $item['delivery callback'] = $parent['delivery callback']; + } // Same for theme callbacks. if (!isset($item['theme callback']) && isset($parent['theme callback'])) { $item['theme callback'] = $parent['theme callback']; @@ -2858,6 +2891,7 @@ function _menu_router_build($callbacks) { 'access callback' => '', 'page arguments' => array(), 'page callback' => '', + 'delivery callback' => '', 'block callback' => '', 'title arguments' => array(), 'title callback' => 't', @@ -2905,6 +2939,7 @@ function _menu_router_save($menu, $masks) { 'access_arguments', 'page_callback', 'page_arguments', + 'delivery_callback', 'fit', 'number_parts', 'tab_parent', @@ -2932,6 +2967,7 @@ function _menu_router_save($menu, $masks) { 'access_arguments' => serialize($item['access arguments']), 'page_callback' => $item['page callback'], 'page_arguments' => serialize($item['page arguments']), + 'delivery_callback' => $item['delivery callback'], 'fit' => $item['_fit'], 'number_parts' => $item['_number_parts'], 'tab_parent' => $item['tab_parent'], @@ -19,25 +19,4 @@ define('DRUPAL_ROOT', getcwd()); 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 or array. -if (is_int($return)) { - switch ($return) { - case MENU_NOT_FOUND: - drupal_not_found(); - break; - case MENU_ACCESS_DENIED: - drupal_access_denied(); - break; - case MENU_SITE_OFFLINE: - drupal_site_offline(); - break; - } -} -elseif (isset($return)) { - // Print anything besides a menu constant, assuming it's not NULL or undefined. - print drupal_render_page($return); -} - -drupal_page_footer(); +menu_execute_active_handler(); diff --git a/modules/book/book.module b/modules/book/book.module index 1daf7d657..eb76ed408 100644 --- a/modules/book/book.module +++ b/modules/book/book.module @@ -165,6 +165,7 @@ function book_menu() { ); $items['book/js/form'] = array( 'page callback' => 'book_form_update', + 'delivery callback' => 'ajax_deliver', 'access arguments' => array('access content'), 'type' => MENU_CALLBACK, 'file' => 'book.pages.inc', @@ -1214,7 +1215,7 @@ function book_menu_subtree_data($link) { $menu_router_alias = $query->join('menu_router', 'm', 'm.path = ml.router_path'); $book_alias = $query->join('book', 'b', 'ml.mlid = b.mlid'); $query->fields($book_alias); - $query->fields($menu_router_alias, array('load_functions', 'to_arg_functions', 'access_callback', 'access_arguments', 'page_callback', 'page_arguments', 'title', 'title_callback', 'title_arguments', 'type')); + $query->fields($menu_router_alias, array('load_functions', 'to_arg_functions', 'access_callback', 'access_arguments', 'page_callback', 'page_arguments', 'delivery_callback', 'title', 'title_callback', 'title_arguments', 'type')); $query->fields('ml'); $query->condition('menu_name', $link['menu_name']); for ($i = 1; $i <= MENU_MAX_DEPTH && $link["p$i"]; ++$i) { diff --git a/modules/book/book.pages.inc b/modules/book/book.pages.inc index 0162fa69e..1e360ab8c 100644 --- a/modules/book/book.pages.inc +++ b/modules/book/book.pages.inc @@ -255,5 +255,8 @@ function book_form_update() { $commands[] = ajax_command_replace(NULL, drupal_render($form['book']['plid'])); } - ajax_render($commands); + // @todo: We could and should just return $form['book']['plid'] and skip the + // ajax_command_replace() above. But for now, this provides a test case of + // returning an AJAX commands array. + return array('#type' => 'ajax_commands', '#ajax_commands' => $commands); } diff --git a/modules/comment/comment.module b/modules/comment/comment.module index 431a9daaf..073e4c74a 100644 --- a/modules/comment/comment.module +++ b/modules/comment/comment.module @@ -364,7 +364,7 @@ function comment_permalink($comment) { drupal_add_link(array('rel' => 'canonical', 'href' => url('node/' . $node->nid))); // Return the node view, this will show the correct comment in context. - return menu_execute_active_handler('node/' . $node->nid); + return menu_execute_active_handler('node/' . $node->nid, FALSE); } drupal_not_found(); } diff --git a/modules/file/file.module b/modules/file/file.module index 9928b1887..fa8330af4 100644 --- a/modules/file/file.module +++ b/modules/file/file.module @@ -17,11 +17,13 @@ function file_menu() { $items['file/ajax'] = array( 'page callback' => 'file_ajax_upload', + 'delivery callback' => 'ajax_deliver', 'access arguments' => array('access content'), 'type' => MENU_CALLBACK, ); $items['file/progress'] = array( 'page callback' => 'file_ajax_progress', + 'delivery callback' => 'ajax_deliver', 'access arguments' => array('access content'), 'type' => MENU_CALLBACK, ); diff --git a/modules/menu/menu.admin.inc b/modules/menu/menu.admin.inc index 9ae47984a..92f78a818 100644 --- a/modules/menu/menu.admin.inc +++ b/modules/menu/menu.admin.inc @@ -44,7 +44,7 @@ function menu_overview_form($form, &$form_state, $menu) { global $menu_admin; $form['#attached']['css'] = array(drupal_get_path('module', 'menu') . '/menu.css'); $sql = " - SELECT m.load_functions, m.to_arg_functions, m.access_callback, m.access_arguments, m.page_callback, m.page_arguments, m.title, m.title_callback, m.title_arguments, m.type, m.description, ml.* + SELECT m.load_functions, m.to_arg_functions, m.access_callback, m.access_arguments, m.page_callback, m.page_arguments, m.delivery_callback, m.title, m.title_callback, m.title_arguments, m.type, m.description, ml.* FROM {menu_links} ml LEFT JOIN {menu_router} m ON m.path = ml.router_path WHERE ml.menu_name = :menu ORDER BY p1 ASC, p2 ASC, p3 ASC, p4 ASC, p5 ASC, p6 ASC, p7 ASC, p8 ASC, p9 ASC"; diff --git a/modules/menu/menu.api.php b/modules/menu/menu.api.php index 6a3405d9e..1339bf04d 100644 --- a/modules/menu/menu.api.php +++ b/modules/menu/menu.api.php @@ -163,6 +163,10 @@ * instead. * - "page arguments": An array of arguments to pass to the page callback * function, with path component substitution as described above. + * - "delivery callback": The function to call to package the result of the + * page callback function and send it to the browser. Defaults to + * drupal_deliver_html_page() unless a value is inherited from a parent menu + * item. * - "access callback": A function returning a boolean value that determines * whether the user has access rights to this menu item. Defaults to * user_access() unless a value is inherited from a parent menu item. diff --git a/modules/poll/poll.module b/modules/poll/poll.module index 95c29ce05..373ee0d03 100644 --- a/modules/poll/poll.module +++ b/modules/poll/poll.module @@ -382,9 +382,7 @@ function _poll_choice_form($key, $chid = NULL, $value = '', $votes = 0, $weight * Menu callback for AHAH additions. Render the new poll choices. */ function poll_choice_js($form, $form_state) { - $choice_form = $form['choice_wrapper']['choice']; - - return drupal_render($choice_form); + return $form['choice_wrapper']['choice']; } /** diff --git a/modules/system/system.api.php b/modules/system/system.api.php index d2a2bffcd..8826e6a08 100644 --- a/modules/system/system.api.php +++ b/modules/system/system.api.php @@ -2507,5 +2507,79 @@ function hook_date_formats_alter(&$formats) { } /** + * Alters the router item for the active menu handler. + * + * Called by menu_execute_active_handler() to allow modules to alter the + * information that will be used to handle the page request. Only use this + * hook if an alteration specific to the page request is needed. Otherwise + * use hook_menu_alter(). + * + * @param $router_item + * An array with the following keys: + * - access: Boolean. Whether the user is allowed to see this page. + * - file: A path to a file to include prior to invoking the page callback. + * - page_callback: The function to call to build the page content. + * - page_arguments: Arguments to pass to the page callback. + * - delivery_callback: The function to call to deliver the result of the + * page callback to the browser. + * @param $path + * The drupal path that was used for retrieving the router item. + * + * @see menu_execute_active_handler() + * @see hook_menu() + * @see hook_menu_alter() + */ +function hook_menu_active_handler_alter(&$router_item, $path = NULL) { + // Turn off access for all pages for all users. + $router_item['access'] = FALSE; +} + +/** + * Alters the delivery callback used to send the result of the page callback to the browser. + * + * Called by drupal_deliver_page() to allow modules to alter how the + * page is delivered to the browser. + * + * This hook is intended for altering the delivery callback based on + * information unrelated to the path of the page accessed. For example, + * it can be used to set the delivery callback based on a HTTP request + * header (as shown in the code sample). To specify a delivery callback + * based on path information, use hook_menu(), hook_menu_alter() or + * hook_menu_active_handler_alter(). + * + * This hook can also be used as an API function that can be used to explicitly + * set the delivery callback from some other function. For example, for a module + * named MODULE: + * @code + * function MODULE_page_delivery_callback_alter(&$callback, $set = FALSE) { + * static $stored_callback; + * if ($set) { + * $stored_callback = $callback; + * } + * elseif (isset($stored_callback)) { + * $callback = $stored_callback; + * } + * } + * function SOMEWHERE_ELSE() { + * $desired_delivery_callback = 'foo'; + * MODULE_page_delivery_callback_alter($desired_delivery_callback, TRUE); + * } + * @endcode + * + * @param $callback + * The name of a function. + * + * @see drupal_deliver_page() + */ +function hook_page_delivery_callback_alter(&$callback) { + // jQuery sets a HTTP_X_REQUESTED_WITH header of 'XMLHttpRequest'. + // If a page would normally be delivered as an html page, and it is called + // from jQuery, deliver it instead as an AJAX response. + if (isset($_SERVER['HTTP_X_REQUESTED_WITH']) && $_SERVER['HTTP_X_REQUESTED_WITH'] == 'XMLHttpRequest' && $callback == 'drupal_deliver_html_page') { + $callback = 'ajax_deliver'; + } +} + +/** * @} End of "addtogroup hooks". */ diff --git a/modules/system/system.install b/modules/system/system.install index 47bc3a968..3c3fc0c71 100644 --- a/modules/system/system.install +++ b/modules/system/system.install @@ -1005,6 +1005,13 @@ function system_schema() { 'type' => 'text', 'not null' => FALSE, ), + 'delivery_callback' => array( + 'description' => 'The name of the function that sends the result of the page_callback function to the browser.', + 'type' => 'varchar', + 'length' => 255, + 'not null' => TRUE, + 'default' => '', + ), 'fit' => array( 'description' => 'A numeric representation of how specific the path is.', 'type' => 'int', @@ -2726,6 +2733,14 @@ function system_update_7040() { } /** + * Adds 'delivery_callback' field to the {menu_router} table to allow a custom + * function to be used for final page rendering and sending to browser. + */ +function system_update_7041() { + db_add_field('menu_router', 'delivery_callback', array('type' => 'varchar', 'length' => 255, 'not null' => TRUE, 'default' => '')); +} + +/** * @} End of "defgroup updates-6.x-to-7.x" * The next series of updates should start at 8000. */ diff --git a/modules/system/system.module b/modules/system/system.module index 98ceebc0f..70c789f8b 100644 --- a/modules/system/system.module +++ b/modules/system/system.module @@ -314,6 +314,13 @@ function system_element_info() { '#attributes' => array(), '#items' => array(), ); + // By default, we don't want AJAX commands being rendered in the context of an + // HTML page, so we don't provide defaults for #theme or #theme_wrappers. + // However, modules can set these properties (for example, to provide an HTML + // debugging page that displays rather than executes AJAX commands). + $types['ajax_commands'] = array( + '#ajax_commands' => array(), + ); // Input elements. $types['submit'] = array( @@ -499,6 +506,7 @@ function system_menu() { $items['system/ajax'] = array( 'title' => 'AHAH callback', 'page callback' => 'ajax_form_callback', + 'delivery callback' => 'ajax_deliver', 'access callback' => TRUE, 'type' => MENU_CALLBACK, 'file path' => 'includes', @@ -1752,7 +1760,7 @@ function system_admin_menu_block($item) { $default_task = NULL; $has_subitems = FALSE; $result = db_query(" - SELECT m.load_functions, m.to_arg_functions, m.access_callback, m.access_arguments, m.page_callback, m.page_arguments, m.title, m.title_callback, m.title_arguments, m.theme_callback, m.theme_arguments, m.type, m.description, m.path, m.weight as router_weight, ml.* + SELECT m.load_functions, m.to_arg_functions, m.access_callback, m.access_arguments, m.page_callback, m.page_arguments, m.delivery_callback, m.title, m.title_callback, m.title_arguments, m.theme_callback, m.theme_arguments, m.type, m.description, m.path, m.weight as router_weight, ml.* FROM {menu_router} m LEFT JOIN {menu_links} ml ON m.path = ml.router_path WHERE (ml.plid = :plid AND ml.menu_name = :name AND hidden = 0) OR (m.tab_parent = :path AND m.type IN (:local_task, :default_task))", array(':plid' => $item['mlid'], ':name' => $item['menu_name'], ':path' => $item['path'], ':local_task' => MENU_LOCAL_TASK, ':default_task' => MENU_DEFAULT_LOCAL_TASK), array('fetch' => PDO::FETCH_ASSOC)); @@ -2468,7 +2476,7 @@ function system_get_module_admin_tasks($module) { if (empty($items)) { $result = db_query(" - SELECT m.load_functions, m.to_arg_functions, m.access_callback, m.access_arguments, m.page_callback, m.page_arguments, m.title, m.title_callback, m.title_arguments, m.theme_callback, m.theme_arguments, m.type, ml.* + SELECT m.load_functions, m.to_arg_functions, m.access_callback, m.access_arguments, m.page_callback, m.page_arguments, m.delivery_callback, m.title, m.title_callback, m.title_arguments, m.theme_callback, m.theme_arguments, m.type, ml.* FROM {menu_links} ml INNER JOIN {menu_router} m ON ml.router_path = m.path WHERE ml.link_path LIKE 'admin/%' AND hidden >= 0 AND module = 'system' AND m.number_parts > 2", array(), array('fetch' => PDO::FETCH_ASSOC)); foreach ($result as $item) { _menu_link_translate($item); diff --git a/modules/user/user.pages.inc b/modules/user/user.pages.inc index 1f94e421d..eef7d3f62 100644 --- a/modules/user/user.pages.inc +++ b/modules/user/user.pages.inc @@ -500,7 +500,7 @@ function user_page() { global $user; if ($user->uid) { menu_set_active_item('user/' . $user->uid); - return menu_execute_active_handler(); + return menu_execute_active_handler(NULL, FALSE); } else { return drupal_get_form('user_login'); |