Sunday, May 10, 2015

CTI integration between CRM USD and Microsoft Lync - Part 1

Last year, Microsoft released USD (Unified Service Desk) for CRM 2013. USD is a framework to build contact centre applications to provides a unified view of customer data stored in CRM and other applications.
It provides the adapters and modules to manipulate CRM UI elements, agent scripting, configuration toolbars and a CTI adapter.  For more information on USD please check https://technet.microsoft.com/en-us/library/dn646899(v=crm.6).aspx
For this tutorial, we are only interested in CTI adapters. The following 2 types of adapters can be used to integrate USD with CTI systems.
  • Generic listener adapter:
    USD provides this adapter out of the box. This adapter listens for HTTP request on a port 5000 ( http://localhost:5000/). On receiving a request, the generic listener adapter extracts a query string from the URL and uses the query string as parameters to raise a CTI screen pop up in USD.


  • Custom CTI adapter:
    User Interface Integration(UII) CTI framework provides the component to build custom CTI adapters. The customer adapter can provide a lot of functionality to manage calls and agent state. Here is the link on how to build a custom CTI adapter for USD.

  • We will be using the generic listener adapter to achieve USD and Lync integration.

    Prerequisite

    1. Access to CRM2013 (I m using a on premise CRM)
    2. Lync client installed on the development machine 
    3. Microsoft Lync  2013 SDK installed on the development machine (Download)
    4. USD (Unified Service Desk) installed on the development machine
      The following article lists the step by step instructions to install USD.
      https://technet.microsoft.com/en-au/library/dn646908.aspx.  The USD installation involves:
      • Installing USD client (UnifiedServiceDesk.msi)
      • Deploying USD Solution using Package Deployer (CRM2013-USD-PackageDeployer.exe) For this tutorial, deploy “CRM 2013 SP1” solution

    Process Flow Diagram


    image
    1. Customer makes a phone call
    2. Phone call pop up appears in the Lync Client
    3. A custom middleware application receives the conversation event and posts a HTTP request on “http://localhost:5000” with customer phone number.
      http://localhost:5000/?phone=+61456564323
    4. USD generic listener pickup the event, extract the query parameter and use these parameters to pop contact record

    Configuring the generic listener adapter in CRM

    Make sure that all the prerequisite steps have been completed
    1. Logon to CRM  and navigate to “Settings>>Hosted Controls”
    2. Click “New” enter the fields as shown in the screen shot below

      image
      USD component, Display Group,Assembly URL and Assembly Type are the important fields and should contain the exactly same values.
      Assembly URL : Microsoft.Crm.UnifiedServiceDesk.GenericListener
      Assembly Type: Microsoft.Crm.UnifiedServiceDesk.GenericListener.DesktopManager
    3. Save the record.

    Creating a Lync Connector (Middleware application)

    A Lync connector application will capture the conversation event of the Lync client when a phone call is made or received.  This application is a client side application.
    I am creating the Lync connector as a WPF application.  I don’t need a user interface for the connector for this blog but may use it add more functionality to it in future.
    1. Create a new WPF application project and name it “LyncConnector”.

      image
    2. Add “Microsoft.Lync.Model.dll” as reference to the project. The file will be available in the following location.
      \Microsoft Lync\SDK\Assemblies\Desktop

      image
    3. The following code is going in the “MainWindow.xaml.cs file.
      using System;
      using System.Collections.Generic;
      using System.Linq;
      using System.Text;
      using System.Threading.Tasks;
      using System.Windows;
      using System.Windows.Controls;
      using System.Windows.Data;
      using System.Windows.Documents;
      using System.Windows.Input;
      using System.Windows.Media;
      using System.Windows.Media.Imaging;
      using System.Windows.Navigation;
      using System.Windows.Shapes;
      using Microsoft.Lync.Model;
      using Microsoft.Lync.Model.Conversation;
      using System.Net;
      using System.Text.RegularExpressions;
      using System.Collections.Specialized;
      
      namespace LyncConnector
      {
          /// 
          /// Interaction logic for MainWindow.xaml
          /// 
          public partial class MainWindow : Window
          {
              private LyncClient _lyncClient;
      
              public MainWindow()
              {
                  InitializeComponent();
      
                  //Get the link client- Make sure Lync client is running
                  _lyncClient = LyncClient.GetClient();
      
                  // Invoke the conversation event when the conversation starts in Lync Client
                  _lyncClient.ConversationManager.ConversationAdded +=
                      new EventHandler(
                          OnConversationAdded);
      
              }
              void OnConversationAdded(object sender, Microsoft.Lync.Model.Conversation.ConversationManagerEventArgs e)
              {
                  // Get the properties for this conversation.
                  IDictionary properties = e.Conversation.Properties;
      
                  // Get the Contact object for the person initiating the conversation.
                  Contact inviterContact = e.Conversation.Properties[ConversationProperty.Inviter] as Contact;
      
                  // Get the URI of the inviter.
                  string inviterUri = inviterContact.Uri;
      
                  // Try to match a phone number in the URI.
                  Regex phoneRegex = new Regex(@"\+(\d+)");
                  Match phoneMatch = phoneRegex.Match(inviterUri);
      
                  string callerId = string.Empty;
                  
                  if (phoneMatch.Success)
                  {
                      // Load the data only if the caller is from a PSTN number.
                      callerId = phoneMatch.ToString();
                      LoadDataForCaller(callerId);
                  }
              }
              private void LoadDataForCaller(string callerId)
              {
                            
                  string UsdLocalAddress = "http://localhost:5000";
      
                  using (var wb = new WebClient())
                  {
                      var data = new NameValueCollection();
                      data["telephone1"] = callerId;
      
      
                      try
                      {
                          if (Uri.IsWellFormedUriString(UsdLocalAddress, UriKind.RelativeOrAbsolute))
                              wb.UploadValues(new Uri(UsdLocalAddress), "POST", data);
                      }
                      catch (System.Exception ex)
                      {
                          //System.Windows.MessageBox.Show(ex.Message, "Failed to send call event");
                      }
                  }
                 
              }
          }
      }
      

    4. Compile  and Run the code



    Test the integration



    1. Make sure Lync client is running
    2. Make sure Lync Connector application is running.
    3. Logon to USD and click on the setting >>Debug

      image

    4. Delete all the entries in the “Action Calls” tab by clicking the delete button as shown in the screen shot below.

      image

    5. Make a phone call or ask someone to ring you on Lync. If everything is working properly,  a new event will appear in Action Calls tab as shown in the screen shot.

       image



    So here you go, we have successfully passed the caller's phone number to USD.
    That is it for today. In the next tutorial we will use this phone number to pop up the contact record.

    Tuesday, March 31, 2015

    How to create a message in a Scribe Insight queue using C#

    If you are familiar with Scribe Insight then you would know that “queue” based integration is the preferred integration method. The reason for that is performance. Multiple message processors can be allocated to process the queues. For more information please read this blog on the “Message Processor”.

    Scribe Adapters can create the queue message automatically, but, what if we need to create the queue message from the portal or a custom program? This blog will show you how to create a MSMQ message using C#.

    The code below will create a message with the label "PortalCustomer" and put it in the ScribeIn queue.

    using System;
    using System.Messaging;
    
    namespace MSMQ
    {
        class Program
        {
            static void Main(string[] args)
            {
                Console.WriteLine("Initiating MSMQ object");
               
               // queue address
                string queueAddress = @"FormatName:Direct=OS:machinename\private$\scribein";
                
                Console.WriteLine("Queue Path: {0}", queueAddress);
    
                //Create an instance of MSMQ Manager
                MSMQManager msmq = new MSMQManager(queueAddress);
                msmq.SendMessageToQueue();
    
                Console.WriteLine("Message sent successfully");
                Console.ReadLine();
            }
        }
    
        // Class that initiate the message queue and send the message
        class MSMQManager
        {
            private string queueAddress;
    
            public MSMQManager(string queueAddress)
            {
                this.queueAddress = queueAddress;
            }
    
    
            //Creating and sending the message
            public void SendMessageToQueue()
            {
                MessageQueue msMq = new MessageQueue(queueAddress);
    
                try
                {
                   //create an instance of customer class
                    Customer req = new Customer()
                    {
                        FirstName="John",
                        LastName="Smith",
                        ReceivedDate=DateTime.Now
    
                    };
                    
                    Console.WriteLine("Creating customer request: {0}", req.ToString());
    
                    //create the message
                    Message m = new Message()
                    {
                        
                        Body = req,
                        Label = "PortalCustomer",
                        Recoverable = true,
                       
                    };
               
                    Console.WriteLine("Initialising message: {0}", m.Label);
    
                    Console.WriteLine("Sending...");
                    msMq.Send(m);
                }
    
                catch (MessageQueueException e)
                {
                    Console.Write(e.Message);
                }
                catch (Exception e)
                {
                    Console.Write("Error Message: {0}. Trace: {1}", e.Message, e.StackTrace);
                }
                finally
                {
                    msMq.Close();
                }
            }
                  
        }
    }
    
     
    Here is the code for the customer class.

    using System;
    
    namespace MSMQ
    {
        [Serializable]
        public class Customer
        {
            public string FirstName { get; set; }
            public string LastName { get; set; }
            public DateTime ReceivedDate { get; set; }
    
            public override string ToString()
            {
                return string.Format("Link: {0} - Response: {1} - ReceivedDate: {2}", FirstName, LastName , ReceivedDate.ToString());
            }
        }
    }
    
    

    Run the code and look in the ScribeIn queue or in the Dead Message queue (if you don’t have any integration using label “PortalCustomer”).

    image

    Make sure that MSMQ are installed on the machine where the code is hosted, and can communicate  with Scribe server on port 1801.

    Thursday, February 5, 2015

    Microsoft CRM publisher stuck on “Initializing” in Scribe Insight

    If you are trying to create a CRM publisher in Scribe Insight and it is stuck on “Initializing” as shown in the screen shot below:
    image
    It means that there is something wrong.

    Cause

    If you check in the event viewer, you may find the following error:
    An unexpected error occurred.
    Microsoft Dynamics CRM Publisher
    http://fwocrm2013.edc.hosts.devnetwork/
    Error setting publisher settings: System.Messaging.MessageQueueException (0x80004005): Invalid queue path name.
       at System.Messaging.MessageQueue.ResolveFormatNameFromQueuePath(String queuePath, Boolean throwException)
       at System.Messaging.MessageQueue.Exists(String path)
       at Scribe.AdapterBase.Publisher.QueueService.InitPublisherQueues(String pubInQueueName, String pubSucceededQueueName, String pubFailedQueueName)
       at Scribe.AdapterBase.Publisher.ScribePubWorkerBase.InitPublisherQueues()
       at Scribe.AdapterBase.Publisher.ScribePubWorkerBase.setSettings(String settingsXML, String entitiesXML, Object lastRunTime, Object lastRunTimeUTC)
    The error suggests that there is problem with the path of the queue

    Solution

    Compare the queue names in the CRM publisher and message queues in component services as shown in the following screen shots.
    image image
    If the queue names are different then we have to update the queue names in the internal database.
    Note: I don’t know if this is a supported solution.
    1. Backup your ScribeInternal database.
    2. Look into the scribe.bridges table and find the bridgeid of your CRM publisher. All the publisher settings including the queues are stored in the settings column.
      image
    3. Now update the settings column with the proper queue paths.
    Note: Settings column stores the data as XML. Please be careful when updating the column.

    Wednesday, January 28, 2015

    'Security Negotiation Exception' during plug-in execution in CRM2013

    A few months ago, we experienced an issue during a plugin execution in the dev. environment. We were using Scribe Insight for integration. We were using the “Microsoft Dynamics CRM publisher” for scribe that registered a plugin on the contact entity. Everything was working perfectly until we received the following error:

    Unhandled Exception: System.ServiceModel.FaultException`1[[Microsoft.Xrm.Sdk.OrganizationServiceFault, Microsoft.Xrm.Sdk, Version=6.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35]]: The following error has occurred in the Scribe Change History plug-in: System.ServiceModel.Security.SecurityNegotiationException: Microsoft Dynamics CRM has experienced an error. Reference number for administrators or support: #0CCC3255  

    Cause


    During troubleshooting we found the following entry in the event viewer of the backend server. 

    The Kerberos client received a KRB_AP_ERR_MODIFIED error from the server serviceaccountname. The target name used was HTTP/CRMFrontEndServer. This indicates that the target server failed to decrypt the ticket provided by the client. This can occur when the target server principal name (SPN) is registered on an account other than the account the target service is using. Ensure that the target SPN is only registered on the account used by the server. This error can also happen if the target service account password is different than what is configured on the Kerberos Key Distribution Center for that target service. Ensure that the service on the server and the KDC are both configured to use the same password. If the server name is not fully qualified, and the target domain (DomainName) is different from the client domain (DomainName), check if there are identically named server accounts in these two domains, or use the fully-qualified name to identify the server 

    The “EventSourceName” was Kerberos.

    Looking at the error we hypothesised that there was an SPN issue for the CRMFrontEndServer server.

    You can receive this error for any plugin published in the sandbox. It will only happen if the sandbox service is not installed on the same server as CRM web site and kerberos authentication is enabled for the website.
     

    Resolution


    We used the following account to list all the SPNs registered for the serviceaccountname.
    setspn –l domain\serviceaccountname

    The command displays only the SPN registered on the HTTP/CRMfrontEndServer.CRM.domainname.com  (Fully Qualified domain name of the CRM front end server).

    We added a new SPN on the servername using the following command setspn –a http/CRMFrontEndServer domain\serviceaccountname That was it. We restarted the sandbox service and the error was gone.

    For more information on SPNs and their configuration you can check out this KB article from Microsoft. http://support.microsoft.com/kb/929650 

















    Wednesday, December 3, 2014

    Strange behaviour of the two options(Boolean) field in CRM2013

    Last week, I came across a strange behaviour of the two options field in CRM2013. I am not sure if it is a bug or it is by design.

    Scenario

    I have a boolean field on an entity. This field is not added to the form. The default value of the field is set to false. When a new record is created in CRM, the integration process copies the record into the external system and updates the boolean field to true in the CRM. This boolean field indicates if the record is copied to the external system or not.

    It works perfectly expect in the following scenario.

    If the user updates the  record immediately after creating the record without closing the form, CRM will reset the value of the boolean field back to false. According to me, it is a bug. CRM should not pass the un modified fields to the save method.

    The following screen shot displays the audit history of the field.

    image

    Solution

    Add the field to the form.

    Monday, November 24, 2014

    InitializeFromRequest in CRM2013

    InitializeFromRequest may be the least used request in the Dynamics CRM. It is available since CRM 4.0. I was talking to some of the guys in my team and most of them have never used this request. It initiates an instance of an entity from an existing entity.

    It can be used when there is a 1:N relationship between the entities. It is equivalent of creating a child record from the parent form.

    For example there is a 1:N relationship between account and contact. if you create a contact from the contact subgrid from the account form, it will prefill the parentcustomer field and all the fields defined in the relationship mappings.

    The following screen shot displays the mappings of 1:N relationship between account and contact.
    image
    The same functionality can be achieved using InitializeFromRequest. This request returns a InitializeFromResponse. The request does not create a new record but the response can be used to create a new record.
    Here is the code.
        // Create the request object
         InitializeFromRequest initialize = new InitializeFromRequest();
    
         // Set the properties of the request object
         initialize.TargetEntityName = "contact";
         
    
         // Create the EntityMoniker
         initialize.EntityMoniker = new EntityReference("account", new Guid("8A5D8108-DE3B-E311-9401-00155D1B7B00"));
        
         // fields to initialised from parent entity
         initialize.TargetFieldType = TargetFieldType.All;
    
         // Execute the request
         InitializeFromResponse initialized =
             (InitializeFromResponse)_serviceProxy.Execute(initialize);
    
         if (initialized.Entity != null)
         {
             //get entity from the response
             Entity entity = initialized.Entity;
    
             // set the name for the contact
             entity.Attributes.Add("firstname", "John");
             entity.Attributes.Add("lastname", "Smith");
    
             //create a new contact
             _serviceProxy.Create(entity);
    
         }
            
    Happy coding..

    Saturday, August 9, 2014

    Setup and Deployment guide for ‘Performance Toolkit’ for Dynamics CRM 2011

    Performance Toolkit for Microsoft Dynamics CRM 2011 is available on ‘Microsoft Pinpoint’. But the hyperlink for ‘setup and deployment’ guide is not working. It always displays the following message.
    service unavailable 

    I have found an old copy of the guide. You can download the guide from here.

    Thanks.