Set height of Google Apps Script showModalDialog when using an HtmlService HtmlTemplate - google-apps-script

I'm currently in the process of changing over my Google Apps Scripts that use the deprecated UI service to the HtmlService.
I've created a modal dialogue using the following code (in a spreadsheet container-bound script):
var htmlTemplate = HtmlService.createTemplateFromFile('testDialogue');
htmlTemplate = template.evaluate().setSandboxMode(HtmlService.SandboxMode.IFRAME);
SpreadsheetApp.getUi().showModalDialog(htmlTemplate, 'Test Dialogue');
The dialogue box opens, but I need to modify its dimensions.
HtmlOutput objects have a setHeight method, but there doesn't seem to be the same method available for HtmlTemplate objects.
I tried using the method anyway on the object like this:
var htmlTemplate = HtmlService.createTemplateFromFile('testDialogue').setHeight(300);
But that produces this error:
TypeError: Cannot find function setHeight in object HtmlTemplate
Also, I checked the SpreadsheetApp Ui Class and showModalDialog method but neither of them seem to have methods for setting the height of HtmlTemplate objects.

The .setHeight() method can be used when chaining it after the .evaulate() method, like so:
template = template.evaluate()
.setSandboxMode(HtmlService.SandboxMode.IFRAME)
.setHeight(300);
Update 2/19/19: The .setSandboxMode() method no longer has any effect - now all scripts now use IFRAME mode regardless of what sandbox mode is set (documentation). That method was not related to setting the height but I figured I'd mention this in case anyone ends up copying and pasting this code sample.

Related

How to determine why an alert dialog was displayed by Chrome?

I am trying to fix a bug in a Chrome extension. When the extension is installed an alert dialog containing the message "undefined" will be displayed seemingly at random. This does not happen when the extension is not installed.
There is not one call to alert, confirm, or prompt in the extension source code. How do I find out why the alert dialog is being displayed?
I have attempted adding the following code to one of the background scripts and to one of the content scripts.
var originalWindowAlert = window.alert;
window.alert = function() {
console.trace();
return originalWindowAlert.apply(window, arguments);
}
I have confirmed that this technique works when used in a webpage, but it is not working for the extension.
I have also built Chromium from source code and I am able to reproduce it but so far I have not been able to figure out how to determine the origin of the alert dialog. I have set a breakpoint in the RenderFrameHostImpl::RunModalAlertDialog function but I see no way to determine what caused the breakpoint to be hit.
I am getting desperate.
I asked this question on the Chromium Extensions Google Group. I got the following very useful response from Scott Fortmann-Roe.
If you do the following in a content script:
var originalWindowAlert = window.alert;
window.alert = function() {
console.trace();
return originalWindowAlert.apply(window, arguments);
}
I don't believe it will actually intercept alerts triggered by the page as you are overriding the content script's window.alert method which is different from the page's method (content script JS is isolated from page JS).
To modify the page's alert method you'll probably need to inject a script tag into the page. E.g. something along these lines in the content script:
let script = document.createElement('script');
script.textContent = `
var originalWindowAlert = window.alert;
window.alert = function() {
console.trace()
return originalWindowAlert.apply(window, arguments);
} `;
document.body.appendChild(script);

Autocomplete in google apps script editor not always aware of type/context

I'm getting started with Google Apps scripting, and find the autocomplete very useful. However, once you are inside a new function, autocomplete doesn't seem to have any way of knowing what the type is for the parameter. I've seen some answers about python ideas that say that using javadoc will work. But I'm not able to figure it out. Any suggestions?
function myfunc1(){
var activeSheet=SpreadsheetApp.getActiveSheet();
activeSheet//.autocomplete works here
myfunc2(activeSheet)
}
function myfunc2(myActiveSheet){
myActiveSheet//.autocomplete doesn't work here
}
There are limitations to what the UI can do in terms of autocomplete.
Usually I just keep the reference documentation open in another tab and refer to that, but you can also trick the UI into auto completing using comments:
function myfunc2(myActiveSheet){
/*
var myActiveSheet = SpreadsheetApp.getActiveSheet()
*/
myActiveSheet //.autocomplete now works here
}
The new editor uses JSDoc for parameter types. So declare the parameter in the docs, and specify its type between the braces {}.
/**
* #param {SpreadsheetApp.Sheet} sheet
*/
function myfunc(sheet) {
sheet //.autocomplete now works here
}

