I am trying to create a Google Doc based on form data. I have done this with other forms that take the Doc and save them as PDFs. With this script, I have been able to create new docs and rename the file, but I need to move them to a different folder. The code I used for doing this with PDFs works (at the very bottom) without problems but I can't figure out what I am doing wrong. I have not had any luck searching for working solutions online either.
var TEMPLATE_ID = 'template id number';
var folder = DriveApp.getFolderById('folder ID number');
// When Form Gets submitted
function onFormSubmit(e) {
var ss = SpreadsheetApp.getActiveSpreadsheet();
sheet = ss.getSheetByName("Goal Tracking"),
activeSheet = SpreadsheetApp.getActiveSheet(),
copyFile = DriveApp.getFileById(TEMPLATE_ID).makeCopy(),
copyId = copyFile.getId(),
copyDoc = DocumentApp.openById(copyId),
copyBody = copyDoc.getActiveSection(),
numberOfColumns = sheet.getLastColumn(),
activeRowIndex = sheet.getActiveRange().getRowIndex(),
activeRow = activeSheet.getRange(activeRowIndex, 1, 1, numberOfColumns).getValues(),
headerRow = activeSheet.getRange(1, 1, 1, numberOfColumns).getValues(),
columnIndex = 0,
fileName = "Goals " + activeRow[0][2] + ' ' + activeRow[0][3]+'_'+activeRow[0][5];
//Get information from form and set as variables.
var lastName = e.values[2];
var firstName = e.values[3];
var programCode = e.values[4];
var goal1 = e.values[6];
var goal2 = e.values[7];
var goal3 = e.values[8];
// Replace place holder keys,in our google doc template
copyBody.replaceText('%First Name%', firstName);
copyBody.replaceText('%Last Name%', lastName);
copyBody.replaceText('%Program Code%', programCode);
copyBody.replaceText('%Goal Statement 1%', goal1);
copyBody.replaceText('%Goal Statement 2%', goal2);
copyBody.replaceText('%Goal Statement 3%', goal3);
// Save and close the temporary document
copyDoc.saveAndClose();
copyDoc.setName(fileName);
(continuing on)
Here is where the problem is
folder.addFile(newFile);
}
I do get an error in the execution transcript but I don't understand why I am getting it: 'Cannot find method addFile(Document)." I also tried to use:
copyDoc.makeCopy(fileName, folder).getId;
to set a file name and folder location but it also failed.
For what its worth, here is what I have used to create a PDF that does work in this script.
copyDoc.saveAndClose();
var pdfFile = DriveApp.createFile(copyFile.getAs("application/pdf"));
var pdfFile2 = pdfFile.setName(fileName);
folder.addFile(pdfFile2);
copyFile.setTrashed(true);
Thanks :)
This code moves the folder, and rename the file.
function myFunction() {
var dstFolderId = "0B1ECfqTCcLE8c09YZUtqTkVwU3c";
var targetFileId = "0B1ECfqTCcLE8SS1PRnJ1cVgxVlk";
var targetFile = DriveApp.getFileById(targetFileId);
var newFileName = "HelloWorld.pdf";
// Remove current parent folders
var parentFolders = targetFile.getParents();
while(parentFolders.hasNext()) {
var parent = parentFolders.next();
parent.removeFile(targetFile);
}
// Add the target file into the new folder
var dstFolder = DriveApp.getFolderById(dstFolderId);
dstFolder.addFile(targetFile);
// Rename if you want.
targetFile.setName(newFileName);
}
Related
I have a Google script triggered on submission of a form. It creates a new doc based on a template with certain variables in-filled from answers in the form.
I also have a folder created on the form submission.
The trouble I am having is creating the newly created doc within the newly created folder. Looking for some help merging the two scripts that work on their own to acheive this.
Creating a folder on form submission:
function createChannelFolder() {
var ss = SpreadsheetApp.getActive();
var names = ss.getSheetByName("Completed Certifications");
var ChannelName = names.getRange(names.getLastRow(), 2).getValue();
var parentFolder=DriveApp.getFolderById("FOLDERID");
return newFolder=parentFolder.createFolder(ChannelName);
}
Creating a document on form submission
function autoFillGoogleDocFromForm(e) {
//e.values is an array of form values
var Timestamp = e.values[0];
var Channel = e.values[1];
var Name = e.values[2];;
var file = DriveApp.getFileById('FILEID');
var folder = DriveApp.getFolderById('FOLDERID')
var copy = file.makeCopy(Channel + ',' + Name, folder);
var newId = copy.getId();
var doc = DocumentApp.openById(newId);
var body = doc.getBody();
body.replaceText('{{Timestamp}}', Timestamp);
body.replaceText('{{Channel}}', Channel);
body.replaceText('{{Name}}', Name);
doc.saveAndClose();
}
Explanation:
You need to call createChannelFolder() inside
autoFillGoogleDocFromForm(e).
just return the folder object within createChannelFolder():
return parentFolder.createFolder(ChannelName);
Solution:
Here is autoFillGoogleDocFromForm(e):
function autoFillGoogleDocFromForm(e) {
//e.values is an array of form values
var Timestamp = e.values[0];
var Channel = e.values[1];
var Name = e.values[2];;
var file = DriveApp.getFileById('FILEID');
var folder = createChannelFolder(); // 1st modification point
var copy = file.makeCopy(Channel + ',' + Name, folder);
var newId = copy.getId();
var doc = DocumentApp.openById(newId);
var body = doc.getBody();
body.replaceText('{{Timestamp}}', Timestamp);
body.replaceText('{{Channel}}', Channel);
body.replaceText('{{Name}}', Name);
doc.saveAndClose();
}
and here is createChannelFolder():
function createChannelFolder() {
var ss = SpreadsheetApp.getActive();
var names = ss.getSheetByName("Completed Certifications");
var ChannelName = names.getRange(names.getLastRow(), 2).getValue();
var parentFolder=DriveApp.getFolderById("FOLDERID");
return parentFolder.createFolder(ChannelName); // 2nd modification point
}
A year ago with the help of another user I was able to use google app script to take the form responses and email a pdf (code below)
I now need to save the pdf to a specific folder
I also need to add a link to the saved pdf within the google sheet. Is this possible?
var docTemplate = "doc ID";
var docName = "Vehicle check with images";
function onFormSubmit(e) {
var replaceTextToImage = function(body, searchText, fileId) {
var width = 300; // Please set this.
var blob = DriveApp.getFileById(fileId).getBlob();
var r = body.findText(searchText).getElement();
r.asText().setText("");
var img = r.getParent().asParagraph().insertInlineImage(0, blob);
var w = img.getWidth();
var h = img.getHeight();
img.setWidth(width);
img.setHeight(width * h / w);
}
//Get information from form and set as variables
var email_address = "myemailaddress#here.com";
var vehicle_vrn = e.values[1];
var front_desc = e.values[2];
var front_image = e.values[3].split("=")[1];
var rear_desc = e.values[4];
var rear_image = e.values[5].split("=")[1];
var driver_desc = e.values[6];
var driver_image = e.values[7].split("=")[1];
var passenger_desc = e.values[8];
var passenger_image = e.values[9].split("=")[1];
// Get document template, copy it as a new temp doc, and save the Doc’s id
var copyId = DriveApp.getFileById(docTemplate)
.makeCopy(docName+' for '+vehicle_vrn)
.getId();
// Open the temporary document
var copyDoc = DocumentApp.openById(copyId);
// Get the document’s body section
var copyBody = copyDoc.getBody();
copyBody.replaceText('keyVrn', vehicle_vrn);
copyBody.replaceText('keyFrontdesc', front_desc);
replaceTextToImage(copyBody, 'keyFrontimage', front_image);
copyBody.replaceText('keyReardesc', rear_desc);
replaceTextToImage(copyBody, 'keyRearimage', rear_image);
copyBody.replaceText('keyDriversdesc', driver_desc);
replaceTextToImage(copyBody, 'keyDriversimage', driver_image);
copyBody.replaceText('keyPassdesc', passenger_desc);
replaceTextToImage(copyBody, 'keyPassimage', passenger_image);
copyDoc.saveAndClose();
var pdf = DriveApp.getFileById(copyId).getAs("application/pdf");
var subject = "sample attachment file";
var body = "sample text: " + vehicle_vrn + "";
MailApp.sendEmail(email_address, subject, body, {htmlBody: body, attachments: pdf});
DriveApp.getFileById(copyId).setTrashed(true);
}```
I believe your goal as follows.
You want to save the PDF data to the specific folder as a file.
You want to retrieve the URL of the saved PDF file and put it to the specific column of the Spreadsheet.
Modification points:
In order to save the PDF data (in your case, it's a blob.) to the specific folder, you can use DriveApp.getFolderById("folderId").createFile(pdf).
In order to retrieve the URL of the created file, you can use getUrl().
In order to put the URL to the specific column of the Spreadsheet.
You want to put the value to the next column of the last row.
Modified script:
Please modify your script as follows. Before you use this modified script, please set the folder ID, Spreadsheet ID and sheet name.
From:
copyDoc.saveAndClose();
var pdf = DriveApp.getFileById(copyId).getAs("application/pdf");
To:
copyDoc.saveAndClose();
var pdf = DriveApp.getFileById(copyId).getAs("application/pdf");
var url = DriveApp.getFolderById("###folderId###").createFile(pdf).getUrl();
var sheet = SpreadsheetApp.openById("###spreadsheetId###").getSheetByName("###sheetName###");
sheet.getRange(sheet.getLastRow(), sheet.getLastColumn() + 1).setValue(url);
When you run the script, pdf is created to the folder ###folderId### as a file, and url is put to the next column of the last row of the sheet ###sheetName### on the Spreadsheet ###spreadsheetId###.
Note:
If you want to give the filename of PDF file, please modify as follows.
From
var pdf = DriveApp.getFileById(copyId).getAs("application/pdf");
To
var pdf = DriveApp.getFileById(copyId).getAs("application/pdf").setName("###filename###");
References:
getFolderById(id)
getUrl()
getRange(row, column)
I've been working on the process of auto populating a form based on a Google form submission. All of my progress to date has been what I learned on this site. Thanks for all the help!
I got everything to work exactly how it needed to run, assuming that the form is filled out in its entirety. The technician fills out the google form and submits. After he/she submits, the script automatically generates a custom document with all the data.
After some runtime, the technicians asked that we make the Google Form editable after submission. I did some research and found that you can make a form editable and wrote some script to auto generate a custom URL for each form.
Now I'm running into an issue with the auto Doc when partial forms are submitted. If the technician fills out the 1'st half of the form and submits, a Doc is auto generated with only the 1st half data, which I expected. Now if they go back and fill out the 2nd half of the form, only the 2nd portion of the Doc is filled out. I thought it would pull the entire row of data from the Sheet, but this isn't the case.
After some thought, I think I need to rewrite the code to not auto generate the Doc when a form is submitted, because more and more forms will be filled out in pieces, and completed at a later date.
Here's is what I would like to do. Any insight or helpful links would be greatly appreciated! Thanks so much!
All of the form data currently goes to the Sheet, Tab A. I would have a second Tab B on the sheet named "Data to Doc". I would manually comb through Tab A and copy and past the data to Tab B that I would like to create Doc's for. On Sheet B, There would be a button that I can click that would run the script to convert the sheet data to a Doc file.
I found a video of what I think I want to do, but I cant seem to make my code work the way they did.
https://www.youtube.com/watch?v=r9uU_KwGgzQ
Below is my current code.
var docTemplate = "fdgfdg";
var docName = "Technician Report";
var folder = DriveApp.getFolderById('hjhjghhgjgh');
function onFormSubmit(e) {
var replaceTextToImage = function(body, searchText, fileId) {
var width = 300; // Please set this.
var blob = DriveApp.getFileById(fileId).getBlob();
var r = body.findText(searchText).getElement();
r.asText().setText("");
var img = r.getParent().asParagraph().insertInlineImage(0, blob);
var w = img.getWidth();
var h = img.getHeight();
img.setWidth(width);
img.setHeight(width * h / w);
}
//Get information from form and set as variables
var Technician = e.values[1];
var Customer_Name = e.values[2];
var Date = e.values[3];
var Facility_Location = e.values[4];
var WO_Project_No = e.values[5];
var PO_No = e.values[6];
var Tag_No = e.values[7];
var Site_Contact = e.values[8];
var Repair_Scope = e.values[9];
var Valve_Serial_No = e.values[10];
var Valve_Model = e.values[11];
var Valve_Condition = e.values[12];
var Valve_Action = e.values[13];
var Act_Serial_No = e.values[14];
var Act_Model = e.values[15];
var Act_Condition = e.values[16];
var Act_Action = e.values[17];
var Cont_Serial_No = e.values[18];
var Cont_Model = e.values[19];
var Cont_Condition = e.values[20];
var Cont_Action = e.values[21];
var Recommended_Actions = e.values[30];
var Call_Notes = e.values[32];
var Picture1_Notes = e.values[23];
var Picture2_Notes = e.values[25];
var Picture3_Notes = e.values[27];
var Picture1_Image = e.values[22].split("=")[1];
var Picture2_Image = e.values[24].split("=")[1];
var Picture3_Image = e.values[26].split("=")[1];
// Get document template, copy it as a new temp doc, and save the Doc’s id
var copyId = DriveApp.getFileById(docTemplate)
.makeCopy(docName+' for '+Customer_Name+' '+Tag_No, folder)
.getId();
// Open the temporary document
var copyDoc = DocumentApp.openById(copyId);
// Get the document’s body section
var copyBody = copyDoc.getBody();
copyBody.replaceText('A1', Technician);
copyBody.replaceText('A2', Customer_Name);
copyBody.replaceText('A3', Date);
copyBody.replaceText('A4', Facility_Location);
copyBody.replaceText('A5', WO_Project_No);
copyBody.replaceText('A6', PO_No);
copyBody.replaceText('A7', Tag_No);
copyBody.replaceText('A8', Site_Contact);
copyBody.replaceText('A9', Repair_Scope);
copyBody.replaceText('B1', Valve_Serial_No);
copyBody.replaceText('B2', Valve_Model);
copyBody.replaceText('B3', Valve_Condition);
copyBody.replaceText('B4', Valve_Action);
copyBody.replaceText('B5', Act_Serial_No);
copyBody.replaceText('B6', Act_Model);
copyBody.replaceText('B7', Act_Condition);
copyBody.replaceText('B8', Act_Action);
copyBody.replaceText('B9', Cont_Serial_No);
copyBody.replaceText('C1', Cont_Model);
copyBody.replaceText('C2', Cont_Condition);
copyBody.replaceText('C3', Cont_Action);
copyBody.replaceText('C4', Recommended_Actions);
copyBody.replaceText('C5', Call_Notes);
copyBody.replaceText('C8', Picture1_Notes);
copyBody.replaceText('D1', Picture2_Notes);
copyBody.replaceText('D3', Picture3_Notes);
replaceTextToImage(copyBody, 'C7', Picture1_Image);
replaceTextToImage(copyBody, 'C9', Picture2_Image);
replaceTextToImage(copyBody, 'D2', Picture3_Image);
copyDoc.saveAndClose();
Assuming you have a script bound to your Form with an onSubmit trigger, you can retrieve the length of the latest response and comprare it to the total amount of questions:
function myFunction(e) {
var length = FormApp.getActiveForm().getItems().length;
if(e.response.getItemResponses().length == length){
//proceed to generate custom document
}else{
Logger.log("Form is not complete");
}
}
UPDATE: Alternative solution create doc if checkbox has been checked
In this case, you do not want to run the function when the form is submitted, but when an edit of the checkbox has been made.
So, replace the onSubmit trigger through an onEdit trigger.
This will alow you to access the event objects e.range, e.oldValue and e.value.
You can implement an if statement that runs the code only if an edit in the checkbox column has been made and if the old value was false (unchecked) and the new one istrue` (checked).
Unfortunately you cannot retrieve your form response onEdit with e.values, instead, you can verify in which row the checkbox has been checked and retrieve the values of this row with sheet.getRange(row, 1, 1, sheet.getLastColumn()).getValues();
To implement the change, create a column with checkboxes (lets say in column 29, since the columns before are ocupied with responses) and mofify your code as following:
function onEdit(e){
if(e.range.getColumn() == 29 || e.oldValue == false && e.value == true){
var row = e.range.getRow();
var sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
var values = sheet.getRange(row, 1, 1, sheet.getLastColumn()).getValues()[0];
var replaceTextToImage = function(body, searchText, fileId) {
var width = 300; // Please set this.
var blob = DriveApp.getFileById(fileId).getBlob();
var r = body.findText(searchText).getElement();
r.asText().setText("");
var img = r.getParent().asParagraph().insertInlineImage(0, blob);
var w = img.getWidth();
var h = img.getHeight();
img.setWidth(width);
img.setHeight(width * h / w);
}
//Get information from form and set as variables
var Technician = values[1];
var Customer_Name = values[2];
...
...
copyDoc.saveAndClose();
}
}
I do not not know coding - I'm trying to run the script below and recieve the error Missing ) after argument list on line 79 copyDoc.saveAndClose();
Blockquote
Here is the url for the doc:
https://docs.google.com/document/d/1FcbVBHmCcDfjcaKXSU5UeaOD30h1d5yTyQp_s28VPzc/edit?usp=sharing
here is the script. Please advise. Sincere Thanks.
function myFunction() {
// rsaez#shsd.org
// Travel Request
// Get template from Google Docs and name it
var docTemplate = "1FcbVBHmCcDfjcaKXSU5UeaOD30h1d5yTyQp_s28VPzc";
// *** replace with your template ID ***
var docName = "Travel_Request";
// When Form Gets submitted
function onFormSubmit(e) {
//Get information from form and set as variables
var email_address = "rsaez#shsd.org, rsaez#shsd.org";
var Building = e.values[5];
var Request = e.values[6];
var Total_Days = e.values[7];
var Substitute_Needed = e.values[8];
var Staff_Member_Type = e.values[9];
var Coverage_Type = e.values[10];
var Time_of_Day = e.values[11];
var Coverage = e.values[12];
var Request_Type = e.values[13];
var Destination = e.values[14];
var Purpose = e.values[15];
var City = e.values[16];
var State = e.values[17];
var Purpose_is_related_to = e.values[18];
var Registration = e.values[19];
var Lodging = e.values[20];
var Lodging_Cost = e.values[21];
var Lodging_Total = e.values[22];
var Meals = e.values[23];
var Meals_Cost = e.values[24];
var Meals_Total = e.values[25];
var Mileage = e.values[26];
var Other = e.values[27];
var Travel_Cost = e.values[28];
var Miscellanous = e.values[29];
// Get document template, copy it as a new temp doc, and save the Doc’s id
var copyId = DocsList.getFileById(docTemplate)
.makeCopy(docName+' for '+full_name)
.getId();
// Open the temporary document
var copyDoc = DocumentApp.openById(copyId);
// Get the document’s body section
var copyBody = copyDoc.getActiveSection();
// Replace place holder keys,in our google doc template
copyBody.replaceText('keyDate',
copyBody.replaceText('keyName',
copyBody.replaceText('keyPosition',
copyBody.replaceText('keyBuilding',
copyBody.replaceText('keyRequest',
copyBody.replaceText('keyDayT',
copyBody.replaceText('keySubstitute',
copyBody.replaceText('keyStaff',
copyBody.replaceText('keyDay',
copyBody.replaceText('keyTime',
copyBody.replaceText('keyCoverage',
copyBody.replaceText('keyRequest',
copyBody.replaceText('keyDest',
copyBody.replaceText('keyPurpose',
copyBody.replaceText('keyCity',
copyBody.replaceText('keySt',
copyBody.replaceText('keyPurpRelated',
copyBody.replaceText('keyRT',
copyBody.replaceText('keyLND',
copyBody.replaceText('keyLCP',
copyBody.replaceText('keyLT',
copyBody.replaceText('keyMND',
copyBody.replaceText('keyMCP',
copyBody.replaceText('keyMT',
copyBody.replaceText('keyMLT',
copyBody.replaceText('keyOT',
copyBody.replaceText('keyTotalBOE',
copyBody.replaceText('keyMisc',
// Save and close the temporary document
copyDoc.saveAndClose();
// copyDoc.saveAndClose() (options.cand_email);
// Convert temporary document to PDF
var pdf = DocsList.getFileById(copyId).getAs("application/pdf"
}
Your last line,
var pdf = DocsList.getFileById(copyId).getAs("application/pdf"
seems to be missing a ')' at the end. Perhaps that is the Missing ) after argument list.
The saveAndClose() method is automatically invoked at the end of script execution, for each open Document. Maybe it'll help: DocsList is deprecated since 2013, see https://developers.google.com/apps-script/sunset. You should use DriveApp instead.
var copyId = DriveApp.getFileById(docTemplate)
.makeCopy(docName+' for '+full_name)
.getId();
var pdf = DriveApp.getFileById(copyId).getAs('application/pdf');
I'm trying to sending a file as e-mail attachment with Google Apps Script, following this rich answer. But instead of a stand alone app, I'm trying to do so within my spreadsheet, using a function like this:
function sendAttachment(){
var activeSheet = ss.getActiveSheet();
ScriptProperties.setProperty('emailRequest', 1);
if(!person_ID) {
person_ID = getCurrentRow();
//if the current line is the Column Headers line then ask the user to specify the ID, very rare case.
if (person_ID == 1) {
var person_ID = Browser.inputBox("Select one name.", "Click in one row:", Browser.Buttons.OK_CANCEL);
}
}
var app = UiApp.createApplication().setHeight(400).setWidth(600);
var panel = app.createVerticalPanel(); // you can embed that in a form panel
var label = app.createLabel("Choose the receiver").setStyleAttribute("fontSize", 18);
app.add(label);
var currentRow = ss.getActiveSelection().getRowIndex();
var personName = activeSheet.getRange(currentRow, 1).getValue();
var personNumber = activeSheet.getRange(currentRow, 5).getValue();
var item1Panel = app.createHorizontalPanel();
var txt = app.createTextBox().setId("item1").setName("item1").setValue(personName);
item1Panel.add(app.createLabel("Person:")).add(txt);
var item2Panel = app.createHorizontalPanel();
var txt = app.createTextBox().setId("item2").setName("item2").setValue(personNumber);
item2Panel.add(app.createLabel("Num:")).add(txt);
var sheet = SpreadsheetApp.openById(letterSpreadsheetId).getSheetByName("emailsDB");
var recipientEmailArray = sheet.getRange(2, 1, sheet.getLastRow(), sheet.getLastColumn()).getValues();
var item3Panel = app.createHorizontalPanel();
item3Panel.add(app.createLabel("Receiver"));
var listBox = app.createListBox().setName('item3');
for(var i = 0; i < (recipientEmailArray.length); i++){
listBox.addItem(recipientEmailArray[i][0] + ": " + recipientEmailArray[i][2]);
}
item3Panel.add(listBox);
var handlerBut = app.createServerHandler("butSendAttachment").addCallbackElement(panel);
var but = app.createButton("submit").setId("submitButton4").addClickHandler(handlerBut);
panel.add(item1Panel)
.add(item2Panel)
.add(item3Panel)
.add(app.createFileUpload().setName('thefile'))
.add(app.createLabel().setId("answer"))
.add(but);
var scroll = app.createScrollPanel().setPixelSize(600, 400).setTitle("My title 1");
scroll.add(panel);
app.add(scroll);
ss.show(app);
// var handlerBut = app.createServerHandler("butSendAttachment").addCallbackElement(panel);
// .add(app.createFileUpload().setName('thefile'));
// var form = app.createFormPanel();
// form.add(panel);
// app.add(form);
;
}
function butSendAttachment(e){
var recipientEmail = e.parameter.item3;
var fileBlob = e.parameter.thefile;
Logger.log("file blob = " + fileBlob);
recipientEmail = recipientEmail.split(':')[1];
var sheet = ss.getActiveSheet();
var person_ID = getCurrentRow();
var columns = getRowAsArray(sheet, 1);
var personData = getRowAsArray(sheet, person_ID);
var sender = actAuthor + " \n " + position;
var name = personData[0];
var motherName = personData[1];
var title = "my title";
var message = my mesage";
var confirm = Browser.msgBox('Send email','Are you sure?', Browser.Buttons.OK_CANCEL);
if(confirm=='ok'){
// MailApp.sendEmail(recipientEmail, title, message, {attachments: [fileBlob]});
MailApp.sendEmail(recipientEmail, title, message, {attachments: [fileBlob]});
var app = UiApp.createApplication().setHeight(150).setWidth(250);
var msg = "An email was sendo to " + recipientEmail;
app.setTitle("E-mail send!");
app.add(app.createVerticalPanel().add(app.createLabel(msg)));
var doc = SpreadsheetApp.getActive();
doc.show(app);
}
else {
return;
}
}
But I get this error: Execution failed: Invalid argument: inlineImages (line 77. Line 77 is this:
MailApp.sendEmail(recipientEmail, title, message, {attachments: [fileBlob]});
I've read the documentation I tried several argument variations. I conclude that fileBlob is Null. Why? How to fix it?
the fileUpload widget works only in a form parent widget and using a doGet/doPost structure.
That's written somewhere in the doc but right now I don't remember exactly where (I'look for it later)
But actually I guess you can build such a structure from within your spreadsheet, just change the names of your functions and use a formPanel as main widget. Don't forget also that you don't need a handler anymore and must use a submitButton widget.
EDIT : how silly I am ! its written in the first line in the widget's doc !!! ;-)