Archive for the ‘JavaScript’ Category

Dynamics CRM 2013 for some reason has decided to remove the Copy a Link button from forms, and instead only shows the Email a Link button. The button still exists on views, however I often find myself on a form wanting to get the URL of the record so I can do something with it. Since the record URL is no longer available in the address bar, the only option is to pop the record and then copy the ID, or use Email a Link and copy it out of the email (doesn’t work if you’re on a server with no email client).

Since custom bookmarklets seem to be trending right now, I decided to make my life a little bit easier by creating a custom bookmarklet to copy the record URL. Since the button still actually exists, and is just hidden in the new UI, it was easy enough to open up the Ribbon Workbench and examine the function being called from the button. It was then just a matter of testing it out to see what parameters the function actually requires (which turns out it doesn’t need any). And finally just combining that with the JavaScript to get the active forms iframe, as found by Jared Johnson in his blog post on creating custom bookmarklets.

To add this custom bookmarklet, create a new bookmark called ‘Copy a Link’, and set the URL to use the following script:

javascript:$("iframe").filter(function () { return ($(this).css('visibility') == 'visible') })[0].contentWindow.Mscrm.CommandBarActions.sendFormShortcut();

 

When we click the button, the CRM function will fire to copy the record URL, and we can see the link pasted into Notepad exactly how CRM 2011 would. This button will only work on an open record, and not on a list view for example (although the CRM command bar button still exists on views). It will also be available and will work on any CRM 2013 environment you log into, so there’s no need to install any additional solutions or add-ins.

Stay tuned for more bookmarklets coming soon!

In a previous blog post by Jared Johnson, he showed how to create some custom browser bookmarks/favourites to help make some common tasks easier in 2013. These included easily copying the ID of a record, and quickly refreshing the data on the form if it has changed by a workflow etc. He also explained how to get the current active iframe so that we can access all of the forms Xrm.Page functions.

In my next few blog posts I’m going to explore some of the other possibilities available to us with custom bookmarkets, starting with how to restore the Save, Save & Close, and Save & New buttons so that they’re always available to us.

So often I find myself pointing into the top left corner to save my changes, and then remember that I need to go into the opposite corner, or just navigate away. I idea of these bookmarklets is that my new custom bookmarks will sit at the start of my bookmarks bar, so that when I go reaching for that save button, I just have to go a little further to get it. Another added benefit is that we can bring back the Save & New button, which has been removed entirely from CRM 2013, making it difficult to create multiple related records from a parent while maintaining the mappings.

To add these 3 custom bookmarklets, simply create some new bookmarks, and set the URL’s to use the following scripts:

Save:

javascript:$("iframe").filter(function () { return ($(this).css('visibility') == 'visible') })[0].contentWindow.Xrm.Page.data.entity.save();

 

Save & Close:

javascript:$("iframe").filter(function () { return ($(this).css('visibility') == 'visible') })[0].contentWindow.Xrm.Page.data.entity.save('saveandclose');

 

Save & New:

javascript:$("iframe").filter(function () { return ($(this).css('visibility') == 'visible') })[0].contentWindow.Xrm.Page.data.entity.save('saveandnew');

 

Whenever we are on a record now, whether it’s new or existing, we can use these buttons. The buttons will of course show up whether you’re on a form or not, however it also means you can use these buttons in any CRM 2013 system; so for someone like me, who needs to use several CRM environments at once, this definitely comes in handy.

Since we have access to all the functions of Xrm.Page, the possibilities really are endless as to what we can do with these bookmarklets. In my next few blog posts I’ll go through some other useful buttons we can add to build up our own custom command bar.

Since CRM 2013 now opens forms in the same browser window as the view, it makes it more difficult to access advanced find, which by default is only available from list views.

In this blog post I will describe how to customize the command bar to add an advanced find button onto all forms in the system, so that advanced find can be launched from anywhere, whether on a view or a form.

First you need to create a solution with the Application Ribbons, and a javascript web resource.

Add the following function into the web resource, which is what will be used to launch the advanced find window:

function openAdvFindCustom(etc) {
    var oUrl = Mscrm.CrmUri.create("")
    oUrl.get_query()["EntityCode"] = etc;

    var oMainUri = Mscrm.CrmUri.create("/main.aspx");
    oMainUri.get_query()["pagetype"] = "advancedfind";
    oMainUri.get_query()["extraqs"] = oUrl.get_queryString();
    openStdWin(oMainUri, "_blank", 900, 600);
}

Next you need to customize the command bar XML. Using the CRM 2013 Ribbon Workbench will make this easier. Edit the solution to export the ApplicationRibbon. This is the global ribbon which will be applied to all entities.

Add a new button onto the Form command bar. I’ve added mine just after the ‘Delete’ button. You can copy the icons, labels, and description from the CRM advanced find button on the home command bar.

Finally you need to create a custom command to open the JavaScript function openAdvFindCustom from the web resource, and pass in the PrimaryEntityTypeCode parameter.

Take a backup of the solution before publishing, just in case something goes wrong so you can roll back. Then hit publish at the top of the ribbon workbench. Once the changes are published, refresh CRM and open a form. You should see the new Advanced Find button, which when clicked will open a new Advanced Find window just like it does from the list view.

Note that this JavaScript is unsupported, and could break with future updates.

If you want a more supported solution, you can call the default advanced find button function directly, which means you don’t need your own web resource and JavaScript. However note that this method won’t open advanced find to the entity you’re on, instead it will always default to the first entity in the list. You will still need to create a custom command, with the Function Name as Mscrm.RibbonActions.openAdvancedFind and the Library as /_static/_common/scripts/RibbonActions.js. For this you don’t need to pass any parameters. I’d say this is still unsupported, but it’s using a CRM function instead of a custom one, so it’s a bit nicer if you don’t mind it not opening the entity you’re on.

In a previous blog post of mine I showed how to filter the N:N Add Existing button in CRM 2011. Since the upgrade to CRM 2013, this no longer works. Given that the code was unsupported, this was expected. Since CRM 2013 has changed the UI, the method in which N:N’s work has also changed.

The old code relied on a CRM function ‘LookupObjects’, which returned the records the user selected from the lookup. Since 2013 now uses ‘light-box’ pop-ups, rather than browser pop-ups, the JavaScript cannot be halted while the user selects their records. Because of this, CRM now uses callback functions to continue processing after the user has selected records from the lookup.

The new function is called ‘LookupObjectsWithCallback’, and is exactly the same as the old function, except that it takes a callback function as the first parameter. I’ve pulled apart the CRM functions, and reconstructed them into my own function, which allows us to pass in a custom view as the default.

The new updated code is below:

//filters an add existing lookup view (N:N)
function addExistingFromSubGridCustom(gridTypeCode, gridControl, context, fetch, layout, viewName) {
    var viewId = "{1DFB2B35-B07C-44D1-868D-258DEEAB88E2}"; // a dummy view ID
    var relName = gridControl.GetParameter("relName");
    var roleOrd = gridControl.GetParameter("roleOrd");

    //creates the custom view object
    var customView = {
        fetchXml: fetch,
        id: viewId,
        layoutXml: layout,
        name: viewName,
        recordType: gridTypeCode,
        Type: 0
    };

    var parent = GetParentObject(null, 0);
    var parameters = [gridTypeCode, "", relName, roleOrd, parent];
    var callbackRef = Mscrm.Utilities.createCallbackFunctionObject("locAssocObjAction", context, parameters, false);

    //pops the lookup window with our view injected
    LookupObjectsWithCallback(callbackRef, null, "multi", gridTypeCode, 0, null, "", null, null, null, null, null, null, viewId, [customView]);
}

//filters the Contact N:N lookup view from Account to show only Pauls!!
function filterAddExistingContact(gridTypeCode, gridControl, primaryEntity) {
    if (primaryEntity != "account") {
        Mscrm.GridRibbonActions.addExistingFromSubGridAssociated(gridTypeCode, gridControl); //default button click function
        return;
    }

    //fetch to retrieve filtered data
    var fetch = "<fetch version='1.0' output-format='xml-platform' mapping='logical' distinct='false'>" +
        "  <entity name='contact'>" +
        "    <attribute name='fullname' />" +
        "    <order attribute='fullname' descending='false' />" +
        "    <filter type='and'>" +
        "      <condition attribute='statecode' operator='eq' value='0' />" +
        "      <condition attribute='firstname' operator='eq' value='Paul' />" +
        "    </filter>" +
        "  </entity>" +
        "</fetch>";

    //columns to display in the custom view (make sure to include these in the fetch query)
    var layout = "<grid name='resultset' object='1' jump='contactid' select='1' icon='1' preview='1'>" +
        "  <row name='result' id='contactid'>" +
        "    <cell name='fullname' width='300' />" +
        "  </row>" +
        "</grid>";

    addExistingFromSubGridCustom(gridTypeCode, gridControl, this, fetch, layout, "Filtered Contacts");
}

The changes are mostly to the first function, however the second function has also been updated to pass ‘this’ into the other function. For this example, I have created a N:N between Account and Contact, and am filtering the ‘Contact’ view from the Account side to show only contacts with a first name of ‘Paul’.

For step by step instructions on how to customize the ribbon to get this working, check out the 2011 post, which has not changed: http://www.magnetismsolutions.com/blog/paulnieuwelaar/2013/02/04/filter-n-n-add-existing-lookup-dynamics-crm-2011-rollup-12

You will also need the Ribbon Workbench for CRM 2013.

Note that this method uses the old popup window where you select multiple records at once, rather than the new CRM 2013 inline lookup where you select 1 at a time (personally I prefer the pop-up). This functionality is still unsupported, and is likely to break with any major updates.