I am trying to automate attaching and sending emails using Gscripts. Each recipient has his own unique attachment so I have a Google sheet containing the details:
Column A contains the email addresses.
Column B contains the message.
Column D contains the filename of the attachment along with the file extension.
Here's my code:
function sendEmails() {
var sheet = SpreadsheetApp.getActiveSheet();
var rowEmails = sheet.getRange(1,1,3);
var rowMessage = sheet.getRange(1,2,3);
var rowAttachments = sheet.getRange(1,4,3);
var vEmails = rowEmails.getValues();
var vMessage = rowMessage.getValues();
var vAttachments = rowAttachments.getValues()
for (i in vEmails) {
var emailAddress = vEmails[1] + '';
var message = vMessage[1];
var subject = "Test"
var path = 'certs\\' + vAttachments[i]
var file = Drive.Files.get(path);
MailApp.sendEmail(emailAddress, subject, vMessage[1], {attachments: files});
}
}
Everything works well but when it comes to var file = Drive.Files.get(path), I get an error saying no file is found. I checked my drive and I'm sure it is there. I've also checked the Drive API. I don't know what is wrong.
Something like this might be a little closer to working:
function sendEmails() {
var sheet=SpreadsheetApp.getActiveSheet();
var Emails=sheet.getRange(1,1,3).getValues();
var Message=sheet.getRange(1,2,3).getValues();
var fileids=sheet.getRange(1,3,3).getValues();//you need to add file ids
Emails.forEach(function(r,i){
MailApp.sendEmail(Emails[i][0],"Test",Message[i][0],{attachments:[DriveApp.getFileById(fileids[i][0])]});
});
}
The code in the question has several flaws:
path : Google Drive doesn't use "path"
for in / vAttachments[i]: a different property name is assigned to the variable on each iteration but vAttachments hasn't named properties, so vAttachments[i] will return null on each iteration.
The files property on the options should be an Array.
How to "fix" the script
The way to directly get files in Google Drive is by using file ids.
If you want to get files by "path", first your script should get the folder, then look for the file inside that folder but bear in mind that getting a folder/file by name returns a folder/file iterator
Instead of for in use for of or use for (most of the scripts I have seen usen use for)
Related
I am accessing a list of folders from a shared drive.
In here, I am converting a few excel files into spreadsheet. My issue is to replace the old converted files with the new file. This is because every time i run the script the new converted file(with same name) keeps on multiplying in the same folder together with the old one.
Here is the code:
function ConvertFiles() {
var sheet =
SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Sheet1");
var r= 2;
for(r= 2;r < sheet.getLastRow(); r++){
// Use getValue instead of getValues
var fileId = sheet.getRange(r,1).getValue();
var folderID = sheet.getRange(r,8).getValue();
var files = DriveApp.getFileById(fileId);
var name = files.getName().split('.')[0];
var blob = files.getBlob();
var newFile = {
// Remove '_converted' from name if existing to avoid duplication of the string before adding '_converted'
// This will allow to have newly converted file "replace" the old converted file properly
title: name.replace('_converted','') + '_converted',
parents: [{
id: folderID
}]
};
var destinationFolderId = DriveApp.getFolderById(folderID);
var existingFiles = destinationFolderId.getFilesByName(newFile.title);
// GOAL #1: To replace/update the old converted file into the latest one everytime the script runs (if it has the same filename)
// Find the file with same name of the file to be converted
while(existingFiles.hasNext()) {
// ID of the file with same converted name
var oldConvertedFileWithSameNameID = existingFiles.next().getId();
// Delete before writing
Drive.Files.remove(oldConvertedFileWithSameNameID);
//DriveApp.getFileById(oldConvertedFileWithSameNameID.getId()).setTrashed(true);
}
// Create new converted file then get ID
var newFileID = Drive.Files.insert(newFile, blob, { convert: true,supportsAllDrives: true }).id;
Logger.log(newFileID);
//var sheetFileID = newFileID.getId();
//var Url = "https://drive.google.com/open?id=" + sheetFileID;
var Url = "https://drive.google.com/open?id=" + newFileID;
// Add the ID of the converted file
sheet.getRange(r,9).setValue(newFileID);
sheet.getRange(r,10).setValue(Url);
}
}
My goal is
To replace the old converted file with the new one(if they have the same name) into the shared drive folder
To get to know how can i implement the setTrashed() inside the above code
I have tried using the Drive.Files.remove(oldConvertedFileWithSameNameID); but I am getting an error message GoogleJsonResponseException: API call to drive.files.delete failed with error: File not found:("fileid"). Then i saw an question on this [https://stackoverflow.com/questions/55150681/delete-files-via-drive-api-failed-with-error-insufficient-permissions-for-this]...so i guess that method is not suitable to implemented in shared folder.
So i how can i use setTrashed() method inside the above code?
I think you need to set the supportsAllDrives parameter:
Drive.Files.remove(oldConvertedFileWithSameNameID, {supportsAllDrives: true});
References:
Files:delete | Google Drive API | Google Developers - Parameters
I have been researching the site but can't find the solution to my question. I a writing a script to extract a list of one or more filenames from a sheet and use these filenames as input to move the actual files from one folder to another in Drive. My issue now is that I don't know how to handle the value "Fileiterator" that is coming back in my script. As a result, the error I am getting when I run my script is "TypeError: Cannot find function makeCopy in object FileIterator"
I'm not sure if I am missing something when I am using the MakeCopy() method or setting up the variable pulling values from the sheet?
here is my code:
// Access Mailing List sheet in gdrive and get filename
var spreadsheet = SpreadsheetApp.openByUrl('spreadsheetURL');
var sheet = spreadsheet.getSheets()[0];
var value = sheet.getSheetValues(2,39,1,1);
Logger.log(value);
var folder = DriveApp.getFolderById("folderid1");
Logger.log(folder);
var files = DriveApp.getFilesByName(value);
Logger.log(files);
var destination = DriveApp.getFolderById("folderid2");
Logger.log(destination);
newfile = files.makeCopy("copy of"+files,destination);
Logger.log(newfile);
Please advise!
The problem occurs because getFilesByName(name) returns a FileIterator object but makeCopy is a File object method.
Example:
This shows how to use a file iterator.
var name = 'File name';
var destination = DriveApp.getFolderById("folderId");
var files = DriveApp.getFilesByName("File name");
while (files.hasNext()) {
var file = files.next();
file.makeCopy("copy of " + name, destination);
}
I attempted to insert jpg images stored in a Google Drive folder into a spreadsheet using the script below. The Url I get using the getUrl() method does not work. However if I use this url to open the image in Chrome and right click on the image and choose 'Get image URL,' I get a Url that does work. Is there a script method that will get me the correct Url? Or is there another way of accomplishing the same result?
function testInsertImage() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheets()[0];
var folder = DocsList.getFolder('DataBasePicts');
var files = folder.getFiles();
var img = files[0].getUrl();
sheet.insertImage(img, 2, 2); // In Class Sheet method 'insertImage(url, column, row)'
}
// Url obtained by the .getUrl() above which does not work
// "https://docs.google.com/open?id=1fxx_KYV46swKQk5vh9h1ideOhW76ZhJVYIPUjopbXm4"
// Url obtained by right clicking the image when opened in Chrome using above Url which does work
// "https://lh6.googleusercontent.com/eWA2oIabdGeXLnIRkTkdXuZFlvt6L_pJbgKBLoTFVDEWVESPxpvziHJnFpeXocMmnwUEvYWIab4=w1318-h612"
//.insertImage gives this error message:
// Error retrieving image from URL or bad URL: https://docs.google.com/open?
The Advanced Drive Service enables use of the Google Drive Web API from Google Apps Script. Once you've enabled the service by following these instructions, its methods and properties will show up in the GAS editor's Autocomplete feature, making it easy to explore the available capabilities.
For instance, an ADS File object has a collection of properties documented here. The one you're interested in is .webContentLink. We can easily combine ADS with the DriveApp methods that replaced the deprecated DocsList, for instance by retrieving the fileId of the image you're interested in and using it with the ADS get() function.
function testInsertImage() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheets()[0];
var folders = DriveApp.getFoldersByName('DataBasePicts');
if (folders.hasNext()) {
// Assume folder name is unique, so use first match
var folder = folders.next();
var files = folder.getFiles();
if (files.hasNext()) {
// For this test, use first found file
var file = files.next();
var img = Drive.Files.get(file.getId()).webContentLink;
sheet.insertImage(img, 2, 2); // In Class Sheet method 'insertImage(url, column, row)'
}
// else error: no file found
}
// else error: no folder found
}
use getWebContentLink() method of File.
File file = serive.files().get(fileId).execute();
file.getWebContentLink()
I am just beginning to learn Javascript and Google Apps Script. I have looked at and played with a few scripts, and so am attempting to create my own. What I want to do is take a list of students (Name and Email) and run a script to create "dropboxes", or folders that uses their name and is shared to their email address. This way, they can easily submit work to me in an organized manner. I have written this rough script, which I know will not work.
I was wondering if anyone can give me some tips?
function createDropbox () {
// Get current spreadsheet
var ss = SpreadsheetApp.getActiveSpreadsheet();
var data = sh1.getDataRange().getValues();
// For each email address (row in a spreadsheet), create a folder, name it with the data from the Class and Name column, then share it to the email
for(n=1;n<data.length;++n){
var class = sheet.getSheetValues(n, 1, 1, 1);
var name = sheet.getSheetValues(n, 2, 1, 1);
var email = sheet.getSheetValues(n, 3, 1, 1);
var folder = DocsList.createFolder('Dropbox');
folder.createFolder(class . name);
var share = folder.addEditor(email);
}
}
You've got the basic structure of your script right, it's only the semantics and some error handling that need to be worked out.
You need to determine how you want to access the contents of your spreadsheet, and be consistent with that choice.
In your question, you are first getting all the contents of the spreadsheet into a two-dimensional array by using Range.getValues(), and then later trying to get values directly from the sheet multiple additional times using .getSheetValues().
Since your algorithm is based on working through values in a range, use of an array is going to be your most effective approach. To reference the content of the data array, you just need to use [row][column] indexes.
You should think ahead a bit. What will happen in the future if you need to add more student dropboxes? As your initial algorithm is written, new folders are created blindly. Since Google Drive allows multiple folders with the same name, a second run of the script would duplicate all the existing folders. So, before creating anything, you will want to check if the thing already exists, and handle it appropriately.
General advice: Write apps script code in the editor, and take advantage of auto-completion & code coloring. That will help avoid mistakes like variable name mismatches (ss vs sh1).
If you are going to complete the exercise yourself, stop reading here!
Script
This script has an onOpen() function to create a menu that you can use within the spreadsheet, in addition to your createDropbox() function.
The createDropbox() function will create a top level "Dropbox" folder, if one does not already exist. It will then do the same for any students in the spreadsheet, creating and sharing sub-folders if they don't already exist. If you add more students to the spreadsheet, run the script again to get additional folders.
I've included comments to explain some of the tricky bits, as a free educational service!
/**
* Create menu item for Dropbox
*/
function onOpen() {
var sheet = SpreadsheetApp.getActiveSpreadsheet();
var entries = [{
name : "Create / Update Dropbox",
functionName : "createDropbox"
}];
sheet.addMenu("Dropbox", entries);
};
function createDropbox () {
// Get current spreadsheet
var ss = SpreadsheetApp.getActiveSpreadsheet();
var data = ss.getDataRange() // Get all non-blank cells
.getValues() // Get array of values
.splice(1); // Remove header line
// Define column numbers for data. Array starts at 0.
var CLASS = 0;
var NAME = 1;
var EMAIL = 2;
// Create Dropbox folder if needed
var DROPBOX = "Dropbox"; // Top level dropbox folder
try {
// getFolder throws an exception if folder not found.
var dropbox = DocsList.getFolder(DROPBOX);
}
catch (e) {
// Did not find folder, so create it
dropbox = DocsList.createFolder(DROPBOX);
}
// For each email address (row in a spreadsheet), create a folder,
// name it with the data from the Class and Name column,
// then share it to the email
for (var i=0; i<data.length; i++){
var class = data[i][CLASS];
var name = data[i][NAME];
var email = data[i][EMAIL];
var folderName = class + ' ' + name;
try {
var folder = DocsList.getFolder(DROPBOX + '/' + folderName);
}
catch (e) {
folder = dropbox.createFolder(folderName)
.addEditor(email);
}
}
}
Even though the question is ~6 years old it popped up when I searched for "create folders from sheets data" .
So , taking the answer as inspiration, I had to update the code to allow for changes in the Google API, i.e. no more DocsList.
So here it is.
function createMembersFolders () {
// Get current spreadsheet
var MembersFolder = DriveApp.getFolderById("1Q2Y7_3dPRFsblj_W6cmeUhhPXw2xhNTQ"); // This is the folder "ADI Members"
var ss = SpreadsheetApp.getActiveSpreadsheet();
var data = ss.getDataRange().getValues() // Get array of values
// Define column numbers for data. Array starts at 0.
var NAME = 1;
// For each name, create a folder,
for (var i=1; i<data.length; i++){ // Skip header row
var name = data[i][NAME];
Logger.log(name);
if(MembersFolder.getFoldersByName(name).hasNext()){
Logger.log("Found the folder, no need to create it");
Logger.log(Object.keys(MembersFolder.getFoldersByName(name)).length);
} else {
Logger.log("Didn't find the folder so I'll create it");
var newFolder = MembersFolder.createFolder(name);
}
}
}
Note that I'm taking the parent folder directly using it's ID (which you get from the URL when viewing the folder).
You could also do this by name using the DriveApp.getFoldersByName("Folder Name").hasNext() condition which checks if the folder exist. If it does exist then you can access that folder you have found via DriveApp.getFoldersByName("Folder Name").next()
I didn't find that use of hasNext and next very intuitive but there it is.
I'm trying to relocate newly created Google Docs file to a folder within google drive (using Google Apps).
var newFile = DocumentApp.create('New File');
var newFileID = Docs.getFileById(newFile);
var newFileRelocated = newFileID.addToFolder(newFolder);
And I'm getting "Cannot find method addToFolder(. (Line ...)". What am I doing wrong? They method drops down as an option when I'm writing it and still it cannot find it.
It's likely that your newFolder is not what's expected. Is it a string? Where you defined it?
Anyway, the parameter expected in addToFolder must be a Folder object you got using some other method in DocsList. e.g. DocsList.getFolder("/path/to/folder") or DocsList.getFolderById("folder-id") and so on.
There seems to be other "inconsistencies" with your code, I'll paste what I you're trying to do:
var newDoc = DocumentApp.create('New Google Doc');
//a DocumentApp file and a DocsList file are not the same object, although they may point to the same Google Doc
var newFile = DocsList.getFileById(newDoc.getId());
var folder = DocsList.getFolder("/path/to/folder"); //I'm assuming the folder already exists
newFile.addToFolder(folder);
The logic of this is not exactly as you tried...
here is a working example :
function myFunction() {
var newFile = DocumentApp.create('New File');
var newFileID = newFile.getId()
var newFolder = DocsList.createFolder('test Folder')
DocsList.getFileById(newFileID).addToFolder(newFolder)
}
Just to add to this, I recently dealt with this issue.
I noticed the default location is to store the DocsList.create() file in the root folder (aka My Drive).
This could lead to a real headache if you were doing lots of files.
I added this as the line after the .addToFolder()
newFile.removeFromFolder(DocsList.getRootFolder());
The following function is a simple google script to pass in an image URL.
function fetchImageToDrive_(imageUrl)
{
//Fetch image blob
var imageBlob = UrlFetchApp.fetch(imageUrl).getBlob();
//Create image file in drive
var folder = DocsList.getFolder('test Folder');
folder.createFile(imageBlob);
}