I'm basically doing a mail-merge (replacing Total: $ {total_amount} for Total: $ 20.00) in a Google Docs document with Google App Script.
However, I want a preview option, where I show a modal dialog or something, and then I append to it a copy of the Body of the actual document. That way I can replace all the variables and keeping the original format (bold, italics, etc).
I already have an implementation that loads the current document as HTML exported and appends it to the dialog.
html = getGoogleDocumentAsHTML();
replaced = replace(html);
output = HtmlService.createHtmlOutput(replaced)
.setSandboxMode(HtmlService.SandboxMode.IFRAME)
.setWidth(700)
.setHeight(500);
DocumentApp.getUi().showModalDialog(output, 'Preview');
/*
* #see http://stackoverflow.com/questions/14663852/get-google-document-as-html#answer-28503601
*/
function getGoogleDocumentAsHTML(){ ...
But with this approach I am unable to show the pages, so that the user knows the paragraphs that fits on each page, layout, etc.
Is there any way to get a copy of the Document's Body to do this, or a similar approach?
1) Create a copy of the active document.
2) Make changes to this copy of the document.
3) Embed the changed document in a dialog box.
4) After user is done previewing the new document, delete the modified copy.
You can display a document in a dialog box using an iframe. Dialog sizes of course can be changed from the example below. The the important part is https://docs.google.com/document/d/{id}/preview?authuser=0. I tacked the authuser=0 to give the hint to use the current users credentials. This isn't necessary, but it can prevent the account chooser box for popping up.
code.gs
function renderDocument(docId){
var html = HtmlService.createTemplateFromFile('dialog');
html.ID = id
var ui = DocumentApp.getUi();
ui.showModalDialog(html.evaluate().setWidth(800).setHeight(910), "html")
}
dialog.html
<!DOCTYPE html>
<html>
<head>
<base target="_top">
</head>
<body>
<iframe frameborder="0" style="width:90%;height:900px" allowfullscreen="" src="https://docs.google.com/document/d/<?=ID?>/preview?authuser=0"></iframe>
</body>
</html>
Related
I'm using a basic Wordpress (or Google Sites) page to randomly redirect to one of three URLs. I'm not sure how to do this in HTML. The idea is to have a big button in the middle of the page which, on click, will send you to one of three links randomly. Any advice would be appreciated.
You couldn't do that only with html, you will need to do that with javascript.
You must store the links in an array and on click on the button call a function that randomly choose a link and go to it.
In the example below I only display the chosen link so you can click how many time you want to see the link change. Remove the line that do that and uncomment the line that make the redirection work.
var locations = ['https://google.com', 'https://stackoverflow.com', 'https://bing.com'];
document.getElementById("redirectButton").onclick = function () {
var link = locations[Math.floor(Math.random() * locations.length)];
// Remove this line
document.querySelector('.link').innerText = link;
// Uncomment this line
// location.href = link;
};
<button id="redirectButton">Random redirection</button>
<!-- You don't need that --> <p class="link"></p> <!-- Remove it on your site-->
I have created a script within Google Scripts Editor so that when a user clicks on a menu on the toolbar in Google Sheets it brings up an application box where users click to print.
However, i'm not sure how I could add custom sizes for that application box that appears without messing up the code.
function onOpen() {
SpreadsheetApp.getUi()
.createMenu('Click to print')
.addItem('Print from Google Cloud', 'openDialog')
.addToUi();
}
function openDialog() {
var html = HtmlService.createHtmlOutputFromFile('Index')
.setSandboxMode(HtmlService.SandboxMode.NATIVE);
SpreadsheetApp.getUi()
.showModalDialog(html, 'Click the button below to print using the Google Cloud Print service.');
}
and the index.html code is:
<!DOCTYPE html>
<html>
<head>
<base target="_top">
<script src="https://www.google.com/cloudprint/client/cpgadget.js">
</script>
<script>
window.onload = function() {
var gadget = new cloudprint.Gadget();
gadget.setPrintButton(
cloudprint.Gadget.createDefaultPrintButton("button"));
var liabilityWaiver = waiver.getAs(MimeType.PDF);
gadget.setPrintDocument("url", "Test Page", "https://drive.google.com/open?id=0B3NraNAa2RhWSldiNklPVGI5OU0");
}
</script>
</head>
<body>
<div id="button"></div>
</body>
</html>
Below are image explanations of what the script does:
As you can see from the last image, that is where the problem is, the application box is too small to fit all of the information on it, therefore cutting it out.
-EDIT-
I managed to work out how to create the application box bigger, with help from a user going by Matt. However, even with the box bigger, the window size where the Google Cloud Print service is located is still small, no matter how big I make the outside application box. (See Image below for reference.)
Could anyone assist with making the inside box the full size of the outside box? Or just simply make it bigger?
Best Regards.
Check out https://developers.google.com/apps-script/reference/base/ui#showmodaldialoguserinterface-title
var htmlOutput = HtmlService
.createHtmlOutput('<p>A change of speed, a change of style...</p>')
.setWidth(250)
.setHeight(300);
SpreadsheetApp.getUi().showModalDialog(htmlOutput, 'My add-on');
Fairly new to HTML Service in apps script, have written a very basic UI.
The problem is that when the button is clicked (no onclick handler set) it opens up a new blank tab (I'm using Chrome).
Code below reproduces the behaviour, I have jquery / jquery UI references which are used in the broader project so left them in here.
How do I stop this blank tab opening on button click? Not shown here but it also happens when entered hit in a text box.
code.js:
function NewProposal() {
var html = HtmlService.createTemplateFromFile('Index');
var ss = SpreadsheetApp.getActiveSpreadsheet();
ss.show(html.evaluate().setHeight(530).setWidth(1100).setSandboxMode(HtmlService.SandboxMode.IFRAME));
}
Index.html:
<!DOCTYPE html>
<base target="_top">
<link rel="stylesheet" href="//code.jquery.com/ui/1.11.2/themes/cupertino/jquery-ui.css">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.0/jquery.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jqueryui/1.11.4/jquery-ui.min.js"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jsgrid/1.4.1/jsgrid.min.js"></script>
<form>
<table>
<tr>
<td>
<button id="Create">Create</button>
</td>
</tr>
</table>
</form>
You'll need to either get rid of the button, and use something else, or get rid of the form tags, and use div tags. If you get rid of the form tags, then it's more difficult to collect data from any input tags in the form. But, I don't see any input tags in your form. If you have no input tags in your form, then using form tags is pointless. Maybe you just left them out for purposes of reproducing the error in as little code as possible.
When a button is clicked inside of a form tag, the default behavior is for the current window to issue a POST request. That refreshes the content in the browser, but if there is no callback to display some other content, the page will go blank.
The only way to avoid this built-in feature of a form with a button, is to not use a button, or not use the form tags.
A click event can be added to multiple different types of HTML elements. Like a link <a> tags. Or a <div>. So, you can use something else other than a button, style it to look like a button if you wish, and add a click event to whatever you decide to use.
If you have lots of different types of input tags, it may be better to continue to use the form. But if you can easily get all the data out of the table some other way, you don't really need the form. The form adds nothing to the capability of styling or layout. So, if the benefit of using the form doesn't fit your circumstance, then you can look at other options.
If you want to give feedback to the user about what inputs are required, that's another issue. The form tags, the required attribute, and the button submission are all part of a system to try to make form submission more automatic, and make data validation and data collection easier. But, in order to use that "built-in" functionality, it all needs to work together in a certain way. As with anything that people try to make generic, it's very difficult to make it fit all circumstances. If you don't want the page to go blank when the button is clicked, all of that built-in behavior can become more of a detriment than a help.
When Apps Script gets the form, it strips out most of the content from the form element, and creates an object of input names and their values. So, when the "form" object (No longer a real form object) gets to the server, the only way you can get the values out of the object is by using the name attributes.
Add onsubmit="return(false)" inside your form openning tag:
<form onsubmit="return(false)">
...
</form>
Background...
I am trying to write a Google Apps Script to get the content of a Google Doc as HTML and use that HTML to create or update a web page in Google Sites. I already know how to do this but the result is a web page that is stripped of almost all of its formatting. After looking at the html from the Google Doc, I see that it is not using inline styles and I believe that Google Sites requires inline styling.
Anyone have a Google Apps Script that I can use to convert the CSS to inline styles before using it to create a Google Sites page? Also, a library that I could use within the Google Apps Script environment that would give me the same functionality would be just as good. It just needs to be a library that I could add within the Google Apps Scripting environment (i.e., through the "resources" - "manage libraries" menu). Thanks.
By the way...
I have tried getting the html from a Google Doc in two ways. Both ways give me the same CSS non-inline-style that gets stripped out when I use it to create a Google Sites Page.
1) I have used Romain Vialard's DocsListExtened Google Script Library at the following link...
https://sites.google.com/site/scriptsexamples/new-connectors-to-google-services/driveservice
2) I have used code suggested by a few people including hgabreu#gmail.com, and others at...
https://code.google.com/p/google-apps-script-issues/issues/detail?can=2&start=0&num=100&q=&colspec=Stars%20Opened%20ID%20Type%20Status%20Summary%20Component%20Owner&groupby=&sort=&id=585
Note: the same problem affects html email messages sent to gmail users.
There are numerous online tools that do this conversion, so you could leverage one of them from Google Apps Script. (If you only need to do this once in a while, why not just use one of those services?)
Here's an example script, that builds on the getElementByVal() function from Does Google Apps Script have something like getElementById?.
inline() function
/**
* Convert html containing <style> tags to instead have inline css.
*
* This example uses an online service from MailChimp Labs, but
* the same principle could be used to leverage several other
* online providers.
*
* #param {Text} htmlWstyle A block of HTML text including
* <style>..</style> tags.
*
* #returns {Text} Same HTML, converted to inline css.
*/
function inline(htmlWstyle) {
// Generate a POST request to inline css web tool.
var payload =
{
"html" : htmlWstyle,
"strip" : "checked"
};
// Because payload is a JavaScript object, it will be interpreted as
// an HTML form. (We do not need to specify contentType; it will
// automatically default to either 'application/x-www-form-urlencoded'
// or 'multipart/form-data')
var options =
{
"method" : "post",
"payload" : payload,
"muteHttpExceptions" : true
};
var url = "http://beaker.mailchimp.com/inline-css";
var response = UrlFetchApp.fetch(url, options);
// The html from this service is non-compliant, so we need
// to massage it to satisfy the XmlService.
var badlink = new RegExp('<link (.*?)[\/]*>',"igm");
var badmeta = new RegExp('<meta (.*?)[\/]*>',"igm");
var badinput = new RegExp('<input (.*?)[\/]*>',"igm");
var xml = response.getContentText()
.replace(badlink,"<link $1></link>" )
.replace(badinput,"<input $1></input>" )
.replace(badmeta,"<meta $1></meta>" )
.replace(/<br>/g,"<br/>");
// So far, so good! Next, extract converted text from page. <textarea name="text" ...>
// Put the receieved xml response into XMLdocument format
var doc = XmlService.parse(xml);
var inlineHTML = getElementByVal( doc, 'textarea', 'name', 'text' );
return (inlineHTML == null) ? null : inlineHTML.getValue();
}
Explanation
There may appear to be some black magic in there. A previous answer described how to use the old Xml Service to examine the structure of a web page to find the bits you wanted. It's still good reading (and voting up, hint, hint!), but that service is now gone, and the new XmlService doesn't have equivalent exploratory support.
To start, we found a web service that did the job we were interested in, and used the UrlFetch Service to simulate a person pasting code into the service. Ideally, we'd like one that returned just the result we wanted, in a format we could use without any further work. Alas, what we had was a complete web page, and that meant that we'd have to farm it for our result. Basic idea there: Use the XmlService to parse and explore the page, extracting just the bit we wanted.
In the Css Inline service that was selected, Chrome's ability to "Inspect Element" was used to determine the tag type (<textarea>) and a way to uniquely identify it (name="text"). Armed with that knowledge, we had everything we needed to use getElementByVal() to dig through the HTML returned from a POST request. (Alternatively, you could use String methods to find and extract the text you want.)
But when that was all put together, XmlService kept complaining about the format of the HTML in the result page - so JavaScript String & RegExp methods were used to balance the malformed tags before passing the page on.
Example
Here's a simple example illustrating use of the inline() function. Note that style information is absorbed from both the external css link AND the tagged styling.
function test_inline() {
var myHtml =
'<html>'
+ '<head>'
+ '<title>Example</title>'
+ '<link rel="stylesheet" href="http://inlinestyler.torchboxapps.com/static/css/example.css" ></link>'
+ '</head>'
+ '<body>'
+ '<style type="text/css">'
+ 'h1{'
+ 'color:yellow'
+ '}'
+ '</style>'
+ '<h1>An example title</h1>'
+ '<p>Paragraph 1</p>'
+ '<p class="p2">Paragraph 2</p>'
+ '</body>'
+ '</html>';
var inlined = inline(myHtml);
debugger; // pause in debugger, have a look at "inlined"
}
Result
<html>
<head>
<title>Example</title>
</head>
<body>
<h1 style="color: yellow;">An example title</h1>
<p style="margin: 0;padding: 0 0 10px 0;">Paragraph 1</p>
<p class="p2" style="margin: 0;padding: 0 0 10px 0;">Paragraph 2</p>
</body>
</html>
I have a UiApp script that display images in flextable; I have assigned mousedown handler to the images so that when it is clicked I want it to open a templated HTML page (in a new window). I wrote the following code; it gets executed but it doesnt return anything.
What should I do to get the Item Detail page containing the image loaded from the imageurl assigned to be opened in a new window?
Here's my script and html code:
var page = HtmlService.createTemplateFromFile("Item Detail");
var image = productDetails[i].imageurl;
page.imageurl = image;
Logger.log(productDetails[i].imageurl);
return page.evaluate();
<img src=<?=imageurl ?> alt="click for more info" height="100%" width="100%">
So suppose you want to make a clickable image. And you want this image to open in a new window when you click on it.
var anchor = app.createAnchor("<img src='URL_TO_IMAGE' />",
true,
ScriptApp.getService().getUrl()+"?detailPage="+SOME_ID)
.setTarget("_blank");
Then when you click on that link, your script will be opened in another window. And also, a GET parameter will be passed along with it. So in your doGet(e) function, you could handle whether to display the main UiApp or an Html detail page.
function doGet(e) {
if (e.parameter.detailPage) {
//show the detail page
} else {
//show the normal UiApp
}
}
Read the top part of this other answer to get a better idea of how GET parameters work with GAS.
You will want to surround the <img> with a <a> tag.
<img ... >