Hi! When you export an imagefield with a default image using features module, the exported code get the original fid of the default image (the default image on imagefield is stored inside the "files" table and it has a specific fid), but when you move the feature on a different installation it's quite improbable that you have the default image already stored inside the "files" table :-)

I suggest to handle the creation of the file entry on the files table when the image field is created (maybe a check could be done in order to keep updated the DB if the imagefield was already available in the destination site).

A good extra feature could be the possibility to export the image file too).

Thanks for the attention.

CommentFileSizeAuthor
#25 source_variable.png27.15 KBrobertoperuzzo
Support from Acquia helps fund testing for Drupal Acquia logo

Comments

hefox’s picture

Title: Incompatibility between imagefield default image and features module » Support imagefield default images
Category: bug » feature

Supporting other contrib modules, unless specially stated as supporting, is generally always a feature request, in general not just features.

FiNeX’s picture

Ok @hefox, I've initially submitted this issue as a bug because the support partially works so I've thought it was considered as "supported" :-) :-) :-)

Anyway thanks for the clarification :-)

daveparrish’s picture

I just wanted to point out that imagefield is in D7 core so it isn't really a contrib module anymore. I would also like to see features support imagefield default image in D6 and D7. Thanks.

mstrelan’s picture

Version: 6.x-1.2 » 7.x-1.x-dev

Updating version. Could features detect the default image, export it to the feature directory and then add a hook_install that adds the image to files_managed and then use the new fid as the default? Sounds difficult.

mstef’s picture

+1

mstef’s picture

This looks like it may be a rough one.. since the fields now rely on file id's, rather than paths.

Perhaps the only answer is exporting the field with no default, then using hook_field_default_fields_alter() to set the default...

This is my current, weird workaround, the only thing that sucks is that it causes the feature to then be overridden:

/**
 * Implements hook_field_default_fields_alter().
 * 
 * Alter the default fields right before they are cached into the database.
 *
 * @param &$fields
 *   By reference. The fields that have been declared by another feature.
 */
function mymodule_field_default_fields_alter(&$fields) {
  // Check the user picture field
  if (isset($fields['user-user-field_user_picture'])) {
    // See if a default image hasn't been set for this field yet
    if (!$fields['user-user-field_user_picture']['field_config']['settings']['default_image']) {
      // Dynamically set the user default image on the field
      $filename = 'default_user.png';
      $destination = file_default_scheme() . '://' . $filename;
      
      // Check to see if it exists already
      $query = db_select('file_managed', 'f');
      $query->fields('f', array('fid'));
      $query->condition('f.uri', $destination);
      $result = $query->execute();
      if ((!$fid = $result->fetchField())) {
        // Simulate an upload of the default user image 
        $file = new stdClass;
        $file->uid = 1;
        $file->filename = $filename;
        $file->status = 1;
        $file->timestamp = REQUEST_TIME;
        $file->uri = drupal_get_path('module', 'mymodule') . '/theme/images/' . $file->filename;
        $file->filemime = 'image/png';
        $file = file_copy($file, 'public://', FILE_EXISTS_REPLACE);
        $fid = $file->fid;
      }
      
      $fields['user-user-field_user_picture']['field_config']['settings']['default_image'] = (string) $fid;
    }
  }
}

EDIT (2): This snippet is better.. seems to be working fine.

wojtha’s picture

Thanks a lot Mike.

I've made your snippet little bit more flexible

/**
 * Implements hook_field_default_fields_alter().
 *
 * Alter the default fields right before they are cached into the database.
 *
 * @param &$fields
 *   By reference. The fields that have been declared by another feature.
 */
function mymodule_field_default_fields_alter(&$fields) {
  $source_dir = drupal_get_path('module', 'mymodule') . '/images';

  $field_default_images = array(
    'user-user-field_user_picture' => 'avatar.png',
    'field_collection_item-field_people-field_people_photo' => 'avatar.png',
    // etc...
  );

  foreach ($field_default_images as $field_name => $filename) {
    $source = $source_dir . '/' . $filename;
    $destination = 'default_images/' . $filename;
    if (isset($fields[$field_name])) {
      mymodule_set_default_image($fields[$field_name], $filename, $source, $destination);
    }
  }
}