What's the difference between ui.alert and Browser.msgBox?

When working with Google Scripting, there's a Browser.msgBox(); (Link) and ui.alert(); (Link). What is the difference between the two? They appear to do the exact same thing.
There are more methods within, such as Browser.inputBox(); and ui.prompt(); which again, appear to be identical.
The Browser Class is only available to a Spreadsheet. The Ui Class can be more widely used. Unfortunately, the documentation for Class Ui only shows an example of the getUi() method with the SpreadsheetApp Class. But getUi() is available to DocumentApp.
DocumentApp.getUi()
And to:
FormApp.getUi()
If you try to call Browser.msgBox() from the wrong context, you'll get an error:
Cannot call Browser.msgBox() from this context; have you tried Logger.log() instead?
Browser.msgBox() is easier to use in a Spreadsheet script. You don't need to first use var ui = SpreadsheetApp.getUi();
To compare:
Browser.msgBox('prompt here');
SpreadsheetApp.getUi().prompt('prompt here');

Is there a way to remove a script from a doc (using the new doc embedded script)

I developed a script extension that uses a Google doc as template AND as script holder.
It gives me a very nice environment to implement a mail merge application (see below).
At some point I use the DocsList class makeCopy(new Name) to generate all the docs that will be modified and sent. It goes simply like that :
var docId=docById.makeCopy('doc_'+Utilities.formatString("%03d",d)).getId();
Everything works quite nicely but (of course) each copy of the template doc contains a copy of the script which is obviously not necessary ! It is also a bit annoying since each time I open a copy to check if data are right I get the sidebar menu that opens automatically which is a time consuming process ...
My question is (are) :
is there any way to remove the embedded script from the copy ? (that would be simple)
or should I copy all the doc elements from the template to an empty document ? (which is also a possible way to go but I didn't try and I don't know what will be in this doc in real life use...
Shall I get a perfect clone in any case ?)
I've read the doc and didn't find any relevant clue but who knows ? maybe I missed something obvious ;-)
below is a reduced screen capture to show the context of this question :
Following Henrique's suggestion I used a workaround that prevents the UI to load on newly created documents... (thanks Henrique, that was smart ;-)
The function that is called by onOpen now goes like that :
function showFields() {
var doc = DocumentApp.getActiveDocument();
var body = doc.getBody();
var find = body.findText('#'); // the new docs have no field markers anymore.
if(find != null){ // show the UI only if markers are present in the document.
var html = HtmlService.createHtmlOutputFromFile('index')
.setTitle("Outils de l'option Publipostage").setWidth(370);
ui.showSidebar(html);
}
}

Global Variables in Chrome Extensions

Is there a simple way where I can access a global javascript variable through content-scripts in chrome extensions?
Accessing global object from content script in chrome extension
I followed the steps mentioned in the above link, but it did not work out for me. Any help would be much appreciated.
Thanks,
Shankar
I managed to complete it. Thanks for the help. I used simple message passing to retrieve the value from the extension script to the content script. The place where I had missed was, the listener at the extension script needs to be at the background page (I think so). Once I changed that, it worked.
For those from the future looking for an answer to this question, here's how I do it:
function getVariable(v) {
var c = document.createElement("div");
c.id = 'var-data';
c.style.display = 'none';
document.body.appendChild(c);
var s = document.createElement('script');
s.innerHTML = 'document.getElementById("var-data").innerText=JSON.stringify('+v+');';
document.head.appendChild(s);
var data = JSON.parse(c.innerText);
c.remove();
s.remove();
return data;
}
And basic usage:
getVariable('globalVarIWantToAccess');
All this script goes in the content-script, not the code for the main webpage, which means that no co-operation is needed from the webpage itself. Basically, the getVariable function creates a script element which is injected into the main page. This script tag retrieves the requested global variable and puts the data into a new div. The function then gets this data from the new div, deletes the new div, deletes the new script element and returns the data.