Button firing only once Google Apps Script - google-apps-script

I'm trying to get a button to fire more than once(I.e. multiple clicks) but it seems to not be working. Any help would be greatly appreciated. Theoretically it should fire multiple times but it only fires once when I check it in the logs
function a
{
...
var addcolumnspanel = app.createHorizontalPanel().setId("addcolumnspanel");
var addbuttonhandler = app.createServerHandler("addcolumn");
var addbutton = app.createButton().setId("btnaddcolumn").setText("Add another column").addClickHandler(addbuttonhandler);
...
return app;
}
function addcolumn()
{
...//Do some stuff
return app;
}
Here is a more precise code example
//set global var
var counter = 1;
var ss = SpreadsheetApp.getActiveSpreadsheet();
//Set up the Menu bar
function onOpen() {
// Logger.clear();
var menuEntries = [ {name: "demo", functionName: "myFunction"}];
ss.addMenu("Demo", menuEntries);
}
function myFunction() {
var myapp = UiApp.createApplication().setHeight(430).setWidth(800);;
var button = myapp.createButton("Clicky");
var myhandler = myapp.createServerHandler("secondfunction");
var myhandler = button.addClickHandler(myhandler)
myapp.add(button);
ss.show(myapp);
}
function secondfunction()
{
counter++;
Logger.log(counter);
}

OK. The problem is with the counter variable. You cannot use global variables that way in Apps Script. See this answer for more information. You can use ScriptProperties or CacheService as an alternative.
var ss = SpreadsheetApp.getActiveSpreadsheet();
//Set up the Menu bar
function onOpen() {
// Logger.clear();
var menuEntries = [ {name: "demo", functionName: "myFunction"}];
ss.addMenu("Demo", menuEntries);
// Store your counter in CacheService as a string.
CacheService.getPrivateCache().put('counter','1',3600);
}
function myFunction() {
var myapp = UiApp.createApplication().setHeight(430).setWidth(800);;
var button = myapp.createButton("Clicky");
var myhandler = myapp.createServerHandler("secondfunction");
var myhandler = button.addClickHandler(myhandler)
myapp.add(button);
ss.show(myapp);
}
function secondfunction()
{
var cache = CacheService.getPrivateCache();
var counter = parseInt(cache.get('counter'));
counter ++;
cache.put('counter', counter.toString());
Logger.log(counter);
}

Related

How can I skip the first page in this script? GoogleSheets

I have this script, tell me how to make sure that the first page is not affected? Thank you very much
function onOpen() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var menubuttons = [ {name: "Сleaning" , functionName: "CleanAllSheets"},];
ss.addMenu("Сleaning", (menubuttons));
}
function CleanAllSheets() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
ss.getSheets().forEach((sheet) => {CleanSheet(sheet);})
}
function CleanSheet(sheet) {
sheet.getRange('A2:Z100').clearContent();
}
You can use slice to shorten the array of sheets to be deleted:
function onOpen() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var menubuttons = [ {name: "Сleaning" , functionName: "CleanAllSheets"},];
ss.addMenu("Сleaning", (menubuttons));
}
function CleanAllSheets() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var l = ss.getSheets().length;
ss.getSheets().slice(1,l).forEach((sheet) => {CleanSheet(sheet);})
}
function CleanSheet(sheet) {
sheet.getRange('A2:Z100').clearContent();
}
Include the index in your forEach and skip if zero.
ss.getSheets().forEach((sheet, i) => { if (i != 0) CleanSheet(sheet); })

google sheets UiApi to htmlservice script file upload

