Why doesn't my Google Apps Script work anymore? - google-apps-script

I don't believe I changed my code, yet it doesn't work anymore. Did Apps Script change something? I enabled the Drive API and tried both the legacy editor and the new editor.
I'm trying to replace links in multiple Google Docs within a folder. Below is a snippet of the code but I have 50+ links.
function myFunction() {
var oldLinkText = "Casarett and Doull’s Essentials of Toxicology, 3e";
var newLinkText = "Casarett & Doull’s Essentials of Toxicology, 4e";
var oldLinkURL = "accesspharmacy.mhmedical.com/book.aspx?bookID=1540";
var newLinkURL = "accesspharmacy.mhmedical.com/Book.aspx?bookid=3000";
var oldLinkText2="Harrison's Principles of Internal Medicine, 19e";
var newLinkText2="Harrison's Principles of Internal Medicine, 20e";
var oldLinkURL2="accessmedicine.mhmedical.com/book.aspx?bookid=1130";
var newLinkURL2="accessmedicine.mhmedical.com/book.aspx?bookid=2129";
var oldLinkText3="Behavioral Medicine: A Guide for Clinical Practice, 4e";
var newLinkText3="Behavioral Medicine: A Guide for Clinical Practice, 5e";
var oldLinkURL3="accessmedicine.mhmedical.com/book.aspx?bookID=1116";
var newLinkURL3="accessmedicine.mhmedical.com/book.aspx?bookid=2747";
var files = DriveApp.getFolderById("folderID").getFilesByType(MimeType.GOOGLE_DOCS);
while (files.hasNext()) {
var file = files.next();
if(file){
var doc = DocumentApp.openById(file.getId());
var link=doc.getBody().findText(oldLinkText);
var link2=doc.getBody().findText(oldLinkText2);
var link3=doc.getBody().findText(oldLinkText3);
}
}
if(link){
link=link.getElement().asText();
link.setLinkUrl(newLinkURL);
doc.replaceText(oldLinkText, newLinkText);
}
if(link2){
link2=link2.getElement().asText();
link2.setLinkUrl(newLinkURL2);
doc.replaceText(oldLinkText2, newLinkText2);
}
if(link3){
link3=link3.getElement().asText();
link3.setLinkUrl(newLinkURL3);
doc.replaceText(oldLinkText3, newLinkText3);
}
Logger.log("Done")
}

I used the code on one file and it worked, but I was trying to get it to work on multiple Google Docs files within a Google Drive folder. It kept stopping at one. In other words, it would look at the first file in the folder, and would stop looking if it didn't find the text in that file. It ignored the other files in the Google Drive folder.
I changed two things in the code and now it worked. I removed the two curly brackets before "if(link){" and put them at the very end of the code. I also changed ".getFilesByType(MimeType.GOOGLE_DOCS);" to just ".getFiles();"

Related

Find and Replace text in entire Google Drive folder, headers, footers, google docs using a script