function mymodule_set_default_image(&$field, $filename, $source, $destination) {
  // See if a default image hasn't been set for this field yet
  if (empty($field['field_config']['settings']['default_image'])) {
    // Dynamically set the user default image on the field
    $destination = file_default_scheme() . '://' . $destination;

    // Check to see if it exists already
    $result = db_select('file_managed', 'f')
      ->fields('f', array('fid'))
      ->condition('f.uri', $destination)
      ->execute();

    $fid = $result->fetchField();

    // Simulate an upload of the default user image
    if (!$fid && file_exists($source)) {
      $file = new stdClass;
      $file->filename = $filename;
      $file->timestamp = REQUEST_TIME;
      $file->uri = $source;
      $file->filemime = file_get_mimetype($source);
      $file->uid = 1;
      $file->status = 1;

      $file = file_copy($file, 'public://', FILE_EXISTS_REPLACE);

      $fid = $file->fid;
    }

    $field['field_config']['settings']['default_image'] = (string) $fid;
  }
}
obrienmd’s picture

Hrm, doesn't seem to be working for me. I still don't see a default image set in the field UI or when a node is added w/ no image (should then render the default, right?).

mstef’s picture

obrienmnd, make sure you edit the code to match your needs; field names, image names, paths, function name to make your module, etc, etc..

obrienmd’s picture

Yeah, I've been developing Drupal for a while and triple-checked all that. I think I just misunderstood how default images worked... Thanks!

Danny Englander’s picture

I ran into this same issue. I uploaded my desired default image to my custom imagefield on production, cloned my live DB to my local dev and then recreated the feature locally. Once I Git pulled to production, the feature did not want to be overridden and my default image was there! I am guessing this is not ideal in many cases and obviously not very time or cost effective but it was really a one-off thing I just needed to solve quickly. I could see this getting tricky as time goes by with more than one of these types of fields.

blazindrop’s picture

Kudos to everyone that worked on this. I used a variant of the patch from comment #7 and I no longer have overrides for default_image!

+1

glesage’s picture

Hey guys, I've been trying to do this with the current version of Drupal (7.26) but cannot seem to get this hook to do anything...

Where can I get info about it? Google search brings up nothing... Has the naming changed?

Thanks!

sphism’s picture

Issue summary: View changes

Thanks everyone for figuring this out.

Took a while to get this working. The hook used in #7 is deprecated, now that field has been split into field bases and field instances... so:

You can swap:
mymodule_content_field_default_field_bases_alter(&$fields)
for
mymodule_content_field_default_field_instances_alter(&$fields)
just make sure you use eg : node-article-field_image .... instead of field_image (see comments in code)

<?php
/**
 * Implements hook_field_default_field_bases_alter().
 *
 * Alter the default fields right before they are cached into the database.
 *
 * @param &$fields
 *   By reference. The fields that have been declared by another feature.
 */
function mymodule_content_field_default_field_bases_alter(&$fields) {
  $source_dir = drupal_get_path('module', 'mymodule') . '/default_images';
  $field_default_images = array(
    // Field Base uses the field name
    'field_image' => 'default.jpg',
    // Field Instance uses entity-bundle-field_name
    // 'node-article-field_image' => 'default.jpg',
  );
  foreach ($field_default_images as $field_name => $filename) {
    $source = $source_dir . '/' . $filename;
    $destination = 'default_images/' . $filename;
    if (isset($fields[$field_name])) {
      _mymodule_content_set_default_image($fields[$field_name], $filename, $source, $destination);
    }
  }
}

/**
 * Custom function to set the default image.
 * (It has an _ at the beginning to make sure it never accidentally gets fired as a hook)
 *
 * @param  array $field
 * @param  string $filename Filename of the source image.
 * @param  string $source Filepath of the source image.
 * @param  string $destination Filepath you want to save to.
 */
