Using Google Drive API and Spreadsheet Api from c#.
How can I add folders and files (speedsheets) to a folder and make sure that they are always shared and visible to the human user?
I know about Google.Apis.Drive.Permission, but it has a daily quota, and send out an email, it does not seem like the right solution.
I would prefer to just be able to work inside a folder, and then all stuff should always be visible to both the API and the human user.
Any tips:)
You would use the Google drive api to create a file and then add permissions on that file to share it with a user. This is how you do it and there really isnt a better way to do it. In order for a user to access it you must grant them permissions on it.
/// <summary>
/// Creates a permission for a file or Team Drive.
/// Documentation https://developers.google.com/drive/v3/reference/permissions/create
/// Generation Note: This does not always build corectly. Google needs to standardise things I need to figuer out which ones are wrong.
/// </summary>
/// <param name="service">Authenticated Drive service.</param>
/// <param name="fileId">The ID of the file or Team Drive.</param>
/// <param name="body">A valid Drive v3 body.</param>
/// <param name="optional">Optional paramaters.</param>
/// <returns>PermissionResponse</returns>
public static Permission Create(DriveService service, string fileId, Permission body, PermissionsCreateOptionalParms optional = null)
{
try
{
// Initial validation.
if (service == null)
throw new ArgumentNullException("service");
if (body == null)
throw new ArgumentNullException("body");
if (fileId == null)
throw new ArgumentNullException(fileId);
// Building the initial request.
var request = service.Permissions.Create(body, fileId);
// Applying optional parameters to the request.
request = (PermissionsResource.CreateRequest)SampleHelpers.ApplyOptionalParms(request, optional);
// Requesting data.
return request.Execute();
}
catch (Exception ex)
{
throw new Exception("Request Permissions.Create failed.", ex);
}
}
Code ripped from my sample project PermissionsSample.cs
If you are having an issue with the quota then you can try to go to google developer console and have your quota increased. Note: Quota is based upon the number of request not the type of request you are making.
Thanks for info about permissions with example!
I found out that if you just manually create a folder that is shared read/write with the google-drive-API-email-address, and then make sure everything is going on within this folder it behaves as wanted and all files are visible to both human and computer.
Best regards Anders
Related
just two questions :
I have a spreadsheet file editable by multiple user. but I'm afraid it will be annoying:
first question: does an onOpen script run when a user opens a file while another user is editing it? (because my onOpen script resets some cells, so it could be annoying).
So second question: is it possible to simply warn the user, when he tries to open the file, that it is already opened by another user and deny him access?
Thank you for your answers (I already search some answers whithout success)
Loïc
You can also consider using Properties Service which stores simple data in key-value pairs scoped to one script (Script Properties), one user of a script (User Properties), or one document (Document Properties).
I decided to use this Properties Service because your main concern is to prevent the execution of onOpen() which modifies some cells when there is someone already using the file. It is somewhat different with Lock Service, since lock service prevents simultaneous/concurrent execution of a code block.
Sample Implementation:
function onOpen(e){
var ui = SpreadsheetApp.getUi();
// Or DocumentApp or FormApp.
ui.createMenu('LockFile')
.addItem('Lock', 'lock')
.addItem('Unlock', 'unlock')
.addToUi();
//Check if file is locked. if not, execute the procedure
var scriptProperties = PropertiesService.getScriptProperties();
var keys = scriptProperties.getKeys();
if(keys.length==0){
//Lock the file
//lock(); empty Session.getActiveUser().getEmail() received when user opens the spreadsheet
showAlert("Please lock the file first");
SpreadsheetApp.getActiveSpreadsheet().getActiveSheet().getRange('A1').setValue(Session.getActiveUser().getEmail());
SpreadsheetApp.getActiveSpreadsheet().getActiveSheet().getRange('B1').setValue("Test");
}else{
showAlert("File is currenly locked by "+scriptProperties.getProperty('user'));
}
}
function lock(){
var scriptProperties = PropertiesService.getScriptProperties();
var userEmail;
var keys = scriptProperties.getKeys();
Logger.log(keys.length);
if(keys.length==0){
//Create new property with current user's email
userEmail = Session.getActiveUser().getEmail();
Logger.log(userEmail);
scriptProperties.setProperties({user: userEmail});
showAlert("File successfully locked by "+userEmail);
}else{
userEmail = scriptProperties.getProperty('user');
showAlert("File is currenly locked by "+userEmail);
}
}
function unlock(){
var scriptProperties = PropertiesService.getScriptProperties();
var userEmail = Session.getActiveUser().getEmail();
var keys = scriptProperties.getKeys();
Logger.log(keys.length);
Logger.log(scriptProperties.getProperty('user'));
if(keys.length>0){
Logger.log(scriptProperties.getProperty('user'));
if(scriptProperties.getProperty('user') == userEmail){
scriptProperties.deleteAllProperties();
showAlert("File successfully unlocked by "+userEmail);
}else{
showAlert("File is currenly locked by "+scriptProperties.getProperty('user'));
}
}else{
showAlert("File is not locked");
}
}
function reset(){
var scriptProperties = PropertiesService.getScriptProperties();
scriptProperties.deleteAllProperties();
showAlert("Lock has been reset");
}
function showAlert(message) {
var ui = SpreadsheetApp.getUi(); // Same variations.
Logger.log(message);
var result = ui.alert(
'File Lock',
message,
ui.ButtonSet.OK);
}
How it works?
When the file was opened, it will create a custom menu for file lock/unlock. Then check if there is an existing key in the script properties.
If key exist, it will not implement the procedure for setting cell values and will show an alert message. If key doesn't exist, then it will execute the procedure and will ask the current user to lock the file.
I cannot lock the file automatically during onOpen() because during file open, Session.getActiveUser().getEmail() returns an empty string. You can either ask the current user to lock the file using the custom menu or create a prompt dialog and ask for the current user's email as the key value.
You can lock/unlock the file using the custom menu. During file lock, it will check if there is no existing key before locking the file. While during the file unlock, it will check if the current user unlocking the file is the same person who locked the file before unlocking.
You can modify the alert messages based on your preference. This is just a guide on how you could use Properties Service in your goals.
Does an onOpen script run when a user opens a file while another user is editing it?
Yes it will run. According to the official documentation:
The onOpen(e) trigger runs automatically when a user opens a
spreadsheet, document, presentation, or form that they have permission
to edit.
Is it possible to simply warn the user, when he tries to open the file, that it is already opened by another user and deny him access?
No, but what you can do is to use LockService which:
Prevents concurrent access to sections of code. This can be useful
when you have multiple users or processes modifying a shared resource
and want to prevent collisions.
There are many resources on how to use LockService and the solutions depends on your specific use case, so I would advice you to look for these resources, do some research and then ask a specific question regarding that if you are not able to implement it yourself.
Resources:
How to understand LockService and implement it correctly?
I'm having an issue when running a function in Cloud Code. It is supposed to check the existence of an object and, if it does exist, create a new user:
Parse.Cloud.define("createUser", function(request, response) {
// Query the existing company by id
var query = new Parse.Query(Parse.Object.extend("Company"));
query.equalTo("objectId", request.params.company.existing.id);
query.find().then(function(result){
var user = new Parse.User();
user.set("username", request.params.username);
user.set("password", request.params.username);
user.set("email", request.params.email);
user.set("permissions", ["User"]);
var company = result[0];
user.signUp(null, {
success: function(user){
// Asign company ACL for User write permission
var cACL = company.getACL();
cACL.setWriteAccess(user.id, true);
company.setACL(cACL);
// Save company
company.save();
console.log(company);
// Establish user-company relationship
var cRelation = user.relation("associated");
cRelation.add(company);
// Save user
user.save();
console.log(user);
// Finish
response.success(user);
},
error: function(user, error){
response.error(JSON.stringify({code: -8000, message: "User creation failed"}));
}
});
}, function(error){
response.error(JSON.stringify({code: -8001, message: "Invalid company"}));
});
});
I first query Parse for the existence of said object. If it does exist I create a new user with the parameters received. In the completion block of the user creation I assign the proper ACLs (to the company object) and later on save them. That's when I encounter the first issue: the ACLs are not saved (checked in the dashboard). I console.log the company for debugging purposes and it shows the ACLs are correctly set. So I assume it must be a saving problem.
NOTE: The user is created, but whatever I try to do later doesn't work.
Later on I add this object to a relationship previously defined in the dashboard, but I have the same problem with that: the relationship does not come up in the dashboard, even though when I console.log the object it shows that the relationship was properly set.
I'm lost here. I don't understand why this isn't working and I've read tons of online documentation and still can't find the answer.
Okay, after a day of work I finally found out my problem. I had ACLs set everywhere and I had no privilege for saving the objects I was trying to save. So saving was indeed failing.
I should note that if you are having the same problem I did, you can easily solve it using the Master Key. To do so, you need to call Parse.Cloud.useMasterKey() before executing any requests that must be authenticated.
This only works in Cloud Code, and you should definitely know what you are doing when you use the Master Key because it basically gives read and write privileges to anyone, everywhere, for everything. So make sure your logic is flawless because you might get big security problems if it's not used wisely. As Uncle Ben said: With great power comes great responsibility.
Hope this helps someone.
I need to get certain details for a user by his AD login ID.
Remember I just don't want to look into that user's contacts only. I want to look in global list and find the details (Similar details is shown when you double click the name of the person in the email message from, to, cc )
I found lot of links out there but they don't show any example for global search of user.
I tried to do something similar shown in this link
http://msdn.microsoft.com/en-us/library/jj220498(v=exchg.80).aspx
however it just within my own contacts.
Can anybody show a simple example or link for the same?
I found that ResolveName method does the trick. I can query by user's full name. I am just posting a method. I assume 'service' is already instantiated using proper domain/url/credentials
public Contact GetContactInfo(string sFullName)
{
Contact contact = null;
try
{
NameResolutionCollection allContacts = service.ResolveName(sFullName, ResolveNameSearchLocation.DirectoryOnly, true);
if (allContacts.Any())
{
contact = allContacts[0].Contact;
}
}
catch (Exception ex)
{
LogHelper.Error("Error in GetContactInfo(): ", ex);
//throw;
}
return contact;
}
Have you tried the ResolveName method?
http://msdn.microsoft.com/en-us/library/microsoft.exchange.webservices.data.exchangeservice.resolvename%28v=exchg.80%29.aspx
You can search the contacts folder and/or global address list with it. Make sure you set the boolean value to return the Contact with it.
I was looking for user's details and GetPersona is the operation.
Sharing with the concern that it may help others who are digging google & Microsoft to get user's information.
GetPersona operation
The GetPersona operation returns a set of properties that are associated with a persona.
Do you know if there is a way to generate comment with hotkey in visual ?
Here is an exemple :
When I press "ctrl + h" (for exemple) I would like the following lines are generated :
/*
* Creation: 2012-07-24 by user
* Modification: 2012-07-25 by user
*/
Int myFunction()
{
}
Thanks !
If you have Generate XML documentation file ticked under the project options (this is not an IDE or solution option, but a project one) then you can type /// directly above a function.
This will create the following example comment, which you can fill in the details between the XML tags...
/// <summary>
///
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
public Lookup FindById(int id)
{
The information you put in between the tags will then be used by Visual Studio to populate intelisense information when using the function in other parts of your code. It can also be used with properties, enum, etc.
It's not exactly what you're asking for (including usernames, dates, etc), but I believe it could be an option for you.
(Note, this is also available for VB.NET, and uses ''' instead)
I'm trying to set up a system that displays calendar items from a resource calendar on a web page using Exchange 2007 and EWS (Exchange Web Services).
I've managed to get access to the calendars of the resources, but when getting the items in the calendars, the subject of each calendar item is not the original subject used when someone created the meeting request and invited the resource, but rather the Username on the account that created the the meeting request eg. if I do something like:
List<CalendarItemType> items =
Calendar.GetCalendarItems("mr1#litwareinc.com",
Calendar.GetNextWeekView(),
binding);
if (items.Count > 0)
{
Console.WriteLine(string.Format("Calendar opened - fetched {0} items",
items.Count));
Console.WriteLine("===================================");
foreach (var item in items)
{
Console.WriteLine();
Console.WriteLine(item.Subject);
Console.WriteLine("----------------------------------------");
Console.WriteLine("\tOrganizer: " + item.Organizer.Item.Name);
Console.WriteLine();
Console.WriteLine("\tStart: " + item.Start.ToString("dd-MM-yyyy HH:mm"));
Console.WriteLine("\tSlut: " + item.Start.ToString("dd-MM-yyyy HH:mm"));
}
}
Where Calendar.GetCalendarItems, is a method that fetches the calendar items of the resource denoted by the first argument, the Calendar.GetNextWeekView() is a static method that creates a CalendarView spanning the next week from today's date, and the binding is set up to use an account with delegate access to the resource mailbox.
The item.Subject comes out as Administrator if the Administrator account was used to book the resource.
Does anyone know how to remedy this - do I need to make some kind of special property access, or fetch another type of item or what?
Regards
Jesper Hauge
Figured this one out - when I started looking outside the code.
Answer lies in resource configuration rather than access code.
If you want to have the subject of the meeting reflect the original subject. Make sure the resource has set the setting properties DeleteSubject and AddOrganizerToSubject to false. It can be achieved with the following shell command:
Set-MailboxCalendarSettings resourcename -DeleteSubject 0 -AddOrganizerToSubject 0
Regards
Jesper Hauge