Get and set user timezone in cookies with jQuery

Scenario:

  1. Find the user timezone from it’s browser default time and then get the confirmation from the user about it’s timezone.
  2. User should be able to select different time zone
  3. Store the timezone in a cookie
  4. Perform the above procedure only if the user is logged in (asp.net identity)

Solution:

  • Using jQuery check if user is authenticated (asp.net identity)
  • If authenticated check if the timezone cookie is already set
  • If the cookie is not set yet, get confirmation from user about it’s timezone using bootstrap modal and store it in a cookie

 

Add a bootstrap modal form to your page with timezone drop-down list:

 <div class='modal fade' id='modalTimeZoneApprove' tabindex='-1' role='dialog' aria-labelledby='myModalLabel'>
 <div class='modal-dialog' role='document'>
 <div class='modal-content'>
 <div class='modal-header'>
 <button type='button' class='close' data-dismiss='modal' aria-label='Close'><span aria-hidden='true'>&times;</span></button>
 <h4 class='modal-title'>Approve Your TimeZone</h4>
 </div>
 <div class='modal-body'>
 <p>Your default time zone is : <span id="spanTimeZone"></span></p>
 <br />
 <p>You can change your timezone: </p>
 <select class="form-control" id="zoneList">
 <option value="-13">--Select--</option>
 <option value="-12">(GMT-12:00) International Date Line West</option>
 <option value="-11">(GMT-11:00) Midway Island, Samoa</option>
 <option value="-10">(GMT-10:00) Hawaii</option>
 <option value="-9">(GMT-09:00) Alaska</option>
 <option value="-8">(GMT-08:00) Pacific Time (US & Canada)</option>
 <option value="-8">(GMT-08:00) Tijuana, Baja California</option>
 <option value="-7">(GMT-07:00) Arizona</option>
 <option value="-7">(GMT-07:00) Chihuahua, La Paz, Mazatlan</option>
 <option value="-7">(GMT-07:00) Mountain Time (US & Canada)</option>
 <option value="-6">(GMT-06:00) Central America</option>
 <option value="-6">(GMT-06:00) Central Time (US & Canada)</option>
 <option value="-6">(GMT-06:00) Guadalajara, Mexico City, Monterrey</option>
 <option value="-6">(GMT-06:00) Saskatchewan</option>
 <option value="-5">(GMT-05:00) Bogota, Lima, Quito, Rio Branco</option>
 <option value="-5">(GMT-05:00) Eastern Time (US & Canada)</option>
 <option value="-5">(GMT-05:00) Indiana (East)</option>
 <option value="-4">(GMT-04:00) Atlantic Time (Canada)</option>
 <option value="-4">(GMT-04:00) Caracas, La Paz</option>
 <option value="-4">(GMT-04:00) Manaus</option>
 <option value="-4">(GMT-04:00) Santiago</option>
 <option value="-3.5">(GMT-03:30) Newfoundland</option>
 <option value="-3">(GMT-03:00) Brasilia</option>
 <option value="-3">(GMT-03:00) Buenos Aires, Georgetown</option>
 <option value="-3">(GMT-03:00) Greenland</option>
 <option value="-3">(GMT-03:00) Montevideo</option>
 <option value="-2">(GMT-02:00) Mid-Atlantic</option>
 <option value="-1">(GMT-01:00) Cape Verde Is.</option>
 <option value="-1">(GMT-01:00) Azores</option>
 <option value="0">(GMT+00:00) Casablanca, Monrovia, Reykjavik</option>
 <option value="0">(GMT+00:00) Greenwich Mean Time : Dublin, Edinburgh, Lisbon, London</option>
 <option value="1">(GMT+01:00) Amsterdam, Berlin, Bern, Rome, Stockholm, Vienna</option>
 <option value="1">(GMT+01:00) Belgrade, Bratislava, Budapest, Ljubljana, Prague</option>
 <option value="1">(GMT+01:00) Brussels, Copenhagen, Madrid, Paris</option>
 <option value="1">(GMT+01:00) Sarajevo, Skopje, Warsaw, Zagreb</option>
 <option value="1">(GMT+01:00) West Central Africa</option>
 <option value="2">(GMT+02:00) Amman</option>
 <option value="2">(GMT+02:00) Athens, Bucharest, Istanbul</option>
 <option value="2">(GMT+02:00) Beirut</option>
 <option value="2">(GMT+02:00) Cairo</option>
 <option value="2">(GMT+02:00) Harare, Pretoria</option>
 <option value="2">(GMT+02:00) Helsinki, Kyiv, Riga, Sofia, Tallinn, Vilnius</option>
 <option value="2">(GMT+02:00) Jerusalem</option>
 <option value="2">(GMT+02:00) Minsk</option>
 <option value="2">(GMT+02:00) Windhoek</option>
 <option value="3">(GMT+03:00) Kuwait, Riyadh, Baghdad</option>
 <option value="3">(GMT+03:00) Moscow, St. Petersburg, Volgograd</option>
 <option value="3">(GMT+03:00) Nairobi</option>
 <option value="3">(GMT+03:00) Tbilisi</option>
 <option value="3.5">(GMT+03:30) Tehran</option>
 <option value="4">(GMT+04:00) Abu Dhabi, Muscat</option>
 <option value="4">(GMT+04:00) Baku</option>
 <option value="4">(GMT+04:00) Yerevan</option>
 <option value="4.5">(GMT+04:30) Kabul</option>
 <option value="5">(GMT+05:00) Yekaterinburg</option>
 <option value="5">(GMT+05:00) Islamabad, Karachi, Tashkent</option>
 <option value="5.5">(GMT+05:30) Sri Jayawardenapura</option>
 <option value="5.5">(GMT+05:30) Chennai, Kolkata, Mumbai, New Delhi</option>
 <option value="5.75">(GMT+05:45) Kathmandu</option>
 <option value="6">(GMT+06:00) Almaty, Novosibirsk</option>
 <option value="6">(GMT+06:00) Astana, Dhaka</option>
 <option value="6.5">(GMT+06:30) Yangon (Rangoon)</option>
 <option value="7">(GMT+07:00) Bangkok, Hanoi, Jakarta</option>
 <option value="7">(GMT+07:00) Krasnoyarsk</option>
 <option value="8">(GMT+08:00) Beijing, Chongqing, Hong Kong, Urumqi</option>
 <option value="8">(GMT+08:00) Kuala Lumpur, Singapore</option>
 <option value="8">(GMT+08:00) Irkutsk, Ulaan Bataar</option>
 <option value="8">(GMT+08:00) Perth</option>
 <option value="8">(GMT+08:00) Taipei</option>
 <option value="9">(GMT+09:00) Osaka, Sapporo, Tokyo</option>
 <option value="9">(GMT+09:00) Seoul</option>
 <option value="9">(GMT+09:00) Yakutsk</option>
 <option value="9.5">(GMT+09:30) Adelaide</option>
 <option value="9.5">(GMT+09:30) Darwin</option>
 <option value="10">(GMT+10:00) Brisbane</option>
 <option value="10">(GMT+10:00) Canberra, Melbourne, Sydney</option>
 <option value="10">(GMT+10:00) Hobart</option>
 <option value="10">(GMT+10:00) Guam, Port Moresby</option>
 <option value="10">(GMT+10:00) Vladivostok</option>
 <option value="11">(GMT+11:00) Magadan, Solomon Is., New Caledonia</option>
 <option value="12">(GMT+12:00) Auckland, Wellington</option>
 <option value="12">(GMT+12:00) Fiji, Kamchatka, Marshall Is.</option>
 <option value="13">(GMT+13:00) Nuku'alofa</option>
 </select>
 </div>
 <div class='modal-footer'>
 <button type='button' class='btn btn-primary' id='confirmModalTimeZone'>Confrim</button>
 <button type='button' class='btn btn-default' data-dismiss='modal' id='closeModalApprove'>Cancel</button>
 </div>
 </div>
 </div>
 </div>

the modal will looks like this:
timezone

Now add JavaScript/jQuery methods to set the cookies if the users is authenticated (using asp.net Identity)

 function getCookie(cname) {
            var name = cname + "=";
            var decodedCookie = decodeURIComponent(document.cookie);
            var ca = decodedCookie.split(';');
            for (var i = 0; i < ca.length; i++) {
                var c = ca[i];
                while (c.charAt(0) == ' ') {
                    c = c.substring(1);
                }
                if (c.indexOf(name) == 0) {
                    return c.substring(name.length, c.length);
                }
            }
            return "";
        }

 function setCookie(cname, cvalue, exdays) {
            var d = new Date();
            d.setTime(d.getTime() + (exdays * 24 * 60 * 60 * 1000));
            var expires = "expires=" + d.toUTCString();
            document.cookie = cname + "=" + cvalue + ";" + expires + ";path=/";
        }

 function checkCookie(cname) {
            var cookieName = getCookie(cname);
            if (cookieName != "") {
                alert("Welcome again " + cookieName);
            }
            else {
                $('#modalTimeZoneApprove').modal('show');
                cookieName = "GMT  " + get_time_zone_offset();
                if (cookieName != "" && cookieName != null) {
                    setCookie(cname, cookieName, 365); MediaQueryList
                }
            }
        }

  function get_time_zone_offset() {
            var current_date = new Date();
            return -current_date.getTimezoneOffset() / 60;
        }

 $(document).ready(function () {
            var isAuthenticated = '<%=HttpContext.Current.User.Identity.IsAuthenticated %>';
            if (isAuthenticated=="True") {

                var cookieName = getCookie("AccountTimeZone");
                if (cookieName != "") {
                    //do nothing!
                }
                else {
                    cookieName = "GMT  " + get_time_zone_offset();
                    $("#spanTimeZone").text(cookieName);
                    $('#modalTimeZoneApprove').modal('show');
                }

                $('#zoneList').on('change', function () {
                    $("#spanTimeZone").text($("#zoneList option:selected").text());
                })

                $("#confirmModalTimeZone").click(function () {
                    var zone = $("#spanTimeZone").text();
                    if (zone != "" && zone != null) {
                        setCookie("AccountTimeZone", zone, 365);
                    }
                    $('#modalTimeZoneApprove').modal('hide');
                });
            }

Working with Microsoft Dynamics CRM Party List

Party lists are system data types and there is no supported way to modify them.

Unsupported : In order to modify party lists you need to access them in the DOM by getting their id attribute.

for instance if you want to change the filter on the “from” party list in activities, use below code on form load:

//change the default filter to account entity and disable the others parties

function FilterFromPartyList() {
  document.getElementById("from_i").setAttribute("defaulttype", "1");
  // document.getElementById("from_i").setAttribute("defaulttype", "1,2"); //+contact
 document.getElementById("from_i").setAttribute("lookuptypes", "1");
 document.getElementById("from_i").setAttribute("lookuptypeIcons", "/_imgs/ico_1 6_1.gif");
 document.getElementById("from_i").disableViewPicker = 1;
}

Here is the list of the attributes that you can access through DOM:


<img width="20" height="18" title="Select a value." class="ms-crm-InlineLookupEdit" id="from_i" alt="Select a value." src="/_imgs/search_normal.gif" isDisplayOnly="false" ForceSubmit="false" isEnableInlineLookupForEditForms="1" lookupstyle="single" lookuptypes="1" lookuptypeIcons="/_imgs/ico_16_1.gif" lookuptypenames="account:1:Account,contact:2:Contact,lead:4:Lead,systemuser:8:User" data-fdeid="PrimaryEntity" resolveemailaddress="0" attrName="from" attrPriv="7" createpermissiondictionary="account:true,contact:true,lead:true,systemuser:true" crmattributeid="ca991199-24b8-4a1f-9bd9-55fddb6dcdcd" isDeDupLookup="0" defaulttype="1" autoresolve="1" showproperty="1" allowUnresolvedPartiesOnEmailSend="0" isInline="true" inlineViewIds="undefined" allowFilterOff="0" disableMru="1" disableQuickFind="0" disableViewPicker="0" DefaultViewId="{A3AF4AB8-861D-4CFA-92A5-E6281FED7FAB}" lookupbrowse="0" lookupDialogMultipleSelect="0">

 

Retrieve Multiple Dynamics CRM Records with XrmServiceToolkit

In order to install XrmServiceToolkit check this blog post.

In this library, I found RetrieveMultiple function really helpful and I use it for different of purposes like to create a custom numbering system in CRM records, for instance since you can retrieve multiple record, you can loop through all your record and get all the fields in those.

here is the signature of the function :

     retrieveMultipleRecords = function (type, options, successCallback, errorCallback, onComplete, async)

One important thing is that successCallback only returns one page of records (50) at the time so you should loop through the results and push the records into an array outside of the function.

Here are Some Sample retrieveMultipleRecords calls:

1. This sample retrieves all accounts with all fields in the account form and push them in accountArray :

      var accountArray = new Array();

        XrmServiceToolkit.Rest.RetrieveMultiple(
                     "AccountSet",
                     "",// if you leave it empty, it retrieves all fields
                     function (results) {
                         if (results.length >= 1)
                             for (var i = 0; i < results.length; i++) {
                                 accountArray.push(results[i]);
                             }
                     },
                     function (error) {
                         alert(error.message);
                     },
                     function onComplete() {
                         //alert(" records should have been retrieved.");
                     },
                     false
                 );

     alert(accountArray.length); //this should show the number of account records in CRM

2. This sample retrieves only AccountID and AcountNumber when “new_AccountingApproved”(custom field) check box is checked:

   var accountArray = new Array();

        XrmServiceToolkit.Rest.RetrieveMultiple(
                     "AccountSet",
                     "$select=AccountId, AccountNumber &$filter=new_AccountingApproved eq true",
                     function (results) {
                         if (results.length >= 1)
                           //accountArray = results;  //don't do this! instead use push to array. for instance if you have 58 accounts in CRM, accountArray will only get 8 of them, because successCallback will run twice.
                             for (var i = 0; i < results.length; i++) {
                                 accountArray.push(results[i]);
                             }
                     },
                     function (error) {
                         alert(error.message);
                     },
                     function onComplete() {
                         //alert(" records should have been retrieved.");
                     },
                     false
                 );

In case you are interested ,Here is RetrieveMultipleRecords function definition from Source code of the project:

var retrieveMultipleRecords = function (type, options, successCallback, errorCallback, onComplete, async) {
        ///<summary>
        /// Sends synchronous/asynchronous request to retrieve records.
        ///</summary>
        ///<param name="type" type="String">
        /// The Schema Name of the Entity type record to retrieve.
        /// For an Account record, use "Account"
        ///</param>
        stringParameterCheck(type, "XrmServiceToolkit.REST.retrieveMultipleRecords requires the type parameter is a string.");
        ///<param name="options" type="String">
        /// A String representing the OData System Query Options to control the data returned
        ///</param>
        if (options != null)
            stringParameterCheck(options, "XrmServiceToolkit.REST.retrieveMultipleRecords requires the options parameter is a string.");
        ///<param name="successCallback" type="Function">
        /// The function that will be passed through and be called for each page of records returned.
        /// Each page is 50 records. If you expect that more than one page of records will be returned,
        /// this function should loop through the results and push the records into an array outside of the function.
        /// Use the OnComplete event handler to know when all the records have been processed.
        /// </param>
        callbackParameterCheck(successCallback, "XrmServiceToolkit.REST.retrieveMultipleRecords requires the successCallback parameter is a function.");
        ///<param name="errorCallback" type="Function">
        /// The function that will be passed through and be called by a failed response.
        /// This function must accept an Error object as a parameter.
        /// </param>
        callbackParameterCheck(errorCallback, "XrmServiceToolkit.REST.retrieveMultipleRecords requires the errorCallback parameter is a function.");
        ///<param name="OnComplete" type="Function">
        /// The function that will be called when all the requested records have been returned.
        /// No parameters are passed to this function.
        /// </param>
        callbackParameterCheck(onComplete, "XrmServiceToolkit.REST.retrieveMultipleRecords requires the OnComplete parameter is a function.");
        ///<param name="async" type="Boolean">
        /// A Boolean representing if the method should run asynchronously or synchronously
        /// true means asynchronously. false means synchronously
        /// </param>
        booleanParameterCheck(async, "XrmServiceToolkit.REST.retrieveMultipleRecords requires the async parameter is a boolean.");

Filtering Dependent Picklist(OptionSet) Javascript for Quick Create Form Microsft Dynamics CRM 2013

Quick create form is a new feature of Microsoft Dynamics CRM , that could be enabled for forms to create a new related record inside the CRM without opening new form and leaving the current form that you are working on. it’s making easy creating records on the fly but make it harder to create java script function on this kind of form.

Quick Create Form purpose is different from main forms and they don’t support all the capabilities of main forms. The following controls cannot be added to quick create forms:

 

  • Sub-grids
  • Quick View Forms
  • Web resources
  • IFRAMEs
  • Notes
  • Bing Maps

Also some other CRM JavaScript controls are not supported in quick create form like Xrm.Page.getControlor Xrm.Page.getAttribute , …

so in order to implement dependent option-set (pick-lists) in quick form we can use JavaScript and JQuery  directly in the form. Although it’s not supported customization, I couldn’t find any supported client side solution for this.

here are the steps for implementing dependent picklist in  create quick form CRM 2013:

Scenario:  I have two option-set issue (new_issue) and issuesubtype(new_issuesubtypewhich the issue sub type should be filtered based on the values in issue option-set. I’m going to filter it based on the appearance of text values from issue to issue sub type. So if issue = electrical the function would find all the issuesubtype  contains electrical string in it.

Use below code on change event of first option-set(issue):

function IssueType_OnChange_Iframe() {

var showOnlyOptionsSimilarToText = function (selectionEl, str) {

// cache the jQuery object of the &lt;select&gt; element
var el = $(selectionEl);
if (!el.data("options")) {
//cache all the options inside the &lt;select&gt; element for easy recover
el.data("options", el.find("option").clone());
} debugger;
try {
if (el.length != 0)
var newOptions = el.data("options").filter(function () {
var text = $(this).text();
if (text != null || text != '') {
return text.match(str);
}
});
el.empty().append(newOptions);
}
catch (err) {
var error = err.message;
}
};

var userInput = $('#new_issue')[0].title;
showOnlyOptionsSimilarToText($("#new_issuesubtype_i"), userInput);
}

In order for this code to work you should add JQuery to your form.

This code only look at text values selected in first optionset and filter the second one based on that text value.

the second optionset id is new_issuesubtype but  I used the id of the second optionset select tag.(you can get it using IE developer or firebug)

<select tabindex=”-1″ title=”” class=”ms-crm-SelectBox ms-crm-Inline-OptionSet-AutoOpen” id=”new_issuesubtype_i” aria-labelledby=”new_issuesubtype_c new_issuesubtype_w” size=”10″ defaultSelected=”” attrName=”new_issuesubtype” attrPriv=”7″ controlmode=”normal” defaultvalue=””>