Saturday, 29 April 2017

Using LESS to parameterize your CSS used in web resources in Dynamics CRM

Hi all!

It has been a while that I have written something. So today I want to present to you an approach that I used to parameterize bootstrap styles CSS that we were using as a web resource embedded in a form in Dynamics.
So the problem I addressed was that since the "Themes" in Dynamics CRM are easily updated from the Dynamics GUI itself, all other web resources that were aligned with the existing color scheme of Dynamics now looked out of place as their CSS would not automatically align to the changes in the Themes from the GUI. So we needed to find a solution that would allow us to easily update the CSS colors when such updates were being made. Since we were using the Bootstrap CSS library to style our web resources, it would be a lot of effort and prone to error if we tried to manually update the CSS (by doing a find and replace) whenever such a change was made.
So here we used LESS to use variables straight into the CSS!

The below snapshots present the starting few lines of the Bootstrap LESS code where we declared the variables and the darken function to be used for the colors throughout the generated CSS


The next snapshot shows how the variables are used in the Bootstrap LESS code itself


The best place to learn the basics of LESS (this article only considers a very trivial use case) is here.

Once the Bootstrap LESS file is ready we can use any LESS "compiler". I have used the LESSC NPM package to convert the LESS code to CSS, which I then updated to Dynamics as a web resource. Details on usage and how to install the package can be found here.

The magic command (from the above link) is (assuming LESSC is installed globally):

lessc Bootstrap.less Bootstrap.css

which compiles the Bootstrap LESS file with all the variables to the Bootstrap CSS file with the variables replaced by the values or results of the functions as specified. The generated CSS can now be replaced in the web resources of Dynamics, possibly after minification with some tool. We can also use grunt to automate the tasks of compiling the LESS to CSS and then minifying the CSS, all using a single command!

Hope this helps and makes your life easier customizing the look and feel of Dynamics CRM

Cheers!

Monday, 6 February 2017

JavaScript code to validate dates in dd/mm/yyyy format

Hi all,

Here is a JavaScript function that I developed in order to validate whether the entered date is correct when they are entered in the dd/mm/yyyy format:



There are a few validations like start year, leap year (when it is a multiple of 100) that have not been performed assuming that the dates entered will be between 1950 and 2100. However, they can also be added if required. Let me know if you have any comments on the code or have any suggestions.
Thanks!

Tuesday, 10 January 2017

Generating traces to debug issues in Outlook for Dynamics CRM

Sometimes getting the error reason for errors in Outlook for Dynamics CRM can be tricky and non-trivial. Please follow the below steps to obtain the error trace in case any error occurs in Outlook for CRM:

  • In the machine where Outlook is installed please install CRM SDK. For example you can obtain CRM 2015 SDK from here.
  • Find the Diagnostics application from within the SDK as shown below:


  • Double click and open the application and select the below option as shown (keep the level to error) and click on save




4      Perform the operation from within Outlook and let the error take place
5
  •          In the background the trace will be generated in the below location (for Windows >= 7)

%%USER_PATH%%\AppData\Local\Microsoft\MSCRM\Traces
Ex:
C:\Users\johndoe\AppData\Local\Microsoft\MSCRM\Traces

Please note AppData might be hidden by default, please unhide it first to reveal the folders underneath.

  • Please open the log from the above location and verify it contains some meaningful text.In case it does not contain any, please update the Level to Verbose in step 3 and perform the next steps again


  • Once a meaningful trace file is generated in step 6, please disable the option selected in step 3, so that the local storage does not get overwhelmed with log files (especially when the trace is Verbose).

Friday, 23 December 2016

Generic code to show SharePoint documents subgrid on main entity form in Dynamics CRM

Hello!

Many times we have faced the ask from our customers to show the SharePoint document sub-grid on the main form for an entity. I have come up with a generic code that shows the SharePoint sub-grid on the main form (taking only the tab name, entity schema name, record GUID and the IFrame name).

