Is it possible that I could add the body of a gdoc into an email? I kinda have an idea of how to do it but I am not completely sure. I have written this code below to kinda help me. I am new to this and I have managed to have a few scripts running, but I am completely lost on this one. I have watched several videos and this is what I was able to do. The code is below.
Basically what I want to do is to be able to have a user input his name and another variable and then go to the google doc file and change it to the value that was input and then put it back in an email and send it to an address... Any ideas of what I am doing wrong or where should I start?? Thanks in advance.
function gsnot() {
var emailaddress="albdominguez25#gmail.net";
var sub="Subject1";
var pattern = Browser.inputBox("Enter your name");
var pattern2 = Browser.inputBox("Enter the minutes:");
var templateDocID= ScriptProperties.getProperty("EmailTemplateDocId");
var doc = DocumentApp.openById(templateDocID);
var body = doc.getActiveSection()
var html = "";
var keys = {
name: pattern,
min: pattern2,
};
for ( var k in keys ) {
body.replaceText("%" + k + "%", keys[k]);
doc.saveAndClose();
html = getDocAsHtml(docId);
DocsList.getFileById(docId).setTrashed(true);
return html;
var emailaddress="albdominguez25#gmail.net";
var sub="Subject1";
MailApp.sendEmail(emailaddress,sub, {htmlBody: body});}}
You might want to change your code by reading the body from the document into a variable, doing the replace on the variable and inserting that into your email. For example:
function gsnot() {
var emailaddress = "albdominguez25#gmail.net";
var sub = "New Subject";
var pattern = Browser.inputBox("Enter your name:");
var pattern2 = Browser.inputBox("Enter the minutes:");
var templateDocID = ScriptProperties.getProperty("EmailTemplateDocId");
var doc = DocumentApp.openById(templateDocID);
var body = doc.getText();
var replacement;
var k;
var keys = {
name: pattern,
min: pattern2
};
for (k in keys) {
if (keys.hasOwnProperty(k)) {
replacement = new RegExp("%" + k + "%",'g');
body = body.replace(replacement, keys[k]);
}
}
MailApp.sendEmail(emailaddress,sub, '', {htmlBody: body});
}
A few notes:
It is good form to have all your var statements at the beginning of
the function
When you use for-in (eg. for (k in keys) ), it returns all properties of the object. You only want the ones you assigned. This is the reason for: for (k in keys)
You had the mail sending for each property, I believe you wanted it outside the for-in loop so it only sent after all the replacements were completed.
Using replace(), you need to create a regular expression object that is set to global
or it will only replace the first instance of the pattern (you have name twice).
In your parameters for sendEmail(), even if you are using the htmlBody option, you need to specify a plain text body. I used empty quotes ''.
Related
I have the following issue. I am trying to create a script that will autofill a template google document using the submission of a google form. I am able to get the script to work for questions that are input with text but am struggling on getting the data from questions in the form that are checkboxes (or multiple choice) to work and fill the google document. Any assistance would be great. For example the variable identified as "offense" is from a question with checkboxes that has about 30 different options, I would like each option that is checked on the form to replace text within my google doc. Thanks.
function autoFillGoogleDocFromForm(e) {
//e.values is an array of form values
var timestamp = e.values[4];
var studentName = e.values[3];
var oe = e.values[16];
var gradelevel = e.values[14];
var program = e.values[15];
var offense = e.values[6];
var action = e.values[18];
var serve = e.values[31];
var makeUp = e.values[32];
var comments = e.values[29];
//file is the template file, and you get it by ID
var file = DriveApp.getFileById('1nPWC0IKc1zUJXYxbGahJsSW4uNWwhxnLM8shcD8kEE4');
//We can make a copy of the template, name it, and optionally tell it what folder to live in
//file.makeCopy will return a Google Drive file object
var folder = DriveApp.getFolderById('1FlpHRKqYrEHttA-3ozU3oUVJlgiqqa-F')
var copy = file.makeCopy(studentName + ', ' + timestamp, folder);
//Once we've got the new file created, we need to open it as a document by using its ID
var doc = DocumentApp.openById(copy.getId());
//Since everything we need to change is in the body, we need to get that
var body = doc.getBody();
//Then we call all of our replaceText methods
body.replaceText('<<Student Name>>', studentName);
body.replaceText('<<Incident Date>>', timestamp);
body.replaceText('<<Student Grade>>', gradelevel);
body.replaceText('<<Open enrolled?>>', oe);
body.replaceText('<<IEP/504?>>', program);
body.replaceText('<<Reason for Referral (Handbook)>>', offense);
body.replaceText('<<Administrative Action>>', action);
body.replaceText('<<Date(s) to be Served>>', serve);
body.replaceText('<<Make up Date(s)>>', makeUp);
body.replaceText('<<Comments>>', comments);
//Lastly we save and close the document to persist our changes
doc.saveAndClose();
}
You need to use the labels assigned to the checkboxes to determine if they have been checked. Same for multiple coice.
You can't use ListItems because you can't set the glyph to a check box so I simply insert text with a checkbox character.
I created a form
I then created an onFormSubmit(e) installed trigger in the spreadsheet to get the form response and put it in the Doc. Here I've simply used an active doc to perform my tests. You will need to adjust the script to handle your template doc.
function onFormSubmit() {
// test data
let e = {"authMode":"FULL","namedValues":{"Timestamp":["8/16/2022 14:40:26"],"Student Grade":["Junior"],"Reason for Referrel":["Bad grades, Disruptive in class, Other"],"Student Name":["Joe Smith"],"Open Enrollment":["Yes"]},"range":{"columnEnd":5,"columnStart":1,"rowEnd":2,"rowStart":2},"source":{},"triggerUid":"12151926","values":["8/16/2022 14:40:26","Joe Smith","Junior","Yes","Bad grades, Disruptive in class, Other"]};
try {
let doc = DocumentApp.getActiveDocument();
let body = doc.getBody();
let referrels = ["Bad grades","Unexcused absence","Disruptive in class","Fighting","Other"];
body.replaceText("<<Student Name>>",e.namedValues["Student Name"]);
body.replaceText("<<Student Grade>>",e.namedValues["Student Grade"]);
body.replaceText("<<Open Enrollment>>",e.namedValues["Open Enrollment"]);
// Notice the regex expression below because findText doesn't seem to handle parenthesis well
let text = body.findText("<<Reason for Referral.*>>");
body.replaceText("<<Reason for Referral.*>>","");
if( text ) {
let index = body.getChildIndex(text.getElement().getParent())+1;
referrels.forEach( item => {
let checked = e.namedValues["Reason for Referrel"][0].indexOf(item);
if( checked >= 0 ) {
let listItem = body.insertListItem(index,item);
index = body.getChildIndex(listItem)+1;
}
}
);
}
}
catch(err) {
Logger.log(err);
}
}
I am officially stuck! Hopefully a fresh set of eyes can help...
I can't figure out out to grab the entire body of my source template and place it in one shot on the target document for reception of the data. As you can see from my code below, my workaround (and literally only thing I stumbled upon that worked) was to grab each line of the template document, and then place each line one-by-one on the target document. However, I don't consider this the appropriate solution for a few reasons: it's not pretty, it's a more resource-expensive run, and it absolutely would not work if I was creating a letter.
Thankfully, since this was envelopes, I got through the job, but I'd like to discover the correct solution before my next mailing. I poured through the documentation, and there were a few functions that were potential candidates (such as 'getBody') but seemed not to be available (I would get 'not a function' errors. So, I'm at a loss.
Another issue with getBody(): it seems to only send plain-text forward. It does not retain any formatting or fonts I arranged in my template.
So my objectives are:
1. Grab the rich-text content of my template document
2. With each loop iteration, apply the content to the next page of target document in one-shot (not line by line).
3. Have this content maintain the formatting (font sizes, fonts, tabbing, spacing, etc.) of my template.
4. Update the dynamic fields with the row of information it's on for that iteration and move on.
I would greatly appreciate any help and/or insight!
Thanks!
function envelopeMailMerge() {
var sourceID = "[id of data sheet]";
var rangeData = 'OnePerFamily!A2:E251';
var values = Sheets.Spreadsheets.Values.get(sourceID,rangeData).values;
var templateID = "[id of template document]";
var targetID = "[id of target document]";
var templateBody = DocumentApp.openById(templateID).getBody();
var targetBody = DocumentApp.openById(targetID).getBody();
//obviously what follows is a ridiculous way to do this, hence my issue
var theContent = templateBody.getChild(0).copy();
var theContent2 = templateBody.getChild(1).copy();
var theContent3 = templateBody.getChild(2).copy();
var theContent4 = templateBody.getChild(3).copy();
var theContent5 = templateBody.getChild(4).copy();
var theContent6 = templateBody.getChild(5).copy();
var theContent7 = templateBody.getChild(6).copy();
var theContent8 = templateBody.getChild(7).copy();
var theContent9 = templateBody.getChild(8).copy();
var theContent10 = templateBody.getChild(9).copy();
var theContent11 = templateBody.getChild(10).copy();
var theContent12 = templateBody.getChild(11).copy();
var theContent13 = templateBody.getChild(12).copy();
var theContent14 = templateBody.getChild(13).copy();
var theContent15 = templateBody.getChild(14).copy();
var theContent16 = templateBody.getChild(15).copy();
var theContent17 = templateBody.getChild(16).copy();
//Clear the target document before creating the new merge
targetBody.clear();
if (!values) {
Logger.log('No data found...');
} else {
for (var row=0; row < values.length; row++) {
var name = values[row][0];
var address = values[row][1];
var city = values[row][2];
var state = values[row][3];
var zip = values[row][4];
//Again, what follows is ridiculous and not an ideal solution
targetBody.appendParagraph(theContent.copy());
targetBody.appendParagraph(theContent2.copy());
targetBody.appendParagraph(theContent3.copy());
targetBody.appendParagraph(theContent4.copy());
targetBody.appendParagraph(theContent5.copy());
targetBody.appendParagraph(theContent6.copy());
targetBody.appendParagraph(theContent7.copy());
targetBody.appendParagraph(theContent8.copy());
targetBody.appendParagraph(theContent9.copy());
targetBody.appendParagraph(theContent10.copy());
targetBody.appendParagraph(theContent11.copy());
targetBody.appendParagraph(theContent12.copy());
targetBody.appendParagraph(theContent13.copy());
targetBody.appendParagraph(theContent14.copy());
targetBody.appendParagraph(theContent15.copy());
targetBody.appendParagraph(theContent16.copy());
targetBody.appendParagraph(theContent17.copy());
//Update the dynamic fields with this row's data
targetBody.replaceText('{{Name}}',name);
targetBody.replaceText('{{Address}}',address);
targetBody.replaceText('{{City}}',city);
targetBody.replaceText('{{ST}}',state);
targetBody.replaceText('{{ZIP}}',zip);
//Insert page break so next iteration begins on new page
targetBody.appendPageBreak();
}
}
}
In the following example I am using a more Javascript approach using String.prototype.replace() to replace the text. I consider the following:
You have a template DOC where you have some strings like these {{Name}}:
You have a spreadsheet where the data to replace the template lives
You want to create a Google Doc for every of the rows
Considering this as true, the example shows this approach:
Grab all the text from the template doc
Replace the text using String.prototype.replace()
Setting the text of the new doc with the replaced one
Code.gs
const templateDocID = "<Template_DOC_ID>"
const dataSsId = "<Data_SS_ID>"
const doC = DocumentApp.openById(templateDocID)
const sS = SpreadsheetApp.openById(dataSsId).getSheets()[0]
function createDocFromTemplate() {
/* Grab the data from the sheets */
const dataToReplace = sS.getRange('A2:E').getValues().filter(n => n[0] !== "")
dataToReplace.forEach((data) => {
let body = doC.getBody().getText()
/* Create a new doc for each row */
const newDocument = DocumentApp.create('New Document')
/* A quick approach to extract the data */
const [name, address, city, state, zip] = data
/* Using string.replace() */
body = body.replace("{{Name}}", name)
body = body.replace('{{Address}}', address)
body = body.replace("{{City}}", city)
body = body.replace("{{ST}}", state)
body = body.replace("{{ZIP}}", zip)
/* Setting the text */
newDocument.getBody().setText(body)
/* Or sending it as an email */
GmailApp.sendEmail('email#gmail.com', 'From Template', body)
Logger.log(newDocument.getUrl())
})
}
This is an example that can help you, but you can adapt it to meet your needs.
Documentation
SpreadsheetApp
GmailApp
Optimize the replace function
I am looking for help from this community regarding the below issue.
// I am searching my Gmail inbox for a specific email
function getWeeklyEmail() {
var emailFilter = 'newer_than:7d AND label:inbox AND "Report: Launchpad filter"';
var threads = GmailApp.search(emailFilter, 0, 5);
var messages=[];
threads.forEach(function(threads)
{
messages.push(threads.getMessages()[0]);
});
return messages;
}
// Trying to parse the HTML table contained within the email
function getParsedMsg() {
var messages = getWeeklyEmail();
var msgbody = messages[0].getBody();
var doc = XmlService.parse(msgbody);
var html = doc.getRootElement();
var tables = doc.getDescendants();
var templ = HtmlService.createTemplateFromFile('Messages1');
templ.tables = [];
return templ.evaluate();
}
The debugger crashes when I try to step over the XmlService.parse function. The msgbody of the email contains both text and HTML formatted table. I am getting the following error: TypeError: Cannot read property 'getBody' of undefined (line 19, file "Code")
If I remove the getParsedMsg function and instead just display the content of the email, I get the email body along with the element tags etc in html format.
Workaround
Hi ! The issue you are experiencing is due to (as you previously mentioned) XmlService only recognising canonical XML rather than HTML. One possible workaround to solve this issue is to search in the string you are obtaining with getBody() for your desired tags.
In your case your main issue is var doc = XmlService.parse(msgbody);. To solve it you could iterate through the whole string looking for the table tags you need using Javascript search method. Here is an example piece of code retrieving an email with a single table:
function getWeeklyEmail() {
var emailFilter = 'newer_than:7d AND label:inbox AND "Report: Launchpad filter"';
var threads = GmailApp.search(emailFilter, 0, 5);
var messages=[];
threads.forEach(function(threads)
{
messages.push(threads.getMessages()[0]);
});
return messages;
}
// Trying to parse the HTML table contained within the email
function getParsedMsg() {
var messages = getWeeklyEmail();
var msgbody = messages[0].getBody();
var indexOrigin = msgbody.search('<table');
var indexEnd = msgbody.search('</table');
// Get what is in between those indexes of the string.
// I am adding 8 as it indexEnd only gets the first index of </table
// i.e the one before <
var Table = msgbody.substring(indexOrigin,indexEnd+8);
Logger.log(Table);
}
If you are looking for more than one table in your message, you can change getParsedMsg to the following:
function getParsedMsg() {
// If you are not sure about how many you would be expecting, use an approximate number
var totalTables = 2;
var messages = getWeeklyEmail();
var msgbody = messages[0].getBody();
var indexOrigin = msgbody.indexOf('<table');
var indexEnd = msgbody.indexOf('</table');
var Table = []
for(i=0;i<totalTables;i++){
// go over each stable and store their strings in elements of an array
var start = msgbody.indexOf('<table', (indexOrigin + i))
var end = msgbody.indexOf('</table', (indexEnd + i))
Table.push(msgbody.substring(start,end+8));
}
Logger.log(Table);
}
This will let you store each table in an element of an array. If you want to use these you would just need to retrieve the elements of this array and use them accordingly (for exaple to use them as HTML tables.
I hope this has helped you. Let me know if you need anything else or if you did not understood something. :)
I am new to Google script and I want to create a script that bring the image name from spreadsheet and insert that image in google docs. My problem is the names of images. In spreadsheet they are stored with the same text in the start 'testui/'. I need to delete it from beginning.
Tried used replaceText('testui/', ''), but got message - TypeError: Cannot find function replaceText in object testui/01-03-2019 11-02-14.Signature.054223.png
function createDocument() {
var headers = Sheets.Spreadsheets.Values.get('1M9biFRsbMtc6zDcAWgOYqJwNbivvVet-ZCD4joI', 'A1:I1');
var tactics = Sheets.Spreadsheets.Values.get('1M9biFRsbMtc6zDcAWgOYqJwNbivvVet-ZCD4joI', 'A2:I3');
var templateId = '1PxbTS67vrK8tqruNqjkqJMcjtGbqzQ';
for(var i = 0; i < tactics.values.length; i++){
var TimeStamp = tactics.values[i][0];
var AktoTipas = tactics.values[i][2];
var Kodas = tactics.values[i][6];
var Signature = tactics.values[i][8];
/*Here I got message TypeError: Cannot find function replaceText in object testui/01-03-2019 11-02-14.Signature.054223.png
Signature.replaceText('testui/', '')
*/
//Make a copy of the template file
var documentId = DriveApp.getFileById(templateId).makeCopy().getId();
//Rename the copied file
DriveApp.getFileById(documentId).setName(AktoTipas + '-' + TimeStamp + '-' + Kodas);
//Get the document body as a variable
var body = DocumentApp.openById(documentId).getBody();
//Insert the supplier name
body.replaceText('##aktas##', AktoTipas)
body.replaceText('##Laikas##', TimeStamp)
body.replaceText('##Signature##', Signature)
insertImageFromDrive(body, Signature);
}
}
function insertImageFromDrive(body, Signature){
var img = DriveApp.getFilesByName(Signature);
while (img.hasNext()) {
var image = img.next();
body.insertImage(3, image);
}
}
If you have same ideas I will be very happy! Thank you very much.
You are on the right track. However, replacetext() is function specific for google document body object.
The values you get from individual cells of the spreadsheet can be classified into various javascript variable as defined here. Yes, I said javascript, google script is based on javascript language and for most function pertaining to manipulating variables you follow javascript syntax and functions.
So, in this case, the Signature is a string variable and you will use string.replace() function of javascript to change or manipulate your string, as documented here
In your specific case you will do the following:
Signature = Signature.replace('testui/', '')
I'm having trouble, replacing text in a Google Doc, using App Script.
Inside the document I have certain tags/tokens like ${TOKEN.SUBTOKEN}. I get the document as text and extract all the TOKEN's as a list with a Regex without issue, but when I want to use the replaceText function, I have issues. When I execute the replaceText line, it doesn't change the value in the document, but returns an element from the document. I can't find a way to replace the text I'm targeting.
var doc = DocumentApp.openById(docId);
var docBody = doc.getBody();
// tokenValues is the Object that contains the values to replace.
var fields = docBody.getText().match(/\$\{[a-z0-9\.\_]+\}/gi);
for (var i = 0; i < fields.length; i++ ) {
fields[i] = fields[i].substring(2, fields[i].length-1);
}
for (var i; i < fields.length; i++) {
Logger.log(docBody.replaceText(new RegExp('\\${ *' + fields[i].replace('/\./g', '\.') + '\ *}', tokenValues[i]));
}
How should I approach this, I'm having trouble with It because the documentation is not that explicit, (or maybe I don't understand it)
I did something similar to your question.
Here's the text:
{{ClientName}} would like to have a {{Product}} {{done/created}}. The purpose of this {{Product}} is to {{ProductPurpose}}. We have experience with such testing and development, and will develop and test the {{Product}} for {{ClientName}}.
Here's the code:
function searchReplace(){
var regex1=new RegExp('{{([a-zA-Z/]+)}}','g');
var tA=[];
var srchA=[];
var fldA=[];
var s=DocumentApp.getActiveDocument().getBody().getText();
while((tA=regex1.exec(s))!==null){//get all fields
fldA.push(tA[1]);
}
for(var i=0;i<fldA.length;i++){//Get unique fields
if(srchA.indexOf(fldA[i])==-1){
srchA.push(fldA[i]);
}
}
var doc=DocumentApp.getActiveDocument();
var body=doc.getBody();
for(var i=0;i<srchA.length;i++){
var searchPattern=Utilities.formatString('\\{\\{(%s)\\}\\}', srchA[i]);//need double backslashes here.
var prompt=Utilities.formatString('Enter Replacement for %s',srchA[i]);
var resp=DocumentApp.getUi().prompt('Replacement Text',prompt , DocumentApp.getUi().ButtonSet.OK_CANCEL)
if(resp.getSelectedButton()==DocumentApp.getUi().Button.OK){
body.replaceText(searchPattern, resp.getResponseText());//replaces all instances of the field
}
}
}