Google Apps Script Stop Function from Running - google-apps-script

UPDATE: Added doGet function.
I have a web app that is used for a registration process. I created an email confirmation that sends the user an email when they click the Register button.
My question is, how do I stop the function from running if the email field is left blank? At this point when I run the whole script, an error message is shown when I click register if I don't enter an email address. I don't want the function to throw an error and I want to be able to still register.
function doGet(e) {
var app = UiApp.createApplication().setTitle('Education Registration');
app.setStyleAttribute("background", "#DBE8C4");
var panel1 = app.createAbsolutePanel().setId('panel1');
panel1.setHeight(900);
panel1.setWidth(1500);
//Register button
var dateSelection = app.createButton('Register').setSize(140, 40);
var loadingWait = app.createLabel('After clicking Register, please allow 5 - 30 seconds for the webpage to process the request.');
var clickHandler = app.createServerHandler("respondToDateSelection");
dateSelection.addClickHandler(clickHandler);
clickHandler.addCallbackElement(panel1);
//Email Handler
var emailHandler = app.createServerHandler("emailConfirmation");
dateSelection.addClickHandler(emailHandler);
emailHandler.addCallbackElement(panel1);
return app;
}
function emailConfirmation(e) {
var app = UiApp.getActiveApplication();
app.getElementById('fNameText1');
app.getElementById('lNameText1');
app.getElementById('eAddressText1');
app.getElementById('dataItemsLB');
app.getElementById('aemailAddressText');
var fNameText1 = e.parameter.fNameText1;
var lNameText1 = e.parameter.lNameText1;
var eAddressText1 = e.parameter.eAddressText1;
var dataItemsLB = e.parameter.dataItemsLB;
var aemailAddressText = e.parameter.aemailAddressText;
var subject = "Class Registration Confirmation - " + fNameText1 + " " + lNameText1;
var emailBody = "This is an Email Confirmation.";
MailApp.sendEmail(eAddressText1, subject,
emailBody, {cc: aemailAddressText});
return app;
}

There are many ways to do that, the most "elegant" way would probably be using a client handler validator in your doGet function but you didn't show it ...(there is a specific validator for emails)
Another way is to have a warning label in your UI that is initially invisible and that gets visible when e.parameter.eAddressText1 is not a valid email or is empty, the same condition would apply to the sendEmail command and skip it if not valid then return to the UI.
feel free to post your doGet function to allow for more accurate answer.
EDIT :
thanks for posting your code, although it was incomplete and needed some work to make it an interesting example, I end up with this code to illustrate the use of clientHandlers, validators and try/catch for email adress...
Here is the code, I changed it to work on a spreadsheet to avoid deploying and versioning, it is just a test, nothing more...
function doGet(e) {
var app = UiApp.createApplication().setTitle('Education Registration');
app.setStyleAttribute("background", "#DBE8C4");
var panel0 = app.createFlowPanel().setId('panel0');
var panel1 = app.createVerticalPanel().setId('panel1');
var fNameText1=app.createTextBox().setName('fNameText1').setId('fNameText1');;
var lNameText1=app.createTextBox().setName('lNameText1').setId('lNameText1');;
var eAddressText1=app.createTextBox().setName('eAddressText1').setId('eAddressText1').setText('mail')
var dataItemsLB=app.createTextBox().setName('dataItemsLB').setId('dataItemsLB');;
var aemailAddressText=app.createTextBox().setName('aemailAddressText').setId('aemailAddressText').setText('mail');
panel1.add(fNameText1).add(lNameText1).add(eAddressText1).add(dataItemsLB).add(aemailAddressText)
panel0.add(panel1)
//Register button
var dateSelection = app.createButton('Register').setSize(140, 40).setId('dateSelection');
var loadingWait = app.createLabel('After clicking Register, please allow 5 - 30 seconds for the webpage to process the request.').setVisible(false).setId('loadingWait');
var clickHandler = app.createServerHandler("respondToDateSelection").validateEmail(eAddressText1);
dateSelection.addClickHandler(clickHandler);
clickHandler.addCallbackElement(panel0);
//Email Handler
var emailHandler = app.createServerHandler("emailConfirmation").validateEmail(eAddressText1);
dateSelection.addClickHandler(emailHandler);
emailHandler.addCallbackElement(panel1);
//client handlers
var warning = app.createLabel('Please enter your email where necessary').setId('warning').setVisible(false).setStyleAttribute('background','yellow')
var clientHandlerwait = app.createClientHandler().forTargets(loadingWait).setVisible(true).validateEmail(eAddressText1)
var clientHandler1 = app.createClientHandler().validateNotEmail(eAddressText1)
.forTargets(warning).setVisible(true).forEventSource().setStyleAttribute('color','red')
var clientHandler2 = app.createClientHandler().validateNotEmail(aemailAddressText)
.forTargets(warning).setVisible(true).forEventSource().setStyleAttribute('color','red')
dateSelection.addClickHandler(clientHandlerwait).addClickHandler(clientHandler1).addClickHandler(clientHandler2)
app.add(panel1.add(dateSelection).add(loadingWait).add(warning))
SpreadsheetApp.getActive().show(app)
// return app;
}
function respondToDateSelection(){
return
}
function emailConfirmation(e) {
var app = UiApp.getActiveApplication();
app.getElementById('warning').setVisible(false);
app.getElementById('dateSelection').setStyleAttribute('color','black')
var fNameText1 = e.parameter.fNameText1;
var lNameText1 = e.parameter.lNameText1;
var eAddressText1 = e.parameter.eAddressText1;
var dataItemsLB = e.parameter.dataItemsLB;
var aemailAddressText = e.parameter.aemailAddressText;
var subject = "Class Registration Confirmation - " + fNameText1 + " " + lNameText1;
var emailBody = "This is an Email Confirmation.";
try{
// MailApp.sendEmail(eAddressText1, subject,emailBody, {cc: aemailAddressText});
Utilities.sleep(500)// simulate a duration to read the message
app.getElementById('loadingWait').setText('mail sent').setVisible(true)
}catch(err){
app.getElementById('loadingWait').setText('error sending mail').setVisible(true)
}
return app;
}