I got the problem since google isnt supporting UIapi anymore i cant use the code below. Could someone help me with it and re-edit to html service? I have no clue about any of those stuff. Code was copied from other site long time ago. Tryed to find a solution for the last 2 days and nothing. Would be really greatfull.
regards
// upload document into google spreadsheet
// and put link to it into current cell
function onOpen(e) {
var ss = SpreadsheetApp.getActiveSpreadsheet()
var menuEntries = [];
menuEntries.push({name: "", functionName: "doGet"});
ss.addMenu("", menuEntries);
}
function doGet(e) {
var app = UiApp.createApplication().setTitle("");
SpreadsheetApp.getActiveSpreadsheet().show(app);
var form = app.createFormPanel().setId('frm').setEncoding('multipart/form-data');
var formContent = app.createVerticalPanel();
form.add(formContent);
formContent.add(app.createFileUpload().setName('thefile'));
// these parameters need to be passed by form
// in doPost() these cannot be found out anymore
formContent.add(app.createHidden("activeCell", SpreadsheetApp.getActiveRange().getA1Notation()));
formContent.add(app.createHidden("activeSheet", SpreadsheetApp.getActiveSheet().getName()));
formContent.add(app.createHidden("activeSpreadsheet", SpreadsheetApp.getActiveSpreadsheet().getId()));
formContent.add(app.createSubmitButton(''));
app.add(form);
SpreadsheetApp.getActiveSpreadsheet().show(app);
return app;
}
function doPost(e) {
var app = UiApp.getActiveApplication();
app.createLabel('');
var fileBlob = e.parameter.thefile;
var doc = DriveApp.getFolderById('0BzI2pkyLXZ5maWo5b2Uyb3JWdzQ').createFile(fileBlob);
var label = app.createLabel('');
// write value into current cell
var value = 'hyperlink("' + doc.getUrl() + '";"' + doc.getName() + '")'
var activeSpreadsheet = e.parameter.activeSpreadsheet;
var activeSheet = e.parameter.activeSheet;
var activeCell = e.parameter.activeCell;
var label = app.createLabel('');
app.add(label);
SpreadsheetApp.openById(activeSpreadsheet).getSheetByName(activeSheet).getRange(activeCell).setFormula(value);
app.close();
return app;
}

Copy single worksheet to multiple workbooks within Google Drive

