Pass Comments & Suggested Edits from a Google Document to a Google Sheet - google-apps-script

Fellow Stackers. I'm using a Google Apps Script to (a) capture all "Comments" in a Google Document...
...and (b) list them in a column of a Google Sheet...
However, I'm wondering if it's possible to...
(1) Array "Comments" into individual cells down a column in my Sheet rather than into a single column, as I have now. This is the bit of GAS I'm currently using to grab comment contents:
var comments = JSON.parse(Drive.Comments.list(id));
var items=comments.items;
var string = "";
for(var i in items){
string+='\n';
string+=items[i].content;
}
(2) Order the "Comments" by anchor position in my Google Document—i.e. the comment anchored highest in the doc would appear in the first cell of the Sheet's column.
(3) Also include "Suggested Edits" from my Google Document alongside the comments. Can those be accessed via API yet?
Thanks in advance to anyone who may be able to help!
Google Document: https://docs.google.com/document/d/1O7zAdkCmxhYihtfJhZ3OGkWfO8UUJ_deoHEYr7rQHW4/edit?usp=sharing
Google Apps Script: https://script.google.com/macros/d/1MgTtU0cKSS_XghRjAMtjZFQAdsbU9SkD_2zx03KVKb1Vy4iBBp3MI2QW/edit?uiv=2&mid=ACjPJvHY-vp53Ek1wBR4-W3Q1Ur8dSdyN0g6ZI7n3I48-e7EWyq6v9gY82OAeVNlnpQBbY3ICOzi4PCRtp-pjuqAbH3oePLelcIp-YUPs2FNbB7Cl7CC-AvgnoJPcXCnrO8CrIJEI2v8ns8&splash=yes
Google Sheet: https://docs.google.com/spreadsheets/d/1uAtmAO0we7h3HUAFlDBLXlShSLvHVM_W6OSXPwpX_t8/edit?usp=sharing

(1) Array "Comments" into individual cells down a column in my Sheet rather than into a single column, as I have now.
This bit of code is taking an array of comments, and concatenating them into a single string:
var string = "";
for(var i in items){
string+='\n';
string+=items[i].content;
}
To be able to put each comment into a separate cell in a column, you need to change that array into a 2-dimensional array, with each of the original elements in its own "row". Something like:
var data = []; // start with an empty array
for (var i=0; i<items.length; i++) {
var item = items[i]; // current comment
// A row is an array of cells
var row = [item.htmlContent,item.author.displayName,item.createdDate];
data.push(row); // Add this row to the data array
}
This line writes the content of a single cell, albeit using setValues() which can fill a rectangular range:
var targetRange = sheet.getRange(lastRow+1,1,1,1).setValues([[string]]);
With the 2-D array created earlier, you can append to the sheet like so:
var targetRange = sheet.getRange(lastRow+1,1,data.length,data[0].length);
targetRange.setValues(data);
Result:
function driveApiComment(id){
var comments = JSON.parse(Drive.Comments.list(id));
var items=comments.items;
var data = []; // start with an empty array
for (var i=0; i<items.length; i++) {
var item = items[i]; // current comment
// A row is an array of cells
var row = [item.htmlContent,item.author.displayName,item.createdDate];
data.push(row); // Add this row to the data array
}
var sheet = SpreadsheetApp.openById(submissionSSKey).getSheets()[0];
var lastRow = sheet.getLastRow();
var targetRange = sheet.getRange(lastRow+1,1,data.length,data[0].length);
targetRange.setValues(data);
}
(2) Order the "Comments" by anchor position in my Google Document—i.e. the comment anchored highest in the doc would appear in the first cell of the Sheet's column.
You're out of luck (for now, at least). See:
How to match comments on an image using kix anchor (or not) in Google Docs
Anchor documentation does not exist?
Creating anchored comments programmatically in Google Docs
Summary: Google's anchors are not decipherable. (Likely they are a key to a hidden database that includes the actual line & char refs to your document, along with your social security number and mother's maiden name.) You could retrieve them & sort them alphabetically... but that would have no relation to where the comments appear in a document.
(3) Also include "Suggested Edits" from my Google Document alongside the comments. Can those be accessed via API yet?
Nope.

Related

getlastrow, then last column, and then populate based on that information

