On the Joys of Raising Free-Range node.tpl.php Links

Ah, chickens -- delicious chickens. Its a timeless truth, however, that not all chickens were born equal. And clearly, the best tasting chickens are those who've been allowed to feed, graze, and socialize on the open range.

Alas, this is not a white paper on the art of raising free-range chickens. Rather, its a tutorial on raising free-range node.tpl.php links such as "add new comment", "read more", or any other link which the multitudes of modules spew forth into that disgustingly generic $links variable. By the end of this tutorial you will no longer be limted by the $links variable. Instead, you will have the necessary code and knowledge to turn any link into a variable that you can place at any point in your node.tpl.php file.

Enter the Sausage Factory

Below is a self contained template.php file that "makes the magic happen". Its commented heavily, as I don't assume this code is necessarily self evident. That said, I am assuming you know SOMETHING about the template.php file. If you don't, then you probably should google it real quick to get a halfway decent background.

Template.php

<?php function _phptemplate_variables($hook, $vars) {    switch ($hook) {        case "node":/*As everyone under 40 knows, $vars['node']->links is an array which can be split up using the foreach(). Like... duh */            foreach ($vars['node']->links as $link) {/*Do the nasty thang with preg_match... truth be told, I have no idea how this thing works. Rest assured however this method is dirty, unsanitary, and very cruel to the poor CPU usage of webservers. However, there is apparently no alternative at the moment (this will change in 4.8, according to my sources). How nasty is this method? Well.. I can't actually use the codefilsters for it because the below function causes it to think the php script is ending. So in a real PHP script this is supposed to just continue as though there was no ending...*/?> preg_match("/<a\s*.*?href\s*=\s*['\"]([^\"'>]*).*?>(.*?)<\/a>/i",$link, $matches);<?php                 /*Now we take our sliced $links carcus, and take out the bones. Or, rather, we've split up the url of the link, and text of the links into a further set of arrays. The text is split up by onespace. the URLs are split up by "/" Thus, the $textparts is an array that has split up the "read more" into "read", and "more".  $urlparts will likely be a sliced array that has "node", and "[some number in it]"*/                $textparts = explode(" ", $matches[2]);                $urlparts = explode("/", $matches[1]);/*I am an idiot, and cannot for the life of me remember wtf $matches[1] is. So I've named them cyborg-monkey-hybrid-friendly names. You'll always want to use $linkurl as your link's URL. Sometimes, you might not want the default title in which case you will be free to add your own title. THEN, sometimes you'll only be interested in attaching a CSS classname so that you can style your links differently. Your smart... I trust you'll figure it out. Remember: you are very special. */                $linkurl = $matches[1];                $default_title = $matches[2];/*This switch statement is where the "magic" begins! */                switch (true) {/*As you'll no doubt note, I've gone ahead and renamed "user's blog" to "User's Blizizog". I trust you can understand whow hyperlinks work. Therefore, I hope you will start to realize how dreadfully simple this method is. All that is required is the ability to look at either the URL, or the name of the hyperlink, and see a workd that is unique enough to allow you to extract the delicious link. In thise case, we know that the $user's blog link is going to without a doubt contain the word "blog". More or less, "case in_array("blog", $urlparts)" says "yo, PHP! If one of the array values of $urlparts is "blog", than that's our stoopid blog link. */                    case in_array("blog", $urlparts):                        $linktitle = $vars['node']->name."'s Blizizog";                        $bloglink =                            '<a href="'.$linkurl.'" class="bloglink">'.$linktitle.'</a>';                        $feedlink =                            '<a href="'.$linkurl.'/feed'                            .'" class="feedlink">RSS</a>';/*Obviously, we'll need to pass our link to the node.tpl.php file. Below, we've done just that. Since you've probably noticed that we  $vars['bloglink']  */                        $vars['bloglink'] = $bloglink ." | ".$feedlink;                        break;/*This method could be improved, but unfortunately, the nature of the l() function stumped me, ad I could only get this working by hardcoding the HREF links. However, if someone else can figure it out, perhaps we could make this more efficent, and easy to work with. Below is something of an example of how I image it working out eventually. A lot like the forms API arrays... I assume */                    case in_array("forward", $urlparts):                        $stylecodename = "emaillink";                        $linktitle = "email page";                        $vars['emaillink'] =                            '<a href="'                            .$linkurl.'" class="'                            .$stylecodename.'">'                            .$linktitle.'</a>';                        break;                    case in_array("comment", $textparts):                    case in_array("comments", $textparts):                            $stylecodename = "commentlink";                            $vars['commentlink'] =                             '<a href="'                            .$linkurl.'" class="'                            .$stylecodename.'">'                            .$default_title.'</a>';                        break;                    case in_array("read", $textparts):                        $stylecodename = "readmorelink";                        $vars['readmorelink'] = '<a href="'.$linkurl.'" class="'.$stylecodename.'">'.$default_title.'</a>';                        break;                    case in_array("printer", $textparts):                        $vars['printlink'] =                         '<a href="'.$linkurl.'" class="printlink">Print Page</a>';                        break;                    default:                        $vars['otherlinks'] .= '<a href="'.$linkurl.'">'.$default_title.'</a>';                }            }        }    break;        }return $vars;}?>