I have numerous Google Docs with a header and footer. Multiple links on our intranet to these documents exist so I don't want to make new documents. There are hundreds of documents to update.
I need to update the physical address and header/footer letterhead in these documents. I need to copy the images, formatting, tables, etc in the header and footers too.
I wish they were more like CSS or templates that could be changed across multiple documents at once.
Multiple considerations have been exhausted trying to do this:
Apply a template with a new header and footer to an existing document - does not seem to be possible. Each has to be copied and pasted manually.
Applications like Dreamweaver allow you to Find and Replace to all files in a folder. There is no way to do this that I could find for Google Docs.
Is there a way to automate this tedious process? Even a starting point hint!
function replaceHeaderAndFooter() {
var headerToCopyandPaste = DocumentApp.openById("<SourceDocID>").getHeader().copy(); // ID contains source header
var footerToCopyandPaste = DocumentApp.openById("<SourceDocID>").getHeader().copy(); // ID contains source footer
var files = DriveApp.getFolderById("<FOLDER ID>").getFiles();
while (files.hasNext()) {
var file = files.next();
var doc = DocumentApp.openById(file.getId());
var headerSectionToBeReplaced = doc.getHeader()
var footerSectionToBeReplaced = doc.getFooter()
headerSectionToBeReplaced.clear()
headerSectionToBeReplaced = headerToCopyandPaste
}
}
I've also tried to put everything in the template (images, paragraphs, formatting) into a single table in the header, for example. This code does not work either. The following code Results in the following error:
The parameters (String) don't match the method signature for DocumentApp.HeaderSection.appendTable
function replaceHeaderAndFooter() {
const headerToCopyandPaste = DocumentApp.openById("<SourceDocID>").getHeader().getTables().toString(); // ID contains source header
const footerToCopyandPaste = DocumentApp.openById("<SourceDocID>").getHeader().copy(); // ID contains source footer
var files = DriveApp.getFolderById("<FolderID>").getFiles(); //ID contains folder that has Google Docs that will have Header and Footer Replaced
while (files.hasNext()) {
var file = files.next();
var doc = DocumentApp.openById(file.getId());
var headerSectionToBeReplaced = doc.getHeader()
var footerSectionToBeReplaced = doc.getFooter()
headerSectionToBeReplaced.clear();
headerSectionToBeReplaced.appendTable(headerToCopyandPaste)
}
}
Yes, there is a way, and that is using replaceText(). If your files do belong in a single folder, then it is easy to search and replace them all.
function replaceTextSpecificFolder() {
var files = DriveApp.getFolderById("<folder_id>").getFiles();
while (files.hasNext()) {
var file = files.next();
var doc = DocumentApp.openById(file.getId());
doc.replaceText("<replace>", "<new string>");
}
}
<folder_id> is everything that comes after “folders/” in the URL when you visit the Google Drive folder.
For example, if the URL was
"https://drive.google.com/drive/folders/moqwiSADN921m03uhwdJ",then the Folder ID would be “moqwiSADN921m03uhwdJ”.
EDIT:
As for the other items like formatting, tables, images, etc., you can check this documentation. Find it under its respective class on the left pane. (E.g. You can find table functions under Table Class)

How to create a shortcut in Google Drive Apps Script instead of multiple parents

