DocumentApp.openById() fails with “Service unavailable” - google-apps-script

I am trying to read the contents of a spreadsheet which contains some folderId, fileName and targetFile and then based on the data entered in the spreadsheet.
I am finding the latest fileId in the drive for the same fileName as multiple files with the same name are getting added into the folder daily (this is done by the function mostRecentFiIeInFolder) and then I am trying to copy the contents of the latest file with ID dociIdSource into a different file with ID docIdTarget (which is done by the function docCopy).
But when I tried Implementing this using DocumentApp, I am getting a weird error which says
Service unavailable: Docs
for the code var baseDoc = DocumentApp.openById(docID);.
May I know where I am going wrong?
// Test function to call applyDocCopytoList.
function test(){
applyDocCopytoList();
}
// Read the values from the spreadsheet.
function applyDocCopytoList(){
var originalSpreadsheet = SpreadsheetApp.openById('sheet Id goes here').getSheetByName("sheet name goes here");
var getRange = originalSpreadsheet.getDataRange();
var data = originalSpreadsheet.getDataRange().getValues();
for (var i = 1; i < data.length; i++) {
var folderId = data[i][1];
var fileName = data[i][2];
var targetFile = data[i][3];
Logger.log('****************Record No: ' + i);
Logger.log('folderId: ' + data[i][1]);
Logger.log('fileName: ' + data[i][2]);
Logger.log('targetFile: ' + data[i][3]);
var latestFileId = mostRecentFiIeInFolder(folderId, fileName);
if(latestFileId!= undefined){
docCopy(latestFileId, targetFile);
}
}
}
// Log the id of the latest file with a particular name in the folder.
function mostRecentFiIeInFolder(folderId, fileName) {
var folder = DriveApp.getFolderById(folderId);
Logger.log(folder);
var files = DriveApp.getFilesByName(fileName);
Logger.log(fileName);
var result = [];
// Checks whether the given file is in the folder or not
if(!files.hasNext()){
Logger.log('No such file in the folder with the given name');
}
else{
while (files.hasNext()) {
var file = files.next();
result.push([file.getDateCreated(), file.getId()]);
}
Logger.log('************All the file ids with the same file name and their dates created************');
Logger.log(result);
result.sort(function (x, y){
var xp = x[0];// get first element in inner array
var yp = y[0];
return xp == yp ? 0 : xp > yp ? -1 : 1;// choose the sort order, here its in descending order of created date
});
var id = result[0][1];
Logger.log(id);
return id;
}
}
// Copy the contents of the latest file in the target file.
function docCopy(dociIdSource, docIdTarget){
Logger.log('The file with id: ' + dociIdSource + ' will be copied to the target id: ' + docIdTarget);
var docID = docIdTarget;
var baseDoc = DocumentApp.openById(docID); //Service unavailable: Docs error is thrown for this line of code
var body = baseDoc.getBody();
var otherBody = DocumentApp.openById(dociIdSource).getBody();
var totalElements = otherBody.getNumChildren();
for( var j = 0; j < totalElements; ++j ) {
var element = otherBody.getChild(j).copy();
var type = element.getType();
if( type == DocumentApp.ElementType.PARAGRAPH )
body.appendParagraph(element);
else if( type == DocumentApp.ElementType.TABLE )
body.appendTable(element);
else if( type == DocumentApp.ElementType.LIST_ITEM )
body.appendListItem(element);
else if( type == DocumentApp.ElementType.INLINE_IMAGE )
body.appendImage(element);
else if( type == DocumentApp.ElementType.TEXT )
body.setText(element);
else
throw new Error("According to the doc this type couldn't appear in the body: " + type);
}
}

