Friday, November 21, 2008

Change Cases Associated Default View

This is a complementary post to the “Changing Activity / History Default View”.
The code sample uses exactly the same concept as the former post so if you’re using that then you should integrate both solution to avoid duplicate functions.


//Case History statecode options
var ServiceOptions =
{
Active : "0",
Resolved : "1",
Canceled : "2",
All : "All"
}

var _loadarea = loadArea;
loadArea = function(sArea, sParams, sUrl, bIsvMode)
{
//load the iframe
_loadarea(sArea, sParams, sUrl, bIsvMode);

if( sArea != "areaService" ) return;

//create the iframe object
var iframe = document.getElementById(sArea + "Frame");
//wait until the iframe is fully loaded ("complete")
iframe.onreadystatechange = function()
{
if( iframe.readyState == "complete")
{
var picklist,option;
//reference to the iframe document
var iframeDoc = iframe.contentWindow.document;
switch(sArea)
{
case "areaService":
picklist = iframeDoc.all.statecode[0];
option = ServiceOptions.All;
break;
}
picklist.value = option;
picklist.FireOnChange();
}
}
}

Friday, November 14, 2008

Creating an UpDown Control

The updown control is merely a control that enables the user to select a number from a predefined range either by direct assignment or by using up and down selectors (arrows). This is usually very useful when the changing of the number has an immediate affect of some sort on the form where it’s placed. This is just a UI enhancement that should be used only where appropriate.

The idea behind the mechanics of the updown control is that it uses a span with an overflow-y: scroll. The span has an inner span which is set to hidden but its height is set to the updown max value. This hack actually creates the range for you. When the user scrolls down the scrollTop attribute reflects the current position (selected number) which is written back to the text field. An opposite approach is used when the users assigns a value directly, then the span scrollTop is updated with the current field datavalue.





/*
parameters: fieldId - crm field id
minValue - positive number
maxValue - positive number
*/
function UpDownControl( fieldId , minValue , maxValue )
{
var Instance = this;
/* crm field */
var upDownCtrlBox = crmForm.all[ fieldId ];
/* field parent element field_d */
var parent = upDownCtrlBox.parentElement;
/* this span affects the spanUpDown scroll */
var span = document.createElement( "<SPAN style='visibility:hidden;width:1;'>" );
var spanUpDownHeight = upDownCtrlBox.clientHeight - 2;
/* this span is the scroller */
var spanUpDown = document.createElement( "<SPAN style='margin-left:-18px;overflow-y:scroll;width:1;height:" + spanUpDownHeight + ";'>" );
/* max range */
Instance.Max = maxValue;
/* min range */
Instance.Min = minValue;
/* fires when the scroll is pressed */
function onScrollChange()
{
/* get the current scroll position */
var scrollTop = parseInt( spanUpDown.scrollTop );
/* adjust size if value is outside of the scroll limits */
if( scrollTop < Instance.Min ){
scrollTop = Instance.Min;
spanUpDown.scrollTop = scrollTop;
}
else if( scrollTop > Instance.Max ){
scrollTop = Instance.Max;
spanUpDown.scrollTop = scrollTop;
}
/* assign the scroll value back to the field */
upDownCtrlBox.DataValue = scrollTop;
}
/* fires when the field value changes */
function onValueChange()
{
/* if the value is valid */
if( upDownCtrlBox.DataValue != null )
{
/* get the value */
var dataValue = parseInt( upDownCtrlBox.DataValue );
/* check that the vlaue is within permitted limits */
if( dataValue < Instance.Min ){
dataValue = Instance.Min;
upDownCtrlBox.DataValue = dataValue;
return false;
}
else if( dataValue > Instance.Max ){
dataValue = Instance.Max;
upDownCtrlBox.DataValue = dataValue;
return false;
}
/* assign the field value back to the scroll */
spanUpDown.scrollTop = dataValue;
}
/* assign min value is the datavalue is null */
else spanUpDown.scrollTop = Instance.Min;
}

/* fire onValueChange when the field is changed */
upDownCtrlBox.attachEvent( "onchange" , onValueChange );
/* adjust inner span height = maxValue + spanHeight */
span.style.height = maxValue + spanUpDownHeight;
/* construct the updown control */
spanUpDown.appendChild( span );
/* fire onScrollChange when the user presses the scrollbar */
spanUpDown.attachEvent( "onscroll" , onScrollChange );
parent.appendChild( spanUpDown );
/* call onValueChange when the form loads / control is created */
onValueChange();
}

