Thursday, March 31, 2011

Getting started with writing JScript in MS CRM 2011: Step by Step

We are just in the process on migrating to 2011 and I was just trying to write some JScripts for testing purposes. However, I found that the way of writing JScript in 2011 has changed a bit and after some trying, the below are the steps that I used to write my JScripts:

1. I created a simple JScript function as shown below in Visual Studio:
function welcome() {

    alert("Welcome to CRM 2011");
}
BTW, it looks like all the JScript in 2011 can only be accessed using functions.
2. Save this file as TestWebResource.js
3. Open CRM and go to your custom entity's customization and click on Form Properties
4. Under Form Libraries -->Click on "Add"
5. In the Look Up Record, click on the "New" Button
6. In the "Web Resource: New" window, fill the mandatory fields and then click on "Browse" -->select the JScript file that we  had saved earlier and open. Save this file(I also published it).
7. Select OK in the "Look Up Record" window
8. You should be back at the main "Form Properties" window as shown below:

9. Now, click the "Add" button under Event Handlers for the onLoad event.
10. The library will default to our jscript file. Type "welcome" as the function name and click on OK
11. Save and Publish.
12. Click on Preview-->Create Form
13. You should see the alert being displayed:

Thanks!

Friday, March 18, 2011

Error while Importing a solution in CRM 2011 - The solution file is invalid. The compressed file must contain the following files at its root: solution.xml, customizations.xml, and [Content_Types].xml

I was trying to add a button to the ribbon but I was not able to test it as I would keep getting the below error while trying to import the solution zip files:

























After spending a huge amount of time checking and rechecking my code, I was finally sure that the issue had to be somewhere else.
Finally, it turned out that when I used to zip the folder using the built in windows compression(zip), it would create a folder inside the zip file and place the solution files inside this folder.

So, as the error above states, CRM is looking for those files in the "Root" but was actually finding a folder in it's place and hence throwing the error. When I selected the solution files and zippped them directly, the import was successful. I know this might be a small thing but it definitely cost me an awful lot of time. :)

Thursday, March 17, 2011

Wednesday, March 16, 2011

Invalid column name 'DeletionStateCode' CRM 2011 upgrade

I was getting the error "Invalid column name 'DeletionStateCode'" when I was trying to upgrade from 4.0 to 2011. After a lot of trials and errors I got to know that there were many stored procedures and triggers in the database which were referencing to this column. 


Since the 'DeletionStateCode' has been dropped in 2011, maybe it was erroring out while trying to upgrade them.


Solution: I deleted all the triggers and stored procs from the database as it was just a test environment and I was able to install 2011 sucessfully.

Bulk Delete/Drop Stored Procedures from Database

I had to recently clean up a database and get rid of all the custom stored procedures in it. Unfortunately, there were more than a 100 SP's in the database and it was not possible to delete them one by one. Fortunately, I found a great line of code here (http://forums.asp.net/t/1340617.aspx) that will give me a list of all the stored procedures with the 'Drop' command. I just had to copy the results from the below query, paste and execute them in the query editor window.

Select 'Drop Procedure ' + name from sys.procedures Where [type] = 'P' and is_ms_shipped = 0 and [name] not like 'sp[_]%diagram%' and name like 'usp_%'

Getting the CRM 4.0 License Key from the Database

Hi,

I found the below query that can help us to retrieve the CRM 4.0 license key from the database. Run it against the MSCRM_Config DB:
SELECT LicenseKey FROM ConfigSettings

Update
The above code does not work for CRM 2013. Please find the updated query to find the license key specific to an Organization in CRM 2013
SELECT B.NVarCharColumn 
FROM [dbo].[ConfigSettings] A , [dbo].[ConfigSettingsProperties] B
Where A.Id=b.Id
And B.ColumnName='LicenseKeyV6RTM'

Wednesday, March 9, 2011

Getting clean HTML from a Word document using Gmail

Recently, I had to convert a word document into an HTML file. First, like everyone else, I tried to directly save the word file as HTML web page from word.
This saved the file as HTML alright, but when I went to view the HTML source, there were just too many unnecessary tags and the HTML looked almost illegible. So I had to find other means to get this done.

After some search on the Internet, I got to know the following method which helped me get clean HTML out of my word document.
1. Send the word document as an attachment to your Gmail account.
2. Log into Gmail.
3. Open the email with this attachment and click on the "View" next to the attachment.
4. This will open up a new window/tab where Gmail tries to read your word document.
5. On this page click on View->Plain HTML
6. Now you should see the same data without all the formatting as just plain text.
7. Right Click on this Page->View Source to get the HTML portion of this page.
8. The HTML you have right now will be very clean and you can easily work with it from here on.

That's it!


Using CRM Attribute Types

The following are the default values to be set for the different attribute types as defined in the SDK.
// Set up the CRM Service.
CrmAuthenticationToken token = new CrmAuthenticationToken();
token.AuthenticationType = 0; 
token.OrganizationName = "AdventureWorksCycle";
 
CrmService service = new CrmService();
service.Url = "http://crm:5555/mscrmservices/2007/crmservice.asmx";
service.CrmAuthenticationTokenValue = token;
service.Credentials = System.Net.CredentialCache.DefaultCredentials;

CrmBoolean boolean = new CrmBoolean();
boolean.Value = true;

CrmDateTime dateTime = new CrmDateTime();
dateTime.Value = "2006/5/27T17:00:00";

CrmDecimal dec = new CrmDecimal();
dec.Value = (decimal)10.1;

CrmFloat f = new CrmFloat();
f.Value = (float)10.1;

CrmMoney money = new CrmMoney();
money.Value = (decimal)10.00;

CrmNumber number = new CrmNumber();
number.Value = 10;

Lookup lookup = new Lookup();
lookup.Value = user.UserId;
lookup.type = EntityName.systemuser.ToString();

Owner owner = new Owner();
owner.type = EntityName.systemuser.ToString();
owner.Value = user.UserId;

Picklist list = new Picklist();
list.Value = 1;

Status status = new Status();
status.Value = 1;

EntityNameReference reference = new EntityNameReference();
reference.Value = EntityName.systemuser.ToString();

Key key = new Key();
key.Value = user.UserId;

Friday, March 4, 2011

PostCreate/UpdatePlugin

Here, when a custom entity is created or updated, I am updating a varchar field in the related contact entity
public void Execute(IPluginExecutionContext context)
        {
            DynamicEntity entity=null;

            //Check if the input parameters contains a target of type Dynamic Entity
            if (context.InputParameters.Properties.Contains("Target") &&
                context.InputParameters.Properties["Target"] is DynamicEntity)
            {
                //obtain the entity from the input parameters
                entity = (DynamicEntity)context.InputParameters.Properties["Target"];

                //TextWriter log1 = TextWriter.Synchronized(File.AppendText(@"D:\Srihari Backup\Log.txt"));
                if (entity.Name == "new_testEntity")
                {
                    
                    try
                    {
                        //log1.WriteLine("inside try");                       

                        
                        DynamicEntity testEntity= (DynamicEntity)context.InputParameters[ParameterName.Target];
                        string clinicName = String.Empty;
                        string contactId= String.Empty;

                        if (testEntity.Properties.Contains("new_clinicname"))
                        {
                            clinicName = (string)testEntity["new_clinicname"];
                            //log1.WriteLine(clinicName);
                        }
                        if (context.MessageName == MessageName.Create)
                        {
                            if (testEntity.Properties.Contains("new_contactid"))
                            {
                                //log1.WriteLine("contains patient id");                                

              //Since the contact is a lookup, cast the object as a lookup.
                                //log1.WriteLine("create message :-");
                                Lookup contactLookup = (Lookup)testEntity["new_contactid"];
                                contactLookup.type = EntityName.contact.ToString();
                                contactId= contactLookup.Value.ToString(); //get the guid of the lookup   
                            }                   
                           
                            
                        }
                        else if (context.MessageName == MessageName.Update)
                        {
                            //log1.WriteLine("update message :-");
//Since this is an update, only the values updated on the form will be available in the context. Hence I am getting the value from an Image that I have registered.
                            DynamicEntity postTestEntity = (DynamicEntity)context.PostEntityImages["contactId"];//The name or 'entityalias' of the image
                            //log1.WriteLine(postCCProvider);
                            Lookup contactLookup = (Lookup)postTestEntity.Properties["new_contactid"];
                            //log1.WriteLine(patientLookup);
                            contactId = contactLookup.Value.ToString();
                            //log1.WriteLine(patientId);

                            //log1.Close();
                        }
                        
                        DynamicEntity contact = new DynamicEntity();
                        contact.Name = EntityName.contact.ToString();

                        Key contactKey = new Key();
                        contactKey.Value = new Guid(contactId);

                        KeyProperty contactGuid = new KeyProperty();
                        contactGuid.Name = "contactid";                 
                        contactGuid.Value = contactKey;
                        contact.Properties.Add(contactGuid);

                        //set the field that needs to be updated in the contact entity.
                        StringProperty ccProviderNameProperty = new StringProperty("new_communitycareprovider", clinicName);
                        contact.Properties.Add(ccProviderNameProperty);

                        ICrmService service = context.CreateCrmService(true);
                        service.Update(contact);


                        
                    }
                    catch (SoapException soapEx)
                    {
                        //log1.Close();
                        throw new InvalidPluginExecutionException("Soap error occured in the plugin", soapEx);                        
                        
                    }
                    catch (Exception ex)
                    {
                        //log1.WriteLine(ex.Message);
                        //log1.Close();
                        throw new InvalidPluginExecutionException("An error occured in the plugin", ex);
                    }
                }
            }
            else
            {
                return; 
            }

           
        }

Creating a document library in Sharepoint through code.


Recently there was a requirement to create a document library in Sharepoint from an ASP.NET page. I used the following code to achive this:
1. Add a reference to the sharepoint list service web service.
2. I then used the List webservice to create the document library using the code below
Web_Reference_Folder.Lists listService = new Web_Reference_Folder.Lists();
listService.Credentials = new System.Net.NetworkCredential("username", "password");
XmlNode ndList = listService.AddList("document library name", "Type any description for your document library", 101);
"101" in the above code is used to create a document library.
Thats it! :)
 –Srihari.

Searching for a file/doc in a document library in Sharepoint


//Call my document library’s listservice webservice 
Sharepoint_WebService.Lists listService = new Sharepoint_WebService.Lists();
                            listService.Credentials = new System.Net.NetworkCredential("username", "password");
                            //get the entire list items from the specified document library
                            XmlNode ndList = listService.GetListItems(accountName, null, null, null, null, null, null);
                            XmlNamespaceManager nsMgr = new XmlNamespaceManager(ndList.OwnerDocument.NameTable);
                            nsMgr.AddNamespace("rs"”, "urn:schemas-microsoft-com:rowset");
                            nsMgr.AddNamespace("z", "#RowsetSchema");
                            //if the document library is empty
                            if (ndList != null)
                            {
                                XmlNode dataNode = ndList.SelectSingleNode("rs:data", nsMgr);
                                int itemCount = Convert.ToInt32(dataNode.Attributes.GetNamedItem("ItemCount").Value);
                                if (itemCount == 0)
                                {
                                    MessageBox.Show("No file to search");                               
                               }
                                else
                                {
                                    //get the entire items in the document library
                                    foreach (XmlNode row in dataNode.SelectNodes("z:row", nsMgr))
                                    {
                                        //if one of the item equals "Group Verification.docx"
                                        if ((row.Attributes.GetNamedItem("ows_LinkFilename").Value).ToString() == "Group Verification.docx")
                                        {
                                            MessageBox.Show("File Found");
                                        }
                                            //no verification document has been uploaded
                                        else
                                        {
                                            MessageBox.Show("File not found");                                       
                                        }
                                    }
                                }
                            }

Points about Pre-update plugin


1. A pre-update plugin can be used to update the field values of the same record/entity.
2. Only the values that we are changing in the entity form will be available in the entity context.
3. If we want to access the values of fields that we are not going to update, then we need to register the image for that entity and include the attributes whose value we need.
4. Finally, we need to directly update the entity context for the changes to get reflected.
Note: We cannot do a service.update(entity) as this will cause the code to go in a circular loop and finally the plugin will throw an error and the changes will not get saved!
Code Sample:
//Updating a boolean value to true or false using Pre-update
// Obtain the target business entity from the input parameters.
entity = (DynamicEntity)context.InputParameters.Properties["Target"];
//Instantiating the boolean property and passing the boolen field name and value to the property
CrmBooleanProperty boolProp = new CrmBooleanProperty("new_verified", new CrmBoolean(false));
//Adding the Property to the entity properties
entity.Properties.Add(boolProp);
-Srihari.

Determining if an Account is a part of a DitributeCampaignActivity


Scenario: Given an account GUID, I should be able to determine if the account is a part of a  "Distribute  Campaign Activity"
The functional flow of the data would be CampaignCampaign ActivitiesCampaign Activity ItemTarget Marketing ListsMarketing List MembersAccounts
 I was trying to look into the database for my answers and before reaching the solution, the following are my findings:
1.       List and ListMember are the tables that contain the data about a marketing list
2.       Similarly, Campaign and CampaignActivity tables contain information about the Campaign records.
3.       Intersect Tables: When there is a many-to-many relationship between two entities, an intersect table is created. This is true for both system relationships built in to the product as well as custom many-to-many relationships. This is how we used to create M:M relationships in CRM 3.0 but it’s implemented OOB in CRM 4.0(See “Using Intersect Tables” in SDK for more info)
4.       The Intersect Table between List and CampaignActivity isCampaignActivityItem.
5.       There is also a ‘DistributeCampaignActivity’ message using which we can distribute a campaign programmatically. However, we cannot register a plugin against this message as it’s not supported.
6.       So, although I was getting all the info individually, I was still not able to determine if an account was a part of a DistributeCampaignActivity.
7.       Then I found that all the plugin registration messages are stored in theSdkMessageBase and the SdkMessageFilterbase tables and there was a‘DistributeCampaignActivity’ message in the table but it was not exposed.
8.       So, I used the below query  in order to expose the‘DistributeCampaignActivity’ message in the plugin registration tool.(Unsupported)

update sdkmessagefilterbase
set isCustomProcessingStepAllowed=1
where sdkmessageid=(select sdkmessageid from sdkmessagebase where name='DistributeCampaignActivity')
9.       I then registered a post-update plugin with message name‘DistributeCampaignActivity’ and Primary Entity: CampaignActivity10.   This time, when I clicked on the ‘DistributeCampaignActivity’ button for a Campaign, I was able to debug the plugin and find out that it was creating a record of type BulkOperationID11.   Naturally, I checked and found that there were 2 tables:  BulkOperation andBulkOperationLog and an entry was made into these tables ONLY when the record was of type “Distribute” or “QuickCampaign“.
12.   Finally, I was able to arrive at the below query which tells me if an account is a part of a ‘DistributeCampaignActivity’ or a QuickCampaign created in the last 7 days based on the account GUID: 
select COUNT(1) 
from FilteredBulkOperation FBO, FilteredBulkOperationLog FLOG 
where FBO.activityid=FLOG.bulkoperationid 
and FBO.actualend >= GETDATE()-7 and actualend<= GETDATE()
and FLOG.regardingobjectid ='20C7A5F8-AD02-DE11-83DE-0003FFE51F61'  /*–The Account GUID–*/
Thats it! :)

Calling an External JS File From a CRM Form

Adding bookmarks to your CRM form.


In my current implementation, instead of adding many tabs to the CRM form, we ended up adding multiple sections on one tab.
The end result was that the form became extremely long and it was getting only longer. So, in order to enhance the user experience, there was a need to add a bookmark style facility to the page.
After a lot of thought I went ahead and asked for some ideas in the CRM newsgroups. I got some great ideas from the community and it turned out that implementing this functionality was simpler than I had imagined.
So, I created one HTML page with links to the various sections(section1, section 2 etc.). When the user clicks on any section link, I will simply set the focus to the first field on that section, there by redirecting the user to that section.
Simple, isn’t it? :-)

Simple Filtered Lookup based on Picklist Values


I had a parent entity containing records classified into 2 different disease categories ('Diabetes' and 'Cholestrol').
In my related entity, there was a picklist which had the above 2 disease categories. The criteria was that when the user selected a particular disease cateogry in the picklist, the parent entity lookup should show as a pop-up showing only the corresponding disease category records.
Ex: If the user selects ‘Diabetes’ in the picklist, then the lookup should pop-up showing only records of type ‘Diabetes’. After some searching, I found this blog:http://advantageworks.blogspot.com/2008/02/pseudo-filtered-lookup-dialog-in.html, which was describing a similar scenario. Here is how I implemented this:
1. I went into customizations and opened my parent entity.
2. Then I clicked on “Forms and views” and opened the lookup view.
3. Then I clicked on “Add Find Columns” and selected “Disease Category” column.
4. Saved and Published the entity
5. Next, I opened my related entity’s form.
6. In the onchange event of my picklist, I wrote the following code:

if(crmForm.all.new_Picklist.SelectedText=="Diabetes")
{
var criteria= 'Diabetes';
crmForm.all.new_parentLookupId.additionalparams =  'search=' + criteria;
crmForm.all.new_parentLookupId.click(); // Launch the parent lookup as a popup.
} 
7. Save and publish the related entity. So, basically this is the same as opening the lookup and typing ‘Diabetes’ in the search box of the lookup. It’s not filtered in a strict sense.
–Srihari.

Accessing ASP.NET controls within an IFRAME in CRM 4.0


There was a requirement to disable an drop-down list which was in an IFRAME in a CRM form.
The below code allowed me to access the value of the ASP.NET drop-down list:
document.frames.IFRAME_Test.document.all.DropDownList1.value;
and the below code allowed me to disable it:
document.frames.IFRAME_Test.document.all.DropDownList1.disabled= true;

Adding Syntax Highlighting to Blogger

A very detailed and great explanation has been provided by Matt Boll at his blog:
http://heisencoder.net/2009/01/adding-syntax-highlighting-to-blogger.html

Failure: contact_activity_parties: Cascade link type ‘NoCascade’ is invalid for Delete


I was trying to import a contact entity into one of my CRM instances today and I was getting the strange error “Failure: contact_activity_parties: Cascade link type ‘NoCascade’ is invalid for Delete”.
Luckily I managed to find this thread http://social.microsoft.com/Forums/en/crmdeployment/thread/c2292223-6d76-4c77-919a-99effa077bc7 where one of the users was having the same problem. I tried out one of the suggestions posted by the user.
Apparently this is caused because of some issues with the relationship structure in the customization file. So, I opened the customiation.xml file and I searched for “contact_activity_parties”.
Sure enough, there was a relationship defined

 
OneToMany 
ActivityParty 
Contact 
partyid 
 
 
 
 
 
 
 
 
 
 

I commented the above section in my xml file and tried to import again. This time the import was successful.
 –Srihari.