1

I'm looking to create a script that makes a copy of the active spreadsheet, gives edit access to the person who created the sheet, but then sets me as the owner.

This works when I run it using the owner account, but I receive

Exception: Access denied: DriveApp

when triggered from a separate Google account.

I thought my script would add both accounts as editors, then set my personal as the owner.

Any idea why?

function makeCopy() {
  
var title = SpreadsheetApp.getActiveSheet().getRange("B1").getValue();
  SpreadsheetApp.getActive().toast("Your Sheet is being created", "Good Luck!", 5);


  var email = Session.getActiveUser().getEmail(); //get the user's email who ran the script
  var ssID = SpreadsheetApp.getActiveSpreadsheet().getId(); //get the current Spreadsheet's ID
  var copyID = DriveApp.getFileById(ssID).makeCopy().setName(title).getId(); //make a copy of the spreadsheet and retrieve the new ID
  DriveApp.getFileById(copyID).addEditor(email); //add the user as an editor to the new copy
   DriveApp.getFileById(copyID).addEditor("amadle11@gmail.com")
  DriveApp.getFileById(copyID).setOwner("amadle11@gmail.com");




var newsheet = SpreadsheetApp.openById(copyID);



// Get the file associated with the sheet
var file = DriveApp.getFileById(newsheet.getId());

// Set the permissions to allow anyone to edit the file
file.setSharing(DriveApp.Access.ANYONE, DriveApp.Permission.EDIT);


//var sheet = newsheet.getSheetByName("Sheet");
//sheet.deleteRows(15,3)



// var sheetId = "1234567890abcdefghijklm";

// Open the sheet in a new tab
var url = "https://docs.google.com/spreadsheets/d/" + copyID;
var html = HtmlService.createHtmlOutput('<script>window.open("' + url + '");</script>').setSandboxMode(HtmlService.SandboxMode.IFRAME);
SpreadsheetApp.getUi().showModelessDialog(html, "Showtime!");


}
Rubén
  • 34,714
  • 9
  • 70
  • 166
Anthony Madle
  • 371
  • 1
  • 10
  • In your situation, how is your script `makeCopy()` run? From your question, I couldn't understand it. I apologize for this. – Tanaike Jan 07 '23 at 23:56
  • Apologies. I have a button on a spreadsheet that anyone can edit, when that button is clicked, this function is triggered – Anthony Madle Jan 08 '23 at 00:04
  • @Tanaike the error is coming from my .setOwner line.. when I remove that, it runs but, but has the account who ran the script as the owner. Very confused why it won't transfer ownership – Anthony Madle Jan 08 '23 at 00:17
  • Try accessing second google account in another browser – Cooper Jan 08 '23 at 00:17
  • @Cooper I'm trying to run the script from a separate google account to mimic the workflow. Basically, a user will receive this link, and click PLAY. At that point, a copy of the sheet should be made, but the OWNER should be set to the email in the script. this is a test link https://docs.google.com/spreadsheets/d/15Js25e-tbEmJQ4vL29n20JaN2sVtRr37CUgKIh9ShNQ/edit#gid=0 – Anthony Madle Jan 08 '23 at 00:21

1 Answers1

2

Pattern 1:

Modification points:

  • In the current stage, it seems that the specification for transferring the file ownership has been changed. Ref I thought that this might be the reason for your current issue of Exception: Access denied:.

When these points are reflected in your script, how about the following modification?

Modified script:

Before you test this script, please enable Drive API at Advanced Google services.

function makeCopy() {
  var title = SpreadsheetApp.getActiveSheet().getRange("B1").getValue();
  SpreadsheetApp.getActive().toast("Your Sheet is being created", "Good Luck!", 5);
  var ssID = SpreadsheetApp.getActiveSpreadsheet().getId();
  var copyID = DriveApp.getFileById(ssID).makeCopy().setName(title).getId();
  Drive.Permissions.insert({ role: "writer", type: "user", value: "amadle11@gmail.com", pendingOwner: true }, copyID);
  DriveApp.getFileById(copyID).setSharing(DriveApp.Access.ANYONE, DriveApp.Permission.EDIT);
  var url = "https://docs.google.com/spreadsheets/d/" + copyID;
  var html = HtmlService.createHtmlOutput('<script>window.open("' + url + '");</script>').setSandboxMode(HtmlService.SandboxMode.IFRAME);
  SpreadsheetApp.getUi().showModelessDialog(html, "Showtime!");
}
  • When the user of another account runs this script, you (the owner) got an email for transferring the ownership of the file. When you accept it, the owner is transferred.

References:

Pattern 2:

From your following reply,

but is there a way for the permission to automatically go through? Unfortunately, I won't be able to accept the emails quick enough - I was hoping in the script it can just automatically set the owner. Any thoughts? This is amazing though.

In this case, as a pattern 2, I would like to propose using Web Apps as I mentioned in my comment.

Usage:

1. Prepare Google Apps Script.

Please copy and paste the following script to the script editor of Spreadsheet and save the script.

