$links the garbage can of node.tpl

28 September 2009 mortendk
A tale of about my hatred to $links and how i almost came to live with it, after i ate my own dog food.

As you may know theres ways to view all the wonderful elements that is hidden inside node.tpl files, and how you can find em' by using the nerdy module:
Devel module. Its an alpha omega module.

Well if youre scared of using dsm('$node'); just look at it as if it was a way of viewing the html source - see now its not so scary ;)

<?php
dsm
('$node');
?>

the garbage can of node.tpl

Inside a basic node.tpl.php file theres a lot of variables defined (http://api.drupal.org/api/drupal/modules--node--node.tpl.php/6 ) that can be used for your basic design needs. One of them have always pissed me off more than anything else is the $links!:

So whats wrong with the $links variable you might ask. Its a quick way to get the read more, number of comment, atachments etc in you node.
And if you wanna modify it, you can just use overwrite THEME_links() (http://api.drupal.org/api/function/theme_links/6) and modify it anyway you want to.

So the problem comes in if you wanna use one of the elements thats inside $links and wanna place it in another place.

Lets look at the note.tpl.php

<h1><?php print $title?></h1>
<div class="meta"><?php print $name ." | " . $date; ?> </div>
<p><?php print $content ?></p>
<div><?php print $links; ?></div>

If you just wants to modify the markup of $links use the drupal theme function that generates the $links variable THEME_links() (http://api.drupal.org/api/function/theme_links/6)
But thats not what we wanna do - We wanna move the number of comments up to the left of the date of the node so the markup we wants is something like this:

<h1><?php print $title?></h1>
<div class="meta"><?php print $name ." | " . $date . " | " . $number_of_comments; ?> </div>
<p><?php print $content ?></p>
<div><?php print $read_more; ?></div>

Lets get nerdy nerdy

  • kudos for knowing where that line is taken from and geekyfied.

We know whats wrong: $links is being build by drupal and we cant use it for anything, cause its already build :(
okay - deep breath now... So lets start by checking out if all that info should be hidden inside the node ...yeah the evil developer are hiding it from us .. ;)
In youre node.tpl.php add this line

<?php
dsm
($node->links);
?>
or use the theme developer module and click you way.

oh and just to make it a bit more complicated the values of $links are changing if by where your watching it from (it dosnt make so much sense to have a link to a node youre allready on ;)) Take a look a the node on the frontpage (site.org/node/), or another overview page - taxonomy pages (site.org/taxonomy/term/[termid]) and compare it to the whole node view (node/[nid]) see the difference?

Lets look at the number of comments out of the $node:

<?php
dsm
($node->links['comment_comments']);
?>

Now we have the array set that holds the information about the number of comments.

If you just wanna get the job done you could write it directly into the node.tpl:

<h1><?php print $title?></h1>
<div class="meta"><?php print $name ." | " . $date . " | " . $vars['node']->links['comment_comments']['title']; ?> </div>
<p><?php print $content ?></p>

$links is kicked out cause we dont wanna have the information repeated.

I know that another aproach could be to just do a print $node->comment_count, but we wanna have the link to the comments, so a user can click on the link from the frontpage and go to the comments.

If you wanna do it - Do it right

To build the link were gonna dig a bit deeper into the world of Drupal so please welcome: the L funktion,l as in link (or looser or lame or ): l();
The link function looks like this:

<?php
print l('Text', 'path', array('attributes' => array('class' => 'link-class')));
?>

api page -> http://api.drupal.org/api/function/l/6

Yup its maybe a bit overkill to do it this way, but now its possible for other themes & modules to manipulate with your link - the power behind having a framework.
hmm WTF are we theming or developing .. oh well lets just get this link stuff done

Heres the $node->links['comment_comments'] splitted out to create a link

<?php

l
(
  
$vars['node']->links['comment_comments']['title'],
  
$vars['node']->links['comment_comments']['href'],
    array(
'attributes' => array('class' => 'comment','title' => $vars['node']->links['comment_comments']['attributes']['title'] ))
);         
?>

To get this into node.tpl we could do something like this:

<?php
//link comment
$link_comment l($vars['node']->links['comment_comments']['title'], $vars['node']->links['comment_comments']['href'], array('attributes' => array('class' => 'comment','title' => $vars['node']->links['comment_comments']['attributes']['title'] )));          

//and lets create the read more links
$link_read_more l($vars['node']->links['node_read_more']['title'], $vars['node']->links['node_read_more']['href'], array('attributes' => array('class' => 'read-more','title' => $vars['node']->links['node_read_more']['attributes']['title'] )));                   
?>


<h1><?php print $title?></h1>
<div class="meta"><?php print $name ." | " . $date . " | " . $link_comment; ?> </div>
<p><?php print $content ?></p>
<div><?php print $link_read_more; ?></div>

Now the node.tpl is beginning to look a bit ugly, and were doing custom work into the node.tpl, so if we later wanna have variant of the node.tpl (node-blog.tpl.php, node-page.tpl.php, node-news.tpl.php) we would end up having the link generating code in multiple places - and stuff like that always comes back and bites you.

template.php & preprocess

So strap on you helmet cause were diving in a bit deeper (and all this for a couple of links! holy crap batman...).
Instead of putting all this stuff inside the node.tpl you can throw the link creation code into the template.php file and clean up you node.tpl

I wont get into to much specific of the template.php file- or read this page http://drupal.org/node/11811. To put it short template.php is where you should put all you "logic" - so all modifications of variables, array sets and other code stuff should go there (I must confess that some simple if/else ends up in my node.tpl...)
But to make the story short create a template.php inside youre themes directory root, so if youre theme is inside the folder "foo" it should be in foo/template.php.

In the template.php you can put all that geeky php "programming" stuff, so youre node.tpl's can begin to look pretty and clean again:

<?php
//----------------------------
//file template.php
//----------------------------
function THEMENAME_preprocess_node(&$vars) {
   
//comments
if($vars['node']->links['comment_comments']){
          
$vars['link_comment'] =  l($vars['node']->links['comment_comments']['title'], $vars['node']->links['comment_comments']['href'],
               array(
                
'attributes' => array('class' => 'comment', 'title' => $vars['node']->links['comment_comments']['attributes']['title']),
                  
'fragment' => $vars['node']->links['comment_comments']['fragment']
               )
     );         
   }

//comment_add
 
if($vars['node']->links['comment_add']){
       
$vars['link_comment_add'] =  l($vars['node']->links['comment_add']['title'], $vars['node']->links['comment_add']['href'],
     array(
            
'attributes' => array('class' => 'comment-add', 'title' => $vars['node']->links['comment_add']['attributes']['title']),
              
'fragment' => 'comment-form'
           
)
      );         
   }

//attachments
 
if($vars['node']->links['upload_attachments']){
    
$vars['link_attachments'] =  l($vars['node']->links['upload_attachments']['title'], $vars['node']->links['upload_attachments']['href'], array('attributes' => array('class' => 'attachments','title' => $vars['node']->links['upload_attachments']['attributes']['title'] )));         
   }

//read more
   
if($vars['node']->links['node_read_more']){
    
$vars['link_read_more'] =  l($vars['node']->links['node_read_more']['title'], $vars['node']->links['node_read_more']['href'], array('attributes' => array('class' => 'read-more','title' => $vars['node']->links['node_read_more']['attributes']['title'] )));         
   }

//statistics_counter
  
if($vars['node']->links['statistics_counter']){
    
$vars['statistics_counter'] = $vars['node']->links['statistics_counter']['title'];
  }
}
?>

To add new variable that can be used in the node.tpl, you add must add it to the $vars variable. So by adding : $vars['NEW_VARNAME'] in the preprocess_node() function, you get acces to it in the node.tpl
So by using the preprocess_node code above we now have these spanky hot new variables:
<?php
print $link_comment
print
$link_comment_add 
print $link_attachments
print
$link_read_more;
print
$statistics_counter;
?>

Now the node.tpl can look something like this:

<h1><?php print $title?></h1>
<div class="meta"><?php print $name ." | " . $date . " | " . $link_comment; ?> </div>
<p><?php print $content ?></p>
<div><?php print $link_read_more; ?></div>

wooho now we have the control back over the $links yeah instant win!
... offcourse only until some module decides to add more stuff to the $links (ahem)

That was easy right for moving a link a bit right ;)
The cool think now is that you can reuse this in you themes if you add it into you template.php - this is (off course) added to the mighty mothership theme so if you dont wanna play around with template.php's n stuff its all prepared for you there.

main pict license:

http://www.flickr.com/photos/selva/ / CC BY-NC 2.0

Pingback

[...] $links the garbage can of node.tpl | morten.dk morten.dk/blog/links-garbage-can-nodetpl – view page – cached A tale of about my hatred to $links and how i almost came to live with it, after i ate my own dog food. — From the page [...]

Thanks for this

No doubt I'll find a use for this soon! :)

Lisa Rex 28 September, 2009 - 02:26

Pingback

[...] the original here:  $links the garbage can of node.tpl | morten.dk SHARETHIS.addEntry({ title: "$links the garbage can of node.tpl | morten.dk", url: [...]

Thanks

It's so hard to explain to people why moving the little comments link up above the post is going to take so long. I'll be using this as a cheat-sheet next time I need to do it. Wish it was easier but thanks for the summary anyway.

Anonymous 28 September, 2009 - 03:44

Pingback

[...] Read more from the original source: $links the garbage can of node.tpl | morten.dk [...]

Pingback

[...] more from the original source:  $links the garbage can of node.tpl | morten.dk By admin | category: page view | tags: almost-came, element-on-each, garbage, [...]

Pingback

[...] Originally posted here: $links the garbage can of node.tpl | morten.dk [...]

Have you thought about making

Have you thought about making it even more generic? Something like this?

<?php
foreach ($node->links as $link_id => $link) {
  $vars["{$link_id}_link"] = $link;
}
?>

This way you explode all the links, so it makes it super easy and avoids
"offcourse only until some module decides to add more stuff to the $links (ahem)"

Scott Reynolds 28 September, 2009 - 07:57

...That was incomplete

Sorry, was thinking on my feet.

<?php
foreach ($node->links as $link_id => $link) {
  // we only want to move this bad boy out of $links if
  //it is an actually link
  // theme('links') can accept 'links' without href
  // http://api.drupal.org/api/function/theme_links/6
  if (isset($link['href'])) {
    $vars["{$link_id}_link"] = l($link['title'], $link['href'], $link);
  }
}
?>

That preserves all that the module developer intended for the link. (for instance, passed the $link['attributes'] to the l() so it gets the class/id properties, passes $link['query'] so it can preserve a query string and $link['fragment'] which is important for comment link)

Scott Reynolds 28 September, 2009 - 08:06

Way to go

Thanks for the snippet Scott, definetly the way to go about it. Not only it's more efficient, but you would have prepared any other links that any other module would be adding there.

Stil we'd have to print each vaiable manualy on our tpl php thouhg. I think we should push this a bit further if possible to print each link we wanted seperately, but stil print something like $links_link at the end, that would only print the ones not already printed. I think CCK does this somehow, don't ask me how though!

Any idea how we could get this done?

manuee 28 September, 2009 - 19:12

"think CCK does this somehow,

"think CCK does this somehow, don't ask me how though!"

Its not CCK but drupal_render(). Each structured array element put through drupal_render() will get a '#printed' property when it has been printed, thus allowing you to drupal_render($content['my_cool_field']) . drupal_render($content);

theme('links') isn't drupal_render-able in d6 and not in d7.

Scott Reynolds 28 September, 2009 - 22:41

For those interested, here is

For those interested, here is the issue to make a drupal_render-able l() element

http://drupal.org/node/602522

Scott Reynolds 13 October, 2009 - 18:48

sweetness

Thanks for the original article--great information.

Scott's snippet is awesome only the first line should read:

      foreach ($vars['node']->links as $link_id => $link) {

I also chose to unset the corresponding link from the $vars['node']->links array like so:

      $vars["{$link_id}_link"] = l($link['title'], $link['href'], $link);
      unset($vars['node']->links[$link_id]);

The revised snippet looks like this:

  foreach ($vars['node']->links as $link_id => $link) {
    if (isset($link['href'])) {
      $vars["{$link_id}_link"] = l($link['title'], $link['href'], $link);
      unset($vars['node']->links[$link_id]);
    }
  }

In case it's not obvious the snippet should be added to hook_preprocess_node function in templete.php.

Cheers,
John

Anonymous 15 August, 2010 - 04:09

One more thing...

Oops, I forgot to wrap the following condition around the whole snippet:

  if (count($vars['node']->links)) {

The newly revised snippet now looks like:

  if (count($vars['node']->links)) {
    foreach ($vars['node']->links as $link_id => $link) {
      // we only want to move this out of $links if it's an actual link
      // theme('links') can accept 'links' without href
      // http://api.drupal.org/api/function/theme_links/6
      if (isset($link['href'])) {
        $vars["{$link_id}_link"] = l($link['title'], $link['href'], $link);
        unset($vars['node']->links[$link_id]);
      }
    }
  }

Sidebar: Drupal annoyingly and stubbornly defines the entire $vars['node']->links string prior to rendering so the above unset function doesn't achieve the desired goal of removing the elements for corresponding variables.

Anonymous 15 August, 2010 - 04:37

The module way

Hey.
I know, its really awkward for designers to split the $links-Array without some developer knowledge. If you want to do it without doing nerdy stuff there is a module that split $links and make its items (e.g. "Submitted by" and comments) reorderable through CCKs manage fields tab in the content type settings: http://drupal.org/project/eldorado_superfly

Stefan

Anonymous 28 September, 2009 - 08:04

uuuh nice module - didnt know

uuuh nice module - didnt know about that one. pretty sure it can help some people who works with drupal in the frontend.
But for us who wanna "Dominate" the frontend (total control over the markup) the problem is that we then completely loosed the ability to customize the markup as we want it, unless we dive in and rebuild all the cck fields's tpl files. -and then shit really begins to be complicated (and all this for just making a simple move of a number of comments)

its not so much that we need to do a $node->link['something] thats a hassle. Its that I have to do preprocess calls n shit to move it around. But okay when we frontend people understands
the methods and gets into building theme frameworks that can handle these problems we have come a long way. - but it sure would have been nice if the variables was just there to begin with ... ooh but we always have Drupal8 ;)

mortendk 28 September, 2009 - 10:21

What about this?

foreach ($vars['node']->links as $link_id => $link) {
if (isset($link['href'])) {
$vars["{$link_id}_link"] = l($link['title'], $link['href'], $link);
}
else { $vars["{$link_id}_link"] = $link['title'];}
}

I got the code from Scott, but changed it a bit. His code worked fine for links which actually linked to content, but not to "links" like statistics. Now with the above code, both links and "fake" links are able to be posted.

Anonymous 13 October, 2009 - 00:14

pagination link

Hi
Thanks for this great article.
As you explained I have added below code in template.php:
function THEMENAME_preprocess_node(&$vars) {
//comments
if($vars['node']->links['comment_comments']){
$vars['link_comment'] = l($vars['node']->links['comment_comments']['title'], $vars['node']->links['comment_comments']['href'],
array(
'attributes' => array('class' => 'comment', 'title' => $vars['node']->links['comment_comments']['attributes']['title']),
'fragment' => $vars['node']->links['comment_comments']['fragment']
)
);
}}
And added the code below at the end in node.tpl.php.

<?php
print $link_comment;
?>

I Could not find comments link. Kindly help what might be going wrong in my code.
I am using pagination module. It provides a pagerat the end of the content and a "show all" link.
This "Show all" link is printed due to the $link variable in node.tpl.php. If i try to replace $link variable, Other links like "login to register or post comments" is also is also moving..
Could you please help me out as to how to access "show all" link with out messing up with "login or register to post comments" link.
Thanks again.

Anonymous 5 November, 2009 - 08:11

OMG... Thanks you so much for

OMG... Thanks you so much for that incredible cheat-sheet! VERY USEFULL!

Anonymous who love the word 'Anonymous' 13 November, 2009 - 01:46

no teme

I want to quote your post in my blog. It can?
And you et an account on Twitter?

andronidze 7 January, 2010 - 19:56

thank

thank you, nice article

Anonymous 25 January, 2010 - 15:36

Reply to:

Is there a way to have drupal comments print out something like this:
"John replied to Jack"
I'm using flat displayed comments (which is an organised mess) and this would improve readability
I've been bugged by this for ever :/

clen 12 September, 2010 - 21:35

Nevermind i got it, something

Nevermind i got it, something like this in comment.tpl.php will do the trick:

<?php
$reply
= _comment_load($comment->pid);
$reply_url "comment-{$comment->pid}";
$re_name = $reply->name;
echo
l(t('In reply to'.$re_name), NULL, array('attributes' => array('class' => 're', 'title' => 'Jump to original  comment.'), 'fragment' => $reply_url, 'external' => TRUE));
?>

clen 14 September, 2010 - 08:14

This is the best direct no

This is the best direct no bullshit explanation of how to theme annoying node $links I have ever seen. Thank you Morten DK.

Anonymous 27 May, 2011 - 17:54

Not sure if anyone is still

Not sure if anyone is still watching this, but I am using Drupal 7 and am trying to make this work, however I keep getting the following error

Notice: Undefined property: stdClass::$links in omega_alpha_preprocess_node()

I added this code to the existing function omega_alpha_preprocess_node(&$vars)
if (count($vars['node']->links)) {
foreach ($vars['node']->links as $link_id => $link) {
// we only want to move this out of $links if it's an actual link
// theme('links') can accept 'links' without href
// http://api.drupal.org/api/function/theme_links/6
if (isset($link['href'])) {
$vars["{$link_id}_link"] = l($link['title'], $link['href'], $link);
unset($vars['node']->links[$link_id]);
}
}
}

THanks

Anonymous 15 March, 2012 - 06:58
The content of this field is kept private and will not be shown publicly.
i went from journey to damn yankees Think i need a beer pretty soon 31 weeks 6 days ago
4,356

good Stüff

Mothership - a clean up the crap "theme"

Miro - a open atrium theme:
more info & comments

freya rocks

the progress for my premature daughter can be folllowed here:
Freya Rocks
sorry its in danish

the blög

Give some Love to the development of the mothership

Flattr this

User login

Recent comments

give some luv

drupal member ...

geek royale