I am trying to copy a single worksheet to multiple workbooks within Google Drive. Just over a month ago, this code worked fine but now it doesn't because I'm thinking that the DocsList command within the code has been deprecated by Google (see https://developers.google.com/apps-script/sunset). I've heard that the new command is DriveApp but I'm not too sure. I'm a total newbie when it comes to writing code so I'm hoping someone can please fix this code?
function onOpen(){
var spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
var menuEntries = [];
menuEntries.push({name: "Copy Active Sheet to Other Spreadsheets", functionName: "doGet"});
spreadsheet.addMenu("Copy Sheet", menuEntries);
}
function doGet()
{
var app = UiApp.createApplication();
app.setTitle("Kishan - Copy Sheet in Multiple Spreadsheets");
var form = app.createFormPanel();
var flow = app.createFlowPanel();
var label = app.createLabel("Select Spreadsheet where you want to copy the current sheet:").setId('selectLabel');
flow.add(label);
var allfiles = DocsList.getAllFiles();
var verticalPanel = app.createVerticalPanel().setId('verticalPanel');
for(var i=0;i<allfiles.length;i++)
{
var temp = app.createCheckBox(allfiles[i].getName()).setName('cb'+i).setId('cb'+i);
var tempvalue = app.createHidden('cbvalue'+i, allfiles[i].getId());
verticalPanel.add(temp);
verticalPanel.add(tempvalue);
}
var scrollPanel = app.createScrollPanel().setId('scrollPanel');
scrollPanel.add(verticalPanel);
scrollPanel.setSize("400", "250")
flow.add(scrollPanel);
var buttonsubmit = app.createSubmitButton("Copy");
flow.add(buttonsubmit);
form.add(flow);
app.add(form);
SpreadsheetApp.getActiveSpreadsheet().show(app);
}
function doPost(eventInfo) {
var app = UiApp.getActiveApplication();
var allfiles = DocsList.getAllFiles();
var tempSsId = "";
for(var i=0;i<allfiles.length;i++)
{
var temp = eventInfo.parameter['cb'+i];
if(temp == 'on')
{
tempSsId = eventInfo.parameter['cbvalue'+i];
var activeSheet = SpreadsheetApp.getActiveSheet().copyTo(SpreadsheetApp.openById(tempSsId));
activeSheet.setName(SpreadsheetApp.getActiveSheet().getSheetName());
}
}
var label = app.createLabel('statusLabel');
label.setText("Copied Active sheet in all selected Spreadsheets...");
label.setVisible(true);
app.add(label);
return app;
}
This is the original code from [http://igoogledrive.blogspot.ca/2012/10/Google-Spreadsheet-Script-to-Copy-Sheet-to-multiple-Spreadsheets-at-a-time.html][1] that was working fine for me before but now isn't. Can anyone help?
Try this
function doGet()
{
var app = UiApp.createApplication();
app.setTitle("Kishan - Copy Sheet in Multiple Spreadsheets");
var form = app.createFormPanel();
var flow = app.createFlowPanel();
var label = app.createLabel("Select Spreadsheet where you want to copy the current sheet:").setId('selectLabel');
flow.add(label);
var allfiles = DriveApp.getFiles();
var verticalPanel = app.createVerticalPanel().setId('verticalPanel');
var i = 0;
while (allfiles.hasNext())
{
var file = allfiles.next();
var temp = app.createCheckBox(file.getName()).setName('cb'+i).setId('cb'+i);
var tempvalue = app.createHidden('cbvalue'+i, file.getId());
verticalPanel.add(temp);
verticalPanel.add(tempvalue);
i++;
}
var scrollPanel = app.createScrollPanel().setId('scrollPanel');
scrollPanel.add(verticalPanel);
scrollPanel.setSize("400", "250")
flow.add(scrollPanel);
var buttonsubmit = app.createSubmitButton("Copy");
flow.add(buttonsubmit);
form.add(flow);
app.add(form);
SpreadsheetApp.getActiveSpreadsheet().show(app);
}
function doPost(eventInfo) {
var app = UiApp.getActiveApplication();
var allfiles = DriveApp.getFiles();
var tempSsId = "";
var i = 0;
while (allfiles.hasNext())
{
var temp = eventInfo.parameter['cb'+i];
if(temp == 'on')
{
tempSsId = eventInfo.parameter['cbvalue'+i];
var activeSheet = SpreadsheetApp.getActiveSheet().copyTo(SpreadsheetApp.openById(tempSsId));
activeSheet.setName(SpreadsheetApp.getActiveSheet().getSheetName());
}
i++;
}
var label = app.createLabel('statusLabel');
label.setText("Copied Active sheet in all selected Spreadsheets...");
label.setVisible(true);
app.add(label);
return app;
}

Can't be called doPost function by shared users in Google Spreadsheets

Now, I'm creating a mail sending application with Google Apps Script in my Google Spreadsheet.
This app do following actions.
Add a menu to call this application when the spreadsheet is opened.
Display UI to input mail subject when the menu is clicked.
Send mails to each addresses contained in the spreadsheet when submit button push by the spreadsheet opend user.
Problem
The app work fine when i use.
But the other users who can edit the spreadsheet can not send emails (display ui is ok).
From dubug logs, I've found that the doPost function is not called.
Please tell me what is the problem.
Program
/*
* #OnlyCurrentDoc
*
* create custome menu in active spreadsheet
*/
function onOpen(e) {
Logger.log("onOpen called");
var sheet = SpreadsheetApp.getActiveSpreadsheet();
var menuEntries = [
{name: "MailSendApp", functionName: "displayAppUi"}
];
sheet.addMenu("CustomAppMenu", menuEntries);
}
/*
* #OnlyCurrentDoc
*
* display ui
*/
function displayAppUi() {
Logger.log("displayAppUi called");
var sheet = SpreadsheetApp.getActiveSpreadsheet();
var app = UiApp.createApplication();
var formPanel = app.createFormPanel().setId('form').setEncoding('multipart/form-data');
var scrollPanel = app.createScrollPanel();
var panel = app.createVerticalPanel();
var appLabel = app.createLabel("Sendmails SpreadSheet App");
var subjectLabel = app.createLabel("Subject");
var subjectTextBox = app.createTextBox().setName("subject");
var bodyLabel = app.createLabel("Body");
var bodyTextArea = app.createTextArea().setName("body");
var attachFile = app.createFileUpload().setName('attachFile');
var sendBtn = app.createSubmitButton().setText("Send ALL");
var sheetName = app.createHidden("sheetName", SpreadsheetApp.getActiveSheet().getName());
// set client handler
var submitClientHandler = app.createClientHandler()
.forEventSource().setEnabled(false)
.forEventSource().setText("Sending...");
sendBtn.addClickHandler(submitClientHandler)
// set sytles
app.setWidth(800);
app.setHeight(400);
panel.setStyleAttributes({width: "100%"});
appLabel.setStyleAttributes({color: "green", fontSize: "2em" });
subjectTextBox.setStyleAttributes({width: "100%"});
bodyTextArea.setHeight("240");
bodyTextArea.setWidth("780");
scrollPanel.setHeight("380");
scrollPanel.setWidth("800");
panel.add(appLabel)
.add(subjectLabel)
.add(subjectTextBox)
.add(bodyLabel)
.add(bodyTextArea)
.add(attachFile)
.add(sheetName)
.add(sendBtn);
scrollPanel.add(panel)
formPanel.add(scrollPanel);
app.add(formPanel);
sheet.show(app);
}
/*
* #OnlyCurrentDoc
*
* handler to send emails to addresses in active sheet with input subject & body.
*/
function doPost(e) {
Logger.log("doPost called");
var param = e.parameter;
var subject = param.subject;
var body = param.body;
var attachFile = param.attachFile;
var sheetName = param.sheetName;
var mailOptions = {};
var mailAttachments = []
// set attachment file if exists
if (attachFile != null) {
mailAttachments.push({
fileName: attachFile.getName(),
mimeType: attachFile.getContentType(),
content: attachFile.getBytes()
});
}
mailOptions["attachments"] = mailAttachments;
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(sheetName);
var emails = _collectEmailAddresses(sheet);
for(var i=0; i < emails.length; i++) {
var email = emails[i];
GmailApp.sendEmail(email, subject, body, mailOptions);
}
var app = UiApp.getActiveApplication();
app.close();
return app;
}
/*
* collect Email addresses from the argument sheet
*/
function _collectEmailAddresses(sheet) {
var emails = [];
var range = sheet.getDataRange();
var values = range.getValues();
// get skip row numbers(that is header row num) from property
var header_rownum = parseInt(PropertiesService.getScriptProperties().getProperty("header_rownum"));
for (var i = header_rownum; i < range.getLastRow(); i++) {
var email = values[i][1];
emails.push(email);
}
return emails;
}
Use openByID instead of .getActiveSpreadsheet().getSheetByName(sheetName) in your doPost trigger.
var ss = SpreadsheetApp.openById(_spreadsheetId);
var sheet = ss.getSheetByName(_sheetName);

Loop with timeout protection that only runs once "as it should"

I wrote a script that restores calendar data from a backup written in a spreadsheet. Since the amount of data is highly unpredictable I designed a loop that stops after a given number of minutes , asking the user to continue or to cancel while showing the actual counter state (this to prevent issue with Google max execution time limit).
It works pretty well but in this simplified test script I used for testing the idea it works only once : when the first "timeout" occurs, it shows the continue/cancel option as expected and then continues from where it started but when the same condition happens for the second time the continue button doesn't shows up.
My question is simply : why ? or better : what's the difference between both situations ?
The spreadsheet with embedded script is publicly testable here (see menu : test)
and the whole script is shown below (it's a bit long of course but the interresting part is near the end)
I used ScriptProperties to keep track of execution time and to continue the loop from where I left.
function onOpen() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var menuEntries = [
{name: "test", functionName: "test"},
];
ss.addMenu("test", menuEntries);
}
function test(){
ScriptProperties.setProperty('restorePointers',[0,0].join('#'))
var app = UiApp.createApplication().setTitle("test");
app.setHeight(150).setWidth(250);
var doc = SpreadsheetApp.getActiveSpreadsheet();
var panel = app.createVerticalPanel();
var handlerCancel = app.createServerHandler('canceltest');
var handlerContinue = app.createServerHandler('continuetest');
var contCHandler = app.createClientHandler();
var cancel = app.createButton("cancel.", handlerCancel).setId('cancel').setVisible(false);
var cont = app.createButton('continue',handlerContinue).setId('continue').setVisible(false).addClickHandler(contCHandler);
var button = app.createButton('start').setId('button');
var handler = app.createServerClickHandler('runtest');
handler.addCallbackElement(panel);
contCHandler.forTargets(button).setEnabled(false).forEventSource().setVisible(false);
var cHandler = app.createClientHandler().forTargets(cancel).setVisible(true).forEventSource().setVisible(false);
button.addClickHandler(handler).addClickHandler(cHandler);
app.add(panel.add(button).add(cont).add(cancel))//.add(trig));
doc.show(app);
}
function canceltest(e){
var app = UiApp.getActiveApplication();
ScriptProperties.setProperty('restoreData','')
ScriptProperties.setProperty('restorePointers','canceled');
SpreadsheetApp.getActiveSpreadsheet().toast(' ','restore aborted');
app.close()
return app;
}
function continuetest(e){
runtest(e)
}
function runtest(e){
var dStart; var dEnd;
ScriptProperties.setProperty('startrestore',new Date().getTime().toString())
if(ScriptProperties.getProperty('restoreData')==null||Utilities.jsonStringify(ScriptProperties.getProperties()).indexOf('restoreData')==-1)
{ScriptProperties.setProperty('restoreData',Utilities.jsonStringify(e))
}
var app = UiApp.getActiveApplication();
var pointers = ScriptProperties.getProperty('restorePointers');
if(pointers=='0#0'){
dStart = 0;
dEnd = 500;
}else{
dStart = Number(pointers.split('#')[0]);
dEnd = Number(pointers.split('#')[1]);
}
// main loop --------------------------
for(var ee=dStart;ee<dEnd;++ee){ // main loop
var ccc = ScriptProperties.getProperty('restorePointers');
if(ccc=='canceled'){ app.close();return app};
Utilities.sleep(85); // simulate some activity
if((ee/10)==parseInt(ee/10)&&ee>0){
SpreadsheetApp.getActiveSpreadsheet().toast(ee+' steps completed')
if(new Date().getTime()-Number(ScriptProperties.getProperty('startrestore'))>12000){ ;// +- 12 sec timeout
ScriptProperties.setProperty('restorePointers',[ee,dEnd].join('#'));
app.getElementById('continue').setHTML('continue from '+ee).setVisible(true)
return app
}
}
}
// end of main loop-----------------
ScriptProperties.setProperty('restoreData','')
ScriptProperties.setProperty('restorePointers',0+'#'+0);
SpreadsheetApp.getActiveSpreadsheet().toast('normal process end');
app.close();
return app;
}
Here's what's keeping the 'continue' button from updating with each interval. Your Server Handler needs to return an app:
function continuetest(e){
return runtest(e) ///<<<
}
This bit, (ee/10)==parseInt(ee/10) is an awkward way to evaluate true every 10th item. Use modulus instead:
if((ee%10==0)&&ee>0){ ///<<< modulus
After each pause, the value of ee is repeated in the toast. This can be fixed by remembering what the last displayed value was, and skipping it.
if (ee == Number(ScriptProperties.getProperty('lastToast'))) continue; ///<<< don't repeat toast
ScriptProperties.setProperty('lastToast',ee); ///<<<
Full script
function onOpen() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var menuEntries = [
{name: "test", functionName: "test"},
];
ss.addMenu("test", menuEntries);
}
function test(){
ScriptProperties.setProperty('restorePointers',[0,0].join('#'))
var app = UiApp.createApplication().setTitle("test");
app.setHeight(150).setWidth(250);
var doc = SpreadsheetApp.getActiveSpreadsheet();
var panel = app.createVerticalPanel();
var handlerCancel = app.createServerHandler('canceltest');
var handlerContinue = app.createServerHandler('continuetest');
var contCHandler = app.createClientHandler();
var cancel = app.createButton("cancel.", handlerCancel).setId('cancel').setVisible(false);
var cont = app.createButton('continue',handlerContinue).setId('continue').setVisible(false).addClickHandler(contCHandler);
var start = app.createButton('start').setId('start');
var handler = app.createServerClickHandler('runtest');
handler.addCallbackElement(panel);
contCHandler.forTargets(start).setEnabled(false).forEventSource().setVisible(false);
var cHandler = app.createClientHandler().forTargets(cancel).setVisible(true).forEventSource().setVisible(false);
start.addClickHandler(handler).addClickHandler(cHandler);
app.add(panel.add(start).add(cont).add(cancel))//.add(trig));
doc.show(app);
}
function canceltest(e){
var app = UiApp.getActiveApplication();
ScriptProperties.setProperty('restoreData','')
ScriptProperties.setProperty('restorePointers','canceled');
SpreadsheetApp.getActiveSpreadsheet().toast(' ','restore aborted');
app.close()
return app;
}
function continuetest(e){
return runtest(e) ///<<<
}
function runtest(e){
var dStart; var dEnd;
ScriptProperties.setProperty('startrestore',new Date().getTime().toString())
if(ScriptProperties.getProperty('restoreData')==null||Utilities.jsonStringify(ScriptProperties.getProperties()).indexOf('restoreData')==-1)
{ScriptProperties.setProperty('restoreData',Utilities.jsonStringify(e))
}
var app = UiApp.getActiveApplication();
var pointers = ScriptProperties.getProperty('restorePointers');
if(pointers=='0#0'){
dStart = 0;
dEnd = 500;
}else{
dStart = Number(pointers.split('#')[0]);
dEnd = Number(pointers.split('#')[1]);
}
// main loop --------------------------
for(var ee=dStart;ee<dEnd;++ee){ // main loop
var ccc = ScriptProperties.getProperty('restorePointers');
if(ccc=='canceled'){ app.close();return app};
Utilities.sleep(85); // simulate some activity
if((ee%10==0)&&ee>0){ ///<<< modulus
if (ee == Number(ScriptProperties.getProperty('lastToast'))) continue; ///<<< don't repeat toast
ScriptProperties.setProperty('lastToast',ee); ///<<<
SpreadsheetApp.getActiveSpreadsheet().toast(ee+' steps completed')
if(new Date().getTime()-Number(ScriptProperties.getProperty('startrestore'))>12000) { // +- 12 sec timeout
ScriptProperties.setProperty('restorePointers',[ee,dEnd].join('#'));
app.getElementById('continue').setHTML('continue from '+ee).setVisible(true)
return app
}
}
}
// end of main loop-----------------
ScriptProperties.setProperty('restoreData','')
ScriptProperties.setProperty('restorePointers',0+'#'+0);
SpreadsheetApp.getActiveSpreadsheet().toast('normal process end');
app.close();
return app;
}