copy spreadsheet to a specific folder and remove viewers from the copy - google-apps-script

I'm trying to make a copy of the active spreadsheet to a specific folder, then remove all editors from the copied spreadsheet and keep myself as Owner.
Here is my code :
//Save Spreadsheet in selected folder
function freezeSS(vname,option) {
if (vname != ""){
var mainSs = SpreadsheetApp.getActiveSpreadsheet();
var SSID = mainSs.getId();
var CopyDate = Utilities.formatDate(new Date(), "GMT-3", "dd/MM/yy-HH:mm"); // Function Date + Format
var file = DriveApp.getFileById(SSID);
if (option == "Public") {
var folder = DriveApp.getFolderById("0B_EpGZ420rEUfk1mZ2Z3RmFKUk9xaGd1bm1CdGhmZ0FCbTdVT2p2MUlJY3NZUTV0MTR0LTQ");
file.makeCopy(vname + "_" + CopyDate, folder);
}
else {
var folder = DriveApp.getFolderById("0B_EpGZ420rEUfk9jTl81NXNVOXNhRDF2N2R4c1FGTW9wQTB5Q3dNS25nd1NEejBTWWd1RFk");
var backup = file.makeCopy(vname + "_" + CopyDate, folder);
var editors = backup.getEditors().getEmail();
Logger.log(editors);
var permision = backup.removeEditor(editors);
}
} return false;
}
Everything works but I have a problem in removing editors. I always get an error and still have the same editors as in the original spreadsheet.

As the documentation states, you have 2 options for removeEditor():
removeEditor(user)
or
removeEditor(emailAddress)
You're using neither, you'll need to make a loop in the getEditors() without the getEmail(), this one will also get into the loop, as so:
var editors = backup.getEditors();
for( i in editors ){
var email = editors[i].getEmail();
var permision = backup.removeEditor(email);
}
or shorter:
var editors = backup.getEditors();
for( i in editors )
var permision = backup.removeEditor(editors[i]);
BTW, this was well documented, a ctrl+c ctrl+v from there would almost solve your problem, please take a little more time to fiddle with it.
https://developers.google.com/apps-script/reference/drive/user#getEmail();

Related

Google Scripts - View Log stuck at "Waiting for logs, please wait..."

I'm trying to run a script for my Google Sheet on Scripts, and a function isn't working properly. I have some loggers in place to check why this is happening, but anytime I try to open the Logs tab, I get this:
... and it's just stuck there forever.
Has anyone ever had this problem? Any potential fixes? Thanks
EDIT: My executions window looks like so:
EDIT 2: Here is the code I'm trying to run, with segment = 1. SPREADSHEETS is just a variable that I'm unfortunately not able to share, but it just contains some import segment information that directs to either 1 or 2.
function CopyPasteAllSheets(segment) {
for (x in SPREADSHEETS) {
if (SPREADSHEETS[x].IMPORTSEGMENT != segment) {
// DRR added app which is redundant to intakeSpreadhseet, but keeps logic more readable
app.toast('running loop')
console.log("ID: " + SPREADSHEETS[x].SOURCE.ID + "NO MATCH");
} else {
// Logger.log("x: "+ x) // keep commented out
var intakeSpreadsheet = SpreadsheetApp.openById(SPREADSHEETS[x].INTAKE.ID);
var intakeSheet = intakeSpreadsheet.getSheetByName(SPREADSHEETS[x].INTAKE.SHEET); //confirm formatting conventions
// This is functionally equivlent to the above, except we don't have a reference to intakeSpreadsheet anymore
// Access the Spreadsheet and sheet you want to copy the data TO
console.log("ID: "+ SPREADSHEETS[x].SOURCE.ID)
var sourceSpreadsheet = SpreadsheetApp.openById(SPREADSHEETS[x].SOURCE.ID);
var sourceSheet = sourceSpreadsheet.getSheetByName(SPREADSHEETS[x].SOURCE.SHEET);
var sourceStartRow = SPREADSHEETS[x].SOURCE.STARTROW;
var sourceStartCol = SPREADSHEETS[x].SOURCE.STARTCOL;
var sourceRangeCol = SPREADSHEETS[x].SOURCE.ENDCOL - SPREADSHEETS[x].SOURCE.STARTCOL + 1;
// Get the range of the data you want and the range where you want the data to go
var rowsToCopy = sourceSheet.getLastRow()-sourceStartRow+1; // is +1 too conservative, check...
var rangeToCopy = sourceSheet.getRange(sourceStartRow,sourceStartCol,rowsToCopy, sourceRangeCol);
var dataToCopy = rangeToCopy.getValues();
var numRows = rowsToCopy;
var numColumns = sourceRangeCol;
var intakeStartRow = SPREADSHEETS[x].INTAKE.STARTROW;
var intakeStartCol = SPREADSHEETS[x].INTAKE.STARTCOL;
var rangeToPaste = intakeSheet.getRange(intakeStartRow,intakeStartCol, numRows,numColumns); // WAS FORMERLY 1,20, ..,.. ~DRR 7/14
rangeToPaste.setValues(dataToCopy);
}
}
}

