Get link to previous versions of a file - view

I am looking for a script like this to pull out the previous versions along with a link to them to put in a google sheet. The goal is to display previous versions and allow others to click on a link to view the previous versions by name.
I found this function here but I am not sure which link in the logs to use to retrieve the old version and display them
function myF() {
// Get the file id
var fileId="FileID"
// Get an array with all the revisions
var revisions = Drive.Revisions.list(fileId);
// Iterate through each revision
for(i=0; i<revisions.items.length;i++){
// Get each revision
var revision = revisions.items[i];
// Log each revision with all its details
Logger.log(revision);
}
}
Which part of the log text shows the link to the previous version(s)

You could do something like this
const fid = '1AnneKZdGOXrJUX1-S4JKGrmVzezg416n_2vFl_yye7w';
const revisions = Drive.Revisions.list(fid);
let lastRevTime = 0;
let lastRev = null;
for (let i = 0; i < revisions.items?.length; i++) {
const revision = revisions.items[i];
const revTime = new Date(revision.modifiedDate).getTime();
if (revTime > lastRevTime) {
lastRev = revision;
}
}
console.log(lastRev.selfLink);
console.log(lastRev.exportLinks);
selflink is a link for file revision, and exportLinks is an array with links for downloading the file revision in specific format (for example pdf, docx, zip etc).

Related

GAS sends multiple slack notifications

I'd like to send notifications to slack using Google Apps Scripts.
It's supposed to happen when any change is made on Gsheet but sometimes the program sends multiple notifications even after only a single change.
I guess my code has any fault or the triggers cause any problem - because I apply this function to more than one sheet.
But it doesn't happen "always", so is it an error caused by GAS specification?
My code is as below. Any idea or advice will be appreciated.
function slackNotification(sheetname,slack_channel){
Logger.log(sheetname)
var mySheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(sheetname);
var myActiveRange = mySheet.getActiveRange();
var lastRow = mySheet.getLastRow();
var pdfColumn = PDF_column_n
var checkColumn = slack_column_n
Logger.log(lastRow)
var unslacked = [];
for (var i=1; i <= lastRow; i++){
var cell = mySheet.getRange(i, checkColumn);
if(cell.isBlank()){
Logger.log(i + "is not yet notified");
unslacked.push(i);
}
}
Logger.log("unslcked yet"+unslacked)
for (var i=unslacked[0]; i <= unslacked[unslacked.length - 1]; i++){
var pdfCell = mySheet.getRange(extention_column+i)
var checkSlack = mySheet.getRange(slack_column+i)
Logger.log(checkSlack.isBlank())
Logger.log(pdfCell.getValue())
if (pdfCell.getValue() == "pdf" && checkSlack.isBlank() == true)
{
var requestId = mySheet.getRange(request_column+i).getValue();
var clientId = mySheet.getRange(client_column+i).getValue();
var claimId = mySheet.getRange(claim_column+i).getValue();
var groupId = mySheet.getRange(group_column+i).getValue();
Logger.log(groupId)
if (branches.includes(groupId)){
var message = "\nrequestId:" + requestId + "\nclientId:" + clientId + "\nclaimId:" + claimId
var postUrl = slack_channel
var username = 'botbot';
var icon = ':hatching_chick:';
var jsonData =
{
"username" : username,
"icon_emoji": icon,
"text" : message,
link_names: 1
};
var payload = JSON.stringify(jsonData);
var options =
{
"method" : "post",
"contentType" : "application/json",
"payload" : payload
};
UrlFetchApp.fetch(postUrl, options);
checkSlack.setValue(true);
}
}
}
}
Apps Script triggers are bound project-based, not script filed based
If you have multiple functions with the same name in your project - there is no way to know to which one the trigger becomes bound.
Most likely your 5 onChange triggers are all bound to the same function and thus the function is executed multiple times (sending multiple Slack notifications as a result)
It does not make a difference if the functions are located within the same script file or within different script files within the same project
Splitting up your code into different code files is only for visibility/structure purposes, for the trigger the functions are still located all within the same project
Solution
Remove from your project all existing triggers that are bound to functions with not unique names
Give to all functions in your project (not just script file) unique names and save all script files
Install new triggers either programmatically or manually
Now you can be sure that each trigger is bound to a different function
Mind that if the content of your functions is the same (e.g. containing request to send Slack notifications), this content will be executed the same amount of times as the number of respective triggers in your project, so if the content of your functions is identical, you actually need to keep only one function bound to one trigger and remove all the functions of identic content.