function _mymodule_content_set_default_image(&$field, $filename, $source, $destination) {
  // See if a default image hasn't been set for this field yet
  if (empty($field['settings']['default_image'])) {
    // Dynamically set the user default image on the field
    $destination = file_default_scheme() . '://' . $destination;
    // Check to see if it exists already
    $result = db_select('file_managed', 'f')
      ->fields('f', array('fid'))
      ->condition('f.uri', $destination)
      ->execute();
    $fid = $result->fetchField();
    // Simulate an upload of the default user image
    if (!$fid && file_exists($source)) {
      $file = new stdClass;
      $file->filename = $filename;
      $file->timestamp = REQUEST_TIME;
      $file->uri = $source;
      $file->filemime = file_get_mimetype($source);
      $file->uid = 1;
      $file->status = 1;
      $file = file_copy($file, 'public://', FILE_EXISTS_REPLACE);
      $fid = $file->fid;
    }
    // field_config key no longer seems to exist.
    // $field['field_config']['settings']['default_image'] = (string) $fid;
    // Use this instead.
    $field['settings']['default_image'] = (string) $fid;
  }
} ?>
sphism’s picture

Caveat with #14

the if statement:

if (empty($field['settings']['default_image'])) {
...
}

seems to make this only work once, then when you clear cache, this hook fires again, and since the default image is set it does nothing.... and I end up with no default image.

Hmm... So i removed the if statement, it's working, but I guess that means i can never manually override the default image, but i've not checked because i never need to.

pmackay’s picture

I'm a bit puzzled how useful Features can be in this case, given that when uploaded the default is stored in the /files directory. Feels like for a default there should be some way to store an image in the Feature itself and reference it there, a bit like a theme references a default logo. Does that sound logical at all or am I missing something?

Elin Yordanov’s picture

Category: Feature request » Bug report

Since image module is in core, this issue is not a feature request, but a bug.

mglaman’s picture

Category: Bug report » Feature request

@pmackay, that's what UUID features does - allows inline or file storage.

@pc-wurm it is not a bug because Drupal core does not support UUIDs, and the field instance utilizes a regular database 'fid'. In the same instance Features does not support UUIDs.

UUID is its own contrib and there is a UUID Features to blend the two. However, UUID features does not help accomplish this task. Should this get bumped over to UUID features to try and hijack field instance saving in the event of a default image?

askibinski’s picture

There is a contrib solution (workaround) for this problem in D7:
https://www.drupal.org/project/default_image_ft

a.milkovsky’s picture

#14 and #19 do not work with drupal commerce product display when images are in product entity

alesr’s picture

If you're looking for an update_hook_N() solution, here it is:

/**
 * Helper to replace default field base image.
 * 
 * @param $field_name
 * @param $filename
 * @param $source
 */
function _YOUR_MODULE_replace_default_image($field_name, $filename, $source) {
  // Load field info.
  $field = field_info_field($field_name);

  // Create new file object and get new fid.
  if (file_exists($source)) {
    $file = new stdClass;
    $file->filename = $filename;
    $file->timestamp = REQUEST_TIME;
    $file->uri = $source;
    $file->filemime = file_get_mimetype($source);
    $file->uid = 1;
    $file->status = 1;
    $file = file_copy($file, 'public://default_images', FILE_EXISTS_REPLACE);
    $fid = $file->fid;

    // Replace old fid with new fid.
    $field['settings']['default_image'] = (string) $fid;

    // Update field.
    field_update_field($field);
  }
}

/**
 * Update default image on field base.
 */
function YOUR_MODULE_update_N() {
  // $source should point to the new file. If you don't have "default_images"
  // folder in your module, create it.
  $field_name = 'NAME_OF_MY_FIELD';
  $filename = 'MY_NEW_DEFAULT_PHOTO.png';
  $source = drupal_get_path('module', 'intaview_global') . '/default_images'. '/' . $filename;
  // Replace default image for $field_name field base.
  _YOUR_MODULE_replace_default_image($field_name, $filename, $source);
}

You need "default_images" folder in your custom module's root folder where you'd put MY_NEW_DEFAULT_PHOTO image file and replace the field name NAME_OF_MY_FIELD to match your field name.
Also don't forget to replace YOUR_MODULE and YOUR_MODULE_update_N with your module name and update_hook number.

acidaniel’s picture

#21 Work Like a charm @alesr ++ you made my day!!

tengoku’s picture

@alesr solution works, however, when the feature is reverted again, the default image changes.. so that solution will not work on time.

https://www.drupal.org/project/default_image_ft works better for me.. featurizes de a variable_get/set that later on i can change in the admin form between environments

rreiss’s picture

