Showing posts with label Ribbon. Show all posts
Showing posts with label Ribbon. Show all posts

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

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, 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.

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!

Saturday, 20 June 2015

Pass Plugin Context Message C# Exception to Form Script (Javascript) during CRM Ribbon Calling Action

Introduction & Example

In my previous post, I was talking about how to validate the mandatory fields when you click a ribbon utilizing the “Xrm.Page.data.save().then(successCallback, errorCallback);” method.

Now, what if we need one more advance validation such as from related entities or external service, we need server side code in plugin, not only because it is needed but also because it is easier for some .NET Programmer rather than writing in javascript and also we might need to use to validate it from anywhere.

Following my previous post, now, there is requirement to check whether the campaign has Campaign Activity defined or not before you make this campaign as launched.

Solution & Result

So, now I create a plugin to validate

Here is my Plugin Code:

using Microsoft.Xrm.Sdk;
using Microsoft.Xrm.Sdk.Query;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Xrm.CRM2015.Plugin.Campaign
{
    public class Campaign : IPlugin
    {
        public void Execute(IServiceProvider serviceProvider)
        {
            #region must to have

            IPluginExecutionContext context = (IPluginExecutionContext)serviceProvider.GetService(typeof(IPluginExecutionContext));

            IOrganizationServiceFactory serviceFactory = (IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOrganizationServiceFactory));

            // Create service with context of current user
            IOrganizationService service = serviceFactory.CreateOrganizationService(context.UserId);

            //create tracing service
            ITracingService tracingService = (ITracingService)serviceProvider.GetService(typeof(ITracingService));

            #endregion

            if (context.InputParameters.Contains("Target") && context.InputParameters["Target"] is Entity)
            {
                //create entity context
                Entity entity = (Entity)context.InputParameters["Target"];

                if (entity.LogicalName != "campaign")
                    return;
                else
                {
                    //if status reason is changed
                    if (entity.Contains("statuscode"))
                    {
                        if (((OptionSetValue)entity["statuscode"]).Value == 2) //Launched
                        {
                            //put MY Logic here
                            if (CheckIsHavingCampaignActivity(service, entity) == false)
                            {
                                throw new InvalidPluginExecutionException("Please add at least one Campaign Activity");
                            }
                        }
                    }
                }
            }

        }

        private bool CheckIsHavingCampaignActivity(IOrganizationService service, Entity entity)
        {
            //get the Campaign Activity from this Campaign
            QueryByAttribute querybyattribute = new QueryByAttribute("campaignactivity");
            querybyattribute.ColumnSet = new ColumnSet(true);

            //  Attribute to query.
            querybyattribute.Attributes.AddRange("regardingobjectid");

            //  Value of queried attribute to return.
            querybyattribute.Values.AddRange(entity.Id);

            //  Query passed to service proxy.
            EntityCollection retrieved = service.RetrieveMultiple(querybyattribute);

            if (retrieved.Entities.Count == 0)
            {
                return false;
            }
            else
            {
                return true;
            }
        }
    }
}

image

As we know that plugin message is very flat, simple, and straight-forward, the user can be confused with only one message in the centre of the screen, different from Javascript validation that you can add alert or you can add notification for CRM 2013 and above. This is one of the disadvantage over its plugin capability, you might need some additional art to reach user expectation in validation.

Now, I want to make sure that this message also passed to the Form Scripting and then I want to add more notification to the users telling them that to launch this Campaign, they need at least one Campaign Activity.

So, I would like to catch the exception from plugin to determine my next action.

From my previous post, we can add the errorCallback method and attach some function on it and I put debugger:

image

Yay,, horreey, we can grab the SaveErrorResponse message…

Now, I add some scripts:

function launch() {
    Xrm.Page.data.save().then(successCallback, errorCallback);
}

function successCallback() {
    //set status to Launched
    alert("Your campaign has been published");
    //then refresh if needed
    var Id = Xrm.Page.data.entity.getId();
    Xrm.Utility.openEntityForm("campaign", Id);

}

function errorCallback(saveErrorResponse) {
    if (saveErrorResponse.message.indexOf("Activity") != -1)
    {
        Xrm.Page.ui.setFormNotification("Hi, don't forget to add the Campaign Activity!!", 'ERROR');
    }
    //remember you can also clean up the notification

}
And this is the result:
image

Not only that, using this message you can do more than that.

Now, I want to put more spices on it..

That is to make the user more aware by set notification in field level

image

image

Then, after I add Campaign Activity, I can launch it successfully

image

The Code

function launch() {
    //do not forget to always clear it
    Xrm.Page.getControl("totalcampaignactivityactualcost").clearNotification();
    Xrm.Page.data.save().then(successCallback, errorCallback);
}

function successCallback() {
    //set status to Launched
    alert("Your campaign has been published");
    //then refresh if needed
    var Id = Xrm.Page.data.entity.getId();
    Xrm.Utility.openEntityForm("campaign", Id);

}

function errorCallback(saveErrorResponse) {
    if (saveErrorResponse != null) {
        if (saveErrorResponse.message != 'undefined' && saveErrorResponse.message != null) {
            if (saveErrorResponse.message.indexOf("Activity") != -1) {
                Xrm.Page.ui.setFormNotification("Hi, don't forget to add the Campaign Activity!!", 'ERROR');
                //remember you can also clean up the notification
                Xrm.Page.getControl("totalcampaignactivityactualcost").setNotification("Please put more activity");
                Xrm.Page.getControl("totalcampaignactivityactualcost").setFocus();
            }
        }
    }
}

Conclusion


*In CRM 2013 and above, I meet new favourite idol feature to love, that is Custom Action.

But, for CRM 2011 this feature is not available and since it is a new feature, I realize many developers still do not want to use it and plugin is more friendly for us since it has been accompanying us for years and we already knew its concept pretty well.

This method can be useful for some circumstances:

1. When you want validate something from Ribbon action or any other javascript that can trigger plugin through onsave event (in this example)

*You can also use oData action that can call plugin and return it.

2. When you want to check duplicate not using duplicate detection, then you can show the user which field is really causing the duplicate trouble by giving it special setFocus treatment

3. Do other action that need server side code

Now, you are be able to pass and obtain the plugin context message that you can use to manipulate the CRM Form UI.

But, remember, plugin will stop the saving if got exception, if you need process to keep going, there is no other choice than using Custom Action, Javascript, or you can put some field yes/no to pass the plugin.

Hope this helps!

Thanks.

Validate Users To Key In Mandatory Field in CRM Form Ribbon And Set Form Notification for Incomplete Data in Ribbon Action

Introduction

When you create a new custom ribbon in order to make user convenient processing something, you might need to validate that the users have key in all mandatory fields in the Form. 

We know that when we hit the ‘Save’ button, CRM will validate mandatory fields plus the validation written in the onSave event script, but what about Ribbon?

Example

We need Ribbon to always check first before we process to other state.
For example here, I take simple example for Campaign.

As we know, in CRM, the status of the Campaign is just a field, which you can change it as much as you want without complex validation, so assuming you now want to control its value using Ribbon, so we create a new ribbon so-called: Launch.

Inside the ribbon, you have a code to set its status to ‘Launch’, while you can do it through javascript using the help of odata endpoint or XrmServiceToolkit to proceed it or you can trigger a plugin, it is up to you.

So, the expectation here you enforce user to click ‘Launch’ button in order to change the Campaign Status from Proposed to Launched.

image

Then just click the button to publish it.

image

As you can see that the ribbon will do processing based on your logic, but… The ribbon did not check the mandatory fields.

In fact, in this case, the requirement is you also need to make sure users to key in all mandatory fields before launching the Campaign, we know that Save button will fix the problem, but is that convenient for users to always click ‘Save’ and then click the ‘Launch’ button? 

Or is that convenient also for developers to always check whether the fields are filled or not using Javascript?

Solution

Well, luckily, we can have one better method.

Just add this script to make sure that the users aware about the required fields:
Xrm.Page.data.save();
So, in your ribbon, just put like this:

function launch()
{
     Xrm.Page.data.save();
     //put your logic here after checking required fields and saving
     //set status to Launched
}

You can also using this code to make sure that the sequence is right
Xrm.Page.data.save().then(successCallback, errorCallback);

Result:

image

Then after that it will check another field

image
















Now, I want to make a form notification also, then you can just add the code in the errorCallback() function.

Expected Result


image

The Code

And here is the Complete code to ensure the order:

function launch()
{
      Xrm.Page.data.save().then(successCallback, errorCallback);
}

function successCallback() {
    //set status to Launched
    alert("Your campaign has been published");
    //then refresh if needed
    var Id = Xrm.Page.data.entity.getId();
    Xrm.Utility.openEntityForm("campaign", Id);

}

function errorCallback(error) {
     //debugger;
     Xrm.Page.ui.setFormNotification("You cannot launch this campaign before key in all mandatory fields", 'ERROR');
     alert("Please fill in mandatory fields");
    
}

You can also clear the notification using the code, just add in in order to prevent duplicate notification.

With this code, you can control the success and error from the ribbon and also you can make users aware about the mandatory fields to keep the validation before doing some next stage processing.

Hope this can help you!

Thanks.

Thursday, 5 February 2015

Control Ribbon Visibility (Show/Hide) based on Specific Form in CRM 2011/2013/2015 Multiple Entity Forms

CRM Supports Multiple Forms and we can put ribbon (button in CRM) or Command in the CRM Forms as well. What if we have requirement to show or hide the ribbon based on Form. In this post I will give example of the code to control ribbon visibility.

Scenario

I have scenario that I have 3 Forms of Account Form.

image

Now, I want to place a ribbon applicable for Account Form, but only for Partner Form. So when the users are in different form, other than Partner Form, this ribbon should not be available.

The Code


The code is in JavaScript code and it’s pretty simple.

function controlFormRibbon() {
   var formLabel;
   var currForm = Xrm.Page.ui.formSelector.getCurrentItem();
   formLabel = currForm.getLabel();

   //this code to show the ribbon if only the current opened form is Partner, 
   //otherwise remain disable/hidden
   if (formLabel == "Partner") //change this with your own specific form name
   {
      return true;
   }
   else
   {
      return false; 
   }
   //you can also apply the different/reverse rule here, for example you want to hide if form is Partner
}

Ribbon Workbench Favor

Here, I need a favor from my favorite tool.

I have solution with the ribbon image and script now I just want to place a ribbon and set the JavaScript there.

I create a Button in the Form section, give the Command, rename the Id, assign the Images to the Ribbon.

image

Then I rename the Label to ‘Partner Only’ that you can see in the final result showing this label Text.

Then Here is the Command.

image

I apply a Rule here to EnableRules.

This is my rule:

image

image

*You can change the Default to True/False to set the default visibility, but in my script I have set other than Partner, will hide this ribbon button, so I don’t bother to change to False.

The key here is just referring the correct FunctionName under the correct Library (your Web Resource where you kept the script) and set parameters if any.

Note: The display rules doesn’t support CustomRule, so just apply it to the EnableRule to show/hide.

Result

I can navigate using Form Selector

image

*My Partner Form

image

*My Account & Information Forms

image












As you can see the ribbon is there if I choose the form to Partner, otherwise it’s hidden.

Hope this can help you!

Thank you.