How overwrite docs with scripts in google? - google-apps-script

I make one script to auto fill a Google Doc from a Google Form Submissiom. Now I need another script to overwrite the document when there is a change in some spreadsheet value . (the user cannot edit the form's response but can change values ​​in the spreadsheet).
enter image description here

As long as you know what paragraph it is that needs to be edited you can try this.
Before:
function myFunction() {
try {
var doc = DocumentApp.getActiveDocument();
var body = doc.getBody();
var paras = body.getParagraphs();
var i = 0;
for( i=0; i<paras.length; i+=2 ) {
paras[i].setText("Line "+(100+i));
}
}
catch(err) {
console.log(err);
}
}
After:

Related

Copy Document Title Using Google App Script

I want to copy the text from the title within this document, but I am only used to using Google App Script in sheets, not docs:
I have tried lot of different methods, but nothing is populating in Logger.log(), so I am not sure it is working. Currently, all I want to return is the text in the Title, so I can go on to search for this in a spreadsheet. So far I have the following:
function autoFill() {
var doc = DocumentApp.getActiveDocument();
var body = doc.getBody();
var searchResult = body.findElement(DocumentApp.ElementType.PARAGRAPH);
var searchHeading = searchResult.getElement(DocumentApp.ParagraphHeading.TITLE);
The only part of the document that has the format of Title is the first line in the document.
Here is an example of how to find the paragraph with the Heading style Title. In my Doc I have a line of text "This is some text" with style Title.
function findTitle() {
try {
let doc = DocumentApp.getActiveDocument();
let body = doc.getBody();
let paras = body.getParagraphs();
let text = null;
paras.some( para => {
let attribs = para.getAttributes();
if( attribs["HEADING"] === DocumentApp.ParagraphHeading.TITLE ) {
text = para.getText();
return true;
}
return false;
}
);
console.log(text);
}
catch(err) {
console.log(err);
}
}
Execution log
8:08:39 AM Notice Execution started
8:08:39 AM Info This is some text
8:08:39 AM Notice Execution completed
Found that using Regular Expression would be able to identify the text needed:
function autoFill() {
var ss = DocumentApp.getActiveDocument();
var body = ss.getBody();
var header = DocumentApp.getActiveDocument().getHeader();
var contents = body.getText();
var racm = SpreadsheetApp.openById('[enter sheet ID').getSheetByName('Risks & Controls matrix');
var lr = racm.getLastRow();
var colID = racm.getRange(1,1,lr,1).getValues();
//you need to tweak below to the control name pattern
var regexName = /\w+[-]\w+[-]\w+/ //use website: regexr.com to create & www.geeksforgeeks.org/write-regular-expressions/
var titleFound=regexName.exec(contents)[0];
let row = colID.findIndex(users => {return users[0] == titleFound})+1;
Then goes on to finding the relevant data in the ranges from the spreadsheet and .replaceText() within the doc.
Works really well. Thanks for your help!

How to delete selected text in a Google doc using Google Apps Script

In a Google document is there a way to delete selected text with Google Apps Script? The find criterion for the text to delete is not a string, but instead is relative to a bookmark. This question is related to a workaround for my open question at https://webapps.stackexchange.com/questions/166391/how-to-move-cursor-to-a-named-bookmark-using-google-apps-script).
Here is code I wish worked.
function UpdateBookmarkedText() {
var doc = DocumentApp.getActiveDocument();
var bookmarks = doc.getBookmarks();
for (var i = 0; i < bookmarks.length; i++){
// Delete the old text, one step in a longer process.
var text = bookmarks[i].getPosition().getElement().asText().editAsText();
var range = doc.newRange().addElementsBetween(text, 5, text, 7).build(); // arbitrary offsets for testing
doc.setSelection(range); // The selected range is successfully highlighted in the document.
doc.deleteSelection(); // This command does not exist.
} }
This documentation seems relevant but is over my head: https://developers.google.com/docs/api/how-tos/move-text
Use deleteText()
You may use the following script as the basis for your script:
function deleteSelectedText() {
var selection = DocumentApp.getActiveDocument().getSelection();
if (selection) {
var elements = selection.getRangeElements();
if (elements[0].getElement().editAsText) {
var text = elements[0].getElement().editAsText();
if (elements[0].isPartial()) {
text.deleteText(elements[0].getStartOffset(), elements[0].getEndOffsetInclusive());
}
}
}
}
This is a modified version of the script featured in the Class Range guide. This modification works for selected sentences within a paragraph. Thus, the use of the for loop (in the sample script) is not anymore necessary since the script operates within a single element/paragraph.
Optimized Script:
function test() {
var selection = DocumentApp.getActiveDocument().getSelection();
var elements = selection.getRangeElements();
var text = elements[0].getElement().editAsText();
(selection && elements[0].getElement().editAsText && elements[0].isPartial()) ? text.deleteText(elements[0].getStartOffset(), elements[0].getEndOffsetInclusive()):null;
}
References:
Class Range
deleteText(startOffset, endOffsetInclusive)
I'm not sure what exactly you're trying to do, so here is a guess. If you able to select something you can remove the selected text about this way:
function UpdateBookmarkedText() {
var doc = DocumentApp.getActiveDocument();
var bookmarks = doc.getBookmarks();
for (var i = 0; i < bookmarks.length; i++){
// Delete the old text, one step in a longer process.
var text = bookmarks[i].getPosition().getElement().asText().editAsText();
var range = doc.newRange().addElementsBetween(text, 5, text, 7).build(); // arbitrary offsets for testing
doc.setSelection(range); // The selected range is successfully highlighted in the document.
// the way to handle a selection
// from the official documentation
// https://developers.google.com/apps-script/reference/document/range
var selection = DocumentApp.getActiveDocument().getSelection();
if (selection) {
var elements = selection.getRangeElements();
for (let element of elements) {
if (element.getElement().editAsText) {
var text = element.getElement().editAsText();
if (element.isPartial()) {
text.deleteText(element.getStartOffset(), element.getEndOffsetInclusive());
} else {
text.setText(''); // not sure about this line
}
}
}
}
}
}

Google apps script - Bookmark a selected image

How can I addBookmark() a selected image from Goole docs.
In your situation, how about the following sample script?
Sample script:
function myFunction() {
const doc = DocumentApp.getActiveDocument();
const selection = doc.getSelection();
if (!selection) return;
selection.getRangeElements().forEach(e => {
const ele = e.getElement();
const type = ele.getType();
if (type == DocumentApp.ElementType.TEXT) {
doc.addBookmark(doc.newPosition(ele, e.getStartOffset()));
} else if (type == DocumentApp.ElementType.INLINE_IMAGE) {
doc.addBookmark(doc.newPosition(ele.asInlineImage().getParent(), 1));
}
});
}
When you use this script, please select an inline image on Google Drive and run the script. By this, the bookmark is added to the inline image. And, when you select a text, the bookmark is added to the text.
Reference:
asInlineImage()

Need to copy Cell Color and Send in EMAIL Body

My Sheet cell colors are changed automatically based on results values. Need to Send Send this data range in email Body... Code is running good, but do not pick colors of cells as displayed in below image. Help me set the code to copy and send body email as the data shown below.
Actual Sheet Display:
Here is the script code copying data from Google Sheet and Sending to email Body (using an html template) you can view here.
function getEmailHtml(stockData) {
var htmlTemplate = HtmlService.createTemplateFromFile("Template.html");
htmlTemplate.stocks = stockData;
var htmlBody = htmlTemplate.evaluate().getContent();
return htmlBody;
}
function sendEmail() {
var stockData = getData();
var body = getEmailText(stockData);
var htmlBody = getEmailHtml(stockData);
MailApp.sendEmail({
to: "email#email.com",
subject: "Operations - Today's Job Details",
body: body,
htmlBody: htmlBody
});
}
function getEmailText(stockData) {
var text = "";
stockData.forEach(function(stock) {
text = text + stock.name + "\n" + stock.ticker + "\n" + stock.price + "\n-----------------------\n\n";
});
return text;
}
/**
* #OnlyCurrentDoc
*/
function getData() {
var values = SpreadsheetApp.getActive().getSheetByName("Email").getRange("Stocks").getDisplayValues();
values.shift(); //remove headers
var stocks = [];
values.forEach(function(value) {
var stock = {};
stock.sr = value[0];
stock.job = value[1];
stock.work = value[2];
stock.worked = value[3];
stock.time = value[4];
stocks.push(stock);
})
//Logger.log(JSON.stringify(stocks));
return stocks;
}
I believe your goal and your current situation as follows.
You want to set the background color of the rows of Spreadsheet to the rows of HTML table.
In your Spreadsheet, each row has one background color.
In this case, I would like to propose to retrieve the background colors of each row of the Spreadsheet, and those are set to the value of style of tr using the template. When your script is modified, it becomes as follows.
Modified script:
Please modify the function of getEmailHtml of Google Apps Script as follows.
function getEmailHtml(stockData) {
var range = SpreadsheetApp.getActive().getSheetByName("Email").getRange("Stocks");
var colors = range.getBackgrounds().map(([a]) => a);
colors.shift();
var htmlTemplate = HtmlService.createTemplateFromFile("Template.html");
htmlTemplate.stocks = stockData;
htmlTemplate.colors = colors;
var htmlBody = htmlTemplate.evaluate().getContent();
return htmlBody;
}
And also, please modify your HTML template as follows.
From:
<? for(var i = 0; i < stocks.length; i++) { ?>
<tr style="height:21px">
To:
<? for(var i = 0; i < stocks.length; i++) { ?>
<tr style="height:21px;background-color:<?= colors[i] ?>;">
Note:
In this modification, the background colors are retrieved from the 1st column of your range of SpreadsheetApp.getActive().getSheetByName("Email").getRange("Stocks"). When you want to change this, please modify above script.
Reference:
getBackgrounds()

how to append sheet before next function run

I'm appending a row in a spreadsheet from a form then taking the data that was added to the spreadsheet and populating a document template. From there I'm creating a PDF and emailing it to myself. The problem I'm facing is that the data is always coming from the second to last row of the spreadsheet instead of the newly appended row (from the latest form data). It seems like the appended data is not being saved before the AutoFillDocFromTemplate function runs. What am I missing?
function doGet(request) {
return HtmlService.createTemplateFromFile('Index').evaluate();
};
/* #Include JavaScript and CSS Files */
function include(filename) {
return HtmlService.createHtmlOutputFromFile(filename)
.getContent();
}
/* #Process Form */
function processForm(formObject) {
var url = "https://docs.google.com/spreadsheets/d/1nz2uIWab1eSirljzvNn6SyNyxz3npDTu4mqVYV0blsU/edit#gid=0";
var ss = SpreadsheetApp.openByUrl(url);
var ws = ss.getSheetByName("Data");
ws.appendRow([formObject.company_name,
formObject.identity_transformation,
formObject.character_want,
formObject.external_problem,
formObject.internal_problem,
formObject.philisophical_problem,
formObject.empathy,
formObject.authority,
formObject.plan_step1,
formObject.plan_step2,
formObject.plan_step3,
formObject.direct_cta,
formObject.transitional_cta,
formObject.failure,
formObject.success]);
}
/* This function creates a new document from a template and updates the placeholder with info from a Google Sheet*/
function AutofillDocFromTemplate(){
// Get the spreadsheet & sheet
var url = "https://docs.google.com/spreadsheets/d/1nz2uIWab1eSirljzvNn6SyNyxz3npDTu4mqVYV0blsU/edit#gid=0";
var ss = SpreadsheetApp.openByUrl(url).getSheetByName("Data");
// Set the range to the last row of data in the Sheet
var data = ss.getRange(ss.getLastRow(),1,1, ss.getLastColumn()).getValues();
// Get the original template Doc
const templateDoc = DriveApp.getFileById("1yu5jzg4NbRtTy_UjwzBmnpc-3_pNOqA-l1_UVsiAIWQ");
// Get the folder for where the docs should go
const folder = DriveApp.getFolderById("1prOQxp5jmDvJqiwIfLbbkLYWoz5QlTUC");
// Create the new file name
const newFileName = ("BrandScript")
// Create a copy of the template doc
const newTempFile = templateDoc.makeCopy(newFileName, folder);
// Open the new temp doc
const openDoc = DocumentApp.openById(newTempFile.getId());
// Get the body of the new temp doc
const body = openDoc.getBody();
// Replace placeholders with spreadsheet data from last row
body.replaceText("%company_name%", data[0][0]);
body.replaceText("%identity_transformation%", data[0][1]);
body.replaceText("%character_want%", data[0][2]);
body.replaceText("%external_problem%", data[0][3]);
body.replaceText("%internal_problem%", data[0][4]);
body.replaceText("%philisophical_problem%", data[0][5]);
body.replaceText("%empathy%", data[0][6]);
body.replaceText("%authority%", data[0][7]);
body.replaceText("%plan_step1%", data[0][8]);
body.replaceText("%plan_step2%", data[0][9]);
body.replaceText("%plan_step3%", data[0][10]);
body.replaceText("%direct_cta%", data[0][11]);
body.replaceText("%transitional_cta%", data[0][12]);
body.replaceText("%failure%", data[0][13]);
body.replaceText("%success%", data[0][14]);
// Save and close the new doc
openDoc.saveAndClose();
//Send email with new document
var message = "Attached is your draft BrandScript"; // Customize message
var emailTo = "to be inserted" // replace with your email
var subject = "Your Draft BrandScript"; // customize subject
var pdf = DriveApp.getFileById(openDoc.getId()).getAs('application/pdf').getBytes();
var attach = {fileName:'DraftBrandScript.pdf',content:pdf, mimeType:'application/pdf'}; // customize file name: "Autogenerated template"
MailApp.sendEmail(emailTo, subject, message, {attachments:[attach]});
}
<script>
// Prevent forms from submitting.
function preventFormSubmit() {
var forms = document.querySelectorAll('form');
for (var i = 0; i < forms.length; i++) {
forms[i].addEventListener('submit', function(event) {
event.preventDefault();
});
}
}
window.addEventListener('load', preventFormSubmit);
function handleFormSubmit(formObject) {
google.script.run.processForm(formObject);
google.script.run.AutofillDocFromTemplate();
document.getElementById("myForm").reset();
}
</script>
I think that the reason of your issue is google.script.run works with the asynchronous process. By this, at the following script,
google.script.run.processForm(formObject);
google.script.run.AutofillDocFromTemplate();
Before processForm is not finished, AutofillDocFromTemplate is run. So in order to remove your issue, I would like to propose the following patterns.
Pattern 1:
In this pattern, withSuccessHandler is used. By this, AutofillDocFromTemplate is run after processForm was run.
From:
google.script.run.processForm(formObject);
google.script.run.AutofillDocFromTemplate();
To:
google.script.run.withSuccessHandler(() => google.script.run.AutofillDocFromTemplate()).processForm(formObject);
Pattern 2:
In this pattern, Google Apps Script is modified. By this, AutofillDocFromTemplate is run after processForm was run.
Google Apps Script side:
From:
function processForm(formObject) {
var url = "https://docs.google.com/spreadsheets/d/1nz2uIWab1eSirljzvNn6SyNyxz3npDTu4mqVYV0blsU/edit#gid=0";
var ss = SpreadsheetApp.openByUrl(url);
var ws = ss.getSheetByName("Data");
ws.appendRow([formObject.company_name,
formObject.identity_transformation,
formObject.character_want,
formObject.external_problem,
formObject.internal_problem,
formObject.philisophical_problem,
formObject.empathy,
formObject.authority,
formObject.plan_step1,
formObject.plan_step2,
formObject.plan_step3,
formObject.direct_cta,
formObject.transitional_cta,
formObject.failure,
formObject.success]);
AutofillDocFromTemplate() // <--- Added
}
HTML&Javascript side:
google.script.run.processForm(formObject);
// google.script.run.AutofillDocFromTemplate(); // <--- removed
Note:
If the issue was not resolved by above modifications, please try to use SpreadsheetApp.flush().
Reference:
Class google.script.run