i create some bookmarks in my Google Docs with this script:
function myFunctionCreate() {
var mydoc = DocumentApp.getActiveDocument();
var cursor = mydoc.getCursor();
var bookmark = mydoc.addBookmark(cursor);
Logger.log(bookmark.getId());
}
How can i jump to this bookmarks and edit the text?
Can I also target a bookmark?
Thanks
Related
Need your help and expert guidance as I need my google sheet to send emails every time a condition becomes true in "K" column which is named "Subject" as a header in the tab name "Analysis". Whenever I run the below code. Similarly last 3 lines I get while running the code as errors. Please explain in a simple possible way and not too much technical
function sendEmail(){
var ss = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Analysis");
var range = ss.getRange("J2:J35");
range.clear();
var n = ss.getLastRow();
for (var i = 2;i<n+1; i++){
var emailRequired = ss.getRange(i,9).getValue();
var subject = ss.getRange(i,11).getvalue();
var message = ss.getRange(i,12).getvalue();
if (emailRequired=="YES"){
MailApp.sendEmail("ksm272364#gmail.com",subject,message);
ss.getRange(i,10).setvalue("YES");
}
}
}
1:21:51 PM Error
TypeError: ss.getRange(...).getvalue is not a function
sendEmail # Code.gs:8
I am trying to search-and-replace linked text from an old url to a new url.
It is not working and I have spent hours and hours. If I remove the "if (found)" it gives me "TypeError: Cannot read property 'getElement' of null" even though my files have text that is linked to this old_url.
Please, help me.
function myFunction() {
var old_url ="http://hurlx1.com";
var new_url ="http://urlxa.com";
var files = DriveApp.getFolderById("my folder id").getFilesByType(MimeType.GOOGLE_DOCS);
while (files.hasNext()) {
var file = files.next();
var doc = DocumentApp.openById(file.getId());
found=doc.getBody().findText(old_url);
if (found) {
var link_element = found.getElement().asText();
var start = found.getStartOffset();
var end = found.getEndOffsetInclusive();
var correct_link = link_element.getText().slice(start, end);
link_element.setLinkUrl(start, end, correct_link);
}
}
}
I believe your situation and goal as follows.
In your Google Document,
Text and hyperlink are the same with old_url.
Hyperlink is old_url. But the text is different from old_url.
You want to update old_url with new_url using Google Apps Script.
For this, how about this answer?
Modification points:
About your error message, when the text of old_url is not found in the Google Document with found=doc.getBody().findText(old_url);, found becomes null even when old_url is set as the hyperlink. Because findText searches the text on Document body, and that cannot search the hyperlinks set to the texts. I think that this is the reason of your issue.
In your script, var new_url ="http://urlxa.com"; is used. But when the link is set, correct_link is used like link_element.setLinkUrl(start, end, correct_link);. By this, new_url is not set.
When you want to update the text of http://hurlx1.com to new_url of var new_url ="http://urlxa.com";, it is required to also modify the text.
In your script, only the 1st old_url is updated. If there are several values of old_url in the Document, it is required to update them using the loop.
Specification of modified script:
This modified script can be used for the following patterns.
Text and hyperlink are the same with old_url.
In this case, the text value of old_url is also updated with old_url.
Hyperlink is old_url. But the text is different from old_url.
In this case, only the hyperlink of old_url is updated.
There are several texts with the hyperlink of old_url in the Google Document.
Modified script:
function myFunction() {
var old_url ="http://hurlx1.com";
var new_url ="http://urlxa.com";
var files = DriveApp.getFolderById("my folder id").getFilesByType(MimeType.GOOGLE_DOCS);
while (files.hasNext()) {
var file = files.next();
var doc = DocumentApp.openById(file.getId());
var body = doc.getBody();
// The following script is used for the situation that the text and hyperlink are the same with `old_url`.
var found = body.findText(old_url);
while (found) {
var link_element = found.getElement().asText();
var start = found.getStartOffset();
var end = found.getEndOffsetInclusive();
var correct_link = link_element.getText().slice(start, end);
link_element.setLinkUrl(start, end, new_url).replaceText(old_url, new_url);
found = body.findText(old_url, found);
}
// The following script is used for the situation that although the hyperlink is `old_url`, the text is different from `old_url`.
var text = body.editAsText();
for (var i = 0; i < text.getText().length; i++) {
if (text.getLinkUrl(i) == old_url) {
text.setLinkUrl(i, i + 1, new_url);
}
}
}
}
References:
replaceText(searchPattern, replacement)
findText(searchPattern, from)
editAsText()
I am trying to create a script in Google Sheets that select a range and print it. I am trying to print some information based on some parameters. I have the following script that sets the desired range, but I do not see a way to print it using script.
function printInvoice() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet();
var range = sheet.getRange("A1:H46");
range.activate();
}
Any suggestions? Thanks!
You can use the following script:
var PRINT_OPTIONS = {
'size': 7, // paper size. 0=letter, 1=tabloid, 2=Legal, 3=statement, 4=executive, 5=folio, 6=A3, 7=A4, 8=A5, 9=B4, 10=B
'fzr': false, // repeat row headers
'portrait': true, // false=landscape
'fitw': true, // fit window or actual size
'gridlines': false, // show gridlines
'printtitle': false,
'sheetnames': false,
'pagenum': 'UNDEFINED', // CENTER = show page numbers / UNDEFINED = do not show
'attachment': false
}
var PDF_OPTS = objectToQueryString(PRINT_OPTIONS);
function onOpen(e) {
SpreadsheetApp.getUi().createMenu('Print...').addItem('Print selected range', 'printSelectedRange').addToUi();
}
function printSelectedRange() {
SpreadsheetApp.flush();
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet();
var range = sheet.getActiveRange();
var gid = sheet.getSheetId();
var printRange = objectToQueryString({
'c1': range.getColumn() - 1,
'r1': range.getRow() - 1,
'c2': range.getColumn() + range.getWidth() - 1,
'r2': range.getRow() + range.getHeight() - 1
});
var url = ss.getUrl().replace(/edit$/, '') + 'export?format=pdf' + PDF_OPTS + printRange + "&gid=" + gid;
var htmlTemplate = HtmlService.createTemplateFromFile('js');
htmlTemplate.url = url;
SpreadsheetApp.getUi().showModalDialog(htmlTemplate.evaluate().setHeight(10).setWidth(100), 'Print range');
}
function objectToQueryString(obj) {
return Object.keys(obj).map(function(key) {
return Utilities.formatString('&%s=%s', key, obj[key]);
}).join('');
}
You will also need to create an html file in your project (File>New>HTML File) with the name js, and paste in the following code:
<script>
window.open('<?=url?>', '_blank', 'width=800, height=600');
google.script.host.close();
</script>
This will create a button in your Sheets menu that will open a PDF with the selected range. You can modify some settings such as the print orientation, its size, or whether to show the gridlines or not on top of the script. If you still want to automatically print the ranges without having to manually go through the print dialog, you can either:
Send the document to your printer using GmailApp API class, if your printer supports such functionality.
Use Google Cloud Print. The following blog post may help you with that: https://ctrlq.org/code/20061-google-cloud-print-with-apps-script
I stumbled on your code quite by chance from an "unallowed question to stack overflow which actually seems to be exactly what I want - could not get any detail on how to print from App Script for sheets.
I have been trying it out but it falls over at the line in your sample
"var htmlTemplate = HtmlService.createTemplateFromFile('js');"
where the service cannot find 'js'. Afraid I do not understand what an html template is anyway - are you able to explain?
I want to get this:
But I need to create the HTML dynamically because it's loaded via AJAX from an external ressource.
I already asked this question here (JQM: Dynamic nested collapsibles - $(collapsible).collapsible() & $(collapsible).trigger('create')) but I got it completly wrong with lists and collapsibles, so I figured a new question would be better.
Here is what I got so far:
function loadTipps() {
console.log("Lade Tipps..");
var categoriesURL = tippsURL+"?type=kategorien&callback=?"; // url for the categories
$.getJSON(categoriesURL,function(data) {
console.log("Lade Kategorien..");
var DIV_tipps_komplett = $("#tipps_komplett");
$.each(data, function(key,value){
var kategorie_ID = value.id;
var kategorie_NAME = value.name;
var collapsible_HTML = $('<div data-role="collapsible"></div>');
var kategorie_Ueberschrift_HTML = $('<h3>'+kategorie_NAME+'</h3>');
var tipps_kategorie_HTML = $('<div id="tipps_kategorie'+kategorie_ID+'" data-role="collapsible-set"></div>');
var tippURL = tippsURL+"?type=tipp&kat_id="+value.id+"&callback=?"; // url for the tipps of the current category
$.getJSON(tippURL,function(data2) {
$.each(data2, function(key2,value2){
var tipp_Ueberschrift_Text_HTML = '<div data-role="collapsible"><b>'+value2.name+'</b><p>'+value2.text+'</p><br></div>';
tipps_kategorie_HTML.append(tipp_Ueberschrift_Text_HTML);
}); //<--each
});//<--getJSON
collapsible_HTML.append(kategorie_Ueberschrift_HTML);
collapsible_HTML.append(tipps_kategorie_HTML);
DIV_tipps_komplett.append(collapsible_HTML);
});//<--each
DIV_tipps_komplett.trigger('create');
});//<--getJSON
}
This results in:
As you can see, the items in the first collapsible set are not in another collapsible set. Any ideas why?
Try changing the line:
var tipp_Ueberschrift_Text_HTML = '<div data-role="collapsible"><b>'+value2.name+'</b><p>'+value2.text+'</p><br></div>';
to
var tipp_Ueberschrift_Text_HTML = '<div data-role="collapsible"><h3>'+value2.name+'</h3><p>'+value2.text+'</p><br></div>';
The Collapsible needs the <h3> element instead of <b> to render.
Here is a demo of your exact code with the AJAX calls removed: http://jsfiddle.net/ezanker/RkLuV/
By the way, this is pretty much the answer I gave you here: collapsible list with collapsed items in jqm, it would probably have been better to continue the conversation there.
I use this code to replace text of bookmark in word :
using (WordprocessingDocument wordDoc = WordprocessingDocument.Open("doc3.docx", true))
{
var bookmarkStarts = wordDoc.MainDocumentPart.Document.Body.Descendants<BookmarkStart>();
foreach (var start in bookmarkStarts)
{
OpenXmlElement elem = item.NextSibling();
while (elem != null && !(elem is BookmarkEnd))
{
OpenXmlElement nextElem = elem.NextSibling();
elem.Remove();
elem = nextElem;
}
item.Parent.InsertBefore<Run>(new Run(new Text("Hello")), item);
}
wordDoc.Close();
}
But this not work where the bookmark is in the table.
Have you checked that you don't delete any bookmarks with your approach?
I've run your test code after editing a little (you don't have a var name items in your example code), and I've succesfully inserted Hello in 2 bookmarks out of a table, and 2 in a table, without any issues.
Which leads me to believe your problem lies elsewhere.
Have you looked at the open-xml in your document after you've run your program?
Is there any errors?
I've experienced bookmarks being placed the oddest places in a word-document when you leave the placing to word, and not you.
You can also end up with bookmarks overlapping each other like this
<bookmark1 start><xml xml><bookmark2 start><bookmark1 end><xml xml><bookmark2 end>
If you run into that case, your code will delete the bookmarkstart 2 before it reaches bookmarkend 1, and that will cause your bookmark to not be replaced.
You'll easily run into that problem with larger complex documents.
The way I solved it was to "sort" the bookmarks before doing any editing.
So the example above would become
<bookmark1 start><xml xml><bookmark1 end><bookmark2 start><xml xml><bookmark2 end>
after the sort
The code I use to do this look like this:
var bookmarks = mainPart.Document.Body.Descendants<BookmarkStart>();
for (int i = 0; i < bookmarks.Count(); i++)
{
var bks = bookmarks.ElementAt(i);
var next = bks.NextSibling();
if (next is BookmarkEnd)
{
var bme = (BookmarkEnd)next;
if (int.Parse(bks.Id) - int.Parse(bme.Id) == 1)
{
var copy = (BookmarkEnd)next.Clone();
bks.Parent.RemoveChild<BookmarkEnd>(bme);
bks.Parent.InsertBefore<BookmarkEnd>(copy, bks);
}
}
}
Which i'll admit isn't totally fool-proof but have worked well for me.
Another check you can add, to avoid deleting bookmarks is in your replace method
This will make sure you don't delete bookmarkstarts as you remove elements when inserting text
while (elem != null && !(elem is BookmarkEnd)) //fjern elementer
{
OpenXmlElement nextElem = elem.NextSibling();
if (elem.LocalName != "bookmarkStart")
elem.Remove();
elem = nextElem;
}
Good luck :)