7.x system.install system_update_7061(&$sandbox)

Migrate upload.module data to the newly created file field.

Related topics

File

modules/system/system.install, line 2841
Install, update and uninstall functions for the system module.

Code

function system_update_7061(&$sandbox) {
  if (!db_table_exists('upload')) {
    return;
  }
  if (!isset($sandbox['progress'])) {
    
    // Delete stale rows from {upload} where the fid is not in the {files} table.
    db_delete('upload')->notExists(db_select('files', 'f')->fields('f', array(
      'fid',
    ))->where('f.fid = {upload}.fid'))->execute();
    
    // Delete stale rows from {upload} where the vid is not in the
    // {node_revision} table. The table has already been renamed in
    // node_update_7001().
    db_delete('upload')->notExists(db_select('node_revision', 'nr')->fields('nr', array(
      'vid',
    ))->where('nr.vid = {upload}.vid'))->execute();
    
    // Retrieve a list of node revisions that have uploaded files attached.
    // DISTINCT queries are expensive, especially when paged, so we store the
    // data in its own table for the duration of the update.
    if (!db_table_exists('system_update_7061')) {
      $table = array(
        'description' => t('Stores temporary data for system_update_7061.'),
        'fields' => array(
          'vid' => array(
            'type' => 'int',
          ),
        ),
        'primary key' => array(
          'vid',
        ),
      );
      db_create_table('system_update_7061', $table);
    }
    $query = db_select('upload', 'u');
    $query->distinct();
    $query->addField('u', 'vid');
    db_insert('system_update_7061')->from($query)->execute();
    
    // Retrieve a list of duplicate files with the same filepath. Only the
    // most-recently uploaded of these will be moved to the new {file_managed}
    // table (and all references will be updated to point to it), since
    // duplicate file URIs are not allowed in Drupal 7.
    // Since the Drupal 6 to 7 upgrade path leaves the {files} table behind
    // after it's done, custom or contributed modules which need to migrate
    // file references of their own can use a similar query to determine the
    // file IDs that duplicate filepaths were mapped to.
    $sandbox['duplicate_filepath_fids_to_use'] = db_query("SELECT filepath, MAX(fid) FROM {files} GROUP BY filepath HAVING COUNT(*) > 1")->fetchAllKeyed();
    
    // Initialize batch update information.
    $sandbox['progress'] = 0;
    $sandbox['last_vid_processed'] = -1;
    $sandbox['max'] = db_query("SELECT COUNT(*) FROM {system_update_7061}")->fetchField();
  }
  
  // Determine vids for this batch.
  // Process all files attached to a given revision during the same batch.
  $limit = variable_get('upload_update_batch_size', 100);
  $vids = db_query_range('SELECT vid FROM {system_update_7061} WHERE vid > :lastvid ORDER BY vid', 0, $limit, array(
    ':lastvid' => $sandbox['last_vid_processed'],
  ))->fetchCol();
  
  // Retrieve information on all the files attached to these revisions.
  if (!empty($vids)) {
    $node_revisions = array(
      
    );
    $result = db_query('SELECT u.fid, u.vid, u.list, u.description, n.nid, n.type, u.weight FROM {upload} u INNER JOIN {node_revision} nr ON u.vid = nr.vid INNER JOIN {node} n ON n.nid = nr.nid WHERE u.vid IN (:vids) ORDER BY u.vid, u.weight, u.fid', array(
      ':vids' => $vids,
    ));
    foreach ($result as $record) {
      
      // For each uploaded file, retrieve the corresponding data from the old
      // files table (since upload doesn't know about the new entry in the
      // file_managed table).
      $file = db_select('files', 'f')->fields('f', array(
        'fid',
        'uid',
        'filename',
        'filepath',
        'filemime',
        'filesize',
        'status',
        'timestamp',
      ))->condition('f.fid', $record->fid)->execute()->fetchAssoc();
      if (!$file) {
        continue;
      }
      
      // If this file has a duplicate filepath, replace it with the
      // most-recently uploaded file that has the same filepath.
      if (isset($sandbox['duplicate_filepath_fids_to_use'][$file['filepath']]) && $record->fid != $sandbox['duplicate_filepath_fids_to_use'][$file['filepath']]) {
        $file = db_select('files', 'f')->fields('f', array(
          'fid',
          'uid',
          'filename',
          'filepath',
          'filemime',
          'filesize',
          'status',
          'timestamp',
        ))->condition('f.fid', $sandbox['duplicate_filepath_fids_to_use'][$file['filepath']])->execute()->fetchAssoc();
      }
      
      // Add in the file information from the upload table.
      $file['description'] = $record->description;
      $file['display'] = $record->list;
      
      // Create one record for each revision that contains all the uploaded
      // files.
      $node_revisions[$record->vid]['nid'] = $record->nid;
      $node_revisions[$record->vid]['vid'] = $record->vid;
      $node_revisions[$record->vid]['type'] = $record->type;
      $node_revisions[$record->vid]['file'][LANGUAGE_NONE][] = $file;
    }
    
    // Now that we know which files belong to which revisions, update the
    // files'// database entries, and save a reference to each file in the
    // upload field on their node revisions.
    $basename = variable_get('file_directory_path', conf_path() . '/files');
    $scheme = file_default_scheme() . '://';
    foreach ($node_revisions as $vid => $revision) {
      foreach ($revision['file'][LANGUAGE_NONE] as $delta => $file) {
        
        // We will convert filepaths to URI using the default scheme
        // and stripping off the existing file directory path.
        $file['uri'] = $scheme . preg_replace('!^' . preg_quote($basename) . '!', '', $file['filepath']);
        
        // Normalize the URI but don't call file_stream_wrapper_uri_normalize()
        // directly, since that is a higher-level API function which invokes
        // hooks while validating the scheme, and those will not work during
        // the upgrade. Instead, use a simpler version that just assumes the
        // scheme from above is already valid.
        if (($file_uri_scheme = file_uri_scheme($file['uri'])) && ($file_uri_target = file_uri_target($file['uri']))) {
          $file['uri'] = $file_uri_scheme . '://' . $file_uri_target;
        }
        unset($file['filepath']);
        
        // Insert into the file_managed table.
        // Each fid should only be stored once in file_managed.
        db_merge('file_managed')->key(array(
          'fid' => $file['fid'],
        ))->fields(array(
          'uid' => $file['uid'],
          'filename' => $file['filename'],
          'uri' => $file['uri'],
          'filemime' => $file['filemime'],
          'filesize' => $file['filesize'],
          'status' => $file['status'],
          'timestamp' => $file['timestamp'],
        ))->execute();
        
        // Add the usage entry for the file.
        $file = (object) $file;
        file_usage_add($file, 'file', 'node', $revision['nid']);
        
        // Update the node revision's upload file field with the file data.
        $revision['file'][LANGUAGE_NONE][$delta] = array(
          'fid' => $file->fid,
          'display' => $file->display,
          'description' => $file->description,
        );
      }
      
      // Write the revision's upload field data into the field_upload tables.
      $node = (object) $revision;
      _update_7000_field_sql_storage_write('node', $node->type, $node->nid, $node->vid, 'upload', $node->file);
      
      // Update our progress information for the batch update.
      $sandbox['progress']++;
      $sandbox['last_vid_processed'] = $vid;
    }
  }
  
  // If less than limit node revisions were processed, the update process is
  // finished.
  if (count($vids) < $limit) {
    $finished = TRUE;
  }
  
  // If there's no max value then there's nothing to update and we're finished.
  if (empty($sandbox['max']) || isset($finished)) {
    db_drop_table('upload');
    db_drop_table('system_update_7061');
    return t('Upload module has been migrated to File module.');
  }
  else {
    
    // Indicate our current progress to the batch update system.
    $sandbox['#finished'] = $sandbox['progress'] / $sandbox['max'];
  }
}