I'm new with Google Apps Script Programming. I have piece mealed this code from other answers on S.O. Am I doing something wrong? I call the msg(s) routine from Google sheet, and nothing pops up. Any help is appreciated. Thx.
function msg(s) { //*************************************************************
var app = UiApp.getActiveApplication();
var pnl = app.createHorizontalPanel().setStyleAttribute('zIndex', '1') ;
var text = app.createTextArea().setName("text");
var handler = app.createServerHandler("count").addCallbackElement(text);
pnl.add(text);
pnl.add(app.createButton("Count", handler));
pnl.add(app.createLabel("0 characters").setId("label"));
return pnl;
}
function count(eventInfo) {
var app = UiApp.createApplication();
app.getElementById("label").setText(eventInfo.parameter.text.length+" characters");
return app;
}
You need to take steps to display your user interface. Read Displaying a User Interface from a Spreadsheet.
var spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
spreadsheet.show(app);
Related
need your help again..
so I have many student. and every week we have exam.
so I want to make individual Graphic of their progress for every week.. and send it ass attachment with autocrat to their email.
my problem is
how do I make an individual graphic continuedly (i mean not one by one change the name)
how to save that individual graphic to google drive
how to get the link image on google drive
those 3 problem is to make report for my student one by one like this pic
please share idea with me.. I'm really thank you for helping me here..
here the spreadsheet link : https://docs.google.com/spreadsheets/d/1fmS7PM65CMGGe5g00ojqqiK2CFr5HFgo_6L_Qki7QLw/edit#gid=1364826426
I can't understand and solve your problem all, since your problem seems a bit too complex.
However, I know how to save a sheet as a PDF easier and get the PDF's URL automatically.
It needs a script program.
I made a sample program based on another answer.
This is my code.
function exportPDF(){
// get parameters
var sheet = SpreadsheetApp.getActiveSheet();
var sheetName = sheet.getRange("B2").getValue();
var fileName = sheet.getRange("B3").getValue();
sheet.getRange("B4").setValue("");
// export the sheet
var fileUrl = exportSheet(sheetName, fileName);
// show the url
sheet.getRange("B4").setValue(fileUrl);
}
// based on https://stackoverflow.com/questions/38335143/export-single-sheet-to-pdf-in-apps-script
function exportSheet(sheetName, fileName) {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheets = ss.getSheets();
var fileUrl = "";
try{
for (var i = 0; i < sheets.length; i++) {
if (sheets[i].getSheetName() !== sheetName) {
sheets[i].hideSheet()
}
}
var file = DriveApp.createFile(ss.getBlob());
file.setName(fileName);
file.setSharing(DriveApp.Access.ANYONE, DriveApp.Permission.VIEW);
fileUrl = file.getUrl();
}
catch(e){
console.log(e);
throw e;
}
finally{
for (var i = 0; i < sheets.length; i++) {
sheets[i].showSheet()
}
}
return fileUrl;
}
If this can help, check this sample sheet and make a copy. ([File]-[Make a copy])
https://docs.google.com/spreadsheets/d/1lCxNSaXsd9tno4xdog6Usa0l1SZeuXrc62Oi6TK-VsY/edit?usp=sharing
I have created three charts in the spreadsheet for you.
Save or publish your chart
I have found an extremely good google script for a google form that allows me to retrieve the answers of a google form in a spreadsheet along with the "getEditResponseUrl()" that will help me build a database that can be modified through the google form.
However, I am struggling in getting the url of an "upload file question". The question where the user is supposed to upload a file is the fourth one "facture".
The answer I get in my spreadsheet is only "[Ljava.lang.Object;#508c8b8b" but not the file.
Any idea how to make this work so that I get the url of the file that was uploaded by the user ?
Thank you and Kind regards
var ss = SpreadsheetApp.openById("spreadsheeturl"); // to be customised
var responseSheet = "Database"; // to be customised
function submitFormFunc(e) {
var items = e.response.getItemResponses();
var responses={};
for(var i = 0; i< items.length; i++) {
responses[items[i].getItem().getTitle()]=items[i].getResponse();
}
var responseRow = [];
responseRow.push(e.response.getTimestamp().toString());
responseRow.push(e.response.getId());
responseRow.push(responses["Challenge"]); // to be customised
responseRow.push(responses["Client"]);
responseRow.push(responses["Date"]);
responseRow.push(responses["Facture"]);
// to be customised
// add as many as needed
responseRow.push(FormApp.getActiveForm().getResponse(e.response.getId()).getEditResponseUrl());
var isNewItem = alreadyExist(ss,e.response.getId());
if(isNewItem<0){
ss.getSheetByName(responseSheet).appendRow(responseRow);
}
else{
ss.getSheetByName(responseSheet).getRange(isNewItem+2, 1, 1, 6).setValues([responseRow]);
}
}
function alreadyExist(ss,id){
var ids = ss.getSheetByName(responseSheet).getRange("B2:B").getValues();
for(var i=0; i< ids.length; i++){
if(ids[i][0]===id){
return(i);
}
}
return(-1);
}
Here's a snippet from the Form Notifications add-on that converts the uploaded file ids into Google Drive URLs and concatenates the result in case of multiple uploads.
var urls = answer.toString().split(",").map(function(f) {
return "https://drive.google.com/open?id=" + f;
}).join(", ");
I'm need a way to save the current page (including, images, CSS, etc.) from an add-on.
Of course I found the saveDocument() function in the SDK but I was not able to make it work.
from add-on script, I do not have access to actual DOM content
from content script, I do not have access to SDK function 'saveDocument()'
I miss something, I would be very happy if someone could help me.
Best regards,
Fred
here's one way to access the DOM document from addon script.
var winutil = require('sdk/window/utils');
function findDocument(predicate){
// searching in focused window only
// you can also get all windows with winutil.windows('navigator:browser')
var win = winutil.getMostRecentBrowserWindow();
var gBrowser = win.gBrowser;
// traverse tabs of focused window
for (var i=0, l=gBrowser.browsers.length; i<l; i++)
{
var br = gBrowser.getBrowserAtIndex(i);
var doc = br.contentDocument;
if (predicate(doc))
return doc;
}
}
// and this is probably how you would save (not tested)
var {Cc, Ci} = require('chrome');
var wbp = Cc["#mozilla.org/embedding/browser/nsWebBrowserPersist;1"]
.createInstance(Ci.nsIWebBrowserPersist);
var doc = findDocument(/* whatever */);
if (doc)
wbp.saveDocument(doc, /* figure out other args */);
I'm trying to code on google app and I've encountered one issue. For example, let's consider this example code on google website ( link's here https://developers.google.com/apps-script/class_formpanel )
function doGet() {
var app = UiApp.createApplication();
var form = app.createFormPanel();
var flow = app.createFlowPanel();
flow.add(app.createTextBox().setName("textBox"));
flow.add(app.createListBox().setName("listBox").addItem("option 1").addItem("option 2"));
flow.add(app.createSubmitButton("Submit"));
form.add(flow);
app.add(form);
return app;
}
function doPost(eventInfo) {
var app = UiApp.getActiveApplication();
app.add(app.createLabel("Form submitted. The text box's value was '" +
eventInfo.parameter.textBox +
"' and the list box's value was '" +
eventInfo.parameter.listBox + "'"));
return app;
}
My issue is that I want to select multiple values on the listbox. I change then line 6 in
flow.add(app.createListBox(true).setName("listBox").addItem("option 1").addItem("option 2"));
to allow multiple selection. But the result is that only the last selected value is taken, preventing multiple selections. Apparently, it is due to the submitButton. I need to keep the formPanel because on a further code I'll like to combine uploading files and listBox multiple selection. How may I fix that? Thank you a lot
As a complement to Mogsdad's answer, note that this bug / issue is only concerning the doPost structured handler... if you don't need the file upload feature you could use a simple doGet + handler with callbackElement and in this case the multiselect list is available and works as expected.
test function :
function doGet() {
var app = UiApp.createApplication().setTitle('test listBox');
var panel = app.createHorizontalPanel().setStyleAttribute('padding','40px');
var sHdlr = app.createServerHandler('validateList').addCallbackElement(panel);
var items = ['category 1','category 2','category 3'];
var list1 = app.createListBox(true).setName('list1');
for(var i =0;i<items.length;++i){list1.addItem(items[i],i)}
panel.add(list1).add(app.createButton('validate',sHdlr));
app.add(panel);
return app;
}
function validateList(e){
var app = UiApp.getActiveApplication();
app.add(app.createLabel("Value(s) in list : "+e.parameter.list1).setStyleAttribute('margin-left','40'));
return app;
}
and below is a working example of the workaround described in the issue tracker.
I used a textBox to show the process, set it to visible(false) or use a hidden widget in a 'real' app. ( Test available here )
function doGet() {
var app = UiApp.createApplication().setTitle('test listBox');
var panel = app.createHorizontalPanel().setStyleAttribute('padding','40px');
var listHandler = app.createServerHandler('updlistVal').addCallbackElement(panel);
var items = ['category 1','category 2','category 3'];
var list1 = app.createListBox(true).setName('list1').addChangeHandler(listHandler);
for(var i =0;i<items.length;++i){list1.addItem(items[i])}
panel.add(list1).add(app.createTextBox().setText(items[0]).setId('listboxVal').setName('listboxVal').setWidth('200'));// set a default value in case the user is happy with that and doesn't touch the listBox
var submitBtn = app.createSubmitButton('test').setStyleAttribute('margin-left','40');
app.add(app.createFormPanel().add(panel.add(submitBtn)));
return app;
}
function doPost(e){
var app = UiApp.getActiveApplication();
app.add(app.createLabel().setStyleAttribute('padding','40').setText("Submitted value(s) from list : "+e.parameter.listboxVal));
return app;
}
function updlistVal(e){
var app = UiApp.getActiveApplication();
app.getElementById('listboxVal').setValue(e.parameter.list1);
return app;
}
EDIT 2 :
As Mentioned in the comments on this post we must find a way to prevent going through the submission before the value of the hidden/text widget has been updated with a valid value. The "default value" I used above is a possible solution, another one is to use a client handler to validate the submit button only if the listValue (or its value in the hidden widget) is right. Here is a code that does it (only the doGet is reproduced, all other functions being identical.
function doGet() {
var app = UiApp.createApplication().setTitle('test listBox');
var panel = app.createHorizontalPanel().setStyleAttribute('padding','40px');
var submitBtn = app.createSubmitButton('test').setStyleAttribute('margin-left','40').setEnabled(false);
var listHandler = app.createServerHandler('updlistVal').addCallbackElement(panel);
var items = ['category 1','category 2','category 3'];
var listVal = app.createTextBox().setText('not defined yet').setId('listboxVal').setName('listboxVal').setWidth('200');
var list1 = app.createListBox(true).setName('list1').addChangeHandler(listHandler).addItem('choose one or more item(s)');
for(var i =0;i<items.length;++i){list1.addItem(items[i])}
var clientH = app.createClientHandler().forTargets(submitBtn).setEnabled(true).validateMatches(list1, 'category');
list1.addClickHandler(clientH);
panel.add(list1).add(listVal);// set a default value in case the user is happy with that and doesn't touch the listBox
app.add(app.createFormPanel().add(panel.add(submitBtn)));
return app;
}
Using the hidden widget as validation source causes a small issue as we need to click twice on the listBox to make it work... in case there are other questions on the form this will be solved by triggering the client handler with every other widgets so that the double click won't be necessary anymore but this is becoming a bit "out of subject" I'm afraid.
EDIT 3 :
just for the fun of it, a last version that works apparently without issue...
test here
function doGet() {
var app = UiApp.createApplication().setTitle('test listBox');
var panel = app.createHorizontalPanel().setStyleAttribute('padding','40px');
var submitBtn = app.createSubmitButton('test').setStyleAttribute('margin-left','40').setEnabled(false).setId('sbmt');
var wait = app.createImage('https://dl.dropboxusercontent.com/u/211279/loading3T.gif').setId('wait').setVisible(false);
var listHandler = app.createServerHandler('updlistVal').addCallbackElement(panel);
var items = ['category 1','category 2','category 3'];
var listVal = app.createTextBox().setText('not defined yet').setId('listboxVal').setName('listboxVal').setWidth('200');
var list1 = app.createListBox(true).setName('list1').addChangeHandler(listHandler).addItem('choose one or more item(s)');
for(var i =0;i<items.length;++i){list1.addItem(items[i])}
var clientH = app.createClientHandler().forTargets(wait).setVisible(true).forTargets(submitBtn).setEnabled(false);
list1.addChangeHandler(clientH);
panel.add(list1).add(listVal);// set a default value in case the user is happy with that and doesn't touch the listBox
app.add(app.createFormPanel().add(panel.add(submitBtn).add(wait)));
return app;
}
function doPost(e){
var app = UiApp.getActiveApplication();
app.add(app.createLabel().setStyleAttribute('padding','40').setText("Submitted value(s) from list : "+e.parameter.listboxVal));
return app;
}
function updlistVal(e){
var app = UiApp.getActiveApplication();
app.getElementById('listboxVal').setValue(e.parameter.list1);
app.getElementById('sbmt').setEnabled(true);
app.getElementById('wait').setVisible(false);
return app;
}
This is a known bug in the issue tracker, Issue 959. Visit and star it for updates.
It's been known and "worked on" since Dec 2011, if you believe the notes added by the support team. Other users have provided a work-around, and a modified version of it appears below.
The idea is to attach a handler function to the ListBox, which will receive all the selected items from the ListBox. The handler will then write those values to a hidden element in the form. When the form is submitted, the list of selections will be available to the doPost(), via the hidden element.
...
var listbox = app.createListBox(true).setName("listBox").addItem("option 1").addItem("option 2");
flow.add(listbox);
// Issue 959 ListBox Workaround: http://code.google.com/p/google-apps-script-issues/issues/detail?id=959
var listboxHidden= app.createHidden("listboxHidden", "").setId("listboxHidden");
flow.add(listboxHidden);
var fixListBoxHandler = app.createServerHandler('fixListBoxHandler');
fixListBoxHandler.addCallbackElement(listbox);
listbox.addChangeHandler(fixListBoxHandler);
...
}
function fixListBoxHandler(e) {
var app = UiApp.getActiveApplication();
app.getElementById('listboxHidden').setValue(e.parameter.listbox);
return app;
}
NOTE: Unfortunately, this work-around is time-sensitive; it can take several seconds for the handler to update the hidden value. If the submit button is hit before the handler completes its job, then post() receives what was in the hidden element before the call to the handler.
i`m trying use Twitter Stream Api for searching some hashtags in Google Spreadsheet. Twitter search api useless cause i wanna trak retweet count too. My function sample here. Can anybody explain me what i must do for working well..
function miniSearch(){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sumSheet = ss.getSheetByName("Readme/Settings");
// Authorize to Twitter
var oauthConfig = UrlFetchApp.addOAuthService("twitter");
oauthConfig.setAccessTokenUrl("https://api.twitter.com/oauth/access_token");
oauthConfig.setRequestTokenUrl("https://api.twitter.com/oauth/request_token");
oauthConfig.setAuthorizationUrl("https://api.twitter.com/oauth/authorize");
oauthConfig.setConsumerKey(TWITTER_CONSUMER_KEY);
oauthConfig.setConsumerSecret(TWITTER_CONSUMER_SECRET);
// "twitter" value must match the argument to "addOAuthService" above.
var options = {
'method': 'POST',
"oAuthServiceName" : "twitter",
"oAuthUseToken" : "always"
};
var url = "https://stream.twitter.com/1/statuses/filter.json?track="+"twitterapi";
var response = UrlFetchApp.fetch(url, options);
var tweets = JSON.parse(response.getContentText());
sumSheet.getRange('B8').setValue(tweets[0]["text"]);
}
this function return error code 504;
I don't think Google Apps Script can keep a persistent HTTP connection open which is resulting in the 504 (See Twitter Streaming APIs Doc)
[I've a basic retweet counter in this Google Spreadsheet Template (TAGS v4.0). The filterUnique formula uses this code (the pseudocode is strip out any links from tweet text then extract 1st 90% of text (to take account of any old style RT+ annotation), then if not in unique array add or add 1 to existing value):
function filterUnique(tweets){
var output = [];
var temp = {};
for (i in tweets){
if (i>0){
var tmp = tweets[i][0];
var urlPattern = /(\b(https?|ftp|file):\/\/[-A-Z0-9+&##\/%?=~_|!:,.;]*[-A-Z0-9+&##\/%=~_|])/ig;
tmp = tmp.replace(urlPattern,"")
tmp = tmp.substring(0,parseInt(tmp.length*0.9));
if (temp[tmp] == undefined){
temp[tmp] = [tweets[i][0],0];
}
temp[tmp] = [tweets[i][0],temp[tmp][1]+1];
}
}
for (i in temp){
output.push([temp[i][0],temp[i][1]]);
}
output.sort(function(a,b) {
return b[1]-a[1];
});
return output.slice(0, 12);
}
]