Note that your mostRecentFiIeInFolder function never actually uses the folder, and doesn't ever check that the files are of the correct type - i.e., are actually Google Docs files. Thus if your searched name should have found nothing (i.e. there is no recent file with that name in your target folder), but you had some alternate file elsewhere in your Drive, one that is not a Google Docs file, your script will find it and treat it as something it is not.
The solution is to restrict your search to your desired folder, and again by actual Google Docs mimetype:
function testIt() {
var folderIds = [
"", // Search all of Drive
"123428349djf8234", // Search that specific folder
];
// Log (in Stackdriver) the file id of the most recently created Google Docs file with the name "some name" in the various folders:
folderIds.forEach(function (folderId) {
console.log(getMostRecentFileIdWithName("some name", folderId, MimeType.GOOGLE_DOCS));
});
}
function getMostRecentFileIdWithName(fileName, folderId = "", mimeType = "") {
// If a folder was given, restrict the search to that folder. Otherwise, search with
// DriveApp. (Throws an error if the folderId is invalid.)
var parent = folderId ? DriveApp.getFolderById(folderId) : DriveApp;
// I assume your fileName variable does not contain any unescaped single-quotes.
var params = "name='" + fileName + "'";
// If a mimetype was given, search by that type only, otherwise search any type.
if (mimeType)
params += " and mimeType='" + mimeType + "'";
var matches = parent.searchFiles(params),
results = [];
// Collect and report results.
while (matches.hasNext())
results.push(matches.next());
if (!results.length)
throw new Error("Bad search query \"" + params + "\" for folder id = '" + folderId + "'.");
// Sort descending by the creation date (a native Date object).
// (a - b sorts ascending by first column).
results.sort(function (a, b) { return b.getDateCreated() - a.getDateCreated(); });
return results[0].getId();
}
You can read more about the acceptable search parameters in the Drive REST API documentation, and more about the Apps Script native implementation in the DriveApp documentation.

Related

Google Sheets - Convert comma to "#" before generate .CSV

I have the following script in a Google Sheet:
/**
* Create CSV file of Sheet2
* Modified script written by Tanaike
* https://stackoverflow.com/users/7108653/tanaike
*
* Additional Script by AdamD.PE
* version 13.11.2022.1
* https://support.google.com/docs/thread/188230855
*/
/** Date extraction added by Tyrone */
const date = new Date();
/** Extract today's date */
let day = date.getDate();
let month = date.getMonth() + 1;
let year = date.getFullYear();
if (day < 10) {
day = '0' + day;
}
if (month < 10) {
month = `0${month}`;
}
/** Show today's date */
let currentDate = `${day}-${month}-${year}`;
/** Date extraction added by Tyrone */
function sheetToCsvModelo0101() {
var filename = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet().getSheetName() + "-01" + " - " + currentDate; // CSV file name
filename = filename + '.csv';
var ssid = SpreadsheetApp.getActiveSpreadsheet().getId();
var folders = DriveApp.getFileById(ssid).getParents();
var folder;
if (folders.hasNext()) {
folder = folders.next();
var user = Session.getEffectiveUser().getEmail();
if (!(folder.getOwner().getEmail() == user || folder.getEditors().some(e => e.getEmail() == user))) {
throw new Error("This user has no write permission for the folder.");
}
} else {
throw new Error("This user has no write permission for the folder.");
}
var SelectedRange = "A2:AB3";
var csv = "";
var v = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet().getRange(SelectedRange).getValues();
v.forEach(function (e) {
csv += e.join(",") + "\n";
});
var newDoc = folder.createFile(filename, csv, MimeType.CSV);
console.log(newDoc.getId()); // You can see the file ID.
}
This script basically creates a .CSV file in the same folder where the worksheet is, using the range defined in var SelectedRange.
This script is applied to a button on the worksheet.
The question is: how do I make every comma typed in this spreadsheet be converted into another sign, like # before generating the .CSV file in the folder?
I would also like to know if instead of generating 1 file in the folder it is possible to generate 2 files, each with a name.
Issue:
The question is: how do I make every comma typed in this spreadsheet be converted into another sign, like # before generating the .CSV file in the folder?
After you get the sheet values via getValues, replace all instances of , in the resulting 2D array with #, using map and replaceAll.
I think this is a better approach than TextFinder since sheet values are not modified.
Code snippet:
From your original sample, just add the following line:
// ...stuff...
var v = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet().getRange(SelectedRange).getValues();
v = v.map(r => r.map(c => c.replaceAll(",", "#"))); // Add this line
v.forEach(function (e) {
csv += e.join(",") + "\n";
});
// ...stuff...
If you are doing this to avoid conflicts between the comma in the cells and the csv delimiter then try doing the csv like this:
function sheetToCsv() {
const ss = SpreadsheetApp.getActive();
const sh = ss.getSheetByName("Sheet0")
const params = { "method": "GET", "headers": { "Authorization": "Bearer " + ScriptApp.getOAuthToken() } };
const url = "https://docs.google.com/spreadsheets/d/" + ss.getId() + "/export?gid=" + sh.getSheetId() + "&format=csv";
const r = UrlFetchApp.fetch(url, params);
const csv = r.getContentText();
return csv;
}
And then put it back in a spreadsheet like this:
function csvToSheet(csv) {
const vs = Utilities.parseCsv(csv,',');
const osh = ss.getSheetByName("Sheet1");
osh.getRange(1,1,vs.length,vs[0].length).setValues(vs);
}
In the meantime I've found a solution that almost works the way I'd like.
I created 2 functions, one to convert , to # and another to convert # to , again, then after the .csv file creation is complete the script switches back from # to , .
/**
* Create CSV file of Sheet2
* Modified script written by Tanaike
* https://stackoverflow.com/users/7108653/tanaike
*
* Additional Script by AdamD.PE
* version 13.11.2022.1
* https://support.google.com/docs/thread/188230855
*/
var SelectedRange = "A2:AB3";
function searchAndReplace_ToHash() {
SpreadsheetApp.getActiveSpreadsheet().getActiveSheet().getRange(SelectedRange).createTextFinder(',').replaceAllWith('#');
}
function searchAndReplace_ToComma() {
SpreadsheetApp.getActiveSpreadsheet().getActiveSheet().getRange(SelectedRange).createTextFinder('#').replaceAllWith(',');
}
function sheetToCsv_02() {
var filename = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet().getSheetName() + "-01" + " - " + currentDate; // CSV file name
filename = filename + '.csv';
var ssid = SpreadsheetApp.getActiveSpreadsheet().getId();
searchAndReplace_ToHash()
// I modified below script.
var folders = DriveApp.getFileById(ssid).getParents();
var folder;
if (folders.hasNext()) {
folder = folders.next();
var user = Session.getEffectiveUser().getEmail();
if (!(folder.getOwner().getEmail() == user || folder.getEditors().some(e => e.getEmail() == user))) {
throw new Error("This user has no write permission for the folder.");
}
} else {
throw new Error("This user has no write permission for the folder.");
}
var csv = "";
var v = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet().getRange(SelectedRange).getValues();
v.forEach(function (e) {
csv += e.join(",") + "\n";
});
var newDoc = folder.createFile(filename, csv, MimeType.CSV);
console.log(newDoc.getId()); // You can see the file ID.
searchAndReplace_ToComma()
}
It solves the problem, but it would be perfect if this change was not visible in the spreadsheet.
Is it possible to make this substitution without displaying it in the spreadsheet?
As for your script suggestion, I would like to change as little as possible in this script I'm using, it works exactly the way I need it to work, except for the fact that the commas of words conflict with the column divisions.
Anyway, thank you very much for all your attention and patience!