Pushing a simple Log string from a Google Ad Script to a Google Sheet

I am trying to set up a script which can push data from an App Script into a Google Sheet.
I have the script successfully logging what I want, which goes in the following format Account budget is 12344, but now I want to push this into a Google Sheet. I have set up a variable containing the URL and another variable containing the sheet name, and also a clear method to delete anything already there.
Find the code I have below:
// - The link to the URL
var SPREADSHEET_URL = 'abcdefghijkl'
// - The name of the sheet to write the data
var SHEET_NAME = 'Google';
// No to be changed
function main() {
var spreadsheet = SpreadsheetApp.openByUrl(SPREADSHEET_URL);
var sheet = spreadsheet.getSheetByName(SHEET_NAME);
sheet.clearContents();
}
function getActiveBudgetOrder() {
// There will only be one active budget order at any given time.
var budgetOrderIterator = AdsApp.budgetOrders()
.withCondition('status="ACTIVE"')
.get();
while (budgetOrderIterator.hasNext()) {
var budgetOrder = budgetOrderIterator.next();
Logger.log("Budget Order Amount " + budgetOrder.getSpendingLimit());
}
}
Assuming you want to clear the entire Sheet every time you extract the data this should work for you. You will need to set the url and shtName variables.
function getActiveBudgetOrder() {
var url = 'https://docs.google.com/spreadsheets/d/xxxxxxxxxxxxxxxxxxxxxxx/';
var shtName = 'Sheet1';
var arr = [];
var sht = SpreadsheetApp.openByUrl(url).getSheetByName(shtName);
// There will only be one active budget order at any given time.
var budgetOrderIterator = AdsApp.budgetOrders()
.withCondition('status="ACTIVE"')
.get();
while (budgetOrderIterator.hasNext()) {
var budgetOrder = budgetOrderIterator.next();
arr.push(["Budget Order Amount " + budgetOrder.getSpendingLimit()]);
}
sht.clearContents();
sht.getRange(1, 1, arr.length, arr[0].length).setValues(arr);
}

Google Drive API - Bulk Uploader, File Renaming and Timeouts

