What is the unexpected error in my code? - google-apps-script

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?

Related

UI to upload/attach file to Google Sites page

These are the errors I'm getting:
[13-12-23 22:43:26:376 EST] Page type: FileCabinetPage
[13-12-23 22:43:26:376 EST] File blob: undefined
[13-12-23 22:43:26:383 EST] Error msg: Cannot find method addHostedAttachment((class)).
Here's an excerpt from my code:
function doGet(){
var app = UiApp.createApplication().setTitle("Shipping Label Request Form");
var form = UiApp.createFormPanel();
var panel = UiApp.createVerticalPanel();
var attachment = app.createFileUpload().setId('attachment').setName('attachment')
var button = app.createSubmitButton('Submit').setId("button");
app.add(form);
form.add(panel);
panel.add(attachment);
panel.add(button);
// not needed with formPanel //
// var handler = app.createServerHandler('submitAnnouncement');
// button.addClickHandler(handler); //
return app;
}
// submitAnnouncement changed to doPost()
function doPost(e) {
var app = UiApp.getActiveApplication();
var page = SitesApp.getPageByUrl('https://sites.google.com/...')
var fileBlob = e.parameter.attachment;
Logger.log('Page type:' +page)
Logger.log('File blob:' +fileBlob)
try {
page.addHostedAttachment(fileBlob)
}
catch(e){
Logger.log('Hosted attachment error msg:' +e.message);
}
}
The parameter attachment will not be available to the server handler in your code, in order to fix this you can either use
var handler = app.createServerHandler('submitAnnouncement')addCallbackElement(attachment)
Or you can envelope the attachment and button in a Vertical Panel which must then be put inside a Form Panel and your code should start working.

Google Apps Script Stop Function from Running

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;
}

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

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).

Error occured: TypeError: Cannot call method "createEvent" of undefined via Google Script

Within the second half of my script and function, I keep getting this Cannot call method error, and I do not know why. I mimic'd the template script exactly, but I am unsure why it does not call the method.
Any insight would be greatly appreciated.
Thanks.
function createEvents(e){
//Get the active application
var app = UiApp.getActiveApplication();
try{
//get the entries;
var eventDate = e.parameter.eventDate;
var eventPeople = e.parameter.eventPeople;
var eventCompany = e.parameter.eventCompany;
var eventName = e.parameter.eventName;
var eventTime = e.parameter.eventTime;
var eventPhone = e.parameter.eventPhone;
var eventEmail = e.parameter.eventEmail;
var eventTaken = e.parameter.eventTaken;
//Get the calendar
var cal = CalendarApp.getCalendarsByName('Phoenix Reservations')[0];//Change the calendar name
var eventStartTime = eventDate;
//End time is calculated by adding an hour in the event start time
var eventEndTime = new Date(eventDate.valueOf()+60*60*1000);
//Create the events
cal.createEvent(eventPeople,eventCompany,eventName,eventTime,eventPhone,eventEmail,eventTaken);
//Log the entries in a spreadsheet
var ss = SpreadsheetApp.openById('KEY_TAKEN_OUT');//Change the spreadhseet key to yours
var sheet = ss.getSheets()[0];
sheet.getRange(sheet.getLastRow()+1, 1, 1, 5).setValues([[new Date(), eventDate,eventPeople,eventCompany,eventName,eventTime,eventPhone,eventEmail,eventTaken, 'Event created']]);
//Show the confirmation message
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;
}
}
This line probably returns nothing :
var cal = CalendarApp.getCalendarsByName('Phoenix Reservations')[0];//Change the calendar name
You could log it to confirm like this Logger.log(cal)
Is 'Phoenix Reservations' a calendar name that you own or that you have write access to ?
EDIT : could you test this simple function to see if everything is ok with this calendar?
It will create an event just now.
function testcal(){
var cal = CalendarApp.openByName('Phoenix Reservations');// or you can replace this with your var definition : same result normally ;-)
if (cal) {
var title = 'Test Event';
var start = new Date();
var end = new Date(start.valueOf()+60*60*1000);
Logger.log(cal.getName()+' '+start+' '+end)
var desc = 'Created using Google Script';
var loc = 'there';
var event = cal.createEvent(title, start, end, {
description : desc,
location : loc
});
}
}
EDIT 2 : I modified the logger to make it show the calendar name.