CRM Tip: Issue with checking Security role GUID of custom security roles
Problem Statement: We often encounter a scenario where we need to perform some operations based on the fact if the user has a particular security role. We also know that the GUID of a custom security role created by us remains the same, even if is installed in some other environment. Consider the following scenario: I want to check if the logged in User has “Custom View Only Role”, I want to hide some fields on the form. Also, the role – “Custom View Only Role” is created by me. Common Solution: For the above problem, this is how a developer would proceed: Get the GUID of the security role, and hardcode it for checking. Get all the User roles of the logged in user. Iterate on all the roles and check if any of the roles from the user roles match the GUID of the “Custom View Only Role”. If any match, then returns true and based on this perform the required operation – in this case, hide some fields. This is how the code would look: function CheckUserRole() { // GUID of the custom role that you created. var CustomViewOnlyRoleId = “XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX”; // Get all the roles of the Logged in User. var currentUserRoles = Xrm.Page.context.getUserRoles(); for (var i = 0; i < currentUserRoles.length; i++) { var userRoleId = currentUserRoles[i]; if (userRoleId.toLowerCase() == CustomViewOnlyRoleId.toLowerCase()) { // Return true if the Role matches return true; } } return false; } Issues with the above Solution This will only work if the D365/ CRM Organization has only root Business Unit and no child business units It will fail if there are any child BUs and the logged in user is in any of the Child BU and also has the “Custom View Only Role”. In this case, the function will return false, even though the User had the role Why this happens: This happens because, in CRM, a copy of all the roles is created for each BU. So the GUID of all the same role within Different BU will be different. Alternative Solution: We can check with Role name instead of GUID, and tweak the above code. But checking with Role names is not a good practice since the role names can be changed in the future. Better Solution Since the issue is with copy of the same role for different BU, we can solve this by leveraging the “Parent root role id” There is a field on the Role entity called Parent Root Role. This stores the reference of the Root role on all the copies of each BU role. So even though the role ids will not be same, the Parent Root Role Id will be same for all the copies. Below is the code to leverage the parent role id and check if the logged in user has the role using the Role GUID. You can also find this code in My Github function CheckUserRole() { // GUID of the custom role that you created. var CustomViewOnlyRoleId = “XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX”; var returnValue = false; // Get all the roles of the Logged in User. var currentUserRoles = Xrm.Page.context.getUserRoles(); // Get the Parent roles for each, and then compare. GetParentRoles(currentUserRoles, function (result) { for (var i = 0; i < result.value.length; i++) { if (result.value[i][“_parentrootroleid_value”].toLowerCase() == CustomViewOnlyRoleId.toLowerCase()) returnValue = true; } }, function (error) { alert(error); } ); return returnValue; } function GetParentRoles(roles, successCallback, errorCallback) { var fetchXml = ”; fetchXml += ‘<fetch version=”1.0″ output-format=”xml-platform” mapping=”logical” distinct=”false”>’; fetchXml += ‘<entity name=”role”>’; fetchXml += ‘ <attribute name=”name” />’; fetchXml += ‘ <attribute name=”businessunitid” />’; fetchXml += ‘ <attribute name=”roleid” />’; fetchXml += ‘ <attribute name=”parentrootroleid” />’; fetchXml += ‘ <order attribute=”name” descending=”false” />’; fetchXml += ‘ <filter type=”or”>’; for (var cnt = 0; cnt < roles.length; cnt++) { fetchXml += ‘ <condition attribute=”roleid” operator=”eq” value=”{‘ + roles[cnt] + ‘}” />’; } fetchXml += ‘ </filter>’; fetchXml += ‘</entity>’; fetchXml += ‘</fetch > ‘; MK.WebAPI.Retrieve(“roles”, null, fetchXml, null, successCallback, errorCallback, true, null, null, false); } In case of queries or feedback, please comment on the post below.
Share Story :
CRM Tip: How to Check Security Role in Plugins – Correct way
Problem Statement: We often have requirements to perform some action based on certain security role. For ex., we only want System Administrator to delete particular record, and no one else should delete irrespective of their security access. There are many ways to achieve this, but many of the times the solution is not foolproof Incorrect/ Misguided Solution: Generally developers achieve the above requirement by using plugin with below steps: Get User ID from the plugin context. Get all the roles of the user Loop and check if any of the role name is “System Administrator”. If Step 3 is true, then allow delete, else restrict delete This solution works most of the time, but this won’t work if the client is using any other language than English in CRM. Since role names are customized based on language, the above plugin won’t find any user with the System Administrator name of the role. Solution: For language proof solution, we must use the role template lookup on the Role entity. For OOB security roles, there is a role template GUID which does not change based on environment. For System Administrator, the Role Template ID is “627090FF-40A3-4053-8790-584EDC5BE201” The following code will get the System Administrator properly. You can find the sample plugin on my GitHub as well. public bool HasAdminRole(Guid systemUserId) { Guid AdminRoleTemplateId = new Guid(“627090FF-40A3-4053-8790-584EDC5BE201”); QueryExpression query = new QueryExpression(“role”); query.Criteria.AddCondition(“roletemplateid”, ConditionOperator.Equal, AdminRoleTemplateId); LinkEntity link = query.AddLink(“systemuserroles”, “roleid”, “roleid”); link.LinkCriteria.AddCondition(“systemuserid”, ConditionOperator.Equal, systemUserId); return service.RetrieveMultiple(query).Entities.Count > 0; } Note: This can be done for other OOB roles as well like Sales Manager, Sales Person, etc. For custom roles, the role template Id is empty. If the custom roles are created by you, then you can used the Role Id (Unique GUID of Role entity) for querying instead of names.
Share Story :
CRM tip: How to Publish Multiple Products from CRM UI
Problem Statement: In CRM, most organizations generally import products from other Source through Excel or other data import means. In general, we do not want to publish the product as soon as they are created because we need to set the price list and units before they are available anywhere in CRM. If there are many products, CRM does not allow to publish all the products at once. It becomes very painful to publish each and every product. This is unimaginable if the count is in thousands and more. See screenshot below which suggests I cannot see the Publish option if I select more than 2 products in the view: Solution: The best way to tackle this is: Create a Default Product Family (Dummy product family). Add all the products in the Dummy product Family. You can do this while importing products as well by setting the parent product as the Default Product Family. In case you already have a family and hierarchy, you can still follow this approach by setting the parent of root family as the Default Product Family. E.g. If you have products as the following defined hierarchy: Product Family A A1 A2 A3 A31 A32 Product Family B B1 B2 … In this case, you can set the parent of “Product Family A” as Default Product Family Once this is done, you can now import all the products, set up pricing and units as required. This is how your Hierarchy will look like in CRM: When all the products are ready, go to the Default Product Family A On the ribbon, Select Publish –> Publish Hierarchy This will publish all the products in the hierarchy. Since all your products are in the hierarchy of the “Default Product Family”, all the products will be published saving A LOT of Time. TAKE THAT CRM … You CANNOT SLOW US DOWN!!
Share Story :
XRM Toolbox – Important tools for Admins and Developers
What is XRM toolbox? XrmToolBox is a Windows application that connects to Microsoft Dynamics CRM, providing tools to ease customization and configuration tasks. It is shipped with more than 30 plugins to make administration, customization or configuration tasks easier and less time consuming. XRMToolBox is free of cost, but you can donate to support development. In this blog, I will list down the top 5 tools from XRM toolbox for both developers and CRM Administrators. How to Use it? Documentation is available through the wiki of XrmToolBox Github’s repository. You will find information about prerequisites, a list of known existing plugins, help on how to connect to your Microsoft Dynamics CRM deployment, and much much more.. Top Tools of XRM toolbox for Developers. 1. WebResources Manager Author- MscrmTools Description – Manage your webresources easily. This tool has saved me hours of time for creating, updating and managing the web resources for Dynamics CRM. This is a must have for all the Developers 2. SiteMap Editor Author – MscrmTools Description – Manage the SiteMap with no XML edition. Editing Sitemap XML is scary for any developer as any mistake in XML can make the Entire CRM unusable. This tool helps to edit your sitemap components. You can easily create areas and subareas, update the order and copy/ paste components from one area to another. You can also update all the sub area details like icon, titles for multiple languages etc. 3. Ribbon Workbench 2016 Author – Scott Durow Description – Edit the Dynamics CRM Ribbon or Command Bar from inside the XrmToolbox. By installing the Ribbon Workbench you’ll quickly be performing customisations that were previously only possible by time consuming and error-prone manual editing of RibbonDiff Xml. Learn More – https://www.develop1.net/public/rwb/ribbonworkbench.aspx 4. FetchXML Builder Author – Jonas Rapp Description – The tool will assist in three major areas: Constructing FetchXML in ways that Advanced Find cannot – aggregates – outer joins – “has no”-queries – attributes from multi-level linked entities Querying CRM for information not (easily) found in the CRM UI – system / internal entities – attributes hidden in CRM UI – join on other fields than relationships Developer assistance – Generate C# QueryExpression code from fetch xml – Generate OData query string from fetch xml – Easy to use UI to compose queries for reports in CRM The tool reads metadata from CRM to assist with selecting entities, attributes, relations and to perform validation of condition values. To make it more appealing, there is also the possibility to have it show “Friendly names”, which will replace the technical names of entities and attributes with their display names in the users’ currently selected language, much like Advanced Find does. 5. Metadata Document Generator Author – MscrmTools Description – A tool to generate excel and word document with entities and attributes information. You’ll no longer have to write “by hand” these awful tables full of metadata information. Top Tools of XRM toolbox for Developers: 1. Your User Security – Magnified Author – NORRIQ Belgium Description – Provides a detailed overview of a specified System User’s security. Very helpful to identify the exact permission a user has on entity based on all the roles assigned to him 2. Form related tools Author – MscrmTools Description – Set of tools for XrmToolBox regarding form management4 3. Solution Components Mover Author – MscrmTools Description – Transfer solution components across solutions. Again a big time saver for moving components from one solution to another. 4. User Settings and Utility Author – MscrmTools Description – Manage and update All user’s personal settings in Bulk 5. Attribute Usage Inspector Author – MscrmTools Description – Inspects the usage of attributes per entity. Admins can review which attributes are not used across all entities and which are used most.
Share Story :
Create Attachment of Signature/ Pen control data in Dynamics CRM
Introduction We already saw how to use Pen/ Signature control in Dynamics CRM in the previous blog: https://www.cloudfronts.com/adding-signature-control-mobile-tablets-dynamics-crm/ In this blog, we will see how to generate an image of the pen control data and store in Record as an attachment. Steps As we saw in the previous blog, the signature captured from phone is stored as a multi-line text field in Dynamics CRM record. And we cannot see the signature image in web browser. Now it will be a common scenario where the Users will need to see the Signature image in browser as well. This is how data is captured on Web browser. We need to write custom code which will: Read this data Convert the data to an image Store the converted image as an attachment in the CRM record For this I, have written a generic plugin which will do the above actions. You can see the core plugin code with comments below. I have also added the plugin to Github for reference: Github Link //// The plugin is registered on the Post Update on “Customer Approval” field on Opportunity. Entity entity = (Entity)context.InputParameters[“Target”]; //// The field which stores the data for Signature string signatureFieldName = “new_customerapproval”; if (entity.Contains(signatureFieldName)) { string encodedData = entity.GetAttributeValue<string>(signatureFieldName); //// Remove the additional Metadata from the text generated. int startIndex = encodedData.IndexOf(“base64,”) + 7; encodedData = encodedData.Substring(startIndex, encodedData.Length – startIndex); tracer.Trace(encodedData); string contentType = “image/png”; Entity Annotation = new Entity(“annotation”); Annotation.Attributes[“objectid”] = new EntityReference(entity.LogicalName, entity.Id); Annotation.Attributes[“objecttypecode”] = entity.LogicalName; Annotation.Attributes[“subject”] = “Customer Signature”; //// You can have any subject as required. Annotation.Attributes[“documentbody”] = encodedData; Annotation.Attributes[“mimetype”] = contentType; Annotation.Attributes[“notetext”] = “Customer Signature Attached”; //// Again, add any note text as needed Annotation.Attributes[“filename”] = “Customer Approval Signature.png”; //// OR Any name as required Guid annotation = service.Create(Annotation); } This plugin should be registered on update of the Signature control field. In this case, it is “Customer Approval”. It is preferable to have the step run asynchronously. We can now see the attachment on the record. If you have any issues or need more information, please post in the comments section below.
Share Story :
Using Real time workflows for Business Validations in Dynamics CRM
Introduction: Like asynchronous workflows, real-time workflows can be used to model and automate real world business processes. Real-time workflows are for business users, for example business analysts, to implement similar functionality to synchronous plug-ins without requiring .NET Framework programming experience. How to use Real time Workflows for Custom Business Validations: Real time workflows can be used to not only implement business logic in Dynamics, but also simple and complex validations in the business flow. Let us understand this using 2 examples: Example 1: We have a business Validation that leads with employee size less than 100 should not be allowed to qualify. Earlier, we used to do this either from JS form scripting or using Plugin. But we can also use Real time workflow to achieve this, and it can throw a validation message as well to user. 1. For it to throw a validation message, we need to stop the workflow and select reason as cancelled. Then we can set the status reason as the message we want to display the user. 2. This is what the User will see if he/ she tries to qualify a lead with employee size less than 100. 3. This is the workflow that we configured: 4. You can specify the validation message in the Stop Workflow properties. Please note that Status of “Stop Workflow” should be cancelled for users to see the message. Example 2: We can have more complex validations as well using Real time workflow. 1. For example, Lets say we don’t want the opportunity to be Won unless the Account (Customer) has Credit limit greater than the Opportunity Budget Amount. We can configure this condition as well in Workflow. Refer Screenshot below: 2. And the End user will see the error message as below when he/ she tries to mark the opportunity as Won when the customer’s credit limit is less than the Opportunity Budget. Conclusion: Real time workflows are very powerful, and can be leveraged to perform server side validations in Dynamics CRM, along with real time CRUD operations and Email messages.
Share Story :
Alternatives of Document storage in Dynamics CRM
Scenario: CRM space is expensive, and often clients want alternatives to CRM storage for storing documents, images as these take up most of the space. Available solutions: SharePoint Online with Dynamics CRM OneDrive for Business with Dynamics CRM Currently, SharePoint document management is the preferred choice for most of the customers as alternative to storing email attachments and documents. Advantages of SharePoint: SharePoint storage cost is very small about $0.20 per GB/Month compared to CRM’s $9.99/GB/Month. So, CRM space is around 50 times costlier than SharePoint space. You can leverage SharePoint Document management features like: Full text Search Metadata sorting Revisions Enterprise grade security There are 2 ways to use SharePoint for document management with Dynamics: Use SharePoint Online Integration with Dynamics CRM. This is the ideal and efficient way to use SharePoint. You can see the steps for SharePoint online integration in one of our previous blogs: https://www.cloudfronts.com/enable-sharepoint-integration-and-onedrive-for-business-in-crm/ Use 3rd Party tools like Power Attachment, which will migrate your File attachments (notes) and Email attachments from CRM storage to Dynamics. More detail and pricing about Power attachments can found here: http://www.powerobjects.com/powerpacks/powerattachment/ 1st approach should be the preferred way for using SharePoint as it is free, and works well. But users complain about an extra step to navigate to attachments, in which case you can go for Approach 2. Alternative Solution: The drawback with using SharePoint is if you have requirement of migrating your documents from CRM storage (Notes Attachments and Email attachments), you need to use 3rd party paid tools like Power Attachment. Developing custom plugins to migrate documents to SharePoint is difficult in CRM online, since we cannot use External libraries in Sandbox plugin. Due to above 2 reason, we can use Azure Blob storage as a possible alternative for migrating CRM documents. What is Azure Blob storage: Massively-scalable object storage for unstructured data With exabytes of capacity and massive scalability, Azure Blob storage easily and cost-effectively stores from hundreds to billions of objects, in hot or cool tiers depending on how frequently data access is needed. Store any type of unstructured data—images, videos, audio, documents and more. Azure Blob Features: Easy to Use – Geo Redundancy Robust API access Very Cheap storage space: It costs about $0.03/ GB/ month- which is 6.5x less than SharePoint storage cost and 300x less than CRM storage cost. Learn more about pricing here: https://azure.microsoft.com/en-us/pricing/details/storage/blobs-general/ API Coding for CRUD Operations in Azure Blob: I have written a sample plugin which will migrate the CRM attachment to Azure blob, and save the Azure blob file link back in CRM. The plugin is registered on Annotation entity For this, I have used a RestHelper and BlobHelper utility code files, which have all the operations of (a) making a web request and (b) performing blob operations. The Helper files and the CRM plugin sample can be found in the below GitHub link: https://github.com/somesh2207/CRMOnlineWithAzureBlob The plugin file is UploadDocumentToBlob.cs The below code from the plugin file takes the document from CRM and creates a blob using REST API: Entity entity = (Entity)context.InputParameters[“Target”]; string documentBlobURL = string.Empty; //// Optional condition to migrate attachments related to particular entity. //// If you want to migrate attachments for all entities, remove this CONDITION if (entity.Contains(“isdocument”) && entity.GetAttributeValue<bool>(“isdocument”) == true && entity.GetAttributeValue<string>(“objecttypecode”) == “account”) { string storageAccount = “<storageaccountname>”; string filename = entity.GetAttributeValue<string>(“filename”); string containerName = “<blobcontainername>”; string storageKey = “<blobstorage_accesskey>”; //// Read File string text = entity.GetAttributeValue<string>(“documentbody”); BlobHelper blobHelper = new BlobHelper(storageAccount, storageKey); bool isUploadSuccess = blobHelper.PutBlob(containerName, filename, text); //// Once blob upload is Success, get the Azure blob download-able URL of the uploaded File if (isUploadSuccess) documentBlobURL = string.Format(“https://{0}.blob.core.windows.net/{1}/{2}”, storageAccount, containerName, filename); }
Share Story :
Understanding Subgrid functions in CRM Online
Using the new subgrid functions, there are 2 ways to get count of the subgrid data. Based on requirement, we have to use one or the other way for achieving the requirement. getTotalRecordCount(): It determines the total no. of records that match the filter criteria of the view. Features: 1. The count is NOT limited by the current page size. You will get the total size across all pages. 2. You may not also get the latest count of the records using this function if new records are added on the subgrid. How to get around #2 above to get correct count: The idea is to make the grid refresh before executing the “getTotalRecordCount” function, which will enable us to get the correct count. We also have to add an onload event handler for the grid control. Whenever grid is refreshed through code or manually, the onload function will trigger. Note that we cannot do this without onload event. If we try to execute “getTotalRecordCount” just after the refresh line without onload event handler, you will get an error as the grid refresh happens asynchronously and it may not be ready. Sample Code var myContactsGridOnloadFunction = function () { console.log(“Contacts Subgrid OnLoad occurred”); var count = Xrm.Page.getControl(“Contacts”).getGrid().getTotalRecordCount(); }; /// bind an onload event to the grid control Xrm.Page.getControl(“Contacts”).addOnLoad(myContactsGridOnloadFunction); /// refresh the grid. This will trigger onload event. Xrm.Page.getControl(“Contacts”).refresh() getRows().getLength() getRows() will return collection of all gridrows in the grid. Features: 1. It will only return the rows currently visible on the grid. 2. getRows().getLength() will return the count of rows currently visible 3. Unlike getTotalRecordCount, we need not refresh the grid to get the correct row length using this function. Uses: In cases, where we want to know if the grid is empty we can use “getRows().getLength()” instead of getTotalRecordCount to know if the count is 0 or Not. Sample code: var rows = Xrm.Page.getControl(opptyProductSubGridName).getGrid().getRows(); var rowLength = rows.getLength();
Share Story :
Accessing Azure Blob storage from CRM Online Plugin
In this blog article, we will see how to access Azure Blob storage and create document in CRM by reading a specific blob document in Azure. Steps: Get the following details of the Azure Blob storage Primary access key Blob container name File Name of the document Now we will write a sample plugin code which will read document from the Azure Blob storage and create an Annotation in CRM. Since we cannot use external DLL in CRM online plugins, we are going to use the Web request to access the Azure blob storage. The HTTP web request has a bunch of headers along the above details to successfully access Azure blob storage. For this, I have used a RestHelper and BlobHelper utility code files, which have all the operations of (a) making a web request and (b) performing blob operations. The Helper files and the CRM plugin sample can be found in the below GitHub link: GitHub: CRM Online Integration with Azure Blob Using this we can get the document and create Annotation in CRM using the below code: #region Connect and fetch the data from Blob storage // Replace the below values with actual details from your Azure Blob storage string storageAccount = “blobstorageaccountname”; string filename = “filenamehere”; // testdocument.pdf string containerName = “containernameHere”; //documents string storageKey = “primaryaccesskiyeofazureblobstorageaccount”; BlobHelper blobHelper = new BlobHelper(storageAccount, storageKey); KeyValuePair<byte[], string> data = blobHelper.GetBlobResponse(containerName, filename); byte[] body = data.Key; string contentType = data.Value; #endregion #region Create Annotation in CRM string encodedData = System.Convert.ToBase64String(body); Entity Annotation = new Entity(“annotation”); Annotation.Attributes[“objectid”] = new EntityReference(workOrder.LogicalName, workOrder.Id); Annotation.Attributes[“objecttypecode”] = workOrder.LogicalName; Annotation.Attributes[“subject”] = “Document from AX Integration”; Annotation.Attributes[“documentbody”] = encodedData; Annotation.Attributes[“mimetype”] = contentType; Annotation.Attributes[“notetext”] = “REST API – Sample document from AX.”; Annotation.Attributes[“filename”] = entity.GetAttributeValue<string>(“cf_name”); Guid annotation = service.Create(Annotation); #endregion