widthUpDownCtrl = new UpDownControl("gi_width" , 20 , 10000 );

Thursday, November 13, 2008

Creating a Dynamic Picklist Control

There are situations where you need to create a Picklist with values from an external source or just don’t want to keep a static list on the Picklist attribute. Initially I posted a simple solution which illustrates how to convert a text attribute into a Picklist.

This post offers a different and more robust solution to the creation of dynamic Picklists.
I call the control a TextList which obviously is a hybridization of a text and a Picklist fields.

The TextList also contains a Dependent Field Collection whose fields are bind to its onchange event. This way, each time the Picklist changes, the option properties are mapped to the form dependent fields. In the code sample bellow I’ve created 2 additional attributes on each option. The first is called Color and the second is called JustANubmer. When the user selects an option the color and number are written to two dependent fields (gi_dependentfield1 and gi_dependentfield2). The TextList also exposes other wrapper methods on the inner Picklist control so you won’t have to deal with two objects.

I’ve added comments which explain the methods functionality.


// JScript File
function OnCrmPageLoad()
{
/* Change gi_name text field to a textlist */
var nameTextList = new TextList( "gi_name" );
/* Set 2 dependent fields */
nameTextList.DependentFields.Add( "gi_dependentfield1" , "JustANumber" );
nameTextList.DependentFields.Add( "gi_dependentfield2" , "Color" );
/* Add blank option */
nameTextList.Add( "" , "" );
for( var i = 0 ; i < 6 ; i++ )
{
/* Fill some values */
var option = nameTextList.Add( "Text " + i , "Value " + i );
/* create 2 new attributes on each option (Color,JustANumber) */
option.Color = "A" + i + "B" + i + "C" + i;
option.JustANumber = i.toString();
}
/* Set the initial value */
nameTextList.SetInitValue(true);
/* Finish the job */
nameTextList.Transform();
}

/*
Holds a list of all dependent fields.
Each textlist has a dependent fields collection
*/
DependentFieldCollection = function()
{
this.List = [];
this.Add = function( name , prop ){
this.List.push( new DependentField( name , prop ) );
}
}
/*
Single Dependent Field.
The Object maps between an option attribute an a crmForm field
*/
DependentField = function( name , prop )
{
this.Property = prop;
this.Name = name;
}
/* Text list control */
TextList = function( txtId )
{
var textList = this;
/* Referech the original text field */
var textCtrl = crmForm.all[ txtId ];
if (!textCtrl) return alert("TextList: " + txtId + " Is Missing");

/* Create a new Picklist control */
textList.Picklist = document.createElement("SELECT");
textList.Picklist.id = textCtrl.id;
textList.Picklist.req = textCtrl.req;
textList.Picklist.tabIndex = textCtrl.tabIndex;
textList.Picklist.className = "ms-crm-SelectBox ";
textList.Picklist.defaultSelected = textCtrl.DataValue;
textList.DependentFields = new DependentFieldCollection();

/* Add the new picklist and remove the text control */
textCtrl.parentElement.appendChild( textList.Picklist );
textCtrl.parentElement.removeChild( textCtrl );

/*
Finish the textlist transformation
Create inline dependent fields properties on the textlist
Attach an update function to the picklist onchange event
*/
textList.Transform = function()
{
for( var i = 0;i < textList.DependentFields.List.length;i++ ){
var depField = textList.DependentFields.List[ i ];
textList[ depField.Name ] = crmForm.all[ depField.Name ];
}
textList.Picklist.attachEvent( "onchange" , UpdateDependentFields );
}
/* Sets the picklist value */
textList.SetValue = function( value )
{
textList.Picklist.DataValue = value;
textList.Picklist.FireOnChange();
}
/*
Sets the picklist initial value.
This should be called when the page load after you fill the picklist options
*/
textList.SetInitValue = function( fireEvent )
{
textList.Picklist.DataValue = textList.Picklist.defaultSelected;
if(fireEvent) textList.Picklist.FireOnChange();
}
/* Add a new picklist Option */
textList.Add = function( text , value )
{
return textList.Picklist.AddOption( text , value );
}
/* Remove a picklist Option by its value */
textList.Remove = function( value )
{
textList.Picklist.DeleteOption( value );
}
/* Clear all Picklist Options */
textList.Clear = function()
{
textList.Picklist.length = 0;
}
/* Helper property for attaching to an event from within the picklist control */
textList.Attach = function( targetId , eventName , callback )
{
var target = document.all[ targetId ];
if ( target ) target.attachEvent( eventName , callback );
}
/* Updates the dependent fields when the picklist onchange fires */
function UpdateDependentFields()
{
if ( textList.Picklist.selectedIndex > -1 )
for( var i = 0;i < textList.DependentFields.List.length;i++ )
{
var depField = textList.DependentFields.List[ i ];
textList[ depField.Name ].DataValue = textList.Picklist.SelectedOption[ depField.Property ];
textList[ depField.Name ].FireOnChange();
}
}
}


