Create a gallery out of an image field back

November 16, 2010
10 Comments

So today I'm going to go through how to create a gallery within a node out of a single image field allowing multiple values. I thought this would be useful for people because when I first did this it took me FOREVER to figure out on my own. I'm going to go over doing this in Drupal 7 but if you know Drupal 6 you should be able to figure it out from reading this.

Before we start you might wanna check out the demo, which is a live site but there really is no need to recreate this for our purposes. Also this is a rather simple implementation and might not be suitable for every project.

Creating the content type
First thing you'll need to do is create a content type (admin/structure/types/add). We're going to give this one the name 'shirt'. The other options on content types are totally dependent on your end goal, but for this demo I don't want comments so I set them to hidden and also unchecked 'display author information.'

On to the fields, I have several fields in here, a lot of them are not displayed but the only one that is important to this demo is the image field. Drupal 7 ships with an image field, we are going to use this but you could create a new one. Under 'Add existing field' I'm going to give my field the label 'Images' and select 'field_image' from the select list. Upon pressing 'save' you then have to edit the basic settings for the field in which you can add a default image if you'd like. Once you press 'save field settings' you are brought to the full field settings page. For this demo I checked that I want this field to be required but the most important option to check here is that 'Number of values' - this needs to be a number higher than 1 or theres no point in reading further! I usually select 'Unlimited.' Now just 'save.'

From here I usually go create my imagecache styles, in Drupal 7 image styles are in core so you don't need to download any additional modules!! We're going to need several image styles here. One is for the teaser display, one is for the full node display and the third is for the gallery thumbnails. Navigate to admin/config/media/image-styles/add and give your style a name, for our gallery thumbnail I used 'product_thumb' and added a Scale and crop effect that resizes the image to 60px x 60px. Repeat these steps for the other styles you want to use for the teaser and full node display and then navigate to admin/structure/types/manage/shirt/display. Here at the right of our image field we'll see a little gear icon, when clicked you can select the image style to use for the full node. Once saved you can click 'Teaser' above and select the image style to use for that.

I don't want to go into too much more detail here since these are just basics of creating a content type. From here you should create a shirt node so you have something to work with!

Template file
So now we're going to create a template file to use for this content type. The naming conventions for Drupal 7 are slightly different than Drupal 6 so instead of node-shirt.tpl.php we need to create node--shirt.tpl.php. Usually I duplicate node.tpl.php and rename. At the top of my templates I like to handle the teaser, so after the opening div I add some PHP to check if its the teaser then, for this demo in my teaser I want a link to the node that says 'buy now' and my teaser image.

<?php if($teaser): ?>
   <a href="<?php print $node_url; ?>" class="buynow"><?php print t('Buy Now'); ?></a>
   <div class="field-type-image">
    <?php print render($content['field_image'][0]); ?>
    </div>
<?php else: ?>
  <!-- full node goes here -->
<?php endif; ?>

If you're not familiar with Drupal, basically in the first line I am printing a link to the node and giving it a class name to use in my CSS. The PHP snippets here are basic, the variable $node_url is available to us so we use that instead of trying to hard code the url. The text of the link is being printed with PHP through the t() function. If you don't know why, its basically for translation purposes; even if you are not going to translate this site into another language its just best practice. Then I am printing the image, this code basically says render the first image that is uploaded to the node. Note that when you do that the outside div wrapper is not printed, which is why I added that in. Now onto the full node.

<?php else: ?>
   <div class="image">
      <?php print render($content['field_image'][1]); ?>
   </div>
   <div class="data">
      <?php print render($title_prefix); ?>
      <h2<?php print $title_attributes; ?>><?php print $title; ?></h2>
      <?php print render($title_suffix); ?>
      <div class="content clearfix"<?php print $content_attributes; ?>>
  <?php print render($content['body']); ?>
       </div>
   </div>
<?php endif; ?>

Here I'm printing the second image uploaded, because I only want my teaser image to show in the teaser. If you wanted the teaser image to appear here you could change that number to '0' and it would show here as well. The number here has nothing to do with the image styles we set before. Any image uploaded to that field when run through render() will show based on the images styles you set in the content type's display settings. So we could display the image at position '2' in the teaser and it will use the teaser image style.