I've been trying to read about it here: https://developers.google.com/drive/api/v3/shortcuts
And I understand the basics in shortcuts but I can't figure out how to use it in the code - the syntax.
I need to instead of placing a folder in multiple folders, placing shortcuts in those folders named the same as the original folder.
I have an original folder called Project1 placed in Drafts. Shortcuts for this folder needs to be places in Folder2/Drafts, Folder3/Drafts and Folder4/Drafts.
If I afterwards change the name of the original folder Project1 ex. to "New Building" I need to find the shortcuts in Folder 2,3 and 4 (by Id? - I can put the ID of the shortcuts in a datasheet from where I can iterate through them) - and then rename them as the original folder's new name.
And when I move the Project1 from Drafts to Confirmed. I need to move the shortcuts from Folder2/Drafts to Folder2/Confirmed etc.
This is mainly how the basic code looked like for placing the new folder
var folder1 = draft.createFolder("DRAFT - "+"Project1");
var folder1Url = folder1.getUrl();
var folder1Id = folder1.getId();
folder2Draft.addFolder(folder1);
When changing the name of the folder the name would (of course) change in the other places. But as I understand this is not the case with shortcuts. I've tested it with manually created shortcuts witch confirmed this.
The renaming part I do like this when confirming the project:
var folder1 = DriveApp.getFolderById(folder1Id);
var folder1NameOld = folder1.getName();
var folder1NameNew = folder1NameOld.replace("DRAFT - ","");
folder1.setName(folder1NameNew);
And I move the file when confirming the project with the simple:
confirmed.addFolder(folder1);
draftsFolder.removeFolder(folder1);
The script is made in a spreadsheet and I already put alle the folderIDs in a data sheet in the file so I can very easy make references to the different folders and if needed also collect the IDs of the shortcuts to be able to rename them.
Update:
To make a more clear question:
How to do this with shortcuts instead of multi-parenting?
function shortcut() {
var s = SpreadsheetApp.getActive();
var sheet = s.getActiveSheet();
var projectFolderId = sheet.getRange('B1').getValue();
var folder1DraftId = sheet.getRange('B2').getValue();
var folder2DraftId = sheet.getRange('B3').getValue();
var folder1 = DriveApp.getFolderById(projectFolderId);
DriveApp.getFolderById(folder1DraftId).addFolder(folder1);
DriveApp.getFolderById(folder2DraftId).addFolder(folder1);
}
I believe your goal is as follows.
You want to achieve the following script as the shortcut.
function shortcut() {
var s = SpreadsheetApp.getActive();
var sheet = s.getActiveSheet();
var projectFolderId = sheet.getRange('B1').getValue();
var folder1DraftId = sheet.getRange('B2').getValue();
var folder2DraftId = sheet.getRange('B3').getValue();
var folder1 = DriveApp.getFolderById(projectFolderId);
DriveApp.getFolderById(folder1DraftId).addFolder(folder1);
DriveApp.getFolderById(folder2DraftId).addFolder(folder1);
}
At above script, folder1 is put in the folders of folder1DraftId and folder2DraftId.
For this, how about this answer?
Modification points:
In this case, the method of files.create in Drive API is used. But in the current stage, the method of Files: insert in Drive API v2 can also create the shortcut. So in this answer, Drive API v2 of Advanced Google services is used.
Modified script:
When your script is modified, it becomes as follows. Before you run the script, please enable Drive API at Advanced Google services.
From:
var folder1 = DriveApp.getFolderById(projectFolderId);
DriveApp.getFolderById(folder1DraftId).addFolder(folder1);
DriveApp.getFolderById(folder2DraftId).addFolder(folder1);
To:
const folderIDs = [folder1DraftId, folder2DraftId];
folderIDs.forEach(f => {
Drive.Files.insert({
shortcutDetails: {targetId: projectFolderId},
parents: [{id: f}],
title: DriveApp.getFolderById(projectFolderId).getName(),
mimeType: "application/vnd.google-apps.shortcut"
});
});
References:
Advanced Google services
Create a shortcut to a Drive file
Files: insert of Drive API v2
The official document says as follows.
Note: Apps creating shortcuts with files.insert must specify the MIME type application/vnd.google-apps.drive-sdk.
But, when application/vnd.google-apps.drive-sdk is used, the shortcut couldn't be created. I'm not sure whether this is the bug or the current specification. So I used application/vnd.google-apps.shortcut as the mimeType.

Create Google Apps Script file with DriveApp

Code to create an appscript file using appscript editor.
for example you type this:
function msWordExt() {
var ss = SpreadsheetApp.openById('1MUwH0Cm1cwHcTWSCGPp2SePbzs_4QQmtjwOEWOrOkMw');
var sheetName = ss.getSheetByName("Sheet2");
var get = sheetName.getRange(2,3).getValue();
var folder = DriveApp.getFolderById("1rsicZccurujGp5Ye5HUecBIAPf3_h5Pc");
folder.createFile("PLAIN TEXT",get,MimeType.MICROSOFT_WORD);}
So the above code creates a MS Word extension file inside a persons Google Drive.
Now I want to try creating a google appscript file extension using code as well, for example the below code doesn't work but it will help give the gist of what I wish to accomplish:
function appScript() {
var ss = SpreadsheetApp.openById('1MUwH0Cm1cwHcTWSCGPp2SePbzs_4QQmtjwOEWOrOkMw');
var sheetName = ss.getSheetByName("Sheet2");
var get = sheetName.getRange(2,3).getValue();
var folder = DriveApp.getFolderById("1rsicZccurujGp5Ye5HUecBIAPf3_h5Pc");
folder.createFile("This is an appscript file",get,MimeType.GOOGLE_APPS_SCRIPTS);
}
Considerations
You can't use the Drive API to create a Google Apps Scripts Project. Here you can see it's already been reported to Google.
Proposed workaround
You can use the Apps Script API to manage your Apps Script projects. Here is a link to the guide: https://developers.google.com/apps-script/api/samples/manage
Reference
Managing Apps Script Projects

