In Finding where Drive hosting is being used in Sites, we created a couple of sheets by examining the hosted files referenced in a Google Site so that we could sort out the world after Google Hosting goes away. The next step was to identify those files and organize them, as described in Identifying hosted files. If you are happy with its proposals, now we can make a copy of the files that are going to be rehosted to the folder structure produced by the Identifying hosted files. Of course, you could do this manually, but automating it avoids mistakes, and more importantly, keeps the workflow going for future stages.

The report

You get 1 report, which now includes the new Drive id of each of the copied files. Any required folder structured are created according to the folderPath in the report.

cUseful library

I’ll be using the cUseful library, specifically this techniques.

Here’s the key for the cUseful library, and it’s also on github, or below.



It starts with the settings, which have been expanded to now look like this, and we’re mainly concerned with the Settings.copy section.

var Settings = (function(ns) {

   ns.sites = {
    search:"",                                 // enter a search term to concentrate on particular pages
    siteName:"share",                          // site name
    domain:"",                       // domain
    maxPages:0,                                // max pages - start low to test
    maxChunk:200                               // max children to read in one go
  }; = {
    sheetId:"19kXFMUh0DNTde_qtvsZ6FH4dAJ4YDCJhIJroLNT3vGY",         // sheet id to write to
    sheetName:'site-' + ns.sites.domain + '-' + ns.sites.siteName,  // sheetName to write to
    sheetHosting:'hosting-' + ns.sites.domain + '-' + ns.sites.siteName

  ns.identify = {,                  // where to write the results
    sheetFiles:"identify-" +,
    treatments: {
      fileName:"{name}",                          // template for output file name - can be {id}-{name}
      path:"/hosting/{mime}/",                    // template for file path  can be  {id}-{name}-{drivePath}-{mime}
      failOnMissing:false,                        // whether to fail if file is missing
  ns.copy = {,                  // where to write the summary
    sheetCopied:"copy-" +,
    treatments: {

  return ns;
})(Settings || {});

Some notes on the settings

  • This will create a new sheet, so sheetId and sheetCopied say where.
  • treatments are how to handle situations and to organize the files
    • removePreviousVersions – if true then any previous versions of files with the same name are removed prior to copying the new ones over
    • clearFolders – if true, this removes all files in each of the hosting folders. This will usually be false, unless you are doing some kind of a clear up.

The created folder structure

Here’s what you get, with a copy of each input file in the re-organized folder structure.

The code

It’s on GitHub, or below, or copy of developing version here. You’ll need the settings namespace at the beginning of this post too and of course the cUseful library reference.

 * copy the matched files from 
 * sheet created in look for hosting
 * make a new sheet with their new names
function copyHostingFiles () {
  // get the data
  var sc = Settings.copy;
  var si = Settings.identify;
  var du = cUseful.DriveUtils.setService(DriveApp);
  var backoff = cUseful.Utils.expBackoff;
  var fiddler = new cUseful.Fiddler();
  fiddler.setValues (
  // write the files
  var work = fiddler.getData().map(function (d) {
    // get the folder to write this to
    if (d.folderPath !== si.treatments.missingText) {
      // this is where the file is going
      var folder = du.getFolderFromPath(d.folderPath);
      // might need to create the receiving folder and its tree
      if (!folder) {
        Logger.log('creating ' + d.folderPath);
        var root = DriveApp;
        d.folderPath.split('/').forEach (function(e,i,a) {
          if (e) {
            var path =  a.slice(0,i+1).join("/");
            folder = du.getFolderFromPath (path);
            if (!folder) {

              folder = backoff (function () {
                return root.createFolder(e);
              Logger.log('created ' + du.getPathFromFolder(folder));
            root = folder;
      // now clear the folder if its required
      if (sc.treatments.clearFolders) {
        var files = backoff (function() {
          return folder.getFiles();
        while(files.hasNext()) {
          return backoff(function() {

      // now copy the file
      var oldFile = du.getFileById(d.originId);
      // remove any previous versions of this file name if required
      if (sc.treatments.removePreviousVersions) {
        var files = backoff(function () {
          return folder.getFilesByName(d.fileName);
        while(files.hasNext()) {
          backoff (function () {
      // copy
      var file = backoff(function() {
        return folder.createFile(oldFile.getBlob());
      // register
      d.copiedId = file.getId();

    else {
      d.copiedId = si.treatments.missingText;
    return d;

  // write out the result to a new sheet
  var ss = SpreadsheetApp.openById(sc.sheetId);
  var sh = ss.getSheetByName(sc.sheetCopied);
  // if if does exist, create it.
  if (!sh) {
    sh = ss.insertSheet(sc.sheetCopied);
  // clear it
  var fileFiddler = new cUseful.Fiddler();
For more like this see Google Apps Scripts Snippets
Why not join our forum, follow the blog or follow me on Twitter to ensure you get updates when they are available.