Script only works when uploading a single file

When an employee submits the google form, this script renames the file they have uploaded based off of information said employee fills out in the form. (At the moment it is pulling their Name, Job ID, and Address of the current job location, the file is always pictures of what they completed on the jobsite.)
However the script only pulls this information if a single file/picture is uploaded the form instead of all files.
What modification would allow this to rename all uploaded files in the form.
while (files.hasNext()) {
for (var i = 0; i < formResponses.length; i++) {
var formResponse = formResponses[i];
var itemResponses = formResponse.getItemResponses();
var itemResponseFname = itemResponses[0];
var itemResponseLname = itemResponses[10];
var itemResponseID = itemResponses[11];
var itemResponsePhoto = itemResponses[13];
var photoID = itemResponsePhoto.getResponse();
var newName = itemResponseFname.getResponse() + " " + itemResponseLname.getResponse() + " - " + itemResponseID.getResponse();
var url = baseString + photoID + endString;
var urlCheck = file.getUrl();
if ( url == urlCheck) {
var modName = newName + ".jpg";
file.setName(modName);
The easiest way of doing such thing is the following:
Install a submit trigger
Get the list of IDs of submitted files
Change its name to whatever you like
In practice this will look something like this:
function submit(e) {
const itemResponses = e.response.getItemResponses()
// Read values
const fname = itemResponses[0].getResponse()
const lname = itemResponses[10].getResponse()
const uid = itemResponses[11].getResponse()
const imageIds = itemResponses[13].getResponse()
// Iterate images
for (let imgId of imageIds){
const image = DriveApp.getFileById(imgId)
// Create new filename (preserves extension)
const filename = `${fname} ${lname} - ${uid}${getExtension(image)}`
// Set file name
image.setName(filename)
}
}
/**
* Returns the extension of a file
*
* For example: A file with name 'example.json' will return '.json'.
*
* #param file {DriveApp.File} File to extract the extension from
* #returns {string} The extension with the dot.
*/
function getExtension(file) {
// Gets the last dot and the characters that follow
const r = /(\.\w+)$/.exec(file.getName())
// If it has no extension return an empty string, otherwise return the captured group
return r === null ? '' : r[1]
}
Obviously you need to add all your other code that you seem to have and install the trigger if you haven't, already.

Get File by name at specific folder

Trying to replicate this code to fine a specific file by name, by I got error at var files = DriveApp.getFilesByName(fileName); as Exception: Invalid argument at fileName, but if I entered the fileNamemanually as string "10504-China-ReflectiveTape-NA.jpg"` it is running correctly!
Below the full code:
// ****EXAMPLE****
//My arguments for the function
var myFileName = "10504-China-ReflectiveTape-NA.jpg";
var myFileParentFolderName = "Catalog";
//Run the function
var getFileID = getFileByName(myFileName, myFileParentFolderName);
//Check if folder exists
if(getFileID.id === false){ //if file cannot be accurately found.
Logger.log(getFileID.error); //alert or log error. Give option to try another FileName
}else{
// If the file ID exists then proceed with the program.
Logger.log(getFileID.id);
};
/*
* ****Get File By Name***
*
*param 1: File Name
*param 2: Parent Folder of File (optional)
*
*returns: Dictionary of file "id" and "error" message {"id": ,"error": }
* -if there is no error, "id" returns file id and "error" returns false
* -if there is an error, "id" returns false and "error" returns type of error as a string for user to display or log.
*/
function getFileByName(fileName, fileInFolder){
var filecount = 0;
var dupFileArray = [];
var folderID = "";
var files = DriveApp.getFilesByName(fileName);
while(files.hasNext()){
var file = files.next();
dupFileArray.push(file.getId());
filecount++;
};
if(filecount > 1){
if(typeof fileInFolder === 'undefined'){
folderID = {"id":false,"error":"More than one file with name: "+fileName+". \nTry adding the file's folder name as a reference in Argument 2 of this function."}
}else{
//iterate through list of files with the same name
for(fl = 0; fl < dupFileArray.length; fl++){
var activeFile = DriveApp.getFileById(dupFileArray[fl]);
var folders = activeFile.getParents();
var folder = ""
var foldercount = 0;
//Get the folder name for each file
while(folders.hasNext()){
folder = folders.next().getName();
foldercount++;
};
if(folder === fileInFolder && foldercount > 1){
folderID = {"id":false,"error":"There is more than one parent folder: "+fileInFolder+" for file "+fileName}
};
if(folder === fileInFolder){
folderID = {"id":dupFileArray[fl],"error":false};
}else{
folderID = {"id":false,"error":"There are multiple files named: "+fileName+". \nBut none of them are in folder, "+fileInFolder}
};
};
};
}else if(filecount === 0){
folderID = {"id":false,"error":"No file in your drive exists with name: "+fileName};
}else{ //IF there is only 1 file with fileName
folderID = {"id":dupFileArray[0],"error":false};
};
return folderID;
};
I wasn't able to replicate your issue where you passed a parameter and it fails vs you passed a string and it works.
But it seems that you are running getFileByName() directly by default thus it errors out like that.
Place everything else inside a main function and run that function instead.
I'm getting the expected output when I do that using your code.
It should look like this:
make sure that the chosen function is your main function that calls the getFileByName and just run the code.

Checking a folder for a filename, and exporting as PDF if it is not found

I am quite new to scripting and has attempted this for embarrassingly many hours now, so I hope you can help me.
I have a dashfolder that contains Google Sheets called "X", and I have a pdffolder containing pdfs that are called "X.pdf". I am trying to loop through the names of my dashfiles + ".pdf" to find those which are missing, and finally create its pdf in that same folder. My script, however, loops too many times. I want it to skip the dashfile if a file with the name+".pdf" are already in the pdffolder. Here is my code
function createPdf() {
var pdfFolder = DriveApp.getFolderById("ID")
var pdfFiles = pdfFolder.getFiles();
var dashFolder = DriveApp.getFolderById('ID');
var dashFiles = dashFolder.getFiles();
var pdfNames = [];
var dashNames = [];
while (pdfFiles.hasNext()) {
var currentFile2 = pdfFiles.next();
var fileName2 = currentFile2.getName();
pdfNames.push(fileName2);
}
while (dashFiles.hasNext()) {
var currentFile = dashFiles.next();
var fileName = currentFile.getName();
dashNames.push(fileName);
for (p in pdfNames) {
if((fileName + ".pdf") == pdfNames[p]) {
Logger.log("YES");
}
else {
var xlsBlob = currentFile.getBlob(); // Blob source of Excel file for conversion
var xlsFilename = currentFile.getName(); // File name to give to converted file; defaults to same as source file
pdfFolder.createFile(currentFile.getAs(MimeType.PDF));
Logger.log("pdf Created");
}
}
}
}
My real problem stems from the fact that I will have 100+ sheets that needs to be converted to pdf's and that will exceed the 6 minutes limit. So I am trying to build a script that can trigger itself and continue where it left off, skipping sheets that are already in the pdffolder and creating those that are missing.
I might be way over my head here, so I hope someone can give me some hints :-)
You wrong use loops. Currently you create more pdfs of same sheet, because when you traversating over dashFiles, you create PDF one more pdf for each exists PDF.
Change part of code like this:
var pdfNames = {}; //Object instead of array
while (pdfFiles.hasNext()) {
var currentFile2 = pdfFiles.next();
var fileName2 = currentFile2.getName();
pdfNames[fileName2] = true; // use PDF name as key for faster searching
}
while (dashFiles.hasNext()) {
var currentFile = dashFiles.next();
var fileName = currentFile.getName();
if(pdfNames[fileName + ".pdf"]) { // is exists pdf?
Logger.log("YES");
}
else {
var xlsBlob = currentFile.getBlob(); // Blob source of Excel file for conversion
var xlsFilename = currentFile.getName(); // File name to give to converted file; defaults to same as source file
pdfFolder.createFile(currentFile.getAs(MimeType.PDF));
Logger.log("pdf Created");
}
}
You can create an object of file names with a value of true, and then check for the existence of the file name in the object. If the file name exists, then continue to loop.
var pdfNames = {};//Create an object - not an array
pdfNames[fileName2] = true;//Put the file name into the object
if (pdfNames[fileName]) {//Test for file name in the object
Code:
function createPdf() {
var currentFile,fileName,xlsBlob,xlsFilename;
var pdfFolder = DriveApp.getFolderById("ID")
var pdfFiles = pdfFolder.getFiles();
var dashFolder = DriveApp.getFolderById('ID');
var dashFiles = dashFolder.getFiles();
var pdfNames = {};
var dashNames = [];
while (pdfFiles.hasNext()) {
var currentFile2 = pdfFiles.next();
var fileName2 = currentFile2.getName();
pdfNames[fileName2] = true;//Put the file name into the object
}
while (dashFiles.hasNext()) {
currentFile = dashFiles.next();
fileName = currentFile.getName();
dashNames.push(fileName);
if (pdfNames[fileName]) {//The file name was found in the object of pdf files
Logger.log("YES");
continue;
}
xlsBlob = currentFile.getBlob(); // Blob source of Excel file for conversion
xlsFilename = currentFile.getName(); // File name to give to converted file; defaults to same as source file
pdfFolder.createFile(currentFile.getAs(MimeType.PDF));
Logger.log("pdf Created");
}
}
Original Answer:
Test for the existence of the file name in the pdf array in a different way.
pdfNames.indexOf(fileName + ".pdf") !== -1
If a value is not found in an array, then indexOf() returns minus one.
So, if the return value is not minus one, then a file name was found. If a file name was found, you don't want a new file created, so continue.
function createPdf() {
var currentFile,fileName,xlsBlob,xlsFilename;
var pdfFolder = DriveApp.getFolderById("ID")
var pdfFiles = pdfFolder.getFiles();
var dashFolder = DriveApp.getFolderById('ID');
var dashFiles = dashFolder.getFiles();
var pdfNames = [];
var dashNames = [];
while (pdfFiles.hasNext()) {
var currentFile2 = pdfFiles.next();
var fileName2 = currentFile2.getName();
pdfNames.push(fileName2);
}
while (dashFiles.hasNext()) {
currentFile = dashFiles.next();
fileName = currentFile.getName();
dashNames.push(fileName);
if (pdfNames.indexOf(fileName + ".pdf") !== -1) {//The file name was found in the array of pdf files
Logger.log("YES");
continue;
}
xlsBlob = currentFile.getBlob(); // Blob source of Excel file for conversion
xlsFilename = currentFile.getName(); // File name to give to converted file; defaults to same as source file
pdfFolder.createFile(currentFile.getAs(MimeType.PDF));
Logger.log("pdf Created");
}
}

Combine Google Docs documents

Is it possible to merge 100 Google Docs documents into one?
I've tried copy-pasting, but it seems too long and it's not possible to copy comments.
This can be done with Google Apps Script. See this example. The most relevant parts (example assumes nothing but Google Docs in the folder):
function combine() {
var folder = DriveApp.getRootFolder();
if (folder == null) { Logger.log("Failed to get root folder"); return; }
var combinedTitle = "Combined Document Example";
var combo = DocumentApp.create(combinedTitle);
var comboBody = combo.getBody();
var hdr = combo.addHeader();
hdr.setText(combinedTitle)
var list = folder.getFiles();
while (list.hasNext()) {
var doc = list.next();
var src = DocumentApp.openById(doc.getId());
var srcBody = src.getBody();
var elems = srcBody.getNumChildren();
for (var i = 0; i < elems; i++ ) {
elem = srcBody.getChild(i).copy();
// fire the right method based on elem's type
switch (elem.getType()) {
case DocumentApp.ElementType.PARAGRAPH:
comboBody.appendParagraph(elem);
break;
case // something
}
}
}
}
Note that you don't copy the source document's contents in one lump; you have to loop through them as individual elements and fire the correct append* method to add them to the merged/destination file.
I expanded on #noltie's answer to support merging docs in a folder structure recursively, starting from an arbitrary folder (not necessarily the root folder of google docs) and guard agains script failures on too many unsaved changes.
function getDocsRec(rootFolder) {
var docs = [];
function iter(folder) {
var childFolders = folder.getFolders();
while (childFolders.hasNext()) {
iter(childFolders.next());
}
var childFiles = folder.getFiles();
while (childFiles.hasNext()) {
var item = childFiles.next();
var docName = item.getName();
var docId = item.getId();
var doc = {name: docName, id: docId};
docs.push(doc);
}
}
iter(rootFolder);
return docs;
}
function combineDocs() {
// This function assumes only Google Docs files are in the root folder
// Get the id from the URL of the folder.
var folder = DriveApp.getFolderById("<root folder id>");
if (folder == null) { Logger.log("Failed to get root folder"); return; }
var combinedTitle = "Combined Document Example";
var combo = DocumentApp.create(combinedTitle);
var comboBody = combo.getBody();
// merely get the files recursively, does not get them in alphabetical order.
var docArr = getDocsRec(folder);
// Log all the docs we got back. Click "Edit -> Logs" to see.
docArr.forEach(function(item) {
Logger.log(item.name)
});
// this sort will fail if you have files with identical names
// docArr.sort(function(a, b) { return a.name < b.name ? -1 : 1; });
// Now load the docs into the combo doc.
// We can't load a doc in one big lump though;
// we have to do it by looping through its elements and copying them
for (var j = 0; j < docArr.length; j++) {
// There is a limit somewhere between 50-100 unsaved changed where the script
// wont continue until a batch is commited.
if (j % 50 == 0) {
combo.saveAndClose();
combo = DocumentApp.openById(combo.getId());
comboBody = combo.getBody();
}
var entryId = docArr[j].id;
var entry = DocumentApp.openById(entryId);
var entryBody = entry.getBody();
var elems = entryBody.getNumChildren();
for (var i = 0; i < elems; i++) {
var elem = entryBody.getChild(i).copy();
switch (elem.getType()) {
case DocumentApp.ElementType.HORIZONTAL_RULE:
comboBody.appendHorizontalRule();
break;
case DocumentApp.ElementType.INLINE_IMAGE:
comboBody.appendImage(elem);
break;
case DocumentApp.ElementType.LIST_ITEM:
comboBody.appendListItem(elem);
break;
case DocumentApp.ElementType.PAGE_BREAK:
comboBody.appendPageBreak(elem);
break;
case DocumentApp.ElementType.PARAGRAPH:
comboBody.appendParagraph(elem);
break;
case DocumentApp.ElementType.TABLE:
comboBody.appendTable(elem);
break;
default:
var style = {};
style[DocumentApp.Attribute.BOLD] = true;
comboBody.appendParagraph("Element type '" + elem.getType() + "' could not be merged.").setAttributes(style);
}
}
// page break at the end of each entry.
comboBody.appendPageBreak();
}
}
You can create and run a script with the above code on https://script.google.com/home
Both the above fail for me with the script returning a red lozenge:
Service unavailable: Docs Dismiss
(the documents in the folder are found, as are the document id's, and the combined doc is created, but empty)
Fixed that - had a document in the list that wasn't owned by me or was created by conversion. Removed that and away we go.
Google Docs does not support any type of merge, yet.
You can select all 100 docs, download them and try to merge them offline.
Download all the files as Docx, then use Microsoft Word or Open Office to merge the documents using the "master document" feature. (Word also refers to this as "Outline.")