Category: C#

CRM Web Portal with Token Based authentication -Part 1

CRM Web Portal with Token Based authentication -Part 1

Sometime we have a requirement to make a web portal with using CRM as back end e.g. ticket management portal. A portal is a custom application that allows external users to access and edit data that is stored in Microsoft Dynamics CRM (a.k.a CRUD – Create, Read, Update and Delete operations). A CRM portal application can be developed with different technologies along with the Dynamics CRM Web Services for data access.

In this article we’ll discuss how we can implement token based authentication in CRM web portal.I’ll divided this article into two parts

  1. CRM Web Portal with JWT authentication -Part 1
  2. Intregate CRM web portal with AngularJS-Part 2(coming soon..)

Token Based Authentication

As I stated before we’ll use token based approach to implement authentication between the front-end application and the CRM API, as we all know the common and old way to implement authentication is the cookie-based approach were the cookie is sent with each request from the client to the server, and on the server it is used to identify the authenticated user.

1. Create a web application (webapi) project in visual studio with no authentication.

create_new_project

create_new_project1

create_new_project2

2.Remove Global.asax from our project and add owin starup class i.e. startup.cs

using System;
using Microsoft.Owin;
using Owin;
using Microsoft.Owin.Security.OAuth;
using Microsoft.Owin.Security;
using System.Web.Http;
using System.Web.Routing;
using System.Web.Mvc;
using System.Net.Http.Headers;
using Microsoft.AspNet.Identity.Owin;

[assembly: OwinStartup(typeof(MSCRMjwtAuth.Startup))]

namespace MSCRMjwtAuth
{
public class Startup
{

public void Configuration(IAppBuilder app)
{
HttpConfiguration httpconfig = new HttpConfiguration();
WebApiConfig.Register(httpconfig);
RouteConfig.RegisterRoutes(RouteTable.Routes);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
app.UseWebApi(httpconfig);
httpconfig.Formatters.JsonFormatter.SupportedMediaTypes.Add(new MediaTypeHeaderValue(“text/html”));

// For more information on how to configure your application, visit http://go.microsoft.com/fwlink/?LinkID=316888
}
}
}

3.After that install below nuget packages

Install-Package Microsoft.AspNet.WebApi.Owin

Install-Package Microsoft.Owin.Host.SystemWeb

Install-Package Microsoft.Owin.Security.OAuth

Install-Package Microsoft.AspNet.Identity.Owin

Install-Package Microsoft.CrmSdk.CoreAssemblies

In this blog we’re authenticating user with contact entity, emailaddress1 field as username and custom attribute am_password  for password and we also store password in encrypted format.

4.Create a new solution i.e. JWTaunthentication for all our schema customization.

create_solution

5. CRMHelper class

CRMhelper class contains functions that check user and password from our existing CRM contact records on behalf of input that user provided.

/// <summary>
/// This class contains all crm functions
/// </summary>
public class CRMHelper
{
//Variable
public IOrganizationService _orgService;

//CRM dynamics Connection
CrmConnection connection = CrmConnection.Parse(ConfigurationManager.ConnectionStrings[“CRMConnectionString”].ConnectionString);

public CRMHelper()
{
_orgService = new OrganizationService(connection);
}

/// <summary>
/// Login Function for parent
/// </summary>
/// <param name=”arry”>array of login id and password</param>
/// <returns>Datacollection of contact entity</returns>
public DataCollection<Entity> tryLogin(string[] arry)
{
try
{
QueryExpression parentQuery = new QueryExpression()
{
EntityName = “contact”,
ColumnSet = new ColumnSet(“firstname”, “lastname”),
Criteria =
{
Filters =
{
new FilterExpression(LogicalOperator.And)
{
Conditions={
new ConditionExpression(“emailaddress1”,ConditionOperator.Equal,arry[0].ToString()),
new ConditionExpression(“am_password”,ConditionOperator.Equal,StringCipher.Encrypt(arry[1].ToString()))
}
}
}
}
};
return _orgService.RetrieveMultiple(parentQuery).Entities;
}
catch (Exception ex)
{
throw;
}
}

/// <summary>
/// Get all Cases related to logged in user
/// </summary>
/// <param name=”customerid”>Contact Entity guid</param>
/// <returns>Datacollection of Incident Entity</returns>
public DataCollection<Entity> getCases(Guid customerid)
{
try
{
// Instantiate QueryExpression QEincident
var QEincident = new QueryExpression(“incident”);

// Add columns to QEincident.ColumnSet
QEincident.ColumnSet.AddColumns(“title”);

// Define filter QEincident.Criteria
QEincident.Criteria.AddCondition(“customerid”, ConditionOperator.Equal, customerid);
return _orgService.RetrieveMultiple(QEincident).Entities;
}
catch (Exception ex)
{

throw;
}
}

}

5. OAuth Bearer Tokens Generation

Add a new class i.e. ApplicationOAuthProvider.cs in our web application that support for OAuth Bearer Tokens Generation.

public class ApplicationOAuthProvider : OAuthAuthorizationServerProvider
{
public override async Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
{
context.Validated();
}

public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
CRMHelper helper = new CRMHelper();
if (context.UserName != null && context.Password != null)
{
var collection = helper.tryLogin(new string[] { context.UserName, context.Password });
if (collection.Count > 0)
{
context.OwinContext.Response.Headers.Add(“Access-Control-Allow-Origin”, new[] { “*” });
var oAuthIdentity = new ClaimsIdentity(context.Options.AuthenticationType);
oAuthIdentity.AddClaim(new Claim(ClaimTypes.Email, context.UserName));
var ticket = new AuthenticationTicket(oAuthIdentity, new AuthenticationProperties());
context.Validated(ticket);
}

}
//return base.GrantClientCredentials(context);
}
}

6. Encryption and Decryption class

public static class StringCipher
{
// This constant string is used as a “salt” value for the PasswordDeriveBytes function calls.
// This size of the IV (in bytes) must = (keysize / 8). Default keysize is 256, so the IV must be
// 32 bytes long. Using a 16 character string here gives us 32 bytes when converted to a byte array.
private static readonly byte[] initVectorBytes = Encoding.ASCII.GetBytes(“tu89geji340t89u2”);

// This constant is used to determine the keysize of the encryption algorithm.
private const int keysize = 256;
private const string passPhrase = “contact”;
public static string Encrypt(string plainText)
{
byte[] plainTextBytes = Encoding.UTF8.GetBytes(plainText);
using (PasswordDeriveBytes password = new PasswordDeriveBytes(passPhrase, null))
{
byte[] keyBytes = password.GetBytes(keysize / 8);
using (RijndaelManaged symmetricKey = new RijndaelManaged())
{
symmetricKey.Mode = CipherMode.CBC;
using (ICryptoTransform encryptor = symmetricKey.CreateEncryptor(keyBytes, initVectorBytes))
{
using (MemoryStream memoryStream = new MemoryStream())
{
using (CryptoStream cryptoStream = new CryptoStream(memoryStream, encryptor, CryptoStreamMode.Write))
{
cryptoStream.Write(plainTextBytes, 0, plainTextBytes.Length);
cryptoStream.FlushFinalBlock();
byte[] cipherTextBytes = memoryStream.ToArray();
return Convert.ToBase64String(cipherTextBytes);
}
}
}
}
}
}

public static string Decrypt(string cipherText)
{
byte[] cipherTextBytes = Convert.FromBase64String(cipherText);
using (PasswordDeriveBytes password = new PasswordDeriveBytes(passPhrase, null))
{
byte[] keyBytes = password.GetBytes(keysize / 8);
using (RijndaelManaged symmetricKey = new RijndaelManaged())
{
symmetricKey.Mode = CipherMode.CBC;
using (ICryptoTransform decryptor = symmetricKey.CreateDecryptor(keyBytes, initVectorBytes))
{
using (MemoryStream memoryStream = new MemoryStream(cipherTextBytes))
{
using (CryptoStream cryptoStream = new CryptoStream(memoryStream, decryptor, CryptoStreamMode.Read))
{
byte[] plainTextBytes = new byte[cipherTextBytes.Length];
int decryptedByteCount = cryptoStream.Read(plainTextBytes, 0, plainTextBytes.Length);
return Encoding.UTF8.GetString(plainTextBytes, 0, decryptedByteCount);
}
}
}
}
}
}
}

7. Create CRM contact record

Also create a record for contact entity in CRM with amankothari@outlook.com as Email and ChHTqB6783q4J/b5BHD7vQ==  as Password (we stored password in encrypted form).

ContactForm.png

8. Add secured controller for Incident

[RoutePrefix(“api/cases”)]
public class CasesController : ApiController
{
CRMHelper helper = new CRMHelper();
[Authorize]
[HttpGet]
[Route(“”)]
public DataCollection<Entity> Getcases()
{
try
{
var identity = (ClaimsIdentity)User.Identity;
foreach (var a in identity.Claims)
{
if (a.Type == “contactid”)
{
return helper.getCases(new Guid(a.Value));
}}
return null;

}
catch (Exception ex)
{
return null;
}
}
}

Notice how we added the “Authorize” attribute on the method “Get” so if you tried to issue HTTP GET request to the end point “http://localhost:port/api/cases&#8221; you will receive HTTP status code 401 unauthorized because the request you send till this moment doesn’t contain valid authorization header.

9.Modified our startup.cs code for support for OAuth Bearer Tokens.

using System;
using Microsoft.Owin;
using Owin;
using Microsoft.Owin.Security.OAuth;
using Microsoft.Owin.Security;
using System.Web.Http;
using System.Web.Routing;
using System.Web.Mvc;
using System.Net.Http.Headers;
using Microsoft.AspNet.Identity.Owin;

[assembly: OwinStartup(typeof(MSCRMjwtAuth.Startup))]

namespace MSCRMjwtAuth
{
public class Startup
{
public static OAuthAuthorizationServerOptions OAuthOptions { get; private set; }
public void Configuration(IAppBuilder app)
{
HttpConfiguration httpconfig = new HttpConfiguration();
ConfigureOAuth(app);
WebApiConfig.Register(httpconfig);
RouteConfig.RegisterRoutes(RouteTable.Routes);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
app.UseWebApi(httpconfig);
httpconfig.Formatters.JsonFormatter.SupportedMediaTypes.Add(new MediaTypeHeaderValue(“text/html”));

// For more information on how to configure your application, visit http://go.microsoft.com/fwlink/?LinkID=316888
}
public void ConfigureOAuth(IAppBuilder app)
{
OAuthAuthorizationServerOptions OAuthOptions = new OAuthAuthorizationServerOptions
{
TokenEndpointPath = new PathString(“/oauth/token”),
Provider = new ApplicationOAuthProvider(),
AccessTokenExpireTimeSpan = TimeSpan.FromMinutes(30),
AllowInsecureHttp = true,
};

// Token Generation
app.UseOAuthBearerTokens(OAuthOptions);
}
}
}

Assuming that you saved the username “amankothari@outlook.com”  with password “ChHTqB6783q4J/b5BHD7vQ==” (123456) in CRM. In the step below, we’ll use the same username to generate token, so to test this out open your favorite REST client application in order to issue HTTP requests to generate token for user “amankothari@outlook.com”. For me I’ll be using PostMan.

Now we’ll issue a POST request to the endpoint http://localhost:57205/oauth/token the request will be as the image below:

Postman1.PNG

If the username and password are not same then we sent bad request in response.

invalid_token_postman

If all is correct we’ll receive HTTP status 200 along with the secured data in the response body, if you try to change any character with signed token you directly receive HTTP status code 401 unauthorized.

Casescaseresponse

Now our back-end API is ready to be consumed from any front end application or native mobile app.

In my next blog we’ll consumed this API with angularJS application.

Below are the links of solution and web project code respectively.

https://1drv.ms/u/s!AvWbcg6Nl97LiUsVexaWI0gZV2TP

https://1drv.ms/u/s!AvWbcg6Nl97LiUyciNWPn4ehGpNH

 

 

 

 

 

Update FetchXml and Grid layout of Entity View using C# in CRM

Update FetchXml and Grid layout of Entity View using C# in CRM

In this post, I am going to tell you how we can update Entity view programmatically using c#.

Before starting let’s cover entity view and their query types.

What is Entity View ?

Entity views are special SavedQuery that retrieve data by using a specific filter. They also contain information about how the data in the view should be displayed in the application.

An Entity view is different from a UserQuery. A user query, called a Saved view in the application, is owned by an individual user, can be assigned and shared with other users, and can be viewed by other users depending on the query’s access privileges.

Let’s create a new view in account entity i.e. My Custom Accounts

1

4

Here is default Grid Layout and FetchXml of this view

2

5.PNG

Now we write some code for getting fetchXml and layoutXml for this view.

 

QueryExpression qe = new QueryExpression(“savedquery“);
qe.Criteria.AddCondition(“returnedtypecode”, ConditionOperator.Equal, “account“);
qe.Criteria.AddCondition(“name”, ConditionOperator.Equal, “My Custom Accounts“);
qe.ColumnSet = new ColumnSet(true);
var viewData= _service.RetrieveMultiple(qe);

 

For our demo purpose we’re trying to add Account Number in FetchXml.

You can also add filter or some complex query which we want to add but can’t add using Advanced find.

 

//Update fetchxml of view
XmlDocument xml1 = new XmlDocument();
xml1.LoadXml(viewData.Entities[0][“fetchxml”].ToString());
XmlNode entityNode= xml1.SelectSingleNode(“//entity”);
var newAttrnode= xml1.CreateElement(“attribute”);
var nameAttribute = xml1.CreateAttribute(“name”);
nameAttribute.Value = “accountnumber”;
newAttrnode.Attributes.Append(nameAttribute);
entityNode.AppendChild(newAttrnode);
dar.Entities[0][“fetchxml”] = xml1.OuterXml;

 

Using below code ,we’re trying to add accountnumber column in our view grid.

 

//Update Gridxml of view
XmlDocument gridxml = new XmlDocument();
gridxml.LoadXml(viewData.Entities[0][“layoutxml”].ToString());
XmlNode rowNode = gridxml.SelectSingleNode(“//row”);
var newrowAttrnode = gridxml.CreateElement(“cell”);
var accountname = gridxml.CreateAttribute(“name”);
accountname.Value = “accountnumber”;
newrowAttrnode.Attributes.Append(accountname);
var width = gridxml.CreateAttribute(“width”);
width.Value = “150”;
newrowAttrnode.Attributes.Append(width);
rowNode.AppendChild(newrowAttrnode);
dar.Entities[0][“layoutxml”] = gridxml.OuterXml;
_service.Update(dar.Entities[0]);

 

//Publish Account Entity
PublishXmlRequest puxml = new PublishXmlRequest()
{
ParameterXml = “<importexportxml><entities><entity>account</entity></entities></importexportxml>”
};
_service.Execute(puxml);

 

That’s it, our custom view successfully updated with Account Number Column. 🙂

7

6

Thanks for visiting my blog.

Do some creative stuff in CRM  !!!  🙂

 

 

 

 

QueryExpression with a paging cookie

QueryExpression with a paging cookie

Query expressions are used in methods that retrieve more than one record, such as the IOrganizationService.RetrieveMultiple method, in messages that perform an operation on a result set specified by a query expression, such as BulkDeleteRequest and when the ID for a specific record is not known.

You can use the paging cookie feature to make paging in an application faster for large datasets. The feature is available in both FetchXML and QueryExpression queries. When you use the paging cookie feature when querying a set of records, the result contains a value for the paging cookie. To improve system performance, you can then pass that value when you retrieve the next set of records.

QueryExpression and FetchXML use different formats for their paging cookies. If you convert from one query format to the other by using theQueryExpressionToFetchXmlRequest message or the FetchXmlToQueryExpressionRequest message, the paging cookie value is ignored. In addition, if you request nonconsecutive pages, the paging cookie value is ignored.

 // Query using the paging cookie.
 // Define the paging attributes.
 // The number of records per page to retrieve.
 int queryCount = 3;
 
 // Initialize the page number.
 int pageNumber = 1;
 
 // Initialize the number of records.
 int recordCount = 0;
 
 // Define the condition expression for retrieving records.
 ConditionExpression pagecondition = new ConditionExpression();
 pagecondition.AttributeName = "parentaccountid";
 pagecondition.Operator = ConditionOperator.Equal;
 pagecondition.Values.Add(_parentAccountId);

 // Define the order expression to retrieve the records.
 OrderExpression order = new OrderExpression();
 order.AttributeName = "name";
 order.OrderType = OrderType.Ascending;

 // Create the query expression and add condition.
 QueryExpression pagequery = new QueryExpression();
 pagequery.EntityName = "account";
 pagequery.Criteria.AddCondition(pagecondition);
 pagequery.Orders.Add(order);
 pagequery.ColumnSet.AddColumns("name", "emailaddress1"); 

 // Assign the pageinfo properties to the query expression.
 pagequery.PageInfo = new PagingInfo();
 pagequery.PageInfo.Count = queryCount;
 pagequery.PageInfo.PageNumber = pageNumber;

 // The current paging cookie. When retrieving the first page, 
 // pagingCookie should be null.
 pagequery.PageInfo.PagingCookie = null;
 Console.WriteLine("Retrieving sample account records in pages...\n");
 Console.WriteLine("#\tAccount Name\t\tEmail Address"); 
 
 while (true)
 {
 // Retrieve the page.
 EntityCollection results = _serviceProxy.RetrieveMultiple(pagequery);
 if (results.Entities != null)
 {
 // Retrieve all records from the result set.
 foreach (Account acct in results.Entities)
 {
 Console.WriteLine("{0}.\t{1}\t{2}", ++recordCount, acct.Name,
 acct.EMailAddress1);
 }
 }

 // Check for more records, if it returns true.
 if (results.MoreRecords)
 {
 Console.WriteLine("\n****************\nPage number {0}\n****************", pagequery.PageInfo.PageNumber);
 Console.WriteLine("#\tAccount Name\t\tEmail Address");

 // Increment the page number to retrieve the next page.
 pagequery.PageInfo.PageNumber++;
 
 // Set the paging cookie to the paging cookie returned from current results.
 pagequery.PageInfo.PagingCookie = results.PagingCookie;
 }
 else
 {
 // If no more records are in the result nodes, exit the loop.
 break;
 }
 }
Create log in Plug-in using Custom Entity

Create log in Plug-in using Custom Entity

Sometime we want to know what is going on in our plugin code. When the plugin code is very large then we need to write the code for every step which is executed successfully or not.

For Debugging a Plugin we can –

  • Use ITracingService
  • Attach Plugin Registration Tool with VS and debug it.

For more info please refer the below links.

To review more options for external logging, you can also go through the below link.

Author by Salim Adamon

Error Logging Options in CRM

https://community.dynamics.com/crm/b/salimadamondynamicscrm/archive/2013/08/14/error-logging-options-in-crm

In this Blog, we’ll talk about creating log in plug-in using custom entity.

Let’s create an entity i.e. am_log in our CRM.

1

and create attributes which are required for this entity like

am_status ,  am_errordescription.

2

As we all know that creating a record in CRM is very easy but If you are writing logs in a failing transaction of Plugin, then the entire transaction will be rolled back including creation of logging records.

To avoid this roll back of logging records we can use ExecuteMultipleRequest.

For more info about ExecuteMultipleRequest refer to below link

https://msdn.microsoft.com/en-in/library/jj863631.aspx

In this process first we create a plugin, here named as LoggingUsingCustomEntity.

First we need to Create object for ExcuteMultipleRequest and assign settings that define execution behavior  i.e.

ContinueOnError =false,

ReturnResponse=true

ex

exmultiple

Lets say before creating a new account we need to count all Accounts(or any other business requirement) that already present in CRM that create success log in our Log Entity.

success

Now we write invalid expression for creating new account that generate error log in our Log entity.error

According above code log records will create two times,  one for getting all accounts successfully and other for error that is Account doesn’t contain “Name” attribute.

Let’s we deployed this plugin code on Account Create Message and see what will be generates in Log entity.

last.PNG

It created successfully what we expected 🙂

It won’t be created and also roll backed if we use normal create request here.

here is the link of  Plugin complete code.

http://1drv.ms/1MZBBzi

 

Thanks for visiting my blog.

Do some creative stuff in CRM  !!!  🙂

 

 

 

MS CRM dynamics with Firebase(using c#)

MS CRM dynamics with Firebase(using c#)

In my previous blog I wrote about how we can use firebase with CRM Dynamics in client side.
Here is the link MS CRM dynamics with Firebase (Using JS)

Today I am going to explain, how we can use Firebase with Plugin or Custom workflow in CRM using C#.

Let’s create a class library and add a class FirebaseWithCRM.cs

For communicate from CRM plugin to Firebase, we use FireSharp. It is a .net library for Firebase Rest Api.

c1

Next we add CRM core assemblies in our Class library project.

c2

We register this plugin on pre-operation event of Account Entity and on create Message ,so whenever new Account is create it ‘ll modify/add Account data in Firebase.

c3

Add new file as FirebaseHelper.cs that contain Push function

c4

c5

But our code is depends on some other DLL like Firesharp, Newtonsoft.Json , System.Net.Http , Microsoft.Threading.Task etc.

So Need to Merge all Dll’s into single one.

c6

What is ILMerge ?

ILMerge is a utility for merging multiple .NET assemblies into a single .NET assembly. It works on executables and DLLs alike and comes with several options for controlling the processing and format of the output.

For info refer this link

http://research.microsoft.com/en-us/people/mbarnett/ilmerge.aspx

 

For Merging Dll’s there is a Nuget Package MSBuild.ILMerge.Task so using this package we can merge all DLL’s.

You can also refer this article for more understanding about DLL’s Merge by Nicolas Niwonski.

 

c7

 

c8

After Merging we can deployed this DLL using Plugin Registration tool on pre-operation event for Account Entity and on Create Message.

You can also register on Post-operation event by this we get Guid of account entity and store it in Firebase.

c11.PNG

That’s it Now we can test. 🙂

c9

C10

Now when we create New account it created in Firebase under Accounts Node.

For more synchronous and asynchronous function for Firesharp, please refer below link

https://github.com/ziyasal/FireSharp/wiki/v1-Docs

 

 

Thanks for visiting my blog.

Do some creative stuff in CRM  !!!  🙂

 

Add Form Library on CRM Form using C# or javascript

Add Form Library on CRM Form using C# or javascript

Hello Everyone, Today I am going to tell how we can add form library on CRM form using C# or  JavaScript.

We want to add library on account form so for that I created one JavaScript web resources e.g.  am_sample for our demo purpose or you can add existing JS from CRM.

2

After that we add alert functionality inside this so we’ll found ourselves that our code is executed on onload of account form. We put our code inside self invoking function so it will show alert dialog on load of account form.(By using self invoking function we don’t need to define any function in Event Handlers)

here is demo link for self invoking function in JavaScript.

http://embed.plnkr.co/TSEL09/

1

3

In below screen we don’t see any library added on account form let’s write some code for adding file here 🙂

4

 

C# Code :

First we add one function for get form xml in which we passes logical name of Account entity as its parameter.

 

public Entity GetForm(string EntityLogicalname)
{
try
{
QueryExpression query = new QueryExpression(“systemform”)
{
Distinct = false,
ColumnSet = new ColumnSet(new[] { “name”, “formxml”, “objecttypecode” }),
Criteria =
{
Filters =
{
new FilterExpression
{
FilterOperator = LogicalOperator.And,
Conditions =
{
new ConditionExpression(“objecttypecode”, ConditionOperator.Equal, EntityLogicalname),
new ConditionExpression(“type”, ConditionOperator.Equal, 2),
new ConditionExpression(“iscustomizable”, ConditionOperator.Equal, true),
new ConditionExpression(“formactivationstate”, ConditionOperator.Equal, 1)
}
}
}
}
};
return Service.RetrieveMultiple(query).Entities.FirstOrDefault();
}
catch (Exception)
{
throw;
}
}

This function return Entity with formxml attribute.

After that we need to add one more function that change current formxml of Account entity (which return from above function) with new JS that we need to add.

Below is the description of what we’re going to perform in our next funtion :

  • First we select Form node, and formLibraries inside it.
  • If formLibraries not found then we’ll create formLibraries xml node.
  • If formLibraries found then we’ll create Library element inside it with name, libraryUniqueId attributes.
  • After that we append this Library tag into formLibraries and update formxml attribute of Entity.

 

public void AddLibrary(Entity form, string libraryName, bool addFirst)
{
var formXml = form.GetAttributeValue<string>(“formxml”);
var formDoc = new XmlDocument();
formDoc.LoadXml(formXml);
var formNode = formDoc.SelectSingleNode(“form”);
if (formNode == null)
{
throw new Exception(“Expected node \”formNode\” was not found”);
}
var formLibrariesNode = formNode.SelectSingleNode(“formLibraries”);
var eventLibrary = formNode.SelectSingleNode(“events”);
if (formLibrariesNode == null)
{
formLibrariesNode = formDoc.CreateElement(“formLibraries”);
formNode.AppendChild(formLibrariesNode);
}
if(libraryName!=null)
{
var libraryNode = formLibrariesNode.SelectSingleNode(string.Format(“Library[@name='{0}’]”, libraryName));
if (libraryNode != null)
{
Console.WriteLine(“This library is already included in this form”);
}
var nameAttribute = formDoc.CreateAttribute(“name”);
var libraryUniqueIdAttribute = formDoc.CreateAttribute(“libraryUniqueId”);
nameAttribute.Value = libraryName;
libraryUniqueIdAttribute.Value = Guid.NewGuid().ToString(“B”);
libraryNode = formDoc.CreateElement(“Library”);
if (libraryNode.Attributes != null)
{
libraryNode.Attributes.Append(nameAttribute);
libraryNode.Attributes.Append(libraryUniqueIdAttribute);
}
if (formLibrariesNode.ChildNodes.Count > 0 && addFirst)
{
formLibrariesNode.InsertBefore(libraryNode, formLibrariesNode.FirstChild);
}
else
{
formLibrariesNode.AppendChild(libraryNode);
}
}
form[“formxml”] = formDoc.OuterXml;
Service.Update(form);
}

 

After that we’ll publish Account Entity.

public void PublishForm(Entity en)
{
var request = new PublishXmlRequest { ParameterXml = String.Format(“<importexportxml><entities><entity>{0}</entity></entities></importexportxml>”, en.GetAttributeValue<string>(“objecttypecode”)) };
Service.Execute(request);
}

 

Here is calling code

CrmConnection con = CrmConnection.Parse(“Url = https://xxxx.crm5.dynamics.com; Username=xyz@abc.onmicrosoft.com; Password=xyzxyz”);
IOrganizationService Service = new OrganizationService(con);
ScriptManage sc = new ScriptManage(Service);
var EntityName = sc.GetForm(“account”);
sc.AddLibrary(EntityName,”am_sample”, false);
sc.PublishForm(EntityName);

Now you can see JS file is added on account form!! 🙂

For more info about C# code, go through below link.

https://github.com/MscrmTools/XrmToolBox/blob/master/Plugins/MsCrmTools.FormRelated/FormLibrariesManager/AppCode/FormManager.cs

 

 

 

5

8

JS Code :

For using below code we need to add these file references.

  1. ClientGlobalContext.js.aspx
  2. jquery.min.js
  3. Sdk.Soap.min.js
function Getformxml  (EntityLogicalname) {
Sdk.jQ.setJQueryVariable($);
var SystemForm = new Sdk.Query.QueryExpression(“systemform”);
SystemForm.getDistinct();
SystemForm.addColumn(“name”);
SystemForm.addColumn(“formxml”);
SystemForm.addColumn(“objecttypecode”);
Sdk.Query.FilterExpression(Sdk.Query.LogicalOperator.And);
SystemForm.addCondition(“systemform”, “objecttypecode”, Sdk.Query.ConditionOperator.Equal, new Sdk.Query.Strings([EntityLogicalname]))
SystemForm.addCondition(“systemform”, “type”, Sdk.Query.ConditionOperator.Equal, new Sdk.Query.Ints([2]))
SystemForm.addCondition(“systemform”, “iscustomizable”, Sdk.Query.ConditionOperator.Equal, new Sdk.Query.Booleans([true]))
SystemForm.addCondition(“systemform”, “formactivationstate”, Sdk.Query.ConditionOperator.Equal, new Sdk.Query.Ints([1]))
FormxmlQuery = SystemForm;
return Sdk.jQ.retrieveMultiple(SystemForm).then(
function (ec) {
formRequestSuccesscall(ec);
})
new ErrorHandler(“An error occured “).getError(err);
}
function formRequestSuccesscall  (data) {
var $xml, $formtagxml, x = true, xml2 = ”;
if (data.getEntities().getCount() > 0) {
data.getEntities().forEach(function (n, i, arr) {
if (n.view().attributes[‘formxml’])
$xml = $($.parseXML(n.view().attributes[‘formxml’].value));
$formtagxml = $xml.find(“formLibraries”);
$($formtagxml).find(‘Library’).each(function () {
$.each(this.attributes, function (i, attrib) {
if (attrib.name == “name”) {
if (attrib.value == “Content_/scripts/wfHover.js”) {
console.log(‘already add on form!!’);
x = false;
}
}
});
});
if (x) {
if ($formtagxml.length == 0) {
xml2 += ‘<formLibraries><Library name=“am_sample” libraryUniqueId=”‘ + guid() + ‘” /></formLibraries>’;
$formtagxml = $($.parseXML(xml2)).find(“formLibraries”);
$xml.find(“form”).append($($.parseXML(xml2)).find(“formLibraries”));
} else {
$formtagxml.append(‘<Library name=“am_sample” libraryUniqueId=”‘ + guid() + ‘” />’)
}
var oSerializer = new XMLSerializer();
//clone.attributes[‘formxml’].value = oSerializer.serializeToString($xml[0]);
n.setValue(“formxml”, oSerializer.serializeToString($xml[0]));
Sdk.jQ.update(n).then(
function (id) {
// console.log(id);
publishXML(entityLogicalname);
})
}
})
}
}
function publishXML (entityName) {
var context = Xrm.Page.context;
var serverUrl = (context.getServerUrl) ? context.getServerUrl() : context.getClientUrl();
var Publishxml = “”;
Publishxml += “<s:Envelope xmlns:s=\”http:\/\/schemas.xmlsoap.org\/soap\/envelope\/\”>”;
Publishxml += ” <s:Body>”;
Publishxml += ” <Execute xmlns=\”http:\/\/schemas.microsoft.com\/xrm\/2011\/Contracts\/Services\” xmlns:i=\”http:\/\/www.w3.org\/2001\/XMLSchema-instance\”>”;
Publishxml += ” <request i:type=\”b:PublishXmlRequest\” xmlns:a=\”http:\/\/schemas.microsoft.com\/xrm\/2011\/Contracts\” xmlns:b=\”http:\/\/schemas.microsoft.com\/crm\/2011\/Contracts\”>”;
Publishxml += ” <a:Parameters xmlns:c=\”http:\/\/schemas.datacontract.org\/2004\/07\/System.Collections.Generic\”>”;
Publishxml += ” <a:KeyValuePairOfstringanyType>”;
Publishxml += ” <c:key>ParameterXml<\/c:key>”;
Publishxml += ” <c:value i:type=\”d:string\” xmlns:d=\”http:\/\/www.w3.org\/2001\/XMLSchema\”>&lt;importexportxml&gt;&lt;entities&gt;&lt;entity&gt;” + entityName + “&lt;\/entity&gt;&lt;\/entities&gt;&lt;\/importexportxml&gt;<\/c:value>”;
Publishxml += ” <\/a:KeyValuePairOfstringanyType>”;
Publishxml += ” <\/a:Parameters>”;
Publishxml += ” <a:RequestId i:nil=\”true\” \/>”;
Publishxml += ” <a:RequestName>PublishXml<\/a:RequestName>”;
Publishxml += ” <\/request>”;
Publishxml += ” <\/Execute>”;
Publishxml += ” <\/s:Body>”;
Publishxml += “<\/s:Envelope>”;
var req = new XMLHttpRequest();
req.open(“POST”, serverUrl + “/XRMServices/2011/Organization.svc/web”, true);
// Responses will return XML. It isn’t possible to return JSON.
req.setRequestHeader(“Accept”, “application/xml, text/xml, */*”);
req.setRequestHeader(“Content-Type”, “text/xml; charset=utf-8”);
req.setRequestHeader(“SOAPAction”, “http://schemas.microsoft.com/xrm/2011/Contracts/Services/IOrganizationService/Execute&#8221;);
req.onreadystatechange = function () {
if (req.readyState === 4) {
if (req.status === 200) {
}
} else {
alert(“An error occurred ” + req.statusText);
}
}
req.send(Publishxml);
}

here is caling code for JS

Getformxml  (“account”);

 

Hope you will like this blog and Thank you for visiting my blog 🙂