Here’s a little snippet I authored this morning, before my cup of coffee, to query the CRM SQL database (YourOrg_MSCRM) for a list of web resources in the order of last ModifiedOn:

1
2
3
4
SELECT ModifiedOn, Name, DisplayName, OrganizationIdName, WebResourceType
FROM WebResource --table for web resources
WHERE IsCustomizable = 1 -- just for custom web resources
ORDER BY ModifiedOn DESC

Anders Fjeldstad posted a neat JS shim on GitHub that adds in the ability to make CRM 2013 notifications in CRM 2011.

You can wrap it in a function that calls during your form’s CRM onLoad event.  That’s it!  Then you can make notifications appears easily:

Xrm.Page.ui.setFormNotification('This is an error',1,101); //Error (signified by the 1, with messageId 101)
Xrm.Page.ui.setFormNotification('This is a warning',2,102); //Warning (signified by the 2, with messageId 102)
Xrm.Page.ui.setFormNotification('This is informational',3,103); //Informational message (signified by the 3, with messageId 103)

Removing a notification is the same as in CRM 2013:

Xrm.Page.ui.clearFormNotification(101); //this clears messageId 101
Xrm.Page.ui.clearFormNotification(102); //this clears messageId 102
Xrm.Page.ui.clearFormNotification(103); //this clears messageId 103

Get it here on GitHub.

I found quite a gem today while browsing the Microsoft Developer blogs for help with debugging JavaScript in CRM 2011. It can be a pain to be updating JavaScript in Dynamics CRM 2011 without shelling out extra money for Visual Studio plug-ins to simplify it.  If you’re new to JavaScript in CRM 2011, this video from Marc Schweigert should help get you started with setting up Visual Studio to do the menial work of publishing web resources and debugging them.

Found at EUREKA: F5 debugging of CRM JavaScript web resources

▶ F5 debugging of CRM JavaScript web resources – YouTube.

It’s a challenge to work with thousands of custom fields in JavaScript in CRM 2011. A tool is available in Visual Studio 2010 and up that automatically imports all of the fields into an object, dubbed “Properties” by default from Visual Studio. This allows us to use Intellisense to ensure that we have the correct names for the fields that we are referencing in JavaScript. Unfortunately the tool, which is fantastic, is not free past its 30 day trial.

Enter custom JS Code. I personally like to use a custom form script library that contains developer-specific functionality (which can be referenced by GUID) and is used with custom key declarations (a previous post explains this).

Here’s the function that I call which will output objects that can almost be copy-and-pasted directly into Visual Studio. I’m sure there’s a better way, but this works for me given the minimal effort that went into it.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
function util_listControlstoConsole() {
try {
//Controls
if (confirm('Do you wish to list all controls to the console?')) {
custAlert('////The below are a listing of all detected controls on the form////');
console.log('var Properties = {');
Xrm.Page.ui.controls.forEach(
function (control) {
if (control.getControlType() !== "subgrid" &&
typeof control.getName === "function") {
console.log(control.getName() + ": '" + control.getName() + "', //Label: " + control.getLabel() + " Parent Name: '"
+ control.getParent().getName() + "' Parent Label: '" + control.getParent().getLabel() + "'");
}
}
);
console.log('}; //remove last comma (,) from above when putting in script, note that there may be duplicates (appended with 1)');
}
if (confirm('Do you wish to list all sections/tabs to the console?')) {
//Sections
custAlert('////The below are a listing of all detected controls on the form////');
console.log('var Displays = {');
console.log(' sections: {');
var tabs = Xrm.Page.ui.tabs;
for (var i = 0; i < tabs.getLength() ; i++) {
var tab = tabs.get(i);
var sections = tab.sections;
for (var j = 0; j < sections.getLength() ; j++) {
var section = sections.get(j);
var name = section.getName();
if (name !== null) {
console.log(" " + name.replace(/\W/g, '') + ": '" + name + "', //Label: " + section.getLabel() + " Parent Tab: '"
+ section.getParent().getName() + "'");
}
//console.log(section.getParent().getName() + "." + section.getName() + ": '" + section.getName() + "'");
}
}
console.log(' }, //remove last comma (,) from above when putting in script');
//Tabs
console.log(' tabs: {');
var tabs = Xrm.Page.ui.tabs;
for (var i = 0; i < tabs.getLength() ; i++) {
var tab = tabs.get(i);
var tabName = tabs.get(i).getName();
if (tabName !== null) {
console.log(" " + tabName.replace(/\W/g, '') + ": '" + tabName + "', //Label: " + tab.getLabel());
}
}
console.log(' }//remove last , from above');
console.log('}; ');
}
} catch (e) {
//alert
}
}

The output will be something like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
var Properties = {
firstname: 'firstname', //Label: First Name Parent Name: 'name' Parent Label: 'Name'
middlename: 'middlename', //Label: Middle Name Parent Name: 'name' Parent Label: 'Name'
lastname: 'lastname', //Label: Last Name Parent Name: 'name' Parent Label: 'Name'
suffix: 'suffix', //Label: Suffix Parent Name: 'name' Parent Label: 'Name'
}; //remove last , from above
var Displays = {
sections: {
name: 'name', //Label: Name Parent Tab: 'general'
}, //remove last , from above
tabs: {
general: 'general', //Label: General
}//remove last , from above
};

Pros:
Now when you are referencing a control, you can put Properties.firstname and have your entry checked by Intellisense rather than relying on typing it yourself. Also if you want to reference a section or tab, you can use Displays.sections.name or Displays.tabs.general. It may be a tad more wordy than just saying ‘general’, but it makes it easier to maintain and error-check.

Cons:
I’d recommend you make changes to the script to get it to work the way you want it. Right now it spits out an extra comma on the last control/section/tab so you have to manually remove it when inputting it into Visual Studio.

The snippet below can be used in a function when you want to bind multiple keydown events to a function. It works by adding the keys pressed to an array, then running a function to check if they equal a predefined keycode sequence.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
        var keys = {};
 
            $(document).keydown(function (e) {
                keys[e.keyCode] = true;
                devMdChk();
            });
 
            $(document).keyup(function (e) {
                delete keys[e.keyCode];
                devMdChk();
            });
 
            function devMdChk() {
                var html = '';
                var list = '';
                for (var i in keys) {
                    if (!keys.hasOwnProperty(i)) continue;
                    //html += '' + i + ';
                    list = list + i;
                }
                if (list === "1661192" || list === "16187192"/* || list === "187192" || list === "61192"*/) {
                    for (var i in keys) {
                        delete keys[i];
                    }
 
                    if (confirm(
                        "You have press Shift, ~, and +. Do you wish to enable developer mode?") {
 
                      alert("Some function here");
                    }
                }
            }

In creating records in CRM entities, it’s often useful to unhide hidden tabs/sections/controls based on if you are a certain role, or if certain keys are pressed that a non-developer or system administrator would not be privy to. For instance, you can bind the simultaneous pressing of certain keys, such as SHIFT, ~, and +, to perform a function that may be used only in development or testing of CRM. Note that this requires jQuery and was tested with 1.10 although any version after 1.4 should work.

This guide will involve several steps, which you can omit if unnecessary for your needs.
1. Bind certain simultaneous key press events to a certain function. Use http://www.asquare.net/javascript/tests/KeyCode.html to find the “keycode” for each keyboard button depending on the browser(s) you wish to target.
2. Set the function to unhide all/some controls on a form, or perform other tasks such as showing current user name, etc.
3. Ensure that only certain users are allowed to call this function.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
if (!window.console) {//declare variable for console if it does not exist
console = {
log: function () { }
}; 
}
var FEXT = {}; //create global variable for storing data used in this example
var userID = Xrm.Page.context.getUserId().substring(1, 37), //store current user ID
FEXT.currentRoles = [], //used to store current user roles in GetUserRoles functions
DEV_ARRAY = ['C3A808EA-37C9-E211-BB3B-0050568866DA', '86A43F37-AFF3-E211-836F-0050568866DA'], //this should contain an array of any developer's GUIDs that should only be allowed to call the function or certain elements of it.
IS_DEVELOPER = false, //default to false at first, so that the function can set it to true if current user id is in developer array
IS_ADMIN = false; //similar as above
IS_DEVELOPER = (util_arrayContains(userID, DEV_ARRAY)); //use the function, shown below, to check if the DEV_ARRAY contains current userID
IS_ADMIN = function () {
try {
GetUserRoles(); //this sets the user roles in FEXT.currentRoles array as all user roles for current user
return util_arrayContains("System Administrator", FEXT.currentRoles); //this checks the above array for "System Administrator", and returns to IS_ADMIN "true" if is Administrator.
} catch (e) {
custAlert("IS_ADMIN", e); //custom alert function
return false; //if errors out, then set IS_ADMIN to false;
}
}(); // the () part will set ensure this function is ran automatically without being called separately.
 
if (IS_DEVELOPER || IS_ADMIN) { //if either is true, then run this.
try {
custAlert("Is Admin: " + util_arrayContains("System Administrator", FEXT.currentRoles)); //shows to console if the user is Admin
var keys = {}; //used to store the keys pressed in the below jQuery functions
 
$(document).keydown(function (e) { //when key is down, this will add it to "keys", and run the devMdChk function mentioned below.
keys[e.keyCode] = true;
devMdChk();
});
 
$(document).keyup(function (e) { //when key is let go, then this deletes it from "keys" and runs devMdChk.
delete keys[e.keyCode];
devMdChk();
});
 
function devMdChk() { //this creates a list of all keys in the keys object array, then checks if it equals a certain sequence
var list = '';
for (var i in keys) {
if (!keys.hasOwnProperty(i)) continue;
list = list + i;
}
if (list === "1661192" || list === "16187192") { //this is the list for 16, 187, and 192, which is SHIFT, ~, and +, for IE, respectively.
for (var i in keys) {
delete keys[i]; //if the above IF is matched, then you want to delete all keys in the keys array so that it does not fire again until the next keydown event sequence.
}
 
if (confirm(
"You have press Shift, ~, and +. Do you wish to enable developer mode? \n\n" +
"This may take a few seconds to load and should only be used for troubleshooting by System Administrators. \n" +
"Saving a form after making modifications during developer mode may result in database corruption. \n\n" +
"DO NOT CLICK OK UNLESS YOU HAVE RECEIVED PRIOR APPROVAL FROM A TECHNICIAN.")) {
try {
FEXT.cons = true; //sets the attribute as true in global variable so it can be "checked" in the future
util_unhideAllControls(); //unhides all controls on form, below
util_DisableAllFormFields(false); //enable all form fields, below
if (Xrm.Page.context.isOutlookOnline()) { //for information purposes, checks if CRM detects Outlook as in online mode.
custAlert("Client online");
}
else custAlert("Client offline");
custAlert("Current server URL is: " + util_getServerUrl());
custAlert("Current user is: " + util_getCurrentUserAJAX());
custAlert("Current owner ID is: " + util_currentOwnerID());
custAlert("Current hub is: " + util_getMyBusinessUnit());
custAlert("Current owner business unit is: " + util_getOwnerBusinessUnit());
} catch (e) {
custAlert("devMdChk", e);
}
}
}
}
}
catch (e) {
custAlert("Cannot check if is developer or admin. ");
}
 
}
});
 
function GetUserRoles() {
try {
var roles = Xrm.Page.context.getUserRoles();
 
for (var i = 0; i < roles.length; i++) {
GetRole(roles[i]);
}
} catch (e) {
console.log("Could not get user roles.");
}
 
}
 
function GetUserRoles() {
try {
var roles = Xrm.Page.context.getUserRoles();
 
for (var i = 0; i < roles.length; i++) {
GetRole(roles[i]);
}
} catch (e) {
custAlert("GetUserRoles", e);
}
 
}
 
function GetRole(roleid) {
try {
var serverUrl = Xrm.Page.context.getServerUrl();
var oDataSelect = serverUrl + "/XRMServices/2011/OrganizationData.svc/RoleSet?$select=Name&$filter=RoleId eq guid'" + roleid + "'";
 
var retrieveReq = new XMLHttpRequest();
retrieveReq.open("GET", oDataSelect, false);
retrieveReq.setRequestHeader("Accept", "application/json");
retrieveReq.setRequestHeader("Content-Type", "application/json;charset=utf-8");
retrieveReq.onreadystatechange = function () {
GetRoleData(this);
};
retrieveReq.send();
} catch (e) {
custAlert("GetRole", e, roleid);
}
 
}
 
function GetRoleData(retrieveReq) {
if (retrieveReq.readyState == 4) {
if (retrieveReq.status == 200) {
var retrieved = this.parent.JSON.parse(retrieveReq.responseText).d;
FEXT.currentRoles.push(retrieved.results[0].Name);
}
}
}
 
function util_arrayContains(value, arrayName) {
///<summary>
///Checks if an arrayName contains a value, and if so, return true; else, false.
///</summary>
try {
if ($.isArray(value) && $.isArray(arrayName)) {
for (var x = 0; x < value.length; x++) {
for (var y = 0; y < arrayName.length; y++) {
if (arrayName[y] === value[x]) {
return true;
}
}
}
return false;
}
else if ($.inArray(value, arrayName) > -1) {
return true;
}
else return false;
} catch (e) {
//console.log("Could not check if array contains value.");
}
 
}
function custAlert(funcname, errorObj, param) {
///<summary>
/// Function to hide caught alert messages in console:
/// Can replace funcname with a message. errorObj should be an error object.
/// Parameter is any relevant parameter
///</summary>
/// <param name="funcname" type="string">
/// A string representing the function or message. Required.
/// </param>
/// <param name="errorObj" type="object">
/// The error object. Optional. Use null if not passing errorObj but passing param.
/// </param>
/// <param name="param" type="string">
/// The parameter that is causing issues. Optional.
/// </param>
if (typeof IS_DEVELOPER !== "undefined") {
if (IS_DEVELOPER) {
//if (!window.console) console = { log: function () { } }; //declare variable for console if it does not exist
//Only do this if it is console:
if (typeof errorObj === "undefined") {
var message = funcname;
console.log(message);
}
else if (typeof param === "undefined") {
console.log("ERROR - " + funcname + "() caused error : '" + errorObj.message + "'")
}
else {
if (errorObj === null) {
console.log("ERROR - " + funcname + "() caused error :  on '" + param + "'");
}
else {
console.log("ERROR - " + funcname + "() caused error : '" + errorObj.message + "' on '" + param + "'");
}
}
}
}
}
function util_unhideAllControls() {
    /// <summary>
    /// Testing. Unhide all controls in form.
    /// </summary>
    /// <returns type="void" />
 
    //unhide all sections/tabs
    var tabs = Xrm.Page.ui.tabs;
    for (var i = 0; i < tabs.getLength() ; i++) {
 
        //Xrm.Page.ui.tabs.get(i).sections.get(j)
        var tab = tabs.get(i);
        custAlert("tab " + tab.getName() + " being unhidden");
        tab.setVisible(true);
        var sections = tab.sections;
        for (var j = 0; j < sections.getLength() ; j++) {
            try {
 
                var section = sections.get(j);
                custAlert("section " + section.getName() + " being unhidden");
                section.setVisible(true);
            } catch (e) {
                custAlert("Could not unhide all sections/tabs for " + tab.getName() + " " + section.getName());
            }
        }
 
    }
    //Unhide all controls
    Xrm.Page.ui.controls.forEach(
         function (control) {
             if (control.getControlType() != "subgrid") {
                 custAlert(control.getName() + " being unhidden");
                 try {
                     control.setVisible(true);
 
                 } catch (e) {
                     custAlert("Could not hide " + control.getName(), e);
                 }
             }
         });
 
}
function util_doesControlHaveAttribute(control) {
    var controlType = control.getControlType();
    return controlType != "iframe" && controlType != "webresource" && controlType != "subgrid";
}
function util_DisableAllFormFields(trueFalse) {
    ///<summary>
    ///Disable all form fields
    ///</summary>
    Xrm.Page.ui.controls.forEach(function (control, index) {
        if (util_doesControlHaveAttribute(control)) {
            control.setDisabled(trueFalse);
        }
    });
}
function util_getServerUrl() {
    //  Return Current Server URL formatted to remove trailing slash
    var server = Xrm.Page.context.getServerUrl();
    if (server.match(/\/$/)) {
        server = server.substring(0, server.length - 1);
    }
    return server;
}
 
function util_getCurrentUserAJAX() {
    ///<summary>
    ///Returns the full name of the user running this script
    ///</summary>
    try {
        var serverUrl;
        if (Xrm.Page.context.getClientUrl !== undefined) {
            serverUrl = Xrm.Page.context.getClientUrl();
        } else {
            serverUrl = Xrm.Page.context.getServerUrl();
        }
        var ODataPath = serverUrl + "/XRMServices/2011/OrganizationData.svc";
        var userRequest = new XMLHttpRequest();
        userRequest.open("GET", ODataPath + "/SystemUserSet(guid'" + Xrm.Page.context.getUserId() + "')", false);
        userRequest.setRequestHeader("Accept", "application/json");
        userRequest.setRequestHeader("Content-Type", "application/json; charset=utf-8");
        userRequest.send();
        if (userRequest.status === 200) {
            var retrievedUser = JSON.parse(userRequest.responseText).d;
            var userFullName = retrievedUser.FullName;
            return userFullName;
        }
        else {
            return "error";
        }
    } catch (e) {
        custAlert('util_getcurrentUserAJAX', e)
    }
}
function util_currentUserId() {
    ///<summary>
    ///Get current user ID.
    ///</summary>
    var userID = Xrm.Page.context.getUserId();
    return userID;
}
function util_currentOwnerID() {
    ///<summary>
    ///Find current Owner ID of form.
    ///</summary>
    var ownerID = Xrm.Page.getAttribute("ownerid").getValue()[0].id;
    return ownerID;
}
function util_getMyBusinessUnit() {
    try {
        var xml = "" +
            "<?xml version='1.0' encoding='utf-8'?>" +
            "<soap:Envelope xmlns:soap='http://schemas.xmlsoap.org/soap/envelope/'" +
            " xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'" +
            " xmlns:xsd='http://www.w3.org/2001/XMLSchema'>" +
            GenerateAuthenticationHeader() +
            "<soap:Body>" +
            "<Fetch xmlns='http://schemas.microsoft.com/crm/2007/WebServices'>" +
            "<fetchXml>" +
            " &lt;fetch mapping='logical' count='1'&gt;" +
            " &lt;entity name='businessunit'&gt;" +
            " &lt;attribute name='name' /&gt;" +
            " &lt;filter&gt;" +
            " &lt;condition attribute='businessunitid' operator='eq-businessid' /&gt;" +
            " &lt;/filter&gt;" +
            " &lt;/entity&gt;" +
            " &lt;/fetch&gt;" +
            "</fetchXml>" +
            "</Fetch>" +
            "</soap:Body>" +
            "</soap:Envelope>";
 
        var xmlHttpRequest = new ActiveXObject("Msxml2.XMLHTTP");
        xmlHttpRequest.Open("POST", "/mscrmservices/2007/CrmService.asmx", false);
        xmlHttpRequest.setRequestHeader("SOAPAction", "http://schemas.microsoft.com/crm/2007/WebServices/Fetch");
        xmlHttpRequest.setRequestHeader("Content-Type", "text/xml; charset=utf-8");
        xmlHttpRequest.setRequestHeader("Content-Length", xml.length);
        xmlHttpRequest.send(xml);
 
        var resultXml = xmlHttpRequest.responseXML;
 
        var resultSet = resultXml.text;
        resultSet.replace('&lt;', '< ');
        resultSet.replace('&gt;', '>');
 
        var oXmlDoc = new ActiveXObject("Microsoft.XMLDOM");
        oXmlDoc.async = false;
        oXmlDoc.loadXML(resultSet);
 
        var result = oXmlDoc.getElementsByTagName('name');
 
        return result[0].text;
    } catch (e) {
        custAlert("util_getMyBusinessUnit", e);
    }
 
}
 
function util_getOwnerBusinessUnit() {
    try {
        var ownerid = new Array();
        ownerid = Xrm.Page.getAttribute("ownerid").getValue();
        var ownerHub = null;
        var id, odataSelect, retrieveReq, name;
        var getFieldData = function getFieldData(retrieveReq) {
            if (retrieveReq.readyState == 4) {
                if (retrieveReq.status == 200) {
                    var retrieved = this.parent.JSON.parse(retrieveReq.responseText).d;
                    if (retrieved.results.length > 0) {
                        var retrievedValue = retrieved.results[0].BusinessUnitId;
                        var id = retrievedValue.Id;
                        name = retrievedValue.Name;
                        return name;
                    }
 
                }
            }
        }
        if (ownerid != null && ownerid.length > 0) {
            id = ownerid[0].id;
            odataSelect = Xrm.Page.context.getServerUrl() + "/XRMServices/2011/OrganizationData.svc/SystemUserSet?$select=BusinessUnitId&$filter=SystemUserId eq guid'" + id + "'";
            retrieveReq = new XMLHttpRequest();
            retrieveReq.open("GET", odataSelect, false);
            retrieveReq.setRequestHeader("Accept", "application/json");
            retrieveReq.setRequestHeader("Content-Type", "application/json; charset=utf-8");
            retrieveReq.onreadystatechange = function () {
                ownerHub = getFieldData(this);
 
            };
            retrieveReq.send();
        }
        //return ownerHub;
        if (name != null) {
            return name;
        }
    } catch (e) {
        custAlert('util_getOwnerBusinessUnit', e);
    }
 
}

Creating a custom CRM 2011 button that performs JavaScript functions can be tedious in CRM 2011.  Most guides that I found on the internet for CRM 2011 still included the antiquated crmForm.all method, which is unsupported.

So far the best method I have found of adding a button, or multiple buttons, to a form created in Dynamics CRM UR12 and possibly higher, is by utilizing an HTML Web resource that will store a generic button and the JavaScript required to change the functionality/text/etc. Borrowed and adapted from Ray and Razvan on the CRM Development forums

The code for the HTML Web Resource (let’s just call it WebResource_ButtonHTML):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<HTML xmlns="http://www.w3.org/1999/xhtml"><HEAD><TITLE></TITLE>
<SCRIPT type=text/javascript src="ClientGlobalContext.js.aspx"></SCRIPT>
<SCRIPT type=text/javascript>
    function setText(text) {
        if (text === undefined) {
            text = decodeURI(location.search).replace("?data=", "").replace('%3a', ':').replace('[br]', '<br>');
        }
        var msg = document.getElementById('crmButton');
        msg.value = text;
    }
    //Set the text for the button & attach event
    function initializeButton(text, clickEvent) {
        if (text !== undefined) {
            document.getElementById('crmButton').value = text;
        }
        if (clickEvent !== undefined) {
            try {
                document.getElementById('crmButton').attachEvent("onclick", clickEvent); //Legacy IE code
                    }
    }
 
    function enable() {
        document.getElementById('crmButton').disabled = false;
    }
 
    function disable() {
        document.getElementById('crmButton').disabled = true;
    }
</SCRIPT>
<META charset=utf-8></HEAD>
<BODY style="BORDER-BOTTOM: 0px; BORDER-LEFT: 0px; BACKGROUND-COLOR: rgb(246,248,250); MARGIN: 0px; PADDING-LEFT: 0px; FONT-FAMILY: Segoe UI, Tahoma, Arial; COLOR: #3b3b3b; FONT-SIZE: 11px; BORDER-TOP: 0px; FONT-WEIGHT: normal; BORDER-RIGHT: 0px; PADDING-TOP: 0px" onload=setText(); contentEditable=true>
    <BUTTON style="WIDTH: 100%; HEIGHT: 100%" id="crmButton">CRM Button</BUTTON>
</BODY></HTML>

Go into the development page for your form, and then insert this HTML Web Resource and call it ButtonName.  Note that this HTML web resource has code that is normally not supported by CRM 2011 (using the native DOM), but this is okay since this HTML web resource is being used directly and does not have to go through the XRM.Page model.

Now that we have the button that will be inserted onto the frame, we can add custom code in our form’s Javascript library to be able to access the new_ButtonHTML for each button and modify accordingly:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
function GetWebResource(name)
{
    try
    {
        var wrt = Xrm.Page.getControl("WebResource_" + name );//automatically adds WebResource_
        if ( wrt != null )
        {
            if ( wrt.getObject().contentWindow.window != undefined )
            {
                return wrt.getObject().contentWindow.window;
            }
            else
            {
                return null;
            }
        }
        else
            return null;
    }
    catch ( e )
    {
        //debugger;
        alert("Error encountered(GetWebResource): " + e.message);
    }
}
var btnName = GetWebResource("ButtonName"); 
if (typeof btnName.initializeButton !== "undefined") {
btnName.initializeButton("Do Stuff", functionToRun); 
}
function functionToRun() {
btnName.disable();
}

The preceding JavaScript defined a function that will check if the web resource has loaded (GetWebResource(“ButtonName”)) and, if the function initializeButton is not undefined, then it will run it and assign it a text value and an event.

Note that with CRM UR12 to UR14 you may have problems with the load order, where initializeButton will sporadically not be defined. If this is the case, you might want to consider setting a timeout to check that the new_ButtonHTML web resource has loaded in the form. That would probably look like this:

26
27
28
29
30
31
32
33
34
function initBtns()  //creating a function so that it can be easily referenced in a timer
{
var btnName = GetWebResource("ButtonName"); 
if (typeof btnName.initializeButton !== "undefined") {
btnName.initializeButton("Do Stuff", functionToRun); 
} else {
setTimeout(initBtns,100); //sets a timeout to run the initBtns function every 100ms
}
}

With these functions, you should be able to reuse the HTML web resource new_ButtonHTML and simply use custom JavaScript to modify them. Make sure that when you add the buttons that you allow the defaults (aside from formatting) such as allowing cross-side scripting or it may not work properly. The page is essentially an iframe or inline frame, hence the GetWebResource function. You might also be able to use other Javascript functions to access the iframe and run the JavaScript for each instance of your new_ButtonHTML.