Recently, there was a program by the name of Eddienuput that allows you to properly train against basically anything in training mode. One issue is that you have to make a text file with all the button presses, as well as the timing for each. Once this text file is made, the inputs are added into a virtual controller.
The working sheet can be seen here.
The idea here is that you put the data of the character on the second sheet, then on the generator sheet you select the buttons you want, then press the [>] to put it to the right of the current buttons stored, and you use the [v] button.
When I hit the down arrow (I've assigned the script to the buttons), the three entries needed for the program to register the input are inserted one row below the active row.
The problem is the Right arrow. I sort of have it right (it puts the three inputs needed to the right.. but it isn't finding the bottom-right entry and putting it to the right of it, but instead it looks for the furthest right column and puts it to the right of everything instead of next to the last cell on the last row.
here is the code as I have it now:
function addbuttontoString()
{
//DEFINE ALL ACTIVE SHEETS
var ss = SpreadsheetApp.getActiveSpreadsheet();
var eddGenSheet = ss.getSheetByName("Eddie Input Generator");
var lrwithcontentofEddGenSheet = eddGenSheet.getLastRow()+1;
var lcwithcontentofEddGenSheet = eddGenSheet.getLastColumn();
var CharacterFrameDataSheet = ss.getSheetByName("CharacterFrameData");
//Current string Variables
var totalcurrentstringRows = lrwithcontentofEddGenSheet-4;
var fullcurrentString = eddGenSheet.getRange(5,1,totalcurrentstringRows, lcwithcontentofEddGenSheet);
var cslastRow = fullcurrentString.getLastRow();
var bottomrightcolumnNumber = eddGenSheet.getRange(lrwithcontentofEddGenSheet,1,1,lcwithcontentofEddGenSheet).getLastColumn();
Logger.log(bottomrightcolumnNumber)
//GET LAST ROW #OF ITEM SHEET
var lastrowItem = CharacterFrameDataSheet.getLastRow();
// Get the perfect Eddie value from Character Frame Data you are grabbing data for
var button = eddGenSheet.getRange('B4').getValue();
// Set up the Perfect Frame Variable
for(var i = 2; i <= lastrowItem; i++)
{
if(button == CharacterFrameDataSheet.getRange(i, 1).getValue())
{
var Perfectframe = CharacterFrameDataSheet.getRange(i, 4).getValue();
}
}
Logger.log(fullcurrentString.isBlank());
if(fullcurrentString.isBlank()){
// POPULATE eddGen SHEET Same Line
eddGenSheet.getRange("A5").setValue(button);
eddGenSheet.getRange("B5").setValue("F");
eddGenSheet.getRange("C5" ).setValue(Perfectframe);
} else {
eddGenSheet.getRange(lrwithcontentofEddGenSheet-1, bottomrightcolumnNumber +1).setValue(button);
eddGenSheet.getRange(lrwithcontentofEddGenSheet-1, bottomrightcolumnNumber +2).setValue("W");
eddGenSheet.getRange(lrwithcontentofEddGenSheet-1, bottomrightcolumnNumber +3).setValue(Perfectframe);
}
}
I believe your goal as follows.
When you click the right arrow button, you want to add the values from the 1st empty column of the row.
In your script, in order to retrieve the coordinate of the column for putting values, getLastColumn() is used as follows. In this case, the 1st empty column of the row is not retrieved. The last column of the sheet is retrieved. So, how about the following modification?
From:
var bottomrightcolumnNumber = eddGenSheet.getRange(lrwithcontentofEddGenSheet,1,1,lcwithcontentofEddGenSheet).getLastColumn();
To:
var bottomrightcolumnNumber = eddGenSheet.getRange(lrwithcontentofEddGenSheet - 1, 1).getNextDataCell(SpreadsheetApp.Direction.NEXT).getColumn();
In this modification, the 1st empty column is retrieved using getNextDataCell().
Note:
I'm not sure about your actual situation. So when above modification was not useful for your situation, can you provide the detail information about your actual situation? By this, I would like to modify it.
References:
getLastColumn()
getNextDataCell(direction)

Script to replace content and formulas in many other spreadsheets

I have a script that allows me to import data from a multitude of "child" spreadsheets to a "parent" spreadsheet.
Each child is composed of many tabs, but all the data that I import is unified thanks to queries in a single sheet of the child ("datasheet").
Problem is: if in the future I want to modify something (for example import more data from the childs), i would then have to go and edit manually every single child datasheet.
I'd rather have a script that would do the following:
In my "Parent", i would create a "datasheet" which would be the template datasheet.
The script would copy the "content" (and the formulas) of the parent datasheet.
It would paste this content+formulas in all the spreadsheets specified (in my Parent, i already have a list of URLs - one for each child).
I don't want to "copy" the parent datasheet, i'd rather replace content.
Is this even possible?
Thank you in advance for your help!
Based on our conversation on comments, I comprehend that you want to copy all data from the Parent sheet into the specified sheets. I'll assume that the list of sheets is located in the upper left corner of the Parent sheet. If that isn't the case, please forgive me and indicate to me where the sheet list is located. This is the code that fulfills your request:
function sheetPopulator(spreadsheetID) {
var masterSheet = SpreadsheetApp.openById(spreadsheetID).getSheetByName(
"Parent");
var specifiedSheets = masterSheet.getRange(2, 1, masterSheet.getLastRow(), 1)
.getValues();
var lastRowMasterSheet = masterSheet.getLastRow();
var lastColumnMasterSheet = masterSheet.getLastColumn();
var allData = masterSheet.getRange(1, 1, masterSheet.getLastRow(), masterSheet
.getLastColumn()).getValues();
for (var i = 0; i < specifiedSheets.length; i++) {
if (SpreadsheetApp.openById(spreadsheetID).getSheetByName(specifiedSheets[
i]) != null) {
SpreadsheetApp.openById(spreadsheetID).getSheetByName(specifiedSheets[i])
.getRange(1, 1, lastRowMasterSheet, lastColumnMasterSheet).setValues(
allData);
}
}
}
That function will first gather the list of specified sheets and after that will read all the data from the Parent sheet. After that, the code will iterate over every element of the list and, if that element coincides with a sheet name, it will copy all the previously read data into that sheet.
Please, take this as one of many possible solutions to your issue. Don't hesitate to ask me for clarifications or further help.
UPDATE
I apologize if the previous script doesn't fulfill your requests. After reading your new comments I studied your spreadsheets and designed a new code. I have tested this new script on copies of your spreadsheets and it works flawlessly. This is the code in question:
function sheetPopulatorII() {
var parentSheetID = "{PARENT SHEET ID}";
var childrenListSheet = SpreadsheetApp.openById(parentSheetID).getSheetByName(
"Childs");
var childrenList = childrenListSheet.getRange(2, 1, childrenListSheet
.getLastRow() - 1, 1).getValues();
for (var i = 0; i < childrenList.length; i++) {
childrenList[i][0] = childrenList[i][0].toString().substring(39, 83);
}
for (var i = 0; i < childrenList.length; i++) {
var children = SpreadsheetApp.openById(childrenList[i][0]);
children.getSheets()[0].setName("OLD SHEET");
SpreadsheetApp.openById(parentSheetID).getSheetByName("Master").copyTo(
children).setName("Data");
children.deleteSheet(children.getSheetByName("OLD SHEET"));
}
}
I am going to explain this code step by step. First, it opens the children list sheet (inside the Parent Sheet) using .openById() for opening the Parent spreadsheet, .getSheetByName() to open the involved sheet, .getRange() for selecting the list, .getLastRow() to know how long the list is (so you can add new URLs on the future using the same code) and .getValues() to gather the data.
Afterwards the code will iterate the list to convert the URLs into IDs with .substring() to cut away the non-ID parts. Next, it will iterate the list again but this time it will rename the old sheet with a temporal name using .setName(), copy the Parent sheet with .copyTo() and delete the renamed sheet with .deleteSheet(). This approach copy both: values and formulas. Please, contact me again if you need further help developing the code or understanding it.

Using Google Slides API is it possible to use the response from one request in all or any of the remaining requests during the same batch Update

I created this code which creates a Google Slides table from a Spreadsheet. But since I had to have the tableObjectId I did it in two batchUpdates. So I'm wondering if it's possible to get the resp1.replies[0].creatTable.objectId from the first request which creates the table, into the requests for loading the cells all in the same batch update.
The Code:
function putTablesIntoSlides() {
var pr=SlidesApp.openById('Presentation Id');
var slide1_ObjectId=pr.getSlides()[1].getObjectId();
Logger.log(slide1_ObjectId);
var ss=SpreadsheetApp.getActive();
var sh=ss.getSheetByName('Sheet10');
var rg=sh.getDataRange();
var v=rg.getValues();
//Create Table
var request1={"createTable": {"elementProperties":{"pageObjectId": slide1_ObjectId},"rows": v.length,"columns": v[0].length}};
var resp1=Slides.Presentations.batchUpdate({requests:request1}, pr.getId());
//Preparing the request for Adding Text into cells
var request2=[];
for(var i=0;i<v.length;i++) {
for(var j=0;j<v[i].length;j++) {
//Is there a way to get the replies[0].createTable.objectId from the create table request in the same batch update.
request2.push({"insertText":{"objectId":resp1.replies[0].createTable.objectId,"cellLocation": {"rowIndex":i,"columnIndex":j},"text": v[i][j].toString()}});
}
}
//Adding text
var resp2=Slides.Presentations.batchUpdate({requests:request2}, pr.getId());
}
In other words is it possible to use the response from one request in any or all of the remaining requests during a single batch update.
You want to run the batchUpdate method of Slides API of the following requests by one API call.
Create new table.
Put values.
You want to achieve this using Google Apps Script.
If my understanding is correct, how about this answer? Please think of this as just one of several possible answers.
Modififcation points:
In this case, when new table is created, the object ID of created table can be set at the request body.
Using the object ID, the values can be put to the table.
The official documentsays as follows.
objectId:
A user-supplied object ID for the placeholder identified above that to be created onto a slide.
If you specify an ID, it must be unique among all pages and page elements in the presentation. The ID must start with an alphanumeric character or an underscore (matches regex [a-zA-Z0-9_] ); remaining characters may include those as well as a hyphen or colon (matches regex [a-zA-Z0-9_-:] ). The length of the ID must not be less than 5 or greater than 50.
If you don't specify an ID, a unique one is generated.
When above points are reflected to your script, it becomes as follows.
Modified script:
From:
//Create Table
var request1={"createTable": {"elementProperties":{"pageObjectId": slide1_ObjectId},"rows": v.length,"columns": v[0].length}};
var resp1=Slides.Presentations.batchUpdate({requests:request1}, pr.getId());
//Preparing the request for Adding Text into cells
var request2=[];
for(var i=0;i<v.length;i++) {
for(var j=0;j<v[i].length;j++) {
//Is there a way to get the replies[0].createTable.objectId from the create table request in the same batch update.
request2.push({"insertText":{"objectId":resp1.replies[0].createTable.objectId,"cellLocation": {"rowIndex":i,"columnIndex":j},"text": v[i][j].toString()}});
}
}
//Adding text
var resp2=Slides.Presentations.batchUpdate({requests:request2}, pr.getId());
To:
var tableObjectId = "sampleId1"; // Here, the object ID of table is set.
//Create Table
var requests = [{"createTable": {"objectId": tableObjectId, "elementProperties":{"pageObjectId": slide1_ObjectId},"rows": v.length,"columns": v[0].length}}];
//Preparing the request for Adding Text into cells
for(var i=0;i<v.length;i++) {
for(var j=0;j<v[i].length;j++) {
//Is there a way to get the replies[0].createTable.objectId from the create table request in the same batch update.
requests.push({"insertText":{"objectId":tableObjectId,"cellLocation": {"rowIndex":i,"columnIndex":j},"text": v[i][j].toString()}});
}
}
//Adding text
var resp = Slides.Presentations.batchUpdate({requests:requests}, pr.getId());
In this case, as a sample object ID, var tableObjectId = "sampleId1" is used.
Reference:
CreateTableRequest
If I misunderstood your question and this was not the direction you want, I apologize.

Writing a string of multiple date / time to a single cell

I have an array of a couple (the array is up to 10) date/time that I want to write to a spreadsheet using getRange().setValues(). I'm converting the array to a string and it looks correct in Logger.
[Mon Feb 02 14:01:00 GMT-06:00 2015, Tue Feb 02 01:00:00 GMT-06:00 2016, , , , , , , , ]
When I try to write the string to a single cell in a sheet:
target6.setValues(source_range6_values);
I get this error:
Incorrect range width, was 10 but should be 1 (line 84, file "Code")
Edited 4/28/2014 adding entire script:
/**
* Copies source range and pastes at first empty row on target sheet
*/
function CopyIt(){
//Establishing source and target sheets
var source_spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
var target_spreadsheet = SpreadsheetApp.openById("0AhCv9Xu_eRnSdHpLTkc0d1ZURUtyTU9oRjdFbmpMUFE");
// Get source and target sheets - can be the same or different
var sourcesheet = source_spreadsheet.getSheetByName("Form Responses");
var targetsheet = target_spreadsheet.getSheetByName("Work_Orders");
//Get row of last form submission
var source_last_row = sourcesheet.getLastRow();
// Check for answer to Do you need a Flyer Created? If No, end now. If Yes, continue.
var check = sourcesheet.getRange("T"+(source_last_row)).getValue();
if (check == 'Yes') {
//Pulling date(s) from the users form entry (source sheet) into an array
var daterange = sourcesheet.getRange("H"+source_last_row+":Q"+source_last_row);
//Getting the values of the array
var classDate = daterange.getValues();
//changing the array values to a string
classDate.toString();
//Building a new variable with the string to be inserted below in the target sheet
var source_range6_values = classDate;
//source_range6_values.toString();
Logger.log(classDate[0]);
// Get the last row on the target sheet
var last_row = targetsheet.getLastRow();
//Setting the target cell in the Marketing Work Order sheet
var target6 = targetsheet.getRange("U"+(last_row+1));
// Aadding a new row in the target sheet
targetsheet.insertRowAfter(last_row);
//Inserting the values of source_range6_values into the target sheet. Unfortunately it does not enter the data into the same field and it's in military time.
target6.setValue(source_range6_values);
Logger.log(source_range6_values);
}
}
To give a correct answer for your question, i guess i need to know how you get the value of source_range6_values.
One quick guess is you might want to use target6.setValue instead of target6.setValues since you want to write the data into one cell only...
A quick & dirty way would be to replace the commas(with spaces):
source = String(source_range6_values).replace("," , " ");
I've had fun with GAS and variables. Casting it as a String should let you use the string functions on it. If that doesn't work can you share a mock-up of your sheets so I can take a look?
edit:
I had to play around with it a bit, seems google's version of .replace() only replaces the first instance (and doesn't allow .replaceAll() ).
I edited your code starting on line 23:
//Getting the values of the array
var classDate = daterange.getValues().toString();
//Building a new variable with the string to be inserted below in the target sheet
//Google has bugs, .replace() seems to only replace the first instance
//-while {} loop replaces all of them
while (!classDate.equals(classDate.replace("," , " "))) { classDate = classDate.replace("," , " "); };
var source_range6_values = classDate;
All the dates are in one cell if you change only those lines (and no errors).
I appreciate the help you two have given me trying to answer this question. #swimmingwood fixed the actual capture of the data into a string, but it left commas and when I inserted it into the target sheet, it wrote it to multiple cells with an error. It did write to the sheet but the error had you use a CTRL-E (inside the taget sheet) to complete the insert and wrote them into separate cells.
#MickATX suggested the code to replace the commas in the string with a space, which would be fine, but apparently he discovered a Google scripting problem that would only allow for the first comma to be replaced and ignore the rest. Great knowledge never-the-less.
I ended up using a formula in an addition cell in the source sheet that looked like this:
=ArrayFormula(CONCATENATE(REPT(TEXT(H2:Q2,"mm/dd/yyyy hh:mm a")&CHAR(10),H2:Q2>0)))
This formula wrote all the date/time entries provided by the form entry into one cell of the source sheet and ONLY the number of entries (1-10). I then wrote that single cell to the target sheet via the script.
Thanks to #swimmingwood and #MickATX for trying to help me, both provided worthy knowledge.
I've read a couple of strange answers here...
If you write an 2D array to a sheet it will obviously be written accross multiple cells... commas are definitely not the issue but the nature of the object is.
Simply convert your array into a string using .toString() or .join() (the latter providing the advantage you can choose the separator to use) and setValue() (without S) at the place you want.
the commas you see in the logger are only typographic representation of array elements separators...
And, last point : the .join() or .toString() methods return new variables, they don't modify the original value so when you write classDate.toString(); you are not doing anything ...
you should write it like this :
classDateAsAString = classDate.toString();
finally your code :
function CopyIt(){
//Establishing source and target sheets
var source_spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
var target_spreadsheet = SpreadsheetApp.openById("0AhCv9Xu_eRnSdHpLTkc0d1ZURUtyTU9oRjdFbmpMUFE");
// Get source and target sheets - can be the same or different
var sourcesheet = source_spreadsheet.getSheetByName("Form Responses");
var targetsheet = target_spreadsheet.getSheetByName("Work_Orders");
//Get row of last form submission
var source_last_row = sourcesheet.getLastRow();
// Check for answer to Do you need a Flyer Created? If No, end now. If Yes, continue.
var check = sourcesheet.getRange("T"+(source_last_row)).getValue();
if (check == 'Yes') {
//Pulling date(s) from the users form entry (source sheet) into an array
var daterange = sourcesheet.getRange("H"+source_last_row+":Q"+source_last_row);
//Getting the values of the array
var classDate = daterange.getValues();
var source_range6_values = classDate.join(' & ');// using & as separator for example
// Get the last row on the target sheet
var last_row = targetsheet.getLastRow();
//Setting the target cell in the Marketing Work Order sheet
var target6 = targetsheet.getRange("U"+(last_row+1));
// Adding a new row in the target sheet
targetsheet.insertRowAfter(last_row);
//Inserting the values of source_range6_values into the target sheet. Unfortunately it does not enter the data into the same field and it's in military time.
target6.setValue(source_range6_values);
Logger.log(source_range6_values);
}
}
Now if you want to format the dates in a more civilized way, that should be handled a bit differently... let me know if you still need it / want it.

How to copy one or more existing pages of a document using google apps script

I have sucessfully written a small script, which creates a serial letter (physical letter to several recipients) based on data in a Google spreadsheet creating a new document for each letter/addresse.
It works, but for large mailings this approach is a bit cumbersome as a large amount of documents are created and need to be printed individually.
Now i would like to do the same but as a result having all of the letters in one single Google document.
Is there any way to copy the content of an existing document and inserting it a number of times into the same or any other documents (i.e. copy/paste via apps script)?
Following your comment, here is the full code I use to merge an undetermined number of docs in a new one.
All document IDs are in an array of IDs as argument for the main function, the results is a new doc with "multi-page" appended to the name. If you need more explanation than provided by the in code comments just let me know... (note that it will work only for documents containing text and tables, if you have images ot other data type you'll have to handle that case in the main loop where we check the ElementType following the same logic)
EDIT : first code removed, following your update I tried this approach assuming you have only paragraphs in your master doc... give it a try and I guess you could start from there to developp your project.
function Serialletter_Singledocument() {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Datenbank");
var LastColumn = sheet.getLastColumn();
//here you need to get document id from url (Example, 1oWyVMa-8fzQ4leCrn2kIk70GT5O9pqsXsT88ZjYE_z8)
var FileTemplateFileId = "1Wrf2qvUTyc5tMmJIly40Z4U4sJb4-QhT5z-UfJmtQ-M" //Browser.inputBox("ID der Serienbriefvorlage (aus Dokumentenlink kopieren):");
var doc = DocumentApp.openById(FileTemplateFileId);
var DocName = doc.getName();
var headpara=' ***** ';
// Fetch entire table containing data
var data = sheet.getDataRange().getValues();
//Create copy of the template document and open it
var SerialLetterID = DocsList.getFileById(FileTemplateFileId).makeCopy(DocName +" Serienbrief").getId();
var docCopy = DocumentApp.openById(SerialLetterID);
var totalParagraphs = docCopy.getBody().getParagraphs() ;// get the total number of paragraphs elements
Logger.log(totalParagraphs);
var elements = [];
for ( var i = 1; i < data.length; i++) { //do for every record in the spreadsheet (containing the content to replace the variables in the letter)
for (var e=0;e<totalParagraphs.length;e++){
var element = totalParagraphs[e].copy();
// Logger.log(element.editAsText().getText())
for(var c=0;c<data[0].length;c++){
element.replaceText("<" +data[0][c] +">", data[i][c]); //replace variable (from column title) with actual value
}
elements.push(element);// store paragraphs in an array
}
for(var el=0;el<elements.length;el++){
var paragraph = elements[el].copy();
docCopy.getBody().appendParagraph(paragraph);
}
docCopy.getBody().appendPageBreak()
}
docCopy.saveAndClose();
Browser.msgBox("Serienbrief ist erstellt. Sie finden die erstellten Dokumente in Google Drive unter Meine Ablage");
}