Google Drive Folder Ownership Cleaner
Last active
September 13, 2019 10:31
-
-
Save JERiv/d8f86863577a137ac45f to your computer and use it in GitHub Desktop.
Google Drive Ownership Cleaner
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Google Drive Folder Ownership Cleaner | |
* @author Jean Runnells - jrunnells@customdyn.com | |
* | |
* Will 'take ownership' of files and folders by creating copies | |
* and removing originals, originals will become 'orphaned files' | |
* in their current owner's datastore. In the case of folders, | |
* sharing permissions are also added note that parent folder | |
* permissions will be inherrited additivly | |
* | |
* Run as a webservice to access a folder picker, don't forget to | |
* set the DEVELOPER_KEY in picker.html. If you don't want to do | |
* this goto File > Project Properties > Script Properties and | |
* set/create OWNER and SHARE_ID manually | |
* | |
* Run the main() function as cron job to enforce ownership in a | |
* folder. | |
*/ | |
//Set these if you have commented out the setting store in main | |
var OWNER = ''; | |
var SHARE_ID = ''; | |
/** | |
* Configures the owner as the current user, only if the current user is the owner of shareID | |
* | |
* @param shareID root filed to be cleaned will be sent to setting store | |
*/ | |
function configure(shareID){ | |
var own = Session.getActiveUser().getEmail(); | |
//Check Ownership of root folder | |
if(DriveApp.getFolderById(shareID).getOwner().getEmail() != own){ | |
Logger.log('Setting not set, Onwer mismatch on: '+DriveApp.getFolderById(shareID)); | |
} | |
else { | |
PropertiesService.getScriptProperties().setProperty('OWNER', Session.getActiveUser().getEmail()); | |
PropertiesService.getScriptProperties().setProperty('SHARE_ID', shareID); | |
Logger.log('Setting set:\nOWNER::'+PropertiesService.getScriptProperties().getProperty('OWNER')+' SHARE_ID::'+ | |
PropertiesService.getScriptProperties().getProperty('SHARE_ID')); | |
} | |
} | |
/** | |
* main function to call to clean up directory referenced by SHARE_ID property. | |
* Use a cron trigger to call this function to keep your Drive clean and enforce | |
* ownership of files | |
*/ | |
function main() { | |
//Comment these two lines out to stop pulling setting from the store, and only use given global variables | |
OWNER = PropertiesService.getScriptProperties().getProperty('OWNER'); | |
SHARE_ID = PropertiesService.getScriptProperties().getProperty('SHARE_ID'); | |
var shareDir = DriveApp.getFolderById(SHARE_ID); | |
scan(shareDir); | |
Logger.log('Run Completed:\n'+OWNER+'::'+ SHARE_ID); | |
} | |
/** | |
* recursivly scans the scanDir folder for files and folders and sets ownership to OWNER | |
* | |
* @param {Folder} scanDir the Folder to be rectified recursivly | |
*/ | |
function scan(scanDir){ | |
var scanFiles = scanDir.getFiles(); | |
var scanFolders = scanDir.getFolders(); | |
// Check Files if found copy, remove, rename | |
while (scanFiles.hasNext()){ | |
var nxt = scanFiles.next(); | |
if (nxt.getOwner().getEmail() != OWNER){ | |
var fc = nxt.makeCopy(); | |
scanDir.removeFile(nxt); | |
fc.setName(nxt.getName()); | |
} | |
} | |
// Recur on subdirs *note will infinitely recur through google folder pointer loop | |
while (scanFolders.hasNext()){ | |
var nxtD = scanFolders.next(); | |
//if ownership mismatch create new folder and trasfer files and settings | |
if (nxtD.getOwner().getEmail() != OWNER){ | |
var dc = DriveApp.createFolder('*'+nxtD.getName()); | |
// Copy Folder Sharing Settings | |
//dc.addEditors(nxtD.getEditors()); TD: expects String[] of emails, now User[] | |
//dc.addViewers(nxtD.getViewers()); | |
dc.setDescription(nxtD.getDescription()); | |
dc.setShareableByEditors(nxtD.isShareableByEditors()); | |
dc.setSharing(nxtD.getSharingAccess(), nxtD.getSharingPermission()); | |
//Add to parent folder *note parent permissions will be inherited | |
scanDir.addFolder(dc); | |
//Move files | |
var dcf = nxtD.getFiles(); | |
while (dcf.hasNext()){ | |
dc.addFile(dcf.next()); | |
} | |
//move folders | |
var dcd = nxtD.getFolders(); | |
while (dcd.hasNext()){ | |
dc.addFolder(dcd.next()); | |
} | |
//remove old folder and set name | |
scanDir.removeFolder(nxtD) | |
dc.setName(nxtD.getName()); | |
scan(dc); | |
} | |
else{ | |
scan(nxtD); | |
} | |
} | |
} | |
/** | |
* Backup in the event of auth failure | |
*/ | |
function getOAuthToken() { | |
DriveApp.getRootFolder(); | |
return ScriptApp.getOAuthToken(); | |
} | |
/** | |
* Displays an HTML-service dialog in Google Sheets that contains client-side | |
* JavaScript code for the Google Picker API. | |
*/ | |
function showPicker() { | |
var html = HtmlService.createHtmlOutputFromFile('picker.html') | |
.setWidth(600) | |
.setHeight(425) | |
.setSandboxMode(HtmlService.SandboxMode.IFRAME); | |
SpreadsheetApp.getUi().showModalDialog(html, 'Select a file'); | |
} | |
/** | |
* Render UI for webservice | |
* @return Returns the webserive | |
*/ | |
function doGet() { | |
return HtmlService.createTemplateFromFile('picker.html') | |
.evaluate() | |
.setSandboxMode(HtmlService.SandboxMode.IFRAME) | |
.setTitle('Picker'); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html> | |
<head> | |
<link rel="stylesheet" href="https://ssl.gstatic.com/docs/script/css/add-ons.css"> | |
<script> | |
// IMPORTANT: Replace the value for DEVELOPER_KEY with the API key obtained | |
// from the Google Developers Console. | |
var DEVELOPER_KEY = ''; | |
var DIALOG_DIMENSIONS = {width: 600, height: 425}; | |
var pickerApiLoaded = false; | |
/** | |
* Loads the Google Picker API. | |
*/ | |
function onApiLoad() { | |
gapi.load('picker', {'callback': function() { | |
pickerApiLoaded = true; | |
}}); | |
} | |
/** | |
* Gets the user's OAuth 2.0 access token from the server-side script so that | |
* it can be passed to Picker. This technique keeps Picker from needing to | |
* show its own authorization dialog, but is only possible if the OAuth scope | |
* that Picker needs is available in Apps Script. Otherwise, your Picker code | |
* will need to declare its own OAuth scopes. | |
*/ | |
function getOAuthToken() { | |
google.script.run.withSuccessHandler(createPicker) | |
.withFailureHandler(showError).getOAuthToken(); | |
} | |
/** | |
* Creates a Picker that can access the user's folders. | |
* | |
* @param {string} token An OAuth 2.0 access token that lets Picker access the | |
* file type specified in the addView call. | |
*/ | |
function createPicker(token) { | |
if (pickerApiLoaded && token) { | |
var pview = new google.picker.DocsView() | |
.setOwnedByMe(true) | |
.setIncludeFolders(true) | |
.setMimeTypes('application/vnd.google-apps.folder') | |
.setSelectFolderEnabled(true); | |
var picker = new google.picker.PickerBuilder() | |
// Instruct Picker to display only Folders in Drive. For other | |
// views, see https://developers.google.com/picker/docs/#otherviews | |
.addView(pview) | |
// Hide the title bar since an Apps Script dialog already has a title. | |
.hideTitleBar() | |
.setOAuthToken(token) | |
.setDeveloperKey(DEVELOPER_KEY) | |
.setCallback(pickerCallback) | |
.setOrigin(google.script.host.origin) | |
// Instruct Picker to fill the dialog, minus 2 pixels for the border. | |
.setSize(DIALOG_DIMENSIONS.width - 2, | |
DIALOG_DIMENSIONS.height - 2) | |
.build(); | |
picker.setVisible(true); | |
} else { | |
showError('Unable to load the file picker.'); | |
} | |
} | |
/** | |
* A callback function that extracts the chosen document's metadata from the | |
* response object. For details on the response object, see | |
* https://developers.google.com/picker/docs/result | |
* | |
* @param {object} data The response object. | |
*/ | |
function pickerCallback(data) { | |
var action = data[google.picker.Response.ACTION]; | |
if (action == google.picker.Action.PICKED) { | |
var doc = data[google.picker.Response.DOCUMENTS][0]; | |
var id = doc[google.picker.Document.ID]; | |
var url = doc[google.picker.Document.URL]; | |
var title = doc[google.picker.Document.NAME]; | |
document.getElementById('result').innerHTML = | |
'<b>You chose:</b><br>Name: <a href="' + url + '">' + title + | |
'</a><br>ID: ' + id; | |
google.script.run.configure(id); | |
} else if (action == google.picker.Action.CANCEL) { | |
document.getElementById('result').innerHTML = 'Folder section canceled.'; | |
} | |
} | |
/** | |
* Displays an error message within the #result element. | |
* | |
* @param {string} message The error message to display. | |
*/ | |
function showError(message) { | |
document.getElementById('result').innerHTML = 'Error: ' + message; | |
} | |
</script> | |
</head> | |
<body> | |
<div> | |
<button onclick='getOAuthToken()'>Select a file</button> | |
<p id='result'></p> | |
</div> | |
<script src="https://apis.google.com/js/api.js?onload=onApiLoad"></script> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment