UI to upload/attach file to Google Sites page - google-apps-script

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.

Related

fileUpload in GAS with the same name and extension

I hope that you can help me, I have a issue.
I need a form in GAS that can upload a file to google drive with the original name. but I canĀ“t make work. I don't know that is wrong, but the file not have extention and the name is Undefined..
this is my simple code;
thanks for all.
function doGet(p) {
var app = UiApp.createApplication();
var flow = app.createFlowPanel().setId('flow');
var gridfile = app.createGrid(5,3);
var flabel0 = app.createLabel('Upload the file');
var flabel1 = app.createLabel('Select file: ');
var thefile = app.createFileUpload().setName('thefile').setId('thefile');
var handlerxx = app.createServerHandler('uploadfile').addCallbackElement(flow);
thefile.addChangeHandler(handlerxx);
gridfile.setWidget(0, 0, flabel0)
.setWidget(2, 0, flabel1)
.setWidget(2, 1, thefile);
flow.add(gridfile);
app.add(flow);
return app;
}
function uploadfile(e) {
var app = UiApp.getActiveApplication();
var fileBlob = Utilities.newBlob(e.parameter.thefile,"application/zip",e.parameter.thefile);
var doc = DocsList.createFile(fileBlob);
app.getElementById('flow').add(app.createLabel('File Uploaded successfully'));
return app;
}
You can only upload a file in a doGet / doPost form structure.
Instead of explaining in details I thought it would be easier to show a working example (honestly it's also simpler ...)
so there it is, note that I had to add a submit button to trigger the form submission.
By the way I added a 'loading' label shown by a client handler because otherwise nothing happens during upload and users can be worrying !!
I commented out the line about zip type and fileName since the uploaded file will keep the name and type automatically.
function doGet() {
var app = UiApp.createApplication();
var form = app.createFormPanel();
var flow = app.createFlowPanel().setId('flow');
form.add(flow);
var gridfile = app.createGrid(5,3);
var flabel1 = app.createLabel('Select file: ');
var lab = app.createLabel('LOADING').setStyleAttributes({'color':'red'}).setVisible(false).setId('lab');
var cliHandler = app.createClientHandler().forTargets(lab).setVisible(true);
var thefile = app.createFileUpload().setName('thefile').setId('thefile');
var button = app.createSubmitButton('Upload the file').addClickHandler(cliHandler);
gridfile.setWidget(2, 0, flabel1)
.setWidget(2, 1, thefile)
.setWidget(2, 2, button);
flow.add(gridfile).add(lab);
app.add(form);
return app;
}
function doPost(e) {
var app = UiApp.getActiveApplication();
Logger.log('doPost');
// var fileBlob = Utilities.newBlob(e.parameter.thefile,"application/zip",e.parameter.thefile);
var doc = DocsList.createFile(e.parameter.thefile);
app.getElementById('lab').setVisible(false);
app.add(app.createLabel('File Uploaded successfully'));
return app;
}

Changing a Textbox to a Button in Google Script

I'm working on a todo list program in google script. I have it so that people can type in their task in a textbox, but I want it so that once they hit the enter key, the textbox turns into a button that, that says what the task is, and they will later press when they finished that task and it will return to a textbox. I can't figure out how to make any changes to the app from inside another function.
What I have so far is:
function doGet(e) {
var app = UiApp.createApplication();
var panel = app.createAbsolutePanel().setHeight("750px").setWidth("1600px");
var button = app.createButton("swag");
var enterHandler = app.createServerKeyHandler('enterPress');
enterHandler.addCallbackElement(panel);
var box = app.createTextBox().addKeyUpHandler(enterHandler);
panel.add(box,0,0);
app.add(panel);
return app;
}
function enterPress(e){
var app = UiApp.getActiveApplication();
if (e.parameter.keyCode==13){
var button2 =app.createButton('swag');
var panel = app.createAbsolutePanel().setHeight('750px').setWidth('1600px');
panel.add(button2,0,0);
app.add(panel);
}
return app;
}
It recognizes the enter press, but the changes won't return to the main app.
You can do it like this :
note : I didn't "place" the button, I guess you have an idea on how you want them to show up...
You could also use a grid to place them... as you want.
function doGet(e) {
var app = UiApp.createApplication();
var panel = app.createAbsolutePanel().setHeight("750px").setWidth("1600px");
var enterHandler = app.createServerKeyHandler('enterPress').addCallbackElement(panel);
var button = app.createButton("confirm",enterHandler).setId('swag');
var tBox = app.createTextBox().setName('tBox').setId('tBox').setWidth(300).setStyleAttributes({'textAlign':'center'});
var bBox = app.createButton("no Text",enterHandler).setVisible(false).setId('bBox').setWidth(300);
panel.add(tBox,20,20).add(bBox,20,20).add(button,140,50);
app.add(panel);
return app;
}
function enterPress(e){
var app = UiApp.getActiveApplication();
if (e.parameter.source=='bBox'){
app.getElementById('bBox').setVisible(false);
app.getElementById('tBox').setVisible(true);
}
if (e.parameter.source=='swag'){
app.getElementById('bBox').setVisible(true).setHTML(e.parameter.tBox);
app.getElementById('tBox').setVisible(false);
}
return app;
}
Test here
Edit: code modified with placement .
EDIT2 : You could also hide the enter button, I find it nicer.
function enterPress(e){
var app = UiApp.getActiveApplication();
if (e.parameter.source=='bBox'){
app.getElementById('bBox').setVisible(false);
app.getElementById('tBox').setVisible(true);
app.getElementById('swag').setVisible(true);
}
if (e.parameter.source=='swag'){
app.getElementById('bBox').setVisible(true).setHTML(e.parameter.tBox);
app.getElementById('tBox').setVisible(false);
app.getElementById('swag').setVisible(false);
}
return app;
}

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?

use return app; without ending current function

Is there another way to update the gui than return app;?
I want to set the text on a label before doing an url fetch, like started downloading, and after it completes turn the label into download complete.
function EventHandler(e) {
var app = UiApp.getActiveApplication();
var url = e.parameter.URLInput;
var label = app.getElementById("label");
label.setText("Download started");
try{
var file = UrlFetchApp.fetch(url).getBlob();
} catch(err){
label.setText(err);
}
label.setText("Download finished");
return app;
}
The label stays empty until UrlFetchApp is completed, and then the label's content is 'Download finished'. Adding return app; before the fetch ends the function.
You have to use a clientHandler to set Text to your label in the doGet function, the clientHandler executes immediately when you click the button.
Here is a test app that shows how it works: (online test available here with simulated download)
function doGet(){
var app = UiApp.createApplication();
var label = app.createLabel('---empty---').setId('label');
app.add(label)
var handler = app.createServerHandler('EventHandler');
var cHandler = app.createClientHandler().forTargets(label).setText('starting download');
var btn = app.createButton('start',handler).addClickHandler(cHandler);
app.add(btn);
return app;
}
function EventHandler(e) {
var app = UiApp.getActiveApplication();
var url = e.parameter.URLInput;
var ulabel = app.getElementById("label");
ulabel.setText("Download started");
try{
//var file = UrlFetchApp.fetch(url).getBlob();
} catch(err){
label.setText(err);
}
ulabel.setText("Download finished");
return app;
}
note : you can use the same client handler to do lots of other usefull things : disable the button, show a spinner... whatever you like that must happen in the doGet function without delay.
EDIT following your comment
Have you tried using 2 server handlers in parallel ? in the displayHandler you could setup any condition you want, I left it simple in the following example :
function doGet(){
var app = UiApp.createApplication();
var label = app.createLabel('---empty---').setId('label');
app.add(label)
var handler = app.createServerHandler('EventHandler');
var displayHandler = app.createServerHandler('displayHandler');
var btn = app.createButton('start',handler).addClickHandler(displayHandler);
// you can add other handlers (keypress, hover... whatever) they will all execute at the same time
app.add(btn);
return app;
}
function displayHandler(e) {
var app = UiApp.getActiveApplication();
var ulabel = app.getElementById("label");
ulabel.setText("Download started");
return app;
}
function EventHandler(e) {
var app = UiApp.getActiveApplication();
var url = e.parameter.URLInput;
var ulabel = app.getElementById("label");
try{
Utilities.sleep(2000);// simulating download
} catch(err){
label.setText(err);
}
ulabel.setText("Download finished");
return app;
}

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