Google Apps Script moving file to Team Drive folder

Background:
At the core of the issue is I have an Alteryx job dropping files into my google drive. These files need, in actuality, in a Team Drive folder. Try as I might, nowhere have I found a way for Alteryx to do this. So hence the need for this script.
Actual problem:
So here is the criteria: I have the files being created with the same naming convention with only the date changing. I need these files to go from my drive to a team drive where they are eventually worked on.
Using the resources already here on stack I found wonderful solutions here: 1 and here 2 that I was able to cobble together a working script.
Understand I am a marginally functional python programmer for data analytics. So my JS and Google scripting are rudimentary at best. The first time I tested the script, it worked. Wonderfully, right up until it didn't.
It moved my first file with no problem. I then created a few copies of that same file in the drive to see how it handled multiple. I now get an error:
Exception: No item with the given ID could be found, or you do not
have permission to access it. (line 15, file "CodeA1")
Here is my code:
function SearchFiles() {
//searches based on naming criteria
var searchFor ='title contains "Reference Data Performance"'; //test file
var names =[];
var fileIds=[];
var files = DriveApp.searchFiles(searchFor);
while (files.hasNext()) {
var file = files.next();
var fileId = file.getId();// To get FileId of the file
fileIds.push(fileId);
var name = file.getName();
names.push(name);
}
var file = DriveApp.getFileById(fileIds);
supportsTeamDrives: true;
supportTeamDrives: true;
var targetFolder = DriveApp.getFolderById('TEAMDriveID');
targetFolder.addFile(file);
}
Exception: No item with the given ID could be found, or you do not
have permission to access it.
This error occurs most often if either
The TeamDriveId is not correct
The account from which you run the script is not member of the team drive
Also note:
supportsTeamDrives: true;
supportTeamDrives: true;
are parameters
for the Drive API, not to be confused with DriveApp
Update:
I ended up going with a simplified version of this script. I had Alteryx schedule the file creation (three different files) at hour increments. Then made a trigger in Google Apps to run the script at the times directly after each Alteryx scheduled drop. Simple? Yes. Inelegant? Maybe. Been running all week and files arrive at their destination for people to work on.
function SearchFiles() {
//searches based on naming criteria
var searchFor ='title contains "Reference Data Performance"'; //looks for file that matches requirements
var files = DriveApp.searchFiles(searchFor);
while (files.hasNext()) {
var file = files.next();
var fileId = file.getId();// To get FileId of the file
var file = DriveApp.getFileById(fileId); //grabs file ready to pass to google drive.
supportsTeamDrives: true;
supportTeamDrives: true;
var targetFolder = DriveApp.getFolderById('FOLDERID');
targetFolder.addFile(file);//good for getting a single file needs loop to grab multiple from Array
}
}

Showing thumbnails in a Google sheet [duplicate]

