USD – Save and Restore a Session

Recently I was asked a question about Unified Service Desk for Microsoft Dynamics 365, the requirement was to be able to save a session. Meaning saving details of all the open tabs, so that someone else could re-open it later.

This could be very useful in several scenarios;

  • Say you have opened an account, case, phone call (etc etc.) but you need to park it. It could be lunchtime and you want to save it and pick up your work again after lunch.
  • Or maybe you want to transfer the current “job” over to a more experienced operator or manager. (Possibly whilst the customer is still on the phone!)

WARNING: This is a complicated USD change. My aim is to explain a design pattern that someone who has advanced USD knowledge could adapt / alter. Therefore, in this post I’m going to assume the reader fully understands USD concepts. This might not be a change you can simply lift and use! E.g. You will probably need to tweak the window navigation rules for your purposes.

Before looking at how I created this change, you may wish to view this video which shows this feature in action;

Steps Involved

Part One : Save the Session

  1. Create a hosted control
  2. Create a custom entity
  3. Create a Scriptlet (Challenge One)
  4. Create more some Scriptlets! (Challenge Two)
  5. Create a “CreateEntity” action to populate the custom entity
  6. Create a “CloseSession” action
  7. Create a toolbar, I put mine in the CTI Panel

Part Two : re-open the session

  1. Create a view of Saved Sessions
  2. Open “Saved Session” custom entity
  3. Create a bunch of actions
  4. Add actions to BrowserDocumentComplete
  5. Create some Window Navigation Rules

Step One – Create a Hosted Control

All good USD changes should start with creating a hosted control! J

Firstly, I created a hosted control called “SavedSession”, this will be used later when I’m re-opening a saved sessions.

Field Details
Name SavedSession
USD Component Type CRM Page
Hosting Type IE Process (I only ever use IE Process!)
Application is Global Must be selected.

Note: I am going to “spawn” a session from this tab opening. It is global but it will trigger sessions to open.

Display Group Hidden

The user does not need to see this! But a tip, whilst testing you might want to use MainPanel and only hide the tab when you know your change is performing correctly.

Step Two – Create a Custom Entity

Next I created a custom entity in CRM. This will be used to hold the details of all the tabs you’ll need to re-open. My USD application is actually quite a complicated one! So, I needed multiple fields on this entity. You may find you need far fewer fields!

I called my entity “Saved Session”. Notice that the ownership is “User or Team”. This is important as I will want to know who created each saved session. Later I might also want to consider security role restrictions, such as maybe only the person who owns the saved session or their manager can open it.

Note:
In my demo environment, I didn’t worry about security roles. In a production environment you will need to add this new entity into the roles used by all USD users.

Notice that I didn’t select any of the communication & collaboration options. I don’t need notes, activities etc. So, I didn’t select them!

In my solution, I have quite complex navigation. I use all activity types and most of the system entities available in CRM Dynamics 365. Plus I have multiple standard web pages that could be displayed.

This entity was essentially a list of text fields. All of the ones used for URLs had a type of url. But Initial Entity and Agent Script were just standard text fields.

A “key” field here is “Initial URL”. In this field, I will hold the url of the first tab opened in the session. Important as that will drive the context of the session when we re-open it.

Next I created a field for each of the tabs / entities that could be opened in a USD session. For readability, I grouped them under headings of Entity Tabs, Associated Views, Activities etc.

Finally, I added a section for all of the urls that related to web pages outside of USD. In my application, these included maps, results for Google searches, LinkedIn pages etc. In yours you may also have line of business applications or pages on your local intranet etc.

Obviously, my entity ended up having loads of fields! This entity might look complicated but it is actually really simple as it’s just a load of text fields.

Step Three – Create a Scriptlet (Challenge One)

Whilst creating this solution I hit a couple of challenges that were solved with scriptlets.

My first challenge was that the context does not always contain the url of the initial entity. And I needed to ensure this was captured to be able to open the session correctly.

