Block Visibility using PHP

in PHP, drupal, blocks

It has become quite the norm for clients to ask things that are not ready "out of the box". One of the most common is asking if a banner (usually an OpenX zone) can be displayed only in a site area, plus all nodes of a specific term / content type. To accomodate this, you need to delve into PHP visibility rules.

If you are comfortable with PHP, it should be much of a problem -- just write any code using the Drupal API that returns true if the block should be visible, false otherwise. There however a few snippets that I've found extremely useful, plus a couple of functions not apparent without a bit of poking around, that I'd like to share.

Be very careful when pasting any of these snippets -- depending on your server configuration, errors on these snippets can fill your page with errors, display white pages instead of content, or simply display an error line. Be sure to match the brackets and parentheses, as that's the most common cause of trouble.

  • Don't forget to change your Page Specific Validation Settings to PHP code otherwise none of these will work!
  • Always embed these in PHP tags. This means always start with "<?php" and end with "?>". This is not shown for each snippet, but you should have all your code within such a pair.

Displaying a block on every page other than the front page

This doesn't actually require PHP, but it's good to have as a snippet to use in combination with the other snippets below. The key here is the function drupal_is_front_page() which conveniently returns true when on the front page. Here's the snippet to use:

if ( drupal_is_front_page() ) {
  return true;
}

If you only want to check for the front page, you can directly invoke drupal_is_front_page() as a return value (ie simply write return drupal_is_front_page(); )

Displaying a block only on a specific path

You need to remember here that regardless of what happens on your browser address bar, all nodes are using paths of the form node/1234, where 1234 is their node id. In addition, you should know that Views only use their alliased paths.

Therefore, when refering to a node path, you need to check for node/XX, whereas for system paths (eg admin) or views paths you need the aliased view. 

The key function here is arg(). The function takes only one parameter, that defines which part of the address you're interested in.

For example, if the address you are currently at is http://mydomain.com/admin/build/blocks/list ( that's youre block list page ):

arg(0) is "admin", arg(1) is "build" etc

If you were at an aliased node, for example http://mydomain.com/content/my-cool-story :

arg(0) is "node", arg(1) is a number representing the node id.

Finally, you should know that when using a taxonomy term page, the "real" path is taxonomy/term/XX, where XX is the id of the term

Now let's put all that together!

if ( arg(0) == 'admin') {
  return false; // Don't show block in admin pages
}

if ( arg(0) == 'node' && is_numeric(arg(1))) {
  return false; // Don't show on single article pages (node/1234)
}

if ( arg(0) == 'taxonomy' && arg(1) == 'term' && is_numeric(arg(2))) {
  retrun false; // Don't show on taxonomy term pages
}

if ( arg(0) == 'myviewpage' ) {
  return false; // Don't show on a view with a page at 'myviewpage'
}

If you're having trouble with a path, you can always just add a line to display the current path as your code tests it using the following line:

drupal_set_message(arg(0).'/'.arg(1).'/'.arg(2).'/'.arg(3));

This will show the path the way the code tests it. Be warned that all users will see this debug information, as the visibility of a block is checked on every page!

Display a block only on single node pages of a specific content type

Let's delve in something a bit more advanced. Using the above, you can ascertain when you are on a single node page (ie a single article). However, we haven't discussed how to display on single article pages of a specific type, eg show the block only on Story nodes, but not on Page nodes.

The way to do that is to load the node object, and check its properties -- don't worry about a performance hit, since node objects are statically cached, and the object will be loaded for this page anyway.

// First make sure we're on a single node
if ( arg(0)=='node' && is_numeric(arg(1))) {

  // Use the node id to load the node object
  $node = node_load(arg(1));
  // Test for the specific content type. 
  if ( $node->type == 'story' ) {
    return true;
  } 
}
return false;

You should obviously substitute story with the content type you're interested in. However, you should remember to only use the machine name -- that the one that only has lower case letter and underscores, and can be found in your content type list under the column "Type".

 

Displaying a block only on single node pages that have a specific term

This is a slight variation to the above -- lets assume you are using story nodes, and you have created a vocabulary attached to it. When you create your nodes, you attach one or more terms to it, and want your block to only appear when the node has a specific term attached to it (eg you have a vocabulary for story type categories, and want a promotion to run only on Horror stories, not Romance).

Your first step will be to acquire the id of the term(s) you are interested in. To do that, you can simply go to the term list of your vocabulary ( admin/content/taxonomy/list , click list terms next to the vocabulary of interest ) . You can either then hover over the edit link to see the path it links to, or just click on the edit link to see it in the address bar. In either case, you're looking for the number at appears after term/ . Eg in the link:

http://www.hexblot.gr/admin/content/taxonomy/edit/term/14?destination=admin%2Fcontent%2Ftaxonomy%2F4

The term id here is 14. Now on to the code!

//First make sure we're on a single node
if( arg(0)=='node' && is_numeric(arg(1)) ) {
  // Load the node using its id
  $node = node_load(arg(1));
  // Check the attached terms array using only the keys 
  if ( in_array(14, array_keys($node->taxonomy)) ) {
    return true;
  }
}
return false;