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);
Related
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.
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();
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.
I'm using ExchangeWebServices C#.
I'm trying to send an email to distributio list, so
I'm created a group as follow:
private void CreateGroup(ExchangeService service)
{
// Create a new contact group object.
ContactGroup myContactGroup = new ContactGroup(service);
// Give the group a name.
myContactGroup.DisplayName = "TestContactGroup";
// Add some members to the group.
myContactGroup.Members.Add(new GroupMember("Euser#mydomain.com"));
myContactGroup.Members.Add(new GroupMember("Euser1#mydomain.com"));
myContactGroup.Members.Add(new GroupMember("Euser2#mydomain.com"));
// Save the group.
myContactGroup.Save();
}
Now I'm trying to send email to this group, how can i do that?
What i'm tried:
EmailMessage email = new EmailMessage(service);
email.ToRecipients.Add("TestContactGroup");//Throw an exception "At least one recipient isn't valid."
//email.ToRecipients.Add("TestContactGroup#mydomain.com");//"Return" the mail that "The email address you entered couldn't be found."
email.Subject = "MySubject";
email.Body = new MessageBody("MyBody");
// Send the mail
email.Send();
If I'm trying to send to TestContactGroup i'm got an exception:
"At least one recipient isn't valid."
And if I'm trying to send to TestContactGroup#mydomain.com I'm got an email that the mail isn't found.
So, how can i send an email to group list that I'm crated? Or another way to create distribution list with EWS?
Thanks
Problem solved.
I'm just need to add the group id as follow:
myContactGroup.Id = grpId;
I'm get my group id as follow:
// Instantiate the item view with the number of items to retrieve from the Contacts folder.
ItemView view = new ItemView(9999);
// Request the items in the Contacts folder that have the properties that you selected.
FindItemsResults<Item> contactItems = service.FindItems(WellKnownFolderName.Contacts, view);
string groupId = string.Empty;
List<ItemId> groupItemIds = new List<ItemId>();
// Loop through all contacts
foreach (Item item in contactItems)
{
//Check to see if ContactGroup
if (item is ContactGroup)
{
//Get the contact group
ContactGroup contactGroup = item as ContactGroup;
groupItemIds.Add(item.Id);//Using to send an email by item id, can classify by DisplayName etc..
}
}
I am using the Exchange Web Services Managed API 2.2 to monitor users inboxes and need to determine if an e-mail is a new item, a reply or a forwarded message.
I have seen various articles on SO such as how to notice if a mail is a forwarded mail? and Is there a way to determine if a email is a reply/response using ews c#? which both help in their specific cases but I still cannot work out how to distinguish between a reply and a forwarded item.
In the first article an extra 5 bytes is added each time (forward or reply) so I don't know what the last action was.
The second article suggests using the InReplyTo however when I examine the property for forwarded e-mails it contains the original senders e-mail address (not null).
I have seen other articles such as this or this that suggest using extended properties to examine the values in PR_ICON_INDEX, PR_LAST_VERB_EXECUTED and PR_LAST_VERB_EXECUTION_TIME.
My code looks as follows but never returns a value for lastVerbExecuted
var lastVerbExecutedProperty = new ExtendedPropertyDefinition(4225, MapiPropertyType.Integer);
var response = service.BindToItems(newMails, new PropertySet(BasePropertySet.IdOnly, lastVerbExecutedProperty));
var items = response.Select(itemResponse => itemResponse.Item);
foreach (var item in items)
{
object lastVerb;
if (item.TryGetProperty(lastVerbExecutedProperty, out lastVerb))
{
// do something
}
}
PR_ICON_INDEX, PR_LAST_VERB_EXECUTED and PR_LAST_VERB_EXECUTION_TIME would only work to tell you if the recipient has acted on a message in their Inbox. Eg if the user had replied or forwarded a message in their inbox then these properties get set on the message in their Inbox. On the message that was responded to or forwarded these properties would not be set. I would suggest you use the In-Reply-To Transport header which should be set on any message that is replied to or forwarded, this should contain the internet messageid of the message that was replied to or forwarded eg.
FindItemsResults<Item> fiRs = service.FindItems(WellKnownFolderName.Inbox, new ItemView(10));
PropertySet fiRsPropSet = new PropertySet(BasePropertySet.FirstClassProperties);
ExtendedPropertyDefinition PR_TRANSPORT_MESSAGE_HEADERS = new ExtendedPropertyDefinition(0x007D, MapiPropertyType.String);
fiRsPropSet.Add(PR_TRANSPORT_MESSAGE_HEADERS);
service.LoadPropertiesForItems(fiRs.Items, fiRsPropSet);
foreach (Item itItem in fiRs)
{
Object TransportHeaderValue = null;
if(itItem.TryGetProperty(PR_TRANSPORT_MESSAGE_HEADERS,out TransportHeaderValue)) {
string[] stringSeparators = new string[] { "\r\n" };
String[] taArray = TransportHeaderValue.ToString().Split(stringSeparators, StringSplitOptions.None);
for (Int32 txCount = 0; txCount < taArray.Length; txCount++)
{
if (taArray[txCount].Length > 12)
{
if (taArray[txCount].Substring(0, 12).ToLower() == "in-reply-to:")
{
String OriginalId = taArray[txCount].Substring(13);
Console.WriteLine(OriginalId);
}
}
}
}
}
Apart from the Subject prefix that was discussed in the other link I don't know of any other proprieties that will differentiate between a reply or forward.
Cheers
Glen
The best way to rely is on the ResponeCode of Extended properties
Refer below scripts
private static int IsForwardOrReplyMail(ExchangeService service, EmailMessage messageToCheck)
{
try
{
// Create extended property definitions for PidTagLastVerbExecuted and PidTagLastVerbExecutionTime.
ExtendedPropertyDefinition PidTagLastVerbExecuted = new ExtendedPropertyDefinition(0x1081, MapiPropertyType.Integer);
ExtendedPropertyDefinition PidTagLastVerbExecutionTime = new ExtendedPropertyDefinition(0x1082, MapiPropertyType.SystemTime);
PropertySet propSet = new PropertySet(BasePropertySet.IdOnly, EmailMessageSchema.Subject, PidTagLastVerbExecutionTime, PidTagLastVerbExecuted);
messageToCheck = EmailMessage.Bind(service, messageToCheck.Id, propSet);
// Determine the last verb executed on the message and display output.
object responseType;
messageToCheck.TryGetProperty(PidTagLastVerbExecuted, out responseType);
if (responseType != null && ((Int32)responseType) == 104)
{
//FORWARD
return 104;
}
else if (responseType != null && ((Int32)responseType) == 102)
{
//REPLY
return 102;
}
}
catch (Exception)
{
return 0;
//throw new NotImplementedException();
}
}
To determine if it was a reply to a email, you can use the EmailMessage objects InReplyTo property, e.g:
EmailMessage mail = ((EmailMessage)Item.Bind(service, new ItemId(UniqueId)));
if (mail.InReplyTo == null)
return;
else
..your code