Sticky

This blog has moved to www.dreamingincrm.com. Please update your feed Url. Thank you.

22 July 2014

Retrieving Server Datetime using Javascript

I recently saw a forum post about retrieving server datetime using Javascript. My initial impressions were
1.) It is a bad idea to do this
2.) It is not possible

I attended DDDMelbourne recently and there was a talk about why bad ideas are the best ideas and so I set about trying to get this working. This looked like it could be done using Actions.

Problem No 1:
The simplest approach would be create a new Action with a Datetime output parameter and set this to Process' Execution Time. I did do this and here is when I found this first issue. CrmDatetime is STILL present in CRM 2013. Here is what I got when I executed the Action from the console.



Create a custom activity
This meant that I have to set the output Datetime parameter using a custom activity. This is the custom activity code. It is pretty simple. All it does it set the various output parameters of the Action.

namespace ServerTimeActivity
{
    using System;
    using System.Activities;
    using System.ServiceModel;
    using Microsoft.Xrm.Sdk;
    using Microsoft.Xrm.Sdk.Workflow;

    public sealed class RetrieveServerTimeActivity : CodeActivity
    {
        /// <summary>
        /// Executes the workflow activity.
        /// </summary>
        /// <param name="executionContext">The execution context.</param>
        protected override void Execute(CodeActivityContext executionContext)
        {
            // Create the tracing service
            ITracingService tracingService = executionContext.GetExtension<ITracingService>();

            if (tracingService == null)
            {
                throw new InvalidPluginExecutionException("Failed to retrieve tracing service.");
            }

            tracingService.Trace("Entered RetrieveServerTimeActivity.Execute(), Activity Instance Id: {0}, Workflow Instance Id: {1}",
                executionContext.ActivityInstanceId,
                executionContext.WorkflowInstanceId);

            // Create the context
            IWorkflowContext context = executionContext.GetExtension<IWorkflowContext>();

            if (context == null)
            {
                throw new InvalidPluginExecutionException("Failed to retrieve workflow context.");
            }

            tracingService.Trace("RetrieveServerTimeActivity.Execute(), Correlation Id: {0}, Initiating User: {1}",
                context.CorrelationId,
                context.InitiatingUserId);

            IOrganizationServiceFactory serviceFactory = executionContext.GetExtension<IOrganizationServiceFactory>();
            IOrganizationService service = serviceFactory.CreateOrganizationService(context.UserId);

            try
            {
                //TimeZoneInfo.ClearCachedData();
                //System.Globalization.CultureInfo.CurrentCulture.ClearCachedData();

                ServerDateTime.Set(executionContext, DateTime.Now);
                ServerDateTimeString.Set(executionContext, DateTime.Now.ToString(System.Globalization.CultureInfo.CurrentCulture));
                ServerTimezoneUtcOffset.Set(executionContext, TimeZoneInfo.Local.BaseUtcOffset.TotalMinutes);
                ServerTimezone.Set(executionContext, TimeZoneInfo.Local.ToString());
            }
            catch (FaultException<OrganizationServiceFault> e)
            {
                ServerDateTime.Set(executionContext, e.StackTrace);
                tracingService.Trace("Exception: {0}", e.ToString());

                // Handle the exception.
                throw;
            }

            tracingService.Trace("Exiting RetrieveServerTimeActivity.Execute(), Correlation Id: {0}", context.CorrelationId);
        }

        [Output("Server Datetime")]
        public OutArgument<DateTime> ServerDateTime { get; set; }

        [Output("Server Datetime String")]
        public OutArgument<string> ServerDateTimeString { get; set; }

        [Output("Server Timezone")]
        public OutArgument<string> ServerTimezone { get; set; }

        [Output("Server Timezone UTC Offset")]
        public OutArgument<double> ServerTimezoneUtcOffset { get; set; }

        [Output("Exception Stack Trace")]
        public OutArgument<string> ExceptionStackTrace { get; set; }
    }
}

The next step is to use this custom activity inside the Action. Here is what my Action looks like

Once this done we have generate the JS code for this action using the Sdk.Soap.js Action Message Generator. Generate the JS code for all the actions by executing the Sdk.SoapActionMessageGenerator.exe.

In order to use this in a form we need two scripts

1.) Sdk.Soap.js
2.) Sdk.new_ServerDateTime.min.js -> This is the name of my action

Add these scripts as webresource and reference them in the form or ribbon button that will require this functionality. Be mindful of the load sequence, as this not guaranteed due to the async nature of the script load. Follow this excellent tutorial from CRM MVP Scott Durow if you want to manage this issue, or refer to my earlier post about using require.js.

