$file) { if (module_exists($module_name)) { include_once($file->filename); } } /** * Get a list of supported node fields for a given type. * * @param $type * String. Node type * * @return * Array ($field => $name) with all the supported fields * for this node type. */ function migrate_fields($type) { static $fields = array(); if (!isset($fields[$type])) { drupal_set_message("$type"); $fields[$type] = migrate_invoke_all('destination', 'fields', 'node'); asort($fields[$type]); } return $fields[$type]; } function migrate_migrate_destination_types() { static $nodetypes = NULL; if (!isset($nodetypes)) { $nodetypes = array(); foreach (node_get_types() as $type => $info) { $nodetypes[$type] = $info->name; } asort($nodetypes); } $types = array( 'comment' => array('comment' => t('Comment')), 'node' => $nodetypes, 'user' => user_roles(TRUE), ); return $types; } function migrate_migrate_destination_delete_node($nid) { // If we've created associations in {upload} between imported nodes // and files directly copied to the Drupal files directory (e.g. using // the auditfiles module), deleting the nodes deletes the files (not // what we typically want when going through clear/import cycles). We // short-circuit this behavior by explicitly deleting the {upload} // table entries for this node, so the upload module leaves the files alone. if (db_table_exists('upload')) { db_query("DELETE FROM {upload} WHERE nid=%d", $nid); } node_delete($nid); } function migrate_migrate_destination_import_node($tblinfo, $row) { $node = (object)array(); $node->type = $tblinfo->desttype; // Make sure the default comment settings are applied $function = 'comment_nodeapi'; if (function_exists($function)) { $function($node, "prepare"); } foreach ($tblinfo->fields as $destfield => $values) { if ($values['srcfield'] && $row->$values['srcfield']) { $node->$destfield = $row->$values['srcfield']; } else { $node->$destfield = $values['default_value']; } } // Prepare the node for import. We could have written the following loop // as: module_invoke_all('node_import_prepare', $node, $preview > 0); // but unfortunately module_invoke_all passes all argumens by value. $errors = array(); foreach (module_list() as $module_name) { $function = $module_name .'_migrate_destination_prepare_node'; if (function_exists($function)) { timer_start($function); $errors = array_merge($errors, (array)$function($node, $tblinfo, $row)); timer_stop($function); } } $success = TRUE; foreach ($errors as $error) { if ($error['level'] != MIGRATE_MESSAGE_INFORMATIONAL) { $success = FALSE; break; } } if ($success) { timer_start('node_save'); node_save($node); timer_stop('node_save'); // Call completion hooks, for any processing which needs to be done after node_save timer_start('completion hooks'); foreach (module_list() as $module_name) { $function = $module_name .'_migrate_destination_complete_node'; if (function_exists($function)) { timer_start($function); $errors = array_merge($errors, (array)$function($node, $tblinfo, $row)); timer_stop($function); } } timer_stop('completion hooks'); $sourcekey = $tblinfo->sourcekey; //@TODO: Factor out into caller //@TODO: Hack for Weblications ArticleID, which is a VARCHAR... Need a way to // identify the column type and handle appropriately... // Warnings suppressed - duplicate entries may be generated by updating // existing content if ($sourcekey == 'ArticleID' || $sourcekey == 'srcfile') { db_query("INSERT INTO $tblinfo->maptable ($sourcekey, nodeid, mcsid) VALUES('%s', %d, %d)", $row->$sourcekey, $node->nid, $tblinfo->mcsid); } else { // @TODO: Check first for existence, we may have updated an existing node // @TODO: Back out a new revision? db_query("INSERT INTO $tblinfo->maptable ($sourcekey, nodeid, mcsid) VALUES(%d, %d, %d)", $row->$sourcekey, $node->nid, $tblinfo->mcsid); } } return $errors; } function migrate_migrate_destination_xlat_node($nid) { return "node/$nid"; } function migrate_migrate_destination_fields_user($type) { // @TODO: Note hard-coded profile node type... $userfields = array( // Note user field is 'name', but this gets overridden from migrate_fields by the author // name for a node 'username' => t('User: Username'), 'pass' => t('User: Password'), 'mail' => t('User: Email address'), 'created' => t('User: Account created'), 'access' => t('User: Account last accessed'), 'login' => t('User: Last logged in'), ); // We're only interested in the CCK fields in this context $profile_fields = migrate_invoke_all('destination', 'fields', 'node', 'profile'); foreach ($profile_fields as $key => $value) { if (!preg_match('/^field_/', $key)) { unset($profile_fields[$key]); } else { $profile_fields[$key] = t('Profile: ').$value; } } $fields = array_merge($userfields, $profile_fields); return $fields; } function migrate_migrate_destination_delete_user($uid) { user_delete(array(), $uid); } function migrate_migrate_destination_import_user($tblinfo, $row) { $sourcekey = $tblinfo->sourcekey; // Begin building user object... $newuser = array(); $newuser['status'] = 1; $newuser['roles'][$tblinfo->desttype] = $tblinfo->desttype; // ...and content_profile node object // @TODO: Generalize $node = new StdClass; $node->type = 'profile'; foreach ($tblinfo->fields as $destfield => $values) { if ($values['srcfield'] && $row->$values['srcfield']) { $newvalue = $row->$values['srcfield']; } else { $newvalue = $values['default_value']; } if (preg_match('/^field_/', $destfield)) { $node->$destfield = $newvalue; } else { $newuser[$destfield] = $newvalue; } } // Prepare the user for import. We could have written the following loop // as: module_invoke_all('node_import_prepare', $node, $preview > 0); // but unfortunately module_invoke_all passes all arguments by value. $errors = array(); foreach (module_list() as $module_name) { $function = $module_name .'_migrate_destination_prepare_user'; if (function_exists($function)) { timer_start($function); $errors = array_merge($errors, (array)$function($newuser, $tblinfo, $row)); timer_stop($function); } } // @TODO: We're depending on email_registration to replace the name $newuser['name'] = 'dlksflkaj'; $username = $newuser['username']; unset($newuser['username']); $account = user_save(NULL, $newuser); if ($account) { db_query("INSERT INTO $tblinfo->maptable ($sourcekey, userid, mcsid) VALUES(%d, %d, %d)", $row->$sourcekey, $account->uid, $tblinfo->mcsid); // email_registration module overrides username, set the one we want here // @TODO: How do we generalize this case? if ($username) { db_query("UPDATE {users} SET name = '%s' WHERE uid = '%s'", $username, $account->uid); $account->name = $username; } unset($newuser['username']); // @TODO: From content_profile_registration_user_register_submit - generalize $node->uid = $account->uid; $node->title = $account->name; $node->name = $account->name; // Create the node. // Prepare the node for import. We could have written the following loop // as: module_invoke_all('node_import_prepare', $node, $preview > 0); // but unfortunately module_invoke_all passes all argumens by value. foreach (module_list() as $module_name) { $function = $module_name .'_migrate_destination_prepare_node'; if (function_exists($function)) { $errors = array_merge((array)$errors, (array)$function($node, $tblinfo, $row)); } } $node = node_submit($node); node_save($node); } } function migrate_migrate_destination_xlat_user($uid) { return "user/$uid"; } function migrate_migrate_destination_fields_comment($type) { $fields = array( 'subject' => t('Comment: Title'), 'comment' => t('Comment: Body'), 'name' => t('Comment: Author'), 'homepage' => t("Comment: Author's website"), 'timestamp' => t('Comment: Post date'), 'nid' => t('Comment: Node'), 'uid' => t('Comment: User'), ); return $fields; } function migrate_migrate_destination_delete_comment($cid) { $path = drupal_get_path('module', 'comment') . '/comment.admin.inc'; include_once($path); // Backdoor deletion - query stolen from comment_delete() $form = array(); $form_state = array(); $form['#comment'] = db_fetch_object(db_query( 'SELECT c.*, u.name AS registered_name, u.uid FROM {comments} c INNER JOIN {users} u ON u.uid = c.uid WHERE c.cid = %d', $cid)); comment_confirm_delete_submit($form, &$form_state); } function migrate_migrate_destination_import_comment($tblinfo, $row) { $comment = array(); $errors = array(); $sourcekey = $tblinfo->sourcekey; foreach ($tblinfo->fields as $destfield => $values) { if ($values['srcfield'] && $row->$values['srcfield']) { $comment[$destfield] = $row->$values['srcfield']; } else { $comment[$destfield] = $values['default_value']; } } // Prepare the comment for import. We could have use migrate_invoke_all() // but unfortunately arguments are passed by value foreach (module_list() as $module_name) { $function = $module_name .'_migrate_destination_prepare_comment'; if (function_exists($function)) { timer_start($function); $errors = array_merge($errors, (array)$function($comment, $tblinfo, $row)); timer_stop($function); } } // nid and uid are initially populated with the source file's ID - translate to // Drupal IDs if ($comment['nid']) { $comment['nid'] = _migrate_xlat_get_new_id('node', $comment['nid']); } if (!$comment['nid']) { $errors[] = array( 'sourceid' => $row->$sourcekey, 'message' => t('No node fond'), ); } if ($comment['uid']) { $comment['uid'] = _migrate_xlat_get_new_id('user', $comment['uid']); if (!$comment['name']) { $commentuser = user_load(array('uid' => $comment['uid'])); $comment['name'] = $commentuser->name; } } if (!$comment['name']) { $errors[] = array( 'sourceid' => $row->$sourcekey, 'message' => t('No user id or name found'), ); } // Do our best to interpret timestamps if ($comment['timestamp']) { $comment['timestamp'] = _migrate_valid_date($comment['timestamp']); if ($comment['timestamp'] <= 0) { $errors[] = array( 'sourceid' => $row->$sourcekey, 'message' => t('Provided timestamp is invalid'), ); } } if (count($errors) == 0) { $cid = comment_save($comment); if ($cid) { // comment_save() always saves time() as the timestamp, override if we have a value if ($comment['timestamp']) { db_query("UPDATE {comments} SET timestamp=%d WHERE cid=%d", $comment['timestamp'], $cid); } $sourcekey = $tblinfo->sourcekey; //@TODO: Factor out into caller db_query("INSERT INTO $tblinfo->maptable ($sourcekey, commentid, mcsid) VALUES(%d, %d, %d)", $row->$sourcekey, $cid, $tblinfo->mcsid); } else { $errors[] = array($row->$sourcekey => t('Comment not saved')); $errors[] = array( 'sourceid' => $row->$sourcekey, 'message' => t('Comment not saved'), ); } } return $errors; } /** * Check if a date is valid and return the correct * timestamp to use. Returns -1 if the date is not * considered valid. */ function _migrate_valid_date($date) { //TODO: really check whether the date is valid!! if (empty($date)) { return -1; } if (is_numeric($date) && $date > -1) { return $date; } // strtotime() doesn't recognize dashes as separators, change to slashes $date = str_replace('-', '/', $date); $time = strtotime($date); if ($time < 0 || !$time) { // Handles form YYYY-MM-DD HH:MM:SS.garbage if (strlen($date) > 19) { $time = strtotime(substr($date, 0, 19)); if ($time < 0 || !$time) { return -1; } } } return $time; }