CryptoLocker - restore Drive file version with Google Apps Scripts - google-apps-script

long story short I got infected by the CryptoLocker Virus. My “normal” local files are not the problem because these files I backup. But I was using the Google Drive Sync client and all my Drive files got encrypted. I didn’t back them up because I thought Google Drive is save and my data is stored all over the world (my fault I know).
Now I can see that Google Drive provides versioning. This means my old uploads are still on the server. I can restore the previous version file by file but by several thousand files, good luck.
I contacted the Google G Suite support team (I’m using Google G Suite for my business) and asked them if they can restore the latest version in one bulk action. The answer was “no you have to do it file by file”. Therefore I was checking the internet for scripts, tools etc.
I found a Google Apps Script in the Google Drive help forum “https://productforums.google.com/forum/#!topic/drive/p08UBFYgFs0https://productforums.google.com/forum/#!topic/drive/p08UBFYgFs0”.
1) I added the “Google Apps Script” app to my drive.
2) I created a new app and past the script:
function testSmallFolder() {
var smallFolder = DriveApp.getFolderById('FOLDER_ID_HERE');
var files = smallFolder.getFiles();
while (files.hasNext())
{
file = files.next();
deleteRevisions(file);
}
var childFolders = smallFolder.getFolders();
while(childFolders.hasNext())
{
var childFolder = childFolders.next();
Logger.log(childFolder.getName());
var files = childFolder.getFiles();
while (files.hasNext())
{
file = files.next();
deleteRevisions(file);
}
getSubFoldersAndDelete(childFolder);
}
}
function deleteRevisions(file)
{
var fileId = file.getId();
var revisions = Drive.Revisions.list(fileId);
if (revisions.items && revisions.items.length > 1)
{
for (var i = 0; i < revisions.items.length; i++)
{
var revision = revisions.items[i];
var date = new Date(revision.modifiedDate);
var startDate = new Date();
var endDate = new Date(revision.modifiedDate);
var fileName = Drive.Files.get(fileId);
if(revision.modifiedDate > "2017-02-16T10:00:00" && revision.modifiedDate < "2017-02-18T10:00:00" && revision.lastModifyingUserName == "ENTER_MODIFIED_USERNAME_HERE]]" && file.getName() !== "HELP_DECRYPT.URL" && file.getName() !== "HELP_DECRYPT.PNG" && file.getName() !== "HELP_DECRYPT.HTML")
{
Logger.log(' %s, Date: %s, File size (bytes): %s',file.getName(),
date.toLocaleString(),
revision.fileSize);
return Drive.Revisions.remove( fileId, revision.id);
}
}
} else
{
Logger.log('No revisions found.');
}
}function getSubFoldersAndDelete(parent)
{
parent = parent.getId();
var childFolders = DriveApp.getFolderById(parent).getFolders();
while(childFolders.hasNext())
{
var childFolder = childFolders.next();
var files = childFolder.getFiles();
while (files.hasNext())
{
file = files.next();
deleteRevisions(file);
}
getSubFoldersAndDelete(childFolder);
}
return;
}
3) The script provides 3 functions “testSmallFolder” / “deleteRevisions” / “getSubFoldersAndDelete”. Looks like the function “festSmallFolder” can just work on a certain folder. Line 2: FOLDER_ID_HERE
4) I created a folder and moved my files into this folder. Afterwards I got the folder ID (URL) and added it to the script.
5) In line 37 you can add the start and end date of the modification. I also adjusted the username in the same line.
6) I saved the script and ran the “testSmallFolder” function.
7) I get an error message: “ReferenceError: "Drive" is not defined. (line 27, file "Code")“.
Line 27 looks like this: „var revisions = Drive.Revisions.list(fileId);”.
I contacted again the Google G Suite support and asked them for help regarding this error. Their answer was “Sorry we do not support scripts.”
Now I’m here guys and asking you for help. Maybe we can get this script running so that I can restore the latest working version of my files.
I really appreciate any help you can provide.

You first of all must be sure to enable Advance Drive Service as documented here:
https://developers.google.com/apps-script/advanced/drive
Go in your script page, click on Resources --> then click on Google Advances Services, then enable (must be GREEN!) Drive API.
Maybe an alert open you a page where you MUST abilitate the script to access at your drive and folders (I think only the first time).
After do that, if you put a breakpoint on the 2nd row of the script and run the debugger istruction after istruction, you pass the 27 line without any problem, and you can see in the variable stack all the string for the first file, then for the second etc....
Stop then and Run normally this time and all will be ok.
Have a good night.

Have a look at https://gitlab.com/strider/delockyfier . This is a single page app in JavaScript which you could probably run as is, or easily convert to Apps Script if that's where you would prefer to run it from.

Related

Search files in shared Google Drive [duplicate]

I have some Google Apps script code that searchs for files and folders on TeamDrive.
The issue I am having is that if a file or folder is created by my colleague, when I run my script it can't find the file. If I create a file, and my colleague runs the script, the script can't find the file even though we both have access to view, edit and can see the files and folders in Drive. If one of us edits the file made by the other person, then it becomes visible from the search.
I ran into a similar problem with the Drive REST api when doing some android development. In Android when calling files().list(), It took my a while to find out that I had to set the following in order for my search to be successfull every single time.
.setSupportsTeamDrives(true)
.setIncludeTeamDriveItems(true)
.setCorpora("teamDrive")
.setTeamDriveId(myFolder.getTeamDriveId())
I assume I am running into the same issue with my apps script code.
//Create the N Google docs files
function CreateNFiles(){
var spreadsheet = SpreadsheetApp.getActive();
var Nmain = spreadsheet.getSheetByName("Nmain")
var spreadsheetId = spreadsheet.getId();
var pdfDir = "Form Data";
var TemplatesFolder = null;
//Check and see if there is a 'Form Data' folder
var NFolderId = null;
var RFolderId = DriveApp.getFileById(spreadsheetId).getParents().next().getId();
var files = DriveApp.searchFolders('parents="'+RFolderId+'" and trashed=false');
while (files.hasNext()) {
var myfile = files.next();
if(myfile.getName() == pdfDir){
NOFolderId = myfile.getId();
}
}
https://developers.google.com/apps-script/reference/drive/drive-app#searchFiles(String)
this says to refer to
https://developers.google.com/drive/api/v3/search-parameters#examples_for_teamdriveslist
so I could potentially use
corpora="teamDrive"
is there a way to setSupportsTeamDrives? and setIncludeTeamDriveItems? and setTeamDriveId? in google apps scripts
Finding Files and Folders in a Team Drive
Here's a couple of functions I've been working on for my own needs. They're still a work in progress but one can file folders within a team drive folder and another can find items within a team drive folder. The Logger.log is setup to display item number, title, id, and mimeType.
This one finds Items (either files or folders). You can tell them apart by their types.
function findItemsInTeamDriveFolder(teamDriveId,folderId){
var teamDriveId=teamDriveId || '0AFN5OZjg48ZvUk9PVA';
var folderId=folderId || '1LK76CVE71fLputdFAN-zuL-HdRFDWBGv';
var options={
"corpora":"teamDrive",
"includeTeamDriveItems":true,
"orderBy":"folder",
"q":Utilities.formatString('\'%s\' in parents',folderId),
"supportsTeamDrives":true,
"teamDriveId":teamDriveId
};
var files=Drive.Files.list(options);
var data=JSON.parse(files);
for(var i=0;i<data.items.length;i++){
Logger.log('\nItem: %s - Title: %s - Id: %s - Type:%s - Trashed: %s\n',i+1,data.items[i].title,data.items[i].id,data.items[i].mimeType,data.items[i].explicitlyTrashed?'true':'false');
}
}
This one just finds folders in a folder. It's not reentrant it's a one level deal but currently that's all I need.
function findFoldersInATeamDriveFolder(teamDriveId,folderId){
var teamDriveId=teamDriveId || '0AAc6_2qyI7C0Uk9PVA';
var folderId=folderId || '1HenWOXTSCg96iAvA0ZkgEA9EGKlch4fz';
var optionalArgs={
"corpora":"teamDrive",
"includeTeamDriveItems":true,
"orderBy":"folder",
"q":Utilities.formatString('\'%s\' in parents and mimeType = \'application/vnd.google-apps.folder\'',folderId),
"supportsTeamDrives":true,
"teamDriveId":teamDriveId
}
var list=Drive.Files.list(optionalArgs)
var data=JSON.parse(list);
for(var i=0;i<data.items.length;i++){
Logger.log('\nItem: %s - Title: %s - Id: %s - Type: %s - Trashed;%s\n',i+1,data.items[i].title,data.items[i].id,data.items[i].mimeType,data.items[i].explicitlyTrashed?'true':'false');
findItemsInTeamDriveFolder(teamDriveId,data.items[i].id)
}
}
I thought that they might be helpful.
Meta Data for a file:
Search Parameters:
Drive.Files.List Documentation:
I just used Coopers code to list files that were in a shared drive. I added the code to find the teamdriveID. Two things that cost me some time and might be helpful for others: the number of files is restricted to 100 per default. So I changed it to 200 here. Also, the options file includes trashed files (very confusing) so I filtered them out with an if statement - I am sure this can be done more elegantly but this worked :)
var resource = {
'value': 'emailstring',
'type': 'user',
'role': 'writer'
}
var teamDriveId
// If you have several Team Drives, loop through and give access
var TeamDrive = Drive.Teamdrives.list();
for(var i = 0; i < TeamDrive.items.length; i++) {
if(TeamDrive.items[i].name == "foldernamestring") {
// This ID may also be a single file inside a Team Drive
teamDriveId = TeamDrive.items[i].id;
Logger.log("found "+TeamDrive.items[i].name);
}
}
var options={
"corpora":"teamDrive",
"maxResults":200,
"includeTeamDriveItems":true,
"supportsTeamDrives":true,
"teamDriveId":teamDriveId
};
var files=Drive.Files.list(options);
var data=JSON.parse(files);
var nritems= data.items.length
Logger.log("nritems "+nritems);
for(var i=0;i<nritems;i++){
if (data.items[i].explicitlyTrashed == false){
Logger.log('\nItem: %s - Title: %s - Id: %s - Type:%s - Trashed: %s\n',i+1,data.items[i].title,data.items[i].id,data.items[i].mimeType,data.items[i].explicitlyTrashed?'true':'false');
}
}
I use simple Apps Script code to search folders in a Shared Drive:
function searchFolderByTitle(title,ShDrId){
var folders = DriveApp.searchFolders("title contains '"+title+"' and '"+ShDrId+"' in parents");
while (folders.hasNext()) {
var folder = folders.next();
Logger.log(folder.getName());
}
}
You should use the operator "in" not "=" with "parents" parameter.

Find and Replace function multiple documents in Shared Drives

The issue : I have multiple documents ( specifically, Google Sheets ) in multiple Shared Drives that need the same single word replaced. Some searching around this forum found me the following code, but it doesn't seem to be working. I'm not getting any error messages, but when I go check the files nothing has been changed. I checked and made sure I put in my search string and replacement string correctly.
Do I need to do something different to get it to check files in Shared Drives? I tried turning on the Drive API in Advanced Google Services, but that just gave me an error in line 6 (
var doc = DocumentApp.openById(file.getId()); )
Here's the code I was using :
function myFunction() {
var files = DriveApp.getFiles(); // Note: this gets *every* file in your Google Drive
while (files.hasNext()) {
var file = files.next();
Logger.log(file.getName());
var doc = DocumentApp.openById(file.getId());
doc.replaceText("My search string or regex", "My replacement string");
}
Logger.log("Done")
}
DriveApp.getFiles() returns only the files on "My Drive". In order to be able to get the files in a Shared Drive (formerly Team Drive) you have to enable the Drive Advanced Service and, instead of
var files = DriveApp.getFiles();
use something like
var files = Drive.Files.list(options);
Related
Finding shared date of a file in GAS
Google Apps Script TeamDrive DriveApp.searchFolders and DriveApp.searchFiles does not return any results
"Shared Drive" support in Google Apps Script
In order to access the shared drives, you need to activate the Drive API in the Advanced Google Services, but in order to access it, you will have to use Drive and not DriveApp.
If you want to retrieve the shared drives specifically and look for files with a certain name, you can try this:
Code
// Copyright 2020 Google LLC.
// SPDX-License-Identifier: Apache-2.0
function retrieveFiles() {
var allDrives = Drive.Drives.list().items;
let fileName = 'FILE_NAME';
for (let i = 0; i < allDrives.length; i++) {
var driveId = allDrives[i].id;
console.log(id)
var optionalArgs = {
'driveId': driveId,
'includeItemsFromAllDrives': true,
'corpora': 'drive',
'supportsAllDrives': true,
'q': "title=" + "'" + fileName + "'"
};
var filesReturned = Drive.Files.list(optionalArgs).items;
for (let j = 0; j < filesReturned.length; j++) {
var fileId = filesReturned[j].id;
var sheet = SpreadsheetApp.openById(fileId);
//sheet manipulation
}
}
}
Explanation
The above code retrieves all the shared drives by making use of the Drives:list method and then for each shared drive, searches for all the files which are named FILE_NAME using Files:list. Then, it accesses each file by using SpreadsheetApp.
Note
Please bear in mind that the code can be customized in order to fit your needs and requirements specifically.
Reference
Drive API Drives:list;
Drive API Files:list;
Advanced Google Services Apps Script;
SpreadsheetApp Class Apps Script.

Delete old files from a Google Drive folder

I created this script to delete files published more than 3 hours ago. And even if the latest file is older than 3 hours, it will not be deleted, so the folder will never be empty.
I enabled google's advanced service called DRIVE API V2.
I activated a trigger to analyze the folder every 5 minutes, but often the files are not deleted, they remain in the folder. The script works sometimes yes and sometimes no.
I would like to know what is wrong or what I need to edit to make it work perfectly.
function getOldFileIDs() {
// Old date is 3 Hours
var oldDate = new Date().getTime() - 3600*1000*3;
var cutOffDate = new Date(oldDate).toISOString();
// Get folderID using the URL on google drive
var folder = DriveApp.getFolderById('XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX');
var files = folder.searchFiles('modifiedDate < "' + cutOffDate + '"');
var obj = [];
while (files.hasNext()) {
var file = files.next();
obj.push({id: file.getId(), date: file.getDateCreated()});
}
obj.sort(function(x, y) {return x.date < y.date ? 1 : -1});
obj.shift();
var fileIDs = obj.map(function(e) {return e.id});
return fileIDs;
};
function deleteFiles() {
var fileIDs = getOldFileIDs();
fileIDs.forEach(function(fileID) {
Drive.Files.remove(fileID);
});
};
I also want some help so that this script can analyze if there are files with the same name inside the folder, if it exists, it delete surplus ones so that there is always only one file with each name.
And new detail, a new error appeared that until earlier did not happen ... Saying that:
API call failed for drive.files.delete with error Insufficient permissions for this file (line 24)
Line 24:
Drive.Files.remove(fileID);
By using consumer (free) accounts, only the file owner is able to delete them. To prevent the error, before calling Drive.Files.remove(fileID) your code should check if you are the file owner.
Related
Is it possible to get the owner for a Google Drive file?
How to get the owner of file using Google Drive API
How to programmticly remove a file from "share wtih me" in Google drive
How to retrieve the owner or Drive ID of a document with Google APIs?
Try this:
obj.sort(function(a, b) {
var a=new Date(a.date).valueOf();
var b=new Date(b.date).valueOf();
return b-a;
});

Access denied Drive App when trying to delete file

I wrote a little script to convert all the .xls and .xlsx files in my google drive to google sheets. However, the results of the script are very unreliable, sometimes I get an error, in some random part of the code, saying: "We're sorry, a server error occurred. Please wait a bit and try again.". And other times I get an error saying "Access denied Drive App" on the line where I try to delete my converted .xls or .xlsx files. I have enabled the Advance Google Services for the Drive API.
function convertFolder(folder){
Logger.log('Folder name '+folder.getName());
var files = folder.searchFiles('(title contains ".xlsx" or title contains ".xls") and (not title contains ".~lock")');
while(files.hasNext()){
var xFile = files.next();
var name = xFile.getName().replace(/\.[^/.]+$/, "");
Logger.log('File name '+name);
if (name.indexOf(".~lock")!=-1)
continue; //for some reason the "and (not title contains ".~lock")" didn't do the trick - why!?!
var parents = xFile.getParents();
var parents_arr = new Array();
while (parents.hasNext()){
parents_arr.push({'kind':"drive#fileLink",'id':parents.next().getId()});
}
var ID = xFile.getId();
var xBlob = xFile.getBlob();
var newFile = { title : name+'.gsheet',key : ID, parents: parents_arr, mimeType: "application/vnd.google-apps.spreadsheet"};
file = Drive.Files.insert(newFile, xBlob, {convert: true});
xFile.setTrashed(true);
}
var folders = folder.getFolders();
while (folders.hasNext())
convertFolder(folders.next());
}
function convertAllExcel(){
var folders = DriveApp.getFoldersByName('Data');
while (folders.hasNext()){
var folder = folders.next();
convertFolder(folder);
}
SpreadsheetApp.getUi().alert("done");
}
I suspect you're hitting a rate limit. If you are doing more than 20 write operations (create, update, delete) in rapid succession, you need to throttle the requests to approximately one every 1.5 seconds.
You're probably thinking "wtf", but that's seriously the maximum throughput you can achieve with Drive.
I had a similar problem! While running the script logged in as the main user seems to not have the maximum throughput limit, sharing a document with other users and letting them run the script that does writing operations in other user's drive does. 'Utilities.sleep(1500);' did the trick for me.
delFolder = DriveApp.getFolderById("folder_id")
delFiles = delFolder.getFiles();
while (delFiles.hasNext()) {
var delFile = delFiles.next();
Utilities.sleep(1500);
delFile.setTrashed(true);

I need to save a copy of a google spreadsheet to a specific directory using script

We currently have a nightly balancing process wherein we open an Excel template spreadsheet, enter the correct processing date, and then click a button, causing a VB script to fire off and create a new copy of the spreadsheet with the appropriate name (eg, "BAL 080114") in the appropriate folder and opens it, at which point the operator then closes the template, and continues work in the new copy.
The folder structure is:
Drive
--->Ops Room
------->Procedural Sheets
----------->Night Shift
--------------->Balancing
------------------->2014
----------------------->01-2014
...
----------------------->12-2014
We are trying to transition this to Google docs spreadsheets, and mostly it's working. But I cannot find a method to allow someone to open the template (stored in Balancing), run a "Start New Day" script, and have the script create the file in the proper sub-sub-folder. To wit, for 08/01/2014, the file should be stored in Balancing/2014/08-2014 as Bal 080114.
This is what I have thus far:
function startNewDay() {
// This code makes a copy of the current spreadsheet and names it appropriately
var ss = SpreadsheetApp.getActiveSpreadsheet();
// The file name is created and stored on sheet "Set Date" in cell B5
var fname = ss.getSheetByName("Set Date").getRange("B5").getValue();
var folderYear = Utilities.formatDate(new Date(), "GMT-6", "yyyy"); // top-level folder is by year in yyyy format
var folderMonth = Utilities.formatDate(new Date(), "GMT-6", "MM-yyyy"); // folder name is in mm-yyyy format
//the above is probably overkill, but I'll work on efficiency once I get it working at all :
//Everything works up to this point...
//This is where I start running into problems...
//The Master Copy SSID is <redacted>
var SSID = '<redacted>'
var folder = DocsList.getFolder(folderYear + "/" + folderMonth);
var backup = DocsList.getFileById(SSID).makeCopy(fname);
backup.addToFolder(folder); //This line will move the file to the appropriate folder
backup.removeFromFolder(DocsList.getRootFolder()); //This line is needed to remove the File from the Root
}
I borrowed the backup.* stuff from another StackOverFlow answer that had similar properties, but my version doesn't create the file.
Is what I'm trying to do even possible in Drive? Or will I just need to have the operators create a copy and then move it manually?
I apologize for any scripting ignorance - I've just started learning Google script this week, and I'm having trouble finding a common ground with my previous VB experience.
Thanks in advance for any help.
James
I am not able to comment so I cannot add on to what is already there.
Are the folders that are "manually" being created being created with the correct permissions by the correct person? Shared Folders can get tricky on Google Drive.
I may even suggest something like this and have the script just create the folders for you:
/*********************************************************
* Function to determine the destination folder based on
* provided criteria.
*
* #param {Folder} root The root folder.
* #param {String} folderYear The year of the report.
* #param {String} folderMonth The month of the report.
* #return {Folder} Destination Folder.
*********************************************************/
function returnFolder(root, folderYear, folderMonth) {
var dir = DocsList.getFolderById(root);
var folders = DocsList.getFolderById(root).getFolders();
var found = false;
var toReturn = "";
for (var i = 0; i < folders.length; i++) {
if (folders[i].getName() == folderYear) {
dir = folders[i];
found = true;
break;
}
}
if (!found) {
dir = dir.createFolder(folderYear);
folders = dir.getFolders();
}
else found = false;
for (var i = 0; i < folders.length; i++)
if (folders[i].getName() == folderMonth) {
toReturn = folders[i].getId();
found = true;
break;
}
if (!found) toReturn = dir.createFolder(folderMonth).getId();
return DocsList.getFolderById(toReturn);
}
Then your code would be as follows:
var SSID = '<redacted>'
var folder = returnFolder(OBJECT_PARENT_FOLDER, folderYear, folderMonth);
var backup = DocsList.getFileById(SSID).makeCopy(fname);
backup.addToFolder(folder); //This line will move the file to the appropriate folder
backup.removeFromFolder(DocsList.getRootFolder()); //This line is needed to remove the File from the Root
You will need to match the code to your needs, but I hope this helps.