registerTypes(array('node')); } public function fields($entity_type, $bundle) { $fields = array(); if (module_exists('content')) { $content_info = _content_type_info(); foreach ($content_info['content types'][$bundle]['fields'] as $machine_name => $instance) { $fields[$machine_name] = t('Node:') . ' ' . $instance['widget']['label'] . ' (' . $instance['type'] . ')'; } } return $fields; } public function prepare(stdClass $entity, stdClass $row) { migrate_instrument_start('MigrateDestinationEntity->prepareFields'); // Look for Field API fields attached to this destination and handle appropriately $migration = Migration::currentMigration(); $destination = $migration->getDestination(); $entity_type = $destination->getEntityType(); $bundle = $destination->getBundle(); $info = content_types($bundle); $instances = $info['fields']; foreach ($instances as $machine_name => $instance) { if (isset($entity->$machine_name)) { // Normalize to an array if (!is_array($entity->$machine_name)) { $entity->$machine_name = array($entity->$machine_name); } $entity->$machine_name = migrate_field_handler_invoke_all($entity, $instance, $entity->$machine_name); } } migrate_instrument_stop('MigrateDestinationEntity->prepareFields'); } } abstract class MigrateFieldHandler extends MigrateHandler { // abstract function arguments(...) abstract public function prepare(stdClass $entity, array $instance, array $values); } class MigrateTextFieldHandler extends MigrateFieldHandler { public function __construct() { $this->registerTypes(array('text', 'text_long', 'text_with_summary')); } static function arguments($summary = NULL, $format = NULL, $language = NULL) { $arguments = array(); if (!is_null($summary)) { $arguments['summary'] = $summary; } if (!is_null($format)) { $arguments['format'] = $format; } if (!is_null($language)) { $arguments['language'] = $language; } return $arguments; } public function prepare(stdClass $entity, array $instance, array $values) { if (isset($values['arguments'])) { $arguments = $values['arguments']; unset($values['arguments']); } else { $arguments = array(); } $migration = Migration::currentMigration(); $destination = $migration->getDestination(); // Setup the standard Field API array for saving. $delta = 0; foreach ($values as $value) { $item = array(); if (isset($arguments['summary'])) { $item['summary'] = $arguments['summary']; } $format = isset($arguments['format']) ? $arguments['format'] : $destination->getTextFormat(); $item['format'] = $item['value_format'] = $format; $item['value'] = $value; $return[$delta] = $item; $delta++; } return isset($return) ? $return : NULL; } } class MigrateValueFieldHandler extends MigrateFieldHandler { public function __construct() { $this->registerTypes(array('value', 'list', 'list_boolean', 'list_number', 'list_text', 'number_integer', 'number_decimal', 'number_float')); } public function prepare(stdClass $entity, array $instance, array $values) { // Setup the standard Field API array for saving. $delta = 0; foreach ($values as $value) { $return[$delta]['value'] = $value; $delta++; } if (!isset($return)) { $return = NULL; } return $return; } } class MigrateFileFieldHandler extends MigrateFieldHandler { public function __construct() { $this->registerTypes(array('filefield')); } /** * Prepare file data for saving as a Field API file field. The job of this * handler is to make sure the file itself ends up in the right place, the * files table row is set up, and the files array returned for each file. * * @return array * Field API array suitable for inserting in the destination object. */ public function prepare(stdClass $entity, array $instance, array $values) { $migration = Migration::currentMigration(); $return = array(); $arguments = $values['arguments']; unset($values['arguments']); // One can override a file_function via CLI or drushrc.php if ($migration->getOption('file_function')) { $file_function = $migration->getOption('file_function'); } else { $file_function = $arguments['file_function']; } foreach ($values as $delta => $file_path) { if (!$file_path) { continue; } $message_saved = FALSE; if ($arguments['source_path']) { $full_path = rtrim($arguments['source_path'], DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . ltrim($file_path, DIRECTORY_SEPARATOR); } else { $full_path = $file_path; } // Set destination_dir, considering user tokens which filefield natively supports. // Also load $account or build a stub (faster) if (!module_exists('filefield_paths') && isset($entity->uid) && strpos($instance['widget']['file_path'], '[')) { $account = user_load(array('uid' => $entity->uid)); $destination_dir = filefield_widget_file_path($instance, $account); } else { // Set a default destination_dir. $destination_dir = file_directory_path() . DIRECTORY_SEPARATOR . $instance['widget']['file_path']; } // file_check_directory does not do $recursive so we create dir ourselves. @mkdir($destination_dir, 0777, TRUE); $destination_file = rtrim($destination_dir, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . basename($full_path); migrate_instrument_start('MigrateFileFieldHandler file_function'); switch ($file_function) { // These physically transfer the file to the destination, applying // the file_replace setting if the file already exists case 'file_copy': case 'file_move': // Check that source exists. If not, mark the entity as 'needs_update' and bail. // Sometimes the source file arrives later, when rsync is slower than DB. if (!file_exists($full_path)) { $message = t('Source file does not exist: !path', array('!path' => $full_path)); if ($arguments['throw_error']) { throw new MigrateException($message, MigrationBase::MESSAGE_ERROR); } else { $migration->saveMessage($message, MigrationBase::MESSAGE_INFORMATIONAL); } $message_saved = TRUE; // TODO $migration->needsUpdate = TRUE; continue; } // TODO: FILE_EXISTS_REPLACE is hardcoded // TODO: Should retrieve and pass validators in 2nd arg $file = field_file_save_file($full_path, array(), $destination_dir, $account); if ($file_function == 'file_move') { // TODO: Optimize by using rename() unlink($full_path); } break; case 'file_fast': static $fid; // Keep re-using an existing file. if (!isset($fid)) { $full_path = 'misc/druplicon.png'; $file = field_file_save_file($full_path, array(), $destination_dir, $account); // $file = file_copy($source, $destination_dir, FILE_EXISTS_RENAME); $fid = $file['fid']; } else { $file = array('fid' => $fid); } break; case 'file_link': // The file is copied by some outside process (e.g., rsync), and we // just need to make sure it's present and has a files table row. // Not present - skip migrate_instrument_start('file_link: file_exists'); if (!file_exists($full_path)) { migrate_instrument_stop('file_link: file_exists'); $message = t('File does not exist in Drupal files directory: !path', array('!path' => $full_path)); if ($arguments['throw_error']) { throw new MigrateException($message, MigrationBase::MESSAGE_ERROR); } else { $migration->saveMessage($message, MigrationBase::MESSAGE_INFORMATIONAL); } $message_saved = TRUE; // TODO $migration->needsUpdate = TRUE; continue; } migrate_instrument_stop('file_link: file_exists'); // Files table entry exists? Use that... migrate_instrument_start('file_link: select existing'); // Note that indexing files.filepath can be very helpful. $file = db_select('files', 'f') ->fields('f') ->condition('filepath', $full_path) ->execute() ->fetchAssoc(); migrate_instrument_stop('file_link: select existing'); if (!$file) { migrate_instrument_start('file_link: create file record'); $file = new stdClass; $file->uri = $full_path; $file->uid = isset($entity->uid) ? $entity->uid : 0; $file->filename = basename($full_path); $file->filepath = $full_path; $file->filemime = file_get_mimetype($full_path); $file->timestamp = $_SERVER['REQUEST_TIME']; $file->filesize = filesize($full_path); $file->status = FILE_STATUS_PERMANENT; drupal_write_record('files', $file); $file = (array)$file; migrate_instrument_stop('file_link: create file record'); } break; default: $migration->saveMessage(t('Unrecognized file_function: !func', array('!func' => $file_function))); $message_saved = TRUE; break; } migrate_instrument_stop('MigrateFileFieldHandler file_function'); if (isset($file) && is_array($file)) { // Build up a return object. $file['list'] = NULL; $file['data'] = array( 'alt' => isset($arguments['source_alt_name']) ? $arguments['source_alt_name'] : NULL, 'title' => isset($arguments['source_title_name']) ? $arguments['source_title_name'] : NULL, 'description' => isset($arguments['source_description_name']) ? $arguments['source_description_name'] : NULL, 'display' => isset($arguments['source_display_name']) ? $arguments['source_display_name'] : NULL, 'process' => TRUE, // This activates filefield_paths crazy move code. ); $return[] = $file; } elseif (!$message_saved) { throw new MigrateException(t('Unable to create file record for !path', array('!path' => $full_path)), Migration::MESSAGE_ERROR); } } return $return; } /* * Arguments for a file_field migration. * * @param source_path * Path to source file. * @param file_function * file_fast, file_move, or file_copy. * @param file_replace * Value of $replace in that file function. Does not apply to file_fast(). Defaults to FILE_EXISTS_RENAME. * @param language * Language of the text (defaults to destination language) * @param source_alt_name * @param source_title_name * @param source_description_name * @param source_display_name * @param throw_error * A non-existent file generates an INFORMATIONAL message by default. If this * parameter is TRUE, throw an error instead (abort node creation). * */ static function arguments($source_path = NULL, $file_function = 'file_copy', $file_replace = FILE_EXISTS_RENAME, $source_alt_name = NULL, $source_title_name = NULL, $source_description_name = NULL, $source_display_name = NULL, $throw_error = FALSE) { return get_defined_vars(); } } class MigrateNodeReferenceFieldHandler extends MigrateFieldHandler { public function __construct() { $this->registerTypes(array('nodereference')); } public function prepare(stdClass $entity, array $instance, array $values) { if (isset($values['arguments'])) { $arguments = $values['arguments']; unset($values['arguments']); } else { $arguments = array(); } // Setup the standard Field API array for saving. $delta = 0; $return = array(); foreach ($values as $value) { // Don't save empty references if ($value) { $return[$delta]['nid'] = $value; $delta++; } } return $return; } } class MigrateUserReferenceFieldHandler extends MigrateFieldHandler { public function __construct() { $this->registerTypes(array('userreference')); } public function prepare(stdClass $entity, array $instance, array $values) { if (isset($values['arguments'])) { $arguments = $values['arguments']; unset($values['arguments']); } else { $arguments = array(); } // Setup the standard Field API array for saving. $delta = 0; $return = array(); foreach ($values as $value) { // Don't save empty references if ($value) { $return[$delta]['uid'] = $value; $delta++; } } return $return; } } class MigrateDateFieldHandler extends MigrateFieldHandler { public function __construct() { $this->registerTypes(array('date', 'datestamp', 'datetime')); } public function prepare(stdClass $entity, array $instance, array $values) { // Setup the standard Field API array for saving. $delta = 0; foreach ($values as $value) { // Work from a timestamp $timestamp = MigrationBase::timestamp($value); // What does the destination field expect? if ($instance['type'] == 'datestamp') { // UNIX timestamp $value = $timestamp; } elseif ($instance['type'] == 'datetime') { // YYYY-MM-DD HH:MM:SS - prevent timezone application $value = format_date($timestamp, 'custom', 'Y-m-d H:i', 0); } elseif ($instance['type'] == 'date') { // ISO date: YYYY-MM-DDTHH:MM:SS - prevent timezone application $value = format_date($timestamp, 'custom', 'Y-m-dTH:i', 0); } else { // Should never get here } $return[$delta]['value'] = $value; $delta++; } if (!isset($return)) { $return = NULL; } return $return; } } class MigrateEmailFieldHandler extends MigrateFieldHandler { public function __construct() { $this->registerTypes(array('email')); } // Copied from ValueFieldHandler::prepare except the propname is email. public function prepare(stdClass $entity, array $instance, array $values) { // Setup the standard Field API array for saving. $delta = 0; foreach ($values as $value) { $return[$delta]['email'] = $value; $delta++; } if (!isset($return)) { $return = NULL; } return $return; } }