Wasn't that fun?! I'm tired, so I am going to just go ahead and provide the node.tpl.php file that is working on this blog to help you get started. I don't have a lot of time these days, so I apologize for the quick explanation. However, at the very least, I hope this will help a few of you on the path towards raising free-range node links. There is joy in it. Truly.

node.tpl.php

<?php if ($page == 0) { ?><div class="node<?php print ($sticky) ? " sticky" : "";  print " ". $node->type; ?>"> <!--Begin Teaser Content--> <h2><?php print_r(date("F j, Y", strtotime($date))); ?> | <?php print $node_title; ?></h2> <div class="content teaser"> <?php print $content;?> </div> <div class="teaserlinks"> <?php print $readmorelink;?> <?php print $commentlink;?> </div> <!--End Teaser Content--> <?php } else { ?> <!--Begin Full Node Content--><div id = "fullpage"><div class="blogtools"><?php print $bloglink;?></div> <div class="pagetools"> <ul><li><?php print $commentlink;?></li> <li><?php print $printlink;?></li> <li><?php print $emaillink;?></li> </ul> <br class="clear" /> </div><div class="node<?php print ($sticky) ? " sticky" : "";  print " ". $node->type; ?>"> <div class="content"> <div class="info"> <div class="author">Posted by <?php print $name; ?></div> <div class="date"><?php print $date; ?></div> </div> <?php print $content; ?> </div></div> <!--End Full Node Content--> <?php } ?></div>

Comments

PHP TPL problem

Hello I am new to tpl so I need some help.

I have a script which I need to modify. So in the tpl.php part I have a variable
{$itemsdesc.link} which return a value I need (a link to a e.x youtube video) when I use it like this:
...
...

{$itemsdesc.link}

...
...
it prints the link.

What I need is to get the value of {$itemsdesc.link} and use it in a php code inline in the tpl.php
where it says "LINK PARAMETER HERE" i need the value of {$itemsdesc.link} something like this:
...
...

{php}
include DOC_ROOT . 'includes/video_embed.php';
$video=new video_embed();
$video->embed("LINK PARAMETER HERE" , "play"); ----- this returns an embed string for the video which link is provided
{/php}

{$itemsdesc.link}

...
...
But it seems not to read it in between the php tags.

Best regards,
Nikola.

Simplification of the regular expression

The regular expression can be simplified if you assume the links are structured reasonably. Assuming one <a ... > ... </a> tag per link, with one href attribute per tag with no spaces and using properly closed double quotes (e.g. href="some/url"), and making some changes to the regular expression quoting and bracketing, the call becomes:

preg_match('{<a.*?href="([^"]*).*?>(.*?)</a>}i', $link, $matches);

This 'should' be marginally faster, and the links generated by drupal seem to meet the assumptions.

Page Title Module

The page title module indicates that the following should be place in template.php: function _phptemplate_variables($hook, $vars) {  $vars = array();  if ($hook == 'page') {      // This is the only important line    $vars['head_title'] = page_title_page_get_title();      }  return $vars;} There's obviously some redundancy with the function you've shown above (which works great, btw). Can you explain how to combine these so it all works together? Thanks!

No go, either

Like many others, I've been looking for this functionality for days, trying different solutions from the Drupal forum. I can't get your solution to work. I tried both to paste it into my existing template.php, as well as replacing that with one that only contained your template.php code. Like previous posters, I'm confused as to the preg_match statement. I tried both to include and exclude it from the ?php / ? tags, but my page still turns out blank. It would be nice with a working code example. / Ayza

I'll do my best to find time

I'll do my best to find time to day that. I'm a bit swamped right now.

Thanks!

That would be so kind of you! I surely hope we won't need to jump through all those hoops in 4.8! / Ayza P.S. I thoroughly enjoyed your "Drupal hell". ;-)

Btw: Missing one div tag

If I counted correctly, you are missing one div tag in the node code. Just thought I let you know. / Ayza

error on preview

I am getting an error on preview when using this code. any idea why? submit does not show any error. warning: Invalid argument supplied for foreach() in /Users/Sites/drupal/themes/blue/template.php on line 36.

Yes, before “foreach

Yes, before "foreach ($vars['node']->links as $link) {" wrap this condition around the the code. This prevents the code from executing during previews.

if (arg(1) != "add" || arg(2) != "edit")) { (foreach $vars['node]-link and on) }

Thank you for the fast response, but

Did not make a different. Still getting the error in preview. Any ideas?

great article, one thing missing.

one brace. i count 4 left and 5 right. not sure where to put the one left or take one right away. any help would be appreciated. thank you.

Looking, but only with one eye

Ugh. I really appreciate that you've gone and done this. I've been hoping that 4.7 (RC.whatever) would magically make all this unecessary. And... well, you know. So here I am, peeking into the Sausage Factory, afraid of what i'll see.

This system has worked

This system has worked really well for me. i need to turn the linkage into a function though... but my attempts to do so caused lots of errorage. Probably because of a few "holes" in my php skills. I think 4.8 will probably make this approach unnecessary (thank freakin' god...)

This post is really helpful.

This post is really helpful. Thanks!

Freakin preg()....

For some reason, tinymce stripped this all out, here is the correct one....

preg_match("/<a\s*.*?href\s*=\s*['\"]([^\"'>]*).*?>(.*?)<\/a>/i",$link, $matches);