I recently found a great google script which allows one to use Google Sheets to list lots of downloads (Jpegs in my case) and set titles. The script transloads(?)... moves the files from a remote place to your Google Drive. So no pointless downloading, uploading in-between.
function SaveToGoogleDrive(){
var folderID = 'FOLDER_HERE'; // put id of the Google Drive folder
var folder = DriveApp.getFolderById(folderID)// get the folder
var sheet = SpreadsheetApp.getActiveSheet();
var data = sheet.getDataRange().getValues();
for (var i = 1; i < data.length; i++) {
var pdfURL = data[i][2];
var myFileName = data[i][1] + '.pdf';
var file = UrlFetchApp.fetch(pdfURL);
folder.createFile(myFileName,file);
}
}
(code comes via - http://unexpectedweb.blogspot.com.es/2017/11/directly-save-file-to-google-drive-by.html )
The script should allow me to set a name for each upload, which will be applied to the file on adding to Google Drive, but this doesn't work for me.
Is there something obvious in the code which doesn't look good to you as renaming doesn't work. Perhaps there's a script that will allow me to rename once the files are all in my Google Drive?
Also- I'm transloading(?) about 500 files and Google's Scripts can only run for 6mins. How would I incorporate something like the script demonstrated here:
That code...
/* Based on https://gist.github.com/erickoledadevrel/91d3795949e158ab9830 */
function isTimeUp_(start) {
var now = new Date();
return now.getTime() - start.getTime() > 300000; // 5 minutes
}
function SaveToGoogleDrive(){
var folderID = 'FOLDER_HERE'; // put id of the Google Drive folder
var folder = DriveApp.getFolderById(folderID)// get the folder
var sheet = SpreadsheetApp.getActiveSheet();
var data = sheet.getDataRange().getValues();
var threads = GmailApp.getInboxThreads(0, 50);
var start = new Date();
for (var i in threads) {
if (isTimeUp_(start)) {
Logger.log("Time up");
break;
}
// Process the thread otherwise
for (var i = 1; i < data.length; i++) {
var pdfURL = data[i][2];
var myFileName = data[i][1] + '.pdf';
var file = UrlFetchApp.fetch(pdfURL);
folder.createFile(myFileName, file);
}
}
}
Thanks for your thoughts. Having so much trouble marrying the two together with my limited knowledge.
Change
folder.createFile(myFileName,file);
to
folder.createFile(file).setName(myFileName);

Google Apps Script Exporting Blank Copy of Copied Template Spreadsheet

I have a source spreadsheet with content.
I have a destination template for that content.
I select and copy the content in the source spreadsheet.
I create a copy of the destination template and paste in the source spreadsheet content.
I then execute some code to export the destination template as an XLSX file and attach it to an email.
The email and the attachment come through, but the contents of the XLSX file match the original template - the content I pasted in is missing.
Yet if I take my export string and run it through the browser, it exports the XLSX file just fine with the contents!
It appears the export function is running before the content paste is complete and sending the newly created destination template without the contents.
I've already tried Utilities.sleep(30000), but no matter how long I wait, I always get a blank copy of the original template. WTF?!
Complete code:
function sendVendor() {
// Open the Active Spreadsheet
var ssMaster = SpreadsheetApp.getActiveSpreadsheet();
var sheetInsert = ssMaster.getSheetByName('Insert RFQ');
var rfqNumber = sheetInsert.getRange('AW2').getValue();
var sheetWorkUp = ssMaster.getSheetByName('WORK UP');
var backgroundColor = '#FF5050';
var row = 11;
var newSheetRow = 13;
var numRows = 0;
var valuesPartNumbers = sheetWorkUp.getRange(12, 2, 233, 1).getValues();
var size = valuesPartNumbers.filter(function(value) { return value != '' }).length;
size = size - 1;
// Create the new RFQ from Template
var template = DocsList.getFileById('1M2f5yoaYppx8TYO_MhctEhKM_5eW-QCxlmJdjWg9VUs'); // Quote Workup Template
var newRFQ = template.makeCopy('Vendor RFQ Request ' + rfqNumber);
var newRFQId = newRFQ.getId();
var folderNew = DocsList.getFolder('Vendor RFQ Requests');
newRFQ.addToFolder(folderNew)
// Open new RFQ
var ssTemplate = SpreadsheetApp.openById(newRFQId);
var sheetVendorRequest = ssTemplate.getSheetByName('Vendor Request');
var newTemplateURL = ssTemplate.getUrl();
var needPricing = new Array();
var valuesFRCosts = sheetWorkUp.getRange(12, 8, size, 1).getValues();
for (var i = 0; i < valuesFRCosts.length; i++) {
row++;
if (valuesFRCosts[i][0] == '') {
var sheetWorkUpRow = sheetWorkUp.getRange(row, 1, 1, sheetWorkUp.getLastColumn());
sheetWorkUpRow.setBackground(backgroundColor);
var sendToTemplate = sheetWorkUp.getRange(row, 1, 1, 6).getValues();
sendToTemplate[0].splice(2, 1);
sheetVendorRequest.getRange(newSheetRow, 2, 1, 5).setValues(sendToTemplate);
newSheetRow++;
}
}
var url = 'https://docs.google.com/feeds/download/spreadsheets/Export?key=' + newRFQId + '&exportFormat=xlsx';
var doc = UrlFetchApp.fetch(url);
var attachments = [{fileName:'Vendor RFQ Request ' + rfqNumber, content: doc.getContent(),mimeType:"application/vnd.ms-excel"}];
MailApp.sendEmail("user#domain.com", "Excel Export Test", "See the attached Excel file.", {attachments: attachments});
}
The solution is to add SpreadsheetApp.flush(); prior to the export (just before var url). The SpreadsheetApp was still occupied with the template (in memory) after being copied - flushing it, and then exporting, forces the script to make a new call to the new Spreadsheet and get the latest data.
Thank you, Faustino Rodriguez.

get form URL from a spreadsheet bound Form

In a spreadsheet script I want to send mail to users that will point them to the URL of a form that will let them enter data. I have tried:
function test1(){
var formID = FormApp.getActiveForm();
var formUrl = DriveApp.getUrl(formID);
sendMail(formUrl);
return
}
This fails because the value of formID is allways NULL.
If there is one form linked to spreadsheet
Use
var ss = SpreadsheetApp.getActiveSpreadsheet(); // or openById, etc
var formUrl = ss.getFormUrl();
to get its Url. If needed, FormApp.openByUrl(formUrl); returns a pointer to the form, which allows any other methods.
Multiple forms linked to spreadsheet
There is no built-in method to return the list of forms linked to a given spreadsheet; this is an open issue in Apps Script issues tracker. A workaround is to search all forms (as Cyrus Loree did), get the destination of each, and return the list of those where the destination is the spreadsheet of interest. This is how:
function linkedForms() {
var ssId = SpreadsheetApp.getActiveSpreadsheet().getId();
var formList = [];
var files = DriveApp.getFilesByType(MimeType.GOOGLE_FORMS);
while (files.hasNext()) {
var form = FormApp.openByUrl(files.next().getUrl());
try {
if (form.getDestinationId() == ssId) {
formList.push(form.getPublishedUrl());
}
}
catch(e) {
}
}
return formList;
}
Remarks:
I put form.getDestinationId() in a try block because this method throws an error what the form's destination spreadsheet happens to be deleted (instead of just returning null)
To get the list of form Ids instead of Urls, use form.getId() in the function.
Because you are working in the Spreadsheet, you need to get the associated form from the Spreadsheet object (e.g. SpreadsheetApp.getActiveSpreadsheet.getFormUrl() ).
You will also need to send the mail message with the htmlBody optional parameter.
Here is a code sniplet:
function sendNotice(recipient){
try{
// either hardcode the folder id below
var formStorageFolderId = '';
// or programmatically get the folder from the spreadsheet parent
var ss = SpreadsheetApp.getActiveSpreadsheet();
var ssFolder = DriveApp.getFileById(ss.getId()).getParents();
if(ssFolder.hasNext()){
// assume there is only one parent folder
formStorageFolderId = ssFolder.next().getId();
}
var formFolder = DriveApp.getFolderById(formStorageFolderId);
var files = DriveApp.getFilesByType(MimeType.GOOGLE_FORMS);
var formId = '';
while(files.hasNext()){
// search for the form (is it the same name as the spreadsheet?)
var file = files.next();
var fileName = file.getName();
var sheetName = ss.getName();
if(fileName == sheetName){
// matched names
formId = file.getId();
break;
}
}
if(formId){
var actualForm = FormApp.openById(formId);
var formName = actualForm.getTitle();
var formURL = actualForm.getPublishedUrl();
var subject = "Please fill out form";
// html mail message (needed for the link
var mailBody = '<div><p>Please fill out the attached form<p>';
mailBody += '<p>' + formName + '';
MailApp.sendEmail(recipient, subject, '',{htmlBody:mailBody});
}
}catch(err){
Logger.log(err.lineNumber + ' - ' + err);
}
}
I know that it's an old question, but I'll still add an answer to whom it may concern. #user3717023's answer works fine, but for most use-cases, there's a better way.
Generally speaking, Form is connected to Sheet, not to Spreadsheet itself. So, one way to get all of the Spreadsheet forms is to go through the Sheets of the Spreadsheets, and get their Form URL, like that:
function getFormsOfSpreadsheet(spreadsheetId) {
const ss = SpreadsheetApp.openById(spreadsheetId);
const sheets = ss.getSheets();
const formsUrls = [];
for (const sheet of sheets) {
const formUrl = sheet.getFormUrl();
// getFormUrl() returns null if no form connected
if (formUrl) {
formsUrls.push(formUrl);
}
}
return formsUrls;
}
Docs
If this way is not optimal for you (e.g. you have way too many Sheets in your Spreadsheet and only a few of them are linked to Forms), there's still a possibility that the Forms you need to capture are stored in the same Folder. In this case, there's no need to search through the whole Drive to capture them, you can use the same method, but with Folder:
function linkedForms() {
var ssId = SpreadsheetApp.getActiveSpreadsheet().getId();
var formList = [];
var folder = DriveApp.getFolderById(yourFolderId);
var files = folder.getFilesByType(MimeType.GOOGLE_FORMS);
while (files.hasNext()) {
var form = FormApp.openByUrl(files.next().getUrl());
try {
if (form.getDestinationId() == ssId) {
formList.push(form.getPublishedUrl());
}
}
catch(e) {
}
}
return formList;
}
Docs