I'm looking to get the contents of a Google word document and put it in a textBox. The following code is spitting out an error:
function listBoxClick(e) {
var tapp = UiApp.getActiveApplication();
var docName = DocsList.find(e.parameter.doclist); //find the document with the same name as what the user clicked on
var docContents = docName[0].getContentAsString(); //get contents of document
tapp.getElementById("songboxID").setValue(songfile2); //set the value of the textBox to the contents of the document
return tapp;
}
This returns the following error:
Unsupported conversion requested.
I read somewhere that we can't do this for Google Documents but we can for other non-google documents that we upload. Is that right?
Here's the answer that I can't post for 5 more hours since I'm new and have no reputation:
With Serge's assistance, here's what worked for me:
function listBoxClick(e) {
var tapp = UiApp.getActiveApplication();
var docName = DocsList.find(e.parameter.doclist); //get document name based on what user clicked on in listBox
var docId = docName[0].getId(); //get document ID
var content = DocumentApp.openById(docId).getText(); //get contents of document
tapp.getElementById("songboxID").setValue(content); //set web app's textBox (called songboxID) to match the contents of the document the user clicked on
return tapp;
}
You have to use DocumentApp.getText(); to get the text content of your document.
in you code it would become :
function listBoxClick(e) {
var tapp = UiApp.getActiveApplication();
Logger.log(e.parameter.doclist)
var docName = DocsList.find(e.parameter.doclist); //find the document with the same name as what the user clicked on
var docId = docName[0].getId();
Logger.log(docName[0].getName()+' = '+docId)
var textContent = DocumentApp.openById(docId).getText();
Logger.log(textContent)
tapp.getElementById("songboxID").setValue(textContent); //set the value of the textBox to the contents of the document
return tapp;
}
EDIT : as you noticed with your experiments DocsList.find(e.parameter.doclist) returns results that you didn't expect... that's simply because the find operator searches not only on doc's names but also on docs content.
To solve that , you should add a smal routine that checks the results of the find query against the documents name, something like this :
var docName = DocsList.find(e.parameter.doclist); //find the document with the same name as what the user clicked on
for(var d in docName){{
if(docName[d].getName()==e.parameter.doclist){
var docId = docName[d].getId();
}
}
This will ensure that the document you are looking for is actually the right one.
PS: sorry for not having mentioned that immediately... it just slipped out of my mind ;-)
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 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 want Apps Script to automatically generate a new set of Slides using data from a Sheets document which has rows of the different information I want inserted into a Slides template replacing the placeholder tags. I want it to do it instantly for each row inside the table with one action, so if there are 10 rows, 10 sets of Slides documents will be generated.
The text replacement works, however I'm not sure how to replace, for example, a shape with "{{image}}" written inside with the image using the URL under the image column. Same goes for charts.
function generateNewSlides() {
var wsID = "worksheet ID here";
var ws = SpreadsheetApp.openById(wsID).getSheetByName("Data");
var data = ws.getRange(2, 1, ws.getLastRow()-1, 6).getValues();
// the above should get the relevant table from the sheet
data.forEach(function(info){
if(info[0]){
var firstname = info[0];
var surname = info[1];
var email = info[2];
var phone = info[3];
var image = info[4];
var presName = info[5];
// the above are columns where the different pieces of data would be taken from for the placeholders in the Slides template
var slidesTemplateID = "slides template ID here";
var template = DriveApp.getFileById(slidesTemplateID);
var folderID = "folder where itll be saved ID here";
var copiedTemplate = template.makeCopy(presName, DriveApp.getFolderById(folderID));
var Presentation = SlidesApp.openById(copiedTemplate.getId());
// the above should create a copy and then open it
Presentation.getSlides().forEach(function(slide) {
slide.getShapes().forEach(function(shape) {
shape.getText().replaceAllText("{{firstname}}",firstname);
shape.getText().replaceAllText("{{surname}}",surname);
shape.getText().replaceAllText("{{email}}",email);
shape.getText().replaceAllText("{{phone}}",phone);
shape.getText().replaceAllText("{{presname}}",presName);
})
// replaces the placeholder tags with the desired text
// I am not sure how to insert something similar for images and charts in the code here
// I've tried variations of the below, none of which have worked
// slide.getShapes().forEach(function(picture) {
// picture.find("{{image}}").replace(image);
// picture.findText("{{image}}").replace(image);
// picture.getText("{{image}}").replaceWithImage(image);
// picture.getText().findText("{{image}}").replace(image);
});
};
});
}
The difference between {{image}} and the other placeholder is that you want to replace the text through an actual image
Unfortunately you cannot paste an image inside of a text box.
Instead, you can specify that if an {{image}} placeholder is present you want to paste an image into the slide that contains the text box.
You can check for existence of the placeholder with
var imageText = shape.getText().replaceAllText("{{image}}",""); if(imageText == true){...}
You insert the image if the condition is fulfilled and specify its size and position, e.g.
slide.insertImage(image).scaleHeight(0.5).scaleWidth(0.5).setLeft(10);
Important: To insert the image in Slides you need the webContentLink instead of the Url and the image must be publibly accesible (see here).
If you do not know the webContentLink of your image, I recommend you to replace the URL in your spreadsheet with the file Id and modify your code to
var id = info[4]; var image = Drive.Files.get(id).webContentLink
Mind that the webContentLink cannot be accessed by DriveApp, but only by the Advanced Drive Service which you need to manually enable.
Working sample:
function generateNewSlides() {
var wsID = "worksheet ID here";
var ws = SpreadsheetApp.openById("xxx").getSheetByName("Data");
var data = ws.getRange(2, 1, ws.getLastRow()-1, 6).getValues();
// the above should get the relevant table from the sheet
data.forEach(function(info){
if(info[0]){
var firstname = info[0];
var surname = info[1];
var email = info[2];
var phone = info[3];
var id = info[4];
var image = Drive.Files.get(id).webContentLink
var presName = info[5];
// the above are columns where the different pieces of data would be taken from for the placeholders in the Slides template
var slidesTemplateID = "xxx";
var template = DriveApp.getFileById(slidesTemplateID);
var folderID = "folder where itll be saved ID here";
var copiedTemplate = template.makeCopy(presName, DriveApp.getFolderById(folderID));
var Presentation = SlidesApp.openById(copiedTemplate.getId());
// the above should create a copy and then open it
Presentation.getSlides().forEach(function(slide) {
slide.getShapes().forEach(function(shape) {
shape.getText().replaceAllText("{{firstname}}",firstname);
shape.getText().replaceAllText("{{surname}}",surname);
shape.getText().replaceAllText("{{email}}",email);
shape.getText().replaceAllText("{{phone}}",phone);
shape.getText().replaceAllText("{{presname}}",presName);
var imageText = shape.getText().replaceAllText("{{image}}","");
if(imageText == true){
slide.insertImage(image).scaleHeight(0.5).scaleWidth(0.5).setLeft(10);
}
})
});
};
});
}
I have a script that is published as a service for a web app and i'm trying to build search functionality in it. the scipt is intended to allow the user to enter a term in a text box called searchBox, click search and show the row(s) of a spreadsheet containing that term.
function searchClickHandler(e) {
var app = UiApp.getActiveApplication();
var searchResultPanel = app.getElementById('searchResultPanel');
var searchResultPane = app.getElementById('searchResultPane');
var searchCloseButton = app.getElementById('searchCloseButton');
var searchTerm = e.parameter.searchBox;
if (searchTerm.toString().length == 6) {
var searchList = ArrayLib.filterByText(dataValues, 1, searchTerm.toString());
var searchTrim = new Array();
for (var i = 0; i < searchList.length; i++) {
var searchTrim1 = searchList[i].slice(1, 6);
var searchTrim2 = searchList[i].slice(8, 10);
var searchTrim3 = searchList[i].slice(17, 19);
searchTrim.push(searchTrim1.concat(searchTrim2,searchTrim3));
}
}
there are a few other else if's below that and then the handler should show the results but e.parameter.searchBox is coming back undefined. if i manually set searchTerm the script runs fine.
i am using e.parameter successfully in another handler in the same script so i am at a loss on this one.
thanks in advance.
The code you show is not really relevant to help... did you give a name to the "searchBox" when you created it or is it its ID ?
The e.parameter uses the name of the widget as reference.
EDIT (following comments): in the GUI builder try to add every callbackElement by their names (for widgets or ID for panels) separated by commas like this :
Question 1) When a button is clicked is it possible to use something like this (see code below)?
function Submit(e) {
var app = UiApp.getActiveApplication();
var checked = app.getElementById("checkbox").getValue();
}
Question 2) When a label is clicked is it possible to use something like this (see code below)?
function LabelClick(e) {
var LabelText = e.parameter.getText();
}
Sorry, this probably stupid, but I can't see to find any decent examples of this and can't seem to work this out from Google's documentation and I'm just getting used to google script too. If you have the answer I would really appreciate it.
you are not very far... but not close enough to get it working...
Ui element's value is sent to the handler function in a so called callbackelement that is added to the handler. This callbackelement may be a button, a label or, more easily, the parent widget that contains all the other widgets. These "elements" are in the "e" of the handler function and are identified by their names.
In the other direction, ie if you need to modify an Ui element from another function then you can get this element by its ID (getElementbyId()) and assign it a value just the same way as you'd do it in the UI definition function.
I copy/paste a sample code from another post to illustrate what I said, you can see the e.parameter.chkmode that holds the value of the checkBox and I'll add a Label to show the reverse process (the text is changed when the button is clicked).
Hoping I was clear enough,
var sh = SpreadsheetApp.getActiveSheet();
var ss = SpreadsheetApp.getActiveSpreadsheet();
//
function move() {
var app = UiApp.createApplication().setTitle("move test")
.setHeight(100).setWidth(400).setStyleAttribute("background-color","beige");
var panel = app.createVerticalPanel();
var next = app.createButton('next').setWidth('180');
var chkmode = app.createCheckBox("moving mode (checked = up/dwn, unchecked=L/R)").setValue(false).setName('chkmode');
var label = app.createLabel("test Label with text that will be modified on click").setId('label');
panel.add(next).add(chkmode).add(label);
var handler = app.createServerHandler('click').addCallbackElement(panel);
next.addClickHandler(handler);
app.add(panel);
ss.show(app);
}
//
function click(e) {
var app = UiApp.getActiveApplication();
var activeline = sh.getActiveRange().getRow();// get the row number of the selected cell/range
var activecol = sh.getActiveRange().getColumn();// get the row number of the selected cell/range
var label = app.getElementById('label');
label.setText('You have clicked the button');
var chkmode=e.parameter.chkmode;
if(chkmode=="true"){
activeline++
}else{
activecol++}
var sel=sh.getRange(activeline,activecol);
sh.setActiveSelection(sel);// make the next row active
return app;
}
For a UI and events tutorial, I recommend:
https://developers.google.com/apps-script/uiapp