This is the client side JS you can use.
/// 
/// 
(function() {
    Sdk.Async.execute(new Sdk.new_ServerDateTimeRequest(),function(response) {
        console.log('Datetime String ' + response.getServerDatetimeString());
        console.log('Server UtcOffset ' + response.getServerUtcOffset());
        console.log('Server Datetime ' + response.getServerDateTime());
        console.log('Server Timezone String ' + response.getServerTimezone());
        
    }, function (message) { alert(message); });
})();
I ran the script from Chrome DevTools console and here is how the output looks like.
Observations
1.) The CRMOnline server that I am using is in APAC, but the server is in UTC timezone
2.) If the output parameter of an action is DateTime, it always returns local time and not the server time
3.) If the Datetime is converted to string then the server time is returned
4.) The culture on the CRMOnline server is en-US
5.) Assign value will throw an exception if the value being assigned is null

I hope this was helpful in understanding how awesome Actions and Sdk.Soap.js library are.

17 July 2014

Broken SVGs while running the Polymer tutorial? Fiddler to the rescue

I am a big fan of Javascript and the Polymer Project. The Webcomponents and ES6 content however generally seem to be targeting the OSX/Sublime setup. So some of the steps in the blogs/tutorials don't work straight away in VS2012/Win8.

The polymer project tutorial specifically says this regarding SVG images rendering:

Note: On Windows, Python’s simple HTTP server may not provide the correct MIME type for SVG images. If the images don’t render, try a different web server.

I am using ASP.Net development server to run the samples and the SVG images are rendered as broken icons.


In order to fix the SVG rending you'll just have to add the following Response Headers when the request is for a SVG.

Content-Type: image/svg+xml
Vary: Accept-Encoding

It is easy to do this Fiddler, instead of trying to install a different HTTP Server. In order to edit the response rules, select Rules -> Customize Rules


Add the following lines inside the OnBeforeResponse method

 if(oSession.uriContains("svg")){
    oSession.oResponse["Content-Type"] = "image/svg+xml";
    oSession.oResponse["Vary"] = "Accept-Encoding";
 }

The SVG images will now be rendered properly. Here is the response with the additional headers injected from Fiddler.

15 July 2014

Highlighting specific records in a view

I recently had a requirement to highlight certain records that are displayed using a System View. It is not possible to use different colours in a supported way to accomplish this. It is possible to rollout your own custom grid using libraries like SparkleXrm and embed this on a form using IFrames. The only problem with this is you still can't highlight a record in System View using SparkleXrm. While it is possible to create a new system view that contains just the records that need to be highlighted, this is not a true highlighting.

A quick solution is to use a simple text attribute, and store a unicode character. These are the miscellaneous symbols in Unicode. You could use the "★" symbol for highlighting the record.

Here is screenshot of what I have done.


All I have done is added a simple text attribute to the entity, and stored "★" character in records that need to be highlighted, and added this to the System View that needs this highlighting. A workflow or business rule could be used to set this attribute based on the highlighting logic. This approach can be used to quickly "highlight" certain records. If you have done this a different way, I would love to hear your approach.

2 July 2014

Setting a Office365 user account password to never expire

Microsoft Dynamics CRM Online user accounts are managed through the Office 365 portal. It might be a requirement is some scenarios, to use some of the user accounts, as service accounts e.g. a customer facing portal talking to CRM using this specific account. The default password policy for Office365 user accounts can be found in the Service Settings area.

The user notification that gets sent after the password expires is not an email notification, as one would expect, but a notification they see after signing in. Here is the relevant bit from Office365 help:

How are users notified that their password will expire? 

Users see a message whenever they sign in, starting at the number of days before password expiration that you specified. The message shows the number of days left before the password expires and gives a link to the Change password page. For more information, see Change your password.

Since the service account is not used for logging in, and is used only for integration with CRMOnline, this message will never been seen. Hence the user account password nearing expiry can go unnoticed, until it is too late. To make a user account password as "Never Expires", please follow this process below:

1.) Install Powershell, if it not already installed
2.) Install Microsoft Online Services SignIn Assistant
      http://www.microsoft.com/en-au/download/details.aspx?id=28177
3.) Install Windows Azure Active Directory Module for Powershell

4.) Open the Windows Azure Active Directory Module for Powershell
5.) Type Connect-MsolService, press Enter and type in your credentials for the CRMOnline instance

6.) Type Get-MsolUser -UserPrincipalName <enter userlogin> | Select PasswordNeverExpires to get the current status of the PasswordNeverExpires setting
7.) Type Set-MsolUser -UserPrincipalName <enter userlogin> -PasswordNeverExpires $true to set the PasswordNeverExpires setting to True.
8.) Confirm if the command has worked correctly.

Here is the screenshot of all the operations:


Now, the password for this user account will not expire, and can be used for integration purposes indefinitely.