Downloading attachments with in sub folders using graph api and C# - c#

I'm creating a solution to download attachments from an outlook365 account. The inbox of this account has subfolders and folders within the subfolders. The structure looks like below.
I've been trying to download the attachments but I'm only able to get the attachment from the messages in the main inbox.
But I want to look into the subfolders. So by studying the Microsoft graph document (https://learn.microsoft.com/en-us/graph/api/attachment-get?view=graph-rest-1.0&tabs=http#http-request) I found the below HTTP URL request.
GET /me/mailFolders/{id}/childFolders/{id}/.../messages/{id}/attachments/{id}
GET /users/{id | userPrincipalName}/mailFolders/{id}/childFolders/{id}/messages/{id}/attachments/{id}
GET /me/mailFolders/{id}/childFolders/{id}/.../messages/{id}/attachments/{id}/$value
GET /users/{id | userPrincipalName}/mailFolders/{id}/childFolders/{id}/messages/{id}/attachments/{id}/$value
I'm pretty new to C# I'm having a hard time converting above mentioned URL method to C#. Below is the current code I use to grab the messages and expand the attachments.
public static async Task<IMailFolderMessagesCollectionPage> GetAttachmentToday()
{
var DateToDay = DateTime.Now.ToString("dd.MM.yyyy");
var SearchOption = new List<QueryOption>
{
new QueryOption("search", $"%22received:{DateToDay}%22")
};
try
{
var attachments = await graphClient.Me.MailFolders.Inbox.Messages
.Request(SearchOption)
.Top(5)
.Select(a => new
{
a.Subject,
a.HasAttachments
})
.Expand("attachments")
.GetAsync();
return attachments;
}
catch(ServiceException ex)
{
Console.WriteLine($"Error getting events: {ex.Message}");
return null;
}
}
I can't figure out how to implement the Microsoft document URL suggestion to work with var attachments = await graphClient.Me.MailFolders.Inbox.Messages. Hope someone can point me down the right path.

I figure this out and used the example HTML code given by Microsoft documents. Which is below.
GET /me/mailFolders/{id}/childFolders/{id}/.../messages/{id}/attachments/{id}
GET /users/{id | userPrincipalName}/mailFolders/{id}/childFolders/{id}/messages/{id}/attachments/{id}
GET /me/mailFolders/{id}/childFolders/{id}/.../messages/{id}/attachments/{id}/$value
GET /users/{id | userPrincipalName}/mailFolders/{id}/childFolders/{id}/messages/{id}/attachments/{id}/$value
So based on the above code I came up with the below C# code.
//To get the first supfolder and it's ID.
var FirstSubFolderIDs = await graphClient.Me.MailFolders["Inbox"].ChildFolders
.Request()
.Select(fid => new
{
fid.Id,
fid.DisplayName
})
.GetAsync();
foreach(var FirstSubFolderID in FirstSubFolderIDs)
{
if(FirstSubFolderID.Id != null)
{ //Using the above ID to get the second folder and it's ID.
var SecondSubFolderIDs = await graphClient.Me.MailFolders["Inbox"]
.ChildFolders[$"{FirstSubFolderID.Id}"]
.ChildFolders
.Request()
.Select(sid => new
{
sid.Id,
sid.DisplayName
})
.GetAsync();
foreach (var SecondSubFolderID in SecondSubFolderIDs)
{
if(SecondSubFolderID.Id != null)
{ //Continuing on using the first 2 ID's to get the 3rd folder and it's ID.
var ThirdSubFolderIDs = await graphClient.Me.MailFolders["Inbox"]
.ChildFolders[$"{FirstSubFolderID.Id}"]
.ChildFolders[$"{SecondSubFolderID.Id}"]
.ChildFolders
.Request()
.Select(tid => new
{
tid.Id,
tid.DisplayName,
})
.GetAsync();
foreach (var ThirdSubFolderID in ThirdSubFolderIDs)
{
if(ThirdSubFolderID.DisplayName == "New")
{ //Now you're at the last folder where the emails are at.
var GetMessageAttachments = await graphClient.Me.MailFolders["Inbox"]
.ChildFolders[$"{FirstSubFolderID.Id}"]
.ChildFolders[$"{SecondSubFolderID.Id}"]
.ChildFolders[$"{ThirdSubFolderID.Id}"]
.Messages
//.Request(SearchOption)
.Request()
.Expand("attachments")
.Select(gma => new
{
gma.Id,
gma.Subject,
gma.HasAttachments,
gma.Attachments
})
.GetAsync();
//Get Message count that includes attachments
var MessageCount = GetMessageAttachments.Count;
if (MessageCount != 0)
{
//Do what you want here
}
Decided to answer my question so someone else might find this helpful.

Related

Add Outlook Category to mail via MS Graph

I am trying to add a Category to an email via Graph.
I found this:
https://github.com/microsoftgraph/microsoft-graph-docs/blob/main/api-reference/v1.0/resources/outlookcategory.md
But it only explains how to create a new category. I already got my categories and I'd like to add one to a mail I've also got in memory.
var categories = await graphService.Users[metadata.UserOid].Outlook.MasterCategories.Request().GetAsync();
var category = categories.SingleOrDefault(c => c.DisplayName == name);
if (category == null)
{
if (KNOWN_OUTLOOK_CATEGORIES.ContainsKey(name))
{
await graphService.Users[metadata.UserOid].Outlook.MasterCategories.Request().AddAsync(KNOWN_OUTLOOK_CATEGORIES[name]);
if (retry > 3)
throw new InvalidOperationException($"Cannot create category in mailbox: {name}.");
await SetCategoryMailByName(name, graphService, metadata, retry + 1);
}
}
// Add my category to the mail
await graphService.Users[metadata.UserOid].Messages[m.MailId].Categories.Add(category);
Problem here is that there is no category in the mail. How could I add one?
Try adding a new Message object with the new category instead of trying to add it to the message categories list:
var message = new Message
{
Categories = ["new category"]
};
await graphClient.Me.Messages["{message-id}"]
.Request()
.UpdateAsync(message);

Assign a License (Microsoft Graph API)

I'm trying to assign a license in C# via Graph API.
https://learn.microsoft.com/en-us/graph/api/user-assignlicense?view=graph-rest-1.0
Parameters:
addLicenses (AssignedLicense collection)
A collection of assignedLicense objects that specify the licenses to add. You can
disable plans associated with a license by setting the disabledPlans
property on an assignedLicense object.
removeLicenses (Guid collection)
A collection of GUIDs that identify the licenses to remove.
Here is my code ...
var userQuery = await client.Users
.Request()
.Filter("userPrincipalName eq 'xxx#xxx.de'")
.GetAsync();
var user = userQuery.FirstOrDefault();
var skus = await client.SubscribedSkus.Request().GetAsync();
do
{
foreach (var sku in skus)
{
AssignedLicense aLicense = new AssignedLicense { SkuId = sku.SkuId };
IList<AssignedLicense> licensesToAdd = new AssignedLicense[] { aLicense };
IList<Guid> licensesToRemove = new Guid[] { };
try
{
client.Users[user.Id].AssignLicense(licensesToAdd, licensesToRemove);
}
}
}
while (skus.NextPageRequest != null && (skus = await skus.NextPageRequest.GetAsync()).Count > 0);
I don't get an error, but it does not work. The user has no license ...
I think you forgot something here:
client.Users[user.Id].AssignLicense(licensesToAdd, licensesToRemove);
I think it should be:
await client.Users[user.Id].AssignLicense(licensesToAdd, licensesToRemove).Request().PostAsync();
PS:
You can get the user with less code like:
var userQuery = await client.Users["xxx#xxx.de"].Request().GetAsync();

Dropbox C# API v2 List Team folder contents

Good afternoon,
I am trying to get the list of directories from the dropbox API for the team members in business dropbox.
The documentation seems to be somewhat unclear and frustratingly difficult to get contents. Therefore my code could off somewhat and hence why i am having issues. I have a development token for both Team and my admin user.
using (var client = new DropboxTeamClient("my token"))
{
var teamInfo = await client.Team.GetInfoAsync();
var teamName = teamInfo.Name;
var numberOfUsers = teamInfo.NumProvisionedUsers;
var memListResult = await client.Team.MembersListAsync();
foreach (var m in memListResult.Members)
{
var accountId = m.Profile.AccountId;
var email = m.Profile.Email;
Console.WriteLine($"Id {accountId} - email is {email}");
}
var accId = memListResult.Members.First(x => x.Profile.Email.Equals("myEmail"))
?.Profile.AccountId;
var memId = memListResult.Members.First(x => x.Profile.Email.Equals("myEmail"))
?.Profile.TeamMemberId;
var dbx = client.AsAdmin(memId);
try
{
var full = await dbx.Users.GetCurrentAccountAsync();
Console.WriteLine("{0} - {1}", full.Name.DisplayName, full.Email);
await ListRootFolder(dbx, true);
}
catch (Exception e)
{
Console.WriteLine(e);
}
}
private async Task ListRootFolder(DropboxClient dbx)
{
var list = await dbx.Files.ListFolderAsync(string.Empty);
//var tlist = await dbx.
// show folders then files
foreach (var item in list.Entries.Where(i => i.IsFolder))
{
Console.WriteLine("D {0}/", item.Name);
}
foreach (var item in list.Entries.Where(i => i.IsFile))
{
Console.WriteLine("F{0,8} {1}", item.AsFile.Size, item.Name);
}
}
No my issue is that i can only ever get what shows in my directory. So for example i have logged into the web browser into dropbox i have three directories.
|
|- User Dir 'This is my home directory'
|- Team Dir 'This is a directory for the team'
|- Sample folder
The team has access to the Team dir and sample folder. All i would like to do is simply get the list of directories.
I must be doing something completely wrong, i have also tried the overrides for dbx.Files.ListFolderAsync i have set includeMountedFolders parameter to true and still only lists two files in my profile.
I have also tried using the user token instead of the Team token and setting asAdmin.
Apologies on the code is somewhat untidy i just want to get it working before i refactor.
Any help would be appreciated.
Thanks
It sounds like you want to access your "team space". You need to explicitly specify this when calling the API. I recommend reading the Namespace Guide, which covers this in detail.
The .NET SDK supports setting the Dropbox-Api-Path-Root header, via DropboxClient.WithPathRoot.
First, you can get the root info from GetCurrentAccountAsync:
var accountInfo = await dbx.Users.GetCurrentAccountAsync();
Console.WriteLine(accountInfo.RootInfo.RootNamespaceId);
Then, you can access the team shared space by using DropboxClient.WithPathRoot to set the Dropbox-Api-Path-Root header as desired, like:
dbx = dbx.WithPathRoot(new PathRoot.NamespaceId(accountInfo.RootInfo.RootNamespaceId));
var res = await this.client.Files.ListFolderAsync(path: "");
foreach (var entry in res.Entries)
{
Console.WriteLine(entry.Name);
}

How to notify user when programatically adding them to a group

I am adding members to a group with the code below.
My question is very simple :
When adding members with this code, the invited person does not get an email notifying them. However when doing the same from the UI there is an option to notify the user. How can I do that from the code?
public void UpdateGoupMembers(string groupName, List<string> loginNames)
{
using (var clientContext = new ClientContext(baseUrl))
{
clientContext.Credentials = credentials;
var web = clientContext.Web;
var group = web.SiteGroups.GetByName(groupName);
if (group != null)
{
foreach (var loginName in loginNames)
{
var user = web.EnsureUser(loginName);
if (user != null)
{
group.Users.AddUser(user);
}
}
var existingUsers = group.Users;
clientContext.Load(existingUsers, includes => includes.Include(
f => f.LoginName,
f => f.UserId,
f => f.PrincipalType,
f => f.Email,
f => f.Id));
clientContext.ExecuteQuery();
foreach (var existingUser in existingUsers)
{
var userName = existingUser.LoginName.Split('|')[2];
if (!loginNames.Contains(userName))
{
group.Users.RemoveByLoginName(existingUser.LoginName);
}
}
}
clientContext.ExecuteQuery();
}
}
I do not believe it is possible to send a Welcome Email when users are added to a group programatically based off of the method documentation.
However, you can use the CSOM library to send an email programatically to the specific user after adding them to the group. Send Emails via SharePoint CSOM will be a great reference on how to do this.

How to adjust DateModified property on a single OneDrive item

I am able to upload a file using the OneDrive SDK no problem. As per the information on the OneDrive Dev Center, The FileSystemInfo.DateModified refers to the time the file was seen by the service, rather than when it was modified locally.
I am attempting to manually alter it to the local value with the suggestion to include them with the request, but the value being set in my code is not sticking and is returning to the time when the PutAsync<Item> request is completed. What Am I doing wrong?
My code:
if (localfile != null)
{
localprop = await localfile.GetBasicPropertiesAsync();
localtime = localprop.DateModified;
try
{
Stream syncstream = await localfile.OpenStreamForReadAsync();
using (syncstream)
{
var upload = await _userDrive.Drive.Special.AppRoot.ItemWithPath(filepath).Content.Request().PutAsync<Item>(syncstream);
upload.FileSystemInfo.LastModifiedDateTime = localtime;
}
}
catch (OneDriveException)
{ }
}
My query against the same:
oneDItem = await _userDrive.Drive.Special.AppRoot.ItemWithPath(filepath).Request().GetAsync();
var oneDtime = (DateTimeOffset)oneDItem.FileSystemInfo.LastModifiedDateTime;
If you upload a file to one drive, there is no parameter for LastModifiedDateTime to request together, you may not change the modified time when uploading. But you can update an item metadata by update request. After uploading, you can get the item you just uploaded and update its LastModifiedDateTime meta data. Code as follows:
if (localfile != null)
{
var localprop = await localfile.GetBasicPropertiesAsync();
var localtime = localprop.DateModified;
try
{
Stream syncstream = await localfile.OpenStreamForReadAsync();
using (syncstream)
{
DriveItem upload = await _userDrive.Me.Drive.Root.ItemWithPath("regfolder/regdata.jpg").Content.Request().PutAsync<DriveItem>(syncstream);
DriveItem updateitem = new DriveItem() {
FileSystemInfo=new Microsoft.Graph.FileSystemInfo()
{
LastModifiedDateTime = localtime
}
};
DriveItem Updated = await _userDrive.Me.Drive.Root.ItemWithPath("regfolder/regdata.jpg").Request().UpdateAsync(updateitem);
}
}
catch (Exception ex)
{ }
}

Categories