After the image I'm printing the title, don't worry too much about the $title_prefix and $title_suffix variables, these are standard Drupal variables and are needed so modules can edit the output. Note that in most sites the title is part of the teaser not the full node. Your template will depend on the final design but for my design I need the title as part of the node so I added it in.

Tip: If you have the devel module installed, you can add

<?php
 krumo
($vars);
?>

to see available variables.

Creating the image thumbnails
Now we have to do something with the rest of the images, for this we need to do some PHP. In your template.php file, if there is not a preprocess_node() function then you'll need to add one.

<?php
function YOURTHEME_preprocess_node(&$vars) {
 
// Check if the image field exists
  
if(isset($vars['field_image'])) {
     
// create the variable that we will use to store our images
   
$vars['addtl_images'] = '';
      
// create a variable of the amount of images uploaded
   
$length = count($vars['field_image']);
     
      
// loop through the array of images
   
for($i=2; $i<$length; $i++) {
          
// variable containing the url to each image
       
$path = file_create_url($vars['field_image'][$i]['uri']);
         
// variable of each image
       
$image = theme_image_formatter(
            array(
           
'item' => $vars['field_image'][$i],
           
'image_style' => 'product_thumb',
           
'path' => array(
               
'path' => $path,
               
'options' => array(
                 
'html' => TRUE,
                   
'attributes' => array(
                       
'class' => 'prodthumb'
                       
)
                    )
                )
            ));
      
// adding each image to the initial variable
   
$vars['addtl_images'] .= $image;
    }
   }
}
?>

Ok so first, you will need to replace 'YOURTHEME' with the name of the theme you're using. So we're checking to see if the field exists, and if it does then we create the $addtl_images variable that we will print in our node template. Then we create a variable that is the length of the image array. Next we do a for loop, if you are unfamiliar with this, basically its saying start this variable at number '2', then repeat the function a certain amount of times which is the $length variable we created before, then increment our variable by 1 each time. If that doesn't make sense you can read up about the for loop function. Within the for loop we're creating the path to each image that we will use to build a link. We use file_create_url() and pass in the 'uri' of the image using our $i variable that will have a different value each time. Next we are creating a variable $image, that we will pass into our $addtl_images variable at the end each time the function is run.

The $image variable is using theme_image_formatter() to create a resized image linked to the original image with the class 'prodthumb' that we will use in our Javascript. In this function 'item' is the image we want to make a thumbnail, 'image_style' is the the name of the image preset you created before and 'path' is an array containing the path we want to link to and options (similar to the l() function) in which we need to specify that our link contains HTML and any attributes, which here is a class.

Ok if you got that then GREAT!

I'm not going to go over any CSS here because you should be able to do that on your own. Once you have this and you don't have a WSOD, open up your node--shirt.tpl.php and add the following:

<?php if($addtl_images): ?>
    <div class="additional_images">
         <?php print $addtl_images; ?>
    </div>
<?php endif; ?>

So your final template should be something like this:

<?php if($teaser): ?>
   <a href="<?php print $node_url; ?>" class="buynow"><?php print t('Buy Now'); ?></a>
   <div class="field-type-image">
    <?php print render($content['field_image'][0]); ?>
    </div>
<?php else: ?>
   <div class="image">
      <?php print render($content['field_image'][1]); ?>
   </div>
   <div class="data">
      <?php print render($title_prefix); ?>
      <h2<?php print $title_attributes; ?>><?php print $title; ?></h2>
      <?php print render($title_suffix); ?>
      <div class="content clearfix"<?php print $content_attributes; ?>>
  <?php print render($content['body']); ?>
          <?php if($addtl_images): ?>
              <div class="additional_images">
                  <?php print $addtl_images; ?>
              </div>
          <?php endif; ?>
       </div>
   </div>
<?php endif; ?>

Writing the JQuery
So now we need to add some JQuery to make this thing function. First your going to need to add hoverIntent to your site, if you don't know how to do this open your themes .info file and add scripts[] = hoverIntent.js Next if your theme doesn't have any JS you will need to create a file, which you can name whatever you want, just make sure you add it to the .info file on the next line after the hoverIntent script. In this file add the following:

