How to replace text in google docs - google-apps-script

I already have code that works, but the formatting is not preserved. When executing the function, a pop-up window is displayed, the words can be entered here. When the script has run through, the words are replaced but the formatting of the file is completely broken. I think it's because of getParagraphs. Does anyone know an alternative?
CODE:
function anpassen(){
var varMap={};
var file=DriveApp.getFileById(DocumentApp.getActiveDocument().getId());
var doc= DocumentApp.openById(file.getId());
var para= doc.getBody().getParagraphs();
for(var i=0; i<para.length;i++){
var text=para[i].getText();
if (!text) continue;
text=text.replace(/{{([^}]*)}}/g, function(m, $1) {
if(!varMap[$1]){varMap[$1]= DocumentApp.getUi().prompt($1+"?").getResponseText();
}
return varMap[$1];
});
para[i].setText(text);
}
}

Use Advanced Google Services
As specified in the documentation you can use Document API directly by using Google Apps Script, keep in mind that you'll need to import this Advanced Service like the following:
what's more there's an example of how to handle the style while adding a new text.

Related

Google Docs Apps Script get number of columns

On a Google Doc you can set columns from Format > Columns. Now, though, I want to access those columns from Apps Script to confirm the number of columns. I don't even need to modify them, just access. I haven't found anything that jumps out at me from the documentation, so I didn't know if there's an extension of the Document service that would allow for such.
I'm sorry not to include any code, but I have no code obvious to show. I did create a document with 2 columns to see exactly what I'm talking about. https://docs.google.com/document/d/1MyttroeN4kPUm9PfYZnTe_gJqstM3Gb5q3vS3c84dNw/edit
Answer
It is possible using the get method of the Google Docs API
How to do it
In Apps Script, enable the Advanced Docs Service.
Use the method get.
Check the array called content.
Search an object called sectionBreak in each element of content.
Check that the object has the following data: sectionBreak>sectionStyle>columnProperties.
Take the length of the array columnProperties.
(keep in mind that the first occurrence of columnProperties is in the first element of content, skip it and start the loop from the second one.
Code
function myFunction() {
var id = DocumentApp.getActiveDocument().getId()
var result = Docs.Documents.get(id)
var content = result["body"]["content"]
for (var i = 1; i < 20; i++) {
try {
var cols = content[i]["sectionBreak"]["sectionStyle"]["columnProperties"]
console.log('number of columns: ' + cols.length)
} catch (error) {
}
}
}
Reference
Google Docs API
Method: documents.get
Advanced Google services
Advanced Docs Service

How to use Google App Scripts to search a Google Document for a specific word?

Is there a Google Scripts function that can search a designated Google Document for a specific string of words? Or any way to do that? I am trying to make a conditional statement that will find a word in a Google Document, and if that word exists, it will carry out a task. Thank you.
An example:
if (for example, the word gym exists in the Google Document){
Send an email to the gym teacher.
}
Something like this should do what you need. I commented out some of the Logger stuff but left it there in case you wanted to see how things worked more clearly.
function findText() {
var doc = DocumentApp.getActiveDocument();//get doc
var body = doc.getBody().getText();//get text as string
//Logger.log(body);
var regex = /gym/g;//look for 'gym'
var searchSuccess = body.search(regex);
Logger.log(searchSuccess);
if(searchSuccess > 0){
Logger.log('Item Found')
} else {
Logger.log('Item not found')
}
}
You can use the findText. For example:
var body = DocumentApp.getActiveDocument().getBody();
if(body.findText("gym"))
{
// send an email
}

Error when trying to get the URL of a page in a Google Apps Script

I am writing a Google Apps Script to be embedded into Google Sites to retrieve the names and URLs for child pages of the current page. When I call the getURL() function I am getting the following error:
'TypeError: Cannot find function getURL in object WebPage.'
My code is as follows:
function doGet() {
var app = UiApp.createApplication();
var pages = SitesApp.getActivePage().getChildren();
for (var i = 0; i < pages.length; i++) {
Logger.log(pages[i].getURL());
}
return app;
}
I am new to Google Apps Scripts so am struggling to work out what this means. Any help would be greatly appreciated.
Use pages[i].getUrl()
You should use the autocomplete feature in the script editor to avoid such typing errors (control space after the dot : page[i].here type CTRL space and you'll see all possible methods...)
Note : the general rule in javascript is to use the so called camelCase format : getRange, createLabel ... there are only a few exceptions like setHTML but every rule must have exceptions doesn't it ?

Embed a list of forms in Google Site per DocList

I used the code given in
Embedding Google DocList in a Google Site
to embed a list of files (taken from a folder in Google drive) in a page of Google Sites. I like to know, if there's a way to modify the links on the output-page, so that they direct to the live-form of the spreadsheet and not to the spreadsheet-mode as it's set in the script.
EDIT : sorry about that, forget this first answer as there is actually a method to get form urls !!
here is how it works
function myFunction() {
var ss=SpreadsheetApp.getActive()
var formurl = ss.getFormUrl()
Browser.msgBox(formurl)
}
so it would be quite easy to embed a list in a site page,
here is a small code that does the job, online version here (needs authorization) be patient : can be slow to show up...
function doGet() {
var app=UiApp.createApplication().setTitle('Available Forms').setStyleAttribute('padding', '25');
var user = Session.getEffectiveUser().getUserLoginId();
app.add(app.createLabel('Available forms in Drive for '+user).setStyleAttribute('background', '#dddd33').setPixelSize(500, 18).setHorizontalAlignment(UiApp.HorizontalAlignment.CENTER).setStyleAttribute('fontWeight', 'bold'))
var flex=app.createFlexTable().setId('flex').setWidth('500').setBorderWidth(1).setCellPadding(2).setStyleAttribute('borderColor', '#dddd33');
var index = 0
var docs = DocsList.getAllFiles()
for (var n in docs){
if(docs[n].getFileType()=="spreadsheet"){
var ss=SpreadsheetApp.openById(docs[n].getId())
if(ss.getFormUrl()){
flex.setText(index, 0, docs[n].getName())
flex.setWidget(index, 1, app.createAnchor('open form', ss.getFormUrl()))
++ index
}
}
}
app.add(flex)
return app
}
again sorry that I forgot this feature.
-Forms are embedded in spreadsheet and don't appear as individual entities in your drive. Their ID (or URL) are not available from apps-script.... I'm afraid you'll have to encode the URLs manually.-
You could create a Document and name it the title of your Google Form. Stick the URL of the form inside the doc.

How to parse an HTML string using CSS selectors? [duplicate]

var page = UrlFetchApp.fetch(contestURL);
var doc = XmlService.parse(page);
The above code gives a parse error when used, however if I replace the XmlService class with the deprecated Xml class, with the lenient flag set, it parses the html properly.
var page = UrlFetchApp.fetch(contestURL);
var doc = Xml.parse(page, true);
The problem is mostly caused because of no CDATA in the javascript part of the html and the parser complains with the following error.
The entity name must immediately follow the '&' in the entity reference.
Even if I remove all the <script>(.*?)</script> using regex, it still complains because the <br> tags aren't closed.
Is there a clean way of parsing html into a DOM tree.
I ran into this exact same problem. I was able to circumvent it by first using the deprecated Xml.parse, since it still works, then selecting the body XmlElement, then passing in its Xml String into the new XmlService.parse method:
var page = UrlFetchApp.fetch(contestURL);
var doc = Xml.parse(page, true);
var bodyHtml = doc.html.body.toXmlString();
doc = XmlService.parse(bodyHtml);
var root = doc.getRootElement();
Note: This solution may not work if the old Xml.parse is completely removed from Google Scripts.
In 2021, the best way to parse HTML on the .gs side that I know of is...
Click + next to Library
Enter 1ReeQ6WO8kKNxoaA_O0XEQ589cIrRvEBA9qcWpNqdOP17i47u6N9M5Xh0
Click "Look up"
Click Add
Sample usage:
const contentText = UrlFetchApp.fetch('https://www.somesite.com/').getContentText();
const $ = Cheerio.load(contentText);
$('.some-class').first().text();
That's it -- this is probably the closest we'll get to doing jQuery-like DOM selection in GAS. The .first() is important or else you may extract more content than you expected (think of it as using querySelector() instead of querySelectorAll()).
Credit where credit is due: https://github.com/tani/cheeriogs
As of May 2020, you can now use the Cheerio library for Google Apps Script to do this.
Returns the content of Wikipedia's Main Page
const content = getContent_('https://en.wikipedia.org');
const $ = Cheerio.load(content);
Logger.log($('#mp-right').text());
Returns the content of the first paragraph <p> of Wikipedia's Main Page
const content = getContent_('https://en.wikipedia.org');
const $ = Cheerio.load(content);
Logger.log($('p').first().text());
To add to your project:
Select Resources - Libraries... in the Google Apps Script editor. Enter the project key 1ReeQ6WO8kKNxoaA_O0XEQ589cIrRvEBA9qcWpNqdOP17i47u6N9M5Xh0 in the Add a library field, and click "Add". Select the highest version number, and click "Save".
I found that the best way to parse html in google apps is to avoid using XmlService.parse or Xml.parse. XmlService.parse doesn't work well with bad html code from certain websites.
Here a basic example on how you can parse any website easily without using XmlService.parse or Xml.parse. In this example, i am retrieving a list of president from "wikipedia.org/wiki/President_of_the_United_States"
whit a regular javascript document.getElementsByTagName(), and pasting the values into my google spreadsheet.
1- Create a new Google Sheet;
2- Click the menu Tools > Script editor... to open a new tab with the code editor window and copy the following code into your Code.gs:
function onOpen() {
var ui = SpreadsheetApp.getUi();
ui.createMenu("Parse Menu")
.addItem("Parse", "parserMenuItem")
.addToUi();
}
function parserMenuItem() {
var sideBar = HtmlService.createHtmlOutputFromFile("test");
SpreadsheetApp.getUi().showSidebar(sideBar);
}
function getUrlData(url) {
var doc = UrlFetchApp.fetch(url).getContentText()
return doc
}
function writeToSpreadSheet(data) {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheets()[0];
var row=1
for (var i = 0; i < data.length; i++) {
var x = data[i];
var range = sheet.getRange(row, 1)
range.setValue(x);
var row = row+1
}
}
3- Add an HTML file to your Apps Script project. Open the Script Editor and choose File > New > Html File, and name it 'test'.Then copy the following code into your test.html
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<input id= "mButon" type="button" value="Click here to get list"
onclick="parse()">
<div hidden id="mOutput"></div>
</body>
<script>
window.onload = onOpen;
function onOpen() {
var url = "https://en.wikipedia.org/wiki/President_of_the_United_States"
google.script.run.withSuccessHandler(writeHtmlOutput).getUrlData(url)
document.getElementById("mButon").style.visibility = "visible";
}
function writeHtmlOutput(x) {
document.getElementById('mOutput').innerHTML = x;
}
function parse() {
var list = document.getElementsByTagName("area");
var data = [];
for (var i = 0; i < list.length; i++) {
var x = list[i];
data.push(x.getAttribute("title"))
}
google.script.run.writeToSpreadSheet(data);
}
</script>
</html>
4- Save your gs and html files and Go back to your spreadsheet. Reload your Spreadsheet. Click on "Parse Menu" - "Parse". Then click on "Click here to get list" in the sidebar.
Xml.parse() has an option to turn on lenient parsing, which helps when parsing HTML. Note that the Xml service is deprecated however, and the newer XmlService doesn't have this functionality.
For simple tasks such as grabbing one value from a webpage, you could use a regular expression. Regex is notoriously bad for parsing HTML as there's all sorts of weird cases it can get tripped up, but if you're confident about the HTML you're accessing this can sometimes be the simplest way.
Here's an example that fetches the contents of the page's <title> tag:
var page = UrlFetchApp.fetch(contestURL);
var regExp = new RegExp("<title>(.*)</title>", "gi");
var result = regExp.exec(page.getContentText());
// [1] is the match group when using parenthesis in the pattern
var value = result ? result[1] : 'No title found';
I know it is not exactly what OP asked, but I found this question when I was looking for some html parsing options - so it might be useful for others as well.
There is an easy to use the library for TEXT parsing. It's useful if you want to get only one piece of information from the html(xml) code.
EDIT 2021: The script library id is:
1Mc8BthYthXx6CoIz90-JiSzSafVnT6U3t0z_W3hLTAX5ek4w0G_EIrNw
It works like in the picture above
function getData() {
var url = "https://chrome.google.com/webstore/detail/signaturesatori-central-s/fejomcfhljndadjlojamaklegghjnjfn?hl=en";
var fromText = '<span class="e-f-ih" title="';
var toText = '">';
var content = UrlFetchApp.fetch(url).getContentText();
var scraped = Parser
.data(content)
.from(fromText)
.to(toText)
.build();
Logger.log(scraped);
return scraped;
}
If you are using
Cheerio library for Google Apps Script
Source code
Library page (⭐ star it!)
Installation by library ID:
1ReeQ6WO8kKNxoaA_O0XEQ589cIrRvEBA9qcWpNqdOP17i47u6N9M5Xh0
A function to get current emojis from unicode.org:
function getEmojis() {
var t = new Date();
var url = 'https://unicode.org/emoji/charts/full-emoji-list.html';
var fetch = UrlFetchApp.fetch(url);
var contentText = fetch.getContentText();
//console.log(new Date() - t);
// Cherio
var $ = Cheerio.load(contentText);
var data = [];
$("table > tbody > tr").each((index, element) => {
var row = [];
$(element).find("td").each((index, child) => {
row.push($(child).text());
});
if (row.length > 0) {
data.push(row);
}
});
//console.log(data);
//console.log(new Date() - t);
// Result
return data;
}
↑ Sample code shows how to parse table and put it into [[array]]
May be used as a custom function:
Bonus
Parsing the site may be a time-consuming operation + you may reach the limit.
Here's a test file with a full version of the script:
https://docs.google.com/spreadsheets/d/1iO7YjYWyfseQu_YCfRbGDPg7NskOgMu_iO1iGjr7KxY/edit#gid=93365395
↑ it uses CasheService to reduce the number of calls.
Natively there's no way unless you do what you already tried which wont work if the html doesnt conform with the xml format.
There are two options
a) One is to use JavaScript's string functions. First locate your tag using string.indexOf() and then extract the data you want using string.substring().
b) The other option is to make use of the Xml Service.
It's not possible to create an HTML DOM server-side in Apps Script. Using regular expressions is likely your best option, at least for simple parsing.