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?
Related
I need to parse a Google Alert RSS Feed with Google Apps Script.
Google Alerts RSS-Feed
I found a script which should do the job but I cant get it working with Google's RSS Feed:
The feed looks like this:
<feed xmlns="http://www.w3.org/2005/Atom" xmlns:idx="urn:atom-extension:indexing">
<id>tag:google.com,2005:reader/user/06807031914929345698/state/com.google/alerts/10604166159629661594</id>
<title>Google Alert – garbe industrial real estate</title>
<link href="https://www.google.com/alerts/feeds/06807031914929345698/10604166159629661594" rel="self"/>
<updated>2022-03-17T19:34:28Z</updated>
<entry>
<id>tag:google.com,2013:googlealerts/feed:10523743457612307958</id>
<title type="html"><b>Garbe Industrial</b> plant Multi-User-Immobilie in Ludwigsfelde - <b>Property</b> Magazine</title>
<link href="https://www.google.com/url?rct=j&sa=t&url=https://www.property-magazine.de/garbe-industrial-plant-multi-user-immobilie-in-ludwigsfelde-117551.html&ct=ga&cd=CAIyGWRmNjU0ZGNkMzJiZTRkOWY6ZGU6ZGU6REU&usg=AFQjCNENveXYlfrPc7pZTltgXY8lEAPe4A"/>
<published>2022-03-17T19:34:28Z</published>
<updated>2022-03-17T19:34:28Z</updated>
<content type="html">Die <b>Garbe Industrial Real Estate</b> GmbH startet ihr drittes Neubauprojekt in der Metropolregion Berlin/Brandenburg. Der Projektentwickler hat sich ...</content>
<author>
...
</feed>
I want to extract entry -> id, title, link, updated, content.
I used this script:
function ImportFeed(url, n) {
var res = UrlFetchApp.fetch(url).getContentText();
var xml = XmlService.parse(res);
//var item = xml.getRootElement().getChild("channel").getChildren("item")[n - 1].getChildren();
var item = xml.getRootElement().getChildren("entry")[n - 1].getChildren();
var values = item.reduce(function(obj, e) {
obj[e.getName()] = e.getValue();
return obj;
}, {});
return [[values.id, values.title, values.link, values.updated, values.content]];
}
I modified this part, but all i got was "TypeError: Cannot read property 'getChildren' of undefined"
//var item = xml.getRootElement().getChild("channel").getChildren("item")[n - 1].getChildren();
var item = xml.getRootElement().getChildren("entry")[n - 1].getChildren();
Any idea is welcome!
In your situation, how about the following modified script?
Modified script:
function SAMPLE(url, n = 1) {
var res = UrlFetchApp.fetch(url).getContentText();
var root = XmlService.parse(res.replace(/&/g, "&")).getRootElement();
var ns = root.getNamespace();
var entries = root.getChildren("entry", ns);
if (!entries || entries.length == 0) return "No values";
var header = ["id", "title", "link", "updated", "content"];
var values = header.map(f => f == "link" ? entries[n - 1].getChild(f, ns).getAttribute("href").getValue().trim() : entries[n - 1].getChild(f, ns).getValue().trim());
return [values];
}
In this case, when you use getChild and getChildren, please use the name space. I thought that this might be the reason of your issue.
From your script, I guessed that you might use your script as the custom function. In that case, please modify the function name from ImportFeed to others, because IMPORTFEED is a built-in function of Google Spreadsheet. In this sample, SAMPLE is used.
If you want to change the columns, please modify header.
In this sample, the default value of n is 1. In this case, the 1st entry is retrieved.
In this script, for example, you can put =SAMPLE("URL", 1) to a cell as the custom function. By this, the result value is returned.
Note:
If the above-modified script was not the direct solution of your issue, can you provide the sample value of res? By this, I would like to modify the script.
As the additional information, when you want to put all values by executing the script with the script editor, you can also use the following script.
function myFunction() {
var url = "###"; // Please set URL.
var res = UrlFetchApp.fetch(url).getContentText();
var root = XmlService.parse(res.replace(/&/g, "&")).getRootElement();
var ns = root.getNamespace();
var entries = root.getChildren("entry", ns);
if (!entries || entries.length == 0) return "No values";
var header = ["id", "title", "link", "updated", "content"];
var values = entries.map(e => header.map(f => f == "link" ? e.getChild(f, ns).getAttribute("href").getValue().trim() : e.getChild(f, ns).getValue().trim()));
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Sheet1"); // Please set the sheet name.
sheet.getRange(sheet.getLastRow() + 1, 1, values.length, values[0].length).setValues(values);
}
References:
XML Service
map()
I have a Google Sheet which hides some sheets from most users, but when an admin user is identified, has a menu option to show all the sheets. It calls my function showAllSheets, as follows:
function showAllSheets() {
var sheets=SpreadsheetApp.getActiveSpreadsheet().getSheets();
for(var i = 0; i < sheets.length; i++){
//Show each sheet
sheets[i].showSheet();
console.log('Showing Sheet: ' + sheets[i].getName())
}
}
But this function doesn't always work. I get very mixed results. Sometimes it shows all sheets as expected. Sometimes it shows some of the sheets. Sometimes it eventually shows all or some of the sheets but only after a long delay (1 minute+) and sometimes, it does nothing at all.
I'm checking my execution time in the "Executions" section of Apps Script. This function typically executes in about 2-3 seconds and the console log contains all expected messages. It will say "completed" and still my sheets aren't showing. Sometimes it will eventually show the sheets some time after it says execution is complete and again, sometimes they never show.
I have an onOpen installable trigger and an onSelectionChange simple trigger, so at first I was concerned maybe my scripts are running into each other. However, I've confirmed I still have this issue even if I make sure all other scripts have completed before I run it.
I have no issues with .hideSheet() in my functions that hide the sheets (one being RestoreDefaultView, the other in my onOpen trigger.). They always get hidden immediately.
Here is the menu code I'm using:
var ui = SpreadsheetApp.getUi();
if (thisuser.group == 'admin') {
ui.createMenu('MyProject(Admin)')
.addSubMenu(
ui.createMenu('View')
.addItem('Restore Default Sheet View', 'restoreDefaultView')
.addItem('Show All Sheets', 'showAllSheets')
)
.addToUi();
}
What is going on and what can I do to fix it?
EDIT: Per request, here is the function that is hiding some of the sheets:
function restoreDefaultView() {
const name_Master = 'PROJECT MASTER';
const name_Lists = 'LISTS';
const name_Guide = 'GUIDE';
const name_Access = 'ACCESS';
const name_ActionForm = 'frm_Action';
const name_ProjectForm = 'frm_Project';
const name_NotesForm = 'frm_Notes';
const name_AdminForm = 'frm_Admin';
const name_GroupAdmin = 'admin_Groups';
//const id_ProjectList = '(redacted)'; // Google Drive file ID of Project List'
//const id_TaskList = ''; //Google Drive file ID of Task List
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sht_Master = ss.getSheetByName(name_Master);
var sht_Guide = ss.getSheetByName(name_Guide);
var sht_Lists = ss.getSheetByName(name_Lists);
var sht_Access = ss.getSheetByName(name_Access);
var sht_ActionForm = ss.getSheetByName(name_ActionForm);
var sht_ProjectForm = ss.getSheetByName(name_ProjectForm);
var sht_NotesForm = ss.getSheetByName(name_NotesForm);
var sht_AdminForm = ss.getSheetByName(name_AdminForm);
var sht_GroupAdmin = ss.getSheetByName(name_GroupAdmin);
//Sheets Normally Displayed
//Immediately activate Master sheet after making visible to minimize confusion
sht_Master.showSheet();
sht_Master.activate();
sht_Guide.showSheet();
//Sheets Normally Hidden
sht_Lists.hideSheet();
sht_Access.hideSheet();
sht_ActionForm.hideSheet();
sht_ProjectForm.hideSheet();
sht_NotesForm.hideSheet();
sht_AdminForm.hideSheet();
sht_GroupAdmin.hideSheet();
}
Try this:
function showAllSheets() {
var sheets=SpreadsheetApp.getActiveSpreadsheet().getSheets();
for(var i = 0; i < sheets.length; i++){
sheets[i].showSheet();
SpreadsheetApp.flush();
console.log('Showing Sheet: ' + sheets[i].getName())
}
}
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 know there are a lot of questions out there related to Google Scripts and some of them touch on my issue but I can't seem to figure out the full answer.
I only want to be able to run 1 or 2 scripts in the same project file for 1 spreadsheet. How do I allow permission for everyone with whom the sheet is shared WITHOUT forcing each user to go through the process of allowing Authorization? Is this possible?
Maybe there is an alternative script? Here is my script (in case this helps):
function onOpen() {
var sheet = SpreadsheetApp.getActiveSpreadsheet();
var entries = [{name:"Go to active row", functionName:"Go to active
row"}];
sheet.addMenu("Scripts", entries);
myFunction();
};
function gotoend() {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
var lastRow = sheet.getDataRange().getValues();
lastRow.forEach(function (row,index) {
if (row[3] == "") { // row[0] is the same as Column A. row[1] = B
lastRow.length = index;
}
});
var newRange = sheet.getRange(lastRow.length,1);
sheet.setActiveRange(newRange);
}
/**
* #OnlyCurrentDoc
*/
I have a google spreadsheet that i have one last problem i cant seem to solve.
i added a button to this script, and when i press the button it triggers the AddClient function.
How can i make the script below loop down all rows in column 3 searching for the yes value, when it finds it, copy the row below it to sheet "client" and then stop?
function AddClient(event) {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var s = event.source.getActiveSheet();
var r = event.source.getActiveRange();
if(s.getName() == "SETUP" && r.getColumn() == 3 && r.getValue() == "yes") {
var row = r.getRow() + 1; // Add 1 to the active row
var targetSheet = ss.getSheetByName("client");
var target = targetSheet.getRange(targetSheet.getLastRow() + 1, 1);
s.getRange(row, 2, 1, 4).copyTo(target, {contentsOnly:true}); //Only selecting column 2, then the following 4 columns
}
}
Edit*
Example document:
https://docs.google.com/spreadsheets/d/1DFbAp0IN8_UFv9u8kWiZBTxDogj4e7FnuAPzC0grDw0/edit?usp=sharing
Any help greatly appreciated!
Cheers,
Reiel
Since you have a static form the position of the informatin to be copied will not change
Since we know we want to copy over the data we won't need to do any validation of where we are so all of that stuff can go
Sheets have an appendRow method that take care of the bookkeeping involved with finding the last row
This allows us to simplify the script to the following:
function AddClient() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var data = ss.getSheetByName("SETUP").getRange(25, 2, 1, 4).getValues()[0];
ss.getSheetByName("client").appendRow(data);
}
Edit:
To remove duplicates you could do the following:
function AddClient() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var data = ss.getSheetByName("SETUP").getRange(25, 2, 1, 4).getValues()[0];
var clients = ss.getSheetByName("client").getDataRange().getValues();
if (!clients.filter(function(row) {return data.join("|") === row.join("|");})) {
ss.getSheetByName("client").appendRow(data);
}
}
Note that for the particular example there are some problems because the leading zero gets cut off. Sheets is a bit weird and sometimes tries to force the format to be a number even when you set the cells' formats to Text...