Thursday, 9 June 2011

Contextual links

Today wasn't the first time I've had a fight with contextual links, but I decided that this time I would get to the bottom of why they are such a pain to implement.

What's a contextual link? The little on-screen tools that let you jump to editing/deleting nodes, views, configure blocks and so on. (But not taxonomy - however I'll get to that later.)

The idea behind a contextual link is as simple as it is useful. If you've viewing a node teaser somewhere on the page then, if you have the permissions, a little dropdown menu will be available that lets you edit that node. Or if it's a block you can configure the block. If it's a Views block you can either edit the view or configure the block.

And so on.

But what if you want to add your own contextual links?

If you are filling a custom block then it's not very hard. I had a situation where I displayed a block which contains some text pulled from a node - but the node can vary depending on the current setting of site taxonomy.

I wanted to add a contextual link that would allow an administrator to edit the text. So I needed to create a contextual link that would link to the currently displayed node:

$build = array(
  '#contextual_links' => array(
    'node' => array('node', array($node->nid)),
 ... rest of the content

The first 'node' is the module that provides the links we want, the second 'node' is the first part of the link URL while the array contains any further parts to add to the link URL.

So what we get here is node/nid/edit and node/nid/delete - the contextual links module figures out what URLs the current user can access from the base supplied URL (in this case node/nid). If the user can edit the node, but cannot delete it he'll only get the 'edit' link.

What this means is that you can't just add any old links, you have to work with the system.

Why is the second parameter an array? Because you might have more than one variable value - these are arguments for a URL, so for a block's contextual links you have:

'block' => array('admin/structure/block/manage', array($block->module, $block->delta)),

So far so good.

For my current project I needed to create a list of taxonomy subjects and I wanted to allow the name of the term to be editable. This is where things started to get hard. It was hard because I did not know three things:

The first thing to understand is that contextual links only work for template themes - not for function-based themes. The reason has nothing to do with the contextual module - it's because generic preprocess theme hooks are only called for templates. And the contextual module uses a generic preprocess function to find the elements that require links.

The second thing to understand is that your template must contain "render($title_suffix)" because that is where the contextual links are contained and they are not rendered until you specifically render them. You can use block.tpl.php as an example.

This was all fine except for the third thing: my taxonomy link would not display even though I thoroughly checked it's format and tried other types of contextual link in the same location which all worked fine. Just not the taxonomy/term/[tid]/edit.

Luckily I'd been reading up on everything to do with contextual links and remembered that someone somewhere had mentioned the 'context' setting in hook_menu(). This is the third thing that has to be in place for a link to appear, so I added a hook_menu_alter() function:

function mymodule_menu_alter(&$items) {
  $items['taxonomy/term/%taxonomy_term/edit']['context'] = MENU_CONTEXT_INLINE;

And suddenly my link appeared.

Finally there is the fact that if you want to put in a node create link more hacking is needed - although it's all legal Drupal. I picked this up from this blog. Essentially you have to make the URL appear to be a child item like this (in hook_menu_alter):

$items['node/add/page']['_tab'] = TRUE;
$items['node/add/page']['tab_parent'] = 'node/add';
$items['node/add/page']['context'] = MENU_CONTEXT_INLINE;

As it says on Marcus's site, it's not clear whether this will actually break the menu system, but it doesn't seem to.

I hope that's some help.

1 comment:

Anonymous said...

Oh, *thank* you. This was just what I needed to know for the same thing - adding a contextual menu for a term.