Stuck with migration of user pictures.
I have Drupal 7 with Migration 7.x-2.2 on board.
Source DB have user_avatar field with plain filename in it with user's avatar.

Destination (user.inc) gets picture as file object. But MigrateFileFieldHandler doesn't handle user->picture fields as this is not actual file field.

What is the best way to migrate those without heavy copy-pasting file_* routines.

Thanks.

Comments

TahitiPetey’s picture

subscribe

Andrey Zakharov’s picture

I figured this out.

  1. Copy all avatar images to public://pictures/ (variable_get('user_picture_path', 'pictures'))
  2. Define AvatarsMigration extends Migration with source of just field user_avatar from user table, and destination MigrateDestinationFile (see below)
  3. Define UsersMigration extends Migration as usual entity migration with
    $this->addFieldMapping('picture', 'user_avatar')
                  ->sourceMigration('Avatars');
    

class AvatarsMigration extends Migration {

   private $base_path;

   public function __construct() {
      parent::__construct();

      $this->base_path = 'public://' . variable_get('user_picture_path', 'pictures') . '/';

      $this->description = t('User avatars images');

      $this->map = new MigrateSQLMap($this->machineName,
                      array('user_avatar' => array(
                              'type' => 'varchar', 'length' => 100,
                              'not null' => TRUE,
                              'description' => 'Image URI.'
                      )),
                      MigrateDestinationFile::getKeySchema());

      $query = Database::getConnection('old.example.ru')
              ->select('users', 'u')
              ->fields('u', array('user_avatar'))
              ->isNotNull('user_avatar')
              ->condition('user_avatar', '', '!=');
      $query->distinct();
      $this->source = new MigrateSourceSQL($query, array(), null, array('map_joinable' => false));

      $this->destination = new MigrateDestinationFile(array(
                  'copy_file' => false,
                  'preserve_files' => true,
              ));

// Just map the incoming URL to the destination's 'uri'
      $this->addFieldMapping('uri', 'user_avatar')->callbacks(array($this, 'add_path'));
      $this->addUnmigratedDestinations(array('fid', 'uid', 'status', 'filemime', 'timestamp'));
   }

   public function add_path($uri) {
      return $this->base_path . $uri;
   }
}
class UsersMigration extends Migration {

   public function __construct() {

      // Always call the parent constructor first for basic setup
      parent::__construct();

      $this->dependencies = array('Avatars');
      
      $this->issuePattern = 'http://drupal.org/node/:id:';

      $query = Database::getConnection('old.example.ru')
              ->select('users', 'u')
              ->fields('u')
              ->condition('user_lastvisit', time() - 29030400, '>')
      ;

      $this->source = new MigrateSourceSQL($query, array(), null, array('map_joinable' => false));
      $this->destination = new MigrateDestinationUser();

      $this->map = new MigrateSQLMap($this->machineName,
                      array('user_id' => array(
                              'type' => 'int',
                              'unsigned' => TRUE,
                              'not null' => TRUE,
                      )),
                      MigrateDestinationUser::getKeySchema()
      );

      $this->addFieldMapping('name', 'user_name');
      $this->addFieldMapping('mail', 'user_email');
      $this->addFieldMapping('created', 'user_joined');
      $this->addFieldMapping('access', 'user_lastvisit');
      $this->addFieldMapping('signature', 'user_sig');
      $this->addFieldMapping('signature_format', 'user_sig_format')->defaultValue('bbcode');
      $this->addFieldMapping('field_icq', 'user_icq');
      $this->addFieldMapping('field_website', 'user_web')->callbacks(array($this, 'callback_addhttp'));
      $this->addFieldMapping('field_birthday', 'user_birthdate');
      $this->addFieldMapping('field_location', 'user_location');
      $this->addFieldMapping('picture', 'user_avatar')
              ->sourceMigration('Avatars');
   }

   public function prepareRow($row) {
      if (empty($row->user_icq))
         unset($row->user_icq);

      if (empty($row->user_web))
         unset($row->user_web);
      //dpm($row->user_web);

      if (empty($row->user_location))
         unset($row->user_location);

      if (empty($row->user_birthdate))
         unset($row->user_birthdate);

      if (empty($row->user_avatar)) 
         unset($row->user_avatar);
      
   }

   protected function callback_addhttp($dest) {
      if (!empty($dest) && (strpos($dest, 'http://') === false)) {
         $dest = 'http://' . $dest;
      }
      return $dest;
   }

}
Andrey Zakharov’s picture

Assigned: Unassigned » Andrey Zakharov
Status: Active » Closed (works as designed)
elBradford’s picture

This was helpful - it seemed to me that such a common migration as Drupal 6 to Drupal 7 would have an easier solution with regards to user avatars. Thanks for the post.

empowerfull’s picture

Issue summary: View changes

There's a much easier way. For those of us pulling from a non-Drupal site user by user with image variable $avatar:

// First define and upload the file. It will automatically return a fid in the file object and upload the file correctly.
$file = new StdClass();
$file->uid = 1;
$file->uri = "../" . $avatar;
$file->filemime = file_get_mimetype($file->uri);
$file->status = 1;
$file->display = 1;
$file->description = "";
$dest = file_default_scheme() . '://pictures';
$file = file_copy($file, $dest);

// Then set up the fields you wish to migrate. To tie the image file to the user fields, simply pass in the $file->fid
//set up the user fields
$fields = array(
'name' => $username,
'mail' => $email,
'pass' => $password,
'status' => 1,
'timezone' => $tz,
'init' => $email,
'roles' => array(
DRUPAL_AUTHENTICATED_RID => 'authenticated user',
),
'picture'=> $file->fid
);

//the first parameter is left blank so a new user is created
$account = user_save('', $fields);