Related

How to get an google app script alert response to run different function?

I've gotten my EmailPDF script to run correctly. But what I want is an alert to popup asking if you want to send the report.
function responseToSend() {
var spreadSheet=SpreadsheetApp.getActiveSpreadsheet();
var s = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Sheet5');
var se = s.getRange('A1').getValue();
var ui = SpreadsheetApp.getUi();
var prompt = ui.alert('Are you sure you wand to send this daily to ' + se + ' ?',ui.ButtonSet.YES_NO)
if(prompt == ui.Button.YES){(EmailPDF)
}
else{
ui.alert('Permission denied.');
}
}
function EmailPDF() {
var spreadSheet=SpreadsheetApp.getActiveSpreadsheet();
var s = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Sheet5');
var se = s.getRange('A1').getValue();
I first tried to write the ui.alert in my EmailPDF function. But I couldn't get it to work. I was able to get it to work if the response answer was YES, but when I would click NO it would still run the script. (i.e send the email)
I don't know what to up after "else {}" to get the script to stop running or return to the beginning.
Then I thought maybe I should create a function (responseToSend) that just runs the ui.alert and if the response is "yes" then run the EmailPDF function.
I'm sure it can be done both ways.
Try it this way:
function responseToSend() {
var spreadSheet=SpreadsheetApp.getActiveSpreadsheet();
var s = spreadSheet.getSheetByName('Sheet5');
var se = s.getRange('A1').getValue();
var ui = SpreadsheetApp.getUi();
var ps = Utilities.formatString('Are you sure you want send this daily to %s ?',se);
var prompt = ui.prompt(ps,ui.ButtonSet.YES_NO);
if(prompt.getSelectedButton() == ui.Button.YES){
EmailPDF();
}else{
ui.alert('Permission denied.');
}
}

What is the unexpected error in my code?

I am trying to create a calendar event in Google App Script, it is similar to the "QuickAdd" format that is used in the calendar. I used the createEventFromDescription method, I checked the execution transcript and it creates the event successfully but then the script shows an error when running the script.
The error message is "Error Encountered: An unexpected error occured"
I don't know what I have done wrong, it creates the event. Below is the code:
function doGet(){
var app = UiApp.createApplication().setTitle('QuickAdd Events');
//Create a penel which holds all the form elelemnts
var parent = app.createHorizontalPanel().setId('parent');
var left = app.createVerticalPanel().setId('left');
var right = app.createVerticalPanel().setId('right');
var eventTitleLabel = app.createLabel('Event Title:');
var eventTitle = app.createTextBox().setName('eventTitle');
var eventButton = app.createButton('Create Events');
var cancelButton = app.createButton('Cancel');
left.add(eventTitleLabel)
.add(eventTitle);
right.add(eventButton)
.add(cancelButton);
var eventHandler = app.createServerClickHandler('createEvents');
eventHandler.addCallbackElement(left);
//Add this handler to the button
eventButton.addClickHandler(eventHandler);
parent.add(left)
.add(right);
app.add(parent);
app.close();
return app;
}
function createEvents(e){
//Get the active application
var app = UiApp.getActiveApplication();
try{
//get the entries;
var event = e.parameter.eventTitle;
//Get the calendar
var cal = CalendarApp.getDefaultCalendar();
cal.createEventFromDescription(event);
app.add(app.createLabel('Event created Successfully'));
//make the form panel invisible
app.getElementById('panel').setVisible(false);
return app;
}
//If an error occurs, show it on the panel
catch(e){
app.add(app.createLabel('Error occured: '+e));
return app;
}
}
You are calling getElementById ('panel') but there is no widget called 'panel'... the closest is the panel called 'parent', is that the one you want to hide?

google apps script TextBox value not passed to e.parameter.TextBoxName

On the code below I am defining a TextBox with name and id. The button handler works fine but I am not been able to get the value entered on the TextBox. The msgBox shows up but the e.parameter.matchValue shows as undefined.
On other part of the app I have the same logic but with a ListBox and it works fine.
What am I doing wrong?
function chooseColumnValueToMatch() {
var app = UiApp.createApplication().setHeight(150).setWidth(250);
var ss = SpreadsheetApp.getActiveSpreadsheet();
var columnName = ScriptProperties.getProperty("columnNameToMatch");
var h1Line = app.createHTML("<h2>Value to match "+columnName+":</h2>");
var textPara = app.createHTML("<p>Type the VALUE that the <b>"+columnName+"</b> column must match EXACTLY to send the message.</p>");
var selectionHandler = app.createServerHandler('chooseColumnValueToMatchSelectionHandler');
var valueInputBox = app.createTextBox()
.setTitle("Match value for "+columnName)
.setName("matchValue")
.setId("matchValue");
var submitBtn = app.createButton("Submit", selectionHandler);
app.add(h1Line)
.add(textPara)
.add(valueInputBox)
.add(submitBtn);
ss.show(app);
}
function chooseColumnValueToMatchSelectionHandler(e){
var app = UiApp.getActiveApplication();
var columnName = ScriptProperties.getProperty("columnNameToMatch");
var ss = SpreadsheetApp.getActiveSpreadsheet();
var msg = e.parameter.matchValue;
Browser.msgBox("Value to match "+columnName+" column is:", msg, Browser.Buttons.OK);
return app;
}
You have to use the ServerHandler.addCallbackElement method. The following code demonstrates it. The method call "tells" GAS internals that the value of the widget pointed as the parameter should be passed to the handler.
If you have multiple widgets, which should be handled by the handler, then, instead of calling the addCallbackElement with every widget, is possible to place all controls to a panel and point only the panel as the addCallbackElement method parameter. The code in the point 4.4 of this tutorial shows this way.
function doGet(e) {
var app = UiApp.createApplication();
var valueInputBox = app.createTextBox()
.setTitle("Match value for XXX")
.setName("matchValue")
.setId("matchValue");
var selectionHandler = app.createServerHandler('chooseColumnValueToMatchSelectionHandler');
selectionHandler.addCallbackElement(valueInputBox);
var submitBtn = app.createButton("Submit", selectionHandler);
var output = app.createLabel().setId("output");
app.add(valueInputBox).add(submitBtn).add(output);
return app;
}
function chooseColumnValueToMatchSelectionHandler(e){
var app = UiApp.getActiveApplication();
var output = app.getElementById("output");
var msg = e.parameter.matchValue;
output.setText("Value to match XXXX column is: " + msg);
return app;
}
It seems that you forgot to add a callbackElement to your handler
you could try like this :
selectionHandler.addCallbackElement(valueInputBox)
right after the submitBtn definition

Is there a way to turn off Auto Fill for users when filling out a form?

I think this is a quick one for you, Folks:
I am collecting data in a UiApp built form, and using validation before enabling them to continue. My problem is that often auto-fill will have different format than what is required in my form (such as the state written out instead of a postal abbreviation). I think this may lead to confusion, so I would love to force the users to fill out each box by hand.
Is there a way to turn off the auto-fill function of browsers?
If I were to build in html, would it give me more control of this?
Thanks for the help~
Martin
NOTE: I am using the "ChangeHandler" as it will at least update the validator when autofill is used.
Here's an example code which is of the style I am using. It includes multiple field validation and a review panel. Relevant to my question, but also may be useful for those building forms.
function doGet(e){
var app = UiApp.createApplication().setTitle("Review and Validation");
var appPanel = app.createVerticalPanel();
var form = app.createFormPanel();
var panel1 = app.createGrid(4,5).setId('panel1');
var firstNameLabel = app.createLabel("First Name:");
var firstName = app.createTextBox().setName('firstName').setId('firstName');
var lastNameLabel = app.createLabel("Last Name:");
var lastName = app.createTextBox().setName('lastName').setId('lastName');
var emailLabel = app.createLabel('Your Email');
var email = app.createTextBox().setName('email').setId('email');
var button1 = app.createButton('Go to Review').setEnabled(false);
var info1 = app.createLabel("Please Enter First Name")
.setVisible(false)
.setStyleAttribute('color', 'red');
var info2 = app.createLabel("Please Enter Last Name")
.setVisible(false)
.setStyleAttribute('color', 'red');
var info3 = app.createLabel("Please Enter Email")
.setVisible(false)
.setStyleAttribute('color', 'red');
var syncChangeHandler = app.createServerHandler('syncText').addCallbackElement(form)
.validateLength(firstName, 2, 30).validateLength(lastName, 2, 30).validateEmail(email);
var onValidInput =
app.createClientHandler().validateLength(firstName,2,30).validateLength(lastName,2,30).validateEmail(email).forTargets(
button1).setEnabled(true);
var onInvalidInput1 =
app.createClientHandler().validateNotLength(firstName, 2, 30).forTargets(button1).setEnabled(false).forTargets(info1).setVisible(true);
var onValidInput1 =
app.createClientHandler().validateLength(firstName, 2, 30).forTargets(info1).setVisible(false);
var onInvalidInput2 =
app.createClientHandler().validateNotLength(lastName, 2, 30).forTargets(button1).setEnabled(false).forTargets(info2).setVisible(true);
var onValidInput2 =
app.createClientHandler().validateLength(lastName, 2, 30).forTargets(info2).setVisible(false);
var onInvalidInput3 =
app.createClientHandler().validateNotEmail(email).forTargets(button1).setEnabled(false).forTargets(info3).setVisible(true);
var onValidInput3 =
app.createClientHandler().validateEmail(email).forTargets(info3).setVisible(false);
firstName.addChangeHandler(onInvalidInput1);
firstName.addChangeHandler(onValidInput1);
lastName.addChangeHandler(onInvalidInput2);
lastName.addChangeHandler(onValidInput2);
email.addChangeHandler(onInvalidInput3);
email.addChangeHandler(onValidInput3);
firstName.addChangeHandler(onValidInput);
lastName.addChangeHandler(onValidInput);
email.addChangeHandler(onValidInput);
panel1.setWidget(0,0, firstNameLabel);
panel1.setWidget(0,1, firstName);
panel1.setWidget(0,2, lastNameLabel);
panel1.setWidget(0,3, lastName);
panel1.setWidget(1,1, info1);
panel1.setWidget(1,3, info2);
panel1.setWidget(2,0, emailLabel);
panel1.setWidget(2,1, email);
panel1.setWidget(2,3, button1);
panel1.setWidget(3,1, info3);
app.add(form);
appPanel.add(panel1);
form.add(appPanel);
var panel2 = app.createGrid(4,5).setId('panel2').setVisible(false);
var reviewFirstNameLabel = app.createLabel("First Name:");
var reviewFirstName = app.createLabel().setId('reviewFirstName');
var reviewLastNameLabel = app.createLabel("Last Name:");
var reviewLastName = app.createLabel().setId('reviewLastName');
var reviewEmailLabel = app.createLabel('Your Email:');
var reviewEmail = app.createLabel().setId('reviewEmail');
var submitButton = app.createSubmitButton('Submit');
var button2 = app.createButton('Edit Response');
panel2.setWidget(0,0, reviewFirstNameLabel);
panel2.setWidget(0,1, reviewFirstName);
panel2.setWidget(0,2, reviewLastNameLabel);
panel2.setWidget(0,3, reviewLastName);
panel2.setWidget(1,0, reviewEmailLabel);
panel2.setWidget(1,1, reviewEmail);
panel2.setWidget(2,0, button2);
panel2.setWidget(3,0, submitButton);
appPanel.add(panel2);
//
var editResponse = app.createClientHandler()
.forTargets(panel1).setVisible(true)
.forTargets(panel2).setVisible(false);
button1.addClickHandler(syncChangeHandler);
button2.addClickHandler(editResponse);
return app;
}
function syncText(e){
var app = UiApp.getActiveApplication();
app.getElementById('reviewFirstName').setText(e.parameter.firstName);
app.getElementById('reviewLastName').setText(e.parameter.lastName);
app.getElementById('reviewEmail').setText(e.parameter.email);
app.getElementById('panel1').setVisible(false);
app.getElementById('panel2').setVisible(true);
return app;
}
function doPost(e){
var ss = SpreadsheetApp.openById('0Aiapuj1KtAujdHYzZzNzMEsxMUtranZhaXhiSFFnanc').getSheets()[0];
var range = ss.getRange(ss.getLastRow()+1, 1, 1,4);
var values = [[new Date(),e.parameter.firstName, e.parameter.lastName, e.parameter.email]];
range.setValues(values);
var app = UiApp.getActiveApplication();
var label = app.createLabel('Thank You!');
app.add(label);
return app;
}
GAS is globally browser independent since the app is running on Google's Server and doesn't interact directly with your browser... I don't think it is possible to tell the browser to do anything like that and I haven't seen any trick that would make that possible... If I'm wrong then I'd be very curious about it ;-)
Edit : I played with your code and I was wondering if it might not be a solution to use a key press handler instead of click handler so users would be forced to use keyboard to fill the form instead of mouse click. I've seen scripts that check if an 'enter' was pressed to validate an answer. (just an idea)
EDIT 2 : Well, Browsers are too smart, I tested it but autocomplete simulates keypress quite efficiently... too bad :-/

