Index: includes/menu.inc =================================================================== RCS file: /cvs/drupal/drupal/includes/menu.inc,v retrieving revision 1.358 diff -u -p -r1.358 menu.inc --- includes/menu.inc 1 Nov 2009 23:02:13 -0000 1.358 +++ includes/menu.inc 4 Nov 2009 03:41:38 -0000 @@ -1796,16 +1796,26 @@ function menu_local_tasks($level = 0) { * If the path "node/123" is passed to this function, then it will return the * links for 'edit' and 'report-as-spam'. * + * @param $module + * The name of the implementing module. This is used to prefix the key for + * each contextual link, which is transformed into a CSS class during + * rendering by theme_links(). For example, if $module is 'block' and the + * retrieved local task path argument is 'edit', then the resulting CSS class + * will be 'block-edit'. * @param $path - * The menu router path of the object to retrieve local tasks for, for example - * "node/123" or "admin/structure/menu/manage/[menu_name]". + * The static menu router path of the object to retrieve local tasks for, for + * example 'node' or 'admin/structure/block/manage'. + * @param $args + * A list of of dynamic path arguments to append to $path to form the fully- + * qualified menu router path, for example array(123) for a certain node or + * array('system', 'navigation') for a certain block. * * @return * A list of menu router items that are local tasks for the passed in path. * * @see system_preprocess() */ -function menu_contextual_links($parent_path, $args) { +function menu_contextual_links($module, $parent_path, $args) { static $path_empty = array(); $links = array(); @@ -1851,12 +1861,9 @@ function menu_contextual_links($parent_p if (!$item['access']) { continue; } - // All contextual links are keyed by the actual "task" path argument. The - // menu system does not allow for two local tasks with the same name, and - // since the key is also used as CSS class for the link item, which may be - // styled as icon, it wouldn't make sense to display the same icon for - // different tasks. - $links[$key] = $item; + // All contextual links are keyed by the actual "task" path argument, + // prefixed with the name of implementing module. + $links[$module . '-' . $key] = $item; } // Allow modules to alter contextual links. Index: includes/update.inc =================================================================== RCS file: /cvs/drupal/drupal/includes/update.inc,v retrieving revision 1.18 diff -u -p -r1.18 update.inc --- includes/update.inc 24 Oct 2009 23:19:21 -0000 1.18 +++ includes/update.inc 4 Nov 2009 03:41:38 -0000 @@ -140,6 +140,8 @@ function update_fix_d7_requirements() { 'not null' => TRUE, 'default' => 0, )); + db_drop_index('menu_router', 'tab_parent'); + db_add_index('menu_router', 'tab_parent', array(array('tab_parent', 64), 'weight', 'title')); db_add_field('menu_router', 'theme_callback', array('type' => 'varchar', 'length' => 255, 'not null' => TRUE, 'default' => '')); db_add_field('menu_router', 'theme_arguments', array('type' => 'varchar', 'length' => 255, 'not null' => TRUE, 'default' => '')); Index: modules/block/block.module =================================================================== RCS file: /cvs/drupal/drupal/modules/block/block.module,v retrieving revision 1.394 diff -u -p -r1.394 block.module --- modules/block/block.module 23 Oct 2009 22:24:11 -0000 1.394 +++ modules/block/block.module 4 Nov 2009 03:41:38 -0000 @@ -283,7 +283,7 @@ function _block_get_renderable_array($li // Add contextual links for this block; skipping the system main block. if ($key != 'system_main') { - $build[$key]['#contextual_links']['block'] = menu_contextual_links('admin/structure/block/manage', array($block->module, $block->delta)); + $build[$key]['#contextual_links']['block'] = array('admin/structure/block/manage', array($block->module, $block->delta)); } $build[$key] += array( Index: modules/book/book.module =================================================================== RCS file: /cvs/drupal/drupal/modules/book/book.module,v retrieving revision 1.522 diff -u -p -r1.522 book.module --- modules/book/book.module 3 Nov 2009 06:47:22 -0000 1.522 +++ modules/book/book.module 4 Nov 2009 05:47:42 -0000 @@ -1087,8 +1087,10 @@ function book_export_traverse($tree, $vi * The HTML generated for the given node. */ function book_node_export(stdClass $node, $children = '') { - node_build_content($node, 'print'); - $node->rendered = drupal_render($node->content); + $build = node_build($node, 'print'); + unset($build['#theme']); + unset($build['#theme_wrappers']); + $node->rendered = drupal_render($build); return theme('book_node_export_html', array('node' => $node, 'children' => $children)); } Index: modules/comment/comment.api.php =================================================================== RCS file: /cvs/drupal/drupal/modules/comment/comment.api.php,v retrieving revision 1.12 diff -u -p -r1.12 comment.api.php --- modules/comment/comment.api.php 10 Oct 2009 13:37:09 -0000 1.12 +++ modules/comment/comment.api.php 4 Nov 2009 03:41:38 -0000 @@ -73,6 +73,34 @@ function hook_comment_view($comment) { } /** + * The comment was built, the module may modify the structured content. + * + * This hook is called after the content has been assembled in a structured array + * and may be used for doing processing which requires that the complete comment + * content structure has been built. + * + * If the module wishes to act on the rendered HTML of the comment rather than the + * structured content array, it may use this hook to add a #post_render callback. + * Alternatively, it could also implement hook_preprocess_comment(). See + * drupal_render() and theme() documentation respectively for details. + * + * @param $build + * A renderable array representing the comment. + * + * @see comment_build() + */ +function hook_comment_build_alter($build) { + // Check for the existence of a field added by another module. + if ($build['#build_mode'] == 'full' && isset($build['an_additional_field'])) { + // Change its weight. + $build['an_additional_field']['#weight'] = -10; + } + + // Add a #post_render callback to act on the rendered HTML of the comment. + $build['#post_render'][] = 'my_module_comment_post_render'; +} + +/** * The comment is being published by the moderator. * * @param $comment Index: modules/comment/comment.module =================================================================== RCS file: /cvs/drupal/drupal/modules/comment/comment.module,v retrieving revision 1.800 diff -u -p -r1.800 comment.module --- modules/comment/comment.module 3 Nov 2009 06:47:22 -0000 1.800 +++ modules/comment/comment.module 4 Nov 2009 03:41:38 -0000 @@ -794,8 +794,6 @@ function comment_build($comment, stdClas '#node' => $node, '#build_mode' => $build_mode, ); - // Add contextual links for this comment. - $build['#contextual_links']['comment'] = menu_contextual_links('comment', array($comment->cid)); $prefix = ''; $is_threaded = isset($comment->divs) && variable_get('comment_default_mode_' . $node->type, COMMENT_MODE_THREADED) == COMMENT_MODE_THREADED; @@ -819,6 +817,9 @@ function comment_build($comment, stdClas $build['#suffix'] = str_repeat('', $comment->divs_final); } + // Allow modules to modify the structured comment. + drupal_alter('comment_build', $build); + return $build; } @@ -857,9 +858,6 @@ function comment_build_content($comment, // Allow modules to make their own additions to the comment. module_invoke_all('comment_view', $comment, $build_mode); - - // Allow modules to modify the structured comment. - drupal_alter('comment_build', $comment, $build_mode); } /** Index: modules/menu/menu.module =================================================================== RCS file: /cvs/drupal/drupal/modules/menu/menu.module,v retrieving revision 1.214 diff -u -p -r1.214 menu.module --- modules/menu/menu.module 1 Nov 2009 12:11:10 -0000 1.214 +++ modules/menu/menu.module 4 Nov 2009 03:41:38 -0000 @@ -427,7 +427,7 @@ function menu_block_view($delta = '') { $data['content'] = menu_tree($delta); // Add contextual links for this block. if (!empty($data['content'])) { - $data['content']['#contextual_links']['menu'] = menu_contextual_links('admin/structure/menu/manage', array($delta)); + $data['content']['#contextual_links']['menu'] = array('admin/structure/menu/manage', array($delta)); } return $data; } @@ -440,7 +440,7 @@ function menu_block_view_alter(&$data, $ if ($block->module == 'system' && !empty($data['content'])) { $system_menus = menu_list_system_menus(); if (isset($system_menus[$block->delta])) { - $data['content']['#contextual_links']['menu'] = menu_contextual_links('admin/structure/menu/manage', array($block->delta)); + $data['content']['#contextual_links']['menu'] = array('admin/structure/menu/manage', array($block->delta)); } } } Index: modules/node/node.api.php =================================================================== RCS file: /cvs/drupal/drupal/modules/node/node.api.php,v retrieving revision 1.42 diff -u -p -r1.42 node.api.php --- modules/node/node.api.php 1 Nov 2009 12:11:10 -0000 1.42 +++ modules/node/node.api.php 4 Nov 2009 03:41:38 -0000 @@ -542,7 +542,7 @@ function hook_node_view(stdClass $node, /** * The node content was built, the module may modify the structured content. * - * This hook is called after the content has been assembled in $node->content + * This hook is called after the content has been assembled in a structured array * and may be used for doing processing which requires that the complete node * content structure has been built. * @@ -551,20 +551,19 @@ function hook_node_view(stdClass $node, * Alternatively, it could also implement hook_preprocess_node(). See * drupal_render() and theme() documentation respectively for details. * - * @param $node - * The node the action is being performed on. - * @param $build_mode - * The $build_mode parameter from node_build(). + * @param $build + * A renderable array representing the node content. + * + * @see node_build() */ -function hook_node_build_alter(stdClass $node, $build_mode) { - // Check for the existence of a field added by another module. - if (isset($node->content['an_additional_field'])) { +function hook_node_build_alter($build) { + if ($build['#build_mode'] == 'full' && isset($build['an_additional_field'])) { // Change its weight. - $node->content['an_additional_field']['#weight'] = -10; + $build['an_additional_field']['#weight'] = -10; } // Add a #post_render callback to act on the rendered HTML of the node. - $node->content['#post_render'][] = 'my_module_node_post_render'; + $build['#post_render'][] = 'my_module_node_post_render'; } /** Index: modules/node/node.module =================================================================== RCS file: /cvs/drupal/drupal/modules/node/node.module,v retrieving revision 1.1161 diff -u -p -r1.1161 node.module --- modules/node/node.module 3 Nov 2009 06:47:22 -0000 1.1161 +++ modules/node/node.module 4 Nov 2009 06:07:15 -0000 @@ -1148,7 +1148,10 @@ function node_build($node, $build_mode = '#build_mode' => $build_mode, ); // Add contextual links for this node. - $build['#contextual_links']['node'] = menu_contextual_links('node', array($node->nid)); + $build['#contextual_links']['node'] = array('node', array($node->nid)); + + // Allow modules to modify the structured node. + drupal_alter('node_build', $build); return $build; } @@ -1210,9 +1213,6 @@ function node_build_content(stdClass $no // Allow modules to make their own additions to the node. module_invoke_all('node_view', $node, $build_mode); - - // Allow modules to modify the structured node. - drupal_alter('node_build', $node, $build_mode); } /** @@ -1544,8 +1544,10 @@ function node_search_execute($keys = NUL foreach ($find as $item) { // Render the node. $node = node_load($item->sid); - node_build_content($node, 'search_result'); - $node->rendered = drupal_render($node->content); + $build = node_build($node, 'search_result'); + unset($build['#theme']); + unset($build['#theme_wrappers']); + $node->rendered = drupal_render($build); // Fetch comments for snippet. $node->rendered .= ' ' . module_invoke('comment', 'node_update_index', $node); @@ -2033,16 +2035,18 @@ function node_feed($nids = FALSE, $chann // The node gets built and modules add to or modify $node->rss_elements // and $node->rss_namespaces. - node_build_content($node, 'rss'); + $build = node_build($node, 'rss'); + unset($build['#theme']); + unset($build['#theme_wrappers']); if (!empty($node->rss_namespaces)) { $namespaces = array_merge($namespaces, $node->rss_namespaces); } - if ($item_length != 'title' && !empty($node->content)) { + if ($item_length != 'title') { // We render node contents and force links to be last. - $links = drupal_render($node->content['links']); - $item_text .= drupal_render($node->content) . $links; + $build['links']['#weight'] = 1000; + $item_text .= drupal_render($build); } $items .= format_rss_item($node->title[FIELD_LANGUAGE_NONE][0]['value'], $node->link, $item_text, $node->rss_elements); @@ -2186,8 +2190,10 @@ function _node_index_node(stdClass $node variable_set('node_cron_last', $node->changed); // Render the node. - node_build_content($node, 'search_index'); - $node->rendered = drupal_render($node->content); + $build = node_build($node, 'search_index'); + unset($build['#theme']); + unset($build['#theme_wrappers']); + $node->rendered = drupal_render($build); $text = '

' . check_plain($node->title[FIELD_LANGUAGE_NONE][0]['value']) . '

' . $node->rendered; Index: modules/system/system.install =================================================================== RCS file: /cvs/drupal/drupal/modules/system/system.install,v retrieving revision 1.415 diff -u -p -r1.415 system.install --- modules/system/system.install 24 Oct 2009 23:19:21 -0000 1.415 +++ modules/system/system.install 4 Nov 2009 03:41:38 -0000 @@ -1097,7 +1097,7 @@ function system_schema() { ), 'indexes' => array( 'fit' => array('fit'), - 'tab_parent' => array('tab_parent'), + 'tab_parent' => array(array('tab_parent', 64), 'weight', 'title'), 'tab_root_weight_title' => array(array('tab_root', 64), 'weight', 'title'), ), 'primary key' => array('path'), Index: modules/system/system.module =================================================================== RCS file: /cvs/drupal/drupal/modules/system/system.module,v retrieving revision 1.836 diff -u -p -r1.836 system.module --- modules/system/system.module 3 Nov 2009 06:47:23 -0000 1.836 +++ modules/system/system.module 4 Nov 2009 03:41:38 -0000 @@ -217,6 +217,10 @@ function system_permission() { 'title' => t('Access administration pages'), 'description' => t('View the administration panel and browse the help system.'), ), + 'access contextual links' => array( + 'title' => t('Access contextual links'), + 'description' => t('Use administrative links associated with items on a page.'), + ), 'access site in maintenance mode' => array( 'title' => t('Access site in maintenance mode'), 'description' => t('Log in when the site is in maintenance mode.'), @@ -3516,17 +3520,24 @@ function theme_system_settings_form($var /** * Template variable preprocessor for contextual links. + * + * @see system_build_contextual_links() */ function system_preprocess(&$variables, $hook) { static $hooks; + // Initialize contextual links template variable. + $variables['contextual_links'] = array(); + + // Nothing to do here if the user is not permitted to access contextual links. + if (!user_access('access contextual links')) { + return; + } + if (!isset($hooks)) { $hooks = theme_get_registry(); } - // Initialize contextual links template variable. - $variables['contextual_links'] = array(); - // Determine the primary theme function argument. if (isset($hooks[$hook]['variables'])) { $keys = array_keys($hooks[$hook]['variables']); @@ -3539,7 +3550,7 @@ function system_preprocess(&$variables, $element = $variables[$key]; } - if (isset($element) && is_array($element) && isset($element['#contextual_links'])) { + if (isset($element) && is_array($element) && !empty($element['#contextual_links'])) { $variables['contextual_links'] = system_build_contextual_links($element); if (!empty($variables['contextual_links'])) { $variables['classes_array'][] = 'contextual-links-region'; @@ -3551,21 +3562,32 @@ function system_preprocess(&$variables, * Build a renderable array for contextual links. * * @param $element - * A renderable array containing a #contextual_links property. + * A renderable array containing a #contextual_links property, which is a + * keyed array. Each key is the name of the implementing module, and each + * value is an array that forms the function arguments for + * menu_contextual_links(). For example: + * @code + * array('#contextual_links' => array( + * 'block' => array('admin/structure/block/manage', array('system', 'navigation')), + * 'menu' => array('admin/structure/menu/manage', array('navigation')), + * )) + * @endcode * * @return * A renderable array representing contextual links. + * + * @see menu_contextual_links() */ function system_build_contextual_links($element) { static $destination; - // Transform contextual links into parameters suitable for theme_link(). - $items = call_user_func_array('array_merge_recursive', $element['#contextual_links']); - $build = array(); - if (empty($items)) { - return $build; + // Retrieve contextual menu links. + $items = array(); + foreach ($element['#contextual_links'] as $module => $args) { + $items += menu_contextual_links($module, $args[0], $args[1]); } + // Transform contextual links into parameters suitable for theme_link(). if (!isset($destination)) { $destination = drupal_get_destination(); } @@ -3584,6 +3606,7 @@ function system_build_contextual_links($ $item['localized_options']['query'] += $destination; $links[$class] += $item['localized_options']; } + $build = array(); if ($links) { $build = array( '#theme' => 'links', Index: modules/user/user.api.php =================================================================== RCS file: /cvs/drupal/drupal/modules/user/user.api.php,v retrieving revision 1.15 diff -u -p -r1.15 user.api.php --- modules/user/user.api.php 1 Nov 2009 21:26:44 -0000 1.15 +++ modules/user/user.api.php 4 Nov 2009 03:41:38 -0000 @@ -327,6 +327,34 @@ function hook_user_view($account) { } /** + * The user was built, the module may modify the structured content. + * + * This hook is called after the content has been assembled in a structured array + * and may be used for doing processing which requires that the complete user + * content structure has been built. + * + * If the module wishes to act on the rendered HTML of the user rather than the + * structured content array, it may use this hook to add a #post_render callback. + * Alternatively, it could also implement hook_preprocess_user_profile(). See + * drupal_render() and theme() documentation respectively for details. + * + * @param $build + * A renderable array representing the user. + * + * @see user_build() + */ +function hook_user_build_alter($build) { + // Check for the existence of a field added by another module. + if (isset($build['an_additional_field'])) { + // Change its weight. + $build['an_additional_field']['#weight'] = -10; + } + + // Add a #post_render callback to act on the rendered HTML of the user. + $build['#post_render'][] = 'my_module_comment_post_render'; +} + +/** * Inform other modules that a user role has been added. * * Modules implementing this hook can act on the user role object when saved to Index: modules/user/user.module =================================================================== RCS file: /cvs/drupal/drupal/modules/user/user.module,v retrieving revision 1.1075 diff -u -p -r1.1075 user.module --- modules/user/user.module 1 Nov 2009 21:26:44 -0000 1.1075 +++ modules/user/user.module 4 Nov 2009 03:41:38 -0000 @@ -2101,6 +2101,9 @@ function user_build($account) { '#account' => $account, ); + // Allow modules to modify the structured user. + drupal_alter('user_build', $build); + return $build; } @@ -2109,7 +2112,6 @@ function user_build($account) { * * @param $account * A user object. - * */ function user_build_content($account) { $account->content = array();