Please note that the code is only for the sub-grids created by using the SharePoint list component and as such it is not supported as we are hard-coding the list component parameters in the URL. So use it at your own risk:

/* Show the documents tab on the main entity form based on the entities that have been provided */
/* This method has dependency on XrmServiceToolkit.js */
common.showDocumentsGridOnMainEntityForm = function (documentGridTabName, entityLogicalName, recordGuid, documentGridIframeName) {
    if (Xrm && Xrm.Page && Xrm.Page.ui) {
        var CREATE_FORM_TYPE = 1;
        var formType = Xrm.Page.ui.getFormType();
        if (formType != CREATE_FORM_TYPE) {
            debugger;
            var sharePointSitesFetch = "<fetch distinct='false' mapping='logical' output-format='xml-platform' version='1.0'>" +
                "<entity name='sharepointsite'>" +
                "<attribute name='name'/>" +
                "<attribute name='parentsite'/>" +
                "<attribute name='relativeurl'/>" +
                "<attribute name='absoluteurl'/>" +
                "<attribute name='validationstatus'/>" +
                "<attribute name='isdefault'/>" +
                "<order descending='false' attribute='name'/>" +
                "<filter type='and'>" +
                "<condition attribute='isdefault' value='1' operator='eq'/>" +
                "<condition attribute='absoluteurl' operator='not-null'/>" +
                "</filter>" +
                "</entity>" +
                "</fetch>";
            var sharePointSiteRecords = XrmServiceToolkit.Soap.Fetch(sharePointSitesFetch);
            var sharePointBaseUrl = "";
            if (sharePointSiteRecords && sharePointSiteRecords.length > 0) {
                sharePointBaseUrl = sharePointSiteRecords[0].attributes["absoluteurl"].value;
            }

            var showDocumentsTab = false;
            if (sharePointBaseUrl) {
                var documentLocationsFetch = "<fetch distinct='false' mapping='logical' output-format='xml-platform' version='1.0'>" +
                    "<entity name='sharepointdocumentlocation'>" +
                    "<attribute name='name'/>" +
                    "<attribute name='regardingobjectid'/>" +
                    "<attribute name='parentsiteorlocation'/>" +
                    "<attribute name='relativeurl'/>" +
                    "<attribute name='absoluteurl'/>" +
                    "<order descending='false' attribute='name'/>" +
                    "<filter type='and'>" +
                    "<condition attribute='regardingobjectid' value='" + recordGuid + "' uitype='" + entityLogicalName + "' operator='eq'/>" +
                    "</filter>" +
                    "</entity>" +
                    "</fetch>";
                var documentLocationsForRecord = XrmServiceToolkit.Soap.Fetch(documentLocationsFetch);
                var documentLocationRelativeUrl = "";
                if (documentLocationsForRecord && documentLocationsForRecord.length > 0) {
                    documentLocationRelativeUrl = documentLocationsForRecord[0].attributes["relativeurl"].value;
                }

                var completeFolderUrl = "";
                if (documentLocationRelativeUrl) {
                    // Sample: http://192.168.85.9:8080/crmgrid/crmgridpage.aspx?langId=en-US&locationUrl=http%3a%2f%2f192.168.85.9%3a8080%2fcontoso_pricingproposal%2f_F66CBD47B6B8E61180D9000D3AA06EB4&pageSize=250
                    completeFolderUrl = sharePointBaseUrl + "crmgrid/crmgridpage.aspx?langId=en-US&locationUrl=" + sharePointBaseUrl + entityLogicalName + "/" + documentLocationRelativeUrl + "&pageSize=250";
                    console.log("The complete folder URL is: " + completeFolderUrl);
                    completeFolderUrl = encodeURI(completeFolderUrl);
                    console.log("The complete folder URL after encoding: " + completeFolderUrl);
                }

                if (completeFolderUrl) {
                    showDocumentsTab = true;
                }

                if (showDocumentsTab) {
                    if (Xrm.Page.ui.tabs) {
                        var documentGridTab = Xrm.Page.ui.tabs.get(documentGridTabName);
                        if (documentGridTab) {
                            documentGridTab.setVisible(true);
                            var documentIFrame = Xrm.Page.ui.controls.get(documentGridIframeName);
                            if (documentIFrame != null) {
                                documentIFrame.setSrc(completeFolderUrl);
                            }
                        }
                    }
                }
            }
        }
    }
}


Sample usage code:

/* Method to call common method to show sharepoint documents in the main entity form*/
pricingproposal.showDocumentsInMainform = function () {
    var recordGuid = Xrm.Page.data.entity.getId();
    if (recordGuid && common.isValidGuidHelper(recordGuid)) {
        common.showDocumentsGridOnMainEntityForm("tab_DocumentsTab", "contoso_pricingproposal", recordGuid, "IFRAME_Documents");
    }
}

As part of the configurations we need to create a tab in the main form inside which we will have an IFrame. These names will be part of the function parameter as shown above.

Let me know if you have any questions.

Merry Christmas and happy Dynamics!


Monday, 31 October 2016

XRMToolBox plugin to convert from FetchXML to C#/JavaScript string

Hello all,

Recently I had created a plugin for XrmToolBox that converts from FetchXML to C#/JavaScript string to help with Dynamics CRM development.
You can find screenshots of the tool here.

In case you want to use the tool (in case you are offline) and do not want to use the online version here, then these are the 2 ways to install the tool in your XrmToolBox:

1. As a Nuget package from the plugins store: Please search for the plugin with the name "Fetch XML To String Converter plugin". This is applicable only for the latest releases of XrmToolbox.

2. Directly install and save the DLL of this plugin in the plugin location used by XrmToolBox.
Please download the version (based on your version of XrmToolBox) from here.

Tuesday, 11 October 2016

Web application to convert FetchXML to C#/JavaScript string

Hi all,

Many times we have the requirement to convert the Fetch XML that we download from the Dynamics CRM advanced find window to strings that are valid for C#/ JavaScript so that we can use them in our Fetch expressions, custom views, etc. I have made a web tool that will easily allow you to perform the conversion without having to manually do find/replace and other operations.
The web tool can be found here: https://ace-racer.github.io/FetchXmlToString/


The tool is proudly hosted by Github pages!

Hope you find the tool helpful!

Thanks!!

Starting with PowerShell for Dynamics CRM

Hi all,

Recently I had a requirement to change a deployment setting in Dynamics CRM. The issue was that this setting could either be modified by updating a flag in the CRM database or using a powershell script but not from any convenient UI. In my quest to solve this issue, I uncovered how to get Powershell configured so that we can use the common Dynamics CRM cmdlets. I will describe those steps as well as give the script that will help to update obscure deployment settings for CRM Online (when there is no access to the CRM database)


Configuring Power Shell with Dynamics CRM: The steps in details are mentioned here.
Once Power Shell is configured, we will focus on the steps to update the deployment settings:

# on premises instance
$CRM_URL = "http://IP:PORT";
$ORG_NAME = "contosoprod";

#Obtain the credentials in the pop up
$cred = Get-Credential 

# Get the connection object (below will change based on deployment type - refer to above link)
$conn = Get-CrmConnection -ServerUrl $CRM_URL -Credential $cred -OrganizationName $ORG_NAME;

# add snap in
Add-PSSnapin Microsoft.Crm.PowerShell;

# get the present team settings 
$set = Get-CrmSetting -SettingType TeamSettings;

# update the required setting (not yet transacted to database)
$set.MaxEntitiesEnabledForAutoCreatedAccessTeams = 8;

# set the required setting (database updated)
Set-CrmSetting -Setting $set;

# view the updated settings
Get-CrmSetting -SettingType TeamSettings;

Here we have updated the MaxEntitiesEnabledForAutoCreatedAccessTeams property in the TeamSettings. However, there exist a host of settings that can be updated using the same procedure as described above. The full list can be found here.

Hope this starting tutorial helps you realize the potential of infusing Powershell with Dynamics CRM!

Let me know your views!

Thanks!