You are here

Exporting image field defaults in D7

CSÉCSY László's picture

We all love image fields' defaults: it's so easy to have a hero image for a product or a colleague's profile even when the editor does not provide one, with all the niceties such as displaying it with various image styles in a list, in the teaser or on the actual page. We all love Features module as it allows us to export Drupal 7 content types with all its settings.

However, we all hate when these two meet, as Features cannot (properly) export the image field defaults.
First of all, let's think about the cause. Image field values are stored as an integer referencing {file_managed}.fid which is the unique identifier of the file known to Drupal, automatically incremented by one for each uploaded file. Features however, has an outstanding problem with numeric, automatically incremented identifiers: when an object's ID is 321 on site A, the same object's ID could be 123 or anything on site B – because the identifier is assigned when the file is introduced to Drupal: when it's uploaded or when the feature is enabled. This confuses Features, so it tries to get around the problem in two ways: either it simply doesn't care about it (and export the ID as it is), or it doesn't care about it (and does not even try to export the object). In our specific case Features doesn't export the image fields' default values at all.

The solution is adding some code that introduce image fields' defaults to Drupal as Features should do. The recipe is easy: 1. create your own feature the regular way, then 2. create a file object when the feature is enabled (ie. when the module is installed), then 3. assign this file object's ID to the image field in question.

1. Create your own feature

Let's name it myfeature – and don't forget to add the image field in question. ;)

2. Create a file object when the module is enabled

Add this code to myfeature.install:

function myfeature_install() {
  // Create the product.hero_image field's default file reference.
  $filename = 'product-hero_image-default.png';
  $origfile = drupal_get_path('module', 'myfeature') . '/images/' . $filename;
  $uri = 'public://' . $filename;
  $file = (object) array(
    'uid' => 1,
    'filename' => $filename,
    'uri' => $origfile,
    'filemime' => 'image/png',
    'filesize' => filesize($origfile),
    'status' => FILE_STATUS_PERMANENT,
  );
  $file = file_copy($file, $uri, FILE_EXISTS_REPLACE);
  variable_set('myfeature_product_hero_image_fid', $file->fid);
}

3. Assign the file ID to the field in question

Add this code to myfeature.module:

/**
 * Implements hook_field_default_field_instances_alter().
 *
 * Set the default image.
 */
function myfeature_field_default_field_instances_alter(&$fields) {
  if (isset($fields['node-product-field_product_hero_image']['settings']['default_image'])) {
    $fid = $fields['node-product-field_product_hero_image']['settings']['default_image'];
    $fields['node-product-field_product_hero_image']['settings']['default_image'] = variable_get('myfeature_product_hero_image_fid', $fid);
  }
}

How does it work?

Any Features-exported feature is just like any other module: when it is installed, its hook_install() implementation runs. Our own implementation introduces the prepared image file to Drupal, then saves the file ID to a Drupal variable. Later on, hook_field_default_field_instances_alter() runs and checks whether the field in question has any default settings (eg. exported by Features). If default settings are found, the code records the old default ID, then retrieves the ID assigned to our own file during installation of the feature module, and updates the field (instance) definition as needed.

Actually, the default image file's ID is not exported, so it cannot be imported in the usual way – but there is an appropriate _alter hook to handle the situation: the final effect is the same if we exported the ID. Now we can use an image field's default as we would be able to do with any editor-supplied content: apply image styles to it, include it in lists, teasers, and so on.

I must mention that this technique may be applied not only to image fields, but to simple file fields as well.

(This article is based upon the gist available at https://gist.github.com/sprice/2003047 and its comments.)

Comments

There's also https://www.drupal.org/project/default_image_ft in case your interested. Thanks for showing an alternative way.