OnCrmPageLoad();

Wednesday, November 12, 2008

Creating Mini Form Sections


The mini sections are a great way to create smaller logical areas inside a CRM section.
If you take a look at the field level security wizard you’ll see that we have used the mini section to create headings for each and every step.

Creating the mini section is fairly simple. All you need to do is create a new text attribute for each section and hide its label from within the customization. The code gets the hidden label text, places it inside the attribute control (DataValue) and formats the layout.

The end result looks like this:




function OnCrmPageLoad()
{
/* Convert the text attribute ‘gi_ministep1’ to a mini section */
CreateMiniSection("gi_ministep1");
}

function CreateMiniSection( ctrlId )
{
var Control = crmForm.all[ ctrlId ];
if ( !Control ) return;
Control.DataValue = " " + crmForm.GetLabel(Control);
Control.Disabled = true;
Control.style.cssText = "font-weight:900;border:1px solid #94b2de;filter: progid:DXImageTransform.Microsoft.Gradient(GradientType=0,StartColorStr=#eff3ff,EndColorStr=#cedfff)";
}

OnCrmPageLoad();

Restricting access to IFRAME (read-only IFRAME)


This is a complementary post to my original post about creating a read-only iframe. The former post opens a new window outside of the users reach and steals the IFRAME focus. The following code makes use of the SECURITY=”restricted” IFRAME attribute that was introduced in IE6.
Since you can’t set this attribute from script once the IFRAME is on the form you need to remove the original IFRAME and create a new one instead.
The following code tells the tail.

One thing to notice is that the IFRAME will be completely inaccessible. This means that you can not script against the IFRAME at all. If you’re using my IFRAME viewer utility this solution won’t work for you.


function OnCrmPageLoad()
{
RestrictAccessToIFrame("IFRAME_Test");
}

function RestrictAccessToIFrame(iframeId)
{
/* Reference the relevant IFRAME */
var testIframe = document.all[iframeId];
/* get the iframe parent container */
var testIframeParent = testIframe.parentElement;

/* if the IFRAME exist */
if (testIframe)
{
/* Save the Original Iframe HTML */
var testIframeHTML = testIframe.outerHTML;
/* Remove the Original IFRAME */
testIframeParent.removeChild(testIframe);
/* Create a new IFRAME Instead */
testIframe = document.createElement(testIframeHTML);
/* Add an IFRAME restriction attribute to the new IFRAME Element */
testIframe.setAttribute("SECURITY","restricted");
/* IF needed rebuild the IFRAME url */
testIframe.src = "/MicrosoftCRM/_root/homepage.aspx?etc=1";
/* Append the IFRAME to its original parent element */
testIframeParent.appendChild(testIframe);
}
}

OnCrmPageLoad();

Tuesday, November 11, 2008

CRM 4.0 Field Level Security Wizard


My company uses the dynamics platform as basis for its products. From a product point of view, dynamics is an awesome foundation but is lacking many important functionalities which our soon to be customers require. In order to fill those gaps and achieve a fast pace product integration with as little development effort as possible we build functional wizards, in-house Utility Add-ons, to a variety of development tasks and business needs. Our goal eventually is to enable our integration team accomplish those tedious tasks in just a few clicks. A good example of that is the Field Level Security wizard which is presented herein.

As we already put time and effort in building those smart solutions, I thought it would be great to somehow share them with you on a “shareware” basis. In simple terms I decided to uncover those wizards with their full source code and installation instructions for a small and very affordable one time fee. Once you acquire the rights you can utilize and implement each wizard in any of your dynamics solutions as if it is your company own property.

The wizards will undoubtedly save you hundreds of development hours, speed up you delivery and much more. Since I’m testing this initiative for the time being I wanted to give you a taste and share this post with you. If you find this interesting, share your thought and questions with me (comment or send an email to support@gicrm.com).

The Field Level Security Wizard is a fully functional solution which enables you to define security settings throughout the organization hierarchy. In other words you can create FLS templates for each business unit, override or extend those settings for each security role and more importantly create exceptions at the user level. This gives you a much tighter security control over the entire dynamics application.

One of the most common tasks partners need to accomplish in every CRM project is hiding or showing specific form function (e.g. tab, toolbar button or left navigation link) depending on the user business unit, role and user. The FLS wizard can also facilitates this type of tasks with ease.

In the following demonstration I implemented a simple scenario on the account entity. First I create a simple FLS template on the business unit level by hiding the Main Phone and City fields, then I override those settings for the system administrator role by giving the role limited access to those fields and finally I create a specific exception for the administrator (me) and override/enable these fields completely.

In order to rewind the video right click on the flash movie, click rewind and then play.



FLS Features and information:
1. Supported Entities - All entity types (System, Customizable and Custom).
You can find the entire entity list inside CRM advanced find window.

2. Supports both online and offline modes.

3. Supports the following CRM modules:
  1. Entity Form (Can be extended using client side API)
  2. Entity Print Form
  3. Entity Views
  4. Entity Views Print page
  5. Static Export to Excel
  6. Advanced Find
  7. Mail Merge
  8. Merge
  9. Filtered View (For Reports)
  10. Entity Associated views
  11. Lookup views
  12. CRM Workflows

4. Supported Languages - FLS Customizations are available in all base languages – no language packs needed, But you’ll need to translate them your self.

5. Supported Layouts - Dir RTL ( Right to Left ) and LTR ( Left to Right ) layouts

6. Supports the following field level security modes:
  1.  Default – Field is not set
  2.  Hidden – Field is hidden on both crm form and views
    a.  Keep layout – the field space is kept
    b.  Collapse layout – the field space is collapsed for better presentation
  3.  Missing – Field is disabled , the user can not see the data
  4.  Disabled – Field is disabled , the user can see the data
  5.  Enabled – Override existing settings, the user has full permissions on the field.

7. Supports “Formless” Entities – Entities that do not have a form like activity

8. Supports Security Hierarchy:


  1. Business Unit level: Creation of FLS Templates that affect the entire business unit.
  2. Role Level: Inheritable security roles with an option to extend or override business unit settings
  3. User Level: Inheritable User settings with the option to create exceptions for users within the same role

9. Fields View - Intelligent Orientation.


The user can filter by:
  1. Field Type: Presents all available field types
  2. Existing: Presents existing Settings
  3. Placement: Presents fields that exist ON and OFF the CRM Form
  4. Tabs: Presents All Fields Categorized by Tab Name
  5. Sections: Presents All Fields Categorized by Section Name

The wizard also supports Internet Facing Deployment (IFD) and IE8

As you can see, we give a lot of attention to the entire usability issue. The idea is to enable the integrator / developer to achieve his FLS goals as fast as possible.

The wizard is now available online. I’m sure you’ll find it worthwhile. Feel free to comment here or send your questions to support@gicrm.com.