英文:
Copying gsheet without bound apps script project
问题
我有一个与之绑定的 Google Apps 脚本项目的 Google 表格。
使用 copy()
和 makeCopy()
复制带有绑定脚本的 gsheet。
如何以编程方式制作此电子表格的副本,而不将绑定的应用程序脚本项目复制到新副本中?
英文:
I have a google sheet with a google apps script project bound to it.
Using copy()
and makeCopy()
copy the gsheet with the bound script.
How can I programmatically, make copies of this spreadsheet without carrying the bound apps script project into the new copy?
答案1
得分: 0
我认为您的目标如下。
- 您想要复制一个电子表格,但不包括与容器绑定的脚本。
问题和解决方法:
在当前阶段,存在以下问题。
-
Spreadsheet
类的copy
方法和File
类的makeCopy
方法不能直接实现您期望的结果。容器绑定的脚本也会被复制。 -
在智能芯片添加之后,似乎通过复制从具有 Sheets API 检索的 JSON 对象的电子表格来复制电子表格不能直接实现。
-
尽管可以使用
Drive.Files.remove("### 容器绑定脚本的脚本ID ###")
来删除复制电子表格中的容器绑定脚本。但是很不幸,在当前阶段,无法直接检索复制 Google 电子表格后的容器绑定脚本的脚本ID。或者说,这样做很困难。
根据上述情况,我想提出一个解决方法。解决方法的流程如下。
-
创建一个新的 Google 电子表格。
-
使用
Sheet
类的copyTo
将所有工作表从源电子表格复制到创建的电子表格。- 通过这种方式,智能芯片也可以被复制。
-
使用 Sheets API 复制受保护的范围和工作表。
- 因为使用
Sheet
类的copyTo
无法复制受保护的信息。
- 因为使用
当这个流程在一个示例脚本中得以体现时,如下所示。
示例脚本:
请将以下脚本复制并粘贴到源 Google 电子表格的脚本编辑器中,并保存脚本。在使用此脚本之前,请在高级 Google 服务中启用 Sheets API。
// 参考: https://tanaikech.github.io/2021/03/26/copying-protections-for-spreadsheet-using-google-apps-script/
function copyProtectedRanges_(srcId, dstId) {
const obj = Sheets.Spreadsheets.get(dstId, { fields: "sheets(properties(sheetId),protectedRanges(protectedRangeId))" }).sheets
.reduce((o, s) => {
o.sheetIds.push(s.properties.sheetId);
if (s.protectedRanges && s.protectedRanges.length > 0) {
s.protectedRanges.forEach(({ protectedRangeId }) => o.protectedRangeIds.push({ deleteProtectedRange: { protectedRangeId } }));
}
return o;
}, { sheetIds: [], protectedRangeIds: [] });
const requests = Sheets.Spreadsheets.get(srcId, { fields: "sheets/protectedRanges" }).sheets
.reduce((ar, s, i) => {
if (s.protectedRanges && s.protectedRanges.length > 0) {
const temp = s.protectedRanges.map(e => {
delete e.protectedRangeId;
e.range.sheetId = obj.sheetIds[i];
if (e.unprotectedRanges) {
e.unprotectedRanges.forEach(f => f.sheetId = obj.sheetIds[i]);
}
return { addProtectedRange: { protectedRange: e } };
});
ar = ar.concat(temp);
}
return ar;
}, obj.protectedRangeIds);
if (requests.length == 0) return;
Sheets.Spreadsheets.batchUpdate({ requests }, dstId);
}
// 请运行此函数。
function main() {
const srcSpreadsheet = SpreadsheetApp.getActiveSpreadsheet();
const dstSpreadsheet = SpreadsheetApp.create(`Copied ${srcSpreadsheet.getName()}`);
const srcSSId = srcSpreadsheet.getId();
const dstSSId = dstSpreadsheet.getId();
DriveApp.getFileById(dstSSId).moveTo(DriveApp.getFileById(srcSSId).getParents().next());
const temp = dstSpreadsheet.getSheets()[0].setName(Utilities.getUuid());
srcSpreadsheet.getSheets().forEach(sheet => sheet.copyTo(dstSpreadsheet).setName(sheet.getName()));
dstSpreadsheet.deleteSheet(temp);
copyProtectedRanges_(srcSSId, dstSSId);
}
-
运行此脚本时,在源电子表格的相同文件夹中创建了一个名为
Copied ###
的复制电子表格。而且,复制的电子表格不包含容器绑定的脚本。 -
您可以通过修改
Copied ${srcSpreadsheet.getName()}
来修改复制的电子表格名称。 -
如果您想要将复制的电子表格创建到特定文件夹中,请将
DriveApp.getFileById(dstSSId).moveTo(DriveApp.getFileById(srcSSId).getParents().next());
修改为DriveApp.getFileById(dstSSId).moveTo(DriveApp.getFolderById("###文件夹ID###"));
。
注意:
-
此示例脚本是一个简单的脚本。我认为大多数数据都可以复制到目标电子表格中。但是,如果有一些未复制的数据,可能需要修改脚本。请注意这一点。
-
如果您的源电子表格没有受保护的范围和工作表,可以删除
copyProtectedRanges_(srcSSId, dstSSId);
。
英文:
I believe your goal is as follows.
- You want to copy a Spreadsheet without including the container-bound script.
Issue and workaround:
In the current stage, there are the following issues.
-
copy
method of Class Spreadsheet andmakeCopy
method of Class File cannot directly achieve your expected result. The container-bound script is also copied. -
After the smart chips were added, it seems that the copy of Spreadsheet by copying the JSON object retrieved from Spreadsheet with Sheets API cannot be directly achieved.
-
Although when
Drive.Files.remove("### script ID of container-bound script ###")
is used, the container-bound script in the copied Spreadsheet can be deleted. But, unfortunately, in the current stage, the script ID of the container-bound script cannot be directly retrieved after a Google Spreadsheet was copied. Or, it is so difficult to do it.
From the above situation, in this answer, I would like to propose a workaround. The flow for the workaround is as follows.
-
Create a new Google Spreadsheet.
-
Copy all sheets from the source Spreadsheet to the created Spreadsheet using
copyTo
of Class Sheet.- By this, the smart chips can be also copied.
-
Copy the protected ranges and sheets using Sheets API.
- Because the protected information cannot be copied with
copyTo
of Class Sheet.
- Because the protected information cannot be copied with
When this flow is reflected in a sample script, it becomes as follows.
Sample script:
Please copy and paste the following script to the script editor of the source Google Spreadsheet and save the script. Before you use this script, please enable Sheets API at Advanced Google services.
// Ref: https://tanaikech.github.io/2021/03/26/copying-protections-for-spreadsheet-using-google-apps-script/
function copyProtectedRanges_(srcId, dstId) {
const obj = Sheets.Spreadsheets.get(dstId, { fields: "sheets(properties(sheetId),protectedRanges(protectedRangeId))" }).sheets
.reduce((o, s) => {
o.sheetIds.push(s.properties.sheetId);
if (s.protectedRanges && s.protectedRanges.length > 0) {
s.protectedRanges.forEach(({ protectedRangeId }) => o.protectedRangeIds.push({ deleteProtectedRange: { protectedRangeId } }));
}
return o;
}, { sheetIds: [], protectedRangeIds: [] });
const requests = Sheets.Spreadsheets.get(srcId, { fields: "sheets/protectedRanges" }).sheets
.reduce((ar, s, i) => {
if (s.protectedRanges && s.protectedRanges.length > 0) {
const temp = s.protectedRanges.map(e => {
delete e.protectedRangeId;
e.range.sheetId = obj.sheetIds[i];
if (e.unprotectedRanges) {
e.unprotectedRanges.forEach(f => f.sheetId = obj.sheetIds[i]);
}
return { addProtectedRange: { protectedRange: e } };
});
ar = ar.concat(temp);
}
return ar;
}, obj.protectedRangeIds);
if (requests.length == 0) return;
Sheets.Spreadsheets.batchUpdate({ requests }, dstId);
}
// Please run this function.
function main() {
const srcSpreadsheet = SpreadsheetApp.getActiveSpreadsheet();
const dstSpreadsheet = SpreadsheetApp.create(`Copied ${srcSpreadsheet.getName()}`);
const srcSSId = srcSpreadsheet.getId();
const dstSSId = dstSpreadsheet.getId();
DriveApp.getFileById(dstSSId).moveTo(DriveApp.getFileById(srcSSId).getParents().next());
const temp = dstSpreadsheet.getSheets()[0].setName(Utilities.getUuid());
srcSpreadsheet.getSheets().forEach(sheet => sheet.copyTo(dstSpreadsheet).setName(sheet.getName()));
dstSpreadsheet.deleteSheet(temp);
copyProtectedRanges_(srcSSId, dstSSId);
}
-
When this script is run, a copied Spreadsheet of
Copied ###
is created in the same folder of the source Spreadsheet. And, the copied Spreadsheet has no container-bound script. -
You can modify the copied Spreadsheet name by modifying
Copied ${srcSpreadsheet.getName()}
. -
If you want to create the copied Spreadsheet to the specific folder, please modify
DriveApp.getFileById(dstSSId).moveTo(DriveApp.getFileById(srcSSId).getParents().next());
toDriveApp.getFileById(dstSSId).moveTo(DriveApp.getFolderById("###folderId###"));
.
Note:
-
This sample script is a simple script. I think that most data can be copied to the destination Spreadsheet. But, if there are some uncopied data, the script might be required to be modified. Please be careful about this.
-
If your source Spreadsheet has no protected ranges and sheets,
copyProtectedRanges_(srcSSId, dstSSId);
can be removed.
References:
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论