By shannon on April 15, 2011

I was trying to create a new CRM user in CRM 4.0 for an Active Directory service account that had been setup for me by one of our network admins. For the purpose of this it was similar to trying to use “ReallyLongCRMUserName”.

I went through the same process I have done many times before. I was used to just entering the domain name and having CRM populate the other required fields. However, when I tried to setup this user, it didn’t auto populate. It was like the below:

Create User Pic 2

So I went ahead and filled in the First and Last Name fields, and when I tried to save the user, I got the following error message:

ErrorMessage pic 1

This was really odd, so I first made sure that I was spelling the name correctly, or at least that I was matching what was setup in the Active Directory account properties. This misspelled name had been an issue on a previous user, so it was my first thought. That wasn’t the issue.

Next, I worked with the network administrator and went through all the properties on the affected account, comparing it to another similar account that worked fine. We didn’t find anything setup differently.

Finally, I turned to internet searches, and found lots of responses, but they all seemed to indicate that this error was caused by doing cross domain lookups or similar setups. That wasn’t the case here.

It turned out the solution was that CRM is looking up the user in Active Directory based on the Pre-Windows 2000 account name.

AD User Properties Pic 3

As indicated above, the Pre-Windows 2000 logon name was missing the final “e” from the Logon name.

So the simple solution was to use the Pre-Windows 2000 Logon name, in this case “ReallyLongCRMUserNam”.  Seems like a really simple solution, and many times it is the simple ones that can eat so much time trying to troubleshoot them.

By shannon on November 24, 2010

We were trying to install the CRM 4.0 Data Connector for SSRS when a named instance of Reporting Services is installed on a separate machine from the SQL Databases.

Trying to install the Data Connector on the Server that has Reporting Services 2008 installed, we would receive the following error: Unable to validate SQL Server Reporting Services Report Server installation. We first tried looking at permissions, and that didn’t change anything. This included running it as a domain admin.

To fix the issue, we had to copy the install directory to the local drive on the server that we were going to install it on. The files should be located in the SrsDataConnector directory on the installation media. Once extracted, navigate to the directory and open the following file for edit: Install-config.xml.

The file should look like the following before you edit it:

<crmsetup>
  <srsdataconnector>
    <configdbserver></configdbserver>
    <autoupdateconfigdb>1</autoupdateconfigdb>
    <autogroupmanagementoff>0</autogroupmanagementoff>
    <instancename>MSSQLSERVER</instancename>
    <configsku>OnPremise</configsku>
    <!-- Set enabled = true for DB webstore integration.  Set configdb="true" for config db webstore integration-->
    <webstore enabled="false" configdb="false" />
    <monitoring>
<!-- Monitoring service account name and password. It can not be local system or network service account -->
      <serviceaccountname></serviceaccountname>
      <serviceaccountpassword></serviceaccountpassword>
    </monitoring>
  </srsdataconnector>
</crmsetup>

 

To get it to work, we need to indicate the Report Server URL and the Instance Name.  To do that, add the needed <reportserverurl> element.  So in our case, the Report Server URL was http://servername/ReportServer_Standard, since we had more than one Reporting Services instance installed on this server.  Then change the < instancename> element to point to your installed instance, in our case, STANDARD.  We edited the file to look like the below. 

<crmsetup>
  <srsdataconnector>
    <configdbserver></configdbserver>
    <reportserverurl>http://servername/ReportServer_STANDARD</reportserverurl>
    <autoupdateconfigdb>1</autoupdateconfigdb>
    <autogroupmanagementoff>0</autogroupmanagementoff>
    <instancename>STANDARD</instancename>
    <configsku>OnPremise</configsku>
    <!-- Set enabled = true for DB webstore integration.  Set configdb="true" for config db webstore integration-->
    <webstore enabled="false" configdb="false" />
    <monitoring>
<!-- Monitoring service account name and password. It can not be local system or network service account -->
      <serviceaccountname></serviceaccountname>
      <serviceaccountpassword></serviceaccountpassword>
    </monitoring>
  </srsdataconnector>