My “SavedSessionURL_INITIAL” scriptlet simply returns the url of the initial entity. Most of the time this will be “[[$Context.url]+]” but as that replacement parameter isn’t available on activities I added in some if conditions to get those details from other parameters. (Such as “[[task.url]+]” etc.

My code looked like this ….

function SavedSessionURL_INITIAL() {
    // ** To explain ...
    // ** Generally speaking, the context will contain the url of the initially loaded record. 
   // ** So most of the time we just need "$Context.url"
   // **   BUT I found that this field doesn't work with activities. 
   // **   so when the session starts from an activity I grab the url from another replacement parameter.

    var url = "[[$Context.url]+]";
    var logicalName = "[[$Context.LogicalName]+]";

    if (logicalName == "task") {
        url = "[[task.url]+]";
    }

    if(logicalName == "appointment") {
        url = "[[appointment.url]+]";
   }

    if(logicalName == "email"){
       url = "[[email.url]+]";
    }
    if(logicalName == "phonecall") {
        url = "[[phonecall.url]+]";
    }

    return url;
}
SavedSessionURL_INITIAL();

Step Four – Create more some Scriptlets! (Challenge Two)

My next challenge was a problem with the url field on CRM entities. The screen shot below shows the replacement parameters for a newly created case. You can see that the case’s GUID is in the “Id” field. But when you look closely at the URL you can see that this Id has not been included. If we use this URL as it stands a new case form will be displayed instead of the case we just created.

Note: I will explain how / when these scriptlets are called in just a second!

So again, I turned to Scriptlets for a solution! This time I wrote a small piece of JavaScript that would insert the Id if it was missing.

function SavedSessionURL() {
  var url = "[[incident.url]+]";
  var id = "[[incident.Id]+]";
  var urlSplit = url.split("&id=&");

 if (urlSplit != "" && urlSplit != null) {
    url=urlSplit[0] + "&id=" + id + "&" +urlSplit[1];
  }

  return url
}

SavedSessionURL();

I ended up creating a scriptlet like this for each of the CRM entities that should be created in my interface. Meaning case, opportunity, phone call etc. So quite a few scriptlets were needed but each one was almost identical. ( Copy and paste time! J )

Step Five – Create a “CreateEntity” action to populate the custom entity

Now we come to the fun bit. Having created my custom entity and the scriptlets I needed to format the URLs, I now needed an action to insert a record into my custom entity. So a CreateEntity action. My action was from my CRM Global Manager and looked like this;

My data field looked like this, notice that for the initial entity url field I call the scriptlet that I described in step 3. And for each of the CRM entities I call one of the scriptlets we created in step 4.

Also notice that some urls, like the standard web pages towards the end can have their urls simply set without the need of a scriptlet.

Tip:
My publisher has “crm_” as its prefix. In your application “crm_name” (etc) might be “new_name”. This CreateEntity action is simply a list of name/value pairs. The first part being the schema name from my custom entity the second being the value to insert.

LogicalName=crm_savedsession

crm_name= [[$Context.LogicalName]+]  ([[$Context.Id]+])
crm_initialentity=[[$Context.LogicalName]+] 
crm_agentscript=[[Agent Scripting.msdyusd_name]+]
crm_initialurl=[[$Scriptlet.SavedSessionURL_INITIAL]]
crm_accounturl=[[$Scriptlet.SavedSessionURL_Account]]
crm_caseurl=[[$Scriptlet.SavedSessionURL_Case]]
crm_contacturl=[[$Scriptlet.SavedSessionURL_Contact]]
crm_leadurl=[[$Scriptlet.SavedSessionURL_Lead]]
crm_opportunityurl=[[$Scriptlet.SavedSessionURL_Opportunity]]
crm_workorderurl=[[$Scriptlet.SavedSessionURL_Workorder]]
crm_appointmenturl=[[$Scriptlet.SavedSessionURL_Appointment]]
crm_emailurl=[[$Scriptlet.SavedSessionURL_Email]]
crm_phonecallurl=[[$Scriptlet.SavedSessionURL_Phonecall]]
crm_taskurl=[[$Scriptlet.SavedSessionURL_Task]]
crm_associatedcaseurl=[[Associated Cases.url]+]
crm_associatedcontacturl=[[Associated Contacts.url]+]
crm_associatedopportunityurl=[[Associated Opportunities.url]+]
crm_associatedworkorderurl=[[Associated Work Orders.url]+]
crm_googlemapurl=[[Google Maps.url]+]
crm_googleurl=[[Google.url]+]
crm_bingurl=[[Bing.url]+]
crm_bingmapurl=[[Bing Maps.url]+]
crm_accountwebsiteurl=[[Account Website.url]+]
crm_linkedinurl=[[LinkedIn.url]+]

Step Six – Create a Close Session Action

Now I created a very simple action to close the session. As once I have saved the session details to my custom entity I would like to force it to close.

As you can see below this is a simple CloseSession action which is applied to my Session Tabs hosted control.

Notice the order of 5. My CreateEntity action had an order of 1. (Meaning I want to fire the CreateEntity action and then the CloseSession action.)

Step Seven – Create a toolbar, I put mine in the CTI Panel

Next you need a toolbar button that will contain a save session button. In my example I decided to show where the CTI Panel typically goes. (As I wasn’t using it!) You could of course add the button to any toolbar.

First I created a hosted control for my toolbar that looked like this. (Notice my display group is “CTiPanel”)

I then created a toolbar, mine is shown below. (Don’t forget to link the toolbar to the hosted control you created above! Using the hosted control option in the navigation of the toolbar.)

Next I created a toolbar button.

I gave my toolbar button an image that I found in the resources of the CRM 2016 SDK.

Notice that I have an enable and visible condition. “[[$Context.LogicalName]+]”!=”” This simply means that my button won’t show if I don’t have a session open.

And then finally I added the CreateEntity action and CloseSession actions we previously created to my button.

Below you can see a screen shot of my USD interface complete with the SAVE SESSION button in the CTiPanel.

Step Eight – Create a view of Saved Sessions

Next I wanted to have a list of the saves sessions. I’d already got a search toolbar and tab. So I decided to re-use that. You may decide to locate elsewhere in your navigation. Below you can see how my Saved Sessions option was shown in my search toolbar.

The button I created looked like this, notice that the button calls an action to “find” my Saved Session custom entity.

My find action was pretty simple. It was just a find action with the schema name of my custom entity in the data field. As shown below.

My view of saved sessions looked like this. Now at this point I will say I don’t like this view! It is functional but from an end user point of view I do not like the way I have shown the GUID of the initial entity in the name. If / when I create a production ready version of this change I will alter my CreateEntity action to set a more user friendly name to the entity.

Step Nine – Open “Saved Session” custom entity

Next I needed to open my custom entity of Saved Session from the list of sessions I’d shown in my search tab. If you recall, in step one we created a hosted control called “SavedSession”. We are going to load custom entity into that tab. Except the tab is hidden so the user will not see this happen!

To load the tab I used a window navigation rule. As shown below.

Field Description
Name Saved Session from Search
Order 1 (You may need a different order!)
From Search

As I had loaded the list of saved sessions into a tab called “Search”.

Entity crm_savedsession

The schema name of my custom entity.

Route type Popup
Action Route Window

I don’t often use a Route Window action on a global tab! What this going to do is open a global tab from the global search tab. BUT the tab is hidden!

Target tab and show tab SavedSession

The name of the hosted control we created in step one. (As hidden I guess I didn’t really need to set the show tab!)

Step Ten – Create a bunch of actions

First of all I created a popup action that will load the initial url. It is important that a popup action is used as I want this action to trigger my window navigation rules which will start my session.

Also, notice that the order is 10. A low number as I want this to be the first action that is run.

Now I created a navigation action for each of the urls that will be loaded from my custom entity. A navigate action as I want these tabs to load within my session.

The actions were simple enough but I had quite a few as I needed one for each url I had held in my custom entity.

Below you can see I have given an example of the action I used for account. Notice that the hosted control is account and the action is “navigate”. Then in the data field I have set the url to the field from my custom entity. So …. url=[[crm.savedsession.crm_accounturl]g]

I also added a condition to each of these navigate actions, as I only want to do them if I have that particular url. “[[crm_savedsession.crm_accounturl]g]”!=”” etc etc.

In my custom entity I had also stored the name of the agent script that is currently active. So I also created a agent scripting GotoTask to load this. For this one the hosted control was Agent Scripting and my action was GotoTask.

Tip:
Notice that the order is high! I want this action to run after all of my navigation actions.

Finally I created an action to close the SavedSession tab. (Which is hidden!) The main thing to notice is that the order is very high. I need this to be the last action that runs.

Step Eleven – Add actions to BrowserDocumentComplete

Next I added all of the actions I’d just created to the BrowserDocumentComplete event of my SavedSession hosted control. The logic here is;

  1. Do a popup, which will start the session.
  2. Do loads of navigate actions for all the possible tabs.
  3. Go to the agent script.
  4. Close the SavedSession hosted control.

Notice that my agent script and close actions had the highest order and therefore came last.

Step Twelve – Create some Window Navigation Rules

In your solution this step could take some thought! Depending on how you have structured you window navigation rules then you may need some to start the sessions as required. In my case I created a navigation rule for each entity that could be an initial entity.

So I had a list like this;

I opted for an order of 300, as that fitted with the structure I have in my rules. As I said at the start my USD interface is pretty complicated so I needed quite a few navigation rules. In yours you may find a much smaller number is required.

Tip: Each navigation rule also has some associated actions to expand the left panel etc. The exact format of yoru rules will differ depending on your applications functionality.

Obviously, this isn’t a simple change! But I hope you can see that it might be a useful addition to your Unified Service Desk interface.
J

4 thoughts on “USD – Save and Restore a Session

  1. Pingback: USD – The Book | Microsoft Dynamics CRM and Unified Service Desk

  2. Pingback: Tip #785: How to restore USD session | Dynamics CRM Tip Of The Day

  3. Hi,

    Interesting article. In a deployment, we have the email channel integrated within Dynamics CRM converting them automatically to cases. It can happen that the person writing the email is not the customer to whom the case needs to be associated to. When I open the case within USD, the case session is opened with the contact information appearing in the session line overview. If I then need to update the customer to be for example the account related to the contact, the session line overview still displays the contact information instead of the account session line overview. All the data within the context of USD is still related to the contact.
    Is there a way to easily reload the USD context once the customer of the case is updated? I was thinking of using the save and restore session to reload the context but it seems like a heavy solution.

    Thanks.

    Like

    • Hi Raphael

      I 100% agree that using the save / restore approach would be “heavy”. I think this would lead to a slow experience for the users. So not ideal.

      You could investigate the CopyToContext action to update the replacement parameters in the context.

      What I am not 100% sure about is if the session overview will be effected by this. I know the session name and tab names dynamically update as replacement parameters change. I am not so sure about the session overview information.

      I haven’t tried exactly the combination you describe so this might be something you just need to try and see what happens.

      Keep me posted …. I’m interested to hear how you get on.

      Neil.

      Like

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s