Google Apps Script Save copy to read only - google-apps-script

How do you save a copy of the current Sheets file to a read-only copy? We have scripts that set values that once confirmed save to a new Sheets file. This new file needs to be set as ReadOnly as a part of the script.

In your situation, how about using content protection?
Sample script:
Before you use this script, please enable Drive API at Advanced Google services.
const fileId = "###"; // Please set the file ID. In your situation, please set the Spreadsheet ID.
Drive.Files.patch({contentRestrictions: [{readOnly: true}]}, fileId);
When this script is run, the Spreadsheet can be only viewed and the Spreadsheet cannot be written.
If you want to unprotect it, please modify {readOnly: true} to {readOnly: false} in the above script.
References:
Protect file content from modification
Files: patch

A Google Sheets file will always be editable by the file onwer
It will not accesiable by other users it all unless it is explicitly shared with them
-The sharing can take place either through the UI or by script
To do it by script, you can use the method Spreadsheet.addViewer() or File.addViewer()
If a user who has is already an editor needs to be "downgraded" to a viewer, you can do remove him from the editors with removeEditor() before adding him as a viewer
You can also remove all editors (apart from the spreadsheet owner), sample:
var ss = SpreadsheetApp.getActiveSpreadsheet();
ss.getEditors().forEach(function(editor){
ss.removeEditor(editor):
})

You can use the DriveApp Permission enum, .setSharing, and related methods to do this.
Assuming you have a reference to the File object represented by the new Sheets file, this function will change all sharing so that the file is "View only" for anyone who previously had edit permission.
/**
* Change sharing permissions on a file or folder.
* #param {File or Folder object} asset
*/
function makeViewOnly(asset)
{
// General sharing permissions: change VIEW to NONE if you only want specific people to view the file
asset.setSharing(DriveApp.Access.ANYONE, DriveApp.Permission.VIEW);
asset.setSharing(DriveApp.Access.ANYONE_WITH_LINK, DriveApp.Permission.VIEW);
// These only apply if the file is on a Workspace domain.
asset.setSharing(DriveApp.Access.DOMAIN, DriveApp.Permission.VIEW);
asset.setSharing(DriveApp.Access.DOMAIN_WITH_LINK, DriveApp.Permission.VIEW);
// Change all edit permissions to view permissions
users = asset.getEditors();
user.forEach(function (user) {
asset.removeEditor(user)
asset.addViewer(user)
});
}
If you have a reference to the Spreadsheet object for the newly created Sheets file instead of the File object, you can get the File object via
var asset = DriveApp.getFileById(spreadsheet.getId())

Related

Apps Script - Search Gmail label for all attachments and overwrite files to google drive folder

Using prior articles and questions found within stack overflow I was able to find a snippet of App Script that searches Gmail labels for attachments and moves them to a specific folder in Google Drive.
function saveAttachmentInFolder(){
var folder = DriveApp.getFolderById('xxosi2');
var userId = "please.thanks#gmail.com";
var query = "label:thankyoucards-reports";
var res = Gmail.Users.Messages.list(userId, {q: query});//I assumed that this works
res.messages.forEach(function(m){
var attA=GmailApp.getMessageById(m.id).getAttachments();
attA.forEach(function(a){
folder.createFile(a.copyBlob()).setName(a.getName());
});
});
}
I need to modify this code to perform the following additional functions:
If file exists, overwrite and retain version history
I have also played around with the answer found in the following thread to no avail as I believe this is hard coded in some way and too specific to the one file type (xlsx) Copying attachments from Gmail to Google Drive folder and overwriting old files with Apps Script.
I believe your goal is as follows.
You want to check this using the filename between the existing file in the folder and the attachment file.
You want to overwrite the existing file with the attachment file.
In this case, how about the following modification? In this case, Drive API is used. So, please enable Drive API at Advanced Google services.
From:
folder.createFile(a.copyBlob()).setName(a.getName());
To:
var filename = a.getName();
var files = folder.getFilesByName(filename);
if (files.hasNext()) {
Drive.Files.update({}, files.next().getId(), a.copyBlob(), {supportsAllDrives: true});
} else {
folder.createFile(a.copyBlob()).setName(filename);
}
When this modified script is run, the existing file is searched from the folder using the filename of the attachment file. When the file is found, the file is overwritten by the attachment file. When the file is not found, the file is created as a new file.
Note:
In this modified script, the existing file is overwritten. So, please be careful about this. I would like to recommend using a sample file for testing the script.
Reference:
Files: update of Drive API v2