</crmsetup>

Then once this file is saved, you simply launch the setup as follows: SetupSrsDataConnector /CONFIG extracted media path\install-config.xml.

This should run the install, using your modified configuration file, and allow you to get the Data Connector installed.

At this point, your install process may be done. We received another error once we tried to actually run a report in CRM. It no longer complained about the Data Connector needing to be installed. It now gave us the generic “The report cannot be displayed” error message.

Once again, because of the separated setup, with Reporting Services and SQL Database on different servers, we had to track down a final permission issue. We needed to make sure that the Log On account that Reporting Services is running as, has the needed permission to run our reports.

To do this, find the account that Reporting Services is using to Log On:

LogOn

In our case, it was Network Service. We needed to add this to the following Local Group on the Reporting Services server. Find the local group for your named instance, which should be something similar to the following: SQLServerReportServerUser$servername$InstanceName.

Once I added the Network Service account to the above group, I was able to run reports from CRM 4.0.

By shannon on October 27, 2010

If you would like to programmatically create a task in CRM, and then route that to a Queue.  This is a pretty common need, but to do so, it isn’t very straight-forward.

1.) Create the task, and retrieve the GUID taskId. 

2.) Use the GUID from above, to assign the task to a specific user in CRM.

3.) Route the Task to the Queue you want to have the task go in to.

It is the 3rd step which is not necessarily very obvious how it supposed to work.  Below it shows you how to achieve this.  I had thought that it should be as simple as knowing the TaskId and the QueueId that I want the task to be placed in. 

CrmServiceUtility utility = null;
 
try
{
    utility = new CrmServiceUtility();
 
    if (utility.CrmService != null)
    {
        // This line is the Key to making it work
        queue wipQueue = QueueManager.GetTaskUserWIPQueue(owningUserId);
        
        // Route the incident from the WIP queue to the public queue.  
        TargetQueuedTask target = new TargetQueuedTask();
        target.EntityId = taskId;
 
        // Setup our Route Request
        RouteRequest route = new RouteRequest();
        route.Target = target;
        route.RouteType = RouteType.Queue; 
        route.EndpointId = queueId;
        // We need to know which Queue the Task currently resides in, this is Key
        route.SourceQueueId = wipQueue.queueid.Value;
 
        RouteResponse routed = null;
 
        // Execute our Route Request
        routed = utility.CrmService.Execute(route) as RouteResponse;
 
    }
}
catch (Exception ex)
{
    ExceptionHelper.HandleException(ex);
}
finally
{
    if (utility != null)
    {
        utility.Dispose();
    }
}

The GetTaskWIPQueue function is below:

public static queue GetTaskUserWIPQueue(Guid owningUserId)
{
    CrmServiceUtility utility = null;
    queue wipQueue = null;
 
    try
    {
        utility = new CrmServiceUtility();
        // Find the WIP queue for the user who currently owns the task.  
        // The queue type code for a WIP queue is 3.  
        QueryByAttribute query = new QueryByAttribute();
        query.ColumnSet = new AllColumns();
        query.EntityName = EntityName.queue.ToString();
        query.Attributes = new string[] { "primaryuserid", "queuetypecode" };
        query.Values = new string[] { owningUserId.ToString(), "3" };
 
        CrmService search = new CrmService();
 
        BusinessEntityCollection results = utility.CrmService.RetrieveMultiple(query);
 
        wipQueue = (queue)results.BusinessEntities[0];
 
        
    }
    catch (Exception ex)
    {
        ExceptionHelper.HandleException(ex);
    }
    finally
    {
        if (utility != null)
        {
            utility.Dispose();
        }
    }
    return wipQueue;
}

So the key is that you need to know which queue the task currently resides in.  For newly created tasks, this is the Work In Progress queue for the user the task is assigned to. 

This just seems like it was a step that shouldn’t have been necessary, and results in additional lookups having to be performed.

By shannon on September 14, 2010

I recently was asked if it was possible to have a static link added to data entry form for an entity in CRM.  Since this link was to be used for a kind of help/wiki link, and was going to be a static value, I didn’t feel like adding it to the entity was the best way to go.  I figured since the form is an HTML page it would be possible to add it as just an html element.  I was partially correct. 

What I did was add a field to the form, that was in the position that I wanted the link.  I used a field that is not being used on our form, and is one of the standard CRM fields.  You could use any field, even a 1 character field created for this reason if needed.  The only requirement is that it can’t be a field that you want to use on your form normally, since you can’t have the same field on a form multiple times in CRM.

So my form looked kind of like the following.

FieldToReplace

This was at the top of my form, where a Help Link would normally be expected to reside.  The next step is to put the following code into the OnLoad event for your form.

function InsertMainHelpLink()
{
  // using this field as a placeholder
  var fieldTable = crmForm.all.createdon_d;  
 
  var html = "<table border='0' cellspacing='0' cellpadding='0' width='100%'><tr><td width='0px'>&nbsp;</td><td width='100%' align='right'><a href='" + MAIN_HELP_LINK + "' target='new'>Help Link</a></td></tr></table>";  
  fieldTable.innerHTML = html;  
 
  //hide the createdon label
  crmForm.all.createdon_c.innerText=""; 
}

Then simply call the above method in at some point during the load of your form as follows: InsertMainHelpLink();

This will replace the control added previously and leave you with a link to whatever location you specified.  It will look something like the following.

FieldReplaced

You can apply whatever styling and change the location URL as needed.

By shannon on July 08, 2010

We recently had a case on a new ASP.Net web application where we needed to add a value to the Handler Mapping in IIS. This was to enable the use of local SQL Server Reporting functionality in the MVC 2.0 web application.

When the mapping was originally configured, it was added with a typo. This was easy enough to fix and everything seemed to work well. Eventually the reporting stopped working, and after troubleshooting the issue, it was discovered that the Handler Mapping was once again set back to the version with the typo.

First thoughts were that there was something in the build/deployment process that was setting this, a script or something. After walking through the deployment process step by step, it was determined that just restarting IIS was resetting the Handler Mapping to the version with the typo in it.

This seemed really odd, and short of deleting the web application and starting from scratch, hoping that this issue would be resolved, I was looking for a less destructive fix.

It turned out to be fairly simple. With a couple adjustments to the web.config for the application, the issue has been fixed.

We had originally created a Handler Mapping entry similar to the following:

Path:
Reserved.ReportViewerWebControl.axd

Type:
Microsoft.Reporting.Microsoft.Reporting.WebForms.HttpHandler, Microsoft.ReportViewer.WebForms, Version=9.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a

Name:
Reserved-ReportViewerWebControl-axd

What we wanted was actually:

Path:
Reserved.ReportViewerWebControl.axd

Type:
Microsoft.Reporting.WebForms.HttpHandler, Microsoft.ReportViewer.WebForms, Version=9.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a

Name:
Reserved-ReportViewerWebControl-axd

To fix this issue we added the following 2 lines to the web.config:

<remove name="Reserved-ReportViewerWebControl-axd" />

and

<add name="Reserved-ReportViewerWebControl-axd" path="Reserved.ReportViewerWebControl.axd" verb="*" type="Microsoft.Reporting.WebForms.HttpHandler, Microsoft.ReportViewer.WebForms, Version=9.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" resourceType="Unspecified" requireAccess="Script" preCondition="integratedMode" />

These lines went into the section shown below making the section look similar to the following:

<system.webServer>
	<handlers>
		<remove name="Reserved-ReportViewerWebControl-axd" />
		<remove name="WebServiceHandlerFactory-Integrated"/>
		<remove name="ScriptHandlerFactory"/>
		<remove name="ScriptHandlerFactoryAppServices"/>
		<remove name="ScriptResource"/>
		<remove name="MvcHttpHandler"/>
		<remove name="UrlRoutingHandler"/>
		<add name="ScriptHandlerFactory" verb="*" path="*.asmx" preCondition="integratedMode" type="System.Web.Script.Services.ScriptHandlerFactory, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
		<add name="ScriptHandlerFactoryAppServices" verb="*" path="*_AppService.axd" preCondition="integratedMode" type="System.Web.Script.Services.ScriptHandlerFactory, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
		<add name="ScriptResource" preCondition="integratedMode" verb="GET,HEAD" path="ScriptResource.axd" type="System.Web.Handlers.ScriptResourceHandler, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
		<add name="MvcHttpHandler" preCondition="integratedMode" verb="*" path="*.mvc" type="System.Web.Mvc.MvcHttpHandler, System.Web.Mvc, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
		<add name="UrlRoutingHandler" preCondition="integratedMode" verb="*" path="UrlRouting.axd" type="System.Web.HttpForbiddenHandler, System.Web, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"/>
		<add name="Reserved-ReportViewerWebControl-axd" path="Reserved.ReportViewerWebControl.axd" verb="*" type="Microsoft.Reporting.WebForms.HttpHandler, Microsoft.ReportViewer.WebForms, Version=9.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" resourceType="Unspecified" requireAccess="Script" preCondition="integratedMode" />
	</handlers>
</system.webServer>
By shannon on June 03, 2010
Recently, I was asked about doing a find and replace for Word documents in code. We had sections that needed to be conditionally shown, based on specific criteria. This meant that the Word document template contained 2 different sections, but after the code ran, only one of them should remain. Since I had never done this, I did a little searching. I found a large number of posts on various ways to do this, but none of them seemed to do exactly what I wanted, and in many cases they didn't seem to work as I was expecting.

I knew the logic, that would work, but it was a matter of figuring out how to actually find and replace the pieces using the Word interop objects. We added Tags to the document to mark the beginning and ending of the sections that we needed to work with.

First I got it working to delete the section of content that I wanted to "hide". Then, I had to remove just the tags for the section we were going to leave. This left me with just the section of text we wanted.

So just say we started with a very simple Word document that looked something like this:

Here is the start of our document.
[FirstTagToFindStart]This is our first section.[FirstTagToFindEnd][SecondTagToFindStart]This is our second section.[SecondTagToFindEnd]
Here is the end of our document

At the end, we want the document to look like this:

Here is the start of our document.
This is our second section.
Here is the end of our document.

Here is the code that seemed to do the trick:
Word.Application word = new Word.Application();
Document doc = new Document();
// Set our missing object as we are required to pass it for any param that we are not setting below
object missing = System.Type.Missing;

// Open our word document
object fileName = @"c:\wordtest\template.doc";
doc = word.Documents.Open(ref fileName, ref missing, ref missing, ref missing, ref missing, 
    ref missing, ref missing, ref missing, ref missing, ref missing, ref missing, ref missing, ref missing, 
    ref missing, ref missing, ref missing);
doc.Activate();

try
{
    // set our search range to the entire document
    Word.Range beginSearchRange = doc.Content;

    beginSearchRange.Find.Text = "[FirstTagToFindStart]";
    beginSearchRange.Find.Execute(ref missing, ref missing, ref missing, ref missing, ref missing, ref missing,
                      ref missing, ref missing, ref missing, ref missing, ref missing, ref missing,
                      ref missing, ref missing, ref missing);

    object sectionStart = beginSearchRange.Start;

    Word.Range endSearchRange = doc.Content;

    endSearchRange.Find.Text = "[FirstTagToFindEnd]";
    endSearchRange.Find.Execute(ref missing, ref missing, ref missing, ref missing, ref missing, ref missing,
                      ref missing, ref missing, ref missing, ref missing, ref missing, ref missing,
                      ref missing, ref missing, ref missing);

    object sectionEnd = endSearchRange.End;

    Word.Range deleteRange = doc.Range(ref sectionStart, ref sectionEnd);

    // Check to make sure we don't just delete the whole document if not found
    if (deleteRange.Start != doc.Content.Start && deleteRange.End != doc.Content.End)
    {
        deleteRange.Delete(ref missing, ref missing);
    }

    // Need to also delete the tags for the section we are going to keep in the document
    Word.Range removeStartTagRange = doc.Content;
    removeStartTagRange.Find.Text = "[SecondTagToFindStart]";

    removeStartTagRange.Find.Execute(ref missing, ref missing, ref missing, ref missing, ref missing, ref missing,
                      ref missing, ref missing, ref missing, ref missing, ref missing, ref missing,
                      ref missing, ref missing, ref missing);

    // Can get both start and end from this one range
    // Check to be sure we don't delete the whole document
    if (removeStartTagRange.Start != doc.Content.Start && removeStartTagRange.End != doc.Content.End)
    {
        removeStartTagRange.Delete(ref missing, ref missing);
    }

    // Now do the same thing for the Ending alternate tag
    Word.Range removeEndTagRange = doc.Content;
    removeEndTagRange.Find.Text = "[SecondTagToFindEnd]";

    removeEndTagRange.Find.Execute(ref missing, ref missing, ref missing, ref missing, ref missing, ref missing,
                      ref missing, ref missing, ref missing, ref missing, ref missing, ref missing,
                      ref missing, ref missing, ref missing);

    // Can get both start and end from this one range
    if (removeEndTagRange.Start != doc.Content.Start && removeEndTagRange.End != doc.Content.End)
    {
        removeEndTagRange.Delete(ref missing, ref missing);
    }
}
catch (Exception ex)
{
    MessageBox.Show(ex.Message);
}
finally
{
    doc.Close(ref missing, ref missing, ref missing);
    word.Quit(ref missing, ref missing, ref missing);
    System.Runtime.InteropServices.Marshal.ReleaseComObject(word);
}
By shannon on April 28, 2010
I recently was asked if I could provide a SQL statement that would convert Columns into Rows, basically the reverse of a PIVOT. I figured that I could just use UNPIVOT, which should do what I need. Then I found out that they needed to UNPIVOT on pairs of columns.

Suppose you have a table similar to the following, which stores something like allocation adjustments in your accounting system:

ProjectNum

CostCategory

Source1

AdjustmentAmount1

Source2

AdjustmentAmount2

Source3

AdjustmentAmount3

1234567890

111-222222

Some partial adjustment here

12.23

Another partial Adjustment

22.23

NULL

NULL

1234567890

111-333333

Final Adjustment only

22.34

NULL

NULL

NULL

NULL

2345678901

111-222222

I made a partial adjustment

1.34

And yet another

6.78

And a third

2.99


If we want to convert the above table into a normalized table, so we can do aggregate calculations, or other reporting on the data, we have a few options. In our case, we have the added requirement of needing to keep the column pairs together, so that we can get the description of the adjustment to go along with the adjustment amount. So in the above example, Source1 and AdjustmentAmount1 would be paired along with Source2 and AdjustmentAmount2, and so on.

The first option would be to write a Transact SQL statement to select each pair of columns and then using the UNION operator to join the result sets together. This would be something similar to the following:

SELECT
	ProjectNumber,
	CostCategory,
	Source1 As Source,
	AdjustmentAmount1 As AdjustmentAmount
FROM
	Adjustment
UNION
SELECT
	ProjectNumber,
	CostCategory,
	Source2,
	AdjustmentAmount2
FROM
	Adjustment

ProjectNumber

CostCategory

AdjSource

Source

AdjustmentAmount

1234567890

111-222222

Source1

Some partial adjustment here

12.23

1234567890

111-333333

Source1

Final Adjustment only

22.34

2345678901

111-222222

Source1

I made a partial adjustment

1.34

2345678901

111-444444

Source1

No real comment here

32.45

3456789012

111-222222

Source1

Another comment here

22.11

1234567890

111-222222

Source2

Another partial Adjustment

22.23

1234567890

111-333333

Source2

NULL

NULL

2345678901

111-222222

Source2

And yet another

6.78

2345678901

111-444444

Source2

Yet another adjustment

6.54

3456789012

111-222222

Source2

NULL

NULL


There are a few problems with performing the SELECT this way. First, the separate SELECT statements can lead to a very lengthy query, especially if you have several pairs of columns that you need to generate this for or have complex criteria as part of your SELECT. Depending on your volume of data, it could lead to performance issues as well.

Starting with SQL 2005, they introduced the PIVOT and UNPIVOT commands for TRANSACT SQL. UNPIVOT does almost exactly the opposite of what PIVOT does. The main difference is that PIVOT will aggregate the data, and UNPIVOT does not remove the aggregation. This is important to realize as you cannot UNPIVOT a result set that you used the PIVOT on, and end up with the same exact rows and column values as you started with.

To accomplish what we are trying to do, we can re-write our SELECT using the UNPIVOT command, similar to the following:

SELECT 
	ProjectNumber,
	CostCategory,
	RIGHT(AdjSource,1) As ColumnGroup,
	AdjSource,
	AdjDescription,
	AdjAmount
FROM
	(SELECT ProjectNumber
		  ,CostCategory
		  ,Source1
		  ,AdjustmentAmount1
		  ,Source2
		  ,AdjustmentAmount2
		  ,Source3
		  ,AdjustmentAmount3
	FROM 
		  Adjustment) p 
UNPIVOT
      (AdjAmount FOR AdjSource IN (AdjustmentAmount1,AdjustmentAmount2,AdjustmentAmount3)) as up1
UNPIVOT
      (AdjDescription for AdjDesc IN (Source1,Source2,Source3)) as up2
WHERE
      RIGHT(AdjSource,1) = RIGHT(AdjDesc, 1)
ORDER BY
	ProjectNumber,
	CostCategory,
	ColumnGroup

This will give us a result set that looks like the following:

ProjectNumber

CostCategory

ColumnGroup

AdjSource

AdjDescription

AdjAmount

1234567890

111-222222

1

AdjustmentAmount1

Some partial adjustment here

12.23

1234567890

111-222222

2

AdjustmentAmount2

Another partial Adjustment

22.23

1234567890

111-333333

1

AdjustmentAmount1

Final Adjustment only

22.34

2345678901

111-222222

1

AdjustmentAmount1

I made a partial adjustment

1.34

2345678901

111-222222

2

AdjustmentAmount2

And yet another

6.78

2345678901

111-222222

3

AdjustmentAmount3

And a third

2.99

2345678901

111-444444

1

AdjustmentAmount1

No real comment here

32.45

2345678901

111-444444

2

AdjustmentAmount2

Yet another adjustment

6.54

2345678901

111-444444

3

AdjustmentAmount3

Final Adjustment

9.9

3456789012

111-222222

1

AdjustmentAmount1

Another comment here

22.11


The key to making this work, is the WHERE clause. It is a fairly simple concept, but it allows us to pair up our Columns assuming that there is a way to group them like we have done. This is using the last digit(s), to do the pairing. Without the WHERE clause being added to our SELECT, we would get many more result sets back. This is because the SELECT would work very similarly to a CROSS JOIN. By adding the WHERE clause, we limit it to just the combinations that we want to see.

The one potential downside to using the UNPIVOT command is that if your column names change, it can break your SQL in ways that are not necessarily the same as a traditional SELECT statement.

The above UNPIVOT example makes life much easier the more pairs of columns that you need to UNPIVOT. I recently had to perform this SELECT with 20 pairs of columns, so the UNION SELECT method was really not a viable option.
By shannon on March 23, 2010

As you work with CRM 4.0, it is fairly common to find a need to customize your entity data entry forms. This includes hiding or showing specific sections on the form, based on a lookup value on the form, for something like a Category, or a Type. There are plenty of examples of this on the internet.

As the form complexity grows, and the length as well, it would be nice to be able to make the form a little more manageable for those who will be using the form. This is where something like collapsible form sections can really improve the usability of form. There are a couple examples of how to do this. One such example is: http://mscrm4ever.blogspot.com/2008/08/creating-collapsable-sections.html.

The example there works fairly well, and even allows us to control the initial collapsed/expanded state, unlike the other cases found on the internet. It doesn’t however lend itself to working with a complex form that might have sections that are hidden based on the value in a drop down on the form. To handle both of these pieces of functionality, the following code should be placed in the OnLoad event for the form.

crmForm.all.new_categoryid.attachEvent( "onchange" , OnCategoryChange );

/* Place anything needed to run ON LOAD here */
function OnCRMFormLoad(){
    // Call our intial collapse state for the sections
    SetInitialCollapseState(true);
    
    // Run the category change on load, in case we are already at "My Category"
    OnCategoryChange();
}

/*  Form Event Functions  */
function OnCategoryChange()
{
   var category = crmForm.all.new_categoryid.DataValue;
   var categoryName = "";
    if(category != null)
        categoryName = category[0].name;

    if( categoryName == "My Category" )
    {
	  // Hide Certain Sections
        HideSection( 0 , 6 , "block" ); 
        // Show Certain Sections
        HideSection( 0 , 1 , "none" ); 

        // set the initial state of the sections
        SetInitialCollapseState(false);
    }
    else
    {
        // Hide Certain Sections
        HideSection( 0 , 1 , "block" ); 
        // Show Certain Sections
        HideSection( 0 , 6 , "none" ); 
    }
}
/* End Form Event Functions */

// tabs and section are zero based
function HideSection( tabIndex , sectionIndex , displayType )
{
      var tab2Hide = document.getElementById( "tab" + tabIndex );
       tab2Hide.childNodes[0].rows[ sectionIndex ].style.display = displayType;
}

/* Expanding Section Section */
function SetInitialCollapseState(addImage) {      
    /*
        Call ConvertSection(Tab,Section,Expanded,AddImage)
        Tab = zero based
        Section = Zero based
        Expanded = true or Collapsed false
	  AddImage = true or false – Only should add very first time
    */
    ConvertSection(0,2,false,addImage);  // Section 3 Collapsed
    ConvertSection(0,3,true,addImage); // Section 4 Expanded
    ConvertSection(0,4,false,addImage); // Section 5 Collapsed
    ConvertSection(0,5,false,addImage);  // Section 6 Collapsed
    ConvertSection(0,6,false,addImage);  // Section 7 Collapsed
}  
  
function ConvertSection( tIndex , sIndex , expandedState , addImage ) {  
    var tab = document.getElementById( "tab" + tIndex);
    var section = tab.childNodes[0].rows[ sIndex ].cells[0].childNodes[0].rows[0].cells[0];
    var sectionHTML = section.innerHTML;

    // determine the status of our expanded state
    expandedState = (typeof(expandedState) == "undefined") ? false : !expandedState;
    // determine which image we are going to apply based on our expanded state 
    if(addImage){
        var imageName = (expandedState == false) ? "/_imgs/navup.gif":"/_imgs/navdown.gif";
        section.innerHTML = "<NOBR style='VERTICAL-ALIGN: middle;cursor:hand' onclick='ExpandCollapseSection(this)'><IMG src='" + imageName + "' align='middle' /> " + sectionHTML + "</NOBR>";  

    }
    section.childNodes[0].childNodes[0].state = expandedState;
    ExpandCollapseSection(section.childNodes[0]);
}  
  
/* Toggle the passed section state */   
function ExpandCollapseSection( section ) {  
    section = section.childNodes[0]; 
    // Toggle our state 
    section.state = !section.state;  
    // Based on the current state, determine the image that should be shown
    section.src = (section.state)? "/_imgs/navup.gif":"/_imgs/navdown.gif";
    // Determine the display type based on our current state  
    var display = (section.state)? "inline" :"none";  
    // Get the sections parent table element
    var tableSection = section.parentElement.parentElement.parentElement.parentElement;  
    // Cycle through all rows in the table and hide/show them
    for( var i =1 ; i < tableSection.rows.length ; i++ )  
        tableSection.rows[i].style.display = display;  
}  

// Expose the collapse/expand method so it can be used by clicks
this.ExpandCollapseSection = ExpandCollapseSection;  
  
/* Expanding Section Section End */

// Call the "starting" items
OnCRMFormLoad();