Set Google Drive file modification time from Google App Script - google-apps-script

I can't find a way to change modification date on a file on Google Drive using Google Apps Script.
After I do a file.makeCopy(newFile, newFolder), I would like to make the modification time on the new copy the same as the original file.
I can't find documented way to do this...

You want to modify the modified time of the file on Google Drive.
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.
In this answer, I used the method of Files: update in Drive API v3.
Sample script:
Before you use this script, please set the variables of newModifiedTime and fileId.
function myFunction() {
var newModifiedTime = "2019-01-01T00:00:00.000Z"; // Please set the new modified time.
var fileId = "###"; // Please set the file ID you want to modify the modified time.
var url = "https://www.googleapis.com/drive/v3/files/" + fileId;
var params = {
method: "patch",
headers: {Authorization: "Bearer " + ScriptApp.getOAuthToken()},
payload: JSON.stringify({modifiedTime: newModifiedTime}),
contentType: "application/json",
};
UrlFetchApp.fetch(url, params);
// DriveApp.createFile(blob);
}
Note:
The last line of // DriveApp.createFile(blob); is used for automatically adding the scope of https://www.googleapis.com/auth/drive and enabling Drive API.
In this case, please use the date string of RFC 3339 date-time. It's like 2019-01-01T00:00:00.000Z.
Although I tested this for Drive API v2, it seems that Drive API v2 cannot modify the modified date of the file.
References:
Files: update(Drive API v3)
Class UrlFetchApp
If I misunderstood your question and this was not the result you want, I apologize.