How to transfer ownership of a Google spreadsheet and to be a viewer

Is it possible to hand over the owner permission to the other user and change the former owner to a viewer using apps script?
Transferring the owner permission to another user and being a viewer
I'm trying to make a script that hands over the owner to another user and changes the former owner to a viewer or prohibits the former owner from editing the Google Spreadsheet. The problem is the script is run by the former owner and the script cannot remove the edit permission of the former owner.
What I have tried
I tried setViewer(), sheet.protect(), and contentRestictions.readOnly but all of them was not a viable solution
removeEditors() and setViewer()
setViewer() method to an editors has no effects, and after applying removeEditors() to the former owner(after changing the owner, of course) the script cannot execute setViewer() since it does not have permission anymore.
sheet.protect()
the method gives the permission to edit the protected range for the owner and the user who runs the script. not eligible.
contentRestrictions.readOnly
the restriction can be unlocked by editors. not feasible.
Code for contentRestrictions:
function setReadOnly() {
var ss = SpreadsheetApp.getActive();
var supervisor = 'xxxxxxxx#gmail.com';
file = DriveApp.getFileById(ss.getId());
try {
Drive.Files.update({
'writersCanShare': false,
'copyRequiresWriterPermission': true,
'contentRestrictions': [{ 'readOnly': true }]
}, ss.getId());
file.setSharing(DriveApp.Access.ANYONE_WITH_LINK, DriveApp.Permission.NONE);
file.setOwner(supervisor);
} catch (e) {
console.log(e);
}
}
I recently found the contentRestrictions.readOnly has been implemented to Google Drive API. While I thought it would be a great way to restrict the editors from modifying the file content, I realized the users who are editors can "UNLOCK" the file with a click even it was locked by the owner.
I don't understand the use of this property if the restriction can be resolved by editors. The explanation in the docs says that we can use this field to prevent modifications to the title, uploading a new revision, and addition of comments. However, editors easily unlock the files, and the viewers cannot modify the file anyway.
I believe your current situation and your goal as follows.
You have a Google Spreadsheet and a script. You are the owner of them.
From your script, the script is the container-bound script of the Spreadsheet.
When you run the script, you want to transfer the owner of Spreadsheet to other user.
After the owner was transferred, you want to keep to have the permission for the Spreadsheet as the reader which is not the writer.
Modification points:
In this case, I thought that the methods of "Permissions: insert" and "Permissions: update" of Drive API (in this case, Drive API is used with Advanced Google services.) might be able to be used for achieving your goal.
The flow of my proposing script is as follows.
Retrieve the permission ID for you.
In the current stage, you are the owner of the file.
Transfer the owner of file (in your case, it's Spreadsheet.).
In the current stage, you are not the owner.3. Update your permission from writer to reader.
Create a shortcut of the owner-transferred Spreadsheet to the root folder.
Because, when the owner is transferred, the file can be seen at the folder of "Shared with me".
If you are not required to create the shortcut, please remove this part.
This flow is reflected to a script, it becomes as follows.
Modified script:
Please copy and paste the following script to the script editor of the container-bound script of Google Spreadsheet. And, before you use this script, please enable Drive API at Advanced Google services.
function myFunction() {
const email = "###"; // Please set the email. The owner of the file is transferred to this email.
const fileId = SpreadsheetApp.getActive().getId();
// 1. Retrieve the permission ID for you. In the current stage, you are the owner of the file.
const permissionId = Drive.Permissions.list(fileId).items[0].id;
// 2. Transfer the owner of file (in your case, it's Spreadsheet.). In the current stage, you are not the owner.
Drive.Permissions.insert({role: "owner", type: "user", value: email}, fileId, {sendNotificationEmail: false, moveToNewOwnersRoot: true});
// 3. Update your permission from `writer` to `reader`.
Drive.Permissions.update({role: "reader"}, fileId, permissionId);
// 4. Create a shortcut of the owner-transferred Spreadsheet to the root folder. Because, when the owner is transferred, the file can be seen at the folder of "Shared with me".
Drive.Files.insert({mimeType: MimeType.SHORTCUT, shortcutDetails: {targetId: fileId}}, null);
}
Note:
When above script is run the owner of Spreadsheet is changed to email. And your permission becomes the reader. So in this case, you cannot see the script of Spreadsheet. Please be careful this.
In this case, after the owner of Spreadsheet is changed, you cannot edit the Spreadsheet and the script. So please be careful this.
References:
Permissions: insert
Permissions: update
I tested my own file below, only me have access to the file.
Code:
function myFunction() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var ssId = ss.getId();
var file = DriveApp.getFileById(ssId);
var owner = file.getOwner();
var new_owner = 'new_owner#gmail.com';
file.setOwner(new_owner);
file.removeEditor(owner); // After this line, you can't view/edit the file
Logger.log(file.getOwner()); // Will fail
}
Things noted during testing:
Running removeEditor on yourself after setting another user as owner will restrict you from even viewing the file as per testing
Owner is immune with removeEditor (can't be removed by such)
Only the owner can set the ownership to other users
Please see a similar question that has an answer pointing to this, it will give you some insights regarding Google Apps Domain-Wide Delegation of Authority.

Can I add a script to a Google Sheet using another script?

I have a script populate() to run an automation script on Google Sheets. I want this script to be run on over a hundred Google Sheets files. Is there a way to write a script to add to (or at least run) my populate() script on all those files? I prefer to be able to add the script to each file because we may need to run the scripts multiple times for each file. Otherwise, I will have to manually copy/paste the script to each sheet, which takes time.
Update: Removed the part about converting Excel files to Google Sheets because I found the answer for that on another thread here.
Solution
From what I have understood from your post, your question is about how to convert a series of Excel files inside a Drive folder into Google Sheets to then execute a function in them.
For that, you could iterate over all the files of your folder and convert them with the use of Drive.insert from the Drive API. For that, you will have to enable in your script the Advanced Google Services. If you do not know how to do so, please follow these indications.
The follow script performs what I think you are aiming for, it has self explanatory comments:
function convertExcelToGoogleSheets() {
// Id of the folder where you have all your excel files
var folderId = 'FOLDERID';
var folder = DriveApp.getFolderById(folderId);
// Get all the files in your folder and iterate over them
var files = folder.getFiles();
// For each file:
while (files.hasNext()){
var file = files.next();
// Get its blob (content)
var blob = file.getBlob();
// Create the resource to insert the Google Sheet file. Put the same name
// as the original and set the type of the file to Google sheets and its
// parent folder to where the excel files are located
var resource = {
title: file.getName(),
mimeType: MimeType.GOOGLE_SHEETS,
parents: [{id: folderId}],
};
// User Drive API to insert the actual file and get its ID
var id = Drive.Files.insert(resource, blob).getId();
// Whatever function you want to perform in the newly created Google Sheet
// pasing the parameter of the file id
myScript(id);
}
}
// Set the first cell of all the converted sheets to "IT WORKED"
function myScript(id){
SpreadsheetApp.openById(id).getActiveSheet().getRange('A1').setValue('IT WORKED!');
}
EDIT: If you want to run a script for each of this converted sheets you can simply call it in the while loop and pass the new file id as a parameter to then be able to use the script (remember to encapsulate this script in a function).
I hope this has helped you. Let me know if you need anything else or if you did not understood something. :)

How do I delete a specific type of file (CSV files) within a specific folder with google app script on a shared google drive folder

I am having trouble adding files to the shared google drive folder as well as not being able to remove specific types of files of any kind within my script. The shared google drive file gives me issues. The code below works for me on my own personal google drive folder for adding and removing CSV's from one google drive folder to any another that I specify. However, it does not work with our general shared google drive folder. I have been authorized permission via the google cloud console API for Drive & Sheets but I am still having permission issues. Any help or clarification on this issue would be greatly appreciated.
Here are two different pieces of code. The first one with function moveFiles() works on my personal drive but not in the shared folders. Here is also some more code that I was playing around with to test the shared folders in a simpler manner. I was able to get the putFile() function to put a newDoc inside a shared google drive folder but not able to remove it.
function moveFiles(source_folder, dest_folder)
{
// set current destination to grab the folder from
var currentFolder=DriveApp.getFolderById("1emSsRay_WI_z_qBUpQIoccxQID28FvB0");
// grab only the csv's within current folder
var docs = DriveApp.getFilesByType(MimeType.CSV);
// set target destination where we will store old csv's that have been processed & Analyzed
var destination = DriveApp.getFolderById("1wYG1Gd5z0-nucedSMOBn8ZJs68ZgR8Hb");
// iterate through the csv's files within the currentFolder, add them to the destination and remove them from the current folder
while (docs.hasNext())
{
var doc = docs.next();
destination.addFile(doc); // get error "Cannot use this operation on a shared drive item(line 13, file "SharedDriveMove")
currentFolder.removeFile(doc);
}
}
function putFile()
{
var newDoc = DocumentApp.create('Testing Team Drive MoveTo').getId();
var file = DriveApp.getFileById(newDoc);
var moveFile = DriveApp.getFolderById('1emSsRay_WI_z_qBUpQIoccxQID28FvB0').addFile(file);
}
function takeFile()
{
var filesIterator = DriveApp.getFilesByName('Testing Team Drive MoveTo');
while (filesIterator.hasNext())
{
var file = filesIterator.next();
}
var cleanup = DriveApp.getFolderById('1wYG1Gd5z0-nucedSMOBn8ZJs68ZgR8Hb').addFile(file,{"supportsAllDrives": true}); // get error "Cannot find method addFile(File,Object).(line 15,file"Code")
var moveFile = DriveApp.getFolderById('1emSsRay_WI_z_qBUpQIoccxQID28FvB0').removeFile(file,{"supportsAllDrives": true});
}
DriveApp is an Apps Script class whereby it has it's limitations among Drive. Also, as the documentation says:
This method does not delete the file, but if a file is removed from
all of its parents, it cannot be seen in Drive except by searching for
it or using the "All items" view.
You should do it with the Drive API instead:
Use "supportsAllDrives", otherwise it won't find the file if it's in a Shared Drive (until this is deprecated in 2020).
Drive.Files.remove("your file ID", {"supportsAllDrives": true});
You also have to authorize the Drive.File scope.

The .makeCopy () function does not duplicate gSite

My code:
function myFunction() {
var file = DriveApp.getFileById("ID");
var folder = DriveApp.getFolderById("ID");
file.makeCopy(folder );
}
effect:
Access denied: DriveApp. (line 5, file "Code")
You want to copy the files of Google Site which has the mimeType of application/vnd.google-apps.site using a script.
If my understanding is correct, unfortunately, in the current stage, it cannot achieved it. I think that file with application/vnd.google-apps.site is a special from the following specification.
When a file of application/vnd.google-apps.site is created, canCopy of the property of capabilities which is a metadata becomes false as the default.
canCopy is not directly writable. So it cannot modify to true.
Files of application/vnd.google-apps.site cannot be created by the script and API.
Because of above situation, the files of application/vnd.google-apps.site cannot be copied by the script and API.
Note:
Although the files of application/vnd.google-apps.site cannot be copied by the script and API, it can manually copy.
Reference:
Metadata of Files
If this information was not useful for you, I apologize.