Physical address order of Zip/City - c#

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

Dynamics CRM: How to access parent entity metadata after creating child?

I am new to not only C#, but also Microsoft Dynamics and I am having difficult accessing parent entity data of my Contact via a plug in. My goal is to auto fill my contact's address with the parent account address if the contact address fields are empty. I feel this is a relatively simple process that I am over complicating it.
public void SetContactAddress()
{
// TODO: Go and get the contact's parent account record and retrieve the
// address fields. Then update the contact's address with the fields from the
// values from the account. Do not overrride the contact's address if one is provided/exists
var item = _baseModel.TargetEntity as Entity;
var parent = _baseModel.TargetEntity as Entity;
Contact contact = new Contact(); //New blank contact in memory
contact.Id = item.Id; //assign Id to the new local contact
var service = _targetContact as IOrganizationService;
Guid parentId = ((EntityReference)contact["new_parentid"]).Id;
Entity parentEntity = service.Retrieve("new_parent", parentId, new ColumnSet(true)); //retrieve parent entity
string[] addressAtrributes = {"Address1_City","Address1_Country","Address1_Line1","Address1_Line2",
"Address1_Line3", "Address1_PostalCode","Address1_StateOrProvince"};
foreach (var key in parentEntity.Attributes.Keys)
{
if (addressAtrributes.Contains(key))
{
//contact.Address1_City = key;
contact[key] = key;
}
}
//contact.Address1_City = "My City"; //I can easily hard code a city in
_baseModel.OrganizationService.Update(contact);
}
First of all, you should change:
foreach (var key in parentEntity.Attributes.Keys)
{
if (addressAtrributes.Contains(key))
{
//contact.Address1_City = key;
contact[key] = key;
}
}
To
foreach (var key in parentEntity.Attributes.Keys)
{
if (addressAtrributes.Contains(key))
{
//contact.Address1_City = key;
contact[key] = parentEntity[key];
}
}
Because right now your code is assigning SchemaNames of addresss fields as values.
And your list of attributes is wrong, they should be all lowercase. It's SchemaName that is camelcase, so you can go with:
contact.Address1_City //Address1_City is SchemaName, so name of column in DB
or
contact["address1_city"] //this is name of the field, you can check it in Customizations area in Dynamics 365
The other way around it will not work. The list of address fields is overcomplication, better way (more maintaneable and easier to understand for the person reading code would simply be:
var parentContact = parentEntity.ToEntity<Contact>();
if(isAddressCloned) // you should only clone address if all the fields are empty, not cloning single fields if are empty
{
contact.Address1_City = parentContact.Address1_City;
contact.Address1_Country = parentContact.Address1_Country;
//etc
}
As stated in other answer, this should be done on Pre-Create of contact, so contact should be your Target (you don't have it in your sample code, but I guess you have it somewhere in your plugin) and you should skip the last update in such case.
Approach 1: (your approach)
I guess you are explicitly updating just created contact once again on post create plugin. That too code is incomplete like you are not checking empty address fields, not sure about assignment contact[key] = key;
Approach 2: (what I recommend)
on pre-create contact plugin, you can check address attributes of contact (target entity) for emptiness, then populate data for those fields in target entity itself from parent entity (custom account?) address fields retrieved.
targetEntity.Attributes["Address1_City"] = parentEntity["Address1_City"];
This way explicit update of contact can be avoided.
My proposal would be to do this using workflows instead. There are workflow extensions you can get (for free) that will enable you to execute a workflow for all associated records (in this case an account's contacts).
Then just use a workflow to get the Account's address details and overwrite the contact's address.
I don't recommend a code-first approach for anything in CRM because it requires specialist knowledge to modify. If you use a workflow it can easily be modified; for example, maybe you also want to copy business phone and/or fax number.

Using Microsoft's EWS to create online Lync/Skype meeting

Anybody knows how to create meeting request with online conference(Lync/Skype) using EWS?
So my approach is first getting an online and regular meeting created via Outlook and then simulate the creation of event with the same property.
Here is my code snippet for getting the meeting (calendarView is already initialized with start date, end date etc.):
ExtendedPropertyDefinition extendedOnlineMeetingProperty =
new ExtendedPropertyDefinition(new Guid("{00062008-0000-0000-c000-000000000046}"), 34112,
MapiPropertyType.Integer);
var properties = new PropertySet(
ItemSchema.Id,
AppointmentSchema.ICalUid,
ItemSchema.Subject,
AppointmentSchema.Start,
AppointmentSchema.End,
AppointmentSchema.Organizer,
AppointmentSchema.Location,
AppointmentSchema.LegacyFreeBusyStatus,
AppointmentSchema.IsCancelled,
AppointmentSchema.ICalRecurrenceId,
AppointmentSchema.MyResponseType, // Mandatory Meeting.MyResponseType can be retrieved without a search in the participant list
ItemSchema.LastModifiedTime,
AppointmentSchema.IsOnlineMeeting,
AppointmentSchema.IsMeeting,
ItemSchema.DisplayTo) { };
properties.Add(extendedOnlineMeetingProperty);
var activeResults = service.FindAppointments(WellKnownFolderName.Calendar, calendarView).ToList();
if (activeResults.Count > 0)
{
service.LoadPropertiesForItems(activeResults, properties);
}
I got the property IsOnlineMeeting with the correct bool value (tested -
created online and regular meeting with Outlook) in variable activeResults but I do not understand where to get conference link and other Lync/Skype properties needed for joining a conference.
Also I am not sure where and how to assign the values of Lync/Skype conference URL and other properties.
Sometimes I ask myself if it's worth it to developed some app based on MS products since their documentation suck.
After one week of cursing MS I have found a solution. Using the tool MFCMAPI you can check what property and their values your item in mailbox have.
download the program link
build and run it
Session - Logon - choose your mail profile - pick the mailbox and double click
actions - open special folder - calendar - double click on calendar
open the item with online S4B/Lync conference
the UC* properties are the one I was looking for.
If you open the property you can see something like this on the top:
ag: 0x8096001E
Type: PT_STRING8
DASL: http://schemas.microsoft.com/mapi/string/{00020329-0000-0000-C000-000000000046}/UCMeetingSetting
Named Prop Name: UCMeetingSetting
Named Prop Guid: {00020329-0000-0000-C000-000000000046} = PS_PUBLIC_STRINGS
So my definition of the extended property was wrong. It is not only one property but actually you need all 7 of them.
So the definition should be:
private static ExtendedPropertyDefinition CreateOnlineMeetingProperty()
{
ExtendedPropertyDefinition extendedUCMeetingSetting =
new ExtendedPropertyDefinition(new Guid("{00020329-0000-0000-C000-000000000046}"), "UCMeetingSetting",
MapiPropertyType.String);
return extendedUCMeetingSetting;
}
With the correct extended definition you can get the values from the item easily.
accessing the Value of ExtendedProperties
Calling TryGetProperty
var activeResults = service.FindAppointments(new
FolderId(WellKnownFolderName.Calendar, resource.Email),calendarView).ToList();
service.LoadPropertiesForItems(activeResults, properties);
foreach (Appointment result in activeResults)
{
// 1.
var b = result.ExtendedProperties[1].Value;
// 2.
string UCMeetingSetting;
result.TryGetProperty(extendedUCMeetingSetting, out UCMeetingSetting);
}
using steps above you can get whatever extended property you want, not only Unified Communications (UC) properties.

Restrict outlook items by Recipient name

mySentMail = Globals.ThisAddIn.Application.GetNamespace("MAPI")
.GetDefaultFolder(Outlook.OlDefaultFolders.olFolderSentMail).Items.Restrict(stringFiler);
What should my string filter look like, if i want to restrict sent mail items to only few addresses like,
stringFilter = "[Recipient] = 'John#foo.com' || [Recipient] = 'Marc#MyCompany.com'";
or if they're within the company and are exchange users, is there a lastName filter?
Like, "[LastName] = 'Smith' || [LastName] = 'Ritchie'"?
Thanks.
Outlook Object Model only filters on the message properties (PR_DISPLAY_TO/CC/BCC in this case), it does not create sub restrictions on the recipient or attachment properties.
You might have better luck with Redemption, which creates a sub restriction on PR_MESSAGE_RECIPIENTS with an OR restriction on PR_DISPLAY_NAME/PR_EMAIL_ADDRESS.
As for the last name, since that property is never set on the message itself, you cannot filter on it.

Retrieve Lotus Notes contact information using contact ID in c#

Im developing a tool that needs to access to the names.nsf database inside IBM Lotus Notes, and, using the lotus contact ID (Employee ID) (this id will be provided by the user), retrieve the full information of the person (Name, Position, Phone #....)
I found an example at Codeproject.com (http://www.codeproject.com/Articles/18517/Lotus-Notes-Integration-with-Microsoft-NET-Platfor), however it takes around 10 minutes to get the information the way the example does it (the database has more or less 5000 entries), so I'm searching for a faster way of doing it (if I actually use Lotus notes for this it takes about a second!).
Is there a way to accomplish this task without having the user waiting for minutes?
Thought that maybe you can help me out with this one.
The sample you are using goes through the view using
NotesViewEntry viewEntry = notesViewCollection.GetNthEntry( rowCount );
This is (one of) the worst methods to use as it goes for every iteration from the top of the view and iterates through all docs until it reached the nth document.
There are two options:
1) Optimize this code by using
NotesViewEntry viewEntry = notesViewCollection.GetFirstEntry();
and at the end
viewEntry = notesViewCollection.GetNextEntry(viewEntry);
2) (in my humble opinion the better way): Change the code:
- you need a view with the first column sorted by your key => contact ID (Employee ID)
- You can the access the ViewEntry by a code like
LotusNotesView.GetEntryByKey( EmployeeID, true);
If you are lucky the names.nsf is full text indexed. If it's not you could try to ask if it could be full text indexed. When it's indexed you can get the person document quicly like this:
LotusNotesView.FTSearch("[EmployeeID]=1234567", 1);
NotesDocument docPerson = LotusNotesView.GetFirstDocument();
The use of GetNthEntry certainly causes some performance issues. I've taken the relevant code from that site and rewrote it to use the GetFirst/GetNext pattern, which is recommended for all view processing in Lotus Notes.
Note this hasn't been tested, of course. The point is to get the first entry in your collection, check that it is an object, and then process it. At the end of the loop, get the next entry and repeat until you hit null.
NotesViewEntryCollection notesViewCollection = LotusNotesView.AllEntries;
NotesViewEntry viewEntry = notesViewCollection.GetFirstEntry();
while (viewEntry != null)
{
//Get the first document of particular entry.
NotesDocument document = viewEntry.Document;
object documentItems = document.Items;
Array itemArray1 = (System.Array)documentItems;
for( int itemCount=0 ; itemCount< itemArray1.Length; itemCount++ )
{
NotesItem notesItem =
(Domino.NotesItem)itemArray1.GetValue( itemCount );
//compare field value with specific value entered by user
if( notesItem.Text !=null )
{
if( (notesItem.Text.ToUpper()).StartsWith( fieldValue ))
{
Contact contact = new Contact();
for( int icount=0 ; icount< itemArray1.Length; icount++ )
{
NotesItem searchedNotesItem =
(Domino.NotesItem)itemArray1.GetValue( icount );
string FieldName = searchedNotesItem.Name.ToString();
//For FirstName
if( searchedNotesItem.Name == "FirstName" )
contact.FirstName= searchedNotesItem.Text;
//For LastName
if( searchedNotesItem.Name == "LastName" )
contact.LastName = searchedNotesItem.Text;
//For Office Phone Number
if( searchedNotesItem.Name == "OfficePhoneNumber" )
contact.OfficePhoneNumber = searchedNotesItem.Text;
if( searchedNotesItem.Name == "InternetAddress" )
contact.EmailId = searchedNotesItem.Text;
}//end for
contactsList.Add( contact );
break;
}//End if
}
}
//Get the nth entry of the selected view according to the iteration.
NotesViewEntry viewEntry = notesViewCollection.GetNextEntry(viewEntry);
}
Why are you asking the user to provide his Employee ID? You should ask him to provide his Notes username (either FullName or ShortName), or his email address. Any of those can be looked up very quickly in the $Users view in names.nsf, giving you fast access to the document containing all the data that you need.
Note: I'm aware that some companies actually enter their Employee ID into the ShortName field in names.nsf. If that's the case for your organization, then what you should be doing is opening a NotesView object using the NotesDatabase.getView() method, and then use the NotesView.getDocumentByKey() method to get the document for the user. E.g., something like this:
NotesView usersView = namesDb.getView("$Users");
NotesDocument userDoc = usersView.getDocumentByKey(employeeId);
Then just read the data that you want, using userDoc.getItemValue() for each information field that you are interested in. You should only do a loop through the entire userdoc.Items array if you are really trying to capture everything, including a bunch of internal-use values.

Create a custom address book for Outlook 2010 programmatically

I'd like to create a Custom Contact List (or address book as it's called) so that in Outlook the user will be looking at "Suggested Contact", "Contacts" etc., and then, "Custom Contacts". I went off and tried the following.
int count = this.Application.GetNamespace("MAPI").AddressLists.Count;
This gives me the value of 8. So, naturally, I've tried to Add something to the address list object but guess what - there's no such method. It's nothing strange about that, since the API clearly states that it's read-only object. However, I need to set up an address book/list (whatever it's called) for my client.
How do I add a new Address Book to Outlook?
You can create an Outlook Address Book entry by using the following code:
Outlook.Folder contacts = this.Application.Session.GetDefaultFolder(Outlook.OlDefaultFolders.olFolderContacts) as Outlook.Folder;
Outlook.Folder addressBook = contacts.Folders.Add("Business Contacts", Outlook.OlDefaultFolders.olFolderContacts) as Outlook.Folder;
addressBook.ShowAsOutlookAB = true; // force display in Outlook Address Book
Outlook.ContactItem contact = addressBook.Items.Add();
contact.FullName = "Custom Industries, Inc.";
contact.Email1Address = "sales#customindustries.com";
contact.Save();

Resources