Skip to content

Instantly share code, notes, and snippets.

@eojji
Created June 26, 2018 06:59
Show Gist options
  • Save eojji/73ef2b6f55cd38953c1c5e767cc56777 to your computer and use it in GitHub Desktop.
Save eojji/73ef2b6f55cd38953c1c5e767cc56777 to your computer and use it in GitHub Desktop.
Copy to team drive - review. Listing folders - 8 https://m.cafe.naver.com/eojji/311
// Web App Exec:
// https://script.google.com/macros/s/AKfycbxa1mBiEPGwHpVDxw6V6p1bRBWSXHm8qbIea7Kjmv9rQUyv68_Z/exec
function doGet(request) {
var output = HtmlService.createTemplateFromFile('Page').evaluate();
return output.addMetaTag('viewport', 'width=device-width, initial-scale=1');
}
function include(filename) {
return HtmlService.createHtmlOutputFromFile(filename)
.getContent();
}
function copyDriveFile_(title, fileId, parentId) {
var resource = {
'title': title,
parents: [{ id: parentId }]
};
var optionalArgs = {
supportsTeamDrives : true
};
try {
return Drive.Files.copy(resource, fileId, optionalArgs);
} catch(error) {
return error; // error.name, error.message;
}
}
function createFolderSpreadsheet_(title, folderId) {
var ssNew, ssId, sheetFolder, sheetFiles;
var title = '_gfolder, ' + Date.now() + ' ' + title;
try {
ssNew = SpreadsheetApp.create(title);
sheetFolder = ssNew.getSheets()[0];
sheetFolder.setName('folder');
sheetFolder.appendRow(["row to read", 3]);
sheetFolder.appendRow(["title", "id", "id2", "files", "fileSize", "start", "end", "file row", "parent"]);
// files
sheetFiles = ssNew.insertSheet('files');
sheetFiles.appendRow(["row to read", 3, "page token"]);
sheetFiles.appendRow(['title', 'id', 'mime', 'size', 'copy', 'name', 'message', 'check', 'parent']);
// https://developers.google.com/drive/api/v2/reference/parents/insert
var resource= { "id": folderId };
var optionalArgs = { "supportsTeamDrives" : true };
// Drive.Parents.insert(resource, fileId, optionalArgs);
ssId = ssNew.getId();
Drive.Parents.insert(resource, ssId, optionalArgs);
Drive.Parents.remove(ssId, DriveApp.getRootFolder().getId());
return ssNew;
} catch(e) {
console.warn('Error: %s, %s - createFolderSpreadsheet: %s', e.name, e.message, folderId);
}
}
function fileCopyFromInput_(file, folderId) {
var appMessage = {};
if (! file.copyable) {
appMessage.url = file.alternateLink;
appMessage.title = file.title;
appMessage.message = 'The file can not be copied to My Drive.';
return appMessage;
}
var aCopy = copyDriveFile_(file.title, file.id, folderId);
if (aCopy.id) {
// console.log('Title: %s, Mimetype: %s, ID: %s', aCopy.title, aCopy.mimeType, aCopy.id);
appMessage.url = aCopy.alternateLink;
appMessage.title = aCopy.title;
appMessage.message = 'The copy is complete. Size: ' + aCopy.fileSize;
} else if (aCopy.name) {
console.warn('An error occurred while copying. Error Name: %s, Message: %s', aCopy.name, aCopy.message);
appMessage.message = 'An error occurred while copying. '+aCopy.name+ ', Message: '+ aCopy.message;
appMessage.url = file.alternateLink;
appMessage.title = file.title;
}
return appMessage;
}
function getFolderByGdUrl_(url) {
var idx = url.lastIndexOf('google.com');
if (idx < 0) {
var rtn = url.match(/[-\w]{15,}/);
if (rtn) {
return getDriveFileById_(rtn[0]);
}
return;
}
var decUrl = url.substr(idx+10);
var arr = ["/d/", "/folders/", "?id=", "&id="];
for (var i=0; i<arr.length; i++) {
idx = decUrl.indexOf(arr[i]);
if (idx < 0) {
continue;
}
var str = decUrl.substr(idx + arr[i].length);
var idMatchStr = str.match(/[-\w]{15,}/);
if (idMatchStr) {
return getDriveFileById_(idMatchStr[0]);
} else {
return;
}
}
}
function setPageTokenProperty(value) {
PropertiesService.getUserProperties().setProperty('PAGE_TOKEN', value);
}
function getPageTokenProperty() {
var pageTokenProperty = PropertiesService.getUserProperties().getProperty('PAGE_TOKEN');
return pageTokenProperty;
}
function deletePageTokenProperty() {
PropertiesService.getUserProperties().deleteProperty('PAGE_TOKEN');
}
function doCopyFolder_(sheetFolder, folderRowNum, sheetFiles, startTime) {
var folderRange, folderInfo;
try {
folderRange = sheetFolder.getRange(folderRowNum, 1, 1, 9);
folderInfo = folderRange.getValues()[0];
} catch(e) {
console.warn('Error: %s, %s - doCopyFolder', e.name, e.message);
return '-1';
}
var title, folderId, copyFolderId, fileRowNum;
title = folderInfo[0];
folderId = folderInfo[1];
copyFolderId = folderInfo[2];
fileRowNum = folderInfo[7];
if (!fileRowNum || fileRowNum < 3) {
var pageToken;
var objFiles = listFilesByPageToken_(folderId, pageToken, startTime);
if (!objFiles) {
return '9';
}
var parentId = folderInfo[8];
var resp = insertFolderById_(parentId, title);
if (!resp || !resp.id) {
console.warn('Error: %s, %s - insertFolderById(parentId: %s, %s)', resp.name, resp.message, parentId, title);
return '-1';
}
folderInfo[7] = sheetFiles.getLastRow() + 1; // fileRowNum
sheetAppendFiles_(resp.id, objFiles.files, sheetFolder, sheetFiles);
folderInfo[2] = resp.id; // copyFolderId
folderRange.setValues([folderInfo]);
if (objFiles.pageToken) {
setPageTokenProperty(folderId+','+objFiles.pageToken);
return '1';
}
return copyFilesBySheet_(startTime, sheetFiles);
}
var pageTokenProperty = getPageTokenProperty();
if (pageTokenProperty) {
var arrIdAndPageToken = pageTokenProperty.split(',');
if (arrIdAndPageToken[0] == folderId && arrIdAndPageToken[1]) {
var objFiles = listFilesByPageToken_(folderId, arrIdAndPageToken[1], startTime);
if (objFiles) {
sheetAppendFiles_(copyFolderId, objFiles.files, sheetFolder, sheetFiles);
if (objFiles.pageToken) {
setPageTokenProperty(folderId+','+objFiles.pageToken);
return '1';
}
}
} else {
deletePageTokenProperty();
}
}
return copyFilesBySheet_(startTime, sheetFiles);
}
function sheetAppendFiles_(parentId, data, sheetFolder, sheetFiles) {
if (!data || data.length < 1) {
return;
}
var folderData = [];
var newDate = new Date();
// title, id
// id2, files, fileSize, start, end, file row, parent
var newFolderContents = ['', 0, 0, newDate, newDate, 0, parentId];
var fileData = [];
// copy, name, message, check, parent
var newFileContents = ['', '', '', '', parentId];
var len = data.length;
for (var i = 0; i < len; i++) {
// title, id, mimeType, fileSize
var mimeType = data[i][2]; // mimeType
if (mimeType == 'application/vnd.google-apps.folder') {
folderData.push(data[i].slice(0, 2).concat(newFolderContents));
} else {
fileData.push(data[i].concat(newFileContents));
}
}
if (folderData && folderData.length > 0) {
sheetFolder.getRange(sheetFolder.getLastRow() + 1, 1, folderData.length, folderData[0].length).setValues(folderData);
}
if (fileData && fileData.length > 0) {
sheetFiles.getRange(sheetFiles.getLastRow() + 1, 1, fileData.length, fileData[0].length).setValues(fileData);
}
}
function insertFolderById_(parentId, folderName) {
var resource= {
"parents": [
{
"id": parentId
}
],
"title": folderName,
"mimeType": "application/vnd.google-apps.folder"
};
// https://stackoverflow.com/questions/28378738/create-a-new-blob-in-memory-using-apps-script
// var blob = Utilities.newBlob(folderName);
var optionalArgs = {
supportsTeamDrives : true
};
// var folder;
try {
// https://developers.google.com/drive/v2/reference/files#resource
return Drive.Files.insert(resource, Utilities.newBlob(folderName), optionalArgs);
} catch(error) {
return error;
}
}
function triggerAfterByName(functionName, time) {
ScriptApp.newTrigger(functionName)
.timeBased().after(time).create();
console.log('%s, %s, Time: %s - trigger AfterByName', Session.getEffectiveUser().getEmail(), functionName, time);
}
function setFolderSSIdProperty(value) {
return PropertiesService.getUserProperties().setProperty('FOLDER_SS_ID', value);
}
function getFolderSSIdProperty() {
return PropertiesService.getUserProperties().getProperty('FOLDER_SS_ID');
}
function deleteFolderSSIdProperty() {
return PropertiesService.getUserProperties().deleteProperty('FOLDER_SS_ID');
}
function deleteTriggerByName(name) {
var getPropTrigger = PropertiesService.getUserProperties().getProperty('atHour'+name);
if (!getPropTrigger) {
getPropTrigger = 'x';
}
var triggers = ScriptApp.getProjectTriggers();
for (var i = 0; i < triggers.length; i++) {
if (triggers[i].getHandlerFunction() == name && triggers[i].getUniqueId() != getPropTrigger) {
ScriptApp.deleteTrigger(triggers[i]);
}
}
}
// https://developers.google.com/drive/v2/reference/files
function getDriveFileById_(id) {
var optionalArgs = {
supportsTeamDrives : true
};
try {
return Drive.Files.get(id, optionalArgs);
} catch(error) {
return error; // error.name, error.message;
}
}
function getSpreadsheetById(ssId) {
if (!ssId || ssId < 10) {
console.warn('!ssId || ssId.length < 10 - getSpreadsheetById');
return;
}
try {
var resp = getDriveFileById_(ssId);
if (resp.id && !resp.labels.trashed) {
return SpreadsheetApp.openById(ssId);
}
} catch(e) {
console.warn('Error: %s, %s - DriveApp.getFileById(%s) in getSpreadsheetById', e.name, e.message, ssId);
}
}
/*
* check: 파일 복사 함수 호출의 반환값으로 다음 작업을 결정합니다.
* '1': 계속 진행
* '-1': 프로세스 중단
* '-2': after 트리거 생성
* '-3': hour 트리거 생성
**/
function callFileCopy_(title, id, parentId) {
var copiedState = {
copy: '1',
link: '', // or Error name
message: 'Ok',
check: '1'
}
var resource = {
'title': title,
parents: [{ id: parentId }]
};
var optionalArgs = {
supportsTeamDrives : true
};
var resp;
try {
resp = Drive.Files.copy(resource, id, optionalArgs);
if (resp && resp.id) {
copiedState.link = resp.alternateLink;
return copiedState;
}
} catch(error) {
resp = error; // error.name, error.message;
}
copiedState.copy = '0'; // Not copy
var rowContents;
var thisFunctionName = 'Call Drive.Files.copy';
var email = Session.getEffectiveUser().getEmail();
if (!resp || !resp.name) {
console.warn('%s, Error !resp.name, Title: %s - %s', email, title, thisFunctionName);
copiedState.link = '';
copiedState.message = '';
copiedState.check = '-2';
return copiedState;
}
copiedState.link = resp.name;
copiedState.message = resp.message;
console.warn('%s, Error: %s, Message: %s, Title: %s - %s', email, resp.name, resp.message, title, thisFunctionName);
if (resp.message.indexOf('User rate limit exceeded') >= 0) {
copiedState.check = '-3'; // create trigger, hour
} else if (resp.message.indexOf('The user has exceeded') >= 0 ||
resp.message.indexOf('The file limit for this Team Drive') >= 0) {
copiedState.check = '-1'; // process kill
} else {
copiedState.check = '1'; // Skip
// Error Name: Exception
// 'Internal Error'
// '빈 응답'
}
return copiedState;
}
function copyFilesBySheet_(startTime, sheet) {
var lastRow = sheet.getLastRow();
if (!lastRow || lastRow < 3) {
return '9';
}
var startRow = sheet.getRange('B1').getValue();
if (!startRow || startRow < 3) {
startRow = 3;
}
if (startRow > lastRow) {
return '9';
}
var rows = lastRow - startRow + 1;
var fileValues = sheet.getRange(startRow, 1, rows, 9).getValues();
var fileCont;
var valuesCopyfile = [];
for (var idx = 0; idx < rows; idx++) {
fileCont = fileValues[idx]
var title = fileCont[0];
var id = fileCont[1];
var parent = fileCont[8];
var copiedState = callFileCopy_(title, id, parent);
var arr = [copiedState.copy];
arr.push(copiedState.link);
arr.push(copiedState.message);
arr.push(copiedState.check);
valuesCopyfile.push(arr);
if (copiedState.check === '1') {
// console.log('copiedState.chek: %s === 1', copiedState.check);
} else {
console.log('%s Not === 1, break copyFilesBySheet, copiedState.chek', copiedState.check);
break;
}
if (!timeCheck(startTime, 30000)) { // needTime: 30 sec
break;
}
// end for var idx
}
var valuesLength = valuesCopyfile.length;
sheet.getRange(startRow, 5, valuesLength, valuesCopyfile[0].length).setValues(valuesCopyfile);
var readToRow = (copiedState.check === '1') ? (startRow + valuesLength) : (startRow + valuesLength - 1);
sheet.getRange('B1').setValue(readToRow);
if (idx >= rows) {
return '9';
}
return copiedState.check;
}
function timeCheck(startTime, needTime) {
var nowDate = new Date();
// remain = 6 min - ( now - start )
var remain = 360000 - (nowDate.getTime() - startTime);
if (remain < needTime) {
return false;
} else {
return true;
}
}
function trigCpFolderBySheet_() {
var newDate = new Date();
var COPY_START_TIME = newDate.getTime();
// Deletes triggers
var thisFunctionName = 'trigCpFolderBySheet_';
deleteTriggerByName(thisFunctionName);
var ssId = getFolderSSIdProperty();
if (!ssId || ssId.length < 10) {
console.warn('Error getProperty(FOLDER_SS_ID), %s', thisFunctionName);
return;
}
var copyFolderSs = getSpreadsheetById(ssId);
if (!copyFolderSs) {
console.warn('getSpreadsheetById(%s), at %s', ssId, thisFunctionName);
return;
}
var sheetFolder = copyFolderSs.getSheetByName('folder');
var sheetFiles = copyFolderSs.getSheetByName('files');
var folderRow = sheetFolder.getRange('B1').getValue(); // row to read
var timeResp;
var title, id, id2, files, filesArrayLength, fileRow;
var lastRowFiles, pageTokenProperty;
do {
var resp = doCopyFolder_(sheetFolder, folderRow, sheetFiles, COPY_START_TIME);
if (resp === '1') {
if (!timeCheck(COPY_START_TIME, 120000)) {
console.log('timeCheck -> triggerAfter');
triggerAfterByName(thisFunctionName, 1000); // trigger 기동
return;
}
}
} while(resp === '1');
}
function listFilesByPageToken_(folderId, pageToken, startTime) {
var query = '"'+ folderId +'" in parents and trashed = false';
var files, resp;
var data = [];
var fileCont = [];
do {
files = Drive.Files.list({
includeTeamDriveItems : true,
supportsTeamDrives : true,
q: query,
maxResults: 100,
pageToken: pageToken,
});
if (files.items && files.items.length > 0) {
for (var i = 0; i < files.items.length; i++) {
var file = files.items[i];
fileCont = [file.title]; // file.title, file.id, file.mimeType, file.fileSize
fileCont.push(file.id);
fileCont.push(file.mimeType);
if (!file.fileSize) {
fileCont.push(0);
} else {
fileCont.push(file.fileSize);
}
data.push(fileCont);
}
}
pageToken = files.nextPageToken;
} while (pageToken && timeCheck(startTime, 30000)); // needTime: 30 sec
if (data.length > 0) {
var objFiles = {};
objFiles.pageToken = pageToken;
objFiles.files = data;
return objFiles;
}
}
function funcCopyFromInput_(argUrl, argFolder) {
var appMessage = {};
appMessage.url = argUrl;
appMessage.title = argUrl;
if (!argUrl || argUrl.length < 15) {
appMessage.message = argUrl + ' is too short to be processed.';
return appMessage;
}
var file = getFolderByGdUrl_(argUrl); // return Drive.Files.get(id, optionalArgs);
if (!file || !file.id) {
var appMessage = file;
if (appMessage && appMessage.name) {
console.log('Error Name: %s, Message: %s, Form Input Url: %s', appMessage.name, appMessage.message, argUrl);
appMessage.message = 'Error Name: '+appMessage.name+', Message: '+appMessage.message;
} else {
console.log('Error input Url Validate. URL does not have file ID. [%s]', argUrl);
appMessage.message = 'Error ['+argUrl+'] Validate.';
}
return appMessage;
}
var title = file.title;
if (file.mimeType != 'application/vnd.google-apps.folder') {
return fileCopyFromInput_(file, argFolder.id);
} else {
var ss = createFolderSpreadsheet_(title, argFolder.id);
var sheetFolder = ss.getSheets()[0];
var newDate = new Date();
sheetFolder.appendRow([title, file.id, '', 0, 0, newDate, newDate, 0, argFolder.id]);
setFolderSSIdProperty(ss.getId());
appMessage.url = ss.getUrl();
appMessage.title = ss.getName();
appMessage.message = 'Create Spreadsheet';
// trigger 기동
triggerAfterByName('trigCpFolderBySheet_', 1000);
return appMessage;
}
}
function processForm(formObject) {
var formFromUrl = formObject.fromFolder.trim();
var formToUrl = formObject.toFolder.trim();
var appMessage = {};
var toFolder = getFolderByGdUrl_(formToUrl);
if (!toFolder || !toFolder.id) {
console.warn('Error Get to folder');
appMessage.message = 'Error Get to folder';
return JSON.stringify(appMessage);
}
var fcResp = funcCopyFromInput_(formFromUrl, toFolder);
if (!fcResp) {
appMessage.url = 'https://drive.google.com';
appMessage.title = 'My Drive';
appMessage.message = 'can not copy!';
} else {
appMessage = fcResp;
}
return JSON.stringify(appMessage);
}
<script>
window.addEventListener('load', function() {
// console.log('Page is loaded');
});
// Prevent forms from submitting.
function preventFormSubmit() {
var forms = document.querySelectorAll('form');
for (var i = 0; i < forms.length; i++) {
forms[i].addEventListener('submit', function(event) {
event.preventDefault();
});
}
}
window.addEventListener('load', preventFormSubmit);
function handleFormSubmit(formObject) {
google.script.run.withSuccessHandler(updateUrl).processForm(formObject);
formObject.fromFolder.value = '';
}
function updateUrl(appMessage) {
var json;
if (appMessage) {
json = JSON.parse(appMessage);
if (json.url) {
// var url
var div = document.getElementById('output');
div.innerHTML = 'Link: <a href="' + json.url + '" target="_blank">'+json.title+'</a>';
}
if (json.message) {
var message = document.getElementById('message');
message.innerHTML = 'Message: '+json.message;
}
}
}
</script>
<!DOCTYPE html>
<html>
<head>
<base target="_top">
<?!= include('Stylesheet'); ?>
</head>
<body>
<h1>Copy to team drive review</h1>
<a href="https://gfoldercopy.eojji.com/group" target="_blank">G folder copy group</a>
<br><br>
<form id="myForm" onsubmit="handleFormSubmit(this)">
From: <input name="fromFolder" type="text" required /> <br>
To: <input name="toFolder" type="text" required />
<input type="submit" value="Copy" />
</form>
<br>
<div id="message">Message: </div>
<div id="output">Link: </div>
<br>
Create a <a href="https://gsuite.google.com/learning-center/products/drive/get-started-team-drive/">Team Drive</a>: <a href="https://goo.gl/forms/1az5A9lwcG2HpL5s2">Request Form</a> | <a href="https://hi.eojji.com/">Contact</a>
<br><br>
<a href="https://copy.gfolder.net/" target="_blank">copy.gfolder.net</a> | <a href="https://hi.eojji.com/privacy" target="_blank">Privacy Policy</a>
<?!= include('JavaScript'); ?>
</body>
</html>
<style>
p {
color: green;
}
</style>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment