Showing posts with label Javascript. Show all posts
Showing posts with label Javascript. Show all posts

Tuesday, 6 September 2016

Utilize Custom Action to Help Filtering The Lookup View in CRM Form

Overview

Sometimes in our project, we have requirement to filter lookup based on some conditions and it can be achieved using addCustomView or addCustomFilter function.

And sometimes it is just not too easy to do it in Javascript or the complex fetch xml, so in my blog I just want to share another method to get the filtered result same as you wanted, that is using Javascript + Custom Action!

Detail

Following my previous post: http://missdynamicscrm.blogspot.sg/2014/08/crm-2013-using-addcustomfilter-to-get-filtered-lookup-field-based-on-linked-entity.html

So, considering you have this filter:
image

To get the result as per expected, you can use Custom Action.

Steps:

1. You need to create a custom action that give you output, either STRING or ENTITYCOLLECTION

2. Inside the custom action code, if you want to return string, you can use comma delimited concept, or using | as delimiter, or you can just return the Final XML Filter already.

If you use EntityCollection, you might need to parse it again.

3. Then create a javascript that can call the Custom Action, you can use this method for easy way:
http://www.magnetismsolutions.com/blog/paulnieuwelaar/2015/08/12/Call-Action-in-CRM-2015-Easily-from-JavaScript-Library

4. Then you get the result as parameter, you can just set it to the filter = “the Result” (if you use Final XML Filter as the Output) or you parse the GUID if you use the comma or | delimited concept, or if you use EntityCollection then you need to parse it back.

5. See the result

Basically, you just need to get this result:

image

Either you just easily using String as output or other method it is up to u.

But the point here is we can use Custom Action for solving complex filtering and remember that we can use impersonation also to get the data you want if it is related to the other entities as well, imagine if we also need to have multiple entities involved then it might be easier if we use Custom Action as we just replace the DLL if there is any other changes using Plugin Registration Tool.

Hope this helps!

Thanks,
Aileen

Modify the Delete (Dustbin icon) Button in the CRM Subgrid

Overview

Sometimes we need to modify the Delete button in the CRM Subgrid, example:

1. For preventing users to perform the delete button (but you dont want to just disable it)

2. Call another function or call custom function that needs client site programming (We can do plugin onDelete or onAssociate, but in case you want to show it in the client site)

3. To do impersonation

The Code

function modifyRibbon() {
    modifySubgridDeleteButtonEventHandler("subgrid_name_in_the_form", deleteSubgridRecord, true);
    discountStructureControl();
}

function deleteSubgridRecord() {
    alert("test");
}

function modifySubgridDeleteButtonEventHandler(subgridName, functionToCall, passGridControl) {
    try {
        //to store the original function ones
        var originalFunctionDeleteRecordSubgrid = Mscrm.GridCommandActions.deleteRecords;
        debugger;
        //add new standard subgrid
        Mscrm.GridCommandActions.deleteRecords = function (selectedControl, selectedControlSelectedItemReferences, selectedEntityTypeCode) {
            //if (typeof (gridControl.get_id).toString().toLowerCase() == "undefined") {} //no need since I replaced by the previous line
            if (selectedControl.get_id() != subgridName) {
                originalFunctionDeleteRecordSubgrid(selectedControl, selectedControlSelectedItemReferences, selectedEntityTypeCode);
            }
            else {
                if (passGridControl) {
                    functionToCall(selectedControl);
                }
                else {
                    functionToCall();
                }
            }
        }
    }
    catch (e) {

    }
}

Result

*After clicking the ‘Delete’ button

image

Note: This method is overwriting the CRM functions and it works for CRM 2013, for CRM 2015/2016, this function [Mscrm.GridCommandActions.deleteRecords] might have been changed, so need to find out the current function name based on your CRM Version. And again, it means it is unsupported Smile

Thanks!

Wednesday, 17 August 2016

Insert Label After the Field in CRM Form using Javascript

Hi all,
Long time I did not blog any post due to workload.
And this is my first post in this month (I know it is end of the month already)
This post basically a quick one because I have the requirement.

So, the users just want to have guidance when they entering inputs in the system.

Which we can have these solutions:

1. Tooltip (using CRM Field description)

See my post here:
http://missdynamicscrm.blogspot.sg/2014/05/crm-20112013-power-of-description.html

2. Using HTML Web Resource

This is a supported way and very useful as well. But it will make the form slower because will load the web resource and how many web resource you should create and insert. Well, you can create 1 common .html then pass the label text as the parameter

3. Using other way

So, unfortunately if those 2 solutions still not the best choice, we could go to another way using the help of CSS and Javascript.

*This is unsupported way…

And here is the Code

The Code


function insertLabelToField(fieldname, text, objSpan) {
    var elemDiv = document.getElementById(fieldname + "_c");
    //var tbl = document.createElement('table');     //elemDiv.parentNode.insertBefore(tbl, elemDiv.nextSibling);
    //var tr = tbl.insertRow();     //var td1 = tr.insertCell();     //td1.appendChild(elemDiv);     var td1 = elemDiv;     //td1.style.borderRight = "2px solid #c1c7c4";     td1.style.paddingBottom = "5.5px";
    var spanPref = null;
    if (!objSpan) {         spanPref = document.createElement('span');                 //you can change the color condition here         if (attr(fieldname).getRequiredLevel() == "required") {             spanPref.style.color = '#e31a1a';         }         else {             spanPref.style.color = '#4b4c54';         }         spanPref.style.fontSize = '10.51px';         spanPref.style.fontStyle = 'italic';     }     else {         spanPref = objSpan;     }     spanPref.id = 'spanPref_' + fieldname;         td1.appendChild(spanPref);     document.getElementById('spanPref_' + fieldname).innerText = text; }

How to Call

 
insertLabelToField(“fieldname”, "(Please describe me)");


And here is the Result

image

Hope this helps.

Thanks

Wednesday, 10 August 2016

Modify Subgrid Ribbon Behaviour Using Form Script

Introduction

A Quick post, just want to post how to Modify Subgrid button behaviour to always “Create New child record”, instead of “Add Existing record”.

This will use the similar method like described in the previous post before, but it does not call the custom funtion, it just will “redirect” the function to your “Create new record”.

Note: This is an unsupported way, for the supported way you can do two things:

1. Make the parent lookup field name to ‘Required’
2. Modify the Ribbon of the subgrid and Hide the ‘Add Existing’ button
*For your reference – > Nice post by Gareth: https://garethtuckercrm.com/2013/11/19/fixing-the-sub-grid-user-experience-in-crm-2013/

But, those will apply to the subgrid regardless the Condition, probably for the second method, you could apply some Enable/Display Rule on the subgrid ribbon.

But, if you want to do it only for specific entity and based on Parent Form condition (can be based on parent fields or even other entity values), then there is another way to do this that I will put the code in this post.

The Code

Ok, so back to the code.

Here is the code:
//function to insist create new record form instead of Add Existing
function openNewRecordFormFromSubgridButton(subgridName, passGridControl) {
    debugger;
    try {
        //to store the original function ones
        var originalFunctionAddNewStandard = Mscrm.GridRibbonActions.addNewFromSubGridStandard;
        var originalFunctionAddExistingStandard = Mscrm.GridRibbonActions.addExistingFromSubGridStandard;
        var originalFunctionAssociated = Mscrm.GridRibbonActions.addExistingFromSubGridAssociated;

        var primaryControl = document.getElementById('crmForm');
        var parentEntityId = Xrm.Page.data.entity.getId();
        var parentEntityTypeCode = Xrm.Page.data.entity.getEntityName();

        //add existing standard subgrid
        Mscrm.GridRibbonActions.addExistingFromSubGridStandard = function (gridTypeCode, gridControl) {
            if (gridControl.get_id() != subgridName) {
                originalFunctionAddExistingStandard(gridTypeCode, gridControl);
            }
            else {
                var primaryControl = document.getElementById('crmForm');
                originalFunctionAddNewStandard(gridTypeCode, parentEntityTypeCode, parentEntityId, null, gridControl);
            }
        }
  
        //and even possible for the N:N (but this is a rare scenario)
        //add associate subgrid (N:N)
        Mscrm.GridRibbonActions.addExistingFromSubGridAssociated = function (gridTypeCode, gridControl) {
            if (gridControl.get_id() != subgridName) {
                originalFunctionAssociated(gridTypeCode, gridControl);
            }
            else {
                var primaryControl = document.getElementById('crmForm');
                originalFunctionAddNewStandard(gridTypeCode, parentEntityTypeCode, parentEntityId, null, gridControl);
            }
        }
    }
    catch (e) {

    }
}

How to call:

openNewRecordFormFromSubgridButton("mysubgrid_name", true);

Result

*Previous
image
As we can see it opens lookup to look to the Existing record, it is 1:N, the record is almost possible already linked to another record, so it will make the users confused when they open this lookup.
*After
image

*And I never set any Required field to the parent lookup value, because this field is NOT always mandatory field, so it is conditional only.

Note: Use this method if you want to control the subgrid plus button based on some conditions and using the Form on Load script (easier), and it only works for the subgrid inside the form that load the function, other subgrid from another parent entity that does not have this function will not be affected, that is the advantage of using this method, to only affect subgrid of the child based on some parent entities and the function will be placed in the Parent Entity Form OnLoad and this is unsupported way.

“This is for dynamically control the subgrid behaviour through parent form onload functions based on some conditions logic, especially for 1:N relationship that usually already linked to another parent record, so that it is supposed to insist to open new Form, instead of asking the user to choose another existing record.

Hope this helps!

Thanks

Friday, 13 May 2016

Filter N to N (Many to Many) Subgrid Lookup for Existing Records in CRM Without Modifying the Ribbon Action

Introduction and Post References

Basically, in this post, I just want to show how to filter the N to N lookup in the subgrid in CRM Form, without even modifying the Global Subgrid Ribbon in CRM.

image

In my previous post: http://missdynamicscrm.blogspot.sg/2015/07/filter-nn-subgrid-in-crm-2013-using.html, I have explained about a way how to filter the N to N Subgrid Lookup, but that is needed to modify the ribbon action globally and you might need to set another condition to actually skip is you need to filter only on specific entity, but your grid will be appearing in many entities, such as:

You want to filter the Contact lookup in, if you modify the subgrid ribbon, in every contact subgrid, the filter will be applied and you might need extra code of criteria to actually limit the  execution.
Good thing about that if your entity only used for single entity and you don’t need to specify is that only for subgrid or associated view, then modifying the ribbon is a better solution. however, if you want to filter only subgrid in specific entity or form, you can actually using another way.

Like you can refer to my posts:


So, in this post, basically I try to combine the ways to become one solution, to filter the N to N lookup that only applicable once the user load the form using single javascript.  This is unsupported customization, but, remember, modifying the N to N view also needs unsupported customization, so yeah, to reach the requirement we have to go further for unsupported customization eventhough strongly I also not recommend.

The Code

So, I just try to combine the code becoming:

*For CRM 2013

function modifyRibbon(subgridName) {
    try {
        //to store the original function ones
        var originalFunctionAddNewStandard = Mscrm.GridRibbonActions.addNewFromSubGridStandard;
        var originalFunctionAddExistingStandard = Mscrm.GridRibbonActions.addExistingFromSubGridStandard;
        var originalFunctionAssociated = Mscrm.GridRibbonActions.addExistingFromSubGridAssociated;
        //add new standard subgrid
        Mscrm.GridRibbonActions.addNewFromSubGridStandard = function (gridTypeCode, parentEntityTypeCode, parentEntityId, primaryControl, gridControl) {
            if (gridControl != null) {
                if (gridControl.get_id() != subgridName) {
                    originalFunctionAddNewStandard(gridTypeCode, parentEntityTypeCode, parentEntityId, primaryControl, gridControl);
                }
                else {
                    originalFunctionAddNewStandard(gridTypeCode, gridControl);
                    filterTrainerProfile(gridTypeCode, gridControl);
                }
            }
        }

        //add existing standard subgrid
        Mscrm.GridRibbonActions.addExistingFromSubGridStandard = function (gridTypeCode, gridControl) {
            if (gridControl != null) {
                if (gridControl.get_id() != subgridName) {
                    originalFunctionAddExistingStandard(gridTypeCode, gridControl);
                }
                else {
                    originalFunctionAddExistingStandard(gridTypeCode, gridControl);
                    filterTrainerProfile(gridTypeCode, gridControl);
                }
            }
        }
        //add associate subgrid (N:N)
        Mscrm.GridRibbonActions.addExistingFromSubGridAssociated = function (gridTypeCode, gridControl) {
            if (gridControl != null) {
                if (gridControl.get_id() != subgridName) {
                    originalFunctionAssociated(gridTypeCode, gridControl);
                }
                else {
                    originalFunctionAssociated(gridTypeCode, gridControl);
                    filterTrainerProfile(gridTypeCode, gridControl);
                }
            }
        }
    }
    catch (ex) {
        alert(ex);
    }
}

*For CRM 2015

function modifyRibbon(subgridName) {
    try {
        //to store the original function ones
         var originalFunctionAddNewStandard = Mscrm.GridCommandActions.addNewFromSubGridStandard;
         var originalFunctionAddExistingStandard = Mscrm.GridCommandActions.addExistingFromSubGridStandard;
         var originalFunctionAssociated = Mscrm.GridCommandActions.addExistingFromSubGridAssociated;
        //add new standard subgrid
        Mscrm.GridRibbonActions.addNewFromSubGridStandard = function (gridTypeCode, parentEntityTypeCode, parentEntityId, primaryControl, gridControl) {
            if (gridControl != null) {
                if (gridControl.get_id() != subgridName) {
                    originalFunctionAddNewStandard(gridTypeCode, parentEntityTypeCode, parentEntityId, primaryControl, gridControl);
                }
                else {
                    originalFunctionAddNewStandard(gridTypeCode, gridControl);
                    filterTrainerProfile(gridTypeCode, gridControl);
                }
            }
        }

        //add existing standard subgrid
        Mscrm.GridRibbonActions.addExistingFromSubGridStandard = function (gridTypeCode, gridControl) {
            if (gridControl != null) {
                if (gridControl.get_id() != subgridName) {
                    originalFunctionAddExistingStandard(gridTypeCode, gridControl);
                }
                else {
                    originalFunctionAddExistingStandard(gridTypeCode, gridControl);
                    filterTrainerProfile(gridTypeCode, gridControl);
                }
            }
        }
        //add associate subgrid (N:N)
        Mscrm.GridRibbonActions.addExistingFromSubGridAssociated = function (gridTypeCode, gridControl) {
            if (gridControl != null) {
                if (gridControl.get_id() != subgridName) {
                    originalFunctionAssociated(gridTypeCode, gridControl);
                }
                else {
                    originalFunctionAssociated(gridTypeCode, gridControl);
                    filterTrainerProfile(gridTypeCode, gridControl);
                }
            }
        }
    }
    catch (ex) {
        alert(ex);
    }
}

* So the filterTrainerProfile(gridTypeCode, gridControl); is your custom function to filter the view, some as you filter and create new custom view that I also have example in my previous post:
http://missdynamicscrm.blogspot.sg/2015/07/filter-nn-subgrid-in-crm-2013-using.html.

Remember, to get the GUID of the list record to show, you can always using CRM Javascript or in CRM 2013 and above you can use smarter way that is to combine with Custom Action, since Custom Action can be called through Javascript.

http://missdynamicscrm.blogspot.sg/search?q=custom+action

Hope this helps! Jiayou!

Wednesday, 25 November 2015

Hide ‘Add Existing Button’ or Plus (+) Button In CRM 2013 Subgrid

Brief Introduction

This requirement often comes and the only supported way is to either hide through Security Role (which in most cases this is not possible and using Ribbon Workbench to hide it), but sometimes you need to only hide in some forms and you need to hide all necessary ribbons using Ribbon Workbench (FYI, one of my favorite tool)
So here is the code to hide the + button for Subgrid:

The Code

function hideAddButtonSubgrid(subgridId) {
    try {
        addEventToGridRefresh(subgridId, setAddButtonDisplayNone);
    }
    catch (e) {

    }
}

function setAddButtonDisplayNone(subgridId) {
    var imageId = subgridId + "_" + "addImageButton";
    if (imageId) {
        document.getElementById(imageId).style.display = 'none';
    }
    //var imageId2 = subgridId + "_" + "addImageButtonImage";
    //document.getElementById(imageId2).style.display = 'none';
}

function addEventToGridRefresh(subgridId, functionToCall) {
    // retrieve the subgrid
    var grid = document.getElementById(subgridId);
    // if the subgrid still not available we try again after 1 second
    if (grid == null) {
        setTimeout(function () { addEventToGridRefresh(subgridId, functionToCall); }, 1000);
        return;
    }

    // add the function to the onRefresh event
    grid.control.add_onRefresh(functionToCall);

    var imageId = subgridId + "_" + "addImageButton";
    if (imageId) {
        if (document.getElementById(imageId).style.display.toLowerCase() == "block") {
            setAddButtonDisplayNone(subgridId);
        }
    }
}



How to Call


Just use this code to call:
hideAddButtonSubgrid("mysubgrid_name");



And here enjoy the result

Result


*Before:

image

*After applied:

image

*Note: This is undocumented in SDK, so use as per your own risk, I need this as the must-to-have requirement so that I have to try workaround to make it happened to the customer.

Hope this helps!
Thanks.

Tuesday, 6 October 2015

Call Print Preview in CRM and Put in the Web Resource

Sometimes we need summary and as we know, CRM has the Print preview picture, though to call this you need to click the gear setting icon.

So, just in case you want to show the print preview and you want to modify it or add some component, you can put it in the web resource.

And here is the sample code

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta charset="utf-8" />
<title></title>
<script src="ClientGlobalContext.js.aspx" type="text/javascript"></script>
<!--change this your correct URL can be 
<script src="../../ClientGlobalContext.js.aspx" type="text/javascript"></script>-->
<script>
debugger;

function CallPrintPreview() {
try {

var x = Xrm.Page.ui;

var formId = window.parent.Xrm.Page.ui.formSelector.getCurrentItem().getId();
var recordId = window.parent.Xrm.Page.data.entity.getId();

var objectType = window.parent.Xrm.Page.context.getQueryStringParameters().etc;

var url = Xrm.Page.context.getClientUrl() + "/_forms/print/print.aspx?allsubgridspages=false&formid=" +
formId + "&id=" + recordId + "&objectType=" + objectType;

var iframeSRC = '';
iframeSRC = url;
if (iframeSRC != '') {
document.getElementById("iframePreview").src = iframeSRC;
}
}
catch (e) {

}
}

//YOU CAN ALSO CALL FROM RIBBON AS WELL

//USE THIS FUNCTION IF YOU WANT TO OPEN THE WEB RESOURCE OUTSIDE THE CRM FORM, YOU NEED PARAMETER FROM THE CRM FORM
function CallPrintPreviewByParams(formId, recordId, objectType) {
try {
var url = Xrm.Page.context.getClientUrl() + "/_forms/print/print.aspx?allsubgridspages=false&formid=" +
formId + "&id=" + recordId + "&objectType=" + objectType;

var iframeSRC = '';
iframeSRC = url;
if (iframeSRC != '') {
document.getElementById("iframePreview").src = iframeSRC;
}
}
catch (e) {

}
}

function Submit() {
//put your save button logic here
}

window.onload = CallPrintPreview;

</script>

</head>
<body onload="CallPrintPreview();">
<div id="divIframePrintPreview">
<iframe
id="iframePreview"
src="about:blank"
style="width: 100%; height: 100%; overflow: hidden; border: none; border-width: 0;" frameborder="0"></iframe>
</div>

<!--//you can also add additional in the web resource

<div id="divBody">
<table>
<tr>
<td>Are you sure to submit?</td>
<td></td>
</tr>
<tr>
<td>Remarks:</td>
<td>
<textarea name="comment" form="usrform" style="height: 68px">Enter text here...</textarea>
</td>
</tr>
<tr>
<td colspan="2" align="center">
<button value="Submit" title="Submit" name="btnSubmit" type="button" onclick="Submit();">Submit</button>
</td>
</tr>
</table>



</div>-->
</body>
</html>

*I use this as the concept only, you might use your own creative design based on the requirement

So, you can add the remark, for example for the modification or like signature to say ‘OK’ and submit, simple example here:

Launch from the button/put in the form

image


image


image



Hope this helps!

Thanks.

Tuesday, 25 August 2015

Modify CRM Subgrid Ribbon for CRM 2015 (Update 1)

Introduction

Based on my previous post we were talking about how to customize the subgrid ribbon in CRM 2013, now how about CRM 2015, especially after the latest update 1? Okay, here we go.

Solution

What we need to do is just replacing:

Mscrm.GridRibbonActions.addExistingFromSubGridStandard to Mscrm.GridCommandActions.addExistingFromSubGridStandard

Just replace the code only.
That’s why I use the form scripting to prepare another release update.

The Code

So the sample code will be like this:

function modifyRibbon() {
    //to store the original function ones
    var originalFunctionAddNewStandard = Mscrm.GridCommandActions.addNewFromSubGridStandard;
    var originalFunctionAddExistingStandard = Mscrm.GridCommandActions.addExistingFromSubGridStandard;
    var originalFunctionAssociated = Mscrm.GridCommandActions.addExistingFromSubGridAssociated;

    //add new standard subgrid
    Mscrm.GridCommandActions.addNewFromSubGridStandard = function (gridTypeCode, parentEntityTypeCode, parentEntityId, primaryControl, gridControl) {
        if (gridControl.get_id() != "subgrid_id") {
            originalFunctionAddNewStandard(gridTypeCode, parentEntityTypeCode, parentEntityId, primaryControl, gridControl);
        }
        else {
            callPopUp(gridControl);
        }
    }

    //add existing standard subgrid
    Mscrm.GridCommandActions.addExistingFromSubGridStandard = function (gridTypeCode, gridControl) {
        if (gridControl.get_id() != "subgrid_id") {
            originalFunctionAddExistingStandard(gridTypeCode, gridControl);
        }
        else {
            callPopUp(gridControl);
        }
    }

    //add associate subgrid (N:N)
    Mscrm.GridCommandActions.addExistingFromSubGridAssociated = function (gridTypeCode, gridControl) {
        if (gridControl.get_id() != "subgrid_id") {
            originalFunctionAssociated(gridTypeCode, gridControl);
        }
        else {
            callPopUp(gridControl);
        }
    }

}

function callPopUp(gridcontrol) {
    if (Xrm.Page.ui.getFormType() == 2) //update {
        //call the pop up
        var dialogoptions = new Xrm.DialogOptions;
        dialogoptions.width = 1080;
        dialogoptions.height = 750;

//*You can call pop up or modal dialog or web resource or other script, running dialog, running workflow, or javascript to call custom action
Xrm.Internal.openDialog('http://mycrmserver?id=' + Xrm.Page.data.entity.getId(),
       dialogoptions, null, null, function (retval) { gridcontrol.Refresh() });
        gridcontrol.Refresh();
    }
}

And the result you can get exactly the same, please put the code in the form and call during onload.

*For CRM 2013 please use this:
http://missdynamicscrm.blogspot.sg/2015/08/tweak-modify-ribbon-in-crm-2013-subgrid.html


Hope this helps!

Thanks.

Tweak & Modify Ribbon in CRM 2013 Subgrid

Introduction

As we know that ribbon/command in CRM 2013/2015 subgrid can only have ‘+’ button inside and you cannot add ribbon to appear in this subgrid design. Well, this behaviour you cannot change, you cannot add new ribbon, but it does not mean you cannot customize it!

So, I have requirement when the user click +, instead of choosing the existing record using CRM standard lookup (for add existing) or open new window for creating new record, they want to open another custom window, such as CRM Dialog, run custom workflow, run custom script with custom action combination as well, open new web resource, or open new custom website.

So, we can have options such as: Force users to use Associated View (well, this option is not the best option becase why did you even need to put subgrid if you enforce users to use associated view? Hm..in this case you might miss the CRM 2011 version.


So, we need to find the alternative like customizing the ribbon. Well, you cannot modify the behaviour, but I believe you can still achieve it by customizing the ribbon or even its action.

The title must be like Modifying Ribbon in CRM 2013 Subgrid without even modifying the ribbon XML (ribbondiff xml) customization.

Solution

As I read some good articles that it is not recommended to modify the out of the box standard ribbon/command bar, so I take it highly, so I decide to not modify the standard ribbon first, but I still need to fulfill the requirement.

Then, what I do is I try to override the standard function.

Yes, you can attach some script to override the Ribbon Action.

And the good thing here is you can apply the custom action without modifying the Ribbon.XML.
This will be useful to modify the Add New record in Subgrid, Add Existing records in Subgrid, and Add Associated records in Subgrid (N:N) relationship.

*To learn about the behaviour of the subgrid you can refer to these posts:


Then, what solution I did is just to put the additional logic to tweak the existing ribbon javascript calling.

So let’s get started

The Code

Here is the code, it is actually pretty simple if you know the CRM Scripting & basic Javascript overriding concept.

function modifyRibbon() {
    //to store the original function ones
    var originalFunctionAddNewStandard = Mscrm.GridRibbonActions.addNewFromSubGridStandard;
    var originalFunctionAddExistingStandard = Mscrm.GridRibbonActions.addExistingFromSubGridStandard;
    var originalFunctionAssociated = Mscrm.GridRibbonActions.addExistingFromSubGridAssociated;

    //add new standard subgrid
    Mscrm.GridRibbonActions.addNewFromSubGridStandard = function (gridTypeCode, parentEntityTypeCode, parentEntityId, primaryControl, gridControl) {
        if (gridControl.get_id() != "subgrid_id") {
            originalFunctionAddNewStandard(gridTypeCode, parentEntityTypeCode, parentEntityId, primaryControl, gridControl);
        }
        else {
            callPopUp(gridControl);
        }
    }

    //add existing standard subgrid
    Mscrm.GridRibbonActions.addExistingFromSubGridStandard = function (gridTypeCode, gridControl) {
        if (gridControl.get_id() != "subgrid_id") {
            originalFunctionAddExistingStandard(gridTypeCode, gridControl);
        }
        else {
            callPopUp(gridControl);
        }
    }

    //add associate subgrid (N:N)
    Mscrm.GridRibbonActions.addExistingFromSubGridAssociated = function (gridTypeCode, gridControl) {
        if (gridControl.get_id() != "subgrid_id") {
            originalFunctionAssociated(gridTypeCode, gridControl);
        }
        else {
            callPopUp(gridControl);
        }
    }

}

function callPopUp(gridcontrol) {
    if (Xrm.Page.ui.getFormType() == 2) //update {
        //call the pop up
        var dialogoptions = new Xrm.DialogOptions;
        dialogoptions.width = 1080;
        dialogoptions.height = 750;
//You can call pop up or modal dialog or web resource or other script, running dialog, running workflow, or javascript to call custom action
Xrm.Internal.openDialog('http://mycrmserver?id=' + Xrm.Page.data.entity.getId(),
       dialogoptions, null, null, function (retval) { gridcontrol.Refresh() });
        gridcontrol.Refresh();
    }
}

Remember that this is the key:

//to store the original function ones
    var originalFunctionAddNewStandard = Mscrm.GridRibbonActions.addNewFromSubGridStandard;
    var originalFunctionAddExistingStandard = Mscrm.GridRibbonActions.addExistingFromSubGridStandard;
    var originalFunctionAssociated = Mscrm.GridRibbonActions.addExistingFromSubGridAssociated;

And this also mandatory to override:

//add new standard subgrid
    Mscrm.GridRibbonActions.addNewFromSubGridStandard = function (gridTypeCode, gridControl) {
        if (gridControl.get_id() != "subgrid_id") {
            originalFunctionAddNewStandard(gridTypeCode, gridControl);
        }
        else {
            callPopUp(gridControl);
        }
    }

    //add existing standard subgrid
    Mscrm.GridRibbonActions.addExistingFromSubGridStandard = function (gridTypeCode, gridControl) {
        if (gridControl.get_id() != "subgrid_id") {
            originalFunctionAddExistingStandard(gridTypeCode, gridControl);
        }
        else {
            callPopUp(gridControl);
        }
    }

    //add associate subgrid (N:N)
    Mscrm.GridRibbonActions.addExistingFromSubGridAssociated = function (gridTypeCode, gridControl) {
        if (gridControl.get_id() != "subgrid_id") {
            originalFunctionAssociated(gridTypeCode, gridControl);
        }
        else {
            callPopUp(gridControl);
        }
    }

You can choose want to put in the N:N or 1:N subgrid only.

Which this one is optional and replaceable, replace it by your OWN CODE LOGIC

And this is the condition to whether you want to override the ribbon with your own custom action or not
if (gridControl.get_id() != "subgrid_id") 

Because once you implement this method, by default all subgrid will be overriden unless you want to exclude it, I just want to  make sure only applicable for the “subgrid_id” so I put that condition, for other subgrid it will still using the original one.

*You just need to put it in the form scripting and call on form onload

image

Result


*Alert action (if put alert action in the action logic)

image

*Calling the pop up

image

image

Then when I click the X button will refresh the ribbon because of this code:
function (retval) { gridcontrol.Refresh() }

Please refer to this for specific article:

http://inogic.com/blog/2014/09/alternative-to-showmodaldialog/

In this case, take not of this: Use of Xrm.Internal is unsupported as per SDK
But, anyway, in my post, this is just an example, you can change the logic to anything you want.
Can call custom dialog or workflow.

Mscrm.FormAction.launchOnDemandWorkflowForm
http://ribbonworkbench.uservoice.com/knowledgebase/articles/132235-create-a-workflow-short-cut-ribbon-button-no-code

Can refer to this good article:
http://ribbonworkbench.uservoice.com/knowledgebase/articles/140652-create-a-run-dialog-short-cut-ribbon-button

Or can use to open the modal popup (like my example) or open the entity form:
http://msdn.microsoft.com/en-us/library/gg328483.aspx

Or you can use to show alert and confirm dialog:
http://blogs.msdn.com/b/shraddha_dhingra/archive/2013/12/22/alertdialog-and-confirmdialog-in-crm-2013.aspx

Or even you can block user to add new record or link records:







All can be done using form scripting without even touching the ribbon.xml

*Note: This action is not documented in the SDK, use this to help you fulfil the requirement, The advantage on this action is in the next release, it might break, but you just need to modify the form scripting.

Like I did in CRM 2013 and CRM 2015, the scripting is changed from

Mscrm.GridRibbonActions.addExistingFromSubGridStandard to Mscrm.GridCommandActions.addExistingFromSubGridStandard

*For CRM 2015 you can refer to this article:
CRM 2015 Update 1 Code

This will be useful for you in case you do not want to use the standard subgrid function and there is no way like CRM 2011 you can have ribbon/command bar appearing same exactly like you have in Associated View.

Hope this helps!

Thanks.

Sunday, 9 August 2015

Microsoft Dynamics CRM + TinyMCE Editor = Dynamic Rich Text, Updated Solution with Plugins

Hi in my previous post, I have mentioned about tinyMCE, but those are solutions without plugin, while sometimes you need it and my project also needs it so I just want to share it out.
Here you can download the managed solution (I chose managed because in case you don’t need it you can just remove it at all)
CRM 2013 TinyMCE Solution
CRM 2011 TinyMCE Solution
Hope this helps!
Thanks.

Wednesday, 5 August 2015

Dynamics CRM Rich Text HTML Editor with TinyMCE Solution

Introduction

We all know that CRM records except Email Template or Email cannot have Rich Text HTML Editor  using CRM Out of The Box Feature. In some situations, we might need this requirement, for example, when we need to supply data to CMS System that need fancy color, fancy formatting, superscript, and subscript as well, we can’t just supply them the plain text only without styling and HTML formatting.

Solution

So, this is one of our requirement in the project, so, together with my geek colleague, JVS Segador trying to make it happens using tinyMCE.


There are 3 choices we can use:

1. Use HTML Web Resources
Then we place it in to the CRM Form

2. Use custom page like ASPX hosted somewhere and then put as iFrame

3. Convert text field in CRM to Rich Text Editor with some ‘hack’ and ‘tweak’

For those we still need the TinyMCE libraries, so the purpose of my blog post now is to help you using the TinyMCE by providing you the CRM solutions I have compiled.

Steps & Download Link

1. You just need to import it to your CRM and choose 1 of 3 choices by referencing its libraries.

Finally, you can download here:

TinyMCE Solution for CRM 2013
*This example is for CRM 2015 Online (with Online Updates 1), so should work for now.


2. Create a HTML, a new Web Resource, then You can refer to the library you have installed into your CRM Solution, for example:

image

3. Then you need a javascript to get the content and to grab it and save it to the expected field value.

To grab the value you can use this:

var xContent = tinyMCE.get('textareaID').getContent();
//or
var aContent = tinyMCE.activeEditor.getContent();

*Note: In the next post I will provide the full HTML Code as solution

4. Put the HTML to the CRM Form

5. For adding new Plugin or new fancy functionalities you can add more plugin libraries that TinyMCE provides to you all.

Result


And here is the result:

image

Thanks to TinyMCE Team! Good night here

Monday, 27 July 2015

Filter N:N Subgrid in CRM 2013 using Javascript

Sometimes, you need to filter the N to N subgrid (not associated view) when you click the Add Existing button and you will see the inline lookup

image

To achieve it, you might need to modify the system ribbon. Well, This method is unsupported because it is not well-documented, but if just in case customers really demanding and want it, I just want to share the ‘how-to’.

Thanks to my friend with this blog post which can convince me that this is not possible to filter subgrid, this is a clue:

http://nycrmdev.blogspot.sg/2014/02/changing-default-view-of-inline-lookup.html

So, this is the common function:

//FUNCTION:AddExistingSubgrid
function filterSubgrid(gridTypeCode, gridControl, entityParentName, subgridName, viewId, entityRelatedName, viewDisplayName, fetchXML, layoutXML) {
    var AGS = window["AGS"] || {};

    AGS.AddExistingSubgrid = function (gridTypeCode, gridControl) {
        Mscrm.GridRibbonActions.addExistingFromSubGridAssociated(gridTypeCode, gridControl);
        if (Xrm.Page.data.entity.getEntityName() == entityParentName &&
            //gridTypeCode == 2 &&
            typeof gridControl['get_viewTitle'] == 'function') {
            var lookupExistingFieldName = 'lookup_' + subgridName + "_i";
            var inlineBehaviour = document.getElementById(lookupExistingFieldName).InlinePresenceLookupUIBehavior;
            setLookupCustomView(inlineBehaviour, viewId, entityRelatedName, viewDisplayName, fetchXML, layoutXML);
        }
    };

    this.AGS = AGS;
    //call this function;
    AGS.AddExistingSubgrid(gridTypeCode, gridControl);
}

// FUNCTION: setLookupCustomView
//must pass the lookup field control, not field name
//to add custom view to the subgrid (Add existing)
function setLookupCustomView(lookupFieldControl, viewId, entityRelatedName, viewDisplayName, fetchXML, layoutXML) {
    // add the Custom View to the primary contact lookup control      
    lookupFieldControl.AddCustomView(viewId, entityRelatedName, viewDisplayName, fetchXML, layoutXML, true);
}


And this is the example how to use that and wrap it in one specific function that would be called during ribbon action execution (clicked)

function filterTrainerProfile(gridTypeCode, gridControl) {
    var entityParentName = "primaryentityname";
   
    // use randomly generated GUID Id for our new view      
    var viewId = "{6fd72744-3676-41d4-8003-ae4cde9ac282}";
    var entityRelatedName = "relatedentityname"; 
    var viewDisplayName = "Filtered View Name";

    var subgridName = "subgrid_name";
   
    var fetchXml = getFetchXML(); //replace this to your own fetch xml

    // build Grid Layout     
    var layoutXml = getLayoutXML(); //replace this to your own layout example

    if (fetchXml != null) {
        filterSubgrid(gridTypeCode, gridControl, entityParentName, subgridName, viewId, entityRelatedName, viewDisplayName, fetchXml, layoutXml);
    }
}

*I used the filterTrainerProfile() function to filter the Trainer profile entity subgrid records (as related record) then I put the subgrid in the Class form entity.

So, the Trainer profile will be my related entity name, while Class is my entity Parent Name.

Then using our friendly tool here, we can modify the system ribbon

image

use number 1 if you want to filter subgrid based on 1:N relationship and use number 2 if you want to filter for N to N relationship (well in my example is N to N)

Then, after that, modify the command:

image

Here you go for the detail command

image

And you can see the result, it would filter my Trainer Profile records based on the xml I defined!

*Note: It can break the associated view, you might need to add try catch or conditional to make sure it works if you really need the associated view. In my case, users do not want to use associated view because we have subgrid already.

I hope this can help you, it seems becoming most requirement recently I found in the forum and also other projects.

Thanks.

Sunday, 5 July 2015

Create Custom Ribbon in CRM Homepage Gridview

A quick tip for creating ribbon for subgrid

We can use Visual Ribbon Editor or RibbonWorkbench, they are two great tools!

Then, the most important thing is of course calling your Web Resources function.

And do not forget to pass the CRM Parameter : SelectedControlSelectedItemIds

image

Then to get the selected ID, you can use this function:

function acknowledge(selectedIds) {
    //check the status first
    debugger;
    if (selectedIds != null && selectedIds != "") {
        var strIds = selectedIds.toString();
        var arrayIds = strIds.split(",");
        for (var i = 0; i < arrayIds.length; i++) {
                //selected id = arrayIds[i]
            }
        }
    }   
}

To get the value of the selected record, you need to query using the ID using odata or fetchxml

Then you can also show hide, this time, i use my custom rule:


image

Or you can use your own rule or available CRM Rules, but you cannot use valueRule as I know since this only take the value rule from formCommand context.

Then here is the result:

image

If you want to validate that only if selected then show the ribbon, you can use another Enable Rule, that is the selectioncountrule

http://nishantrana.me/2011/08/24/enabling-button-in-subgrid-on-selection-of-record-selectioncountrule-in-crm-2011/

https://msdn.microsoft.com/en-us/library/gg309316.aspx

image

Then here is the result

image

If I select 1 or 2

image

And if I select more than 2

image

And here is when you click and you get selected IDs

image

Hope this helps.

Thanks.

Tuesday, 30 June 2015

The difference: Xrm.Page.data.refresh() vs Xrm.Utility.openEntityForm(entityName, id) in Practice

Brief post here, Assuming, in the ribbon, I have successCallback function that will refresh the form.
Then, in the form onLoad I have script to show Form Notification.

Now, I want to research the difference of those function usages.

image

Now, In the Submit Button SuccessCallback() I have specified one is using .refresh() and another one is using the .openEntityForm()

Xrm.Page.data.refresh()

What happened after I put it in the code?

image

It is just refreshing the Form like a modal popup async and it is fast!

image

But..It does not trigger my notification onLoad(), it did not reload the whole form.

Now, the second research:

Xrm.Utility.openEntityForm()

image

Well, it is really refreshing and reloading your form, like you were opening a new window of entity record.

image

And here is the result after it comes back.

image

*My Notification which i put in the formLoad is appearing, different from the first function result I used before.

Hope this can make you clear which function you’d like to use!

As I know, both are also refreshing the ribbon without additional ribbon refresh function based on my research.

*additional one on 19 October 2015
the Xrm.Page.data.refresh() function will not refresh the whole thing, so you might find that the footer is not updated like this:



Thanks!

Sunday, 28 June 2015

Tab State Change Event CRM Javascript

Introduction

In CRM Form, Tab has important role to make your CRM Form more structured and easy to read.
But, not many people try to play with JavaScript for this Tab Event.
Basically, CRM Tab same as field, also has event, but Tab only has event triggered once you click it and then it will be expanded or collapsed and its state will be changed accordingly.

‘Hidden Required Fields’ in Collapsed Tabs

I give you one scenario, for example:
You save the form and then you found that you need to fill some fields based on its requirement option or your custom validation, then what you can find is you cannot save the form, but you don’t know where to find the field since this field is inside a Tab that is in collapsed mode! If you realize that, it makes users difficult to finish the form.
image
Yeah, this is very clear prompting me to fill in the ‘Course Title’ but how to find it if this field inside the Collapsed tab? That’s why sometimes you might need to expand it programmatically.

Here is the article about Tab:

https://msdn.microsoft.com/en-us/library/gg328067.aspx

Which I want to put more spices on it.

The Code

Here is the Code to add the event handler of the Tab state changes programmatically

Add Event Handler Programmatically

Xrm.Page.ui.tabs.get("tab_general").add_tabStateChange(tabGeneralStateChange);

*Change to your tab name and place this on the form OnLoad()

To Get/Set the display state programmatically


*To Get

Xrm.Page.ui.tabs.get("tab_general").getDisplayState();

*To Set

Xrm.Page.ui.tabs.get("tab_general").setDisplayState(string);
//change the string to either 'expanded' or 'collapsed

*To Set Focus

Xrm.Page.ui.tabs.get("tab_general").setFocus()

*As per mentioned before, those methods can be useful for users to find the field location

Sample Code – Event Handler Code

Then here is sample code for the event

Xrm.Page.ui.tabs.get("tab_general").add_tabStateChange(tabGeneralStateChange);

function tabGeneralStateChange() {
    var currTabState = Xrm.Page.ui.tabs.get("tab_general").getDisplayState();
    if (currTabState == "expanded") {
        alert("Expanded, horreey");
    }
    else {
        alert("Collapsed, ooh noo.");
    }
}

Result:

*Expanded

image

*Collapsed

image

*In one of my project, I can use this event to call my custom page once user click it, so that I do not use ribbon for this as well.

image

Now, I want to change the label accordingly also:

Sample Code – Event Handler with Label Handler

function tabGeneralStateChange() {
    var currTabState = Xrm.Page.ui.tabs.get("tab_general").getDisplayState();
    if (currTabState == "expanded") {
        alert("Expanded, horreey");
        Xrm.Page.ui.tabs.get("tab_general").setLabel("- Hide Me -");
    }
    else {
        alert("Collapsed, ooh noo.");
        Xrm.Page.ui.tabs.get("tab_general").setLabel("+ Show Me +");
    }
}

Result:

image

SNAGHTML11e6de4

Hope this helps!

Thanks.