Tuesday, 25 January 2011

Creating Links (aka Anchor tags) - The "l" Function

l() is a function that is available anywhere within a Drupal installation. It accepts three arguments:
  1. $text: The text that will be visible to users for your link
  2. $path: The URL that the link will go to when clicked.
  3. $options: An associative array which allows you to set additional parameters to further customize the link with CSS classes etc… It is here that you would need to establish that the link will be an image if you so desire (I’ll go into this later). The $options array is optional.

 l($text, $path, $options = array())

 

Specifying Anchor Tag Attributes

Two common anchor tag attributes are class and target. Here's how we add them to the previous example:
<?php

   $attributes
= array( 'class' => 'foo', 'target' => '_blank' );
  
$link = l('Permalink', 'node/1', $attributes);
?>

Linking to Images

The function's default action assumes that $text is pure text, not HTML. If < and > are present they're automatically converted into &lt; and &gt;. If you're linking an image you need the HTML so set the $html parameter TRUE. As you'll see in the l function's full description the $html parameter is the last of seven parameters, so you need to specify all of the preceding parameters. This call would therefore look something like:
<?php

   $image
= '<img src="…" alt="Alternative Text" />';
  
$link = l($image, $url, array(), NULL, NULL, FALSE, TRUE);
?>

How to create an image based link using the l() function for Drupal

The $text section of the l() function accepts any text so this text could be html and therefore could be an image. However by default it filters out html unless you tell it otherwise. To tell the l() function to accept html for its text you need to build the $options array and pass it as an argument.

   $options = array(
              'attributes' => array(),
              'html' => TRUE,
            );
