The previous recipe explained how to show an image beside the text.

But what if we want to replace the whole text with an image?

There's a certain CSS solution to this as well. But you may find that it's not perfect, so in this recipe we're not going to use CSS: we're going to use HTML's{IMG} tag.

We have explained previously that Flag uses a template to generate the output. Embedded in this template are some variables, among them $link_text, which is the textual label for the link. Fortunately, Drupal lets us alter variables before they arrive at the template, and we're going to use this mechanism to replace this text with an image tag.

Instructions for Drupal 6

Put the following preprocess function in your 'template.php'.

function phptemplate_preprocess_flag(&$vars) {
  $image_file = path_to_theme() . '/flag-' . $vars['flag']->name . '-' . ($vars['action'] == 'flag' ? 'off' : 'on') . '.png';
  // Uncomment the following line when debugging.
  //drupal_set_message("Flag is looking for '$image_file'...");
  if (file_exists($image_file)) {
    $vars['link_text'] = '<img src="' . base_path() . $image_file . '" />';
  }
}

However, for the theming system to notice this function you'll have to copy 'flag.tpl.php' into your theme folder (better still, create a symlink). (This also makes path_to_theme() point to this theme folder and not to Flag's folder.)

And remember to clear Drupal's cache.

How does it work?

The function above is called whenever a flag is about to be printed. It looks for an image with the name flag-bookmarks-on.png or flag-bookmarks-off.png --depending on the state of the flag-- and, if exists, it shows it instead of the text. "bookmarks" is only an example: the machine-name of the actual flag used will be used. The images should be placed in your theme folder.

Instructions for Drupal 5

We'll use the function above. Paste it into your template.php. However, Drupal 5 is less elegant and is not going to call it. So we're going to call it ourselves, by adding yet more code:

function _phptemplate_variables($hook, $vars) {
  if ($hook == 'flag') {
    phptemplate_preprocess_flag($vars);
    return $vars;
  }
  return array();
}

