I'm trying to write a script to take a folder from my drive and share it with a list of users.
The script is scheduled to run on a sheet which collects answers from a google form and I set the trigger to run the script at every new submission of the form.
First Attempt. I think there is something wrong with how I create the array containing the list of emails, but I couldn't figure out how to fix it.
function driveShare_array() {
var getEmails =[];
var sheet= SpreadsheetApp.getActiveSheet();
var getEmails = sheet.getRange('emailstoshare').getValues(); // emailtoshare is a named range including only the one column where the sheet collects emails from the form submission
var folder = DriveApp.getFolderById('xxxx'); // added the folder ID in here
for ( i in getEmails) {
folder.addViewer(getEmails[i])
}
}
Second attempt to try to control how the array "getEmails" gets shaped by adding elements (push) one by one
function driveShare_push(){
var getEmails =[];
var sheet= SpreadsheetApp.getActiveSheet();
var range = sheet.getRange('emailstoshare'); // emailtoshare is a named range including only the one column where the sheet collects emails from the form submission
for (var i=0;i<range.getNumRows();i++){
getEmails.push(range.offset(i,0,1,1).getValue());
}
var folder = DriveApp.getFolderById('xxxx'); // added the folder ID in here
// for ( var j=0;j<range.getNumColumns();j++) {
for (var j in getEmails){
folder.addViewer(getEmails[j]) ;
}
}
Both versions (and other variations I have tested) give errors. Could someone help me understand what I'm doing wrong and how to fix it?
Thanks!!
It might be possible that the email address may not have a Google account and thus the folder could not be shared with them. You might consider adding a try/catch block to ignore such errors.
function driveShare_array() {
var getEmails =[];
var sheet= SpreadsheetApp.getActiveSheet();
var folder = DriveApp.getFolderById('xxxx');
var getEmails = sheet.getRange('emailstoshare').getValues();
getEmails.forEach(function(email) {
if (/\S+\.\S+/.test(email)) {
try {
folder.addViewer(email)
} catch (f) {
Logger.log("Cannot share with " + email);
}
}
})
}
If your triggering the function with onFormSubmit trigger then you can do something like this.
function driveShare_array(e) {
var folder=DriveApp.getFolderById('xxxx');
folder.addViewer(e.namedValues['Email Address']);
}
You will probably want to store them on some spreadsheet and retrieve them to make sure you haven’t already added it to the list. You will need to create an onFormSubmit trigger for the function in script editor Edit/Project Triggers.
Used logger.log to understand the structure of the data and the code was creating a matrix instead of an array.
Here the corrected code for reference:
function driveShare_push(){
var getEmails =[];
var sheet= SpreadsheetApp.getActiveSheet();
var range = sheet.getRange('emailstoshare');
for (var i=1;i<range.getNumRows();i++){
if(range.offset(i, 0, 1, 1).getValue().indexOf("gmail.com") > -1){
getEmails.push(range.offset(i,0,1,1).getValue());
}
}
var folder = DriveApp.getFolderById('xxxx');
folder.addViewer(getEmails[getEmails.length-1]) ;
}
Related
I've searched extensively for an answer to this, but have not had much luck. I think it should be a fairly simple answer. Here's what I'm trying to do:
I have a list of (other) Google Sheets document URLs in a Google Sheet.
I'd like to have a simple function that loads the URL in each line and returns the file name.
To illustrate, if the URLs are in A2:A10, a user could input a custom function like =GetFileName(A2) and it would return the file name corresponding to the Google Sheet URL that is in A2.
Here's the code that I have thus far, but it's returning an error, " TypeError: SpreadsheetApp.openByURL is not a function (line 3)."
function getFileName(fileURL) {
var GetFile = SpreadsheetApp.openByURL(fileURL);
var GetName = GetFile.getName();
return GetName;
}
UPDATE *************
Here's the code that worked for me. Basically, it loops through the sheet with IDs to pull file names. Not the most efficient, but works for relatively short lists just fine.
function getFileName() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
// DocumentIDs are listed in the 'tool' sheet
var targetSheet = ss.getSheetByName("tool");
// Find the last row
var LastRow = targetSheet.getLastRow();
//Loop Begins
//Initializing variable assumes that the first valid Drive ID is in the 3rd row.
for(var i = 3; i < LastRow; i++) {
// Get the Document ID. This gets the Document ID
var LookupID = targetSheet.getRange(i,1).getValue();
// Get the Filename
var FileOpen = SpreadsheetApp.open(DriveApp.getFileById(LookupID));
var FileName = FileOpen.getName();
// Write the FileName in the second column
targetSheet.getRange(i,2).setValue(FileName);
Logger.log(i);
Logger.log(LookupID);
Logger.log(FileName);
}
}
Suggestion:
You can try this script together with using clickable image to act as a button for the function to run the script below:
function convertURLtoSheetName() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var cell = ss.getActiveCell().getValue();
var getSheetName = SpreadsheetApp.openByUrl(cell).getName();
var seturl = SpreadsheetApp.newRichTextValue().setText(getSheetName).setLinkUrl(cell).build();
ss.getActiveCell().setRichTextValue(seturl);
}
Here is how you assign a script function to an image:
Custom Functions Cannot open other spreadsheets (SpreadsheetApp.openById() or SpreadsheetApp.openByUrl()).
reference
I am creating a summary sheet for teachers to pull in all their student's data. Each column is a different student and has a different URL for the student's file. I have over 50 teachers and 850+ students. I am trying to find a way using google apps script to find and replace the URLs in a column. I have this code so far, and I can see the 'baseLink' and the 'replaceLink' in the log, but I cannot figure out what to do to get them to replace. I'm attaching a snapshot of the spreadsheet I'm using in case that helps explain my issue better. You can see I use the importrange formula to pull in the URLs and those are what need to be changed. I do need to read through all the columns, but I'm happy just learning how to do one column first. Thanks for any help!
This is the code I have, with a line of pseudo code where I need help:
function UpdateColumnLinks() {
var ss = SpreadsheetApp.getActiveSheet();
var range = ss.getRange('G1:G76');
var baseLink = ss.getRange('R2').getValue(); //This is the URL of a spreadsheet I want to replace that is in the formula
var replaceLink = ss.getRange('S10').getValue(); //This is the URL of a spreadsheet I want to see in the formula
Logger.log(baseLink)
Logger.log(replaceLink)
for i = 0; i<range.length; i++ {
pseudo code: find baseLink in range and replace with replaceLink
}
}
Replacing Links in a Spreadsheet
Rich Text Links:
function UpdateColumnLinks() {
var sh=SpreadsheetApp.getActiveSheet();
var rg=sh.getRange(1,3,sh.getLastRow(),1);
var dvs=rg.getRichTextValues();
var rl=sh.getRange(1,1).getRichTextValue().getLinkUrl();replacement link
var ls=SpreadsheetApp.newRichTextValue().setText(rl).build();
for(var i=0;i<dvs.length;i++) {
var link=dvs[i][0]=ls;
}
rg.setRichTextValues(dvs);
}
RichTextValueBuilder
Class RichTextValue
Formula Links:
function UpdateColumnLinks1() {
const sh=SpreadsheetApp.getActiveSheet();
const rl=sh.getRange(1,1).getFormula();
const rg=sh.getRange(1,3,sh.getLastRow(),1);
var vs=rg.getFormulas();
vs.forEach(function(r,i){
r[0]=rl;
});
rg.setFormulas(vs);
}
Your specific case:
function UpdateColumnLinks() {
var ss=SpreadsheetApp.getActiveSheet();
var rg=ss.getRange('G1:G76);
var fA=rg.getFormulas();
var rl=ss.getRange('S10').getFormula();
fA.forEach(function(r){r[0]=rl;});
rg.setFormulas(fA);
}
My script creates a Google Form programmatically and sets the destination to the current Spreadsheet:
var sheetId = SpreadsheetApp.getActiveSpreadsheet().getId();
var form = FormApp.create(acctName).setTitle(acctName).setDestination(FormApp.DestinationType.SPREADSHEET, sheetId);
// several form items added here
Next, I'd like to rename the destination Sheet and make a few formatting edits. When I do, however, the sheet doesn't seem to exist. For example, getSheets() does not contain the new Sheet! (And yes, ideally I'd like to open the sheet by ID; see function below which also doesn't work.)
var sheets = SpreadsheetApp.openById(form.getDestinationId()).getSheets();
SpreadsheetApp.setActiveSheet(sheets[0]);
The above code opens what I would consider index 1, not index 0, because the sheet isn't indexed. I even tried creating a few second delay (hoping that it was just a matter of time to allow the sync to happen between the client and server) but without any success.
I have also tried something like the following, as suggested elsewhere here on Stack Overflow, but the destination Sheet doesn't come up in getSheets() even when called through a separate function:
function getFormDestinationSheetId(form) {
var destinationId = form.getDestinationId();
if(destinationId) {
var spreadsheet = SpreadsheetApp.openById(destinationId);
spreadsheet.getSheets().forEach(
function(sheet) {
var sheetFormUrl = sheet.getFormUrl();
if(sheetFormUrl) {
var sheetForm = FormApp.openByUrl(sheetFormUrl);
if(sheetForm.getId() == form.getId()) {
return sheet.getSheetId();
}
}
}
);
}
return null;
}
I haven't been able to find anyone have a similar problem on the webs. Any advice would be appreciated. Thanks!
Welcome to Stack!
I assume your script is bound to a sheet? Depending on how you're calling the script you may not see the new sheets because of browser caching.
function myFunction() {
var sheet = SpreadsheetApp.getActive();
var sheetId = sheet.getId();
var form = FormApp.create('acctName').setTitle('acctName').setDestination(FormApp.DestinationType.SPREADSHEET, sheetId);
var ssSheets = sheet.getSheets();
var respSheet = ssSheets[0]
respSheet.getRange('A1').setBackground('RED');
respSheet.getRange('C1').setFormula('=COUNTA($C$2:$C)');
respSheet.setColumnWidth(2, 100);
SpreadsheetApp.flush();
}
I've designed a spreadsheet for tracking status of systems in an enterprise.
It gets updated every 12 hours and then manually renamed based on the date and report number, then it gets archived.
I have created a script that allows the user to create a copy of the current spreadsheet by pressing on a drawing
function copyDocs() {
for(i=0; i<1; i++){
var drive=DriveApp.getFileById('190X72ZGwHtKaUa5MQerlW2k1ops-zYTnr1UXwuweJxGY');
drive.makeCopy();
}
}
I have created a script which allows users to reset all the data fields to empty by pressing on an drawing.
function reset() {
var s = SpreadsheetApp.getActive().getSheetByName('STATUS');
s.getRange("a2:b4").clearContent();
s.getRange("c3:d4").clearContent();
s.getRange("e2:f4").clearContent();
s.getRange("b5").clearContent();
s.getRange("e5:f5").clearContent();
s.getRange("c6:d6").clearContent();
s.getRange("f6").clearContent();
s.getRange("c7:f10").clearContent();
s.getRange("a12:f18").clearContent();
s.getRange("b20:f25").clearContent();
s.getRange("b27:f31").clearContent();
s.getRange("b33:f37").clearContent();
s.getRange("b39:f39").clearContent();
s.getRange("b41:f46").clearContent();
s.getRange("b48:f52").clearContent();
s.getRange("b54:f58").clearContent();
s.getRange("b60:f60").clearContent();
s.getRange("b62:f79").clearContent();
s.getRange("b81:f81").clearContent();
s.getRange("d83:f91").clearContent();
s.getRange("d94:f96").clearContent();
s.getRange("d98:f105").clearContent();
s.getRange("d107:f109").clearContent();
s.getRange("c112:f123").clearContent();
s.getRange("b113:f113").clearContent();
s.getRange("b115:f115").clearContent();
s.getRange("b117:f117").clearContent();
s.getRange("b119:f119").clearContent();
s.getRange("b121:f121").clearContent();
s.getRange("b123:f123").clearContent();
s.getRange("d124:f124").clearContent();
}
What I would like to do is to have one "button" for the user that they press and it runs four scripts in succession. I have tried to think through the best way to make this happen and here is what I need help with.
Rename the Spreadsheet they are currently working on to the data in merged cells (A2:B4) it will be a number like "17-087"
Make a copy of the whole spreadsheet that will automatically save in the same folder
Clear data from the specific cells in the "reset" script above
Rename the current spreadsheet from the data in cell 'J2'
In theory the user will press a button, it will rename the spreadsheet (not the sheet, but the whole file) to the data in the merged cell ie. "status 17-087", then it will make a copy of the whole Spreadsheet (file), then clear all the designated cells, then rename the spreadsheet back to the data in 'J2' which is "STATUS TEMPLATE".
Can anyone help me work through this multi-step script?
Thanks in advance.
Could try something like this:
var ss = SpreadsheetApp.getActiveSpreadsheet();
function onOpen() {
// Add a menu item based on properties (doesn't work in AuthMode.NONE).
var items = [
{name: 'Backup', functionName: 'backup'},
];
ss.addMenu('Backup', items);
}
function backup() {
rename();
copyDocs();
reset();
rename2();
}
function rename(){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var first = ss.getSheetByName("Sheet1");
// renames the google sheet to the data that is in 2nd Row, 3rd Column
ss.rename(first.getRange(2, 3).getValue());
}
function copyDocs() {
for(i=0; i<1; i++){
var drive=DriveApp.getFileById('190X72ZGwHtKaUa5MQerlW2k1ops-zYTnr1UXwuweJxGY');
drive.makeCopy();
}
}
function reset() {
var s = SpreadsheetApp.getActive().getSheetByName('STATUS');
s.getRange("a2:b4").clearContent();
s.getRange("c3:d4").clearContent();
s.getRange("e2:f4").clearContent();
s.getRange("b5").clearContent();
s.getRange("e5:f5").clearContent();
s.getRange("c6:d6").clearContent();
s.getRange("f6").clearContent();
s.getRange("c7:f10").clearContent();
s.getRange("a12:f18").clearContent();
s.getRange("b20:f25").clearContent();
s.getRange("b27:f31").clearContent();
s.getRange("b33:f37").clearContent();
s.getRange("b39:f39").clearContent();
s.getRange("b41:f46").clearContent();
s.getRange("b48:f52").clearContent();
s.getRange("b54:f58").clearContent();
s.getRange("b60:f60").clearContent();
s.getRange("b62:f79").clearContent();
s.getRange("b81:f81").clearContent();
s.getRange("d83:f91").clearContent();
s.getRange("d94:f96").clearContent();
s.getRange("d98:f105").clearContent();
s.getRange("d107:f109").clearContent();
s.getRange("c112:f123").clearContent();
s.getRange("b113:f113").clearContent();
s.getRange("b115:f115").clearContent();
s.getRange("b117:f117").clearContent();
s.getRange("b119:f119").clearContent();
s.getRange("b121:f121").clearContent();
s.getRange("b123:f123").clearContent();
s.getRange("d124:f124").clearContent();
}
function rename2(){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var first = ss.getSheetByName("Sheet1");
// renames the google sheet to the data that is in J2
ss.rename(first.getRange(2, 10).getValue());
}
Having issues getting the following to work.
The intent was on edit to push information from the specific cell or cells of active sheet to specific cells on a separate worksheet.
Note: I am new to google sheets
function onEdit(e) {
var source = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Working");
var cell = source .getActiveCell();
if (cell.getRow() == 9 && cell.getColumn() == 2) {
var target = DriveApp.getFileById("1biaIVlafaNQTHjtR8ctASCpDmC2O1wwfJfAUCmzIztI")
.getSheetByName("Master_Sheet");
target.getRange("A1").setValue(cell.getValue());
}
}
The reason it does not work is because you are using onEdit(). This is a simple trigger that will fire off whenever you edit the sheet. Since simple triggers cannot perform operations that require authorization you are limited to working only in the Spreadsheet and cannot access any other files.
Read up on restrictions here
I am now able to push information to the target sheet using the following code.
function myFunction() {
var sourceSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Source");
var sourceData = sourceSheet.getRange("A3:C3").getValues();
sourceData.splice(0,1); // Remove header
var targetSS = SpreadsheetApp.openById("1pyJzZ86WDh2FNXFufUAt2SkAUod32i7AzvG0EKmnvEU").getSheetByName("Destination");
var targetRangeTop = targetSS.getLastRow(); // Get # rows currently in target
targetSS.getRange(targetRangeTop+1,1,sourceData.length,sourceData[0].length).setValues(sourceData);
};