l('<img src="/images/path-to-image.png" />', 'node/1', $options);
The 'html' =>TRUE, is the key here. Yes, I know, for building an image based link this is a little more than an ideal amount of code but this is the current state of affairs. I personally would rather have 4 arguments with $html = FALSE being the default for the third argument and $options for the 4th. This was changed in Drupal 5 where all of the options were individual arguments, and honestly the Drupal 6 version is better.
<?php
l
('Very thin abstraction layer', 'node/11554', array('fragment' => 'comment-37717', 'class' => 'active'); // really you don't need the class set to active, because l() adds class="active" by default.?>
<?php
l
('Very thin abstraction layer', 'node/11554', array('fragment' => 'comment-37717', 'target' => _blank); ?>
<?php
l
('Very thin abstraction layer', 'node/11554', array('fragment' => 'comment-37717', 'attributes' => array('class' => 'active', 'target' => '_blank'));> // really you don't need the class set to active, because l() adds class="active" by default.?>

Example with custom class, attributes

This will produce a link with the class="widelink" and rel="lightbox" attributes on the a tag.

<?php
l
(
 
t('My link'),
 
'node/56',
  array(
   
'attributes' => array(
     
'class' => 'widelink',
     
'rel' => 'lightbox',
    )
  )
);
?>

Links to an anchor, or hash-only links

To create a link to a named anchor (e.g. #namedanchor ) you will need to use a small work-around.
l('linktext', '', array('fragment' => 'namedanchor', 'external' => TRUE));
to create a hash-only link (to #) you'll need to adapt it to:
l('linktext', '', array('fragment' => ' ', 'external' => TRUE));
(note that fragment does contains a space.)

how to prevent l() from screwing up an ubercart purchase link

In porting a D5 site to D6, I found within their Ubercart logic they are generating a purchase item image-link like this:
   $buy_img = theme_image('sites/all/themes/customTheme/images/buy.png', 'BUY', 'Buy The' . check_plain($node->title), array('class' => 'coins'));
    $buy_link = 'cart/add/e-p' . $node->nid . '_q1_m0?destination=cart/checkout';
    print l( $buy_img, $buy_link, array('html' => true) );
The problem is the l() function is supposed to generate this:
http://localhost/cart/add/e-p176_q1_m0?destination=cart/checkout
but is generating this incorrect link with character encoding rather than '?' and '=' characters:
http://localhost/cart/add/e-p176_q1_m0%3Fdestination%3Dcart/checkout
Apparently, the D5 l() function does not do that same character encoding, and was not an issue... So here's the fix for D6:

    $buy_link = 'cart/add/e-p' . $node->nid . '_q1_m0';
    print l( $buy_img, $buy_link, array('html' => true, 'query' => 'destination=cart/checkout') );
The solution being to place the query string portion of the link into a 'query' attribute.

Add permissions to view links

I thought this was helpful...
if (user_access('create blog entries')) {
    $items[] = l(t('Create new blog entry.'), "node/add/blog");
  }
Used in blog module function blog_page_last

Internal Module Links

To clarify for those who want their module to support the 'Clean URLs' option.
<?php
  $var
= l("Click Text", "ModuleName");?>

returns a link complete with <a href= tags. For a link that goes with your hook_menu() and 'page arguments' you just add the arguments to the path.
<?php
  $var
= l("Click Text", "ModuleName/value/value");?>
If you need a link without the <a href= tags use 'url' instead.
<?php
  $var
= url("ModuleName/value/value")?>

Examples

Various implementation of l() function
simple:

<?php
  l
(t('Link text'), 'about-us');?>
with class:

<?php
  l
(t('Link text'), 'about-us', array('attributes' => array('class' => 'about-link')));?>
link to user:

<?php
  l
(t($user->name), 'user/'.$user->uid, array('attributes' => array('class' => 'link', 'id' => 'xxx', 'title' => 'Title')));?>

Always use user/user id!!! simple link with image:

<?php
$img
= '<img src="'.$base_path . $directory . '/images/rssIcon.png" />';
l($img, 'user/3', array('html' => array('html' => 'true')));
// also valid
l($img, 'user/3', array('html' => 'true'));?>
complex link with image:

<?php
l
($img, 'user/3', array('attributes' => array('class' => 'link', 'id' => 'xxx', 'title' => 'Title'), 'html' => 'true'));?>

Some of those examples

*might* lead to security issues.. (I mean... sure I did some of those mistakes... back in the days... :) )
the l() function sanitize the first argument (the text) when the option "html" isn't TRUE...
it means that you need to really take care about what the first argument will be if using the "html" option
For example:

<?php// making an img tag like might not be the safest solution
//$img = '<img src="'.$base_path . $directory . '/images/rssIcon.png" />';
// this would be much better, but still, you need to know that the $directory variable is also safe
$img = theme('image', $directory .'/images/rssIcon.png');// this is, I think, not possible...
//l($img, 'user/3', array('html' => array('html' => 'true')));
// is ok
l($img, 'user/3', array('html' => 'true'));?>
<?php// well, stop me if I am wrong, but I guess might be useful to wrap $user->name within t()..
//l(t($user->name), 'user/'.$user->uid, array('attributes' => array('class' => 'link', 'id' => 'xxx', 'title' => 'Title')));
// Something who might be handy if you need to "define" an HTML id is the form_clean_id()
// it will prevent to have twice the same id in the HTML (which is bad :) )
l($user->name, 'user/'.$user->uid, array('attributes' => array('class' => 'link', 'id' => form_clean_id('xxx'), 'title' => 'Title')));?>
Just a note..
if you need to have a variable in a translation.. like it might have been the case in the example

<?php
l
(t('Some string related to @user_name', array('@user_name' => $user->name)), 'user/'. $user->uid);// I might explain why it might be better to use @user_name over a simple @user... but it's not the topic ;)?>

or...
<?php
theme
('username', $user);// whatever.. ?>
anyway, it was nice from you to help!

in $options you forget to

in $options you forget to explain language options, to protect links against language prefix names like 'site.name.com/en/sites/defailt/files/blag.pdf'
I protect my site against this using below solution

<?php
$link
= l($title, $path, array('language' => 'pl'));?>

'boxes' table in database

<?php
l
($img, 'user/3', array('attributes' => array('class' => 'link', 'id' => 'xxx', 'title' => 'Title'), 'html' => 'true'));?>

link -localized_options-query

I'm using ThemeKey to change the theme on a series of pages on my website that have a completely different theme from the rest of the site -- based on a parameter appended to the url. Some of these pages are nodes that must be able to be updated from the subtheme side, as well as the main theme. So I’m trying to hook into the menu_item_link in template.php, but it doesn’t like my code. I copied the applicable snippet from the zen theme template php, and modified it as below for my zen_dis sub theme -- It still seems to be looking at the main Zen theme for this option, though – how do I phrase the Hook_ language to force it to use this?:
function zen_dis_menu_item_link($link) {
if (empty($link['localized_options'])) {
$link['localized_options'] = array();
}
// If an item is a LOCAL TASK, render it as a tab
if ($link['type'] & MENU_IS_LOCAL_TASK) {
$link['title'] = '' . check_plain($link['title']) . '';
$link['localized_options']['query'] = '?dis-site-search-organization';
$link['localized_options']['html'] = TRUE;
}
return l($link['title'], $link['href'], $link['localized_options']);
}

For Drupal6

If you look at the description of the function in Drupal 6 parameters you will find under $options:
Additional $options elements used by the url() function.
But I was not able to put additional parameters into URL in this case:

print l("Click here", $internal_path, array(
   'listing_url' => 'data'
));
So I made the following changes as the user described above and it worked:

print l("Click here", $internal_path, array(
   'query' => array(
      'listing_url' => 'data'
    ),
));

Class Active

Hi,
I've tried experimenting with this function in order to append the class="active". It works great with the internal path (node/#), but if I supply the url alias it doesn't seem to work.
I've tried this way :
<?php
print l(t('Link Text'),'my-url-alias',array('alias' => 'TRUE')); ?>
Maybe I'm doing this wrong. Any thoughts?
Regards

Link to #

This is for you modesia...
to create a hash-only link (to #) you'll need to adapt it to:
l('linktext', '', array('fragment' => ' ', 'external' => TRUE));
(note that fragment does contains a space.)
Thanks to alexanderpas!

Title and Alt attributes are sanitized

Actually all the "attributes" values are sanitized in drupal_attributes, so if you do anything to sanitize it yourself, you can end up double encoding. For example, l($title, $path, array('attributes' => array('title' => t('All about @names', array('@names' => 'Adam & Eve'))))) will result in the ampersand being double encoded. In this example, it is okay to use '!names' because drupal_attributes will do a check_plain for you.

Link to #

to create a hash-only link (to #) you'll need to adapt it to:
l('linktext', '', array('fragment' => ' ', 'external' => TRUE));
(note that fragment does contains a space.)
Thanks to alexanderpas!

Can't figure out how to

Can't figure out how to combine attributes and fragment, since I need both - the first one to apply a class, second one to generate proper URI to the comment. What is wrong in my code below?
<?php
$block_content
.= l(t('Conversate'), "node/$links->nid", array('attributes'=>array('class'=>'comment')), array('fragment' => "comment-$links->cid")).'</div>';?>
Login or register to post comments

No comments:

Post a Comment

Source base installation of php, mysql and apache in ubuntu/ linux

Compile and Install a LAMP(Linux/Apache/MySQL/PHP) Server from Source In the last post, I described the method to install a LAMP ser...