The default_image_ft module works great for me.
Sounds like a sub-module ;)

robertoperuzzo’s picture

FileSize
27.15 KB

(#21 + #23) I merged the two solutions proposed by @alesr and @tengoku to solve their cons.

#21: as @tengoku said, when you revert the feature the default image changes
#23: you have to upload the default image (admin/content/default-image) at least one time for each environments.

With my solution you can upload and set your default image variable using hook_update_N().

First of all you have to install/enable Default image ft and set your variable name into content-type field (see the image).
 source_variable

Than you can create your module and add the following code into YOUR_MODULE_NAME.install file:

/**
 * Helper to replace default field base image.
 *
 * @param $filename
 * @param $source
 */
function _YOUR_MODULE_NAME_replace_default_image($filename, $source) {
  // Set the destination dir.
  $destination_dir = 'public://default_images';

  // Create new file object and get new fid.
  if (file_exists($source)
    && file_prepare_directory($destination_dir, FILE_CREATE_DIRECTORY)) {

    $file = new stdClass;
    $file->filename = $filename;
    $file->timestamp = REQUEST_TIME;
    $file->uri = $source;
    $file->filemime = file_get_mimetype($source);
    $file->uid = 1;
    $file->status = 1;
    $file = file_copy($file, $destination_dir, FILE_EXISTS_REPLACE);
    $fid = $file->fid;

    // Set the default_image_ft variable.
    variable_set('my_default_image_variable', $fid);
    watchdog('your_module_name', 'Setted my_default_image_variable default image.');
  }
}

/**
 * Set the default images for field_your_image.
 */
function YOUR_MODULE_NAME_update_7100() {
  $filename = 'my_default_image.jpg';
  $source = drupal_get_path('module', 'YOUR_MODULE_NAME')
    . '/images/' . $filename;
  // Replace default image for $field_name field base.
  _YOUR_MODULE_NAME_replace_default_image($filename, $source);
}

I improve the #21 code adding a further check
file_prepare_directory($destination_dir, FILE_CREATE_DIRECTORY)
which create the 'public://default_images' directory if it doesn't exist.
samgao’s picture

#21 #23 #25 are all helpful. I know this post is supposed for D7, but you will have the same issue on D8. A few things have been changed in D8, and Default image ft module is still not available yet, so I would like to put my solution for D8 here if anyone is interested.
(1) Upload the default image, and set a Drupal variable with hook_update_N().

\Drupal::state()->set('your_drupal_variable_name', $file);

(2) Implements hook_entity_prepare_view().

/**
 * Implements hook_entity_prepare_view().
 */
function your_module_entity_prepare_view($entity_type, array $entities, array $displays) {
  $change = FALSE;

  if ($entity_type == 'node') {
    $field_name = 'your_field_name';
    foreach ($entities as $entity) {
      $bundle = $entity->bundle();

      if ($bundle == 'your_bundle' && $entity->hasField($field_name)) {
        $field_value = $entity->get($field_name)->getValue();
        $display_component = $displays[$bundle]->getComponent($field_name);

        if (empty($field_value) && !empty($display_component)) {
          // Get the default image if it is set before.
          $default_image = \Drupal::state()->get('your_drupal_variable_name'');

          if ($default_image) {
            $entity->set($field_name, $default_image);
            $change = TRUE;
          }
        }
      }
    }
  }

  return $change;
}

(3) Implements hook_entity_display_build_alter(). This is only required if the image field is used as a custom view field.

/**
 * Implements hook_entity_display_build_alter().
 */
function your_module_entity_display_build_alter(&$build, $context) {
  $entity = $context['entity'];
  $entity_type = $entity->getEntityTypeId();
  $bundle = $entity->bundle();

  // If the field is used as a view field.
  if ($entity_type == 'node' && $bundle == 'your_bundle' && $context['view_mode'] == '_custom') {
    $field_name = 'your_field_name';
    $component = $context['display']->getComponent($field_name);

    if (!empty($component)) {
      $entities = [$entity];
      $displays = [$bundle => $context['display']];

      // Run through our entity_prepare to process the entity.
      $change = your_module_entity_prepare_view($entity_type, $entities, $displays);
      if ($change) {
        $build[$field_name] = $entity->{$field_name}->view($component);
      }
    }
  }
}