function doGet(e) {
  const { spreadsheetId, email, title, key } = e.parameter;
  if (key == "samplekey") {
    var file = DriveApp.getFileById(spreadsheetId).makeCopy(DriveApp.getRootFolder()).setName(title);
    file.addEditor(email);
    file.setSharing(DriveApp.Access.ANYONE, DriveApp.Permission.EDIT);
    return ContentService.createTextOutput(JSON.stringify({ id: file.getId() }));
  }
  return ContentService.createTextOutput(JSON.stringify({ error: "Invalid key." }));
}

function makeCopy() {
  var webAppsUrl = "https://script.google.com/macros/s/###/exec"; // Please set your Web Apps URL.

  var title = SpreadsheetApp.getActiveSheet().getRange("B1").getValue();
  SpreadsheetApp.getActive().toast("Your Sheet is being created", "Good Luck!", 5);
  var ssID = SpreadsheetApp.getActiveSpreadsheet().getId();
  var email = Session.getActiveUser().getEmail();
  var res = UrlFetchApp.fetch(`${webAppsUrl}?spreadsheetId=${ssID}&email=${email}&title=${title}&key=samplekey`);
  var obj = JSON.parse(res.getContentText());
  if (obj.error) return;
  var url = "https://docs.google.com/spreadsheets/d/" + obj.id;
  var html = HtmlService.createHtmlOutput('<script>window.open("' + url + '");</script>').setSandboxMode(HtmlService.SandboxMode.IFRAME);
  SpreadsheetApp.getUi().showModelessDialog(html, "Showtime!");
}
  • In this sample, the copied file is put to the root folder of your Google Drive. If you want to change this, please modify the above script.

2. Deploy Web Apps.

The detailed information can be seen in the official document.

Please set this using the new IDE of the script editor.

  1. On the script editor, at the top right of the script editor, please click "click Deploy" -> "New deployment".
  2. Please click "Select type" -> "Web App".
  3. Please input the information about the Web App in the fields under "Deployment configuration".
  4. Please select "Me" for "Execute as".
  5. Please select "Anyone" for "Who has access".
  6. Please click "Deploy" button.
  7. When "The Web App requires you to authorize access to your data." is shown, please click "Authorize access" button. And, please authorize the scopes.
  8. Copy the URL of the Web App. It's like https://script.google.com/macros/s/###/exec. This URL is used for your HTML.
  9. Please copy and paste your Web Apps URL to var webAppsUrl = "https://script.google.com/macros/s/###/exec"; of the above script.
  10. Please reflect the latest script to the Web Apps.

3. Testing.

When other user who is not you (the owner of Spreadsheet) is run makeCopy(), the active Spreadsheet is copied to your root folder by you (the owner). By this, you are the owner of copied Spreadsheet. And, the executed user is added as a writer.

Note:

References:

Tanaike
  • 181,128
  • 11
  • 97
  • 165
  • this is very helpful, but is there a way for the permission to automatically go through? Unfortunately, I won't be able to accept the emails quick enough - I was hoping in the script it can just automatically set the owner. Any thoughts? This is amazing though. – Anthony Madle Jan 08 '23 at 02:59
  • 1
    @Anthony Madle Thank you for replying. About `but is there a way for the permission to automatically go through?`, this could be achieved before. But, unfortunately, in the current stage, it seems that it is required to authorize the permission in the email. I apologize for this. I think that if the owner is transferred between accounts of a Google workspace, `setOwner` can be used. But, from your error message, I proposed the above modification. – Tanaike Jan 08 '23 at 08:20
  • 1
    @Anthony Madle About `I won't be able to accept the emails quick enough - I was hoping in the script it can just automatically set the owner`, in this case, about the process for copying a Spreadsheet, when the active Spreadsheet is copied, how about executing it as the owner by Web Apps? [Ref](https://developers.google.com/apps-script/guides/web) By this, I think that the owner is not changed and your expected result might be able to be used. But, if this was not your expected direction, I apologize. – Tanaike Jan 08 '23 at 09:39
  • 1
    @Anthony Madle About my latest comment, I added it as a pattern 2. Please confirm it. But, if that was not useful, I apologize. For example. when the installable OnEdit trigger is used by firing a checkbox, you can be the owner of the copied file. But, in this case, the user's email cannot be retrieved. In this case, I thought that this might not be your expected result. So, I proposed to use Web Apps. – Tanaike Jan 08 '23 at 11:44
  • this is amazing. I'm truly blown away - this operates exactly as I needed. I was spinning in circles trying to figure out a solution - confirmed!!!! – Anthony Madle Jan 08 '23 at 17:32
  • 1
    @Anthony Madle Thank you for replying and testing it again. I'm glad your issue was resolved. Thank you, too. – Tanaike Jan 09 '23 at 01:36
  • @Anthony Madle When Drive API is used on the new owner side, the ownership transfer can authorize without using the email. So, I published this in my blog. https://tanaikech.github.io/2023/01/09/transferring-owner-of-file-to-other-user-using-google-apps-script/ But, in your situation, I think that pattern 2 in my answer might be useful. – Tanaike Jan 09 '23 at 07:00