Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Transfer ownership of a file to another user in Google Apps Script

I have created a little upload UI which I include in a Google Sites page. It let's a user upload a document. Since the script is published it runs under my account and the uploaded file is uploaded to my google drive. I can from apps script add the person that uploaded the file as an editor, but what I would like to do is make them the owner of a file. (from apps script you can do a .getOwner() on a file... but not a .setOwner() ..... does anyone know a workaround?

like image 312
Spithout Avatar asked May 29 '12 09:05

Spithout


3 Answers

Unfortunately changing the owner of a file isn't supported in Apps Script. Issue 74 is a feature request to add this ability, and if you star the issue you'll show your support for the feature and get notified of updates.

------EDITED------

Now there is a handy method called setOwner, which can be found here: https://developers.google.com/apps-script/reference/drive/file#setOwner(User)

There's also the possibility to pass the new owner's email address instead of the User object, which is even more handy in some cases: https://developers.google.com/apps-script/reference/drive/file#setOwner(String)

like image 89
Eric Koleda Avatar answered Nov 15 '22 13:11

Eric Koleda


Updated 12 Sep 2016:

This code uses a service account that's been granted Google Apps Domain-Wide Delegation of Authority. This lets you change the owner of any file owned by any user on the domain. This code uses the Google apps-script-oauth2 library by Eric Koleda.

var PRIVATE_KEY  = PropertiesService.getScriptProperties().getProperty('PRIVATE_KEY'); 
var CLIENT_EMAIL = PropertiesService.getScriptProperties().getProperty('CLIENT_EMAIL');

/**
* Transfers ownership of a file or folder to another account on the domain.
*
* @param  {String} fileId The id of the file or folder
* @param  {String} ownerEmail  The email address of the new owner.
*/
function transferOwnership(fileId, ownerEmail) {
  var file = DriveApp.getFileById(fileId);
  var currentOwnerEmail = file.getOwner().getEmail(); //the user that we need to impersonate

  var service = getService(currentOwnerEmail);
  if (service.hasAccess()) {
    var url = 'https://www.googleapis.com/drive/v2/files/' + fileId + '/permissions'; 
    var payload = {value: ownerEmail,
                   type: 'user',
                   role: 'owner'};
    var options = {method: "post",
                   contentType: "application/json",
                   headers : {
                     Authorization: 'Bearer ' + service.getAccessToken()
                   },
                   payload: JSON.stringify(payload)//,
                   ,muteHttpExceptions: true
                  };        
    //debugger;
    //throw 'test my errors';

    var response = UrlFetchApp.fetch(url, options);
    if (response.getResponseCode() === 200 || 
        (response.getResponseCode() === 400 && response.getContentText().indexOf('were successfully shared'))
    ) {
      return response.getContentText();
    }
    if (response.getResponseCode() === 401 && response.getContentText().indexOf('Invalid Credentials')) {
      throw 'Unable to transfer ownership from owner ' + currentOwnerEmail + ' ... ' +
        'Please make sure the file\'s owner has a Google Apps license (and not a Google Apps Vault - Former Employee license) and try again.';
    }
    throw response.getContentText(); 
  } else {
    throw service.getLastError();
  }
}

/**
 * Reset the authorization state, so that it can be re-tested.
 */
function reset() {
  var service = getService();
  service.reset();
}

/**
 * Configures the service.
 */
function getService(userEmail) {
  return OAuth2.createService('GoogleDrive:' + userEmail)
      // Set the endpoint URL.
      .setTokenUrl('https://accounts.google.com/o/oauth2/token')

      // Set the private key and issuer.
      .setPrivateKey(PRIVATE_KEY)
      .setIssuer(CLIENT_EMAIL)

      // Set the name of the user to impersonate. This will only work for
      // Google Apps for Work/EDU accounts whose admin has setup domain-wide
      // delegation:
      // https://developers.google.com/identity/protocols/OAuth2ServiceAccount#delegatingauthority
      .setSubject(userEmail)

      // Set the property store where authorized tokens should be persisted.
      .setPropertyStore(PropertiesService.getScriptProperties())

      // Set the scope. This must match one of the scopes configured during the
      // setup of domain-wide delegation.
      .setScope('https://www.googleapis.com/auth/drive');
}

Old answer (obsolete now as the Document List API is deprecated):

You can achieve this in Google Apps Script by accessing the Document List API using the raw atom xml protocol. You need to be a super admin of the domain. Here's an example that works for me:

/**
* Change Owner of a file or folder 
* Run this as admin and authorise first in the script editor.
*/ 
function changeOwner(newOwnerEmail, fileOrFolderId){
  var file = DocsList.getFileById(fileOrFolderId);
  var oldOwnerEmail = file.getOwner().getEmail();
  if (oldOwnerEmail === newOwnerEmail) {
    return;
  }
  file.removeEditor(newOwnerEmail);
  var base = 'https://docs.google.com/feeds/';
  var fetchArgs = googleOAuth_('docs', base);
  fetchArgs.method = 'POST';
  var rawXml = "<entry xmlns='http://www.w3.org/2005/Atom' xmlns:gAcl='http://schemas.google.com/acl/2007'>"
      +"<category scheme='http://schemas.google.com/g/2005#kind' "
      +"term='http://schemas.google.com/acl/2007#accessRule'/>"
      +"<gAcl:role value='owner'/>"
      +"<gAcl:scope type='user' value='"+newOwnerEmail+"'/>"
      +"</entry>";
  fetchArgs.payload = rawXml;
  fetchArgs.contentType = 'application/atom+xml';
  var url = base + encodeURIComponent(oldOwnerEmail) + '/private/full/'+fileOrFolderId+'/acl?v=3&alt=json';
  var content = UrlFetchApp.fetch(url, fetchArgs).getContentText(); 
}

//Google oAuth
function googleOAuth_(name,scope) {
  var oAuthConfig = UrlFetchApp.addOAuthService(name);
  oAuthConfig.setRequestTokenUrl("https://www.google.com/accounts/OAuthGetRequestToken?scope="+scope);
  oAuthConfig.setAuthorizationUrl("https://www.google.com/accounts/OAuthAuthorizeToken");
  oAuthConfig.setAccessTokenUrl("https://www.google.com/accounts/OAuthGetAccessToken");
  oAuthConfig.setConsumerKey("anonymous");
  oAuthConfig.setConsumerSecret("anonymous");
  return {oAuthServiceName:name, oAuthUseToken:"always"};
}
like image 37
Pete Avatar answered Nov 15 '22 12:11

Pete


Try this out! (at least it worked for me)

var newFolder = DriveApp.createFolder(folderName).addEditor(ownerEmail).setOwner(ownerEmail).addEditor(group.getEmail()).removeEditor(Session.getActiveUser().getEmail());     

The above creates a folder and adds a new editor, sets the new editor to be the owner, adds another group to the editors and finally removes the user who has just created the folder from the editors.

like image 2
ioannis Avatar answered Nov 15 '22 12:11

ioannis