Getting custom field value of item in Outlook Public Folder (C#) - c#

How to get custom field value of item in Outlook Public Folder?
I need to do it in C# and preferably using ExchangeService and not Interop.
Do you have any code examples handy?
So far I am getting into desired Public Folder and can read item(s). However, I can't find any custom fields in properties.

The following example shows how to retrieve a collection of items from the Exchange mailbox, including the extended property that is identified by the GUID. The example searches each retrieved item and displays the subject line of each item and the extended property name and value, if it exists.
// Retrieve a collection of all the mail (limited to 10 items) in the Inbox. View the extended property.
ItemView view = new ItemView(10);
// Get the GUID for the property set.
Guid MyPropertySetId = new Guid("{C11FF724-AA03-4555-9952-8FA248A11C3E}");
// Create a definition for the extended property.
ExtendedPropertyDefinition extendedPropertyDefinition =
new ExtendedPropertyDefinition(MyPropertySetId, "Expiration Date", MapiPropertyType.String);
view.PropertySet =
new PropertySet(BasePropertySet.IdOnly, ItemSchema.Subject, extendedPropertyDefinition);
FindItemsResults<Item> findResults = service.FindItems(WellKnownFolderName.Inbox, view);
// Search the e-mail collection for the extended property.
foreach (Item item in findResults.Items)
{
Console.WriteLine(item.Subject);
if (item.ExtendedProperties.Count > 0)
{
// Display the extended name and value of the extended property.
foreach (ExtendedProperty extendedProperty in item.ExtendedProperties)
{
Console.WriteLine(" Extended Property Name: " + extendedProperty.PropertyDefinition.Name);
Console.WriteLine(" Extended Property Value: " + extendedProperty.Value);
}
}
}
See Viewing custom extended properties by using the EWS Managed API 2.0 for more information.

Related

ExchangeService.FindItems()

I've been requested to create an application that should create a common base from 3 different email addresses sources and update each base taking the most common updated dataset.
Among the three sources I have an Exchange server Contacts addressbook. I know that I can access such data trough EWS and in specific I should probably use the ExchangeService.FindPeople() method or maybe FindPersona().
This should work but, since I'm looking only for new/updated contacts, it will load significantly the server (maybe not for new records but I cannot understand how to retrieve update records) and this it's not a good practice.
I've found in MSDN docs a way to be notified on updates on message base but there's nothin related on updates on contacts:
https://msdn.microsoft.com/en-us/library/office/dn458791(v=exchg.150).aspx
Is there something to be notified on updates on contacts (even trough third party products/APIs).
P.S. I would like to code in C# (or other .NET languages) but I'm open to anything else.
You should be able to check for newly created contacts by iterating through the contacts and accessing the DateTimeCreated property on each.
To check for updated contacts, you could use the LastModifiedTime property.
// Get the number of items in the Contacts folder.
ContactsFolder contactsfolder = ContactsFolder.Bind(service, WellKnownFolderName.Contacts);
// Set the number of items to the number of items in the Contacts folder or 50, whichever is smaller.
int numItems = contactsfolder.TotalCount < 50 ? contactsfolder.TotalCount : 50;
// Instantiate the item view with the number of items to retrieve from the Contacts folder.
ItemView view = new ItemView(numItems);
// To keep the request smaller, request only the DateTimeCreated and LastModifiedTime properties.
view.PropertySet = new PropertySet(BasePropertySet.IdOnly, ContactSchema.DateTimeCreated, ContactSchema.LastModifiedTime);
// Retrieve the items in the Contacts folder that have the properties that you selected.
FindItemsResults<Item> contactItems = service.FindItems(WellKnownFolderName.Contacts, view);
// Display the list of contacts.
foreach (Item item in contactItems)
{
if (item is Contact)
{
Contact contact = item as Contact;
if (contact.DateTimeCreated.Date == DateTime.Today.Date)
{
//Notify - Newly created contact
}
if (contact.LastModifiedTime.Date == DateTime.Today.Date)
{
//Notify - Newly modified contact
}
}
}
This is simple example to find contact by nikname. You can find a more advaced search here.
// Create a view with a page size of 1.
ItemView view = new ItemView(1);
// Create the search filter.
SearchFilter searchFilter = new SearchFilter.IsEqualTo(ContactSchema.NickName, "806555335");
FindItemsResults<Item> contactItems = service.FindItems(WellKnownFolderName.Contacts, searchFilter, view);
if(contactItems != null && contactItems.Count()>0){
//contact found!
Contact contact = contactItems.First() as Contact;
}

Find a recipient with a given address in a folder

My company needs an add-in for automatically adding offers to the email when it is the first time we send an email to a recipient.
My question is :
How can I check if this is the first time the user sends an email to the recipients?
I tried this but I receive error that Recipient is unknown property. And I also think that this is not the right approach...
object folderItem;
Boolean AlreadyEmailed = false;
if (mail != null)
{
const string PR_SMTP_ADDRESS =
"http://schemas.microsoft.com/mapi/proptag/0x39FE001E";
Outlook.Recipients recips = mail.Recipients;
foreach (Outlook.Recipient recip in recips)
{
Outlook.PropertyAccessor pa = recip.PropertyAccessor;
string smtpAddress =
pa.GetProperty(PR_SMTP_ADDRESS).ToString();
string filter = "[Recipient] = 'John#foo.com'";
filter = filter.Replace("John#foo.com", smtpAddress);
Debug.WriteLine(filter);
folderItem = items.Restrict(filter);
if(folderItem != null)
{
Debug.WriteLine("We found items that have the filter");
AlreadyEmailed = true;
}
//Debug.WriteLine(recip.Name + " SMTP=" + smtpAddress);
}
if(!AlreadyEmailed)
{
Debug.WriteLine("This is the first time we email ... ");
}
}
The Sent property of the MailItem class returns a Boolean value that indicates if a message has been sent. In general, there are three different kinds of messages: sent, posted, and saved. Sent messages are items sent to a recipient or public folder. Posted messages are created in a public folder. Saved messages are created and saved without either sending or posting.
Also you may use the following Extended MAPI properties that deal with the message state (replied/forwarded):
PR_ICON_INDEX (http://schemas.microsoft.com/mapi/proptag/0x10800003)
PR_LAST_VERB_EXECUTED (the DASL name is http://schemas.microsoft.com/mapi/proptag/0x10810003)
PR_LAST_VERB_EXECUTION_TIME (0x10820040)
To get these values use the PropertyAccessor class (see the corresponding properties of Outlook items).
Be aware, new Outlook items don't have the EntryID property set.
You can Use To/CC/BCC properties in Items.Find/Restrict. Note that it is better to use Find in your case since you only need a single match, not all of them. Also note that Restrict will not return null if no matches are found, but rather an Items collection with Items.Count == 0.
That being said, To/CC/BCC might not include addresses, only names, so search won't help you. You can still loop through all items in the folder and explicitly check the Recipients collection of each item, but that will be hugely inefficient.
On the Extended MAPI level (C++ or Delphi), one can create subrestrictions on message recipients (or attachments), but the Outlook Object Model does not expose that functionality.
If using Redemption is an option (I am its author), its implementation of Find/Restrict does support queries on the Recipients collection:
set Session = CreateObject("Redemption.RDOSession")
Session.MAPIOBJECT = Application.Session.MAPIOBJECT
set YourOutlookFolder = Application.ActiveExplorer.CurrentFolder
set rFolder = Session.GetFolderFromID(YourOutlookFolder.EntryID)
set rItems = rFolder.Items
set rMsg = rItems.Find("Recipients LIKE 'John#foo.com' ")
while not (rMsg Is Nothing)
Debug.print rMsg.Subject
set rMsg = rItems.FindNext
wend
In C# (not tested):
Redemption.RDOSession Session = new Redemption.RDOSession();
Session.MAPIOBJECT = Application.Session.MAPIOBJECT;
set rFolder = Session.GetFolderFromID(YourOutlookFolder.EntryID);
Redemption.RDOMail rMsg = rFolder.Items.Find("Recipients LIKE 'John#foo.com' ") ;
AlreadyEmailed = rMsg != null;

List all available extended properties using EWS

I'm trying to find a way to list ALL extended properties for set of calendar items using EWS.
The problem is that all examples I managed to find online require me to know what those extended properties are, in advance. Here's the official MSDN example.
What am I supposed to do if I do not know the IDs or names of extended properties? Or if I don't even know if any extended properties exist at all?
I've tried the following code but it returns an exception...
var calendarItems = service.FindAppointments(WellKnownFolderName.Calendar, view);
var propertySet = new PropertySet(AppointmentSchema.ExtendedProperties);
service.LoadPropertiesForItems(calendarItems, propertySet);
Here's the exception:
Microsoft.Exchange.WebServices.Data.ServiceResponseException: The request failed schema validation: The required attribute 'FieldURI' is missing.
There is no call in EWS to get all extended properties. The idea behind extended properties is that applications use them to store app-specific data, so only that app needs to know the specifics on their properties.
Extended MAPI can discover this information. https://github.com/stephenegriffin/mfcmapi has a ton of sample code for different tasks, including iterating named properties.
I was also looking similar and I just did a kind of reverse engineering. Since the extended property is the combination of Id (integer) and data type which we could not know as they are not documented on any MSDN. So, iterate 1 to some huge number like 15000 for string type property and find those which can be loaded successfully - this is the main trickest part which we can do by putting try-catch to bind that extended property. Then you could get the required one.
Hope that helps.
List<int> allStringIds = new List<int>();
for (int i = 0; i <= 15000; i++)
{
allStringIds.Add(i);
}
ParallelOptions options = new ParallelOptions
{
MaxDegreeOfParallelism = 200,
CancellationToken = CancellationToken.None,
};
Parallel.For(0, allStringIds.Count, options, index =>
{
try
{
ExtendedPropertyDefinition extendedPropertyDefinition = new ExtendedPropertyDefinition(index,
MapiPropertyType.String);
latestMessage = EmailMessage.Bind(service, item.Id.UniqueId,
new PropertySet(BasePropertySet.FirstClassProperties, extendedPropertyDefinition));
_logger.Write("Supported string property id=" + index);
supportedListId.TryAdd(index, index);
}
catch(Exception ex)
{
}
});
foreach (var a in supportedListId)
{
ExtendedPropertyDefinition extendedPropertyDefinition = new ExtendedPropertyDefinition(a.Key,
MapiPropertyType.String);
allExtendedPropertyDefinitions.Add(extendedPropertyDefinition);
}
latestMessage = EmailMessage.Bind(service, item.Id.UniqueId,
new PropertySet(BasePropertySet.FirstClassProperties, allExtendedPropertyDefinitions));
foreach (var extendedProperty in latestMessage.ExtendedProperties)
{
if (extendedProperty.PropertyDefinition != null && extendedProperty.PropertyDefinition.Tag != null)
{
if (extendedProperty.Value != null)
{
_logger.Write($"OMG... extendedProperty id={extendedProperty.PropertyDefinition.Id}," +
$" name={ extendedProperty.PropertyDefinition.Name}, value={extendedProperty.Value}");
}
}
}

Why does TotalCount property on public folder always return 0 items?

Using Exchange 2013 SP1 and Exchange Web Services managed API 2.2 to try and get a list of the contacts in a contact folder that I have stored in Public Folders. I would like to limit the size of the ItemView to the total number of contacts that I have in this contact folder, but when I try to return that property (contactfolder.TotalCount) it always returns 0. If I try this with using the Contact folder under my mailbox a value other than 0 is returned. I can get around this issue by either specifying a value for the constructor for the ItemView as either a specific number or by using int.MaxValue, but I would rather use the total number of items that are in the contact list. Any assistance is greatly appreciated! Here is the relevant code:
private FindItemsResults<Microsoft.Exchange.WebServices.Data.Item> ExchangeContacts()
{
// Setup the exchange server connection.
ExchangeService service = new ExchangeService(ExchangeVersion.Exchange2013_SP1);
service.AutodiscoverUrl("someone#mydomain.com");
// Set the filter to choose the correct contact list
SearchFilter filter = new SearchFilter.IsEqualTo(FolderSchema.DisplayName, "My Public Contacts");
SearchFilter.SearchFilterCollection filterCollection = new SearchFilter.SearchFilterCollection();
filterCollection.Add(filter);
// Get the FolderId using the search filter.
Folder parent = Folder.Bind(service, WellKnownFolderName.PublicFoldersRoot);
FindFoldersResults results = parent.FindFolders(filter, new FolderView(1));
FolderId fid = results.Single().Id;
// Get the Contact folder based on the folderid.
ContactsFolder contactsfolder = (ContactsFolder)results.Single();
ItemView view = new ItemView(contactsfolder.TotalCount);
// Set the property that need to be shown in the page.
view.PropertySet = new PropertySet(BasePropertySet.IdOnly, ContactSchema.DisplayName, ContactSchema.CompanyName, ContactSchema.LastModifiedTime, ContactSchema.BusinessAddressCity, ContactSchema.BusinessAddressPostalCode, ContactSchema.BusinessAddressState, ContactSchema.BusinessAddressStreet, ContactSchema.HomeAddressCity, ContactSchema.HomeAddressPostalCode, ContactSchema.HomeAddressState, ContactSchema.HomeAddressStreet, ContactSchema.ItemClass, ContactSchema.FileAs, ContactSchema.LastModifiedName);
//view.PropertySet = new PropertySet(BasePropertySet.IdOnly, ContactSchema.DisplayName);
// Order the results by one of the selected properties
//view.OrderBy.Add(ContactSchema.LastModifiedTime, Microsoft.Exchange.WebServices.Data.SortDirection.Descending);
FindItemsResults<Microsoft.Exchange.WebServices.Data.Item> contactItems = contactsfolder.FindItems(view);
return contactItems;
}
I recreated your public folder (immediately under the public folder root), added a contact to it, and ran your code and get a contactItems.TotalCount value of 1 (as expected). However, after discussing this with the Exchange product team I learned that FindFolder can return an incorrect value if the FindFolder request gets routed to the public folder mailbox that doesn't have the content of the public folder. So TotalCount can return an incorrect value and is not supported for public folders. We will get the documentation updated to reflect this issue.
FindFolder operation (Exchange 2013):
Using the Default value for the BaseShape, the response returns the
folder name, the folder ID, the number of subfolders, the number of
child folders found in the folder, and the count of unread items.
<...>
FindFolder responses to a request with the AllProperties response
shape will not return the TotalCount and UnreadCount elements for
public folder searches.
Guess you need to specify a property filter in the search criteria.

casting objects in c# (Exchange Web Services Item to Email)

I cannot work out how I can do what I need to do.
I have a method called 'MarkAsRead' that takes an ItemID and should mark the mail item as read.
But it seems that the Item doesn't have an 'IsRead' proeprty, only an email does, so I need to cast my Exchange WebServices mail item to an email message.
here is the code:
try
{
System.Diagnostics.Debugger.Break();
//creates an object that will represent the desired mailbox
Mailbox mb = new Mailbox(common.strInboxURL);
//creates a folder object that will point to inbox fold
FolderId fid = new FolderId(WellKnownFolderName.Inbox, mb);
//this will bind the mailbox you're looking for using your service instance
Microsoft.Exchange.WebServices.Data.Folder inbox = Microsoft.Exchange.WebServices.Data.Folder.Bind(service, fid);
//// if the property is not loaded yet, first load it
//mail.Load(PropertySet(BasePropertySet.IdOnly, EmailMessageSchema.IsRead));
//if (!mail.IsRead) // check that you don't update and create unneeded traffic
//{
// mail.IsRead = true; // mark as read
// mail.Update(ConflictResolutionMode.AutoResolve); // persist changes
//}
// As a best practice, limit the properties returned to only those that are required.
PropertySet propSet = new PropertySet(BasePropertySet.IdOnly, ItemSchema.Subject);
// Bind to the existing item by using the ItemId.
// This method call results in a GetItem call to EWS.
Item item = Item.Bind(service, itemId, propSet);
EmailMessage mail = Item.Bind(service, itemId, propSet);
item.Load(new PropertySet(BasePropertySet.IdOnly,EmailMessageSchema.IsRead));
item.IsRead = true;
item.Update(ConflictResolutionMode.AlwaysOverwrite);
return true;
}
I am trying to do something like this:
// if the property is not loaded yet, first load it
mail.Load(PropertySet(BasePropertySet.IdOnly, EmailMessageSchema.IsRead));
if (!mail.IsRead) // check that you don't update and create unneeded traffic
{
mail.IsRead = true; // mark as read
mail.Update(ConflictResolutionMode.AutoResolve); // persist changes
}
unfortunately I need to be able to get a unique email message from the item.
how can I do that please?
Philip-
You can add EmailMessageSchema.IsRead to your property set, so that you can get it in your Bind call, and then you don't have to call Load at all.
PropertySet propSet = new PropertySet(BasePropertySet.IdOnly,
ItemSchema.Subject, EmailMessageSchema.IsRead);
The EmailClass derives from the Item class, so Bind works on both. So you can do the following:
EmailMessage mail = EmailMessage.Bind(service, itemId, propSet);
Put that together and you've got this:
PropertySet propSet = new PropertySet(BasePropertySet.IdOnly, ItemSchema.Subject, EmailMessageSchema.IsRead);
EmailMessage mail = EmailMessage.Bind(service, itemId, propSet);
if (!mail.IsRead) // check that you don't update and create unneeded traffic
{
mail.IsRead = true; // mark as read
mail.Update(ConflictResolutionMode.AutoResolve); // persist changes
}
Philip,
When testing my code I added just a little bit to the Item.Bind() from your code to make this work:
EmailMessage mail = Item.Bind(service, itemId, propSet) as EmailMessage;
The as EmailMessage will cast it to the appropriate type for you. After that I was able to see the isRead property.
I hope this helps. If this does resolve your problem, please mark the post as answered.
Thanks,
--- Bob ---

Categories

Resources