getFilesByType not working - google-apps-script

I am trying to update my code to work with the new DriveApp API update. I got most of it working (I think) but it is hanging up on line 68 saying that it can't getFilesByType. I did try and change the loop for adding to be based off the iterator (I think) but still get this error: TypeError: Cannot find function getFilesByType in object FolderIterator
Any help would be appreciated thanks!
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/*
Template Generator By: Andre Fecteau - klutch2013#gmail.com
Original Code From: kiszal#gmail.com (Found in the template gallery by searching "Templates" It is the first One.
Major Help from: Serge Insas On Stack Overflow (He did most of the work.)
Link 1: http://stackoverflow.com/questions/18147798/e-undefined-google-script-error
Link 2: http://stackoverflow.com/questions/18132837/have-a-listbox-populate-with-every-folder-in-mydrive
How To Use:
First: each column is designated in your Template by {Column Letter} for example for column A it would be {A}
Second: You can change this, but your Template must be in a folder called "Templates." This folder can be anywhere in your drive.
Third: Click "Generate Documents Here!" Then click "Export Row to Document"
Fourth: Type in the row you want to export. Chose your Folder Path. Click Submit.
NOTE ON FOURTH STEP: If you want your number to skip the header row add a +1 to line 32.
This would mean if you typed "2" in the row box it actually exports row 3. I took this out because it can get confusing at times.
NOTE: Line 71 you can edit the word "Templates" to whatever folder you saved your Template into.
NOTE: Line 36 you can edit to change what comes up in the name of the file that is created. To do this just edit the column letter in the following piece: "+Sheet.getRange('E'+row).getValue()+"
You can then delete any other columns you do not want by deleteing the section of code that looks like the following: '+Sheet.getRange('D'+row).getValue()+'
Feel free to edit this code as you wish and for your needs. That is what I did with the original code.
So there is no reason I should restrict what others do with this code.
Bug Fix Log:
* changed row: 32 to remove the +1 6/18/2014
* changed row: 40 to remov e the row-- 6/18/2014
* changed row: 68 to update to DriveApp because of Google API Update - 4/21/15
* changed row: 68 to update to getFoldersByName and getFilesByType to update to new Google API - 4/21/15
*/
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
function generateDocument(e) {
var template = DriveApp.getFileById(e.parameter.Templates);
Logger.log(template.getName());
var Sheet = SpreadsheetApp.getActiveSpreadsheet();
var row = Number(e.parameter.row) //+1; // Remove the // in this line next to the +1 to skip headers
Logger.log(row);
var currentFID = e.parameter.curFID;
Logger.log(currentFID);
var myDocID = template.makeCopy(Sheet.getRange('B' + row).getValue() + ' - ' + Sheet.getRange('E' + row).getValue() + ' - ' + Sheet.getRange('D' + row).getValue() + ' - ' + Sheet.getRange('X' + row).getValue()).getId();
var myDoc = DocumentApp.openById(myDocID);
var copyBody = myDoc.getActiveSection();
var Sheet = SpreadsheetApp.getActiveSpreadsheet();
//row--; // decrement row number to be in concordance with real row numbers in sheet
var myRow = SpreadsheetApp.getActiveSpreadsheet().getRange(row + ":" + row);
for (var i = 1; i < Sheet.getLastColumn() + 1; i++) {
var myCell = myRow.getCell(1, i);
copyBody.replaceText("{" + myCell.getA1Notation().replace(row, "") + "}", myCell.getValue());
}
myDoc.saveAndClose();
var destFolder = DriveApp.getFolderById(currentFID);
Logger.log(myDocID);
var doc = DriveApp.getFileById(myDocID); // get the document again but using DriveApp this time...
doc.addToFolder(destFolder); // add it to the desired folder
doc.removeFromFolder(DriveApp.getRootFolder()); // I did it step by step to be more easy to follow
var pdf = DriveApp.getFileById(myDocID).getAs("application/pdf");
destFolder.createFile(pdf); // this will create the pdf file in your folder
var app = UiApp.getActiveApplication();
app.close();
return app;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
function getTemplates() {
var doc = SpreadsheetApp.getActiveSpreadsheet();
var app = UiApp.createApplication().setTitle('Generate from template');
// Create a grid with 3 text boxes and corresponding labels
var grid = app.createGrid(5, 2);
grid.setWidget(0, 0, app.createLabel('Template name:'));
var list = app.createListBox();
list.setName('Templates');
grid.setWidget(0, 1, list);
var docs = DriveApp.getFoldersByName("Templates").getFilesByType(documents); //Change the word "Templates" to whatever folder you saved your template into
/* OLD LOOP
for (var i = 0; i < docs.length; i++) {
list.addItem(docs[i].getName(),docs[i].getId());
}
*/
while (docs.hasNext()) {
var docs = docs.next();
list.addItem(docs[i].getName(), docs[i].getId());
}
grid.setWidget(1, 0, app.createLabel('Row:'));
var row = app.createTextBox().setName('row');
row.setValue(SpreadsheetApp.getActiveSpreadsheet().getActiveRange().getRow());
grid.setWidget(1, 1, row);
var curFN = app.createTextBox().setText('MyDrive/').setName('curFN').setId('curFN').setWidth('400');
var curFID = app.createTextBox().setText(DriveApp.getRootFolder().getId()).setName('curFID').setId('curFID').setVisible(false);
var listF = app.createListBox().setName('listF').setId('listF').addItem('Please Select Folder', 'x');
grid.setText(2, 0, 'Type Path:').setWidget(2, 1, curFN).setText(3, 0, 'OR').setText(4, 0, 'Choose Path:').setWidget(4, 1, listF).setWidget(3, 1, curFID);
var folders = DriveApp.getRootFolder().getFolders();
for (var i = 0; i < folders.length; i++) {
listF.addItem(folders[i].getName(), folders[i].getId())
}
var handlerF = app.createServerHandler('folderSelect').addCallbackElement(grid);
listF.addChangeHandler(handlerF);
var panel = app.createVerticalPanel();
panel.add(grid);
var button = app.createButton('Submit');
var handler = app.createServerClickHandler('generateDocument');
handler.addCallbackElement(grid);
button.addClickHandler(handler);
// Add the button to the panel and the panel to the application, then display the application app in the Spreadsheet doc
panel.add(button);
app.add(panel);
doc.show(app);
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
function folderSelect(e) {
var app = UiApp.getActiveApplication();
var currentFN = e.parameter.curFN;
var currentFID = e.parameter.listF;
Logger.log(currentFID);
var listF = app.getElementById('listF');
var curFN = app.getElementById('curFN');
var curFID = app.getElementById('curFID');
if (currentFID == 'x') {
currentFID = DriveApp.getRootFolder().getId();
curFN.setText('MyDrive/')
};
var startFolder = DriveApp.getFolderById(currentFID);
var folders = startFolder.getFolders();
listF.clear().addItem('No More Sub Folders!', 'x').addItem('Go back to Root', 'x');
if (folders.length > 0) {
listF.clear();
listF.addItem('Select Sub Folder', 'x')
};
for (var i = 0; i < folders.length; i++) {
listF.addItem(folders[i].getName(), folders[i].getId())
}
curFN.setText(currentFN + DriveApp.getFolderById(currentFID).getName() + '/');
if (currentFID == DriveApp.getRootFolder().getId()) {
curFN.setText('MyDrive/')
};
curFID.setText(currentFID);
return app;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
function onOpen() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var menuEntries = [{
name: "Export Row to Document",
functionName: "getTemplates"
}];
ss.addMenu("Generate Documents Here!", menuEntries);
}

You need two while loops:
var folders = DriveApp.getFoldersByName("Templates");
while (folders.hasNext()) {
var folder = folders.next();
Logger.log(folder.getName());
var allMyFilesByType = folder.getFilesByType(mimeType)
};
while (allMyFilesByType.hasNext()) {
var file = allMyFilesByType.next();
Logger.log(file.getName());
};
For MIME Types, see:
Google Documentation - MIME Types

Related

=URL() Script code issue | ReferenceError: btn is not defined (line 9, file "Code")

I am having an issue trying to use a script to extract the URL from one test with hyperlink.
Here is the script I am using:
function URL(reference) {
var sheet = SpreadsheetApp.getActiveSheet();
var formula = SpreadsheetApp.getActiveRange().getFormula();
var args = formula.match(/=\w+\((.*)\)/i);
try {
var range = sheet.getRange(args[1]);
}
catch(e) {
throw new Error(args[1] + 'is not a valid range');
}
var formulas = range.getFormulas();
var output = [];
for (var i = 0; i < formulas.length; i++) {
var row = [];
for (var j = 0; j < formulas[0].length; j++) {
var url = formulas[i][j].match(/=hyperlink\("([^"]+)"/i);
row.push(url ? url[1] : '');
}
output.push(row);
}
return output
}
After I run the script I get this error message:
TypeError: Cannot read property '1' of null (line 9, file "Code")
Any idea where the issue comes from and I can solve this?
You have try/catch block that throws error, but in catch block you are trying to iterate null object again:
try {
var range = sheet.getRange(args[1]);
}
catch(e) {
throw new Error(args[1] + 'is not a valid range'); // <- `args[1]` is producing new error
}
If you change catch content to throw new Error(formula + 'is not a valid range');, it should work.
Retrieve the hyperlink with the Advanced Sheets Service and assign the script to a button
The way you are following so far (extracting a hyperlink from a formula) is an old solution that does not work anymore
Currently, to retrieve a spreadsheet you need to use the Sheets API method spreadsheets:get
To use the Sheest API in Apps Script, you need to enable it in the editor
Sample script:
function URL() {
var spreadsheet =SpreadsheetApp.getActive();
var sheet = spreadsheet.getActiveSheet();
var id = spreadsheet.getId();
var range = sheet.getActiveRange();
var linkArray = Sheets.Spreadsheets.get(id, {ranges: sheet.getName() + "!" + range.getA1Notation(), fields: "sheets/data/rowData/values/hyperlink"});
var link = linkArray.sheets[0].data[0].rowData[0].values[0].hyperlink;
var cellInB = sheet.getRange(range.getRow(), 2);
cellInB.setValue(link);
}
As mentioned in the comments, in your use case it is not possible to use custom formulas, instead create a drawing and assign the script to it:
Now, select a cell in column A, click on the button and the link will be populated in column B.
UPDATE:
To retrieve all URLs at once you can use the followign script and run it manually:
function getAllURLs() {
var spreadsheet = SpreadsheetApp.getActive();
var sheet = spreadsheet.getActiveSheet();
var id = spreadsheet.getId();
var lastRow = sheet.getRange("A2").getNextDataCell(SpreadsheetApp.Direction.DOWN).getRow();
var range = sheet.getRange(2,1,lastRow-1, 1);
var linkArray = Sheets.Spreadsheets.get(id, {ranges: sheet.getName() + "!" + range.getA1Notation(), fields: "sheets/data/rowData/values/hyperlink"});
var links = [];
for (var i = 0; i< linkArray.sheets[0].data[0].rowData.length; i++){
var link = linkArray.sheets[0].data[0].rowData[i].values[0].hyperlink;
links[i] = [];
links[i].push(link);
}
var cellsInB = sheet.getRange(2,2,lastRow-1, 1);
cellsInB.setValues(links);
}

"TypeError: Cannot read property 'getBlob' of undefined" when trying to get two different images and insert them into a document

I have a Google Apps Script that automatically extracts data from a Google Sheet and inserts it into a pre specified template. The data is found using unique tagNumbers/identifiers.
The data being extracted includes 3 signatures. I can only extract one of these signatures before I encounter the aforementioned error: TypeError: Cannot read property 'getBlob' of undefined.
This code is being used in two different functions, using all the same variables and names. I have tried changing variable names but this has resulted in the same TypeError.
The spreadsheet can be found here.
The script is here.
And the document template being filled is here.
Here is the code.
function electInstallSignature(row, body){
var signature = row[17];
var sign = signature.substring(signature.indexOf("/") + 1);
var sigFolder = DriveApp.getFolderById("16C0DR-R5rJ4f5_2T1f-ZZIxoXQPKvh5C");
var files = sigFolder.getFilesByName(sign);
var n = 0;
var file;
while(files.hasNext()){
file = files.next();
n++;
} if(n>1){
SpreadsheetApp.getUi().alert('there is more than one file with this name' + sign);
}
var sigElectInstaller = "%SIGNELECTINSTALL%";
var targetRange = body.findText(sigElectInstaller); // Finding the range we need to focus on
var paragraph = targetRange.getElement().getParent().asParagraph(); // Getting the Paragraph of the target
paragraph.insertInlineImage(1, file.getBlob());// As there are only one element in this case you want to insert at index 1 so it will appear after the text // Notice the .getBlob()
paragraph.replaceText(sigElectInstaller, ""); // Remove the placeholder
}
function commEngineerSignature(row, body){
var signature = row[35];
var sign = signature.substring(signature.indexOf("/") + 1);
var sigFolder = DriveApp.getFolderById("16C0DR-R5rJ4f5_2T1f-ZZIxoXQPKvh5C");
var files = sigFolder.getFilesByName(sign);
var n = 0;
var file;
while(files.hasNext()){
file = files.next();
n++;
} if(n>1){
SpreadsheetApp.getUi().alert('there is more than one file with this name' + sign);
}
var sigCommEngineer = "%SFCE%";
var targetRange = body.findText(sigCommEngineer); // Finding the range we need to focus on
var paragraph = targetRange.getElement().getParent().asParagraph(); // Getting the Paragraph of the target
paragraph.insertInlineImage(1, file.getBlob());// As there are only one element in this case you want to insert at index 1 so it will appear after the text // Notice the .getBlob()
paragraph.replaceText(sigCommEngineer, ""); // Remove the placeholder
}
As you can see, the code is the exact same in both functions, but only works in the electInstallSignature(row, body) function.
Below you can find where the row and body parameters are declared.
function chooseRowMethodI(templateId, rowNumber){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet();
var dataRange = sheet.getDataRange();
var values = dataRange.getValues();
var data = sheet.getRange(2, 2, 10, 41).getValues();//starting with row 2 and column 1 as our upper-left most column, get values from cells from 1 row down, and 15 columns along - hence (2,1,1,15)
var docTitle = sheet.getRange(2, 2, 10, 1).getValues();//this is grabbing the data in field B2
var docTitleTagNumber = sheet.getRange(2, 5, 11, 1).getValues();
var today = new Date();
var dd = today.getDate();
var mm = today.getMonth() + 1;
var yyyy = today.getFullYear();
today = dd + '/' + mm + '/' + yyyy;
for(var i = 0; i < values.length; i++){
for(var j = 0; j < values[i].length; j++){
if(values[i][j] == response){
Logger.log(i);
var row = data[rowNumber];
var docId = DriveApp.getFileById(templateId).makeCopy().getId();
var doc = DocumentApp.openById(docId);
var body = doc.getActiveSection();
//************************** All Instruments data in here**********************
instrumentDetails(body, row);
electInstallSignature(row, body);
commEngineerSignature(row, body);
doc.saveAndClose();
var file = DriveApp.getFileById(doc.getId());
var newFolder = DriveApp.getFolderById("1Jylk3uO_WU0ClLQdm9y-mwRfHxlh2Ovn");
newFolder.addFile(file);
var newDocTitle = docTitle[i - 1][0];
var newDocTagNumber = docTitleTagNumber[i - 1][0];
doc.setName(newDocTitle + " " + newDocTagNumber + " " + today);
}
}
}
}
Should it be required, I have included the function where everything is run from (note that any ui and user input code is tabbed out to avoid having to navigate back to the spreadsheet every time the code is run).
var response = "FT101";
function chooseRow(){
// var ui = SpreadsheetApp.getUi(); // Same variations.
// var result = ui.prompt('Please enter the Tag number of the row you wish to print.', ui.ButtonSet.OK_CANCEL);
//
// // Process the user's response.
// var button = result.getSelectedButton();
// response = result.getResponseText();
// if (button == ui.Button.OK) {
// // User clicked "OK".
// ui.alert('Your tag number is' + response + '.');
// } else if (button == ui.Button.CANCEL) {
// // User clicked X in the title bar.
// ui.alert('You closed the dialog.');
// return 'the end';
// }
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet();
var dataRange = sheet.getDataRange();
var values = dataRange.getValues();
var category = sheet.getRange(2, 3, 11, 1).getValues();//Needs to be verified to ensure correct cell is chosen by script
var tags = sheet.getRange(2, 5, 11, 1).getValues();//Needs to be verified to ensure correct cell is chosen by script
for(var i = 0; i < tags.length; i++){
if(tags[i][0] == response && category[i][0] == "Instrument"){
var templateId = "1N3o951ECS5CAVGE6UgqBiCPC7H7LiJbL7Cd59G1xTnA";
chooseRowMethodI(templateId, i);
return "";
} else if(tags[i][0] == response && category[i][0] == "Motor" || tags[i][0] == response && category[i][0] == "Valve"){
var templateId = "1cSPD23qFd-34-IIr5eJ5a5OgHp9YR6xav9T28Y4Msec";
chooseRowMethodMV(templateId, i);
return "";
}
}
}
This errors TypeError: Cannot read property 'getBlob' of undefined means that the object you are trying to getBlob from it does not have any blob data.
The only difference with the frist function and the second one is the first line: row[17] instead of row[35] this means the following:
var signature = row[17];
var sign = signature.substring(signature.indexOf("/") + 1);
var sigFolder = DriveApp.getFolderById("16C0DR-R5rJ4f5_2T1f-ZZIxoXQPKvh5C");
var files = sigFolder.getFilesByName(sign);
var n = 0;
var file;
while(files.hasNext()){
file = files.next();
n++;
} if(n>1){
SpreadsheetApp.getUi().alert('there is more than one file with this name' + sign);
}
So, you are probably never accessing to the while loop:
while(files.hasNext())
because var files = sigFolder.getFilesByName(sign); never had a next, and thus, as file is not initialized, it is undefined.
In summary, the error you are getting is:
file is undefined
This means that you never assigned anything on this variable, which only happens if you never accessed the while, which only happens if files never had a next.
Which happens because there aren't any files at all there, this means that there isn't any file with the name sign on the sigFolder. Or that the row[17] does not contain any substantial information about the filename you want to access.
So, check this.
Also, take into account the following documentation about iterators like the one you are handling on files:
When you do files.next() you are accesing the first element of the iterator:
File iterator
General documentation on JavaScript iterators:
Iterators and generators on Javascript

Google Script is outputting image file path instead of image

I am working on an app for use on sites that deal with water treatment and waste management.
This app is being developed using appsheet.com
Appsheet creates apps using spreadsheets and stores all data input into the app in these spreadsheets.
I have a script that extracts data from a spreadsheet and applies it to a Google doc template.
The at the end of the template there is a signature section.
Appsheet has a built in signature feature, which stores these signatures as a png file in a subfolder.
In the spreadsheet, the image is kept as a file path, as seen here
When run, the script interprets this as the text you see and applies it to my template.
I need to be able to read this and apply the image, not the file path.
I have tried using some of the various image classes explained on the Apps Script Reference, none of which have worked for me.
function chooseRowMethod(templateId){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet();
var dataRange = sheet.getDataRange();
var values = dataRange.getValues();
var data = sheet.getRange(2, 2, 11, 18).getValues();//starting with row 2 and column 1 as our upper-left most column, get values from cells from 1 row down, and 15 columns along - hence (2,1,1,15)
var docTitle = sheet.getRange(2, 2, 11, 1).getValues();//this is grabbing the data in field B2
var docTitleTagNumber = sheet.getRange(2, 3, 11, 1).getValues();
var today = new Date();
var dd = today.getDate();
var mm = today.getMonth() + 1;
var yyyy = today.getFullYear();
today = dd + '/' + mm + '/' + yyyy;
for(var i = 0; i < values.length; i++){
for(var j = 0; j < values[i].length; j++){
if(values[i][j] == response){
Logger.log(i);
var row = data[i - 1];
var docId = DriveApp.getFileById(templateId).makeCopy().getId();
var doc = DocumentApp.openById(docId);
var body = doc.getActiveSection();
body.replaceText("%SITEID%", row[0]);
...
body.replaceText("%SIGNED%", row[16]);
doc.saveAndClose();
var file = DriveApp.getFileById(doc.getId());
var newFolder = DriveApp.getFolderById("16wRGBVdV0OZ5YfKhqEQSFMsux-ekGCCa");
newFolder.addFile(file);
var newDocTitle = docTitle[i - 1][0];
var newDocTagNumber = docTitleTagNumber[i - 1][0];
doc.setName(newDocTitle + " " + newDocTagNumber + " " + today);
}
}
}
}
I am aware that the body.replaceText() line does exactly as it says. I have tried using body.setImage(), but this also failed to get any results.
The expect result is picture that is taken from its folder and applied to the template using the file path that is given in the spreadsheet.
Problem
You want to insert image from Google Drive to Google Docs document body.
Solution
Use the InlineImage element (class) and corresponding appendImage() or insertImage() methods. This is done in three simple steps:
Access your image file (e.g. DriveApp.getFileById() or similar, it's up to you);
Get file data as Blob class instance via getBlob() method;
invoke appendImage() or insertImage() with blob as argument (note that insertImage() requires child element index as its first argument).
Sample
function insertImage() {
var doc = DocumentApp.create('TEST');
var body = doc.getBody();
var image = DriveApp.getFileById('yourIdHere').getBlob();
var inline = body.appendImage(image);
}
Reference
insertImage() method reference;
appendImage() method reference;
getBlob() method reference;
After spending some time on other parts of this project, I have come back to this problem and found a solution using the following code.
var signature = row[17];
var sign = signature.substring(signature.indexOf("/") + 1);
var sigFolder = DriveApp.getFolderById("16C0DR-R5rJ4f5_2T1f-ZZIxoXQPKvh5C");
var files = sigFolder.getFilesByName(sign);
var n = 0;
var file;
while(files.hasNext()){
file = files.next();
n++;
} if(n>1){
SpreadsheetApp.getUi().alers('there is more than one file with this name' + sign);
}
var sigElectInstaller = "%SIGNELECTINSTALL%";
var targetRange = body.findText(sigElectInstaller); // Finding the range we need to focus on
var paragraph = targetRange.getElement().getParent().asParagraph(); // Getting the Paragraph of the target
paragraph.insertInlineImage(1, file.getBlob());// As there are only one element in this case you want to insert at index 1 so it will appear after the text // Notice the .getBlob()
paragraph.replaceText(sigElectInstaller, ""); // Remove the placeholder

Create PDF copy of doc App script

I am attempting to create a PDF based on the entries in a spreadsheet with the basic flow of the following.
Fill in spreadsheet
Fire off the submit from a menu button (pop up for asking for Job Number)
Templated Doc gets a copy made and cell contents appended to a table in the copy doc.
Copy doc gets saved as a PDF in a specific folder.
copy of doc gets deleted.
I am just trying to get the process of getting the PDF created in the specified folder. It was working prior to my creating a copy of the Doc and just using the template so I am not sure why it will not work now. Any input would be appreciated as well as any info on how to append the cell data from the sheet into the copy of the doc. Newby to be sure so any help would be appreciated. Code attached
function createDoc () {
var job = Browser.inputBox('Enter Job Number', 'Job Number', Browser.Buttons.OK);
var dtStr = Utilities.formatDate(new Date(), "GMT", "MMddyy")
// create temp file before edited with spreadsheet data
var tmpName = "tmpname"
var folder = DriveApp.getFolderById('1C_k3MvoT33WhSXVNMmFQNFhqaW8')
var tmpl = DriveApp.getFileById('225xZAECq0rkdJnsr4k9VjL91B7vgJh8Y- t9YrsbCEgc').makeCopy(tmpName).getID();
// get document and make PDF in folder
var doc = DriveApp.getFileByID(tmpl).getAs("application/pdf");
var pdf = doc.setName(job +"-"+dtStr+".pdf");
folder.createFile(pdf)
}
I only see some typos in some functions names, remember Google Apps Script is a scripting language based on JavaScript, so:
JavaScript is a case-sensitive language. This means that language
keywords, variables, function names, and any other identifiers must
always be typed with a consistent capitalization of letters.
On this line:
var tmpl = DriveApp.getFileById('').makeCopy(tmpName).getID();
.getID() the D must be lowercase, just change it to .getId()
On this line:
doc = DriveApp.getFileByID(tmpl).getAs("application/pdf");
.getFileByID() the D must also be lowercase, just change it to .getFileById()
To delete the temporary document, you can use the removeFile() but first you need to get the file, not just the id, so I recommend before getting the id of the copy, you get the file and then get the file's id, like this:
var blob = DriveApp.getFileById('yourId').makeCopy(tmpName)
var tmpl = blob.getId();
Then after the creation of the pdf, you can delete with this:
folder.removeFile(blob);
To create Custom Menus, the official documentation has some good examples.
EDIT:
This is an example to append a table to a Google Doc that can get you started, the cell variable you can change it to the range of data of your Spreadsheet:
function appendTable(){
var document = DocumentApp.openById('docId');
var body = document.getBody();
var cells = [
['Row 1, Cell 1', 'Row 1, Cell 2'],
['Row 2, Cell 1', 'Row 2, Cell 2']
];
body.appendTable(cells);
document.saveAndClose();
}
I have included the simple of the code that I use for this same purpose. This includes a checker that denotes where the merge code is in the process. This is helpful when processing many rows at a time. It also provides the url of the created file. The way that I use this is with Google Forms where the data is sent to "Form Responses" and is then sent to "Merge Data" for continued functions using =QUERY().
This code leaves you with both the Google Doc and the final .pdf file. If you would like to have only the .pdf, simply repeat the .setTrashed() method on the Google Doc variable.
I do realize there are some redundencies in the code as the more complicated version contains vastly more if/else statements, condition checkers, data processing, etc. You can pare down the code as you see fit.
In my Google Sheet, the first two columns were the timestamp of submission and the name of the person submitting. These were used in the naming of the file.
function mergeApplication() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName("Merge Data");
var range = sheet.getActiveRange();
var formSheet = ss.getSheetByName("Form Responses");
var lastRow = formSheet.getLastRow();
var lastColumn = sheet.getMaxColumns();
function checkAndComplete() {
var urlColumn = lastColumn;
var checkColumn = (urlColumn - 1);
var checkRange = sheet.getRange(2, checkColumn, (lastRow - 1), 1);
var check = checkRange.getBackgrounds();
var red = "#ff0404";
var yellow = "#ffec0a";
var green = "#3bec3b";
for (var i = 0; i < check.length; i++) {
if (check[i] == green) {
continue;
} else {
var statusCell = sheet.getRange((i+2), checkColumn, 1, 1);
var urlCell = sheet.getRange((i+2), urlColumn, 1, 1);
var dataRow = sheet.getRange((i+2), 1, 1, (lastColumn - 2));
function mergeTasks() {
function docCreator() {
var docTemplate = DriveApp.getFileById("docid");
var docToUse = docTemplate;
var folderDestination = DriveApp.getFolderById("folderid");
var name = sheet.getRange((i+2), 2, 1, 1).getValue();
var rawSubmitDate = sheet.getRange((i+2), 1, 1, 1).getValue();
var submitDate = Utilities.formatDate(rawSubmitDate, "PST", "MM/dd/yy");
var docName = "File Name - " + name + " - " + submitDate;
var docCopy = docToUse.makeCopy(docName, folderDestination);
var docId = docCopy.getId();
var docURL = DriveApp.getFileById(docId).getUrl();
var docToSend = DriveApp.getFileById(docId);
var docInUse = DocumentApp.openById(docId);
var docBody = docInUse.getBody();
var docText = docBody.getText();
function tagReplace() {
var taggedArray = docText.match(/\<{2}[\w\d\S]+\>{2}/g);
var headerArray = sheet.getRange(1, 1, 1, (lastColumn - 2)).getValues();
var dataArray = dataRow.getValues();
var strippedArray = [];
function tagStrip() {
for (var t = 0; t < taggedArray.length; t++) {
strippedArray.push(taggedArray[t].toString().slice(2, -2));
}
}
function dataMatch() {
for (var s = 0; s < strippedArray.length; s++) {
for (var h = 0; h < headerArray[0].length; h++) {
if (strippedArray[s] == headerArray[0][h]) {
docBody.replaceText(taggedArray[s], dataArray[0][h]);
}
}
}
docInUse.saveAndClose();
}
tagStrip();
dataMatch();
}
tagReplace();
statusCell.setBackground(yellow);
var pdfDocBlob = docToSend.getAs(MimeType.PDF);
var pdfDocInitial = DriveApp.createFile(pdfDocBlob).setName(docName);
var pdfDoc = pdfDocInitial.makeCopy(folderDestination);
pdfDocInitial.setTrashed(true);
urlCell.setValue(docURL);
}
statusCell.setBackground(red);
docCreator();
statusCell.setBackground(green);
}
mergeTasks();
}
}
}
checkAndComplete();
}
This process will run through systematically, takes about 5 seconds per row, and will be creating each file at the root of your Drive but quickly deletes it from the root. There may be a more simple way to perform this that saves space in your trash but I did not research more efficient methods.

Error when passing a blob file to send it as e-mail attachment

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 !!! ;-)