There is a way to modify the modifiedTime with the advanced Drive service (don't forget to turn it on in the project settings).
function setModifiedDate(originalFile /* DriveApp.File */, copyFile /* DriveApp.File */) {
/* based on the Drive API v2 */
Drive.Files.patch({
modifiedDate: originalFile.getLastUpdated().toISOString()
}, copyFile.getId(), {
setModifiedDate: true
})
}

This changes the lastUpdated() date and the Last Modified date on the Drive. The file I'm using is just an ascii text file.
function modifyFile() {
var file=DriveApp.getFileById("fileId");
Logger.log(file.getLastUpdated());
var content=file.getBlob().getDataAsString();
content+='\nThis is a new line';
file.setContent(content);
Logger.log(file.getLastUpdated());
}
This changes the Last Modified Date of a Google Doc
function modifyADocFile() {
var file=DriveApp.getFileById("Document Id");
var doc=DocumentApp.openById(file.getId());
var body=doc.getBody();
var bodytext=body.getText();
bodytext+='\nThis is a new line';
body.setText(bodytext);
doc.saveAndClose();
}

Related

How to use Drive v3 in google app script?

I have the following code that I would like to use Drive v3 in App Script
function myFunction() {
let id = "<YOUR ODS FILE ID>";
let file = DriveApp.getFileById(id);
let fileBlob = file.getBlob();
newFile = {
name: "New File",
mimeType: "application/vnd.google-apps.spreadsheet"
}
try{
Drive.Files.create(newFile, fileBlob);
}catch(e){
Logger.log("Error");
Logger.log(e);
}
}
However by default the google app script only makes v2 available. The documentation does not seem so easy to do this migration directly in App Script.
How can I use Drive v3 in this code directly in the google app script?
I believe your goal as follows.
You want to convert the file of let file = DriveApp.getFileById(id) (ODS file from let id = "<YOUR ODS FILE ID>") as Google Spreadsheet.
You want to achieve this using Drive API v3 with UrlFetchApp of Google Apps Script.
Modification points:
Although, unfortunately, I'm not sure about the file size of let file = DriveApp.getFileById(id) from your question, I think that in your situation, the file content is required to be sent as multipart/form-data. Ref
At Advanced Google services, this multipart/form-data is achieved at the internal server side. But, when you want to achieve this using UrlFetchApp, it is required to create the request body.
When above points are reflected to the sample script, it becomes as follows.
Sample script:
In this case, Drive API is used. So please enable Drive API at Advanced Google services.
function myFunction() {
const fileId = "<YOUR ODS FILE ID>"; // Please set the file ID.
const metadata = {
name: "New File",
mimeType: MimeType.GOOGLE_SHEETS,
// parents: ["### folder ID ###"], // If you want to put the converted Spreadsheet to the specific folder, please use this.
};
const payload = {
metadata: Utilities.newBlob(JSON.stringify(metadata), "application/json"),
file: DriveApp.getFileById(fileId).getBlob(),
};
const options = {
method: "post",
payload: payload,
headers: { authorization: "Bearer " + ScriptApp.getOAuthToken() },
};
const url = "https://www.googleapis.com/upload/drive/v3/files?uploadType=multipart"
const res = UrlFetchApp.fetch(url, options).getContentText();
console.log(res);
// DriveApp.createFile(blob) // This is used for automatically detecting the scope of "https://www.googleapis.com/auth/drive".
}
Note:
At uploadType=multipart method, the official document says as follows.
Use this upload type to quickly transfer a small file (5 MB or less) and metadata that describes the file, in a single request.
When you want to use the file more than 5 MB for this, please use the resumable upload. Ref
By the way, in your script, it seems that the ODF file on your Google Drive is used. In this case, you can also achieve your goal using the method of "Files: copy" in Drive API v3. The sample script is as follows.
function myFunction2() {
const fileId = "<YOUR ODS FILE ID>"; // Please set the file ID.
const url = `https://www.googleapis.com/drive/v3/files/${fileId}/copy`;
const params = {
method: "post",
headers: {authorization: `Bearer ${ScriptApp.getOAuthToken()}`},
contentType: "application/json",
payload: JSON.stringify({name: "New name", mimeType: MimeType.GOOGLE_SHEETS})
};
const res = UrlFetchApp.fetch(url, params);
console.log(res.getContentText())
}
References:
Files: create
Upload file data
fetch(url, params)
Files: copy

ScriptApp.getOAuthToken not getting the right permissions for drive through url fetch app?

Trying to explore this with a very simple script but I'm getting an insufficient permissions error:
function mini(){
var gdriveId = "1hp8ncIG4Ww7FH8wi7HjJzzzzzzz";
var options = {
method: "GET",
headers: {
'Authorization': 'Bearer ' + ScriptApp.getOAuthToken()
},
}
var url = "https://www.googleapis.com/drive/v2/files/"+gdriveId+"/children";
var response = JSON.parse(UrlFetchApp.fetch( url, options).getContentText());
}
I tried enabling the v2 drive api in the advanced google services dropdown but that didn't work.
I believe your situation and goal as follows.
From gdriveId in your script, I thought that you want to retrieve the folder list in the root folder of gdriveId using the method of "Children: list" in Drive API v2.
You have already enabled Drive API at Advanced Google Services.
For this, how about this answer?
Modification points:
When your script is put to new GAS project and Drive API is enabled at Advanced Google Services, the scopes of the project is only https://www.googleapis.com/auth/script.external_request. The required scope can be automatically detected by the script editor. But, even when Drive API is only enabled, it seems that no scopes are added. I think that the reason of your issue is this.
Under above situation, if you want to retrieve the access token including the required scopes, in order to make the script editor automatically detect the scope of https://www.googleapis.com/auth/drive.readonly, for example, please put // DriveApp.getFiles() to the script as a comment line.
In this case, when you use the methods for other scopes in your script, those scopes can be automatically detected and added by the script editor.
Modified script 1:
When your script is modified, it becomes as follows.
function mini(){
var gdriveId = "1hp8ncIG4Ww7FH8wi7HjJzzzzzzz";
var options = {
method: "GET",
headers: {
'Authorization': 'Bearer ' + ScriptApp.getOAuthToken()
},
}
var url = "https://www.googleapis.com/drive/v2/files/"+gdriveId+"/children";
var response = JSON.parse(UrlFetchApp.fetch( url, options).getContentText());
}
// DriveApp.getFiles() // <--- Added this comment line. By this, the scope of https://www.googleapis.com/auth/drive.readonly is added.
Modified script 2:
When the method of Advanced Google service is used, the scope of https://www.googleapis.com/auth/drive is automatically added. By this, the following script works.
function test() {
var gdriveId = "1hp8ncIG4Ww7FH8wi7HjJzzzzzzz";
var res = Drive.Children.list(gdriveId);
console.log(res)
}
Other pattern:
From June 1, 2020, the files and folders in the shared Drive can be retrieved by Drive service. So you can also use the following script.
function myFunction() {
const getFolderList = (id, folders = []) => {
const f = DriveApp.getFolderById(id);
const fols = f.getFolders();
let temp = [];
while (fols.hasNext()) {
const fol = fols.next();
temp.push({name: fol.getName(), id: fol.getId(), parent: f.getName()});
}
if (temp.length > 0) {
folders.push(temp);
temp.forEach((e) => getFolderList(e.id, folders));
}
return folders.flat();
};
var gdriveId = "###"; // Please set the Drive ID.
const res = getFolderList(gdriveId);
console.log(res);
}
References:
Advanced Google services
Children: list of Drive API v2
Authorization Scopes
If you want to give permission to write with ScriptApp.getOAuthToken(), just add the following code in a commented out form and authorize it at runtime. If you don't do this, you'll only be able to download and browse.
//DriveApp.addFile("test");
Reference URL:https://00m.in/UeeOB

Calling Google Apps Script in another Project File

I am trying to call a Google Apps Script file that is in another project file following the sample here using UrlFetchApp.fetch.
I'm getting the same error that the original poster mentions but I am not having an success with my sample.
Did Google change something in the last 4 years that prevents me from calling the other script file?
See script below.
Below is the function that I am using to call the other project file
function makeRequest()
{
var webAppUrl = "https://script.google.com/macros/s/***/exec";
var auth = ScriptApp.getOAuthToken();
var header = { 'Authorization': 'Bearer ' + auth };
var options = { 'method':'post', 'headers':header };
var resp = UrlFetchApp.fetch(webAppUrl, options);
Logger.log(resp);
}
Below is the function that I am trying to call. Additionally, I have ran the authorizeDrive function and published as a webapp.
function authorizeDrive()
{
var forScope = DriveApp.getRootFolder();
}
function doPost()
{
var ss = SpreadsheetApp.openById('ssID');
var name = ss.getName();
Logger.log('called');
return ContentService.createTextOutput(name);
}
You want to run the Google Apps Script in the GAS project A by accessing to Web Apps from the GAS project B.
In your case, Web Apps is deployed by Who has access to the app: of Only myself or Anyone.
You want to access to Web Apps using the access token.
The GAS project A and B are in your Google Drive.
If my understanding is correct, how about this answer? Please think of this as just one of several possible answers.
I think that in your case, the scope is required to be added to the project including makeRequest(). So in order to add the scope for accessing to Web Apps using the access token, how about the following modification?
Modified script:
function makeRequest()
{
var webAppUrl = "https://script.google.com/macros/s/***/exec";
var auth = ScriptApp.getOAuthToken();
var header = { 'Authorization': 'Bearer ' + auth };
var options = { 'method':'post', 'headers':header };
var resp = UrlFetchApp.fetch(webAppUrl, options);
Logger.log(resp);
}
// DriveApp.getFiles() // This comment line is used for automatically detecting the scope.
Please add the // DriveApp.getFiles() of the comment line. This comment line is used for automatically detecting the scope.
In this case, https://www.googleapis.com/auth/drive.readonly is added to the scopes. If this didn't resolve your issue, please add the comment line of // DriveApp.createFile(blob). In this case, https://www.googleapis.com/auth/drive is added.
Note:
When the script of Web Apps side is modified, please redeploy it as new version. By this, the latest script is reflected to Web Apps. Please be careful this.
If the owner of GAS project of Web Apps is not your account which has the script of makeRequest(), at first, please share the GAS project file of Web Apps with your account. Then, please test it. This specification has added at April 11, 2018. Also, please be careful this.
References:
Web Apps
Taking advantage of Web Apps with Google Apps Script
If I misunderstood your question and this was not the result you want, I apologize.

Copying a spreadsheet copies all linked files as well

I want to be able to only copy the spreadsheet and all it's sheets along with all defined sheet names, when I utilize the library method:
spreadSheet.copy(newSSName);
Or,
myFile.makeCopy(newNameOfFile);
Currently these methods copy all linked forms and scripts used in the forms. This is an unnecessary side effect for what I need and will result in a large mess in the Drive folder. Is there a way to do this quickly and efficiently without copying cell by cell, sheet by sheet? Or is that the only option?
Thanks.
How about this workaround? In this workaround, Sheets API is used for copying a Spreadsheet. In the case of copy() of Class Spreadsheet, makeCopy() of Class File and Files: copy of Drive API, the copied spreadsheet includes the bound scripts and linked forms. So I thought to use Sheets API. The flow of this workaround is as follows.
Retrieve object of source Spreadsheet using spreadsheets.get.
Create new Spreadsheet by including the retrieved object using spreadsheets.create.
By this flow, the copied Spreadsheet without including the bound scripts and linked forms can be created. The sample script is as follows. When you use this script, please enable Sheets API at Advanced Google Services and API console. You can see about how to enable Sheets API at here.
Sample script :
var fileId = "### fileId of source Spreadsheet ###"; // Please set here.
var obj = Sheets.Spreadsheets.get(fileId, {fields: "namedRanges,properties,sheets"});
Sheets.Spreadsheets.create(obj);
Note :
When you use this script, please set fileId of source Spreadsheet.
At spreadsheets.create of Sheets API, the Spreadsheet cannot be created in the specific folder. So the copied Spreadsheet is created to root folder. If you want to create it in the specific folder, please move it after the Spreadsheet was copied. Of course, you can do it using script.
If you want to include developerMetadata of Spreadsheet, please add it to fields.
References :
copy() of Class Spreadsheet
makeCopy() of Class File
Files: copy of Drive API
Method: spreadsheets.get of Sheets API
Method: spreadsheets.create of Sheets API
If this was not what you want, I'm sorry.
Copy Spreadsheet to Destination Folder without copying Scripts
function copyspreadsheetwithoutgoogleappsscript() {
const ssid = "";//source spreadsheet id
const fldrid = "";//destination folder id
Drive.Files.update({ parents: [{ id: fldrid }] }, Sheets.Spreadsheets.create(Sheets.Spreadsheets.get(ssid,{fields:"sheets,namedRanges,properties"})).spreadsheetId);
}
for sub properties use /
Working With Field Masks
available fields between spreadsheets and spreadsheet properties
Can also do it using the API endpoint if you wish:
function copyssurl() {
const ssid = "";
const dfldrid = "";
const url = `https://sheets.googleapis.com/v4/spreadsheets/${ssid}?fields=(sheets%2CnamedRanges%2Cproperties)`;
const options = { "method": "get", "muteHttpExceptions": true, "headers": { "Authorization": "Bearer " + ScriptApp.getOAuthToken() } };
const resp = UrlFetchApp.fetch(url,options);
const obj = JSON.parse(resp.getContentText());
Drive.Files.update({ parents: [{ id: dfldrid }] }, Sheets.Spreadsheets.create(obj).spreadsheetId);
}
%2C is a comma
%2F is a forward slash

The modification date of Drive files change when Google Apps Script move a file to other folder

I am moving file this way:
var idOriginFolder = 'ABCDEFG12345abcdefg';
var originFolder = DriveApp.getFolderById(idOriginFolder);
var destinationFolder = DriveApp.createFolder('New Folder');
var searchString = '"'+idOriginFolder+'" in parents'
var foundFiles = DriveApp.searchFiles(searchString);
while (foundFiles.hasNext()){
var file = foundFiles.next();
destinationFolder.addFile(file);
originFolder.removeFile(file);
}
The files are moved correctly, but the modification date of every one moved file is changed to script execution date. Do you know any way to avoid this? When I move files throught of the Web Interface of Google Drive this not happen.
In my experience, the modification date of files is not changed by moving using Drive API v3. In your question, when the files were moved using DriveApp, the modification date was changed. I think that DriveApp uses Drive API v2. So I investigated this, because I was interested in this situation.
For Drive API v2
It was found that when the files were moved using drive.files.update and drive.files.patch, the modification date was changed.
For Drive API v3
It was found that when the files were moved using drive.files.update, the modification date was NOT changed.
Sample script :
The sample script for using Drive API v3 is as follows.
var idOriginFolder = 'ABCDEFG12345abcdefg';
var destinationFolder = DriveApp.createFolder('New Folder').getId();
var searchString = '"'+idOriginFolder+'" in parents'
var foundFiles = DriveApp.searchFiles(searchString);
var requests = [];
while (foundFiles.hasNext()){
var file = foundFiles.next();
requests.push({
url: "https://www.googleapis.com/drive/v3/files/" + file.getId() + "?addParents=" + destinationFolder + "&removeParents=" + idOriginFolder,
method: "patch",
headers: {"Authorization": "Bearer " + ScriptApp.getOAuthToken()},
muteHttpExceptions: true,
});
}
var res = UrlFetchApp.fetchAll(requests);
Logger.log(res)
Note :
From these results, it is considered that moving files by Web Interface may be due to Drive API v3.
This is a simple sample script. So if you want to move a lot of files, I recommend to use the Batching Requests.
Reference :
Files: update for Drive API v2
Files: patch for Drive API v2
Files: update for Drive API v3
Batching Requests
If this was not useful for you, I'm sorry.