使用 Google 脚本创建 and/or 更新邮件合并 Google 文档
Creating and/or update mail merge Google document using Google Script
我目前有一个 Google Sheet,其中包含要使用模板文件合并到 Google 文档中的数据行。每行都会根据模板的布局生成一个新的 Google 文档。我是 运行 的脚本当前将检查与 Google Sheet 行相关的文件(使用标题单元格匹配)是否已经存在。如果是这样,那么它将被删除并在其位置创建一个新文档,其中包含 Google Sheet 中所做的所有更改。如果不存在,将创建一个新文档。
不过,我现在需要从外部网站link到这些Google文件。使用当前脚本,这意味着如果对电子表格中的一行进行了更改,那么它的相应文档将被删除,并且 re-created 连同 URL,从而使网站 link 没用,需要手动修改
所以理想情况下,我想要实现的不是删除和 re-creating 文档,而是更新同一个文档(如果它已经存在)。这样,文档得到更新并且 URL 将始终相同。
目前的脚本是这样的
/*###################################################################
* Create multiple versions of a document based on Google Sheet Data
* and a Google Doc Template (Mailmerge)
*###################################################################
*/
/*###################################################################
* Main run file sets up variables for insertion into the mailMerge()
* function.
*/
function mergeSheetDoc() {
const TEMPLATE_ID = '16YfyeDjGDp-88McAtLCQQyZ1xz4QX5zEKAS09SaLkJI';//Add your Google Doc template ID
const SS_ID = '1C5gtJCSzHMuSz-oVWEItl2EUVRDwF5iH_RVr6BxLkOU'; // Add your Google Sheet ID
const SHEET_NAME = "data"; // Add your Google Sheet Tab name
const MAPPED = mappedDocToSheet; // Go to Map.gs to update
const FILE_NAME = ["Titre de la formation"] // Header IDs from your Sheet. Change to make your own file name.
docMerge(TEMPLATE_ID,SS_ID,SHEET_NAME,MAPPED, FILE_NAME);
}
/** ###################################################################
* Merges data from a Google Sheet into a newly created doc based on a
* Google Doc template.
*
* param {string} templateID: The id from the Google Doc template.
* param {string} ssID: The id of the Google Sheet.
* param {string} sheetName: the name of the sheet tab you are referencing.
* param {object} mapped: Object array of data you mapped from your Doc template against your Google Sheet headers
* param {array} fileNameDara: An array of data used to generate the file name.
* param {string} rowLen: (optional) If you want to add a number rows to create your merged documents.
*/
function docMerge(templateID,ssID, sheetName, mapped, fileNameData, rowLen = "auto"){
//Get the Spreadsheet and sheet tab
const ss = SpreadsheetApp.openById(ssID);
const sheet = ss.getSheetByName(sheetName);
//Get number of rows to process
rowLen = (rowLen = "auto") ? getRowLen() : rowLen;
//Gets the range of data in the sheet then grabs the values of the range
const range = sheet.getRange(1,1,rowLen,sheet.getDataRange().getNumColumns());
const matrix = range.getValues();
// Searches the file mapped object and finds the corresponding number returns the column number in an array.
const fileNameRows = getFileNameRows()
//Loops through each row of the sheet grabbing the data from each row and putting it into a new doc.
for(let i = 1; i < rowLen; i++){
let row = matrix[i];
//Get the title for the file.
let fileName = buildFileName(row);
//This query parameter will search for an exact match of the filename with Doc file type
let params = "title='"+fileName+"' and mimeType = 'application/vnd.google-apps.document'"
let files = DriveApp.searchFiles(params);
if(files.hasNext()){
while (files.hasNext()) {
//Filename exist
var file = files.next();
//Create a new file
var tmpDoc = DriveApp.getFileById(templateID).makeCopy("tempfile");
//Update the file
updateFileData(row, tmpDoc.getId());
//Copy contents of the new file to the existing file
copyFileData(tmpDoc.getId(), file.getId());
//Delete temporary file
tmpDoc.setTrashed(true);
}
}else{
//Create a new file
let newDoc = DriveApp.getFileById(templateID).makeCopy(fileName);
updateFileData(row, newDoc.getId());
}
}
};
function copyFileData(sourceDoc, destinationDoc){
var srcParagraph = DocumentApp.openById(sourceDoc).getBody().getParagraphs();
var dstParagraph = DocumentApp.openById(destinationDoc).getBody().getParagraphs();
for(var i = 0; i<dstParagraph.length; i++){
//Paragraph Index of description and suivantes information
if(i==5||i==21){
dstParagraph[i].editAsText().setText(srcParagraph[i].editAsText().getText());
dstParagraph[i].setAttributes(srcParagraph[i].getAttributes());
dstParagraph[i].setLeftToRight(true);
dstParagraph[i].setIndentFirstLine(srcParagraph[i].getIndentFirstLine());
dstParagraph[i].setIndentStart(srcParagraph[i].getIndentStart());
dstParagraph[i].editAsText().setFontSize(srcParagraph[i].editAsText().getFontSize());
}
}
}
此代码还引用了可以在此处找到的映射脚本
解决方法摘要:
- 如果文件名已经存在,创建一个临时文档来替换模板文档中的 textId 标签
- 将临时文档的段落文本和属性复制到现有文档。
示例修改代码:
仅包含 modified/new 函数。
function docMerge(templateID,ssID, sheetName, mapped, fileNameData, rowLen = "auto"){
//Get the Spreadsheet and sheet tab
const ss = SpreadsheetApp.openById(ssID);
const sheet = ss.getSheetByName(sheetName);
//Get number of rows to process
rowLen = (rowLen = "auto") ? getRowLen() : rowLen;
//Gets the range of data in the sheet then grabs the values of the range
const range = sheet.getRange(1,1,rowLen,sheet.getDataRange().getNumColumns());
const matrix = range.getValues();
// Searches the file mapped object and finds the corresponding number returns the column number in an array.
const fileNameRows = getFileNameRows()
//Loops through each row of the sheet grabbing the data from each row and putting it into a new doc.
for(let i = 1; i < rowLen; i++){
let row = matrix[i];
//Get the title for the file.
let fileName = buildFileName(row);
//This query parameter will search for an exact match of the filename with Doc file type
let params = "title='"+fileName+"' and mimeType = 'application/vnd.google-apps.document'"
let files = DriveApp.searchFiles(params);
if(files.hasNext()){
while (files.hasNext()) {
//Filename exist
var file = files.next();
//Create a new file
var tmpDoc = DriveApp.getFileById(templateID).makeCopy("tempfile");
//Update the file
updateFileData(row, tmpDoc.getId());
//Copy contents of the new file to the existing file
copyFileData(tmpDoc.getId(), file.getId());
//Delete temporary file
tmpDoc.setTrashed(true);
}
}else{
//Create a new file
let newDoc = DriveApp.getFileById(templateID).makeCopy(fileName);
updateFileData(row, newDoc.getId());
}
};
function copyFileData(sourceDoc, destinationDoc){
var srcParagraph = DocumentApp.openById(sourceDoc).getBody().getParagraphs();
var dstParagraph = DocumentApp.openById(destinationDoc).getBody().getParagraphs();
for(var i = 0; i<dstParagraph.length; i++){
//Paragraph Index of description and suivantes information
if(i==5||i==21){
dstParagraph[i].editAsText().setText(srcParagraph[i].editAsText().getText());
dstParagraph[i].setAttributes(srcParagraph[i].getAttributes());
dstParagraph[i].setLeftToRight(true);
dstParagraph[i].setIndentFirstLine(srcParagraph[i].getIndentFirstLine());
dstParagraph[i].setIndentStart(srcParagraph[i].getIndentStart());
dstParagraph[i].editAsText().setFontSize(srcParagraph[i].editAsText().getFontSize());
}
}
}
它有什么作用?
- 如果找到文件名,根据电子表格中的更新信息创建一个临时文档
- 将特定段落从临时文档复制到现有文档
- 使用setTrashed()方法删除临时文档。
我目前有一个 Google Sheet,其中包含要使用模板文件合并到 Google 文档中的数据行。每行都会根据模板的布局生成一个新的 Google 文档。我是 运行 的脚本当前将检查与 Google Sheet 行相关的文件(使用标题单元格匹配)是否已经存在。如果是这样,那么它将被删除并在其位置创建一个新文档,其中包含 Google Sheet 中所做的所有更改。如果不存在,将创建一个新文档。
不过,我现在需要从外部网站link到这些Google文件。使用当前脚本,这意味着如果对电子表格中的一行进行了更改,那么它的相应文档将被删除,并且 re-created 连同 URL,从而使网站 link 没用,需要手动修改
所以理想情况下,我想要实现的不是删除和 re-creating 文档,而是更新同一个文档(如果它已经存在)。这样,文档得到更新并且 URL 将始终相同。
目前的脚本是这样的
/*###################################################################
* Create multiple versions of a document based on Google Sheet Data
* and a Google Doc Template (Mailmerge)
*###################################################################
*/
/*###################################################################
* Main run file sets up variables for insertion into the mailMerge()
* function.
*/
function mergeSheetDoc() {
const TEMPLATE_ID = '16YfyeDjGDp-88McAtLCQQyZ1xz4QX5zEKAS09SaLkJI';//Add your Google Doc template ID
const SS_ID = '1C5gtJCSzHMuSz-oVWEItl2EUVRDwF5iH_RVr6BxLkOU'; // Add your Google Sheet ID
const SHEET_NAME = "data"; // Add your Google Sheet Tab name
const MAPPED = mappedDocToSheet; // Go to Map.gs to update
const FILE_NAME = ["Titre de la formation"] // Header IDs from your Sheet. Change to make your own file name.
docMerge(TEMPLATE_ID,SS_ID,SHEET_NAME,MAPPED, FILE_NAME);
}
/** ###################################################################
* Merges data from a Google Sheet into a newly created doc based on a
* Google Doc template.
*
* param {string} templateID: The id from the Google Doc template.
* param {string} ssID: The id of the Google Sheet.
* param {string} sheetName: the name of the sheet tab you are referencing.
* param {object} mapped: Object array of data you mapped from your Doc template against your Google Sheet headers
* param {array} fileNameDara: An array of data used to generate the file name.
* param {string} rowLen: (optional) If you want to add a number rows to create your merged documents.
*/
function docMerge(templateID,ssID, sheetName, mapped, fileNameData, rowLen = "auto"){
//Get the Spreadsheet and sheet tab
const ss = SpreadsheetApp.openById(ssID);
const sheet = ss.getSheetByName(sheetName);
//Get number of rows to process
rowLen = (rowLen = "auto") ? getRowLen() : rowLen;
//Gets the range of data in the sheet then grabs the values of the range
const range = sheet.getRange(1,1,rowLen,sheet.getDataRange().getNumColumns());
const matrix = range.getValues();
// Searches the file mapped object and finds the corresponding number returns the column number in an array.
const fileNameRows = getFileNameRows()
//Loops through each row of the sheet grabbing the data from each row and putting it into a new doc.
for(let i = 1; i < rowLen; i++){
let row = matrix[i];
//Get the title for the file.
let fileName = buildFileName(row);
//This query parameter will search for an exact match of the filename with Doc file type
let params = "title='"+fileName+"' and mimeType = 'application/vnd.google-apps.document'"
let files = DriveApp.searchFiles(params);
if(files.hasNext()){
while (files.hasNext()) {
//Filename exist
var file = files.next();
//Create a new file
var tmpDoc = DriveApp.getFileById(templateID).makeCopy("tempfile");
//Update the file
updateFileData(row, tmpDoc.getId());
//Copy contents of the new file to the existing file
copyFileData(tmpDoc.getId(), file.getId());
//Delete temporary file
tmpDoc.setTrashed(true);
}
}else{
//Create a new file
let newDoc = DriveApp.getFileById(templateID).makeCopy(fileName);
updateFileData(row, newDoc.getId());
}
}
};
function copyFileData(sourceDoc, destinationDoc){
var srcParagraph = DocumentApp.openById(sourceDoc).getBody().getParagraphs();
var dstParagraph = DocumentApp.openById(destinationDoc).getBody().getParagraphs();
for(var i = 0; i<dstParagraph.length; i++){
//Paragraph Index of description and suivantes information
if(i==5||i==21){
dstParagraph[i].editAsText().setText(srcParagraph[i].editAsText().getText());
dstParagraph[i].setAttributes(srcParagraph[i].getAttributes());
dstParagraph[i].setLeftToRight(true);
dstParagraph[i].setIndentFirstLine(srcParagraph[i].getIndentFirstLine());
dstParagraph[i].setIndentStart(srcParagraph[i].getIndentStart());
dstParagraph[i].editAsText().setFontSize(srcParagraph[i].editAsText().getFontSize());
}
}
}
此代码还引用了可以在此处找到的映射脚本
解决方法摘要:
- 如果文件名已经存在,创建一个临时文档来替换模板文档中的 textId 标签
- 将临时文档的段落文本和属性复制到现有文档。
示例修改代码:
仅包含 modified/new 函数。
function docMerge(templateID,ssID, sheetName, mapped, fileNameData, rowLen = "auto"){
//Get the Spreadsheet and sheet tab
const ss = SpreadsheetApp.openById(ssID);
const sheet = ss.getSheetByName(sheetName);
//Get number of rows to process
rowLen = (rowLen = "auto") ? getRowLen() : rowLen;
//Gets the range of data in the sheet then grabs the values of the range
const range = sheet.getRange(1,1,rowLen,sheet.getDataRange().getNumColumns());
const matrix = range.getValues();
// Searches the file mapped object and finds the corresponding number returns the column number in an array.
const fileNameRows = getFileNameRows()
//Loops through each row of the sheet grabbing the data from each row and putting it into a new doc.
for(let i = 1; i < rowLen; i++){
let row = matrix[i];
//Get the title for the file.
let fileName = buildFileName(row);
//This query parameter will search for an exact match of the filename with Doc file type
let params = "title='"+fileName+"' and mimeType = 'application/vnd.google-apps.document'"
let files = DriveApp.searchFiles(params);
if(files.hasNext()){
while (files.hasNext()) {
//Filename exist
var file = files.next();
//Create a new file
var tmpDoc = DriveApp.getFileById(templateID).makeCopy("tempfile");
//Update the file
updateFileData(row, tmpDoc.getId());
//Copy contents of the new file to the existing file
copyFileData(tmpDoc.getId(), file.getId());
//Delete temporary file
tmpDoc.setTrashed(true);
}
}else{
//Create a new file
let newDoc = DriveApp.getFileById(templateID).makeCopy(fileName);
updateFileData(row, newDoc.getId());
}
};
function copyFileData(sourceDoc, destinationDoc){
var srcParagraph = DocumentApp.openById(sourceDoc).getBody().getParagraphs();
var dstParagraph = DocumentApp.openById(destinationDoc).getBody().getParagraphs();
for(var i = 0; i<dstParagraph.length; i++){
//Paragraph Index of description and suivantes information
if(i==5||i==21){
dstParagraph[i].editAsText().setText(srcParagraph[i].editAsText().getText());
dstParagraph[i].setAttributes(srcParagraph[i].getAttributes());
dstParagraph[i].setLeftToRight(true);
dstParagraph[i].setIndentFirstLine(srcParagraph[i].getIndentFirstLine());
dstParagraph[i].setIndentStart(srcParagraph[i].getIndentStart());
dstParagraph[i].editAsText().setFontSize(srcParagraph[i].editAsText().getFontSize());
}
}
}
它有什么作用?
- 如果找到文件名,根据电子表格中的更新信息创建一个临时文档
- 将特定段落从临时文档复制到现有文档
- 使用setTrashed()方法删除临时文档。