(If you already have a _phptemplate_variables() function in your template.php, PHP will shout at you. You'll have to merge this code into your existing _phptemplate_variables().)

For all this Drupal 5 trickery to work, you must first copy the little glue function shown in Flag's 'theme/README.txt' into your template.php.

Another Simple Option

For anyone else interested in this, a simple way is to add your images to the appropriate .css class (add flag and unflag), in the admin section set the link text to something short--like ( + ) and ( - ) .

Then, override the flag-wrapper a { class, and set the link color the background (usually white, #FFF ).

Comments

ajayg’s picture

In the above 6.x version refer to this code

if (file_exists($image_file)) {

Is the file_exists check above expensive? Meaning will the existance of file will be checked for every node view, meaning disk access for each page view?

pradeep_’s picture

Good recipe. Very clearly explained. 5/5 :-)

YK85’s picture

I was wondering if someone could help achieve the same thing above for Flag Form where it uses Checkboxes. I would like to replace the checkbox icon (image?) with a custom one for checked/unchecked targeting only a specific flag type. Can anyone assist? Thanks!

jgreep’s picture

I was able to use a variation of the above to achieve an image with a rollover. I used the preprocess hook to add an empty span instead of the image. No check is needed to see if the file exists.

function phptemplate_preprocess_flag(&$vars) {
  $vars['link_text'] = '<span></span>';
}

For the CSS, we use a single image (32px by 64 px) with the icons being stacked. In this case, the off image is on top and the on image is on the bottom. On the hover, it simply adjusts the image to show only the part of the background we're interested in.

span.flag-bookmarks a span{
    display:inline-block;
    height:32px;
    width:32px;
    background-image: url('images/flag-bookmarks.png');
}
span.flag-bookmarks a.unflag-action span{
    background-position: 0px 32px;
}
span.flag-bookmarks a.unflag-action span:hover {
    background-position: 0px 0px;
}
span.flag-bookmarks a.flag-action span{
    background-position: 0px 0px;
}
span.flag-bookmarks a.flag-action span:hover {
    background-position: 0px 32px;
}
FatherShawn’s picture

I could not get this function to fire, but using my theme name instead of phptemplate worked great - hope this saves someone else some time.

Aanjan’s picture

Fatal error: Cannot redeclare acquia_marina_preprocess_flag() (previously declared in C:\wamp\www\drupal-6.22\sites\default\themes\acquia_marina\flag.tpl.php:44) in C:\wamp\www\drupal-6.22\sites\default\themes\acquia_marina\flag.tpl.php on line 49

Anonymous’s picture

function THEMENAME_preprocess_flag(&$vars) {
  $image_file = $vars['directory'] . '/flag-' . $vars['flag']->name . '-' . ($vars['action'] == 'flag' ? 'off' : 'on') . '.png';
  if (file_exists($image_file)) {
    $vars['link_text'] = theme('image', $image_file, $vars['link_title'], $vars['link_title']);
  }
}
wooody’s picture

Thanks a lot.

unimarkt’s picture

Should it also work for D7?
Thx for support

//edit:

Sorry: it's really easy: You just can insert <img src="path/to/image.png" /> in the "link text" field.

Pafla’s picture

How in Drupal 7

Will the image be flag0 or flag1

1 flag field, how to make 2 images when only 1 field?

Pafla

leseulsteve’s picture

Hi,

I've been looking all around the internet do this with the flags module and I think your solution is the most easy to "live" with after it's implementation.

I tried it on Drupal 7 and it just doesn't seem to "see" the added fonction in template.php.

All the png files are at the right place and I've emptied the caches too.

Would love to have toggle buttons instead of the text for a specific flag type

Someone can help me? :)

dotist’s picture

this worked for me in a D7 setup... if your template.php file isn't picking up the function, be sure you've got your theme name in the function title

function TEMPLATE_preprocess_flag(&$vars) {
    $image_file = $vars['directory'] . '/images/toggle-' . ($vars['action'] == 'flag' ? 'off' : 'on') . '.png';
    if (file_exists($image_file)) {
      $image = theme_image(array('path' => $image_file, 'attributes' => array('class' => array('MY-CLASS'))));
      $vars['link_text'] = $image;
    }
}
nerdoc’s picture

I enhanced the function a tiny bit and used it like this in D7.
This way I can have different images for different flags, all named like flag-<flag-machine-name>-<off|on>

function THEME_preprocess_flag(&$vars) {
    $state = ($vars['action'] == 'flag' ? 'off' : 'on');
    $image_file = $vars['directory'] . '/images/flag-' . $vars['flag']->name . '-' . $state . '.png';
    // Uncomment the following line when debugging.
//    drupal_set_message("Flag is looking for '$image_file'...");
    if (file_exists($image_file)) {
      $vars['link_text'] = theme_image(array('path' => $image_file, 'attributes' => 
          array('class' => array('flag-' . $vars['flag']->name . '-' . $state))));
    }
}

The copying/linking of flag.tpl.php into the theme directory is not necessary. Just add this code to your theme themplate.php file.

schnippy’s picture

For my D7 use case, I wanted to use the excellent fontawesome library instead of images for each state. Following droetker's example I came up with this simple snippet (the state names correspond directly with the font awesome aliases)

function THEME_preprocess_flag(&$vars) {
  switch($vars['flag']->name) {
  case "bookmark":
      $state = ($vars['action'] == 'flag' ? 'fa-bookmark' : 'fa-bookmark-o');
      break;
    case "flag":
      $state = ($vars['action'] == 'flag' ? 'fa-flag' : 'fa-flag-o');
      break;
  }
  $vars['link_text'] = "<span class='fa $state'></span>";
}
nithinkolekar’s picture

when used with ajax instead of icon text is replacing. Is there other hook for ajax process?

Mr. Red’s picture

On the bookmarks settings page, instead of the link text, insert an HTML tag
<i class="fa fa-star-o" aria-hidden="true"></i>