(function ($) {
Drupal.behaviors.YOURTHEME = {
  attach: function(context) {
  // attach click function to our thumbnails which removes the default click action
  $('a.prodthumb').click(function(e){
  e.preventDefault();
  });
   // create a variable of the original image so that we can add it back later
   var originalimg = $('.node-shirt .image').children('img').attr('src');
  //hover over and out functions
function imgOver(){
          //grab the hrep attributes of the thumb to pass into the bigger image's source
  var image = $(this).attr('href');
  $(this).parents('.node-shirt').children('.image').children('img').attr('src', image);
}
function imgOut(){
            //revert back to our original image
    if($('.node-shirt .image').children('img').attr('src') == $(this).attr('href')) {
       $('.node-shirt .image').children('img').attr('src', originalimg);
            }
}
       // hoverIntent configuration
var config = {   
     sensitivity: 3, // number = sensitivity threshold (must be 1 or higher)   
     interval: 0, // number = milliseconds for onMouseOver polling interval   
     over: imgOver, // function = onMouseOver callback (REQUIRED)   
     timeout: 500, // number = milliseconds delay before onMouseOut   
     out: imgOut // function = onMouseOut callback (REQUIRED)   
};
        // attaching hoverIntent to our thumbnails
$('a.prodthumb').hoverIntent(config);
  }
}
})(jQuery);

Ok so thats it for the JQuery, its pretty basic. If you're confused about the wrapper around the functions this is just standard Drupal practice and you can read more about it here. First we attach a click function to our thumbnails that prevents them from just opening the image, it actually removes click functionality. Then we have our hover over function that grabs the href of the thumbnail link and passes it into the full image div. Then the hover out function that replaces the image with the original image. Then a few configuration options for hoverIntent and finally attaching the hoverIntent options to our thumbnails.

This seems like a lot but after you've done it once it gets much easier!! You could get more snazzy with more JQuery and whatnot but this is just a basic implementation for those who have limited knowledge. Enjoy! :)

Jason:

I was curious if there was any specific reason why you did the additional images in a theme preprocess function instead of just handling them in the node specific template like you do with the first image. This would seem to simplify by having all of the image handling code in one place.

Thanks for the article.

Guest:

I tried but the thumbnail path generated is wrong :
/?q=sites/default/files/styles/product_thumb/public/case3.png

my thumbnail images are in : /?q=sites/default/files/styles/thumbnail/public/

amandar:

Jason, I just do that because thats what I'm used to and I prefer to keep most php out of my templates apart from if statements and printing content.

amandar:

Guest - the reason the path is being generated wrong is because you did not replace the image style in my code with the image style you are using. Mine is "product_thumb" but yours is "thumbnail."

Frank del Cayo :

nice post :-), I will give u a try så i am back and will let you know how it was ;-), are u the one from the tatoos at the demo Site?

Guest:

great tutorial!!!
i love you !!

Guest:

great tutorial !!

drupizee:

Hi
thank you for this very educative blog, it has helped me solve a bit of my issue. I Must mention I am totally new to drupal and this is my first drupal site. I am using drupal 7.

I followed this tutorial and I was successful in displaying an image in my node, but what I needed to achieve is different, I have two content types: Profile and projects where I used a node reference to link projects to a profile and vice versa:

Node type profile has a node reference field: Project worked;
Node type Projects has a reference field : Staff involved-- this provides a links to all staff related with a project

in the Project Content type I don't want just the link, instead I want to access the images that was uploaded in the profile page and let it link to the node( i.e not just a text link which the reference module provides but I want to access the properties of the referenced node)

After searching on the internet,i have been able to access the image field from the profile node successfully with the code:

<?php foreach ((array)$field_staff_involved as $value) {$value2 = node_load($value[nid]);$nodetoview = $value2;$var = theme_image_formatter(array(   'item' => $nodetoview->field_profile_image['und'][0],   'image_style' => 'smallsizeimage'     ) );print $var;}?>
However I will like for the images to link to its content. please how do I go about doing this? I will appreciate any help.

thanks
zee

amandar:

drupizee you should be able to just throw that $var in a link function and print it out:

<?php $link = l($var, 'node/'. $nodetoview->nid, array(attributes => ('html' => TRUE)));?>

Theo:

Amanda, you rock for this!

I'm new to Drupal and this little tut alone helped wrap my head around so many concepts.

Thanks :)