CloudBlobDirectory' does not contain a definition for 'ListBlobs' and no accessible extension method 'ListBlobs' in .net core 3.1 upgrade

I am upgrading a .net45 app to .net core 3.1 and I have a piece of code there like below.
private void GetContainerDirectories(IEnumerable<IListBlobItem> blobList)
{
// First list all the actual FILES within
// the current blob list. No recursion needed:
foreach (var item in blobList.Where
((blobItem, type) => blobItem is CloudBlockBlob))
{
var blobFile = item as CloudBlockBlob;
sb.Add(new Tree { Name = blobFile.Name, Id = blobFile.Name, ParentId = blobFile.Parent.Prefix, Title = Path.GetFileName(blobFile.Name), IsDirectory = false });
}
// List all additional subdirectories
// in the current directory, and call recursively:
foreach (var item in blobList.Where
((blobItem, type) => blobItem is CloudBlobDirectory))
{
var directory = item as CloudBlobDirectory;
sb.Add(new Tree { Name = directory.Prefix, Id = directory.Prefix, ParentId = directory.Parent.Prefix, Title = new DirectoryInfo(directory.Prefix).Name, IsDirectory = true });
// Call this method recursively to retrieve subdirectories within the current:
GetContainerDirectories(directory.ListBlobs()); ***////////Here i am getting error***
}
}
In the last line [ GetContainerDirectories(directory.ListBlobs()) ], I am getting error for ListBlobs and I am not able to find any useful solution for this. The error like this -
'CloudBlobDirectory' does not contain a definition for 'ListBlobs' and no accessible extension method 'ListBlobs' accepting a first argument of type 'CloudBlobDirectory' could be found (are you missing a using directive or an assembly reference?)
Has anyone any idea how to fix this ? Many thanks in advance :)
The WindowsAzure.Storage SDK you are using is too old, .net core does not support the synchronous methods under this SDK, and the ListBlobs method is a synchronous method.
I suggest you use the latest SDK instead:
https://www.nuget.org/packages/Azure.Storage.Blobs/12.8.0
If you don't want to use Azure.Storage.Blobs SDK, you can use ListBlobsSegmentedAsync method under WindowsAzure.Storage SDK
Update:
You can use the code below to instead of your original code:
var blobs = directory.ListBlobsSegmentedAsync(false, BlobListingDetails.Metadata, 100, null, null, null).Result.Results;
GetContainerDirectories(blobs);

How can i get the picture filename from a google drive url?

I have a lot of picture links in my Google Sheet:
https://drive.google.com/file/d/1BDj24_6uz-ZQlGWLy0o1_DES6tJI3NMu/view?usp=sharing
I want to get the filename behind the link. When i open the link and save the image the image filename is: "FILENAME.jpg" or something similar
I want to get this information in Sheets.
How can i do that?
An Apps Script Solution
Thanks to #Tanaike for obtaining clarifications in the comments.
First enter your links in the format shown below in a Google Sheet.
The name of the titles is not important, but this particular script starts getting names from the 2nd row.
If you don't know how to create a script, there are tutorials here offered by Google. You should create the script from the same Sheet. That is, from the menu in the Sheet:
In the script editor, copy and paste the following code:
function getNames() {
var activeRange = SpreadsheetApp.getActiveSheet().getDataRange();
var height = activeRange.getHeight();
var links = SpreadsheetApp.getActiveSheet()
.getRange("A2:A" + height)
.getValues();
var nameValues = [];
links.forEach((row) => {
try {
var link = row[0];
var fileID = getIdFromLink(link);
var name = DriveApp.getFileById(fileID).getName();
nameValues.push([name]);
} catch (e) {
nameValues.push(["NO NAME FOUND"]);
}
});
var nameRange = SpreadsheetApp.getActiveSheet().getRange("B2:B" + height);
nameRange.setValues(nameValues);
}
function getIdFromLink(link) {
var regex = new RegExp(
/(?<=https:\/\/drive\.google\.com\/file\/d\/)(.+)(?=\/)/
);
return regex.exec(link)[0];
}
Run the getNames function. The first time it will ask you to grant some permissions, grant them. Then it should fill in the files names.
References
Apps Script Overview
Spreadsheet service in Apps Script
Drive service in Apps Script

How to list all children in Google Drive's appfolder and read file contents with Xamarin / c#?

I'm trying to work with text files in the apps folder.
Here's my GoogleApiClient constructor:
googleApiClient = new GoogleApiClient.Builder(this)
.AddApi(DriveClass.API)
.AddScope(DriveClass.ScopeFile)
.AddScope(DriveClass.ScopeAppfolder)
.UseDefaultAccount()
.AddConnectionCallbacks(this)
.EnableAutoManage(this, this)
.Build();
I'm connecting with:
googleApiClient.Connect()
And after:
OnConnected()
I need to list all files inside the app folder. Here's what I got so far:
IDriveFolder appFolder = DriveClass.DriveApi.GetAppFolder(googleApiClient);
IDriveApiMetadataBufferResult result = await appFolder.ListChildrenAsync(googleApiClient);
Which is giving me the files metadata.
But after that, I don't know how to read them, edit them or save new files. They are text files created with my app's previous version (native).
I'm following the google docs for drive but the Xamarin API is a lot different and has no docs or examples. Here's the API I'm using: https://components.xamarin.com/view/googleplayservices-drive
Edit:
Here is an example to read file contents from the guide:
DriveFile file = ...
file.open(mGoogleApiClient, DriveFile.MODE_READ_ONLY, null)
.setResultCallback(contentsOpenedCallback);
First I can't find anywhere in the guide what "DriveFile file = ..." means. How do I get this instance? DriveFile seems to be a static class in this API.
I tried:
IDriveFile file = DriveClass.DriveApi.GetFile(googleApiClient, metadata.DriveId);
This has two problems, first it complains that GetFile is deprecated but doesn't say how to do it properly. Second, the file doesn't have an "open" method.
Any help is appreciated.
The Xamarin binding library wraps the Java Drive library (https://developers.google.com/drive/), so all the guides/examples for the Android-based Drive API work if you keep in mind the Binding's Java to C# transformations:
get/set methods -> properties
fields -> properties
listeners -> events
static nested class -> nested class
inner class -> nested class with an instance constructor
So you can list the AppFolder's directory and files by recursively using the Metadata when the drive item is a folder.
Get Directory/File Tree Example:
await Task.Run(() =>
{
async void GetFolderMetaData(IDriveFolder folder, int depth)
{
var folderMetaData = await folder.ListChildrenAsync(_googleApiClient);
foreach (var driveItem in folderMetaData.MetadataBuffer)
{
Log.Debug(TAG, $"{(driveItem.IsFolder ? "(D)" : "(F)")}:{"".PadLeft(depth, '.')}{driveItem.Title}");
if (driveItem.IsFolder)
GetFolderMetaData(driveItem.DriveId.AsDriveFolder(), depth + 1);
}
}
GetFolderMetaData(DriveClass.DriveApi.GetAppFolder(_googleApiClient), 0);
});
Output:
[SushiHangover.FlightAvionics] (D):AppDataFolder
[SushiHangover.FlightAvionics] (F):.FlightInstrumentationData1.json
[SushiHangover.FlightAvionics] (F):.FlightInstrumentationData2.json
[SushiHangover.FlightAvionics] (F):.FlightInstrumentationData3.json
[SushiHangover.FlightAvionics] (F):AppConfiguration.json
Write a (Text) File Example:
using (var contentResults = await DriveClass.DriveApi.NewDriveContentsAsync(_googleApiClient))
using (var writer = new OutputStreamWriter(contentResults.DriveContents.OutputStream))
using (var changeSet = new MetadataChangeSet.Builder()
.SetTitle("AppConfiguration.txt")
.SetMimeType("text/plain")
.Build())
{
writer.Write("StackOverflow Rocks\n");
writer.Write("StackOverflow Rocks\n");
writer.Close();
await DriveClass.DriveApi.GetAppFolder(_googleApiClient).CreateFileAsync(_googleApiClient, changeSet, contentResults.DriveContents);
}
Note: Substitute a IDriveFolder for DriveClass.DriveApi.GetAppFolder to save a file in a subfolder of the AppFolder.
Read a (text) File Example:
Note: driveItem in the following example is an existing text/plain-based MetaData object that is found by recursing through the Drive contents (see Get Directory/File list above) or via creating a query (Query.Builder) and executing it via DriveClass.DriveApi.QueryAsync.
var fileContexts = new StringBuilder();
using (var results = await driveItem.DriveId.AsDriveFile().OpenAsync(_googleApiClient, DriveFile.ModeReadOnly, null))
using (var inputStream = results.DriveContents.InputStream)
using (var streamReader = new StreamReader(inputStream))
{
while (streamReader.Peek() >= 0)
fileContexts.Append(await streamReader.ReadLineAsync());
}
Log.Debug(TAG, fileContexts.ToString());

Unable to set firefox bookmark description for in Add-On Kit API

I found the nsINavBookmarksService, however since Firefox 3, it does not seem to have any API methods for getting/setting the Bookmark description. (API doc)
I've seen other Add-Ons modify the description as a method of synchronized data storage (which is exactly what I'm trying to do). I'm guessing perhaps the description is a non-gecko standard, and that's why it is not directly supported, but then there must be a completely different interface for manipulating Bookmarks that I haven't discovered.
Can anyone help with this newbie problem?
Starting with Firefox 3 the bookmarks have been merged into the Places database containing all of your browsing history. So to get the bookmarks you do a history query, like this (first line is specific to the Add-on SDK which you appear to be using):
var {Cc, Ci} = require("chrome");
var historyService = Cc["#mozilla.org/browser/nav-history-service;1"]
.getService(Ci.nsINavHistoryService);
var options = historyService.getNewQueryOptions();
var query = historyService.getNewQuery();
query.onlyBookmarked = true;
var result = historyService.executeQuery(query, options);
result.root.containerOpen = true;
for (var i = 0; i < result.root.childCount; i++)
{
var node = result.root.getChild(i);
console.log(node.title);
}
That's mostly identical to the code example here. Your problem is of course that nsINavHistoryResultNode has no way of storing data like a description. You can set annotations however, see Using the Places annotation service. So if you already have a node variable for your bookmark:
var annotationName = "my.extension.example.com/bookmarkDescription";
var ioService = Cc["#mozilla.org/network/io-service;1"]
.getService(Ci.nsIIOService);
var uri = ioService.newURI(node.uri, null, null);
var annotationService = Cc["#mozilla.org/browser/annotation-service;1"]
.getService(Ci.nsIAnnotationService);
annotationService.setPageAnnotation(uri, annotationName,
"Some description", 0, Ci.nsIAnnotationService.EXPIRE_NEVER);
For reference: nsIAnnotationService.setPageAnnotation()

Resources