I am using EWS to add contact items to an office365 account.
Everything works fine, just one detail is not as expected.
When I create a new contact and add e.g. the home address like this:
if (ewsContact.PhysicalAddresses.Contains(PhysicalAddressKey.Home) == false)
{
ewsContact.PhysicalAddresses[PhysicalAddressKey.Home] = new PhysicalAddressEntry();
}
if (string.IsNullOrEmpty(contact.HomeZip) == false)
{
ewsContact.PhysicalAddresses[PhysicalAddressKey.Home].PostalCode = contact.HomeZip;
}
if (string.IsNullOrEmpty(contact.HomeCity) == false)
{
ewsContact.PhysicalAddresses[PhysicalAddressKey.Home].City = contact.HomeCity;
}
The data is written to the contact item, but in the combined field and on the card view in Outlook the ordering of Zip and City is always the order that is used in the US - e.g. Washington 98155
As I have a lot of addresses from europe, I need the correct order - e.g. 10115 Berlin.
If I open the contact in Outlook, change the Zip code at one position and save it back, the order is saved correctly and the display in Outlook is correct.
Is there any way to have the correct order with EWS?
You will need to set the PidLidWorkAddress property https://msdn.microsoft.com/en-us/library/office/cc815905.aspx which should contain the address information formatted in the locale of the client. So in EWS you need to set this using the Extended property definition eg
ExtendedPropertyDefinition PidLidWorkAddress = new ExtendedPropertyDefinition(DefaultExtendedPropertySet.Address, 0x801B, MapiPropertyType.String);
ewsContact.SetExtendedProperty(PidLidWorkAddress, AddressValue);
Related
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;
}
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;
currently I´m writing on a outlook plugin for syncing goolge contacts with outlook but I have to cover some special case:
When a contact gets deleted on google side, my application detects the missing contact and creates a new contact based on the contact info from the outlook one.
Is there a way to get an event or history from google that tells me a user deleted this contact(s)?
Edit 1:
Here is my code how I´m accessing the contacts (what is working FINE):
public GoogleAccessor()
{
var parameters = new OAuth2Parameters()
{
ClientId = CLIENTID,
ClientSecret = CLIENTSECRET,
RedirectUri = REDIRECTURI,
Scope = SCOPES
};
string url = OAuthUtil.CreateOAuth2AuthorizationUrl(parameters);
//An own webbrowser for processing the access tokens
IAuthorizationCodeProvider authcodeProvider = new Presentation.BrowserAuthorizationCodeProvider(new Presentation.BrowserAuthentificatorVM());
parameters.AccessCode = authcodeProvider.GetAuthorizationCode(url);
if(parameters.AccessCode == null)
throw new GoogleOAuthException("AccesCode returned 'null' and failed!");
OAuthUtil.GetAccessToken(parameters);
this._contactsRequest = new ContactsRequest(new RequestSettings(APPLICATIONNAME, parameters) {AutoPaging = true});
}
public IList<IContact> GetAllMappedContacts()
{
Feed<Google.Contacts.Contact> f = _contactsRequest.GetContacts();
this._feedUri = new Uri(f.AtomFeed.Feed);
var photoList = new List<PhotoObject>();
foreach (var entry in f.Entries)
{
var photoObject = GetContactPhoto(entry);
if(photoObject != null)
photoList.Add(photoObject);
}
_googleMapper = new GoogleMapper(f.Entries);
return _googleMapper.MapToLocalContacts();;
}
The thing about syncing in general is that syncing is normally meant to work in one direction.
Source Data -> Data Flow -> Received Data.
In this instance, Outlook is your source data and Google is your received data. All information needs to come from your source. Since this is an Outlook add-in you are creating my suggestion would be to add a button to your add-in ribbon. You can call the button whatever ever you like (maybe "dontSyncButton"), but it's purpose is going to be Categorization of your contact.
Make it so that that when a contact is selected and then the button is clicked, the contact is given a special categorization (perhaps "Dont Sync").
Now give some logic to your code that executes the sync, and have that logic decide whether to sync the contact. Also, give some logic to tell the program to delete the contact out of Google for you if the contacts contains the special category. Semi-Pseudo Code below:
if(contact.Categories.ToString() == "Dont Sync")
{
//Don't Sync Contact
If(googleContact.Exists())
{
//Delete contact from Google if it exist
googleContact.Delete();
}
}
else
{
//Sync Contact
}
It would be nice if Outlook had many modifiable properties that weren't visible to users, but since it does not this is really one of the best options I can think of. I do this to sync contacts from a shared Outlook folder to personal ones and it has worked well so far.
Hope this helps!
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.
We're using EWS to generate some analytics on some of our mailboxes.
Part of this is getting a count/name/start/end of conversations. A conversation being analogous to the way Outlook 2010 shows them when grouping by conversation.
I was hoping to be able to use the ConversationId to group items, but that seems to be an Exchange 2010-only feature.
I can group by subject within a folder to get a simple idea of threads... however this does not handle split conversations, as Outlook 2010 does - specifically, it doesn't handle bringing in the replies that are in the sent items (these are important to us - we can't get good metrics without also looking at replies).
My current code for getting thread info looks like this:
private IEnumerable<EmailThread> GetThreads(Folder folder)
{
var view = new ItemView(int.MaxValue) {PropertySet = new PropertySet(BasePropertySet.IdOnly)};
// view.PropertySet.Add(ItemSchema.ConversationId); - Can't use this as we're stuck on Exchange 2007 !!!
view.PropertySet.Add(ItemSchema.Subject);
view.PropertySet.Add(ItemSchema.DateTimeReceived);
var grouping = new Grouping(ItemSchema.Subject, SortDirection.Descending, ItemSchema.DateTimeReceived, AggregateType.Maximum);
var groupResults = folder.FindItems(view, grouping);
return groupResults.Select(x => new EmailThread
{
Name = x.Items.First().Subject,
Items = x.Items.Count,
StartDate = x.Items.Last().DateTimeReceived, // Assume last in thread is first email
EndDate = x.Items.First().DateTimeReceived // Assume first in thread is most recent
});
}
I am hoping someone knows of a neat way to efficiently get information on replies that constitute part of a conversation?
You can fetch the ConversationId and the ConversationIndex via extended properties:
private static readonly ExtendedPropertyDefinition ConversationIdProperty = new ExtendedPropertyDefinition(0x3013, MapiPropertyType.Binary);
private static readonly ExtendedPropertyDefinition ConversationIndexProperty = new ExtendedPropertyDefinition(0x0071, MapiPropertyType.Binary);
var items = service.FindItems(WellKnownFolderName.Inbox, new ItemView(512) { PropertySet = new PropertySet(BasePropertySet.FirstClassProperties,
ConversationIdProperty, ConversationIndexProperty)});
Both are binary properties. Their content is described in great detail here:
[MS-OXOMSG]: E-Mail Object Protocol Specification, section 2.2.1.2 and 2.2.1.3.
The properties themselves are defined in [MS-OXPROPS]: Exchange Server Protocols Master Property List.