Created
December 15, 2022 15:29
-
-
Save phillypb/4ea4381cafc232bcf2612e93113a99f6 to your computer and use it in GitHub Desktop.
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
/* | |
Global Variables that may need to be tweaked and are easier to access by placing here. | |
*/ | |
// Maximum runtime of script in minutes to prevent timeout (5 minutes) | |
var maxRuntime = 5 * 60 * 1000; | |
/* | |
This Function gets the necessary data from the spreadsheet. | |
*/ | |
function getSpreadsheetData() { | |
try { | |
// Log information | |
logEvent('Script has started'); | |
// get spreadsheet | |
var ss = SpreadsheetApp.getActiveSpreadsheet(); | |
// create toast popup to inform user | |
ss.toast('Task has now started ...', 'Start'); | |
// get 'Welcome' sheet | |
var welcomeSheet = ss.getSheetByName('Welcome'); | |
// get folder Id containing Files to be copied | |
var filesFolderId = welcomeSheet.getRange(5, 4).getValue().trim(); | |
// get folder Id of Parent folder containing subfolders to copy files into | |
var parentFolderId = welcomeSheet.getRange(9, 4).getValue().trim(); | |
// get value for including subfolder name | |
var includeName = welcomeSheet.getRange(13, 4).getValue().trim(); | |
// create name:value pair so array easier to work with | |
var sheetData = { ss: ss, filesFolderId: filesFolderId, parentFolderId: parentFolderId, includeName: includeName }; | |
// run Function to copy files into existing folders | |
var copyFilesToFoldersFlag = copyFilesToFolders(sheetData); | |
if (copyFilesToFoldersFlag) { | |
// no errors occurred during process | |
// Log information | |
logEvent('Script has finished without errors'); | |
} else { | |
// there were errors during process, relevant popup will already be displayed | |
// Log information | |
logEvent('Script completed with errors'); | |
} | |
} | |
catch (error) { | |
// there was a problem getting spreadsheet data | |
logEvent("Error with 'getSpreadsheetData' Function: " + error); | |
// create popup to inform user | |
var popupTitle = "'getSpreadsheetData' Function Error"; | |
var popupMessage = "Error message: " + error; | |
htmlPopup(popupTitle, popupMessage); | |
} | |
} | |
/* | |
This Function gets the Google Drive folder containing the files to be copied. | |
It get the Parent Drive folder containg the subfolders to iterate through. | |
It iterates through each subfolder and makes a copy of the files to be placed into it. | |
It then optionally appends the subfolder name to new file copy. | |
*/ | |
function copyFilesToFolders(sheetData) { | |
try { | |
// Log information | |
logEvent("Starting 'copyFilesToFolders' Function"); | |
// extract Sheet data from name:value pair array | |
var ss = sheetData['ss']; | |
var filesFolderId = sheetData['filesFolderId']; | |
var parentFolderId = sheetData['parentFolderId']; | |
var includeName = sheetData['includeName']; | |
// Log information | |
logEvent("Option to append subfolder name is: " + includeName); | |
// get Google Drive folder containing files to copy | |
var filesFolder = DriveApp.getFolderById(filesFolderId); | |
// get Parent Google Drive folder containing subfolders to iterate through | |
var parentFolder = DriveApp.getFolderById(parentFolderId); | |
} catch (error) { | |
// error getting Google Drive folders | |
logEvent('Error getting Google Drive folders: ' + error.stack); | |
// create popup to inform user | |
var popupTitle = "Error getting Google Drive folders"; | |
var popupMessage = "Error message: " + error.stack; | |
htmlPopup(popupTitle, popupMessage); | |
// return Flag to Parent Function | |
return false; | |
}; | |
// check there are files and subfolders to work through | |
var filesLength = filesFolder.getFiles().length; | |
logEvent('Number of files to copy is: ' + filesLength); | |
var parentFolderLength = parentFolder.getFolders().length; | |
logEvent('Number of subfolders is: ' + parentFolderLength); | |
try { | |
// proceed IF successfully got Google Drive folders | |
if (parentFolder) { | |
// Log information | |
logEvent("Got all Google Drive folders"); | |
// create Counter to log number of copied files | |
var copiedFilesCounter = 0; | |
// create Counter to log number of subfolders iterated through | |
var subfoldersCounter = 0; | |
// run Function to check Script Properties for 'Continuation Token' | |
var continuationToken = getScriptProperties(); | |
// check no error getting Script Properties before proceeding ************************ | |
if (continuationToken !== false) { | |
// get start time so can manage duration of script and avoid timeout | |
var startTime = new Date().getTime(); | |
} else { | |
// error occurred, return false flag to Parent Function | |
return false; | |
} | |
// check no error getting Script Properties before proceeding ************************ | |
// check if 'Continuation Token' exists to determine if continuing conversion | |
if (continuationToken === null) { | |
// no Token exists so proceed as first time running the script: | |
// log message in sheet | |
logEvent('No Continuation Token exists. Copying files ...'); | |
// get all subfolders in Parent Drive folder | |
var subFolders = parentFolder.getFolders(); | |
} else { | |
// Token does exist in Script Properties | |
// log message in sheet | |
logEvent('Continuation Token does exist. Copying files ...'); | |
// continue iterating through Drive subfolders with Token | |
var subFolders = DriveApp.continueFolderIterator(continuationToken); | |
} | |
// cycle through each subfolder | |
while (subFolders.hasNext()) { | |
// get subfolder | |
var subFolder = subFolders.next(); | |
// get subfolder name | |
var subFolderName = subFolder.getName(); | |
// create toast popup to inform user and log | |
ss.toast('Folder: ' + subFolderName, 'Copying files into it ...'); | |
logEvent('Folder: ' + subFolderName + ' Copying files into it ...'); | |
// iterate through files and copy into subfolder **************************************** | |
// get all files in folder - need to do here so 'while' loop refreshes after each iteration | |
var filesToCopy = filesFolder.getFiles(); | |
// cycle through each file | |
while (filesToCopy.hasNext()) { | |
// get file | |
var file = filesToCopy.next(); | |
// check what user selected for including the subfolder name in file ***************** | |
var fileName = file.getName(); | |
logEvent("File name to copy is: " + fileName); | |
if (includeName == 'Append subfolder name') { | |
// append file name | |
fileName = fileName + " - " + subFolderName; | |
} else if (includeName == 'Prepend subfolder name') { | |
// prepend file name | |
fileName = subFolderName + " - " + fileName; | |
} else { | |
// user did not select to include subfolder name to file | |
}; | |
// check what user selected for including the subfolder name in file ***************** | |
// make copy of file into subfolder | |
file.makeCopy(fileName, subFolder); | |
// increment Counter by 1 to log number of copied files | |
copiedFilesCounter++; | |
}; | |
// iterate through files and copy into subfolder **************************************** | |
// increment Counter by 1 to log number of subfolders iterated through | |
subfoldersCounter++; | |
// *************************** perform runtime check *************************** | |
// get current time | |
var endTime = new Date().getTime(); | |
// find elapsed time by subtracting from start time | |
var elapsedTime = endTime - startTime; | |
// check against maximum runtime | |
var timeLimitExceeded = elapsedTime >= maxRuntime; | |
// check status of runtime | |
if (timeLimitExceeded) { | |
// runtime has been met/exceeded, store 'Continuation Token' so can resume later | |
// log message in sheet | |
logEvent('Runtime has been met/exceeded.'); | |
// get Token from last subfolder | |
var continuationToken = subFolders.getContinuationToken(); | |
/* | |
run Function to add Token to Script Properies for use later. | |
capture in error variable as this is the last step in process anyway, | |
so no matter what will be using 'break' | |
*/ | |
setPropertyFlag = setScriptProperties(continuationToken); | |
if (setPropertyFlag) { | |
// successfully completed | |
// break out of loop to prevent script from continuing | |
break; | |
} else { | |
// problem occurred, return false Flag | |
return false; | |
} | |
} else { | |
// runtime has not been met/exceeded, script can continue looping through files | |
} | |
// *************************** perform runtime check *************************** | |
}; | |
// Log information | |
logEvent("Completed copying files into a total of " + subfoldersCounter + " subfolders"); | |
logEvent("Created " + copiedFilesCounter + " files from the copy"); | |
} else { | |
// do nothing as script will have terminated at this point | |
} | |
// check if there are still subfolders to iterate through | |
if (subFolders.hasNext()) { | |
// yes there are subfolders to iterate through | |
// log message in sheet | |
logEvent('Successfully iterated through ' + subfoldersCounter + ' subfolders.'); | |
// log message in sheet | |
logEvent('There are still subfolders left.'); | |
// no errors have occurred in the script | |
var popupTitle = 'Runtime reached - ' + subfoldersCounter + ' subfolders iterated through'; | |
var popupMessage = 'No errors, but there are still subfolders to iterate through, so you may wish to run this tool again.'; | |
htmlPopup(popupTitle, popupMessage); | |
// log message in sheet | |
logEvent('Script completed without errors.'); | |
// return success Flag | |
return true; | |
} else { | |
// no all subfolders have been iterated through | |
// delete Script Properties of Token as no longer required | |
deletePropertyFlag = deleteScriptProperties('CONTINUATION_TOKEN'); | |
// check no error deleting Script Property before proceeding *********** | |
if (deletePropertyFlag) { | |
// log message in sheet | |
logEvent('All subfolders have been iterated through.'); | |
// no errors have occurred in the script | |
var popupTitle = 'Tool complete - ' + subfoldersCounter + ' subfolders iterated through'; | |
var popupMessage = 'No errors. All files have been copied into all subfolders.'; | |
htmlPopup(popupTitle, popupMessage); | |
// return success Flag | |
return true; | |
} | |
else { | |
// error deleting Script Property - do nothing as popup will already be displayed | |
} | |
// check no error deleting Script Properties before proceeding *********** | |
} | |
} catch (error) { | |
// there was a problem during file copying | |
logEvent('Error during file copying: ' + error.stack); | |
// create popup to inform user | |
var popupTitle = "Error during file copying"; | |
var popupMessage = "Error message: " + error.stack; | |
htmlPopup(popupTitle, popupMessage); | |
return false; | |
} | |
} | |
/* | |
Function to look for 'Continuation Token' in Script Properties. | |
https://developers.google.com/apps-script/guides/properties | |
*/ | |
function getScriptProperties() { | |
try { | |
// log message in sheet | |
logEvent('Getting Script Properties.'); | |
// access Script Properties | |
var scriptProperties = PropertiesService.getScriptProperties(); | |
// look for 'Continuation Token' Property | |
var continuationToken = scriptProperties.getProperty('CONTINUATION_TOKEN'); | |
// return value to Parent Function | |
return continuationToken; | |
} | |
catch (error) { | |
// log error in sheet | |
logEvent('Problem getting Script Properties: ' + error); | |
// display user popup to inform of error | |
var popupTitle = 'Get Script Properties error'; | |
var popupMessage = 'Problem getting Script Properties: ' + error; | |
htmlPopup(popupTitle, popupMessage); | |
// return false to Parent Function | |
return false; | |
} | |
} | |
/* | |
Function to set 'Continuation Token' in Script Properties. | |
https://developers.google.com/apps-script/guides/properties | |
*/ | |
function setScriptProperties(continuationToken) { | |
try { | |
// log message in sheet | |
logEvent('Setting Script Properties.'); | |
// access Script Properties | |
var scriptProperties = PropertiesService.getScriptProperties(); | |
// set 'Continuation Token' Script Property | |
var continuationToken = scriptProperties.setProperty('CONTINUATION_TOKEN', continuationToken); | |
// log message in sheet | |
logEvent('Successfully set Script Properties.'); | |
// return true to Parent Function | |
return true; | |
} | |
catch (error) { | |
// log error in sheet | |
logEvent('Problem setting Script Property: ' + error); | |
// display user popup to inform of error | |
var popupTitle = 'Set Script Property error'; | |
var popupMessage = 'Problem setting Script Property: ' + error; | |
htmlPopup(popupTitle, popupMessage); | |
// return false to Parent Function | |
return false; | |
} | |
} | |
/* | |
Function to delete 'Continuation Token' in Script Properties. | |
https://developers.google.com/apps-script/guides/properties | |
*/ | |
function deleteScriptProperties(property) { | |
try { | |
// log message in sheet | |
logEvent('Deleting Script Properties.'); | |
// access Script Properties | |
var scriptProperties = PropertiesService.getScriptProperties(); | |
// delete 'Continuation Token' Property | |
scriptProperties.deleteProperty(property); | |
// log message in sheet | |
logEvent('Successfully deleted Script Properties.'); | |
// return true to Parent Function | |
return true; | |
} | |
catch (error) { | |
// log error in sheet | |
logEvent('Problem deleting Script Property: ' + error); | |
// display user popup to inform of error | |
var popupTitle = 'Delete Script Property error'; | |
var popupMessage = 'Problem deleting Script Property Token: ' + error; | |
htmlPopup(popupTitle, popupMessage); | |
// return false to Parent Function | |
return false; | |
} | |
} | |
/* | |
Reset all data within Google Sheet to be able to start afresh. | |
*/ | |
function reset() { | |
try { | |
// log that reset Function has been selected | |
logEvent("Initiated 'Reset' Function"); | |
// get Spreadsheet UI | |
var ui = SpreadsheetApp.getUi(); | |
// create Alert popup box | |
var result = ui.alert( | |
'Reset spreadsheet', | |
'Do you wish to remove all inputted information to start again?', | |
ui.ButtonSet.OK_CANCEL | |
); | |
// process user response from Popup Box | |
if (result == ui.Button.OK) { | |
// log selection | |
logEvent("Selected 'OK' to reset spreadsheet"); | |
// get current spreadsheet | |
var ss = SpreadsheetApp.getActiveSpreadsheet(); | |
// get Welcome sheet | |
var welcomeSheet = ss.getSheetByName('Welcome'); | |
// clear source folder ID box | |
welcomeSheet.getRange(5, 4).clearContent(); | |
// clear destination folder ID box | |
welcomeSheet.getRange(9, 4).clearContent(); | |
// clear delete select box | |
welcomeSheet.getRange(13, 4).clearContent(); | |
// delete Script Properties of Token | |
deletePropertyFlag = deleteScriptProperties('CONTINUATION_TOKEN'); | |
// log completion of Function | |
logEvent("Completed 'Reset' Function."); | |
// display alert popup to user to confirm completion | |
ui.alert('Successfully cleared all sheet data.'); | |
} | |
else { | |
// User clicked 'No' or 'X' in title bar so do nothing | |
logEvent("Selected 'CANCEL' to reset spreadsheet"); | |
} | |
} | |
catch (error) { | |
// problem clearing sheet data | |
logEvent("Problem with 'reset' Function" + error + error.stack); | |
// run Function to launch HTML popup | |
var popupTitle = "'reset' Function error"; | |
var popupMessage = "Unable to clear sheet data: " + error; | |
htmlPopup(popupTitle, popupMessage); | |
} | |
} | |
/* | |
Function to output messages to the 'Log' sheet. | |
Can be called anywhere else in script. | |
*/ | |
function logEvent(action) { | |
// get the user running the script | |
var theUser = Session.getActiveUser().getEmail(); | |
// get the relevant spreadsheet to output log details | |
var ss = SpreadsheetApp.getActiveSpreadsheet(); | |
var logSheet = ss.getSheetByName('Log'); | |
// create and format a timestamp | |
var dateTime = new Date(); | |
var timeZone = ss.getSpreadsheetTimeZone(); | |
var niceDateTime = Utilities.formatDate(dateTime, timeZone, "dd/MM/yy @ HH:mm:ss"); | |
// create array of data for pasting into log sheet | |
var logData = [niceDateTime, theUser, action]; | |
// append details into next row of log sheet | |
logSheet.appendRow(logData); | |
} | |
/* | |
This Function creates a menu item to run this script. | |
*/ | |
function onOpen() { | |
SpreadsheetApp.getUi() | |
.createMenu('Admin') | |
.addItem('Start now', 'getSpreadsheetData') // label for menu item, name of function to run. | |
.addItem('Reset sheet', 'reset') // label for menu item, name of function to run. | |
.addToUi(); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment