Recent twitter entries...

  •  

Javascript : How to find out which action raised the OnSave event in a Form

Posted by AnonymousDeveloper | Posted in Javascript Snippets | Posted on 17-07-2009

0

Recently, I was asked by a customer if it was possible to execute some javascript when an Opportunity is marked as won. As you all know, the value of statecode is in fact on a separate dialog box and technically, there is no way to capture the event. However, this is another way around it, and that is the use the event.Mode value to determine which action raised the OnSave event in the first place.

Here is the list of events:

  • None : 0
  • Save : 1
  • SaveAndClose : 2
  • Delete : 3
  • Load : 4
  • Deactivate : 5
  • Reactivate : 6
  • Email Send : 7
  • Email Reply : 8
  • Email Forward : 9
  • Kb Submit : 10
  • Kb Reject : 11
  • Kb Publish : 12
  • Kb UnPublish : 13
  • Kb Rate : 14
  • Lead Unqualify : 15
  • Lead Qualify : 16
  • Quote Accept : 17
  • Quote CreateOrder : 18
  • Order ProcessOrder : 19
  • Opportunity AddRelatedOrder : 21
  • Opportunity AddRelatedQuote : 22
  • Opportunity AddRelatedInvoice : 23
  • Quote CreateRevision : 24
  • Quote CloseQuote : 25
  • Order CancelOrder : 26
  • Invoice Close : 27
  • Quote GetProducts : 28
  • Quote Activate : 29
  • Email ReplyAll : 30
  • Contract Hold : 31
  • Contract ReleaseHold : 32
  • Contract Cancel : 33
  • Contract Renew : 34
  • Product ConvertToKit : 35
  • Product ConvertFromKit : 36
  • ContractDetail Cancel : 37
  • Contract Invoice : 38
  • Contract Clone : 39
  • Incident Cancel : 40
  • Email Assign : 41
  • Change SalesStage : 42
  • SalesOrder GetProducts : 43
  • InvoiceGetProducts : 44
  • TemplateMakeOrgAvailable : 45
  • TemplateMakeOrgUnavailable : 46
  • Assign : 47
  • IncidentAssignToUser : 49
  • OrderLock : 50
  • OrderUnlock : 51
  • InvoiceLock : 52
  • InvoiceUnlock : 53
  • ConvertResponse : 54
  • ReportMakeOrgAvailable : 60
  • ReportMakeOrgUnavailable : 61
  • WorkflowAddCheckStep : 62
  • WorkflowUpdateCondition : 63
  • WorkflowCreateAction : 64
  • SendInvite : 65
  • WorkflowAddElseIfStep : 66
  • WorkflowAddElseStep : 67
  • WorkflowDeleteStep : 68

Example Usage :

if(event.Mode == 5) //deactivation

{

// Do some custom logic here

}

If you dont know which action triggered the event, you can use the following line in the onSave event of the form to find out :

alert(event.Mode);

Customisation of an ISV button : Toggles, Colours and other Fun

Posted by AnonymousDeveloper | Posted in CRM Configuring, Javascript Snippets | Posted on 02-06-2009

0

Further to previous posts HERE on how to hide ISV buttons, I recently came across a new wrinkle in the problem, essentially, I needed to be able to access the properties of that ISV button in order to create a toggle button. Knowing as we do that CRM4 adds some random number to the definition of a button, I really needed to find a way around this.

 

The Problem

If you create a new ISV button for yourself (on accounts in this case), and you use the Developer Toolbar to access the information about it, you will notice the following :

image

 

This number ‘increments’ every time the page is loaded so it you dont have a static name any longer. This presents something of a problem when it comes to creating a toggle button and changing the styles. So, how can we do something about it?

The first question is….what doesnt change? Well, the tooltip definition doesn’t change for a start. When you create the ISV button in ISV.config, you add your tooltip variable right into the XML, like so.

image

The Solution

As well as the ToolTip variable, we also know that the menu bar is always designated as mnuBar1. We can iterate through a collection to find our particular button.

function OnCrmPageLoad()
{
  //Configure Display when the form loads.
  ConfigureToolbarDisplay();
  //Configure the display each time a user manually changes the window width size.
  attachEvent("onresize",ConfigureToolbarDisplay);
}

function ConfigureToolbarDisplay()
{

    //Toolbar buttons that are to be affected - referenced by the ToolTip value
   //assigned in the ISV.config
   ShowHideToolbarButton( "ISV Button" );
}

function ShowHideToolbarButton( btnTitle )
{

    //Get all toolbar buttons
    var toolBarButtons = document.all.mnuBar1.rows[0].cells[0].childNodes[0].childNodes;

    for (var i = 0 ; i < toolBarButtons.length ; i++)
    {


        var button = toolBarButtons[i];

	//Loop through the collection and find the btnTitle we are interested in
        if( button.title.match(btnTitle) != null )
        {

	//Assign our button to the ButtonID variable
	var ButtonID = button.id;


         }
    }

}

OnCrmPageLoad();

 

Lovely, we have a basic structure for defining our custom ISV button.

Now, lets have some fun.

I want my button font colour to be RED

Now we have the definition for our ISV button, we can do almost anything else we want with it despite CRM shenanigans with spurious numbers. However, there is one more fundamental problem and that is ‘how’ CRM creates the pages at run time. If you were to use the following code under the ‘var ButtonID….’ line, you would be able to change the text as expected, but your icon would disappear.

document.all[ButtonID].children[0].innerHTML = "Do Something";

image

Not really very helpful. You can also change the colour quite happily by using the following additional line :

document.all[ButtonID].children[0].style.color = "red";

 

image

Lets take a look at the developer toolbar and see what code is actually being generated when a normal ISV button is displayed.

image

As you can see, there is a SPAN class, then an A class, then the IMG and another SPAN which actually holds the text property which we want to influence.

By looking at the code generated when we use the lines above, we see the following :

image

Where did our IMG class go? Good question, and the short answer is, I dont know. However, it still leaves us with the problem of how to overcome this little limitation and the answer is to cheat. We know that the text is contained in the SPAN element, but we also want the IMG element to display. Luckily, there is an element called innerHTML which will do very well for our purposes. So, we create a new function called, ModifyButton with some additional parameters.

 

function ModifyButton (btnName, btnTitle, btnIcon){

try
	 {
	 //find the button we have specified
	 var navigationBar = document.getElementById(btnName);

	 //find the correct element of the menubar/button we have specified
	 var textArray = navigationBar.getElementsByTagName("SPAN");

	 //read our variables
	 var buttonIcon = "http://crm:5555/_imgs/" + btnIcon; //change this reference
	 var buttonText = btnTitle;

	 for (i = 0; i < textArray.length; i++) {

            textArray[i].innerText = buttonText;

	   textArray[i].innerHTML = "<A class=ms-crm-Menu-Label><IMG class=ms-crm-Menu-ButtonFirst src='" + buttonIcon + "'><SPAN class=ms-crm-MenuItem-TextRTL>" + textArray[i].innerText + "</A></></SPAN>";

          }
	}

 catch(ex){
 }
}

This function can be invoked from within the original ShowHideToolBarButton function like so

function FindToolBarButton( btnTitle )
{

    //Get all toolbar buttons
    var toolBarButtons = document.all.mnuBar1.rows[0].cells[0].childNodes[0].childNodes;

    for (var i = 0 ; i < toolBarButtons.length ; i++)
    {


        var button = toolBarButtons[i];

	//Loop through the collection and find the btnTitle we are interested in
        if( button.title.match(btnTitle) != null )
        {

	//Assign our button to the ButtonID variable
	var ButtonID = button.id;



        }

    }



   //Invoke ModifyButton function....
  ModifyButton(ButtonID,"My New Button","ico_16_4200.gif");



}

 

So, what do we get when we put all of this together?

image

My New Button now has the text that we specified above as well as the IMG tag containing the correct icon, but it still isnt red. Well, that is now fairly easy to handle. Just add the following line just above the ‘innerHTML’ element within the ModifyButton function.

textArray[i].style.color = "red";

image

In fact, you can now add any DOM style you want to your new button

textArray[i].style.backgroundColor = "Navy";

 

image

Practical Applications

Now that we know we can influence the title of the ISV button and it’s style, it’s really a question of how to use this. The original premise that took me off down this path was that I required a toggle button. I wanted to be able to change the caption of an ISV button and it’s function based on the value in another attribute on the form. This lends itself very nicely to setting account flags, changing addresses, updating records etc. Coupled with the power of SQL stored procedures, you could update the database directly although this is highly unsupported.

Productive JavaScript development in Microsoft Dynamics CRM 4.0

Posted by AnonymousDeveloper | Posted in CRM Development, Javascript Snippets | Posted on 12-05-2009

0

1)  Use an external JavaScript file while doing development.  It will save you a lot of time.  Regularly you make a code change and then you need to save and publish every time you want to retest.  Yes, the preview feature is helpful some of the time, but it is still very time consuming.   Using an external file will also allow you to leverage Visual Studio which offers IntelliSense and syntax checking.

Add this code to your onload.  It loads a JS file from within your ISV folder.   It uses a query string parameter to prevent caching and it waits until the dom is loaded before it executes the code.

var head = document.getElementsByTagName(‘head’)[0];
var script = document.createElement(’script’);
script.type= ‘text/javascript’;
script.src= ‘/ISV/Entity/accounttesting.js?noCache=’ +  Math.random();
script.onreadystatechange= function () {
if (this.readyState == ‘complete’ || this.readyState == ‘loaded’)
{
document.OnLoadCode()
}
}
head.appendChild(script);

Second, you can add calls in your OnSave and OnChange events to call code in the JavaScript file.  This will allow you to do all development in the one file.
OnSave:  document.OnSaveCode();
OnChange of account name:  document.OnChangeCode_AccountName();

Third, add a file name accounttesting.js to your /ISV/Entity folder under the website folder.  It will contain the following code.
document.OnLoadCode = function()
{
   alert("OnLoad code");
}

document.OnSaveCode = function()
{
   alert("OnSave code");
}

document.OnChangeCode_AccountName = function()
{
    alert("OnChange of Account Code");
}

// other functions
Last, start testing.  Any time you make a code change to the file you can refresh the account screen (F5) and the newest code will be executed.  Instant results!!!

Disclaimer:  You should only use this for development.  After you have tested your changes using this method you should copy the code back into the OnLoad and comment out the code that loads the JavaScript file.  Keeping the file in your OnLoad will save you time and headaches in deployment.  Plus, this is not supported.

2)  Use the debugger statement in your code if you want to walk through code.  You are able to leverage Visual Studio to analyze variable values as you execute your code.  Very useful!

First, within Internet Explorer, uncheck “Disable script debugging  (Internet Explorer)” and “Disable script debugger (Other)”

Next, add following code before any code you would like to debug.

debugger;

3) Use a helper library for calling the CRM web service through AJAX.    Calling the CRM web service requires you to work with the XML request using string concatenation which is very error-prone.  The libraries provide an object-oriented API to return data from CRM.  They also provide better error handling to help you troubleshoot errors

You can use my helper objects Click Here.  Ascentium has one as well Click Here.  Their library uses fetchxml while mine uses RetrieveMultiple.

4) Use Intellisense to help prevent mistakes as you code.  The book CRM as a Rapid Development Platform comes with a excellent Intellisense generator for Visual Studio.  http://www.thecrmbook.com/Video/Customization/EnableCRMJavascriptIntellisense/viewvideo.aspx.  It is well worth the 50 dollar investment.

You point the generator application against your instance of CRM and it generates JavaScript files for you to reference in your JS file.  Now you have Intellisense.  No more copy and pasting the field name from the forms window to make sure you don’t mis-spell it.  No more bugs because you spelled the word crmForm as crmform.

5)  Follow the DRY principle.  Don’t repeat yourself.  If you need to hide a field both onload and when a specific dropdown is selected, don’t copy and paste the code around.  Add functions to OnLoad and call the function wherever you need to.  Also, as you find useful code, such as hiding and showing fields, create functions for them and re-use them from project to project.

6)  Use try catch statements where possible.  Depending on your browser setings, it may be hard to determine what error is actually being thrown, especially in OnLoad.  Catching the error message and displaying an alert will help save time while troubleshooting errors.

try

   // Implementation here
}
catch (er)
{
alert(er.message);
}

 

First posted by Andrew Zimmer of Inetium

How to detect a duplicate entry in Microsoft CRM 3.0

Posted by AnonymousDeveloper | Posted in CRM Development, Javascript Snippets | Posted on 26-03-2009

0

Introduction

CRM systems are great but they are very prone to human error, the ease of data entry is sometimes counterproductive especially if you want to keep your data clean.

Using the code

In this code I used JavaScript for posting data to my detection script and C# for the .ASPX page.

Essentially all I needed to do was test against a single field.
On the change event of an input field in the Account entity I sent some data to a web service using JavaScript and returned a response.
If the response indicated that an account already exists than I gave the user the option to open the existing account’s form.

The C# Code:
Essentially, I have the Fetch xml that will be sent to CRM 3.0 via the API.
In this particular case I needed to test the accountnumber field for duplicates.
I pass the account number to my script via a form post (will be shown later), that is why i am testing for the Request["AccountNumber"].
After building the xml the script sends the request to CRM and retrieves the response.
Load the xml into a System.Xml.Xm lDocument object and test to see if the number of rows returned is more than zero.
If it is more than zero, for my purposes the script returns the accountid.

 

 crmService.CrmService crm;
  // Put user code to initialize the page here
  crm = new CrmService();
  crm.Credentials = System.Net.CredentialCache.DefaultCredentials;

  string fetchxml = "<fetch mapping='logical'>";
  fetchxml+= "<entity name='account'>";
  fetchxml+="<all-attributes/>";
  fetchxml+="<filter type='and'>";
  fetchxml+=    "<condition attribute='accountnumber' operator='eq' value='{0}'/>";
  fetchxml+=    "</filter>";
  fetchxml+=    "</entity>";
  fetchxml+="</fetch>";

 if(Request["AccountNumber"]!="")
  {

  fetchxml = string.Format(fetchxml,Request["AccountNumber"]);
  string xmlResponse = crm.Fetch(fetchxml);
  System.Xml.XmlDocument xdoc = new System.Xml.XmlDocument();
  xdoc.LoadXml(xmlResponse);

 if(xdoc.SelectNodes("resultset/result").Count>0)
  {
     Response.Write(xdoc.SelectNodes("resultset/result")[0]["accountid"].InnerText);
  }else
  {
     Response.Write("0");
  }
 }else
  {
    Response.Write("0");
  }

 

The JavaScript:

This portion can be used in either the OnSave event for any entity or the OnChange event for any field.

I don’t need to worry about browser incompatibility when developing for Microsoft CRM 3.0, because the platform was built to be used in IE6+.

I create the URL along with the data of the field i want to test against, and use the Microsoft.XMLHTTP ActiveX object to post it to your script.

If you take a look at the C# code for the script, it returns "0" if nothing is found and the acount id otherwise.

At this point i ask the user if they would like to be redirected to the existing account.

 

var url = "http://localhost/CheckDuplicateAccounts/CheckDuplicateAccount.aspx?AccountNumber="
+ crmForm.all.accountnumber.DataValue;
var xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
xmlhttp.open("POST", url, false);
xmlhttp.setRequestHeader("Content-Type", "application/x-www-form-urlencoded; charset=utf-8");
xmlhttp.send("");



var result = xmlhttp.responseText;
if(result != "0")
{
  if(confirm("This account number already exists in the system, would you like to open the
EXISTING account?"))
  {
    document.location = "http://"+document.domain+":5555/sfa/accts/edit.aspx?id=" + result;
  }
}

How To : Making a field Business Required with Javascript

Posted by AnonymousDeveloper | Posted in CRM Development, Javascript Snippets | Posted on 06-03-2009

0

A recent client of mine had a number of very complex requirements within a new CRM implementation. One of these was that certain fields became 'Business Required' based on a certain set of criteria. I really wanted this to be a client side fix so I began investigations. Here is what I discovered :

Make the field 'Business Required'

My code was placed in the onLoad event of the form, but you could just as easily use the 'OnChange' event of an attribute instead.

SetFieldRequiredOrRecommended(crmForm.all.parentcustomerid_c,FORM_FIELD_TYPE_REQUIRED,
LOCID_FORM_REQUIRED_ALT);
crmForm.all.parentcustomerid.req = 2;

Make the field 'No Requirement'

SetFieldRequiredOrRecommended(crmForm.all.parentcustomerid_c,FORM_FIELD_TYPE_NORMAL,"");
crmForm.all.parentcustomerid.req = 0;

Make the field 'Business Recommended'

SetFieldRequiredOrRecommended(crmForm.all.customertypecode_c,
FORM_FIELD_TYPE_RECOMMENDED,"");
crmForm.all.customertypecode.req = 1;

Where did that function come from?

The function 'SetFieldRequireOrRecommended' is actually found in the Global.js file within CRM. You can find this file in (typically) C:\Program Files\Microsoft Dynamics CRM\CRMWeb\_static\_common\scripts

 

Practical Uses…

if (crmForm.all.customertypecode.DataValue == 3) {
  crmForm.SetFieldReqLevel("accountnumber", 1);
}
else {
  crmForm.SetFieldReqLevel("accountnumber", 0);
}

Other Useful Functions : CopyTextToClipboard

You can use any of the functions within Global.js. For instance, onChange of a particular attribute, I would like to copy the value of another to the clipboard for use with 'Ctrl-V'. Add the following to the OnChange event of your chosen attribute….

var sMessage = "Success!";
var sFailMessage = "Failure - boo!";
CopyTextToClipboard(crmForm.all.telephone1.DataValue, sMessage, sFailMessage);

How to hide buttons on CRM Forms

Posted by AnonymousDeveloper | Posted in CRM Configuring, CRM Development, Javascript Snippets | Posted on 27-02-2009

0

One of the most common operations we do is to hide certain buttons within the CRM using DHTML. This ought to have been relatively easy and using CRM3 it was. You simply used the following :

document.getElementById('[yourcontrolnamehere]').style.display = "none";
 

Unfortunately, things have changed in CRM 4 and not for the better. In CRM 4.0 there is some auto-incremented number or randomly generated number that gets placed at the end of all of the ElementId’s on a page. Which means that the code used for CRM 3.0 will break.

Recently, due to some operational reasons, my client wanted to disable the ‘Convert Campaign Response’ button on the Campaign Response Form.

ConvertCampaignResponseButton

So, there are a number of ways this can be achieved. First of all, we need to know the actual name of the control we are about to hide. I find that the IE Developer ToolBar is one of the very best methods of achieving this. You can download the tool bar from HERE . When you have downloaded and installed the toolbar, you will be able to use it to find the actual name of the control inside CRM.

1) Navigate to the form you wish to check

2) Press F11 and then the Toolbar Icon

Devtoolbar

ClickFind 

3) Click ‘Find’ and then ‘Select Element By Click’

4) Select the button that you want to hide.

ConvertCampaignName

This handy little tool gives you the names of all elements on the page and now, all you have to do is hide them. There are a number of solutions out there.

Solution Number One

var Hide = function(menuItem){
if (document.getElementById(menuItem) != null)
{
document.getElementById(menuItem).style.display = "none";
}
}
 
var RemoveSpacerAfter = function(menuItem){
if (document.getElementById(menuItem) != null)
{
var item = document.getElementById(menuItem);
if (item.nextSibling != null)
item.nextSibling.style.display="none";
}
}
 
Hide('_MBConvertResponse');
RemoveSpacerAfter('_MBConvertResponse');

 

Solution Number Two

//Get all of the List Elements
var lis = document.getElementsByTagName('LI');
 
var i = 0;
//Loop through the list items
while (i < lis.length) {
//Don't worry about any list item that doesn't have the title you are looking for.
if (lis[i].getAttribute('title') == 'View directions to this account.')
{
//Replace the DHTML with blank tags to hide the button
lis[i].outerHTML='<SPAN></SPAN>'
}
i = i + 1;
}

You can also delete menu options from the Actions menu on any entity. Here’s the same code again but removing the ‘Delete  Case’ menu item from the Actions Menu

//Get all of the List Elements
var lis = document.getElementsByTagName('LI'); 
var i = 0;
//Loop through the list items
while (i < lis.length) 
{ 
//Don't worry about any list item that doesn't have the title you are looking for.
if (lis[i].getAttribute('id') == '_MIonActionMenuClickdelete112')
{ 
//Replace the DHTML with blank tags to hide the button
lis[i].outerHTML='<SPAN></SPAN>'
}
i = i + 1;
}

 

 Item Names

Item names are also fairly easy to figure out. You can view them by opening the relevant form, pressing Ctrl-N and then ‘View’, Source. Here are some basic ones that are much the same for all entities.

Actions Menu

Add Activity = _MIlocAddActTo4401

Add a Note = _MIlocAddObjTo5

Attach a file = _MIlocAddFileTo5

Delete Account = _MIonActionMenuClickdelete1 (Entity: Contact would be 2)

Mail Merge = _MIonActionMenuClickwebmailmerge1 (Entity:Contact would be 2)

Sharing = _MIonActionMenuClickshare1

 

General Buttons

Jewel Button = ms-crm-Menu-JewelButton

Save = _MBcrmFormSave

Save & Close = _MBcrmFormSaveAndClose

Print = _MBcrmFormPrint

 

A word of explanation for the Delete menu item. The number at the end of the id is in fact the ObjectTypeCode for the entity. So, the name of the delete button for the Accounts entity has the number 1 appended, contacts has the number 2 and so on. Once you have the hang of the naming conventions, it is pretty easy to guess what any given button or menu item is likely to be called. Once you have this information, you can view the source, search for the value and then confirm the correct information prior to applying the javascript.

Using the DOM in Microsoft CRM 4.0

Posted by AnonymousDeveloper | Posted in CRM Development, Javascript Snippets | Posted on 04-11-2008

0

One of the beauties of CRM is the ability to use client side code, in particular, I am fond of attachEvent(). Here is a brief demonstration of how to change the colours of a field within crmForm using the DOM and attachEvent(). It should be noted that this particular little gem is from the department of ‘Unsupported but fun to do anyway’.

Attach this to the Onload event of the Account Form (or any form you like, just change the references)…

function CompanyName_GlowField()
{
/* The Event’s SrcElement will be the element that fired
the event in this case, it will be the Microsoft Dynamics
CRM form element */
event.srcElement.runtimeStyle.backgroundColor =”#ffdddd”;
}
function CompanyName_RevertField()
{
/* Runtimestyle will automatically revert the style back to
what it was prior to our code changing it. */
event.srcElement.runtimeStyle.backgroundColor =”";
}
/* Hook up events to the Account Name field */
crmForm.all.name.attachEvent(“onmouseover”,
CompanyName_GlowField);
crmForm.all.name.attachEvent(“onmouseout”,
CompanyName_RevertField);

 

As you can see, this is quite a powerful little feature. You can add custom logic to this function to further enhance the effect, perhaps changing colours depending on the value, a range of values or a particular dependency.

Have fun.

Count Activities / History

Posted by AnonymousDeveloper | Posted in CRM Configuring, CRM Development, CRM General, CRM General Resources, Javascript Snippets | Posted on 11-08-2008

0

This script (again by Jim Wang) will ‘count’ the number of activities and history records there are for a record and show the results Hotmail style as a bracketed number in the link.

 
var buXml = GetRegardingActivity();
 
if(buXml != null)
{
    var buNodes = buXml.selectNodes("//BusinessEntity/statecode"); // CRM 3.0
 
    //var buNodes = buXml.selectNodes("//BusinessEntity/q1:statecode"); // CRM 4.0
    var iActivity = 0;
    var iHistory = 0;
 
    if(buNodes != null )
    {
        /*get values*/
        for( i = 0; i < buNodes.length; i++)
        {
            switch(buNodes[i].text)
            {
                case "Open" : iActivity++; break;
                case "Scheduled" : iActivity++; break;
                case "Completed" : iHistory++; break;
                case "Canceled" : iHistory++; break;
            }
        }
 
        if(document.getElementById('navActivities') != null)
        {
            document.getElementById('navActivities').getElementsByTagName('NOBR')[0].innerText = document.getElementById('navActivities').getElementsByTagName('NOBR')[0].innerText + " (" + iActivity + ")";
        }
 
        if(document.getElementById('navActivityHistory') != null)
        {
            document.getElementById('navActivityHistory').getElementsByTagName('NOBR')[0].innerText = document.getElementById('navActivityHistory').getElementsByTagName('NOBR')[0].innerText + " (" + iHistory + ")";
        }
    }
}
 
function GetRegardingActivity()
{
    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\">" +
    " <soap:Body>" +
    " <query xmlns:q1=\"http://schemas.microsoft.com/crm/2006/Query\" xsi:type=\"q1:QueryExpression\" xmlns=\"http://schemas.microsoft.com/crm/2006/WebServices\">" +
    " <q1:EntityName>activitypointer</q1:EntityName>" +
    " <q1:ColumnSet xsi:type=\"q1:ColumnSet\">" +
    " <q1:Attributes>" +
    " <q1:Attribute>statecode</q1:Attribute>" +
    " </q1:Attributes>" +
    " </q1:ColumnSet>" +
    " <q1:Distinct>false</q1:Distinct>" +
    " <q1:Criteria>" +
    " <q1:FilterOperator>And</q1:FilterOperator>" +
    " <q1:Conditions>" +
    " <q1:Condition>" +
    " <q1:AttributeName>regardingobjectid</q1:AttributeName>" +
    " <q1:Operator>Equal</q1:Operator>" +
    " <q1:Values>" +
    " <q1:Value xsi:type=\"xsd:string\">" + crmForm.ObjectId + "</q1:Value>" +
    " </q1:Values>" +
    " </q1:Condition>" +
    " </q1:Conditions>" +
    " </q1:Criteria>" +
    " </query>" +
    " </soap:Body>" +
    "</soap:Envelope>" +
    "";
 
    var xmlHttpRequest = new ActiveXObject("Msxml2.XMLHTTP");
    xmlHttpRequest.Open("POST", "/mscrmservices/2006/CrmService.asmx", false);
    xmlHttpRequest.setRequestHeader("SOAPAction","http://schemas.microsoft.com/crm/2006/WebServices/RetrieveMultiple");
    xmlHttpRequest.setRequestHeader("Content-Type", "text/xml; charset=utf-8");
    xmlHttpRequest.setRequestHeader("Content-Length", xml.length);
    xmlHttpRequest.send(xml);
 
    var resultXml = xmlHttpRequest.responseXML;
    return resultXml;
}

I have tested this myself and it causes the same problems as described here. You should note that this script should be modified with a switcher to take account of the differences in XMLHttpRequest handling between the browser versions.

Use Javascript to execute a CRM Workflow

Posted by AnonymousDeveloper | Posted in CRM Configuring, CRM Development, CRM General, CRM General Resources, Javascript Snippets | Posted on 11-08-2008

0

I love this method. Thanks to Jim Wang for this very useful script.

/* the function */
ExecuteWorkflow = function(entityId, workflowId)
{
    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>" + 
    "    <Execute xmlns=\"http://schemas.microsoft.com/crm/2007/WebServices\">" + 
    "      <Request xsi:type=\"ExecuteWorkflowRequest\">" + 
    "        <EntityId>" + entityId + "</EntityId>" + 
    "        <WorkflowId>" + workflowId + "</WorkflowId>" + 
    "      </Request>" + 
    "    </Execute>" + 
    "  </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/Execute");
    xmlHttpRequest.setRequestHeader("Content-Type", "text/xml; charset=utf-8");
    xmlHttpRequest.setRequestHeader("Content-Length", xml.length);
    xmlHttpRequest.send(xml);
    var resultXml = xmlHttpRequest.responseXML;
    return(resultXml.xml);
}
 
/* call */
var theWorkflowId = "3FD2DD58-4708-43D7-A21B-F0F90A0AA9F2"; //change to your workflow Id
ExecuteWorkflow(crmForm.ObjectId, theWorkflowId);

However, further to my previous post about XMLHttp request, it should be noted that you will require a ’switcher’ to differentiate between browser versions that use this function.

See here

How To : Pop Up Notes

Posted by AnonymousDeveloper | Posted in CRM Configuring, CRM Development, CRM General, Javascript Snippets | Posted on 11-08-2008

0

I recently came across a new requirement and that was to view the related notes of a record without having to actually enter the record. Dynamics offers this functionality by allowing us to add buttons within the ISV, so there are in fact two ways of doing this.

Option 1 : From the Grid View

1) Add a new button to the ISV and change the ‘winmode’ parameter to 2

2) Note that for some reason (still to be investigated) buttons from the Grid view do not pass the parameters of the selected record properly. No, I have no idea why and would welcome any feedback on this.

3) Add the following code to a blank page and call it from the ISV button

 
         function listselecteditems()
         {
            var placeholder = document.getElementById("test");
            var sGUIDValues = "";
            var selectedValues;
            //Make sure window.dialogArguments is available.
            if (window.dialogArguments)
            {
               selectedValues = new Array(window.dialogArguments.length -1);
            }
            else
            {
               placeholder.innerText = "window.dialogArguments is not available.";
               return
            }
            selectedValues = window.dialogArguments;
            if (selectedValues != null)
            {
               for (i=0; i < selectedValues.length; i++)
               {
                  sGUIDValues += selectedValues[i] +"\n";
               }
               var notesUrl = "/_controls/notes/notesdata.aspx?id="+ selectedValues + "&ParentEntity=112&EnableInlineEdit=false&EnableInsert=false"
                            document.write('<Iframe id="PopUpNotes" name="PopUpNotes" src="'+notesUrl+'" width="100%" height="90%" scrolling="yes" frameborder="0"></iframe>')
                             
                            
            }
            else
            {
               placeholder.innerText = "No records were selected.";
            }
         
                 }

 

Explanation of Option 1

As you can see, this is a modification of the code using in the SDK to return the GUID’s of selected records. It uses the window.dialogArguments method and all we have done is add an appropriate IFrame to the popup page. The GUID is matched from the dialogArguments to the related notes within the entity and the Iframe shows the results. It should be noted that this method does not allow notes to be edited or appended. I suspect this is because the Iframe is not passing the information back to the CRM in order to perform this action.

Option 2 : From the Form View

Although you are probably asking yourself why you would want to do this since each form has a native ‘notes’ tab, you would be right. This example demonstrates how to take the information from the querystring and use that information with Javascript to achieve the same goal. Now, if I understood why the button on the Grid view does not pass the parameters properly, I could use this method from Option 1 and have truly popup notes without having to use dialogArgumets.

1) Add yourself a new button within ISV but this time, you can use any winmode you like including 0 or 1

2) Paste this code into a clean js file and call it from the button

function parseSearchString()
{
var pairs=unescape(location.search.substring(1).replace(/\+/g," ")).split('&');
for (var i=0;i<pairs.length;i++){
var pair = pairs[i].split('=');
this[pair[0]]=pair[1];
}
}
var search = new parseSearchString();
 
function listselecteditems()
                 
{
var notesUrl = "/_controls/notes/notesdata.aspx?id="+ search["id"] + "&ParentEntity=112&EnableInlineEdit=false&EnableInsert=false"
//output passed variables
document.write('<div id="test">');
document.write('<Iframe id="PopUpNotes" name="PopUpNotes" src="'+notesUrl+'" width="100%" height="90%" scrolling="yes" frameborder="0"></iframe>')
document.write('</div>');
 
}     
    

Explanation of Option 2

As you can see, this code takes the URL, breaks it into little pieces and then using the piece that we need (the GUID) to return the correct records within the Iframe. This approach can be extended to accommodate any ‘related’ information for the record and not just notes.