Image Upload Block

I have come across many times where I need to create a block that has an image, with an image style, as the main content of the block. There are some great modules out there that can help you accomplish this, such as: Bean and Fieldable Panel Panes, which essentially give you fieldable blocks and panes, but what if you want to create this on your own, in your own custom module, read on.

There are some things that you must do in Drupal 7 to get an image upload in a block to work properly. Here is a step by step approach to writing your own block.

First, let's start by declaring our block:

<?php
/**
 * Implements hook_block_info().
 */
function myblock_block_info() {
  
// Declaring your block.
  
$blocks['my_image_block'] = array(
    
'info' => t('My Image Block'),
  );

  return 

$blocks;
}
?>

Nothing special there… Then for the configuration of the block we will use hook_block_configure().

<?php
/**
 * Implements hook_block_configure().
 */
function myblock_block_configure($delta '') {
  
$form = array();
  if (
$delta == 'my_image_block') {
    
$form['my_image_block_image'] = array(
      
'#type' => 'managed_file',
      
'#title' => t('Upload Block Image'),
      
'#size' => 40,
      
'#description' => t('Upload your image for the block.'),
      
'#upload_location' => 'public://',
      
'#default_value' => variable_get('my_image_block_image'),
    );
    
$form['my_image_block_url'] = array(
      
'#type' => 'textfield',
      
'#title' => t('URL'),
      
'#description' => t('The URL to link to.'),
      
'#default_value' => variable_get('my_image_block_url'),
    );
  }
  return 
$form;
}
?>

Here we have defined two fields that we are going to have a "managed file" type which allows us to have a complete ajax/progress aware widget for uploading a file and saving it to the {file_managed} table. In this example, I added a url that we will wrap around the image with an tag (my_image_block_url).

With hook_block_save() we can save our items, but with the image we need to make sure that we set the status to a permanent file status and notify Drupal that we are using the file or else we will lose the image after Drupal runs cron and cleans up orphaned files.

<?php
/**
 * Implements hook_block_save().
 */
function myblock_block_save($delta ''$edit = array()) {
  if (
$delta == 'my_image_block') {
    
// Storing the file id
    
variable_set('my_image_block_image'$edit['my_image_block_image']);
    
$file file_load(variable_get('my_image_block_image'$edit['my_image_block_image']));
    if (
is_object($file)) {
      
$file->status FILE_STATUS_PERMANENT;
      
file_save($file);

      

// Get the bid so I can log this in file_usage table.
      
$bid db_query("SELECT bid FROM {block} WHERE module = :module AND delta = :delta",
        array(
':module' => 'myblock'':delta' => $delta)
      )->
fetchField();
      
file_usage_add($file'myblock''block'$bid);
    }

    

variable_set('my_image_block_url'$edit['my_image_block_url']);
  }
}
?>

Here we load the file, set it to a permanent status, and save the file back to the database with file_save(). Now we have to tell Drupal that something is using the file we just uploaded. We get the block id with db_query() and then record that a module is using the file with the file_usage_add() function. THIS IS A VERY IMPORTANT STEP.

Now that everything is saved properly, we can go ahead and output this image with a link that we are able to output as a renderable array:

<?php
/**
 * Implements hook_block_view().
 */
function myblock_block_view($delta '') {
  
$block = array();

  switch (

$delta) {
    case 
'my_image_block':
      
$file file_load(variable_get('my_image_block_image'));
      );
      if (
file_exists($file->uri)) {
        
$image = array(
          
'#theme' => 'image_style',
          
'#path' => $file->uri,
          
'#style_name' => 'block_image_style',
        );
        
$content['image_link'] = array(
          
'#theme' => 'link',
          
'#theme_wrappers' => array('myblock_item_wrapper'),
          
'#wrapper_class' => 'my-block-image',
          
'#text' => drupal_render($image),
          
'#path' => variable_get('my_image_block_url'),
          
'#options' => array(
            
'attributes' => array(),
            
'html' => TRUE,
          ),
        );
      }

      

$block['subject'] = t('My New Block with an Image');
      
$block['content'] = $content;
      break;
  }
  return 
$block;
}
?>

Using hook_block_view() we need to load the file with file_load() in order to construct the image for use. Into file_load(), we pass in the variable that we set in hook_block_configure which returns the object representing the file. In the $image variable we build a renderable array for the image. Using ‘#theme’ => ‘image_style’, we need to pass two arguments to the theme function, path and style_name. We set the #path argument to the uri for the image. The #style_name is the machine name of an image style that gives your image the proper styling.

Now within another renderable array, the $content array, we can create an image_link to wrap this image with a link tag. Using ‘#theme’ => ‘link’ we need to pass it three arguments: text, path, and options. For the text argument I render out the image render array I created earlier, ‘#text’ => drupal_render($image), and make sure I set ‘html’ to TRUE in the ‘#options’ array, because the ‘#text’ option is html that is getting passed in. The ‘#path’ argument takes the URI for the location we want to send the user when a user clicks the image.

I also needed a wrapping div around my link, so I used #theme_wrappers to accomplish this (more on this at Disobey.com).

Well there ya go... an option for a custom image upload block rather than using a 3rd party module for a block with an image upload. Enjoy!

Comments

tfjonz (not verified)

I like your use of render arrays. Nicely done.

Michael

Wow sweet! I'll have to keep this in mind for the next time I am creating an image block!

Lance (not verified)

This is great, thank you!

One issue: old files are not deleted; if you replace the file you uploaded, the old file is still in the system. Proof: upload a file, save; edit block, replace file, save. Get fid of saved file. Next, use file_load with the last fid -1; this will load the previously saved file (assuming other files haven't been uploaded since).

Lance (not verified)

Adding this to the save function seems to work:

<?php
$current = variable_get("my_var", NULL);
$current_fid = isset($current["report"]["fid"]) ? $current["report"]["fid"] : NULL;

if($current_fid){
// Replacing or deleting
if($data["report"]["fid"] != $current_fid){

// Remove existing file
$file = file_load($current_fid);
if(is_object($file)) {
file_usage_delete($file, "my_module", "report");
file_delete($file);
watchdog("my_module", "File $file->uri removed", NULL, WATCHDOG_NOTICE);
} else {
watchdog("my_module", "Unable to remove existing file with id $current_fid", NULL, WATCHDOG_NOTICE);
}
}
} ?>

Add comment

Recent Posts

6.29.2012
By: Matthew Krick | 0 Comments
6.27.2012
By: Evan Jenkins | 4 Comments
3.14.2012
By: Tom Friedhof | 1 Comment
2.28.2012
By: Tom Friedhof | 9 Comments

From this Author

6.27.2012
By: Evan Jenkins | 4 Comments
Twitter icon
Facebook icon
Flickr icon