Actions Archives - Aric Levin's Digital Transformation Blog http://aric.isite.dev/tag/actions/ Microsoft Dynamics 365, Power Platform and Azure Thu, 12 May 2022 03:45:05 +0000 en-US hourly 1 https://wordpress.org/?v=6.5.3 Considerations of Embedding Canvas Apps in Model Driven forms – Revisited http://aric.isite.dev/dynamics/post/embedded-canvas-app-in-uci-revisited/ Sun, 15 Nov 2020 10:19:00 +0000 https://aric.isite.dev/index.php/2020/11/15/considerations-of-embedding-canvas-apps-in-model-driven-forms-revisited/ A few weeks ago, I wrote a blog post about embedding a Canvas App in a Model Driven form, and compared the options of whether, in my case I should have embedded the app using the Canvas App control, or as we implemented using an iframe on the form.

The post Considerations of Embedding Canvas Apps in Model Driven forms – Revisited appeared first on Aric Levin's Digital Transformation Blog.

]]>
A few weeks ago, I wrote a blog post about embedding a Canvas App in a Model Driven form, and compared the options of whether, in my case I should have embedded the app using the Canvas App control, or as we implemented using an iframe on the form.

Although, while making this decision to use the iframe it seemed like the correct one to do, as we would be able to control the security of the Canvas app by passing a parameter from the Model-driven form into the app, that ended up being a crash and burn situation as we needed this to be available on both Desktop and Mobile applications. At the time of writing this article, Canvas apps embedded in iframes are not accessible on mobile devices.

Even Scott Durow’s Ribbon Workbench, using Smart buttons has the ability to open up a Canvas App in a new modal window, but that too has limitations as it will not work in mobile devices due to the Cross Domain authentication restrictions that are blocked by the Power Apps mobile application. You can vote for Scott’s support request by clicking on the link below:

https://powerusers.microsoft.com/t5/Power-Apps-Ideas/Support-Canvas-App-modal-popup-inside-Model-Driven-Mobile-App/idi-p/704962

So, now that we had this issue, we had to find a way to resolve it. As I mentioned in my previous post, we had a complex security model, which included the sharing of records, but also a custom implemented tab level security which needed to determine whether a read-only mode or a read-write mode of this app would be accessible to the end user. All of this logic was executed via JavaScript, so it was very easy to pass a parameter, but that wasn’t accessible via mobile device.

We consulted with Microsoft, and one approach was to get all the entities that make up that security model and write logic within our Canvas app to deal with that security model. That would be an overkill.

The other approach which we thought of, was of course that Power Apps can call Power Automate flows. We want to see if this could be done, and after thorough testing, we determined this would be a good choice. We needed to write the JavaScript logic that would test tab level security into our flow, we needed additional security to check whether the user the is logged in is the owner of the record or a member of any of the teams, we need to check if the user had the correct sharing privileges via principal object access, which would also give us the status of the record was read-only or not.

Most of the flow handled the tab level security, but the problem that we had was that we could not query the principal object access entity, and it is not available on the list of entities within of CDS.

Although, I was eager to start “playing” with Custom APIs, I reverted to using custom actions and plugin code to handle the Principal Object Access security. For some organizations that you work with, preview and new features require approvals.

Embedded Canvas App - Power Automate Flow calling Custom Action

So, I created a custom action to check whether user had the correct sharing (Write Access) and whether the user was an owner (or member of owner team) of the record. Since the accessrightsmask return value enumeration would return the sum of all possible access rights that are being shared with ta user, I used a BitArray in order to get the value of Write Access (2). The link below shows the write access types on the Microsoft Docs site.

https://docs.microsoft.com/en-us/dynamics365/customer-engagement/web-api/accessrights?view=dynamics-ce-odata-9

Once I created the functions and tested the logic, I passed the two parameters back from the Custom Action of whether the user is the owner and the user has access.

The code below shows the code that would check if the principal (user or team) has the correct access in the principalobjectaccess entity.

private bool principalHasAccess(Guid principalId, Guid objectId, int position) {
  bool hasAccess = false;

  QueryExpression query = new QueryExpression("principalobjectaccess");
  query.ColumnSet.AddColumns("accessrightsmask");

  query.Criteria.AddCondition("principalid", ConditionOperator.Equal, principalId);
  query.Criteria.AddCondition("objectid", ConditionOperator.Equal, objectId);

  EntityCollection results = Context.SystemOrgService.RetrieveMultiple(query);
  if (results.Entities.Count > 0) {
    foreach(Entity poa in results.Entities) {
      int accessRightsMask = poa.GetAttributeValue < int > ("accessrightsmask");
      BitArray accessRights = new BitArray(new int[] { accessRightsMask });
      hasAccess = accessRights.Get(position);
    }
  }
  return hasAccess;
}

Once the flow was completed, the only thing that was left to do was call it from the Canvas app, and set the return variable to whether the App should run as read-only and read-write.

The image below shows the temporary screenshot that was displayed to the user while the security flow was being executed

Embedded Canvas App - App calling flow

Now that we had to go through all this functionality to implement this, my thoughts were that has to be a way that we can pass parameters between a Model-driven app and a Canvas app in a supported way. Unfortunately, none that I am aware of (or my Microsoft contacts).

I created a new idea in the Power Apps forum, so if you think that this is something that would be helpful to you in future implementations, please vote up.

https://powerusers.microsoft.com/t5/Power-Apps-Ideas/Pass-parameters-from-Model-driven-for-to-embedded-Canvas-app/idi-p/746134

UPDATE: My friend and fellow MVP, Alex Shlega just posted a link on quering the Principal Access Object from within Power Automate flows. Check it out if you are looking for that part of the solution. Thank you Alex:
https://www.itaintboring.com/dynamics-crm/how-to-verify-principle-object-access-directly-from-the-flow/

The post Considerations of Embedding Canvas Apps in Model Driven forms – Revisited appeared first on Aric Levin's Digital Transformation Blog.

]]>
Global Cloning functionality for Dynamics 365 http://aric.isite.dev/dynamics/post/global-cloning-for-dynamics-365/ Thu, 15 Feb 2018 05:28:00 +0000 https://aric.isite.dev/index.php/2018/02/15/global-cloning-functionality-for-dynamics-365/ Recently I had a requirement to provide cloning capabilities for one of the projects that I was working on. It wasn't so simple as to just clone an individual record, but also provide the ability to clone the relationships.

The post Global Cloning functionality for Dynamics 365 appeared first on Aric Levin's Digital Transformation Blog.

]]>
Recently I had a requirement to provide cloning capabilities for one of the projects that I was working on. It wasn’t so simple as to just clone an individual record, but also provide the ability to clone the relationships.

This is where it becomes tricky, as some entities cannot be easily cloned due to some of the restrictions, so we wanted to provide this solution the ability to restrict certain actions from happening. For example, the address1_addressid and address2_addressid fields in the Account and Contact entities cannot be cloned as they point to the Customer Address record, so the Guid there has to be unique.

So for the first scenario, we needed to provide the system with the ability to restrict certain fields from being cloned, by providing a status of Active or Inactive, the cloning solution will decide whether or not to clone the record. The screenshot below shows the Clone Settings entity, with the list of attributes that are available for cloning. Notice the highlighted row is marked as Inactive., which means it will not be cloned.

Clone Settings Entity

Next we had to deal with relationships. One of the issue with relationships is that there are probably many relationships that you do not want to be cloned. In the related entities, we provided 3 statuses: Duplicate, Reassociate or Inactive. The Inactive option skips the cloning procedure for the relationship. The Duplicate will make a duplicate of the record Related entity record and the Reassociate will reassociate the related lookup from the source record to the cloned record.

Clone Relationships

We modifies the application ribbon so that the Clone button will appear on every entity (based on a webapi call to check if the entity is enabled for cloning in the Clone Settings), and added a global script library for this purpose on for calling the Clone action. The end result is as follows.

Original and Cloned records

This solution works for our purpose, but I would consider it a Beta Release for anyone who is interested in source code. It’s available on github, so you can make whatever changes that you want. I will add instructions on how to use sometime soon.

The post Global Cloning functionality for Dynamics 365 appeared first on Aric Levin's Digital Transformation Blog.

]]>
Action Parameters referencing deleted entities http://aric.isite.dev/dynamics/post/action-parameters-referencing-deleted-entities/ Fri, 02 Feb 2018 06:35:00 +0000 https://aric.isite.dev/index.php/2018/02/02/action-parameters-referencing-deleted-entities/ While working on a project, I recently encountered a strange behavior in Dynamics 365. I am not sure if this is a bug (or feature), but something that we should be really careful about. One of the entities that we were working on, was created without following the organizational guidelines and had to be recreated. In this particular scenario it is not really a big deal because the project is still in early stages of implementation so changes and cleanup occur on a regular basis.

The post Action Parameters referencing deleted entities appeared first on Aric Levin's Digital Transformation Blog.

]]>
While working on a project, I recently encountered a strange behavior in Dynamics 365. I am not sure if this is a bug (or feature), but something that we should be really careful about. One of the entities that we were working on, was created without following the organizational guidelines and had to be recreated. In this particular scenario it is not really a big deal because the project is still in early stages of implementation so changes and cleanup occur on a regular basis.

We removed all of the references to the entity, and the entity got deleted. So far no issues. One of the developers on the project created an action which one of the Input Parameters was an Entity Reference to the deleted entity. After the entity was deleted, when the developer tried to open the Action, he got a Query error, and could not open the action. In the Url of the designer we could see that there was an error that said “Entity xyz_customentity does not exist in the metadata cache”.

After a little brainstorming, we exported the action (as a solution), and found the Xml of the input parameter that was pointing to the entity. We could remove the Input Parameter Xml and reimport the solution and hope that everything would work, or the probably easier solution was to just recreate the entity (no extra customization needed) and keep the same schema name.

We recreated the entity, and immediately the action opened up. We then went on and removed the Input Parameter that was causing the issue, activated the action and deleted the entity. Quick Fix to an annoying problem.
The bottom line is this this should probably not happen.

I submitted this to ideas.dynamics.com. If anyone is interested in voting up this idea, you can click on the following link:
https://ideas.dynamics.com/ideas/dynamics-crm/ID0003925

The post Action Parameters referencing deleted entities appeared first on Aric Levin's Digital Transformation Blog.

]]>
Cloning a Record in Dynamics CRM http://aric.isite.dev/dynamics/post/clone-record-in-dynamics-crm/ Sun, 22 Oct 2017 04:22:00 +0000 https://aric.isite.dev/index.php/2017/10/22/cloning-a-record-in-dynamics-crm/ Recenly we received requests from clients and some questions from Dynamics Community members on how to Clone records. Although there are some available solutions out there, and the various possibilities on how to implement this, we would like to demonstrate here one possibly and not to complicated way on how to implement this. This implementation involved using Ribbon Workbench to create the Clone button and a Command that will execute a JavaScript function, which will call an action and execute Plugin/Action code to copy the record.

The post Cloning a Record in Dynamics CRM appeared first on Aric Levin's Digital Transformation Blog.

]]>
Recenly we received requests from clients and some questions from Dynamics Community members on how to Clone records. Although there are some available solutions out there, and the various possibilities on how to implement this, we would like to demonstrate here one possibly and not to complicated way on how to implement this. This implementation involved using Ribbon Workbench to create the Clone button and a Command that will execute a JavaScript function, which will call an action and execute Plugin/Action code to copy the record.

So the first step of course is using Ribbon Workbench to create the button, enable/display rules and the command. We start of by adding our entity that we want to customize to a new Unmanaged solution. We only need to add the entity, and not all of the components of the entity. Once we created the solution we open Ribbon Workbench, and select the new solution that we created.

We can not add a new button with an image to the form command bar, as shown in the picture below:

Add Button to Form Command Bar

You should also add the Label, Alt, Tool Tip Text and Tool Tip Description. It is a good habit to have the Tooltip Description display a different text that the Tool Tip Text. We can now create the display rule. We created a simple display rule only with a FormStateRule that has a State of Existing. This means that the Clone Command will only be available for records that already exist in the System, but not for newly or disabled records. You can change the display rule, as you wish.

Once we have the display rule, we will create the command. You should have a JavaScript Library in place for your entity that you wish to clone ahead of time, so that you can use it for the Command. You can also use a Global library if you are planning to use the same logic for multiple entities. In the command we will Create a Custom JavaScript action and specify the name of the library and function that will be used when the Clone Command Bar button is clicked (shown is the image below).

Clone Record Command

Let’s go ahead and look at the finalized button properties before publishing the solution changes.

Clone Button

We can now click on the Publish button in Ribbon Workbench to finish adding the button to our form. The next step is to add the JavaScript code to our library that we previously created. There are a few ways of calling actions from JavaScript, but we prefer to use the process.js JavaScript Library. This library can be downloaded from github, and is available here: https://github.com/PaulNieuwelaar/processjs. Add this library to your form where you added the Clone button to, so that you can use it in your JavaScript Library.

function cloneGlobalSetting()
{
    var entityId = Xrm.Page.data.entity.getId();
    var entityName = "new_globalsetting";
    var messageName = "new_CloneGlobalSetting";
    var success = callCloneAction(entityId, entityName, messageName);
    if (success) {
        Xrm.Page.ui.setFormNotification("Record has submitted for cloning.", "INFO", "GEN")
        setTimeout(function () {
            reloadPage(entityId);
        }, 3000);
    }
}

The above function calls the callCloneAction, and passes the Guid of the entity record, the name of the entity and the name of the SDK message that will be created. The below code snippets show the callCloneAction and callProcessAction functions. Although these can be combined into one, we separated them, since we have multiple function calls to Actions in our code, and use the callProcessAction for multiple purposes. If you only have one call to callProcessAction, you can keep it separate, or add it to your global script library to be used across multiple entities.

function callCloneAction(entityId, entityName, messageName, name) {

    var inputParams =
        [
            {
                key: "Target",
                type: Process.Type.EntityReference,
                value: new Process.EntityReference(entityName, entityId)
            }
        ];
    var success = callProcessAction(entityId, entityName, messageName, inputParams);
    return success;
}

function callProcessAction(entityId, entityName, messageName, inputParams) {
    var success = true;
    Process.callAction(messageName, inputParams,
    function (params) {
        // Success
        var result = "";
        for (var i = 0; i < params.length; i++) {
            result = params[i].key + "=" + params[i].value + ";";
        }
        // alert(result);
        success = true;
    },
    function (e, t) {
        // Error
        success = false;
        alert(e);

        if (window.console && console.error)
            console.error(e + "n" + t);
    }
    );
    return success;
}

Now that we have added our JavaScript code to our library, we need to upload and publish this file, as well as add it to the form. Once that is done, we need to create the Action Process in our solution. We do this by navigating to Settings -> Processes and Creating a New Action. The screenshot below shows the Action Process that we created. Note that there is a parameter called Action Type, which is not required for this particular case, but is used for our cloning process, since the record can be cloned in a few different ways.

Create Action Process

We now have to add the Plugin/Action code and register it. The first thing to do is create a new plugin. If this is the first time you are creating a new plugin, please follow the Microsoft MSDN article on how to create a Basic Plugin here. In our Plugin project, we created two classes, although this can be done with one. The first class shown below is just the entry point which calls the actual class that processes the action.

    public class GlobalSetting : Plugin
    {
        public GlobalSetting()
            : base(typeof(GlobalSetting))
        {
            this.RegisteredEvents.Add(new Tuple<int, string, string, Action<LocalPluginContext>>(40, "new_CloneGlobalSetting", "new_globalsetting", new Action<LocalPluginContext>(ExecutePostGlobalSettingClone)));
        }

        protected void ExecutePostGlobalSettingClone(LocalPluginContext localContext)
        {
            if (localContext == null)
            {
                throw new ArgumentNullException("localContext");
            }
            string entityName = localContext.PluginExecutionContext.PrimaryEntityName;
            Guid entityId = localContext.PluginExecutionContext.PrimaryEntityId;
            string actionType = localContext.PluginExecutionContext.InputParameters["ActionType"].ToString();


            ITracingService tracingService = localContext.TracingService;
            tracingService.Trace("Entered {0} Plugin Method", "ExecutePostGlobalSettingClone");

            using (GlobalSettingLogic logic = new GlobalSettingLogic(localContext.OrganizationService, localContext.TracingService))
            {
                logic.CloneGlobalSetting(entityName, entityId, actionType);
            }
        }
    }

In our second class (GlobalSettingLogic), which is only a class that hold the logic of the requirement we will add the following function:

        private Guid CloneGlobalSetting(Entity globalSetting)
        {
            Entity newGlobalSetting = new Entity(new_GlobalSetting.EntityLogicalName);

            foreach (KeyValuePair<String, Object> attribute in globalSetting.Attributes)
            {
                string attributeName = attribute.Key;
                object attributeValue = attribute.Value;

                switch (attributeName.ToLower())
                {
                    case "new_globalsettingid":
                        break;
                    case "new_name":
                        newGlobalSetting[attributeName] = attributeValue + " - Cloned";
                        break;
                    default:
                        newGlobalSetting[attributeName] = attributeValue;
                        break;
                }

            }

            try
            {
                Guid globalSettingId = service.Create(newGlobalSetting);
                return globalSettingId;
            }
            catch (FaultException<OrganizationServiceFault> ex)
            {
                throw new InvalidPluginExecutionException("An error occurred in the CloneGlobalSetting function of the plug-in.", ex);
            }
        }

We now how to Build our Plugin, and deploy it using the Plugin Registration Tool (as shown in the following screenshot). The Action Process must have been completed prior to this step in order for it to appear as a Message in the Plugin Registration Tool.

Register Action

Once the Plugin is registered, you are basically done. You can now test it out, and go to your form, click on the Clone button and a newly Cloned record can be created. The final form will look like this with the Clone button:

Clone Button on Form

The post Cloning a Record in Dynamics CRM appeared first on Aric Levin's Digital Transformation Blog.

]]>
Creating an Approval Process http://aric.isite.dev/dynamics/post/create-an-approval-process/ Wed, 11 Nov 2015 04:07:00 +0000 https://aric.isite.dev/index.php/2015/11/11/creating-an-approval-process/ We recently had a requirement to add an approval process to an entity in CRM. This was a custom entity (Comment), where most of the fields were prepopulated from the web, but had a response field that had to go through multiple layers of approval/review. The review process was once the comment was received it would be assigned to a responder, which would then go through manager, legal and director approvals.

The post Creating an Approval Process appeared first on Aric Levin's Digital Transformation Blog.

]]>
We recently had a requirement to add an approval process to an entity in CRM. This was a custom entity (Comment), where most of the fields were prepopulated from the web, but had a response field that had to go through multiple layers of approval/review. The review process was once the comment was received it would be assigned to a responder, which would then go through manager, legal and director approvals.

We first created a field in CRM called Review Stage that would hold the value of the review stage.

Added Submit for Approval, Approve and Lock/Unlock buttons using Ribbon Workbench. Thank you Scott DuBrow.

Ribbon Workbench

The next step is that we needed to provide custom privileges for the users. Some users would have multiple roles and particular rights that had to be validated before a particular action would need to be performed. Although this could have been done via Security Roles, the restrictions had to be per user, so we added certain security rights to the User Entity. We also considered doing this in custom entity, but since there were not that many specials privileges, we ended up adding the fields to the user entity.

Privileges

The different roles of the users required particular rights, such as which users could assign or reassign a record to another user, which users could submit for approval (mark complete), and who can approve. The main reasoning behind this, is that users could perform multiple actions (such as being a responder and a manager, or being a manager and a director).

Comment Flow

There were three main Actions (or custom messages) that would be called from the Comment record.
These would be Assign, Submit for Approval and Approve. Each of these functions would call the Action Process via JavaScript and would then execute the Plugin Message code.

The following is an example of the Approve Comment Action that is being called via JavaScript.

Action Properties

Once the Action is called, it will execute the Plugin code, which would validate whether the user has the proper privileges, and whether the user is the proper user in the chain of approvals.

Sample Code

When the plugin code finished executing, the form would refresh, and show the user that the process has advances to the next stage. We did this by creating a custom wizard web resource (looks similar to the Business Process Flow, but without all the bells and whistles). This would also allow us to lock down records once it reached certain stages of the approval process.

Final Flow

Since Directors are not really used to going through hundreds of records in CRM, we created an Export Process for them, that will allow them to export their comments to Excel. We restricted the user of the out-of-the-box Export to Excel feature, since we needed the Excel workbook to be protected/restricted to only certain columns. We created a process Console application called by a Task scheduler running on a separate application server. This process would check for requests from users for Excel exports and would generate the files and send them to the users via email. The process was implemented using the Infragistics Excel framework (an easier way to generate Excel files then using the Excel object model). The files would be locked for editing with the exception of the columns/rows that we needed.

We then created an SSIS import package using the SSIS Integration Toolkit for Microsoft Dynamics CRM (by Kingswaysoft) that allowed us to validate and upload the changes from the high level approvers that wanted to go over the responses in back into CRM.

The post Creating an Approval Process appeared first on Aric Levin's Digital Transformation Blog.

]]>