How To Allow Users to Review Answers before Submiting Form?

I've built a form with UiApp to collect information from the user. It's rather complex with multiple panels and file uploads, so I would like to give the user the opportunity review their inputs before submitting. I was hoping to display their inputs on one final review panel that would then allow them to decide to edit the info and move back to a earlier panel to edit.
Following is the test script. The farthest I've gotten is getting it to return 'textBox' and not the value of the textBox. Is it possible to get the values while staying in the doGet portion of my script, or must I move to doPost to access the values?
What would be the work around you would suggest?
Thanks for any and all help!
function doGet(e){
var app = UiApp.createApplication();
var appPanel = app.createVerticalPanel();
var form = app.createFormPanel();
var panel1 = app.createHorizontalPanel();
var emailLabel = app.createLabel('Your Email');
var email = app.createTextBox().setName('email').setId('email');
app.add(form);
var button1 = app.createButton('Go to Review');
panel1.add(emailLabel);
panel1.add(email);
panel1.add(button1);
appPanel.add(panel1);
form.add(appPanel);
var panel2 = app.createHorizontalPanel().setVisible(false);
var reviewLabel = app.createLabel('Your Email:');
var reviewEmail = app.createLabel(email);
panel2.add(reviewLabel);
panel2.add(reviewEmail);
appPanel.add(panel2);
//
var reviewPageTwo = app.createClientHandler()
.forTargets(panel1).setVisible(false)
.forTargets(panel2).setVisible(true);
button1.addClickHandler(reviewPageTwo);
return app;
}
UPDATE 8.24.12
I'm including the resulting script. It includes the review function, the button to lead the user back to edit, and the submitButton to post it. (You will need to replace the spreadsheet ID for the post to work.)
Thank for the help all!
Martin
function doGet(e){
var app = UiApp.createApplication();
var appPanel = app.createVerticalPanel();
var form = app.createFormPanel();
var panel1 = app.createHorizontalPanel().setId('panel1');
var emailLabel = app.createLabel('Your Email');
var email = app.createTextBox().setName('email').setId('email');
var syncChangeHandler = app.createServerHandler('syncText').addCallbackElement(form);
app.add(form);
var button1 = app.createButton('Go to Review');
panel1.add(emailLabel);
panel1.add(email);
panel1.add(button1);
appPanel.add(panel1);
form.add(appPanel);
var panel2 = app.createHorizontalPanel().setId('panel2').setVisible(false);
var reviewGrid = app.createGrid(3,3).setId('reviewGrid');
var reviewEmail = app.createLabel().setId('reviewEmail');
var reviewLabel = app.createLabel('Your Email:');
var submitButton = app.createSubmitButton('Submit');
var button2 = app.createButton('Edit Response');
panel2.add(reviewLabel);
panel2.add(reviewEmail);
panel2.add(button2);
panel2.add(submitButton);
appPanel.add(panel2);
//
var editResponse = app.createClientHandler()
.forTargets(panel1).setVisible(true)
.forTargets(panel2).setVisible(false);
button1.addClickHandler(syncChangeHandler);
button2.addClickHandler(editResponse);
return app;
}
function syncChangeHandler(e){
var app = UiApp.getActiveApplication();
app.getElementById('reviewEmail').setText(e.parameter.email);
app.getElementById('panel1').setVisible(false);
app.getElementById('panel2').setVisible(true);
return app;
}
function doPost(e){
var ss = SpreadsheetApp.openById('*your spreadsheet id here*').getSheets()[0];
var range = ss.getRange(ss.getLastRow()+1, 1, 1,2);
var values = [[new Date(),e.parameter.email]];
range.setValues(values);
var app = UiApp.getActiveApplication();
var label = app.createLabel('Thank You!');
app.add(label);
return app;
}
You have your entire function inside doGet(). The doGet() function is executed when your UI is first loaded.
So,
var email = app.createTextBox().setName('email').setId('email');
actually resolves to a text box. When you do
var reviewEmail = app.createLabel(email);
you are trying to pass a text box as an argument to createLabel, which is not allowed. Therefore this won't work. You must handle the changes to the text box in a handler.
function doGet(){
var syncChangeHandler = app.createServerHandler('syncText').addCallbackElement(form);
var email = app.createTextBox().setName('email').setId('email');
...
var reviewEmail = app.createLabel().setId('reviewEmail');
...
}
function syncText(e){
var app = UiApp.getActiveAplication();
app.getElementById('reviewEmail').setText(e.parameter.email);
return app;
}
What Srik said is true (of course ;-)), you can't indeed assign a label this type of value... What I would do (since you work in a doGet/doPost structure) is to create a second button just aside of the submit button that triggers a handler to a 'review' function that populates all the corresponding textBoxes , listBoxes or whatever you have with the values coming from your main form (a sort of copy of it) in the review panel that you already have. To achieve this you will need to add the form as a callBackElement to the handler (which was not necessary with the doPost scheme).
Another option could be to add this handler to all the widgets separately with 'key up' triggers or 'Value change triggers' so that the review panel is always up to date in real time, in this case the 'review before submit panel' could be visible at any time without further action from the user other than make it eventually visible (although it could also be always visible). In this option the handler function would be more like a 'synchroniser'. I'm afraid you'll have some difficulties with file upload though (since this can only work in a doGet/doPost structure).