Skip to content

Instantly share code, notes, and snippets.

@JERiv
Last active September 13, 2019 10:31
Show Gist options
  • Save JERiv/d8f86863577a137ac45f to your computer and use it in GitHub Desktop.
Save JERiv/d8f86863577a137ac45f to your computer and use it in GitHub Desktop.
Google Drive Ownership Cleaner

Google Drive Folder Ownership Cleaner

/**
* 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');
}
<!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