This question already has answers here:
How to get the file URL from file name in Google Sheets with correct Authorization via custom function/script
(3 answers)
Closed 4 years ago.
The code below generates the IMAGE function for the sheet to show thumbnails of all (PDF) files in a chosen folder, obtained with a URL and the file ID:
function scannedMail() {
var files, file, sheet;
sheet = SpreadsheetApp.getActive().getSheetByName('ScannedMail');
files = DriveApp.getFoldersByName("ScannedMail").next().searchFiles('');
var i = 1;
while (files.hasNext()) {
var file = files.next();
var ID = file.getId();
sheet.getRange('A' + i).setValue("=IMAGE(\"https://drive.google.com/thumbnail?authuser=0&sz=w320&id=" + ID + "\"\)");
sheet.getRange('B' + i).setValue(file.getName());
i=i+1;
}
}
Yet it does not show the thumbnails. I found out that it shows just the ones where I manually retrieved the ID from getting a "shareable link". Apparently this ensures the right share settings to get the thumbnails of my own files.
1) Is the previous assumption correct, and why do I need to adapt share settings somehow, where I have read other files without any issues?
2) How can I adapt the script to adapt the share settings, or make it work otherwise?
The script is meant to operate just within my own Google account, and to keep the files private.
I tried sharing the folder with myself, but that does not make a difference (or sense). Is the script somehow regarded as being another user than myself?
Following suggestions from #Rubén and #Cooper, I have tried using insertImage either based on a URL:
sheet.insertImage(file.thumbnailLink, 1, i)
or based on a blob:
sheet.insertImage(file.getThumbnail(), 1, i)
But the most I could get out of Google was "We're sorry, a server error occurred. Please wait a bit and try again", with the code below:
function ScannedMail() {
var files, file, , name, blob, sheet;
sheet = SpreadsheetApp.getActive().getSheetByName('ScannedMail');
files = DriveApp.getFoldersByName("ScannedMail").next().searchFiles('');
var i = 1;
while (files.hasNext()) {
file = files.next();
name = file.getName(); //not needed, just for debugging
blob = file.getThumbnail();
sheet.insertImage(blob, 1, i); // it runs up to here...
i = i + 1;
}
}
The code execution gets stuck on the first occurrence of insertImage().
So we have 3 approaches (IMAGE function in sheet, insertImage(URL,1,1), and insertImage(blob,1,1)) but all 3 do not make a thumbnail appear, apart from the first method when you make the file public (not a serious option).
I don't see a duplicate question and answer that helps me find out what is wrong with my code, or helps me to somehow get the required thumbnails in the spreadsheet. The kindly proposed solutions did not succeed in that yet.
Try something like this:
function imgArray() {
var ss=SpreadsheetApp.getActive();
var sh=ss.getSheetByName('ImageArray');
if(!sh){
sh=ss.insertSheet('ImageArray');
}
var imgA=[];
var folder=DriveApp.getFolderById('folderid');
var files=folder.getFiles();
while(files.hasNext()){
var file=files.next();
var filename=file.getName();
imgA.push(file.getBlob());
}
for(var r=0;r<imgA.length;r++){
sh.insertImage(imgA[r],1,r+1);
}
}
This was adapted from an answer from #Tanaike.
I guess this is what you were looking for:
function ScannedMail() {
var sheet = SpreadsheetApp.getActive().getSheetByName('ScannedMail');
var files = DriveApp.getFoldersByName("ScannedMail").next().searchFiles('');
var i = 1;
while (files.hasNext()) {
var file = files.next();
var blob = file.getBlob();
sheet.insertImage(blob, 1, i); // it runs up to here...
i = i + 1;
}
}
Google Sheets IMAGE built-in function only is able to retrieve images that are publicly available, so, yes you should have to adapt the sharing settings to make the images viewable by anyone.
In order to keep the files private you should not use IMAGE built-in function, you could use one of the methods of Class Sheet to insert images like insertImage(blobSource,column,row). See answer to Adding Image to Sheet from Drive for an example.
NOTES:
As custom functions run anonymously they can't be used them either.
According to Mogsdad answer to InsertImage(url,x,y) doesn't work with Google Drive insertImage(url,row,column) can't be used to insert images from Google Drive
Related
Image function or .insertImage not working for Google Apps Script and Sheets
What is the right way to put a Drive image into a Sheets cell programmatically?