Commit a8ec4e1d authored by Dries's avatar Dries

- Patch #607244 by sun: added permission to decrease performance impact of contextual links.

parent 803bd4f9
......@@ -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_path, $args) {
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 the implementing module.
$links[$module . '-' . $key] = $item;
}
// Allow modules to alter contextual links.
......
......@@ -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' => ''));
......
......@@ -283,7 +283,7 @@ function _block_get_renderable_array($list = array()) {
// 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(
......
......@@ -1087,8 +1087,10 @@ function book_export_traverse($tree, $visit_func) {
* 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']);
// @todo Rendering should happen in the template using render().
$node->rendered = drupal_render($build);
return theme('book_node_export_html', array('node' => $node, 'children' => $children));
}
......
......@@ -72,6 +72,34 @@ function hook_comment_view($comment) {
$comment->time_ago = time() - $comment->changed;
}
/**
* 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.
*
......
......@@ -794,8 +794,6 @@ function comment_build($comment, stdClass $node, $build_mode = 'full') {
'#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, stdClass $node, $build_mode = 'full') {
$build['#suffix'] = str_repeat('</div>', $comment->divs_final);
}
// Allow modules to modify the structured comment.
drupal_alter('comment_build', $build);
return $build;
}
......@@ -844,6 +845,7 @@ function comment_build_content($comment, stdClass $node, $build_mode = 'full') {
'#markup' => check_markup($comment->comment, $comment->format, '', TRUE),
);
// Build fields content.
field_attach_prepare_view('comment', array($comment->cid => $comment), $build_mode);
$comment->content += field_attach_view('comment', $comment, $build_mode);
......@@ -857,9 +859,6 @@ function comment_build_content($comment, stdClass $node, $build_mode = 'full') {
// 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);
}
/**
......
......@@ -430,7 +430,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;
}
......@@ -443,7 +443,7 @@ function menu_block_view_alter(&$data, $block) {
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));
}
}
}
......
......@@ -540,9 +540,9 @@ function hook_node_view(stdClass $node, $build_mode) {
}
/**
* The node content was built, the module may modify the structured content.
* 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, $build_mode) {
* 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';
}
/**
......
......@@ -1148,7 +1148,11 @@ function node_build($node, $build_mode = 'full') {
'#build_mode' => $build_mode,
);
// Add contextual links for this node.
$build['#contextual_links']['node'] = menu_contextual_links('node', array($node->nid));
// @todo Make this configurable per build mode.
$build['#contextual_links']['node'] = array('node', array($node->nid));
// Allow modules to modify the structured node.
drupal_alter('node_build', $build);
return $build;
}
......@@ -1177,7 +1181,6 @@ function node_build($node, $build_mode = 'full') {
* A node object.
* @param $build_mode
* Build mode, e.g. 'full', 'teaser'...
*
*/
function node_build_content(stdClass $node, $build_mode = 'full') {
// Remove previously built content, if exists.
......@@ -1190,6 +1193,9 @@ function node_build_content(stdClass $node, $build_mode = 'full') {
}
// Build fields content.
// @todo field_attach_prepare_view() is only invoked by node_build_multiple(),
// all other entities invoke it _here_.
//field_attach_prepare_view('node', array($node->nid => $node), $build_mode);
$node->content += field_attach_view('node', $node, $build_mode);
// Always display a read more link on teasers because we have no way
......@@ -1210,9 +1216,6 @@ function node_build_content(stdClass $node, $build_mode = 'full') {
// 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);
}
/**
......@@ -1543,8 +1546,9 @@ function node_search_execute($keys = NULL) {
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']);
$node->rendered = drupal_render($build);
// Fetch comments for snippet.
$node->rendered .= ' ' . module_invoke('comment', 'node_update_index', $node);
......@@ -2032,16 +2036,17 @@ function node_feed($nids = FALSE, $channel = array()) {
// 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']);
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);
......@@ -2185,8 +2190,9 @@ 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']);
$node->rendered = drupal_render($build);
$text = '<h1>' . check_plain($node->title[FIELD_LANGUAGE_NONE][0]['value']) . '</h1>' . $node->rendered;
......
......@@ -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'),
......
......@@ -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 contextual links to perform actions related to elements 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($variables) {
/**
* Template variable preprocessor for contextual links.
*
* @see system_build_contextual_links()
*/
function system_preprocess(&$variables, $hook) {
static $hooks;
// Initialize the $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, $hook) {
$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, $hook) {
* 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($element) {
$item['localized_options']['query'] += $destination;
$links[$class] += $item['localized_options'];
}
$build = array();
if ($links) {
$build = array(
'#theme' => 'links',
......
......@@ -314,8 +314,10 @@ function hook_user_logout($account) {
*
* @param $account
* The user object on which the operation is being performed.
* @param $build_mode
* Build mode, e.g. 'full'.
*/
function hook_user_view($account) {
function hook_user_view($account, $build_mode) {
if (user_access('create blog content', $account)) {
$account->content['summary']['blog'] = array(
'#type' => 'user_profile_item',
......@@ -326,6 +328,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_user_post_render';
}
/**
* Inform other modules that a user role has been added.
*
......
......@@ -2084,13 +2084,15 @@ function _user_cancel($edit, $account, $method) {
*
* @param $account
* A user object.
* @param $build_mode
* Build mode, e.g. 'full'.
*
* @return
* An array as expected by drupal_render().
*/
function user_build($account) {
function user_build($account, $build_mode = 'full') {
// Retrieve all profile fields and attach to $account->content.
user_build_content($account);
user_build_content($account, $build_mode);
$build = $account->content;
// We don't need duplicate rendering info in account->content.
......@@ -2099,8 +2101,12 @@ function user_build($account) {
$build += array(
'#theme' => 'user_profile',
'#account' => $account,
'#build_mode' => $build_mode,
);
// Allow modules to modify the structured user.
drupal_alter('user_build', $build);
return $build;
}
......@@ -2109,19 +2115,19 @@ function user_build($account) {
*
* @param $account
* A user object.
*
* @param $build_mode
* Build mode, e.g. 'full'.
*/
function user_build_content($account) {
function user_build_content($account, $build_mode = 'full') {
// Remove previously built content, if exists.
$account->content = array();
$accounts = array($account->uid, $account);
field_attach_prepare_view('user', $accounts, 'full');
// Build fields content.
$account->content += field_attach_view('user', $account);
field_attach_prepare_view('user', array($account->uid, $account), $build_mode);
$account->content += field_attach_view('user', $account, $build_mode);
// Populate $account->content with a render() array.
module_invoke_all('user_view', $account);
module_invoke_all('user_view', $account, $build_mode);
}
/**
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment