We have been using google drive for a while now to select an existing doc, owned by one of our employees, copy it and then merge data into placeholder fields to then download a pdf version of the doc. It's been working fine except now other employees want access to the created docs. So when we give them a link, the original employee has to give them access. We want to move the docs to a shared drive where all employees can see anything in the shared drive. From what I could find in google on this, it looks like we need to set the SupportsAllDrives property to true on the request. However, I can't find that property on any of the objects that we're creating when copying the base document. Because of this I keep getting a 404 from google when trying to copy the file. Can anyone suggest how to get this working?
var secrets = new ClientSecrets
{
ClientId = GoogleCredentials.accesskey,
ClientSecret = GoogleCredentials.secretkey
};
var refreshToken = _credService.GetRefreshToken();
if (string.IsNullOrEmpty(refreshToken))
{
throw new Exception("Missing google refresh token for google doc processor task.");
}
var token = new TokenResponse { RefreshToken = refreshToken };
var credentials = new UserCredential(new GoogleAuthorizationCodeFlow(
new GoogleAuthorizationCodeFlow.Initializer
{
ClientSecrets = secrets
}),
"user",
token);
var docService = new DocsService(new BaseClientService.Initializer
{
HttpClientInitializer = credentials,
ApplicationName = "Contract Merge"
});
var driveService = new DriveService(new BaseClientService.Initializer
{
HttpClientInitializer = credentials,
ApplicationName = "Contract Merge"
});
var newTitle = "Agreement for " + contract.FirstName + " " + contract.LastName + " " + DateTime.Now.Month.ToString() + "-" + DateTime.Now.Day.ToString() + "-" + DateTime.Now.Year.ToString();
var newFile = new google.Apis.Drive.v2.Data.File { Title = newTitle };
var documentCopyFile = driveService.Files.Copy(newFile, GoogleConstants.TemplateDocId).Execute();
As you can see in the C# library reference, an optional query parameter like supportsAllDrives (see query parameters on the API docs) is handled by a property of the CopyRequest class.
Therefore, after building the CopyRequest, but before executing it, you have to set the property SupportsAllDrives to true, as shown here:
FilesResource.CopyRequest copyRequest = driveService.Files.Copy(newFile, GoogleConstants.TemplateDocId);
copyRequest.SupportsAllDrives = true;
copyRequest.Execute();
Reference:
Class FilesResource.CopyRequest
Related
Hello Office / SharePoint Developers,
I am working on a project based on the Office Developer Patterns and Practices Sample where a console application accesses a WebAPI which then access SharePoint Online as the logged in user: The sample is here: https://github.com/SharePoint/PnP/tree/master/Samples/AzureAD.WebAPI.SPOnline
Question:
When I attempt to upload a file to the document library, I get an error 401 "The remote server returned an error: (401) Unauthorized".
The file read options such as listing the documents and querying for documents works fine.
The user credentials I supply are of a user that is the site collection admin, owner, and global admin on the tenant.
I get an access token from SharePoint online based on the token I get in the native client.
public string GetAccessToken(string accessToken)
{
string clientID = _clientId;
string clientSecret = _clientSecret;
var appCred = new ClientCredential(clientID, clientSecret);
var authContext = new Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContext("https://login.windows.net/common");
AuthenticationResult authResult = authContext.AcquireToken(new Uri(_spoUrl).GetLeftPart(UriPartial.Authority), appCred, new UserAssertion(accessToken));
return authResult.AccessToken;
}
This is the CSOM that uploads the file. I know it works as I can paste it into a console app and using (SharePointOnlineCredentails) it works fine.
string newToken = _tokenSvc.GetAccessToken(accessToken);
using (ClientContext cli = new ClientContext(_spoUrl))
{
cli.ExecutingWebRequest += (s, e) => e.WebRequestExecutor.WebRequest.Headers.Add("Authorization", "Bearer " + newToken);
cli.AuthenticationMode = ClientAuthenticationMode.Default;
using (var fs = new FileStream(#"c:\test.txt", FileMode.Open))
{
var fi = new FileInfo("test.txt");
var list = cli.Web.Lists.GetByTitle("documents");
cli.Load(list.RootFolder);
cli.ExecuteQuery();
var fileUrl = String.Format("{0}/{1}", list.RootFolder.ServerRelativeUrl, fi.Name);
Microsoft.SharePoint.Client.File.SaveBinaryDirect(cli, fileUrl, fs, true);
Web web = cli.Web;
Microsoft.SharePoint.Client.File newFile = web.GetFileByServerRelativeUrl(fileUrl);
cli.Load(newFile);
cli.ExecuteQuery();
ListItem item = newFile.ListItemAllFields;
item["CRUID"] = "CRU_1337";
item.Update();
cli.ExecuteQuery();
}
}...
TLDR: I get 401 on file upload. Reads work. I am using CSOM with an access token that is supposed to be a webAPI on behalf of the logged in user.
I look forward to hearing your advice!
Chris
I am not sure whether we could upload/download files from SP using access tokens with CSOM now , see discussion here two years ago . But we could use sharepoint online rest api to upload files to sharepoint online , i tried below code and it works fine in the code sample AzureAD.WebAPI.SPOnline :
string sharePointUrl = ConfigurationManager.AppSettings["SharePointURL"];
string newToken = GetSharePointAccessToken(sharePointUrl, this.Request.Headers.Authorization.Parameter);
using (ClientContext cli = new ClientContext(sharePointUrl))
{
cli.AuthenticationMode = ClientAuthenticationMode.Default;
cli.ExecutingWebRequest += (s, e) => e.WebRequestExecutor.WebRequest.Headers.Add("Authorization", "Bearer " + newToken);
cli.AuthenticationMode = ClientAuthenticationMode.Default;
byte[] bytefile = System.IO.File.ReadAllBytes(#"e:\log.txt");
HttpWebRequest endpointRequest = (HttpWebRequest)HttpWebRequest.Create("https://xxx.sharepoint.com/sites/xxx/" + "/_api/web/GetFolderByServerRelativeUrl('Shared%20Documents')/Files/add(url='log.txt',overwrite=true)");
endpointRequest.Method = "POST";
endpointRequest.Headers.Add("binaryStringRequestBody", "true");
endpointRequest.Headers.Add("Authorization", "Bearer " + newToken);
endpointRequest.GetRequestStream().Write(bytefile, 0, bytefile.Length);
HttpWebResponse endpointresponse = (HttpWebResponse)endpointRequest.GetResponse();
}
The code below is ended up being the solution to my question:
/* Beginning CSOM Magic */
using (ClientContext cli = new ClientContext(_spoUrl))
{
/* Adding authorization header */
cli.ExecutingWebRequest += (s, e) => e.WebRequestExecutor.WebRequest.Headers.Add("Authorization", "Bearer " + newToken);
cli.AuthenticationMode = ClientAuthenticationMode.Default;
//Get Document List
List documentsList = cli.Web.Lists.GetByTitle(_libraryName);
var fileCreationInformation = new FileCreationInformation();
//Assign to content byte[] i.e. documentStream
var data = System.IO.File.ReadAllBytes(#"c:\test.txt");
fileCreationInformation.Content = data;
//Allow owerwrite of document
fileCreationInformation.Overwrite = true;
//var siteURL = _spoUrl;
var documentListURL = "shared documents";
//var documentName = "/test.txt";
//Upload URL
fileCreationInformation.Url = string.Concat(_spoUrl,"/",documentListURL,"/",documentName);
Microsoft.SharePoint.Client.File uploadFile = documentsList.RootFolder.Files.Add(
fileCreationInformation);
//Update the metadata for a field having name "DocType"
uploadFile.ListItemAllFields["CRUID"] = cruId;
uploadFile.ListItemAllFields.Update();
cli.ExecuteQuery();
}
I am trying to upload an image on google drive using webapi. I copied the following chunk from Google drive doc but I am getting an error.
Here is the code:
var clientSecret = ConfigurationManager.AppSettings["GoogleDriveClientSecret"];
var credential = GoogleWebAuthorizationBroker.AuthorizeAsync(new ClientSecrets { ClientId = clientId, ClientSecret = clientSecret },
scopes, Environment.UserName, CancellationToken.None).Result;
var service = new DriveService(new BaseClientService.Initializer() { HttpClientInitializer = credential });
var folderId = "0B2bBiMQICgHCMlp6OUxuSHNaZFU";
var fileMetadata = new File()
{
Name = "photo.jpg",
Parents = new List<string>
{
folderId
}
};
FilesResource.CreateMediaUpload request;
using (var stream = new System.IO.FileStream("files/photo.jpg",
System.IO.FileMode.Open))
{
request = service.Files.Create(
fileMetadata, stream, "image/jpeg");
request.Fields = "id";
request.Upload();
}
var file = request.ResponseBody;
Now I am getting 2 errors in this code. First "Cannot resolve symbol Upload" at request.Upload() and second "Cannot resolve symbol ResponseBody" at request.ResponseBody
Any help?
You may refer with this related thread. The adding the attribute Inherits="Library.Account.RootVerifyUsers". Based from this reference, Visual Studio can often get confused about things like this. It is recommended to close Visual Studio and reopen it. Also, closing all open instances of VS may be required.
I'm trying to add new spreadsheet if it not exists with GData Spreadsheet API for .NET but it gives me following exception:
Can not update a read-only feed
Here's my code:
var service = new SpreadsheetsService("<my-app>");
service.setUserCredentials("<login>", "<password>");
// Instantiate a SpreadsheetQuery object to retrieve spreadsheets.
SpreadsheetQuery query = new SpreadsheetQuery();
var title = "test";
query.Title = title;
// Make a request to the API and get all spreadsheets.
SpreadsheetFeed feed = service.Query(query);
if (!feed.Entries.Any())
{
var worksheet = new WorksheetEntry(20, 20, title);
service.Insert(feed, worksheet);
}
Through Fiddler I see that I'm doing request to:
GET /feeds/spreadsheets/private/full?title=test
and it goes fine, but I don't see any requests for updating data. I suppose that I should change somehow SpreadsheetQuery to make it capable to write data, but I can't find how.
It's me being inattentive because google documentation on Spreadsheet API says:
It is possible to create a new spreadsheet by uploading a spreadsheet
file via the Google Drive API. The Spreadsheets API does not currently
provide a way to delete a spreadsheet, but this is also provided in
the Google Drive API. For testing purposes, you may create a
spreadsheet manually or upload one.
So I basically installed GoogleDrive API with Nuget. And then added following method for adding file:
private static void AddFile(string title)
{
var clientID = "put here a clientID";
var clientSecret = "put here a clientSecret";
UserCredential credential = GoogleWebAuthorizationBroker.AuthorizeAsync(
new ClientSecrets
{
ClientId = clientID,
ClientSecret = clientSecret,
},
new[] { DriveService.Scope.Drive },
"here goes your account",
CancellationToken.None).Result;
// Create the service.
var service = new DriveService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = "Drive API Sample",
});
var body = new Google.Apis.Drive.v2.Data.File();
body.Title = title;
//body.Description = "A test document";
body.MimeType = "application/vnd.google-apps.spreadsheet";
service.Files.Insert(body).Execute();
}
When I run code above at the first time - I received an exception that said
Could not load file or assembly
'Microsoft.Threading.Tasks.Extensions.Desktop, Version=1.0.16.0
I run these in Package Manager Console:
Uninstall-Package Microsoft.Bcl.Async -Force
Install-Package Microsoft.Bcl.Async
and it worked. Hope it would help somebody who will stumble over the same issue.
I had a Google Apps Script to take appointments from my Google Calendar, copy them into a Google Sheet, convert it to XLS and email it. It was working fine until this week.
The initial problem was a 302 error, probably caused by the new version of Sheets. This has been discussed here: Export (or print) with a google script new version of google spreadsheets to pdf file, using pdf options
I got the new location of the file by muting the HTTP exceptions and adjusting the URL accordingly. I also updated the OAuth scope to https://docs.google.com/feeds/ as suggested.
The program is failing with an "OAuth error" message. When muteHttpExceptions is set to true, the message is "Failed to authenticate to service: google".
I guess this is a scope problem but I really can't see what I've done wrong. Naturally, I've tried a few other possibilities without luck.
I've included the code below. Commented code is the instruction that worked until this week.
function getSSAsExcel(ssID)
{
var format = "xls";
//var scope = "https://spreadsheets.google.com/feeds/";
var scope = "https://docs.google.com/feeds/";
var oauthConfig = UrlFetchApp.addOAuthService("google");
oauthConfig.setAccessTokenUrl("https://www.google.com/accounts/OAuthGetAccessToken");
oauthConfig.setRequestTokenUrl("https://www.google.com/accounts/OAuthGetRequestToken?scope=" + scope);
oauthConfig.setAuthorizationUrl("https://www.google.com/accounts/OAuthAuthorizeToken");
oauthConfig.setConsumerKey("anonymous");
oauthConfig.setConsumerSecret("anonymous");
var requestData = {
//"muteHttpExceptions": true,
"method": "GET",
"oAuthServiceName": "google",
"oAuthUseToken": "always"
};
//var url = "https://spreadsheets.google.com/feeds/download/spreadsheets/Export?key=" + ssID
var url = "https://docs.google.com/spreadsheets/d/" + ssID
+ "/feeds/download/spreadsheets/Export?"
+ "&size=A4" + "&portrait=true" +"&fitw=true" + "&exportFormat=" + format;
var result = UrlFetchApp.fetch(url , requestData);
var contents = result.getContent();
return contents;
}
Thanks for your help!
Instead of using OAuthConfig (which must be auth'ed in the Script Editor) you can pass an OAuth2 token instead, retrievable via ScriptApp.getOAuthToken().
The code snippet below uses the Advanced Drive service to get the export URL, but if you hand construct the URL you'll need to ensure that the Drive scope is still requested by your script (simply include a call to DriveApp.getRootFolder() somewhere in your script code).
function exportAsExcel(spreadsheetId) {
var file = Drive.Files.get(spreadsheetId);
var url = file.exportLinks[MimeType.MICROSOFT_EXCEL];
var token = ScriptApp.getOAuthToken();
var response = UrlFetchApp.fetch(url, {
headers: {
'Authorization': 'Bearer ' + token
}
});
return response.getBlob();
}
I've created a project in Big Query and within API Access pane, I've created Service Account so that i can make Big Query API accessible through windows application on behalf of user interaction. Since i am new to using and accessing Google API, I want to know the basic steps so that i can access Big Query via Windows Service.
There are some .NET code samples here using web-based OAuth flows:
Google BigQuery with .NET documentation/ samples
For an example from another dev using service accounts in .NET, see this:
Google OAuth2 Service Account Access Token Request gives 'Invalid Request' Response
These manually implement service accounts on top of the .NET library. The .NET library has recently added native support for service accounts, though I haven't yet found an official example.
Here's an unofficial sample working with the Analytics API. It should be directly analogous to using service accounts with BigQuery:
How do I use a Service Account to Access the Google Analytics API V3 with .NET C#?
Finally here's the working code for Authentication process to Big Query API and retrieving records from Big Query table:-
I've created a class having a method of return type OAuth2Authenticator<AssertionFlowClient>.
internal class clsGetOAuth2Authentication
{
public OAuth2Authenticator<AssertionFlowClient> objGetOAuth2(string strPrivateFilePath, string strPrivateFilePassword, string strServiceAccEmailId,string strScope)
{
AuthorizationServerDescription objAuthServerDesc;
X509Certificate2 objKey;
AssertionFlowClient objClient;
OAuth2Authenticator<AssertionFlowClient> objAuth = null;
string ScopeUrl = "https://www.googleapis.com/auth/" + strScope;
string strSrvAccEmailId = strServiceAccEmailId;
string strKeyFile = strPrivateFilePath; //KeyFile: This is the physical path to the key file you downloaded when you created your Service Account.
string strKeyPassword = (strPrivateFilePassword != "") ? strPrivateFilePassword : "notasecret"; //key_pass: This is probably the password for all key files, but if you're given a different one, use that.
objAuthServerDesc = GoogleAuthenticationServer.Description; //objAuthServerDesc: Description of the server that will grant Authentiation.
objKey = new X509Certificate2(strKeyFile, strKeyPassword, X509KeyStorageFlags.Exportable); //objkey: Load up and decrypt the key.
objClient = new AssertionFlowClient(objAuthServerDesc, objKey) { ServiceAccountId = strSrvAccEmailId, Scope = ScopeUrl }; //objClient: Using the AssertionFlowClient, because we're logging in with our certificate.
objAuth = new OAuth2Authenticator<AssertionFlowClient>(objClient, AssertionFlowClient.GetState); //objAuth: Requesting Authentication.
return objAuth;
}
}
Now, another class calls the above method:-
clsGetOAuth2Authentication objOAuth2 = new clsGetOAuth2Authentication();
try
{
var objAuth = objOAuth2.objGetOAuth2(strKeyFile, strKeyPassword, strSrvAccEmailId, strScope); //Authentication data returned
objBQServ = new BigqueryService(objAuth); //Instantiate BigQueryService with credentials(objAuth) as its parameter
#region Retrieving Records:-
JobsResource j = objBQServ.Jobs;
QueryRequest qr = new QueryRequest();
qr.Query = strQuery;
DateTime dtmBegin = DateTime.UtcNow;
QueryResponse response = j.Query(qr, strProjId).Fetch();
DateTime dtmEnd = DateTime.UtcNow;
string strColHead = "";
foreach (var colHeaders in response.Schema.Fields)
{
strColHead += colHeaders.Name.ToString() + "\t";
}
Console.WriteLine(strColHead);
int intCount = 0;
foreach (TableRow row in response.Rows)
{
intCount += 1;
List<string> list = new List<string>();
foreach (var field in row.F)
{
list.Add(field.V);
}
Console.WriteLine(String.Join("\t", list));
}
TimeSpan tsElapsed = dtmEnd - dtmBegin;
Console.WriteLine("\n" + "Total no. of records:- " + intCount + ". Time taken:- " + tsElapsed);
#endregion
}
catch (Exception ex)
{
Console.WriteLine("Error Occured!" + "\n\n" + "Statement:- " + ex.Message.ToString() + "\n\n" + "Description:- " + ex.ToString() + "\n\n");
Console.WriteLine("\nPress enter to exit");
Console.ReadLine();
}