<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-3003801964503297388</id><updated>2012-01-26T21:43:46.865+02:00</updated><title type='text'>Microsoft Dynamics CRM 4.0 - Unleashed</title><subtitle type='html'></subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://mscrm4ever.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3003801964503297388/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://mscrm4ever.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><link rel='next' type='application/atom+xml' href='http://www.blogger.com/feeds/3003801964503297388/posts/default?start-index=101&amp;max-results=100'/><author><name>Adi Katz</name><uri>http://www.blogger.com/profile/16783428437179825705</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>102</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-3003801964503297388.post-7933880171942692548</id><published>2010-07-19T23:25:00.011+03:00</published><updated>2010-07-20T10:24:36.341+03:00</updated><title type='text'>CRM 4.0 Enhancing Picklists presentation layer</title><content type='html'>&lt;div&gt;&lt;/div&gt;&lt;br /&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 249px;border:1px solid #000000;" src="http://2.bp.blogspot.com/_M-mdv3Tfarg/TES2MmSZKlI/AAAAAAAAARI/jwiiC8lTbmQ/s400/reltypepicklist.JPG" border="0" alt=""id="BLOGGER_PHOTO_ID_5495717772851227218" /&gt;&lt;br /&gt;&lt;br /&gt;Picklists are usually used to provide a short range of predefined options that further describe our data.  To make them usable it’s suggestible to keep the number of Picklist options to a minimum. Although a Picklist presentation layer is functional data is not always that boring and deserves a better re-presentation.  &lt;br /&gt;&lt;br /&gt;I personally think that controls should convey more emotions and strengthen bond between what is showing and how it’s shown. Take for example a simple Picklist that has values 1-5 that describe the account rating or a Picklist of shipping methods with well known company names. No doubt presenting the following control to the user makes more sense than just selecting a number. &lt;br /&gt;&lt;br /&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 392px; height: 211px;border:1px solid #000000;" src="http://3.bp.blogspot.com/_M-mdv3Tfarg/TES2MZF36nI/AAAAAAAAARA/CcwWqiNTnwQ/s400/ratingpicklist.JPG" border="0" alt=""id="BLOGGER_PHOTO_ID_5495717769309055602" /&gt;&lt;br /&gt;&lt;br /&gt;The concept used in this example can be developed further to support more advanced scenarios. The nice thing about it is that most of the bits that handle the actual show and hide of the Picklist menu can be reused. So if you find yourself needing to create a more complex Picklist you should center most of your effort by overriding the buildPopup function (see code).&lt;br /&gt;&lt;br /&gt;The code exposes most of the styling attributes which means you don’t have to tear it apart if you feel like redecorating ;+). &lt;br /&gt;&lt;br /&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 384px; height: 119px;border:1px solid #000000;" src="http://3.bp.blogspot.com/_M-mdv3Tfarg/TES2LxVtsAI/AAAAAAAAAQ4/T0RPWbJ2twg/s400/categorypicklist.JPG" border="0" alt=""id="BLOGGER_PHOTO_ID_5495717758638075906" /&gt;&lt;br /&gt;&lt;br /&gt;In order to facilitate the construction of options and their respective images the code uses a simple technique that accepts an Image base URL and utilizes the picklist id to identify specific images folder. The image names represent their index i.e. 0.gif, 1.gif, 2.gif and so on. If need to use option’s value instead you must also change the code that constructs the image URL.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;CRMWeb &lt;br /&gt;   | -- ISV &lt;br /&gt;      |-- IMGPL  (this is the ImageBaseUrl)&lt;br /&gt;         |-- selected.gif (A small arrow that points to the selected option)&lt;br /&gt;         |-- gi_rating       (This is the Picklist id)&lt;br /&gt;            |-- 0.gif    (Empty option X image – index 0)&lt;br /&gt;            |-- 1.gif    (first option – index 1)&lt;br /&gt;            |-- 2.gif    (second option – index 2 and so on..,)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Eventually the image URL is http://servername:port/iSV/IMGPL/gi_rating/1.gif&lt;br /&gt;Feel free to comment.&lt;br /&gt;&lt;br /&gt;&lt;pre class="js" name="code"&gt;&lt;br /&gt;&lt;br /&gt;String.prototype.Format = function( args )&lt;br /&gt;{&lt;br /&gt;   return this.replace( /\{(\d{1})\}/ig , function match(){return args[arguments[1]];});&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;function ImagePicklist(picklistId)&lt;br /&gt;{&lt;br /&gt;   var ipl = this;&lt;br /&gt; &lt;br /&gt;   if (!(ipl.Picklist = crmForm.all[picklistId])) &lt;br /&gt;         return alert("Picklist is missing");&lt;br /&gt; &lt;br /&gt;   if (ipl.Picklist.Disabled) &lt;br /&gt;         return;&lt;br /&gt; &lt;br /&gt;   ipl.ImageBaseUrl = "/ISV/IMGPL/";&lt;br /&gt;   ipl.HoverStyle = {Color: "#FFFFFF",Background: "#000000"}&lt;br /&gt;   ipl.HasEmptyOption = true;&lt;br /&gt;   ipl.Height = 213;&lt;br /&gt;   ipl.BackgroundColor = "#FFFFFF";&lt;br /&gt;   ipl.Scroll = true;&lt;br /&gt; &lt;br /&gt;   ipl.Picklist.ondblclick = function(){show();}&lt;br /&gt;   ipl.Picklist.onmousedown  = function(){show();}&lt;br /&gt;   ipl.Picklist.onfocusout = function()&lt;br /&gt;   {&lt;br /&gt;      ipl.Picklist.Popup.document.body.innerHTML = "";&lt;br /&gt;      ipl.Picklist.Popup.hide(); &lt;br /&gt;   }&lt;br /&gt; &lt;br /&gt;   function show()&lt;br /&gt;   {&lt;br /&gt;      ipl.Picklist.Disabled = true;&lt;br /&gt;      buildPopup();&lt;br /&gt;      var left  = ipl.Picklist.Position.X;&lt;br /&gt;      var top   = ipl.Picklist.Position.Y;&lt;br /&gt;      var width = ipl.Picklist.offsetWidth;&lt;br /&gt;      var height = ipl.Height &lt;br /&gt;      ipl.Picklist.Popup.show(left ,top ,width ,ipl.Height ,document.body);&lt;br /&gt;      setTimeout(function(){ipl.Picklist.Disabled = false;},100);&lt;br /&gt;      return false;&lt;br /&gt;   }&lt;br /&gt; &lt;br /&gt;   ipl.MouseOver = function(option)&lt;br /&gt;   {&lt;br /&gt;      option.style.backgroundColor = ipl.HoverStyle.Background;&lt;br /&gt;      option.style.color = ipl.HoverStyle.Color;&lt;br /&gt;   }&lt;br /&gt; &lt;br /&gt;   ipl.MouseOut = function(option)&lt;br /&gt;   {&lt;br /&gt;      option.style.backgroundColor = ipl.BackgroundColor;&lt;br /&gt;      option.style.color = "#000000";&lt;br /&gt;   }&lt;br /&gt; &lt;br /&gt;   ipl.Onclick = function(option)&lt;br /&gt;   {&lt;br /&gt;      ipl.Picklist.onfocusout();  &lt;br /&gt;      ipl.Picklist.selectedIndex = option.index&lt;br /&gt;      ipl.Picklist.focus();&lt;br /&gt;   }&lt;br /&gt; &lt;br /&gt;   function getPosition(control)  &lt;br /&gt;   {      &lt;br /&gt;      var left = 0;&lt;br /&gt;      var top = control.offsetHeight; &lt;br /&gt;  &lt;br /&gt;      do  {&lt;br /&gt;         left += control.offsetLeft;&lt;br /&gt;         top  += control.offsetTop;&lt;br /&gt;      }   while (control = control.offsetParent);&lt;br /&gt;&lt;br /&gt;      return {X:left,Y:top}&lt;br /&gt;   } &lt;br /&gt; &lt;br /&gt;   function buildPopup()&lt;br /&gt;   {&lt;br /&gt;      ipl.Picklist.Position = getPosition(ipl.Picklist);&lt;br /&gt;      ipl.Picklist.Popup.document.body.style.backgroundColor = ipl.BackgroundColor; &lt;br /&gt;  &lt;br /&gt;      var div = document.createElement("DIV");&lt;br /&gt;      div.style.cssText = "overflow-y:{0};height:{1}px;".Format([(ipl.Scroll?"scroll":"hidden"),(ipl.Height-13)]);&lt;br /&gt;   &lt;br /&gt;      for (var i=(ipl.HasEmptyOption?0:1);i&lt; ipl.Picklist.options.length;i++)&lt;br /&gt;      {&lt;br /&gt;         var option = ipl.Picklist.options[i];&lt;br /&gt;    &lt;br /&gt;         var item = document.createElement("DIV");&lt;br /&gt;         item.index = i;&lt;br /&gt;         item.onmouseover = "document.ImagePicklist.MouseOver(this);";&lt;br /&gt;         item.onmouseout  = "document.ImagePicklist.MouseOut(this)";&lt;br /&gt;         item.onclick = "document.ImagePicklist.Onclick(this)";&lt;br /&gt;         item.title = "Value = {0}".Format([option.value]);&lt;br /&gt;         item.style.lineHeight = "25px"&lt;br /&gt;         item.style.cursor = "hand";&lt;br /&gt;    &lt;br /&gt;         var selItem = null;&lt;br /&gt;         if (option.selected)&lt;br /&gt;         {&lt;br /&gt;            selItem = document.createElement("IMG");&lt;br /&gt;            selItem.style.height = "16px";&lt;br /&gt;            selItem.style.width  = "16px";&lt;br /&gt;            selItem.src = "{0}selected.gif".Format([ipl.ImageBaseUrl]);&lt;br /&gt;            selItem.align = "middle";&lt;br /&gt;         }&lt;br /&gt;         else&lt;br /&gt;         {&lt;br /&gt;            selItem = document.createElement("SPAN");&lt;br /&gt;            selItem.innerHTML = "&amp;nbsp;"; &lt;br /&gt;            selItem.style.width = "16px";&lt;br /&gt;         }&lt;br /&gt;     &lt;br /&gt;         item.appendChild(selItem);&lt;br /&gt;         item.appendChild(document.createTextNode(" "));&lt;br /&gt;         &lt;br /&gt;         var img  = document.createElement("IMG");&lt;br /&gt;         img.src  = "{0}{1}/{2}.gif".Format([ipl.ImageBaseUrl,ipl.Picklist.id,i]);&lt;br /&gt;     &lt;br /&gt;         item.appendChild(img);&lt;br /&gt;         var optText = null;&lt;br /&gt;         if (option.selected)&lt;br /&gt;         {&lt;br /&gt;            optText = document.createElement("B");&lt;br /&gt;            optText.innerText = " {0}".Format([option.innerText]);&lt;br /&gt;         }&lt;br /&gt;         else&lt;br /&gt;         {&lt;br /&gt;            optText = document.createTextNode(" {0}".Format([option.innerText]));&lt;br /&gt;         }&lt;br /&gt;         item.appendChild(optText);&lt;br /&gt;         div.appendChild(item);&lt;br /&gt;      }&lt;br /&gt;   &lt;br /&gt;      ipl.Picklist.Popup.document.body.innerHTML = div.outerHTML;&lt;br /&gt;   }&lt;br /&gt;  &lt;br /&gt;   { //Initialize&lt;br /&gt;   &lt;br /&gt;      ipl.Picklist.Popup = window.createPopup();&lt;br /&gt;      /* A reference from the window popup to ipl */&lt;br /&gt;      ipl.Picklist.Popup.document.ImagePicklist = ipl;&lt;br /&gt;   &lt;br /&gt;      var popUpBodyStyle = ipl.Picklist.Popup.document.body.style;&lt;br /&gt;      popUpBodyStyle.border  = "1px solid gray";&lt;br /&gt;      popUpBodyStyle.padding = 0;&lt;br /&gt;      popUpBodyStyle.margin  = 5;&lt;br /&gt;   }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;function OnCrmPageLoad()&lt;br /&gt;{&lt;br /&gt;   &lt;br /&gt;   window.ctcPicklist = new ImagePicklist("customertypecode");&lt;br /&gt;   ctcPicklist.ImageBaseUrl = "/ISV/IMGPL/";&lt;br /&gt;   ctcPicklist.HoverStyle = {Color: "gold",Background: "#FF3454"}&lt;br /&gt;   ctcPicklist.HasEmptyOption = false;&lt;br /&gt; &lt;br /&gt;   window.accPicklist = new ImagePicklist("accountcategorycode");&lt;br /&gt;   accPicklist.Height = 85;&lt;br /&gt;   accPicklist.Scroll = false;&lt;br /&gt;   accPicklist.BackgroundColor = "yellow";&lt;br /&gt; &lt;br /&gt;   window.graPicklist = new ImagePicklist("gi_rating");&lt;br /&gt;   graPicklist.Height = 165;&lt;br /&gt;   graPicklist.Scroll = false;&lt;br /&gt;   graPicklist.HoverStyle = {Color: "navy",Background: "#FFFFFF"}&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;OnCrmPageLoad();&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3003801964503297388-7933880171942692548?l=mscrm4ever.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mscrm4ever.blogspot.com/feeds/7933880171942692548/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3003801964503297388&amp;postID=7933880171942692548&amp;isPopup=true' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3003801964503297388/posts/default/7933880171942692548'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3003801964503297388/posts/default/7933880171942692548'/><link rel='alternate' type='text/html' href='http://mscrm4ever.blogspot.com/2010/07/crm-40-enhancing-picklists-presentation.html' title='CRM 4.0 Enhancing Picklists presentation layer'/><author><name>Adi Katz</name><uri>http://www.blogger.com/profile/16783428437179825705</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_M-mdv3Tfarg/TES2MmSZKlI/AAAAAAAAARI/jwiiC8lTbmQ/s72-c/reltypepicklist.JPG' height='72' width='72'/><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3003801964503297388.post-8299915607292253577</id><published>2010-06-15T16:57:00.015+03:00</published><updated>2010-06-15T17:38:19.587+03:00</updated><title type='text'>CRM 4.0 Network Resource Image Control</title><content type='html'>&lt;div&gt;&lt;/div&gt;&lt;br /&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 373px; height: 273px;border:solid 1px black;" src="http://4.bp.blogspot.com/_M-mdv3Tfarg/TBeQMpP8x7I/AAAAAAAAAQw/utbHUw5bcpw/s400/NRImageControl.JPG" border="0" alt=""id="BLOGGER_PHOTO_ID_5483009618252449714" /&gt;&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;As the name suggest this is an on-premise / VPN solution so consider if you need this to work across the web / IFD. The idea is to utilize a simple network share together with VB 6.0 Common Control Open Dialog which can be summoned using JavaScript. The nice thing about the Dialog is that it enables you to see files as thumbnails, set the initial directory and return the selected image path into a text attribute. That’s pretty much what you need to make this work.&lt;br /&gt;&lt;br /&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 268px;border:1px solid black;" src="http://3.bp.blogspot.com/_M-mdv3Tfarg/TBeNcdZQphI/AAAAAAAAAQo/-0vOGoidPus/s400/SelectAvatar.JPG" border="0" alt=""id="BLOGGER_PHOTO_ID_5483006591413298706" /&gt;&lt;br /&gt;&lt;br /&gt;&lt;img style="TEXT-ALIGN: center; MARGIN: 0px auto 10px; WIDTH: 400px; DISPLAY: block; HEIGHT: 251px; CURSOR: hand;border:1px solid black;" id="BLOGGER_PHOTO_ID_5483001761203806306" border="0" alt="" src="http://3.bp.blogspot.com/_M-mdv3Tfarg/TBeJDTdk4GI/AAAAAAAAAQA/I7IWrlEs3Lw/s400/NewContact.JPG" /&gt;&lt;br /&gt;&lt;br /&gt;You can add an ISV toolbar button to pop the dialog but I find that this solution works hand in glove with the text image button post I wrote a while ago.&lt;br /&gt;Here is what is did in a nutshell:&lt;br /&gt;&lt;br /&gt;1. Add a new text attribute. I used the pager attribute on the contact form for the sake of this example.&lt;br /&gt;&lt;br /&gt;2. Add a new Fixed Field 1:1 section to the contact form. This is done so the IFRAME&lt;br /&gt;that displays the image will occupy half the screen.&lt;br /&gt;&lt;br /&gt;&lt;img style="BORDER-BOTTOM: black 1px solid; TEXT-ALIGN: center; BORDER-LEFT: black 1px solid; MARGIN: 0px auto 10px; WIDTH: 354px; DISPLAY: block; HEIGHT: 375px; BORDER-TOP: black 1px solid; CURSOR: hand; BORDER-RIGHT: black 1px solid" id="BLOGGER_PHOTO_ID_5483001777259982626" border="0" alt="" src="http://4.bp.blogspot.com/_M-mdv3Tfarg/TBeJEPRqryI/AAAAAAAAAQQ/pNEzzT2J96M/s400/NewSection.JPG" /&gt;&lt;br /&gt;&lt;br /&gt;3. Then add a the image IFRAME and pointed it to a default blank.jpg image.&lt;br /&gt;&lt;br /&gt;&lt;img style="BORDER-BOTTOM: black 1px solid; TEXT-ALIGN: center; BORDER-LEFT: black 1px solid; MARGIN: 0px auto 10px; WIDTH: 400px; DISPLAY: block; HEIGHT: 245px; BORDER-TOP: black 1px solid; CURSOR: hand; BORDER-RIGHT: black 1px solid" id="BLOGGER_PHOTO_ID_5483001771288589506" border="0" alt="" src="http://3.bp.blogspot.com/_M-mdv3Tfarg/TBeJD5B-dMI/AAAAAAAAAQI/9h_UUrbSdZk/s400/newIframe.JPG" /&gt;&lt;br /&gt;&lt;br /&gt;4. Arrange the form attributes as you like … here is what I did.&lt;br /&gt;&lt;br /&gt;&lt;img style="BORDER-BOTTOM: black 1px solid; TEXT-ALIGN: center; BORDER-LEFT: black 1px solid; MARGIN: 0px auto 10px; WIDTH: 400px; DISPLAY: block; HEIGHT: 259px; BORDER-TOP: black 1px solid; CURSOR: hand; BORDER-RIGHT: black 1px solid" id="BLOGGER_PHOTO_ID_5483001756807201266" border="0" alt="" src="http://4.bp.blogspot.com/_M-mdv3Tfarg/TBeJDDFWAfI/AAAAAAAAAP4/E-O77WpNwhM/s400/CustForm.JPG" /&gt;&lt;br /&gt;&lt;br /&gt;5. Set the path attribute to read-only so selection is possible only when using the dialog.&lt;br /&gt;&lt;br /&gt;6. Add the following code to the contact on load event box.&lt;br /&gt;&lt;br /&gt;And Finally don't forget to create a network share anywhere on the server and set appropriate user permission.&lt;br /&gt;&lt;br /&gt;&lt;img style="BORDER-BOTTOM: black 1px solid; TEXT-ALIGN: center; BORDER-LEFT: black 1px solid; MARGIN: 0px auto 10px; WIDTH: 283px; DISPLAY: block; HEIGHT: 400px; BORDER-TOP: black 1px solid; CURSOR: hand; BORDER-RIGHT: black 1px solid" id="BLOGGER_PHOTO_ID_5483001913262600482" border="0" alt="" src="http://3.bp.blogspot.com/_M-mdv3Tfarg/TBeJMJ7N8SI/AAAAAAAAAQg/J4l_6PofW88/s400/SharedResource.JPG" /&gt;&lt;br /&gt;&lt;br /&gt;That’s it … pretty simple ah … feel free to comment.&lt;br /&gt;&lt;br /&gt;&lt;pre class="js" name="code"&gt;&lt;br /&gt;&lt;br /&gt;TextHelperButton = function(fieldId)&lt;br /&gt;{&lt;br /&gt;    var fldButton = this;&lt;br /&gt;&lt;br /&gt;    fldButton.Field = crmForm.all[fieldId];&lt;br /&gt;&lt;br /&gt;    if (!fldButton.Field)&lt;br /&gt;    {&lt;br /&gt;        return alert("Unknown Field: " + fieldId);&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    fldButton.Click = null;&lt;br /&gt;    fldButton.Image = new ButtonImage();&lt;br /&gt;    fldButton.Paint = function()&lt;br /&gt;    {&lt;br /&gt;        var field_d = document.all[fldButton.Field.id + "_d"];&lt;br /&gt;        if (field_d)&lt;br /&gt;        {&lt;br /&gt;            field_d.style.whiteSpace = "nowrap";&lt;br /&gt;            field_d.appendChild(fldButton.Image.ToObject())&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    fldButton.MouseOver = function()&lt;br /&gt;    {&lt;br /&gt;        event.srcElement.src = fldButton.Image.MouseOver;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    fldButton.MouseOut = function()&lt;br /&gt;    {&lt;br /&gt;        event.srcElement.src = fldButton.Image.MouseOut;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    function ButtonImage()&lt;br /&gt;    {&lt;br /&gt;        this.MouseOut  = "/_imgs/lookupOff.gif";&lt;br /&gt;        this.MouseOver = "/_imgs/lookupOn.gif";&lt;br /&gt;        this.Width = 21&lt;br /&gt;&lt;br /&gt;        this.ToObject = function()&lt;br /&gt;        {&lt;br /&gt;            var img = document.createElement("IMG");&lt;br /&gt;            img.onmouseover = fldButton.MouseOver;&lt;br /&gt;            img.onmouseout = fldButton.MouseOut;&lt;br /&gt;            img.onclick = fldButton.Click;&lt;br /&gt;            img.src = this.MouseOut;&lt;br /&gt; &lt;br /&gt;            var cssText = "vertical-align:bottom;";&lt;br /&gt;            cssText+= "margin:1px;";&lt;br /&gt;            cssText+= "position:relative;";&lt;br /&gt;            cssText+= "right:" + (this.Width + 1) + "px";&lt;br /&gt;            img.style.cssText = cssText;&lt;br /&gt;            return img;&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;function OnCrmPageLoad()&lt;br /&gt;{&lt;br /&gt; /* Build the Avatar path attribute Text Helper Button */&lt;br /&gt;    var avaterBtn = new TextHelperButton("pager");&lt;br /&gt;        avaterBtn.Click = SelectAvatar;&lt;br /&gt;        avaterBtn.Paint();&lt;br /&gt;     &lt;br /&gt;     /* Set the avatar IFRAME when the form loads*/&lt;br /&gt;     if (crmForm.all.pager.DataValue)&lt;br /&gt;     {&lt;br /&gt;  document.all.IFRAME_image.src = crmForm.all.pager.DataValue;&lt;br /&gt;     }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;function SelectAvatar()&lt;br /&gt;{&lt;br /&gt;     var dialog = new ActiveXObject("MSComDlg.CommonDialog");&lt;br /&gt;     /* You may set the filter to only show image files */&lt;br /&gt;     dialog.Filter = "All Files (*.*)";&lt;br /&gt;     /* Point the dialog to the current (selected) image */&lt;br /&gt;     dialog.FileName = document.all.IFRAME_image.src;&lt;br /&gt;     dialog.MaxFileSize = 1024;&lt;br /&gt;     dialog.ShowOpen();&lt;br /&gt;  &lt;br /&gt;     /* Save the readonly path attribute with the new file selection */&lt;br /&gt;     crmForm.all.pager.DataValue = dialog.FileName;&lt;br /&gt;     /* Force submit since this is a readonly attribute */&lt;br /&gt;     crmForm.all.pager.ForceSubmit = true;&lt;br /&gt;     /* Update the image iframe */&lt;br /&gt;     document.all.IFRAME_image.src = crmForm.all.pager.DataValue;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;OnCrmPageLoad();&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3003801964503297388-8299915607292253577?l=mscrm4ever.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mscrm4ever.blogspot.com/feeds/8299915607292253577/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3003801964503297388&amp;postID=8299915607292253577&amp;isPopup=true' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3003801964503297388/posts/default/8299915607292253577'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3003801964503297388/posts/default/8299915607292253577'/><link rel='alternate' type='text/html' href='http://mscrm4ever.blogspot.com/2010/06/crm-40-network-resource-image-control.html' title='CRM 4.0 Network Resource Image Control'/><author><name>Adi Katz</name><uri>http://www.blogger.com/profile/16783428437179825705</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_M-mdv3Tfarg/TBeQMpP8x7I/AAAAAAAAAQw/utbHUw5bcpw/s72-c/NRImageControl.JPG' height='72' width='72'/><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3003801964503297388.post-8136238550686644290</id><published>2010-06-14T18:30:00.004+03:00</published><updated>2010-06-14T18:46:47.897+03:00</updated><title type='text'>CRM 4.0 Formatting Form Fields</title><content type='html'>The following is an intuitive Mask/UnMask JavaScript object. If you’re looking to toggle other clients (other than an entity form) you might use it as a template for a server side plug-in. The object makes it easy to enforce various types of formatting such as phone numbers , a social security number, credit card numbers and many more. The sample below formats the account Main and Other phone fields. &lt;br /&gt;&lt;br /&gt;When a user gives focus to a phone field the object unmasks the field’s value leaving only numbers (the original user input) or blank. Once focus is lost the mask is applied again, if a value exists, giving it its final display. The object also fires when the form is saved to support a situation where focus is not fired i.e. when the user uses the keyboard to save the record. &lt;br /&gt;&lt;br /&gt;There’re also other features such as displaying the format as a title or telling the mask to fire when the form loads. The later is hardly required but you might find it useful to leverage the user interaction with the form … and cleans/reformat imported/external values along the way. &lt;br /&gt;&lt;br /&gt;If you take a closer look you’ll notice that the object receives both a format (mask) such as “(##) ###-####” and a regular expression which is an unmask filter e.g. “\\D”. “\\D” means anything but numbers which is what you need to remove/replace to get the user’s original input.&lt;br /&gt;&lt;br /&gt;The code goes in the account entity onload event.  &lt;br /&gt;Give it a go and feel free to leave comments. &lt;br /&gt;&lt;br /&gt;&lt;pre class="js" name="code"&gt;&lt;br /&gt;&lt;br /&gt;function Mask(format)&lt;br /&gt;{&lt;br /&gt;    var m = this;&lt;br /&gt;   &lt;br /&gt;    /* e.g. (##) ###-#### */&lt;br /&gt;    m.Format = format;&lt;br /&gt;    m.Field  = null;&lt;br /&gt;    /* OnLoad   - Might be Used to cleans imported values. */&lt;br /&gt;    m.OnLoad = false;&lt;br /&gt;    /*&lt;br /&gt;        A regular expression for unwanted (bad) characters. &lt;br /&gt;        e.g. A field (e.g. phone number) original (before formatting) characters must contain numbers only.  &lt;br /&gt;    */&lt;br /&gt;    m.Filter = null;&lt;br /&gt;   &lt;br /&gt;    var onloadbound = false;&lt;br /&gt;    var onsavebound = false;&lt;br /&gt;    var onfocusbound = false;&lt;br /&gt;   &lt;br /&gt;    m.Mask   = function()&lt;br /&gt;    {&lt;br /&gt;        if (!m.Field)&lt;br /&gt;        {&lt;br /&gt;            return alert("Mask is missing a field");&lt;br /&gt;        } &lt;br /&gt; &lt;br /&gt;        /* Sets the textbox title to required Format */&lt;br /&gt;        m.Field.title = m.Format;  &lt;br /&gt; &lt;br /&gt;        if (!onfocusbound)&lt;br /&gt;        {&lt;br /&gt;            onfocusbound = m.Field.attachEvent("onfocusin", unmask);&lt;br /&gt;            m.Field.attachEvent("onfocusout", mask);&lt;br /&gt;        }&lt;br /&gt; &lt;br /&gt;        if (m.OnLoad &amp;&amp; !onloadbound)&lt;br /&gt;        {&lt;br /&gt;            onloadbound = mask() == undefined;&lt;br /&gt;        }&lt;br /&gt; &lt;br /&gt;        if (!onsavebound)&lt;br /&gt;        {&lt;br /&gt;            onsavebound = crmForm.attachEvent("onsave",mask);&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;   &lt;br /&gt;    function unmask()&lt;br /&gt;    {&lt;br /&gt;        m.Field.DataValue = strip();&lt;br /&gt;    }&lt;br /&gt;   &lt;br /&gt;    function mask()&lt;br /&gt;    {&lt;br /&gt;        /* Reformat */&lt;br /&gt;        var formated = m.Format;&lt;br /&gt;        /* value as Array of characters */&lt;br /&gt; &lt;br /&gt;        var splitValue = strip().split("");&lt;br /&gt; &lt;br /&gt;        if (splitValue.length == 0)&lt;br /&gt;        {&lt;br /&gt;            m.Field.DataValue = null;&lt;br /&gt;            return;&lt;br /&gt;        }&lt;br /&gt;  &lt;br /&gt;        /* Regex defining a single placeholder */&lt;br /&gt;        var placeHolderReg = new RegExp("#{1}");&lt;br /&gt;        /* Replace each placeholder with a single character */&lt;br /&gt;        for(var i = 0; i &lt; splitValue.length ; i++)&lt;br /&gt;        {&lt;br /&gt;            formated = formated.replace(placeHolderReg,splitValue[i]);          &lt;br /&gt;        }&lt;br /&gt; &lt;br /&gt;        m.Field.DataValue = formated;&lt;br /&gt;    }&lt;br /&gt;   &lt;br /&gt;    function strip()&lt;br /&gt;    {&lt;br /&gt;        if (!m.Field.DataValue)&lt;br /&gt;        {&lt;br /&gt;            return "";&lt;br /&gt;        }&lt;br /&gt;        /* Strip field from unwanted characters */&lt;br /&gt;        var valueOnlyReg = new RegExp(m.Filter,"gi");&lt;br /&gt;        return m.Field.DataValue.replace(valueOnlyReg,"");&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;function OnCrmPageLoad()&lt;br /&gt;{&lt;br /&gt;    var phoneMask = new Mask("(##)###-####");&lt;br /&gt;    phoneMask.Field = crmForm.all.telephone1;&lt;br /&gt;    phoneMask.Filter = "\\D"; //Unmask Filter - Strip Everything which is not a Number&lt;br /&gt;    phoneMask.Mask();&lt;br /&gt;  &lt;br /&gt;    var cellMask = new Mask("+(##) ### ####");&lt;br /&gt;    cellMask.Field = crmForm.all.telephone2;&lt;br /&gt;    phoneMask.OnLoad = true;&lt;br /&gt;    cellMask.Filter = "\\D"; //Unmask Filter - Strip Everything which is not a Number&lt;br /&gt;    cellMask.Mask();&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;OnCrmPageLoad();&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3003801964503297388-8136238550686644290?l=mscrm4ever.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mscrm4ever.blogspot.com/feeds/8136238550686644290/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3003801964503297388&amp;postID=8136238550686644290&amp;isPopup=true' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3003801964503297388/posts/default/8136238550686644290'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3003801964503297388/posts/default/8136238550686644290'/><link rel='alternate' type='text/html' href='http://mscrm4ever.blogspot.com/2010/06/crm-40-formatting-form-fields.html' title='CRM 4.0 Formatting Form Fields'/><author><name>Adi Katz</name><uri>http://www.blogger.com/profile/16783428437179825705</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3003801964503297388.post-6447954919903725674</id><published>2009-11-21T04:26:00.006+02:00</published><updated>2009-11-21T11:16:04.020+02:00</updated><title type='text'>CRM 4.0 Read Only Iframe - A better solution</title><content type='html'>&lt;div&gt;&lt;/div&gt;&lt;br /&gt;&lt;a href="http://1.bp.blogspot.com/_M-mdv3Tfarg/SwdP-i79YaI/AAAAAAAAAO8/a1EjvGQD1Yg/s1600/iframero.JPG"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 168px;border:1px solid black;" src="http://1.bp.blogspot.com/_M-mdv3Tfarg/SwdP-i79YaI/AAAAAAAAAO8/a1EjvGQD1Yg/s400/iframero.JPG" border="0" alt=""id="BLOGGER_PHOTO_ID_5406377813630345634" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;I already posted about creating a read only iframe in the past but the solution offered here is by far better and integrates nicely with CRM.&lt;br /&gt;The idea, in a nut shell, is to create an upper blocking layer inside the iframe. The layer is decorated with an alpha filter which makes it look disabled and also presents a watermark text to the user. The helper object also accepts an alert text that is shown to the user when he tries to click inside the frame.&lt;br /&gt;Feel free to change the opacity, colors and fonts for best appearance. This code, as usual, should be pasted into the onload event handler and published.&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;&lt;pre class="js" name="code"&gt;&lt;br /&gt;&lt;br /&gt;function IframeReadOnlyHelper(sIframeId)&lt;br /&gt;{&lt;br /&gt;    var iro = this;&lt;br /&gt;    iro.Iframe = document.getElementById(sIframeId);&lt;br /&gt;    if (!iro.Iframe)&lt;br /&gt;    {&lt;br /&gt;        alert("IFRAME with id " + sIframeId + " is missing");&lt;br /&gt;    }&lt;br /&gt; &lt;br /&gt;    iro.WatermarkText = "";&lt;br /&gt;    iro.AlertMessage = "";&lt;br /&gt; &lt;br /&gt;    iro.OnIframeReady = function()&lt;br /&gt;    {&lt;br /&gt;        if (iro.Iframe.readyState != 'complete')&lt;br /&gt;        {&lt;br /&gt;            return;&lt;br /&gt;        }&lt;br /&gt;  &lt;br /&gt;        var iframeDoc = iro.Iframe.contentWindow.document;&lt;br /&gt;        var iframeBody = iframeDoc.body;&lt;br /&gt;        var roSpan = iframeDoc.createElement("SPAN");&lt;br /&gt;  &lt;br /&gt;        var roStyle = "overflow:hidden;";&lt;br /&gt;        roStyle += "position:absolute;";&lt;br /&gt;        roStyle += "z-index:1;";&lt;br /&gt;        roStyle += "left:0px;";&lt;br /&gt;        roStyle += "top:0px;";&lt;br /&gt;        roStyle += "height:100%;";&lt;br /&gt;        roStyle += "text-align:center;";&lt;br /&gt;        roStyle += "background-color:gray;";&lt;br /&gt;        roStyle += "font:72px Tahoma;";&lt;br /&gt;        roStyle += "filter:alpha(opacity=20)";&lt;br /&gt;  &lt;br /&gt;        roSpan.style.cssText = roStyle;&lt;br /&gt;  &lt;br /&gt;        iframeBody.appendChild(roSpan);&lt;br /&gt;  &lt;br /&gt;        if (iro.WatermarkText != "")&lt;br /&gt;        {&lt;br /&gt;            roSpan.innerHTML = "&amp;lt;div style='height:100;'&amp;gt;&amp;lt;br&amp;gt;" + iro.WatermarkText + "&amp;lt;/div&amp;gt;";&lt;br /&gt;        }&lt;br /&gt;  &lt;br /&gt;        if (iro.AlertMessage != "")&lt;br /&gt;        {&lt;br /&gt;            roSpan.attachEvent("onclick", function(){alert(iro.AlertMessage);});&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt; &lt;br /&gt;    iro.Disable = function()&lt;br /&gt;    {&lt;br /&gt;        iro.Iframe.onreadystatechange = iro.OnIframeReady;&lt;br /&gt;        iro.OnIframeReady();&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;function OnCrmPageLoad()&lt;br /&gt;{&lt;br /&gt;    //Load IFRAME with any URL&lt;br /&gt;    var iframeActivity = document.all.IFRAME_account_association;&lt;br /&gt;    iframeActivity.src = "areas.aspx?oId=%7b50387202-6F73-DE11-9F19-0003FF230264%7d&amp;oType=1&amp;security=852023&amp;tabSet=areaActivities";&lt;br /&gt;    &lt;br /&gt;    //Use IframeReadOnlyHelper &lt;br /&gt;    var roIframe = new IframeReadOnlyHelper("IFRAME ID");&lt;br /&gt;    roIframe.WatermarkText = "Read Only";&lt;br /&gt;    roIframe.AlertMessage = "This Grid is Disabled";&lt;br /&gt;    roIframe.Disable();&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;OnCrmPageLoad();&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3003801964503297388-6447954919903725674?l=mscrm4ever.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mscrm4ever.blogspot.com/feeds/6447954919903725674/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3003801964503297388&amp;postID=6447954919903725674&amp;isPopup=true' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3003801964503297388/posts/default/6447954919903725674'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3003801964503297388/posts/default/6447954919903725674'/><link rel='alternate' type='text/html' href='http://mscrm4ever.blogspot.com/2009/11/crm-40-read-only-iframe-better-solution.html' title='CRM 4.0 Read Only Iframe - A better solution'/><author><name>Adi Katz</name><uri>http://www.blogger.com/profile/16783428437179825705</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_M-mdv3Tfarg/SwdP-i79YaI/AAAAAAAAAO8/a1EjvGQD1Yg/s72-c/iframero.JPG' height='72' width='72'/><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3003801964503297388.post-279359427499697976</id><published>2009-10-11T13:06:00.011+02:00</published><updated>2010-07-21T12:06:24.805+03:00</updated><title type='text'>CRM 4.0 Make your announcements come to life</title><content type='html'>&lt;div&gt;&lt;/div&gt;&lt;br /&gt;&lt;a href="http://1.bp.blogspot.com/_M-mdv3Tfarg/StG8bfGsCYI/AAAAAAAAAOk/Bp2eVOVhCo4/s1600-h/htmlann.JPG"&gt;&lt;img alt="" border="0" id="BLOGGER_PHOTO_ID_5391297409331235202" src="http://1.bp.blogspot.com/_M-mdv3Tfarg/StG8bfGsCYI/AAAAAAAAAOk/Bp2eVOVhCo4/s400/htmlann.JPG" style="border-bottom: black 1px solid; border-left: black 1px solid; border-right: black 1px solid; border-top: black 1px solid; cursor: hand; display: block; height: 127px; margin: 0px auto 10px; text-align: center; width: 400px;" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;CRM has a nice feature (or at least a decent idea) called announcements which allows you to integrate and share textual messages with all CRM users.&lt;br /&gt;The main issue with announcements is that the body or description field does not interprets hyper text. &lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;Now wouldn’t it be nice to be able to integrate reach html messages into CRM using announcements. Although you can easily create your own reach text announcement mechanism and integrate it into CRM site map I actually find it more appealing leveraging an existing feature.&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;In order to force the announcement page support HTML is made a simple modification to the home / homepage / home_news.aspx. now, since modifying CRM files is unsupported consider the ramification before actually doing so. &lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;The home_news.aspx page contains html that looks like this:&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;&lt;pre class="html" name="code"&gt;&amp;lt;body&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;table width="100%" height="100%"&amp;gt;&lt;br /&gt;&amp;lt;tr&amp;gt;&lt;br /&gt;&amp;lt;td&amp;gt;&lt;br /&gt;&amp;lt;table width="100%" height="100%" cellspacing="0" cellpadding="0" border="0"&amp;gt;&lt;br /&gt;&amp;lt;tr&amp;gt;&lt;br /&gt;&amp;lt;td&amp;gt;&lt;br /&gt;&amp;lt;div style="width:100%;height:100%;overflow-y:scroll;padding:10px; border: 1px solid #cccccc; background-color:#ffffff;"&amp;gt;&lt;br /&gt;&amp;lt;table width="100%" height="100%" cellspacing="2" cellpadding="3" border="0" style="table-layout:fixed;"&amp;gt;&lt;br /&gt;&amp;lt;col width="20"&amp;gt;&amp;lt;col&amp;gt;&lt;br /&gt;&amp;lt;% =RenderAnnouncements(false) %&amp;gt;&lt;br /&gt;&amp;lt;tr height="100%" colspan="2"&amp;gt;&amp;lt;td&amp;gt;&amp;nbsp;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;&amp;lt;/table&amp;gt;&lt;br /&gt;&amp;lt;/div&amp;gt;&lt;br /&gt;&amp;lt;/td&amp;gt;&lt;br /&gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;&amp;lt;/table&amp;gt;&lt;br /&gt;&amp;lt;/td&amp;gt;&lt;br /&gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;&amp;lt;/table&amp;gt;&lt;br /&gt;&amp;lt;/body&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;If you take a closer look at the generated html (using iedevtoolbar) you’ll notice that the announcement messages are rendered as table rows. I wanted to make as little change to this page as possible and still support HTML. In order to do so I used a simple technique which wraps the inner body content (table element) in a textarea and reads the textarea value into a new span element as innerHTML.&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;Here is how the page looks like after the modification is made:&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;&lt;pre class="html" name="code"&gt;&amp;lt;body&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;textarea style="display:none" id="anntext"&amp;gt;&lt;br /&gt;&amp;lt;table width="100%" height="100%"&amp;gt;&lt;br /&gt;&amp;lt;tr&amp;gt;&lt;br /&gt;&amp;lt;td&amp;gt;&lt;br /&gt;&amp;lt;table width="100%" height="100%" cellspacing="0" cellpadding="0" border="0"&amp;gt;&lt;br /&gt;&amp;lt;tr&amp;gt;&lt;br /&gt;&amp;lt;td&amp;gt;&lt;br /&gt;&amp;lt;div style="width:100%;height:100%;overflow-y:scroll;padding:10px; border: 1px solid #cccccc; background-color:#ffffff;"&amp;gt;&lt;br /&gt;&amp;lt;table width="100%" height="100%" cellspacing="2" cellpadding="3" border="0" style="table-layout:fixed;"&amp;gt;&lt;br /&gt;&amp;lt;col width="20"&amp;gt;&amp;lt;col&amp;gt;&lt;br /&gt;&amp;lt;% =RenderAnnouncements(false) %&amp;gt;&lt;br /&gt;&amp;lt;tr height="100%" colspan="2"&amp;gt;&amp;lt;td&amp;gt;&amp;nbsp;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;&amp;lt;/table&amp;gt;&lt;br /&gt;&amp;lt;/div&amp;gt;&lt;br /&gt;&amp;lt;/td&amp;gt;&lt;br /&gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;&amp;lt;/table&amp;gt;&lt;br /&gt;&amp;lt;/td&amp;gt;&lt;br /&gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;&amp;lt;/table&amp;gt;&lt;br /&gt;&amp;lt;/textarea&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;span id="annhtml"&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;&amp;lt;script&amp;gt; &lt;br /&gt;window.document.all.annhtml.innerHTML = document.all.anntext.value;&lt;br /&gt;&amp;lt;/script&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;/body&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;One thing that you need to minded of is that the html you paste in the announcement body should be a one liner. This is because the line breaks are interpreted as br elements. &lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;That’s all, enjoy your new announcement feature.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3003801964503297388-279359427499697976?l=mscrm4ever.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mscrm4ever.blogspot.com/feeds/279359427499697976/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3003801964503297388&amp;postID=279359427499697976&amp;isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3003801964503297388/posts/default/279359427499697976'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3003801964503297388/posts/default/279359427499697976'/><link rel='alternate' type='text/html' href='http://mscrm4ever.blogspot.com/2009/10/crm-40-make-your-announcements-come-to.html' title='CRM 4.0 Make your announcements come to life'/><author><name>Adi Katz</name><uri>http://www.blogger.com/profile/16783428437179825705</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_M-mdv3Tfarg/StG8bfGsCYI/AAAAAAAAAOk/Bp2eVOVhCo4/s72-c/htmlann.JPG' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3003801964503297388.post-1025015802856613141</id><published>2009-10-11T01:27:00.005+02:00</published><updated>2009-10-11T02:04:54.493+02:00</updated><title type='text'>CRM 4.0 cloning using entity mapping</title><content type='html'>&lt;div&gt;&lt;/div&gt;&lt;br /&gt;&lt;a href="http://4.bp.blogspot.com/_M-mdv3Tfarg/StEhCv42Z_I/AAAAAAAAAOc/_356DfldiIA/s1600-h/endresult.JPG"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 26px;border:1px solid black;" src="http://4.bp.blogspot.com/_M-mdv3Tfarg/StEhCv42Z_I/AAAAAAAAAOc/_356DfldiIA/s400/endresult.JPG" border="0" alt=""id="BLOGGER_PHOTO_ID_5391126560037562354" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;In the past I wrote a few posts about cloning an entity using client side scripting. Most of the posts implemented complex script especially to non developers. I wanted to show you a simple cloning technique that leverages CRM built in features and can also work for CRM online. &lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;The benefits of using this technique are: &lt;br /&gt;1. Gain full control over which attributes are cloned&lt;br /&gt;2. Ability to change which attributes are cloned without adding / changing your code.&lt;br /&gt;3. Usage of a very simple script that does not need to be changed when reused.&lt;br /&gt;4. Ability to easily track the parent record from which the cloned entity originated.&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;Following is the list of built in CRM features I am going to utilize in the post:&lt;br /&gt;1. Creation of CRM form toolbar button &lt;br /&gt;2. Creation of entity relationship. &lt;br /&gt;3. Creation of mapping between related entities.&lt;br /&gt;4. Adding simple script to the cloned entity onload  handler.&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;So how can we actually make the above features work to our advantage.  The main feature that lies in the heart of this technique is the ability to create a self referencing relationship between the an entity to itself and use the mapping wizard to tell CRM which attributes we what to pass from the parent record. Once we have that all that remains is to understand how CRM uses the mapping on the client side much like when you create a child contact record from within an account from. &lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;If you take a close look at the URL that is used by CRM when you create a child related record you’ll notice that the URL uses a specific format that tells CRM what is the parent record id and type. Once you learn how to replicate this behavior you’re half way implementing this solution. &lt;br /&gt;The following script which you’ll eventually need to paste in the entity onload event handler shows how to construct the child record or in our case the cloned record url:&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;&lt;pre class="js" name="code"&gt;&lt;br /&gt;Clone = function()&lt;br /&gt;{&lt;br /&gt;    var cloneUrl  = location.pathname + "?";&lt;br /&gt;        cloneUrl += "_CreateFromType=" + crmForm.ObjectTypeCode + &lt;br /&gt;        cloneUrl += "&amp;_CreateFromId=" + crmForm.ObjectId + &lt;br /&gt;        cloneUrl += "&amp;etc=" + crmForm.ObjectTypeCode + "#";&lt;br /&gt;        &lt;br /&gt;    var cloneFeatures = 'toolbars=0,status=1,width=' + document.body.offsetWidth + "height=" + document.body.offsetHeight; &lt;br /&gt;    &lt;br /&gt;    window.open(cloneUrl,'',cloneFeatures);&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;Once we have the script in place we need to add a toolbar button that will fire the actual cloning process. &lt;br /&gt;Following is a sample clone button xml which you should add to your isv.config &lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;&lt;pre class="xml" name="code"&gt;&lt;br /&gt; &amp;lt;Entity name="gi_test"&amp;gt;&lt;br /&gt;   &amp;lt;ToolBar ValidForCreate="0" ValidForUpdate="1"&amp;gt;&lt;br /&gt;      &amp;lt;Button Icon="/_imgs/ico_18_debug.gif" JavaScript="Clone();"&amp;gt;&lt;br /&gt;         &amp;lt;Titles&amp;gt;&lt;br /&gt;            &amp;lt;Title LCID="1033" Text="Clone" /&amp;gt;&lt;br /&gt;         &amp;lt;/Titles&amp;gt;&lt;br /&gt;         &amp;lt;ToolTips&amp;gt;&lt;br /&gt;            &amp;lt;ToolTip LCID="1033" Text="Clone" /&amp;gt;&lt;br /&gt;         &amp;lt;/ToolTips&amp;gt;&lt;br /&gt;      &amp;lt;/Button&amp;gt;&lt;br /&gt;      &amp;lt;ToolBarSpacer /&amp;gt;&lt;br /&gt;   &amp;lt;/ToolBar&amp;gt;&lt;br /&gt; &amp;lt;/Entity&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;Next you’ll need to create a self referencing relationship. To do that open the entity customization and select 1:N relationship. Then select create new relationship. Inside the relationship form choose the same entity on both sides of the relationship. You should select not to display the left navigation link. The bellow image encapsulates the process of creating the self referencing relationship. &lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;&lt;a href="http://3.bp.blogspot.com/_M-mdv3Tfarg/StEaCE_TEBI/AAAAAAAAAOE/OeVRtlcsiK4/s1600-h/selfref.JPG"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 322px;border:1px solid black;" src="http://3.bp.blogspot.com/_M-mdv3Tfarg/StEaCE_TEBI/AAAAAAAAAOE/OeVRtlcsiK4/s400/selfref.JPG" border="0" alt=""id="BLOGGER_PHOTO_ID_5391118851940487186" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;Once the relationship is saved you’ll see a mapping link on the relationship form. Select the mapping link to open the mapping wizard.&lt;br /&gt;Add as much mapping as required. Before you publish the entity consider the final step.&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;&lt;a href="http://4.bp.blogspot.com/_M-mdv3Tfarg/StEacZTrmkI/AAAAAAAAAOM/4ku4mzdr4Bc/s1600-h/mapping.JPG"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 45px;border:1px solid black;" src="http://4.bp.blogspot.com/_M-mdv3Tfarg/StEacZTrmkI/AAAAAAAAAOM/4ku4mzdr4Bc/s400/mapping.JPG" border="0" alt=""id="BLOGGER_PHOTO_ID_5391119304071289410" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;Finally, if you wish to track the originating record you only need to add the entity lookup (parent test in this case) that was created as a result of the self referencing relationship. You can also set the lookup field as read only so users can’t change it manually.&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;&lt;a href="http://1.bp.blogspot.com/_M-mdv3Tfarg/StEbn_SWSwI/AAAAAAAAAOU/m6mCgonsjOY/s1600-h/originatingrecord.JPG"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 205px;border:1px solid black;" src="http://1.bp.blogspot.com/_M-mdv3Tfarg/StEbn_SWSwI/AAAAAAAAAOU/m6mCgonsjOY/s400/originatingrecord.JPG" border="0" alt=""id="BLOGGER_PHOTO_ID_5391120602756434690" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;That’s it! publish your entity and you’re done.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3003801964503297388-1025015802856613141?l=mscrm4ever.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mscrm4ever.blogspot.com/feeds/1025015802856613141/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3003801964503297388&amp;postID=1025015802856613141&amp;isPopup=true' title='32 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3003801964503297388/posts/default/1025015802856613141'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3003801964503297388/posts/default/1025015802856613141'/><link rel='alternate' type='text/html' href='http://mscrm4ever.blogspot.com/2009/10/crm-40-cloning-using-entity-mapping.html' title='CRM 4.0 cloning using entity mapping'/><author><name>Adi Katz</name><uri>http://www.blogger.com/profile/16783428437179825705</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_M-mdv3Tfarg/StEhCv42Z_I/AAAAAAAAAOc/_356DfldiIA/s72-c/endresult.JPG' height='72' width='72'/><thr:total>32</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3003801964503297388.post-1791789694167766551</id><published>2009-10-10T10:27:00.006+02:00</published><updated>2009-10-10T11:54:15.114+02:00</updated><title type='text'>CRM 4.0 Creating Inline Toolbar and Buttons</title><content type='html'>&lt;div&gt;&lt;/div&gt;&lt;br /&gt;Here is a nice usability feature that I really like. Currently CRM 4.0 only supports adding functional buttons via form toolbar. This suffices most of the time and mainly on strait forward data input forms. But as CRM takes giant leaps toward becoming a xRM platform, as an application architect and designer, you bow to search for more flexible ways to convey the system to the end user. &lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;The following post presents a simple and effective way of adding an inline toolbar buttons at the section level. This is especially useful when creating complex data entry forms like designers and wizards that require multi-step / section oriented logic. It is also much more simpler to add a button to the form then going through the entire isv.config process. &lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;Adding an inline toolbar to the form is pretty simple and involves 2 steps. &lt;br /&gt;The first step is to add a new text field to the form, where you want the toolbar to appear (e.g. gi_toolbar) and hide it’s label through the form field customizations (i.e. double click on the field and uncheck display label on form checkbox).&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;Here is how it looks after the above step is completed:&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 212px;border:1px solid black;" src="http://3.bp.blogspot.com/_M-mdv3Tfarg/StBGSYmLpMI/AAAAAAAAAN0/t_2hSc1Bwd0/s400/Cusotmization.JPG" border="0" alt=""id="BLOGGER_PHOTO_ID_5390886035616670914" /&gt;&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;The final step is to add the following code to the entity onload event handler and add an OnCrmPageLoad function which creates a new instance of InlineToolbar and adds the necessary buttons.&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;The end result looks like this:&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 57px;border:1px solid black;" src="http://2.bp.blogspot.com/_M-mdv3Tfarg/StBGSrhhkKI/AAAAAAAAAN8/l9QCmfk_3Lo/s400/Runtime.JPG" border="0" alt=""id="BLOGGER_PHOTO_ID_5390886040697409698" /&gt;&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre class="js" name="code"&gt;&lt;br /&gt;function InlineToolbar(containerId)&lt;br /&gt;{&lt;br /&gt;    var toolbar = this;&lt;br /&gt;    var container = document.all[containerId];&lt;br /&gt;    &lt;br /&gt;    if (!container)&lt;br /&gt;    {&lt;br /&gt;        return alert("Toolbar Field: " + containerId + " is missing");&lt;br /&gt;    }&lt;br /&gt;    &lt;br /&gt;    container.style.display = "none";&lt;br /&gt;    container = container.parentElement;&lt;br /&gt;    &lt;br /&gt;    toolbar.AddButton = function(id,text,width,callback,imgSrc)&lt;br /&gt;    {&lt;br /&gt;        var btn = document.createElement("button");&lt;br /&gt;        var btStyle = new StyleBuilder();&lt;br /&gt;         btStyle.Add( "font-family" , "Arial" );&lt;br /&gt;         btStyle.Add( "font-size" , "12px" );&lt;br /&gt;         btStyle.Add( "line-height" , "16px" );&lt;br /&gt;         btStyle.Add( "text-align" , "center" );&lt;br /&gt;         btStyle.Add( "cursor" , "hand" );&lt;br /&gt;         btStyle.Add( "border" , "1px solid #3366CC" );&lt;br /&gt;         btStyle.Add( "background-color" , "#CEE7FF" );&lt;br /&gt;         btStyle.Add( "background-image" , "url( '/_imgs/btn_rest.gif' )" );&lt;br /&gt;         btStyle.Add( "background-repeat" , "repeat-x" );&lt;br /&gt;         btStyle.Add( "padding-left" , "5px" );&lt;br /&gt;         btStyle.Add( "padding-right" , "5px" );&lt;br /&gt;         btStyle.Add( "overflow" , "visible" );&lt;br /&gt;         btStyle.Add( "width" , width );&lt;br /&gt;          &lt;br /&gt;     btn.style.cssText = btStyle.ToString();&lt;br /&gt;     btn.attachEvent("onclick",callback);&lt;br /&gt;     btn.id = id;&lt;br /&gt;     &lt;br /&gt;     if (imgSrc)&lt;br /&gt;     { &lt;br /&gt;         var img = document.createElement("img");&lt;br /&gt;            img.src = imgSrc;&lt;br /&gt;            img.style.verticalAlign = "middle";&lt;br /&gt;            btn.appendChild(img);&lt;br /&gt;            btn.appendChild(document.createTextNode(" "));&lt;br /&gt;            var spn = document.createElement("span");&lt;br /&gt;            spn.innerText = text;&lt;br /&gt;         btn.appendChild(spn);&lt;br /&gt;     }&lt;br /&gt;     else&lt;br /&gt;     {&lt;br /&gt;         btn.innerText = text;&lt;br /&gt;     }&lt;br /&gt;    &lt;br /&gt;     container.appendChild(btn);&lt;br /&gt;     container.appendChild(document.createTextNode(" "));&lt;br /&gt;    &lt;br /&gt;     return btn;&lt;br /&gt;    }&lt;br /&gt;   &lt;br /&gt;    toolbar.RemoveButton = function(id)&lt;br /&gt;    {&lt;br /&gt;        var btn = toolbar.GetButton(id)&lt;br /&gt;        if (btn)&lt;br /&gt;        {&lt;br /&gt;            btn.parentNode.removeChild(btn);&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;    &lt;br /&gt;    toolbar.GetButton = function(id)&lt;br /&gt;    {&lt;br /&gt;        return document.getElementById(id);&lt;br /&gt;    }&lt;br /&gt;   &lt;br /&gt;    function StyleBuilder()&lt;br /&gt;    {&lt;br /&gt;     var cssText   = new StringBuilder();&lt;br /&gt;     this.Add      = function( key , value ){cssText.Append( key ).Append( ":" ).Append( value ).Append( ";" );}&lt;br /&gt;     this.ToString = function(){return cssText.ToString();} &lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    function StringBuilder()&lt;br /&gt;    {&lt;br /&gt;     var parts     = [];&lt;br /&gt;     this.Append   = function( text ){parts[ parts.length ] = text;return this;}&lt;br /&gt;     this.Reset    = function(){parts = [];}&lt;br /&gt;     this.ToString = function(){return parts.join( "" );}&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;/* Start Script Execution */&lt;br /&gt;function OnCrmPageLoad()&lt;br /&gt;{&lt;br /&gt;    window.GeneralToolbar = new InlineToolbar("gi_toolbar");&lt;br /&gt;    GeneralToolbar.AddButton("btnReset","Reset","15%",Reset_Click);&lt;br /&gt;    GeneralToolbar.AddButton("btnLookup","Lookup","10%",Lookup_Click);&lt;br /&gt;    //GeneralToolbar.RemoveButton("btnLookup");&lt;br /&gt;    GeneralToolbar.AddButton("btnAddNote","Create Note","16px",AddNote_Click,"/_imgs/ico_16_5_d.gif");&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;function Reset_Click()&lt;br /&gt;{&lt;br /&gt;    alert('Reseting Fields...');&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;function Lookup_Click()&lt;br /&gt;{&lt;br /&gt;    alert('lookup records...');&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;function AddNote_Click()&lt;br /&gt;{&lt;br /&gt;    alert('Add new note');&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3003801964503297388-1791789694167766551?l=mscrm4ever.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mscrm4ever.blogspot.com/feeds/1791789694167766551/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3003801964503297388&amp;postID=1791789694167766551&amp;isPopup=true' title='9 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3003801964503297388/posts/default/1791789694167766551'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3003801964503297388/posts/default/1791789694167766551'/><link rel='alternate' type='text/html' href='http://mscrm4ever.blogspot.com/2009/10/crm-40-creating-inline-toolbar-and.html' title='CRM 4.0 Creating Inline Toolbar and Buttons'/><author><name>Adi Katz</name><uri>http://www.blogger.com/profile/16783428437179825705</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_M-mdv3Tfarg/StBGSYmLpMI/AAAAAAAAAN0/t_2hSc1Bwd0/s72-c/Cusotmization.JPG' height='72' width='72'/><thr:total>9</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3003801964503297388.post-4949395491046966084</id><published>2009-09-29T13:59:00.005+02:00</published><updated>2009-09-29T14:07:01.698+02:00</updated><title type='text'>CRM 4.0 Deploying a custom assembly</title><content type='html'>&lt;div&gt;&lt;/div&gt;&lt;br /&gt;The most common and suggested deployment scenario for custom CRM web extensions (i.e. custom web service or web application) is to deploy them under the ISV folder (e.g. ISV / MyApp) and put the application assembly inside the CRMWeb \ bin folder (or wwwroot / bin when deployed on the default website). &lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;As of rollup2 MS also recommends putting the product assemblies of your custom extensions in their own bin folder (i.e. ISV / MyApp / bin). This option also requires you to add an assembly directive &lt;br /&gt;&lt;pre class="xml" name="code"&gt;&lt;br /&gt;&lt;%assembly name=”MyApp” %&gt;&lt;br /&gt;&lt;/pre&gt; &lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;to all your pages so the asp.net will be able to bind to your server side code behind. &lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;Here is an example:&lt;br /&gt;Test.aspx&lt;br /&gt;&lt;pre class="xml" name="code"&gt;&lt;br /&gt;&lt;%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Test.aspx.cs" Inherits="MyApp.Test" %&gt;&lt;br /&gt;&lt;%@ Assembly Name="MyApp" %&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;Deployment Tree:&lt;br /&gt;&lt;pre class="xml" name="code"&gt;&lt;br /&gt;CRMWeb \ wwwroot&lt;br /&gt;    |-- ISV &lt;br /&gt;          |-- MyApp&lt;br /&gt;                  |-- bin&lt;br /&gt;                        |-- MyApp.dll&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;Looks simple enough but many find that this does not work and produces the error &lt;br /&gt;&lt;pre class="xml" name="code"&gt;&lt;br /&gt;[HttpException: Could not load type 'MyApp.Test'].&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;The reason this does not work is that in order for asp.net to recognize and load your assembly the assembly directive must be the first directive in the page e.g. &lt;br /&gt;&lt;pre class="xml" name="code"&gt;&lt;br /&gt;&lt;%@ Assembly Name="MyApp" %&gt;&lt;br /&gt;&lt;%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Test.aspx.cs" Inherits="MyApp.Test" %&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;And lastly a few tips to ensure successful deployment for both IFD and On-Premise environments:&lt;br /&gt;Ensure that the MyApp folder is a regular Virtual Directory (not an application) – in other word your app will run under CRMAppPool.&lt;br /&gt;Remove any application level nodes from your application web.config e.g. &lt;br /&gt;&lt;pre class="xml" name="code"&gt;&lt;br /&gt;&lt;authentication mode="Windows" /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3003801964503297388-4949395491046966084?l=mscrm4ever.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mscrm4ever.blogspot.com/feeds/4949395491046966084/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3003801964503297388&amp;postID=4949395491046966084&amp;isPopup=true' title='13 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3003801964503297388/posts/default/4949395491046966084'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3003801964503297388/posts/default/4949395491046966084'/><link rel='alternate' type='text/html' href='http://mscrm4ever.blogspot.com/2009/09/crm-40-deploying-custom-assembly.html' title='CRM 4.0 Deploying a custom assembly'/><author><name>Adi Katz</name><uri>http://www.blogger.com/profile/16783428437179825705</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>13</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3003801964503297388.post-6087610205708966824</id><published>2009-09-13T14:06:00.005+03:00</published><updated>2009-09-13T21:15:21.973+03:00</updated><title type='text'>CRM 4.0 Records Per Page Wizard</title><content type='html'>&lt;div&gt;&lt;/div&gt;&lt;br /&gt;&lt;a href="http://1.bp.blogspot.com/_M-mdv3Tfarg/SqzS7ltsnBI/AAAAAAAAANs/jAhqUzddt1Q/s1600-h/RPP.JPG"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 271px;border:1px solid black;" src="http://1.bp.blogspot.com/_M-mdv3Tfarg/SqzS7ltsnBI/AAAAAAAAANs/jAhqUzddt1Q/s400/RPP.JPG" border="0" alt=""id="BLOGGER_PHOTO_ID_5380907575978794002" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;A few months back I answered a &lt;a href="http://social.microsoft.com/forums/en-us/crm/thread/ad6b29b9-279b-4f62-8cef-b5bca53c7106"&gt;thread on ms forums&lt;/a&gt; regarding running a workflow on more then 250 records which is the grid paging limit. I trimmed our record per page wizard and made it available for free on GI company website -&gt; free wizards (left navigation).&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;If you’re looking for a cool productivity enhancement that lets you control how many records are displays per entity this one is surly something any user can appreciate.  Feel free to send us feedbacks (feedback@gicrm.com).&lt;br /&gt;&lt;br /&gt;RPP Wizard features:&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;Runs from CRM main application toolbar.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Identifies the current entity being browsed and loads with user the settings (either CRM paginglimit or saved setting).&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Refreshes the current entity view.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Supports all views including associated views.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Supports quick and advanced find.&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3003801964503297388-6087610205708966824?l=mscrm4ever.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mscrm4ever.blogspot.com/feeds/6087610205708966824/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3003801964503297388&amp;postID=6087610205708966824&amp;isPopup=true' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3003801964503297388/posts/default/6087610205708966824'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3003801964503297388/posts/default/6087610205708966824'/><link rel='alternate' type='text/html' href='http://mscrm4ever.blogspot.com/2009/09/crm-40-records-per-page-wizard.html' title='CRM 4.0 Records Per Page Wizard'/><author><name>Adi Katz</name><uri>http://www.blogger.com/profile/16783428437179825705</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_M-mdv3Tfarg/SqzS7ltsnBI/AAAAAAAAANs/jAhqUzddt1Q/s72-c/RPP.JPG' height='72' width='72'/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3003801964503297388.post-4670164621257494848</id><published>2009-09-10T08:01:00.005+03:00</published><updated>2009-09-12T01:37:30.232+03:00</updated><title type='text'>CRM 4.0 Running an On Demand Workflow from JavaScript</title><content type='html'>&lt;div&gt;&lt;/div&gt;&lt;br /&gt;&lt;a href="http://3.bp.blogspot.com/_M-mdv3Tfarg/SqiOdLPw9uI/AAAAAAAAANk/C2kwYqs9POE/s1600-h/pic.JPG"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 148px;border:1px solid black" src="http://3.bp.blogspot.com/_M-mdv3Tfarg/SqiOdLPw9uI/AAAAAAAAANk/C2kwYqs9POE/s400/pic.JPG" border="0" alt=""id="BLOGGER_PHOTO_ID_5379706386780321506" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;Running a workflow from JavaScript is a great alternative to developing server side (web services) components. This is since the workflow designer is able to accomplish many programmable tasks without actually writing complex dot.net code. Implementing a simple JS framework that activates workflows is a great enhancement to any CRM project and can simplify many of your tasks. &lt;br /&gt;&lt;br /&gt;The code integrates the notification (yellow ribbon) as a textual progress bar so the user can observe the progress of his actions. &lt;br /&gt;The code also exposes a few properties that control the interval (in milliseconds) that checks the WF status, whether to refresh the page automatically or not and so forth.&lt;br /&gt;&lt;br /&gt;The OnCrmPageLoad function below demonstrates the usage of the OnDemandWorkflow class. Paste the entire code in your onload event&lt;br /&gt;and set the OnDemandWorkflow instance with the correct workflow id and properties.&lt;br /&gt;&lt;br /&gt;Don’t forget to check your workflow as On Demand if you need to run them from code. You should also consider the logic that implements the client side call since user interaction can cause your workflow to run more then once.&lt;br /&gt;&lt;br /&gt;Enjoy…&lt;br /&gt;&lt;br /&gt;&lt;pre class="js" name="code"&gt;&lt;br /&gt;function OnDemandWorkflow()&lt;br /&gt;{&lt;br /&gt;   var odw = this;&lt;br /&gt;   var request = null;&lt;br /&gt; &lt;br /&gt;   odw.DefaultProgress = new ProgressInfo();&lt;br /&gt;   odw.WorkflowId  = "";&lt;br /&gt;   odw.OperationId = "";&lt;br /&gt;   odw.EntityId = "";&lt;br /&gt;   odw.Name = "";&lt;br /&gt;   odw.WorkflowStatus = -1;&lt;br /&gt;   odw.OnFinishCallback = null;&lt;br /&gt;&lt;br /&gt;   function UpdateNotification(msg)&lt;br /&gt;   {&lt;br /&gt;      if (!odw.DefaultProgress.Visible)&lt;br /&gt;      {&lt;br /&gt;         return;  &lt;br /&gt;      }&lt;br /&gt;  &lt;br /&gt;      var notification = document.all.Notifications;&lt;br /&gt;      notification.style.display = "inline";&lt;br /&gt;      notification.style.width = "100%";&lt;br /&gt;      notification.style.height = "20px";&lt;br /&gt;      notification.innerHTML = odw.Name + ": &lt;b&gt;" + msg + "&lt;/b&gt;";&lt;br /&gt;    }&lt;br /&gt; &lt;br /&gt;    odw.Activate = function(sWorkflowId)&lt;br /&gt;    {&lt;br /&gt;      UpdateNotification("Activated");&lt;br /&gt;  &lt;br /&gt;      if (sWorkflowId)&lt;br /&gt;      {&lt;br /&gt;         odw.WorkflowId = sWorkflowId&lt;br /&gt;      }&lt;br /&gt;  &lt;br /&gt;      if (!odw.WorkflowId)&lt;br /&gt;      {&lt;br /&gt;         return UpdateNotification("Missing Workflow ID");&lt;br /&gt;      }&lt;br /&gt;  &lt;br /&gt;      if (!odw.EntityId &amp;&amp; crmForm.FormType == 2)&lt;br /&gt;      {&lt;br /&gt;         odw.EntityId = crmForm.ObjectId;&lt;br /&gt;      }&lt;br /&gt;      else if (!odw.EntityId)&lt;br /&gt;      {&lt;br /&gt;         return UpdateNotification("Workflow is missing an Entity name");&lt;br /&gt;      }&lt;br /&gt;  &lt;br /&gt;      var xmlActReq = new StringBuilder();&lt;br /&gt;      xmlActReq.Append(GenerateSoapHeader());&lt;br /&gt;      xmlActReq.Append("&lt;Execute xmlns='http://schemas.microsoft.com/crm/2007/WebServices'&gt;");&lt;br /&gt;      xmlActReq.Append("&lt;Request xsi:type='ExecuteWorkflowRequest'&gt;");&lt;br /&gt;      xmlActReq.Append("&lt;EntityId&gt;").Append(odw.EntityId).Append("&lt;/EntityId&gt;");&lt;br /&gt;      xmlActReq.Append("&lt;WorkflowId&gt;").Append(odw.WorkflowId).Append("&lt;/WorkflowId&gt;");&lt;br /&gt;      xmlActReq.Append("&lt;/Request&gt;");&lt;br /&gt;      xmlActReq.Append("&lt;/Execute&gt;");&lt;br /&gt;      xmlActReq.Append(GenerateSoapFooter());&lt;br /&gt;  &lt;br /&gt;      odw.Execute("Execute",xmlActReq.ToString(),odw.ActivateEnd);&lt;br /&gt;   }&lt;br /&gt; &lt;br /&gt;   odw.Execute = function(sMethodName,sXmlRequest, fCallback)&lt;br /&gt;   {&lt;br /&gt;      if (request)&lt;br /&gt;      {&lt;br /&gt;         request.abort();&lt;br /&gt;      }&lt;br /&gt;      request = new ActiveXObject("Microsoft.XMLHTTP");&lt;br /&gt;      request.Open("POST", "/mscrmservices/2007/CrmService.asmx", true);&lt;br /&gt;      request.onreadystatechange = fCallback;&lt;br /&gt;      request.setRequestHeader("SOAPAction","http://schemas.microsoft.com/crm/2007/WebServices/" + sMethodName);&lt;br /&gt;      request.setRequestHeader("Content-Type", "text/xml; charset=utf-8");&lt;br /&gt;      request.setRequestHeader("Content-Length", sXmlRequest.length);&lt;br /&gt;      request.send(sXmlRequest);&lt;br /&gt;   }&lt;br /&gt; &lt;br /&gt;   odw.ActivateEnd = function()&lt;br /&gt;   {&lt;br /&gt;      if (request.readyState == 4)&lt;br /&gt;      {&lt;br /&gt;         if (!validateResponse())&lt;br /&gt;         {&lt;br /&gt;            return;&lt;br /&gt;         }&lt;br /&gt;   &lt;br /&gt;         odw.OperationId = request.responseXML.selectSingleNode("//Response/Id").nodeTypedValue;&lt;br /&gt;         odw.CheckStatus();&lt;br /&gt;      }&lt;br /&gt;   }&lt;br /&gt; &lt;br /&gt;   odw.CheckStatus = function()&lt;br /&gt;   {&lt;br /&gt;      var xmlChkReq = new StringBuilder();&lt;br /&gt;      xmlChkReq.Append(GenerateSoapHeader());&lt;br /&gt;      xmlChkReq.Append("&lt;Retrieve xmlns='http://schemas.microsoft.com/crm/2007/WebServices'&gt;");&lt;br /&gt;      xmlChkReq.Append("&lt;entityName&gt;asyncoperation&lt;/entityName&gt;");&lt;br /&gt;      xmlChkReq.Append("&lt;id&gt;{").Append(odw.OperationId).Append("}&lt;/id&gt;");&lt;br /&gt;      xmlChkReq.Append("&lt;columnSet xmlns:q1='http://schemas.microsoft.com/crm/2006/Query' xsi:type='q1:ColumnSet'&gt;");&lt;br /&gt;      xmlChkReq.Append("&lt;q1:Attributes&gt;");&lt;br /&gt;      xmlChkReq.Append("&lt;q1:Attribute&gt;statuscode&lt;/q1:Attribute&gt;");&lt;br /&gt;      xmlChkReq.Append("&lt;/q1:Attributes&gt;");&lt;br /&gt;      xmlChkReq.Append("&lt;/columnSet&gt;");&lt;br /&gt;      xmlChkReq.Append("&lt;/Retrieve&gt;");  &lt;br /&gt;      xmlChkReq.Append(GenerateSoapFooter());&lt;br /&gt;  &lt;br /&gt;      odw.Execute("Retrieve",xmlChkReq.ToString(),odw.CheckStatusEnd);&lt;br /&gt;   }&lt;br /&gt; &lt;br /&gt;   odw.CheckStatusEnd = function()&lt;br /&gt;   {&lt;br /&gt;      if (request.readyState == 4)&lt;br /&gt;      {&lt;br /&gt;         if (!validateResponse())&lt;br /&gt;         {&lt;br /&gt;            return setTimeout(odw.CheckStatus, odw.DefaultProgress.CheckInterval);&lt;br /&gt;         }&lt;br /&gt;   &lt;br /&gt;         odw.WorkflowStatus = request.responseXML.selectSingleNode("//q1:statuscode").nodeTypedValue;&lt;br /&gt;   &lt;br /&gt;         switch(parseInt(odw.WorkflowStatus))&lt;br /&gt;         {&lt;br /&gt;            case 30:&lt;br /&gt;            if (odw.DefaultProgress.RefreshWhenDone)&lt;br /&gt;            {&lt;br /&gt;               if (odw.DefaultProgress.RequestRefresh)&lt;br /&gt;               {&lt;br /&gt;                  window.onbeforeunload = function()&lt;br /&gt;                  {&lt;br /&gt;                     return "Operation has succeeded, Do you wish to refresh the page?";&lt;br /&gt;                  }&lt;br /&gt;               }&lt;br /&gt;            else&lt;br /&gt;            {&lt;br /&gt;               crmForm.detachCloseAlert();&lt;br /&gt;            }&lt;br /&gt;      &lt;br /&gt;            setTimeout(function(){&lt;br /&gt;               window.location.reload();&lt;br /&gt;            },1000);&lt;br /&gt;         }&lt;br /&gt;     &lt;br /&gt;         UpdateNotification("Operation has succeeded");&lt;br /&gt;         if (odw.OnFinishCallback)&lt;br /&gt;         {&lt;br /&gt;             odw.OnFinishCallback(odw);&lt;br /&gt;         }&lt;br /&gt;&lt;br /&gt;      break;&lt;br /&gt;      default:&lt;br /&gt;    &lt;br /&gt;         switch(parseInt(odw.WorkflowStatus))&lt;br /&gt;         {&lt;br /&gt;               case 32: //canceled&lt;br /&gt;                  UpdateNotification("Operation was canceled");&lt;br /&gt;               break;&lt;br /&gt;               case 22: //canceling&lt;br /&gt;                  UpdateNotification("Operation is being canceled");&lt;br /&gt;               break;&lt;br /&gt;               case 31: //failed&lt;br /&gt;                  UpdateNotification("Operation has failed");&lt;br /&gt;               break;&lt;br /&gt;               case 20: //In progress&lt;br /&gt;                  UpdateNotification("Operation is in progress");&lt;br /&gt;               break;&lt;br /&gt;               case 21: //Pausing&lt;br /&gt;                  UpdateNotification("Operation is pausing");&lt;br /&gt;               break;&lt;br /&gt;               case 10: //Waiting&lt;br /&gt;                  UpdateNotification("Operation is waiting");&lt;br /&gt;               break;&lt;br /&gt;               case 0: //Waiting for resources&lt;br /&gt;                  UpdateNotification("Operation is waiting for resources");&lt;br /&gt;               break;&lt;br /&gt;            }&lt;br /&gt;    &lt;br /&gt;            return setTimeout(odw.CheckStatus, odw.DefaultProgress.CheckInterval);&lt;br /&gt;         }   &lt;br /&gt;      }&lt;br /&gt;   }&lt;br /&gt; &lt;br /&gt;   function validateResponse()&lt;br /&gt;   {&lt;br /&gt;  &lt;br /&gt;      var error = request.responseXML.selectSingleNode("//error");&lt;br /&gt;      var faultstring = request.responseXML.selectSingleNode("//faultstring");&lt;br /&gt;  &lt;br /&gt;      if (error == null &amp;&amp; faultstring == null)&lt;br /&gt;      {&lt;br /&gt;         return true;&lt;br /&gt;      }&lt;br /&gt;      else &lt;br /&gt;      {&lt;br /&gt;         odw.DefaultProgress.Visible = true;&lt;br /&gt;         if (error)&lt;br /&gt;         {&lt;br /&gt;            UpdateNotification(error.text);&lt;br /&gt;         }&lt;br /&gt;         else&lt;br /&gt;         {&lt;br /&gt;            UpdateNotification(faultstring.text);&lt;br /&gt;         }&lt;br /&gt;   &lt;br /&gt;         return false;&lt;br /&gt;      }&lt;br /&gt;   }&lt;br /&gt; &lt;br /&gt;   function GenerateSoapHeader()&lt;br /&gt;   {&lt;br /&gt;      var soapHeader = new StringBuilder();&lt;br /&gt;      soapHeader.Append("&lt;?xml version='1.0' encoding='utf-8'?&gt;");&lt;br /&gt;      soapHeader.Append("&lt;soap:Envelope xmlns:soap='http://schemas.xmlsoap.org/soap/envelope/'");&lt;br /&gt;      soapHeader.Append(" xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'");&lt;br /&gt;      soapHeader.Append(" xmlns:xsd='http://www.w3.org/2001/XMLSchema'&gt;");&lt;br /&gt;      soapHeader.Append(GenerateAuthenticationHeader());&lt;br /&gt;      soapHeader.Append("&lt;soap:Body&gt;");&lt;br /&gt;      return soapHeader.ToString();&lt;br /&gt;   }&lt;br /&gt; &lt;br /&gt;   function GenerateSoapFooter()&lt;br /&gt;   {&lt;br /&gt;      var soapFooter = new StringBuilder();&lt;br /&gt;      soapFooter.Append("&lt;/soap:Body&gt;");&lt;br /&gt;      soapFooter.Append("&lt;/soap:Envelope&gt;");&lt;br /&gt;      return soapFooter.ToString();&lt;br /&gt;   }&lt;br /&gt;  &lt;br /&gt;   function ProgressInfo()&lt;br /&gt;   {&lt;br /&gt;      this.RequestRefresh = true;&lt;br /&gt;      this.RefreshWhenDone = false;&lt;br /&gt;      this.Visible = false;&lt;br /&gt;      this.CheckInterval = 2000;&lt;br /&gt;   }&lt;br /&gt;&lt;br /&gt;   function StringBuilder()&lt;br /&gt;   {  &lt;br /&gt;      var parts     = [];  &lt;br /&gt;      this.Append = function( text ){parts[ parts.length ] = text;return this;}  &lt;br /&gt;      this.Reset    = function(){parts = [];}  &lt;br /&gt;      this.ToString = function(){return parts.join( "" );}  &lt;br /&gt;   }  &lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;function OnCrmPageLoad()&lt;br /&gt;{&lt;br /&gt;   if (confirm("Run Workflow"))&lt;br /&gt;   {&lt;br /&gt;      var odWorkflow  = new OnDemandWorkflow();&lt;br /&gt;      /* The workflow name - for presentation */&lt;br /&gt;      odWorkflow.Name = "Create Task";  &lt;br /&gt;      var odwProgress = odWorkflow.DefaultProgress;&lt;br /&gt;      /* true  is default - true = user has to approve the page reload. */&lt;br /&gt;      odwProgress.RequestRefresh  = false; &lt;br /&gt;      /* false is default - true = try to refresh when the workflow is done successfully. */&lt;br /&gt;      odwProgress.RefreshWhenDone = true;  &lt;br /&gt;      /* false is default - true = see notification progress */&lt;br /&gt;      odwProgress.Visible = true;  &lt;br /&gt;      /* 2000  is default - ping the server each x milliseconds */ &lt;br /&gt;      odwProgress.CheckInterval = 1000;    &lt;br /&gt;      odWorkflow.WorkflowId = "76f5cecb-987c-4635-8d78-66d2caa2f9ae";&lt;br /&gt;      /* default Entity Id - the entity instance id (guid) */&lt;br /&gt;      odWorkflow.EntityId = crmForm.ObjectId; &lt;br /&gt;      odWorkflow.OnFinishCallback = CallmeWhenTheWFIsDone&lt;br /&gt;      odWorkflow.Activate();&lt;br /&gt;   }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;function CallmeWhenTheWFIsDone(odw)&lt;br /&gt;{&lt;br /&gt;    if (odw.WorkflowStatus == 30)&lt;br /&gt;    {&lt;br /&gt;        alert('more code here');&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;OnCrmPageLoad();&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3003801964503297388-4670164621257494848?l=mscrm4ever.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mscrm4ever.blogspot.com/feeds/4670164621257494848/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3003801964503297388&amp;postID=4670164621257494848&amp;isPopup=true' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3003801964503297388/posts/default/4670164621257494848'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3003801964503297388/posts/default/4670164621257494848'/><link rel='alternate' type='text/html' href='http://mscrm4ever.blogspot.com/2009/09/crm-40-running-on-demand-workflow-from.html' title='CRM 4.0 Running an On Demand Workflow from JavaScript'/><author><name>Adi Katz</name><uri>http://www.blogger.com/profile/16783428437179825705</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_M-mdv3Tfarg/SqiOdLPw9uI/AAAAAAAAANk/C2kwYqs9POE/s72-c/pic.JPG' height='72' width='72'/><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3003801964503297388.post-633036156356557873</id><published>2009-09-08T11:31:00.003+03:00</published><updated>2009-09-09T22:56:50.979+03:00</updated><title type='text'>Formatting CRM DateTime Field</title><content type='html'>&lt;div&gt;&lt;/div&gt;&lt;br /&gt;As you might have noticed CRM DateTime field DataValue returns the following value: “Tue Sep 29 13:00:00 PDT 2009”&lt;br /&gt;Sometimes you need to change the value to another format e.g. dd/mm/yyyy or mm-dd-yyyy hh:MM. &lt;br /&gt;This is quite easy to do with C# since the format string is pretty extensive however the JavaScript Date object is pretty slim and so the only option is to write your own framework / prototype extension. &lt;br /&gt;&lt;br /&gt;The following script extends the JavaScript Date Object and adds a toFormattedString function which accepts common format strings such as&lt;br /&gt;dd – days, mm – months, yyyy – full year, hh – hours , MM – minutes , ss – seconds , ms – milliseconds , APM – AM/PM&lt;br /&gt;You can extend the prototype function further if you require additional formatting.&lt;br /&gt;&lt;br /&gt;Here is the code and usage example:&lt;br /&gt;&lt;pre class="js" name="code"&gt;&lt;br /&gt;Date.prototype.toFormattedString = function(format)&lt;br /&gt;{&lt;br /&gt;   var d = this;&lt;br /&gt;   var f = "";&lt;br /&gt;   f = f + format.replace( /dd|mm|yyyy|MM|hh|ss|ms|APM|\s|\/|\-|,|\./ig , &lt;br /&gt;   function match()&lt;br /&gt;   {&lt;br /&gt;      switch(arguments[0])&lt;br /&gt;      {&lt;br /&gt;         case "dd": &lt;br /&gt;            var dd = d.getDate();&lt;br /&gt;            return (dd &lt; 10)? "0" + dd : dd;&lt;br /&gt;         case "mm":&lt;br /&gt;            var mm = d.getMonth() + 1;&lt;br /&gt;            return (mm &lt; 10)? "0" + mm : mm; &lt;br /&gt;         case "yyyy": return d.getFullYear();&lt;br /&gt;         case "hh": &lt;br /&gt;            var hh = d.getHours();&lt;br /&gt;            return (hh &lt; 10)? "0" + hh : hh;&lt;br /&gt;         case "MM": &lt;br /&gt;            var MM = d.getMinutes(); &lt;br /&gt;            return (MM &lt; 10)? "0" + MM : MM;&lt;br /&gt;         case "ss": &lt;br /&gt;            var ss = d.getSeconds(); &lt;br /&gt;            return (ss &lt; 10)? "0" + ss : ss;&lt;br /&gt;         case "ms": return d.getMilliseconds();&lt;br /&gt;         case "APM": &lt;br /&gt;            var apm = d.getHours(); &lt;br /&gt;            return (apm &lt; 12)? "AM" : "PM";&lt;br /&gt;         default: return arguments[0];&lt;br /&gt;      }&lt;br /&gt;   });&lt;br /&gt;&lt;br /&gt;   return f;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;function OnCrmPageLoad()&lt;br /&gt;{&lt;br /&gt;    var d = new Date(crmForm.all.&lt;DateTime Field Id&gt;.DataValue);&lt;br /&gt;    alert(d.toFormattedString("mm-dd-yyyy hh:MM:ss ms"));&lt;br /&gt;    alert(d.toFormattedString("dd/mm/yyyy hh:MM APM"));&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;OnCrmPageLoad();&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3003801964503297388-633036156356557873?l=mscrm4ever.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mscrm4ever.blogspot.com/feeds/633036156356557873/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3003801964503297388&amp;postID=633036156356557873&amp;isPopup=true' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3003801964503297388/posts/default/633036156356557873'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3003801964503297388/posts/default/633036156356557873'/><link rel='alternate' type='text/html' href='http://mscrm4ever.blogspot.com/2009/09/formatting-crm-datetime-field.html' title='Formatting CRM DateTime Field'/><author><name>Adi Katz</name><uri>http://www.blogger.com/profile/16783428437179825705</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3003801964503297388.post-1791791539302217486</id><published>2009-09-08T09:50:00.003+03:00</published><updated>2009-09-08T09:59:47.050+03:00</updated><title type='text'>CRM 4.0 Creating a Readonly picklist</title><content type='html'>CRM Picklist control (HTML select tag) does not have a read-only attribute like other input html controls. The only way to make it read-only is to disable it which grays out the control completely. Once you disable the Picklist you can’t change its border or font color (i.e. make it look like a read-only field). The only way to achieve the functionality is to change the control behavior so each time the user tries to change the Picklist value the Picklist initial value is re-selected.&lt;br /&gt;&lt;br /&gt;Here is the JS code that does the job:&lt;br /&gt;&lt;br /&gt;&lt;pre class="js" name="code"&gt;&lt;br /&gt;function OnCrmPageLoad()&lt;br /&gt;{&lt;br /&gt;      ReadOnlyPicklist(crmForm.all.&lt;picklist id&gt;);&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;function ReadOnlyPicklist(picklist)&lt;br /&gt;{&lt;br /&gt;      picklist.savedIndex = picklist.selectedIndex;&lt;br /&gt;      picklist.onchange = function()&lt;br /&gt;&lt;br /&gt;      {&lt;br /&gt;            this.selectedIndex = this.savedIndex;&lt;br /&gt;      }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;OnCrmPageLoad();&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3003801964503297388-1791791539302217486?l=mscrm4ever.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mscrm4ever.blogspot.com/feeds/1791791539302217486/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3003801964503297388&amp;postID=1791791539302217486&amp;isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3003801964503297388/posts/default/1791791539302217486'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3003801964503297388/posts/default/1791791539302217486'/><link rel='alternate' type='text/html' href='http://mscrm4ever.blogspot.com/2009/09/crm-40-creating-readonly-picklist.html' title='CRM 4.0 Creating a Readonly picklist'/><author><name>Adi Katz</name><uri>http://www.blogger.com/profile/16783428437179825705</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3003801964503297388.post-2068441178338301378</id><published>2009-09-08T09:45:00.005+03:00</published><updated>2009-09-08T09:49:27.027+03:00</updated><title type='text'>CRM 4.0 Changing Tab Order</title><content type='html'>A while back i posted a solution for changing vertical tabbing to horizontal tabbing. The code ran a bit slow with lookups and so the code below is a revision of the previous posting with the fix.&lt;br /&gt;&lt;br /&gt;&lt;pre class="js" name="code"&gt;&lt;br /&gt;function OnCrmPageLoad()&lt;br /&gt;{&lt;br /&gt;     ReArangeTabIndex();&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;function ReArangeTabIndex()&lt;br /&gt;{&lt;br /&gt;    for( var i = 0 ; i &lt; crmForm.all.length ; i++ )&lt;br /&gt;    {&lt;br /&gt;        var element = crmForm.all[ i ];&lt;br /&gt;        if (element.tabIndex)&lt;br /&gt;        {&lt;br /&gt;            if (element.className == "ms-crm-Hidden-NoBehavior" || element.tagName == "A")&lt;br /&gt;            {&lt;br /&gt;                continue;&lt;br /&gt;            }&lt;br /&gt;   &lt;br /&gt;            element.tabIndex = 1000 + (i*10);&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;OnCrmPageLoad();&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3003801964503297388-2068441178338301378?l=mscrm4ever.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mscrm4ever.blogspot.com/feeds/2068441178338301378/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3003801964503297388&amp;postID=2068441178338301378&amp;isPopup=true' title='8 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3003801964503297388/posts/default/2068441178338301378'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3003801964503297388/posts/default/2068441178338301378'/><link rel='alternate' type='text/html' href='http://mscrm4ever.blogspot.com/2009/09/crm-40-changing-tab-order.html' title='CRM 4.0 Changing Tab Order'/><author><name>Adi Katz</name><uri>http://www.blogger.com/profile/16783428437179825705</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>8</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3003801964503297388.post-1035984495871706995</id><published>2009-07-18T02:19:00.004+03:00</published><updated>2009-07-18T02:33:34.438+03:00</updated><title type='text'>CRM 4.0 Setting a Picklist Default Value</title><content type='html'>&lt;div&gt;&lt;/div&gt;&lt;br /&gt;Currently Dynamics supports setting default values for two attribute types: PicklistAttributeMetaddata (Picklist) and BooleanAttributeMetadata (bit fields). If you intend to set the default using custom code you need to follow the below process:&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;1. Retrieve the attribute metadata (unless you’re creating a new one) using RetrieveAttributeRequest&lt;br /&gt;2. Cast the returned AttributeMetadata to a PicklistAttributeMetadata.&lt;br /&gt;3. Set the picklist metadata Default value to an integer of you choice.&lt;br /&gt;4. Update the attribute metadata using UpdateAttribute Request.&lt;br /&gt;5. Publish the changes using PublishXml Request.&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;The bellow code is a working example which sets the account Relationship Type (customertypecode) picklist to Customer (value = 3).&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;&lt;pre class="js" name="code"&gt;&lt;br /&gt;using System;&lt;br /&gt;using System.Collections.Generic;&lt;br /&gt;using System.Text;&lt;br /&gt;using Microsoft.Crm.Sdk;&lt;br /&gt;using Microsoft.Crm.SdkTypeProxy;&lt;br /&gt;using Microsoft.Crm.Sdk.Metadata;&lt;br /&gt;using Microsoft.Crm.SdkTypeProxy.Metadata;&lt;br /&gt;&lt;br /&gt;namespace GI.Sendbox&lt;br /&gt;{&lt;br /&gt;   class Program&lt;br /&gt;   {&lt;br /&gt;      static void Main(string[] args)&lt;br /&gt;      {&lt;br /&gt;         try&lt;br /&gt;         {&lt;br /&gt;            /* Create Authenticatio Token */&lt;br /&gt;            CrmAuthenticationToken token = new CrmAuthenticationToken();&lt;br /&gt;            token.AuthenticationType = 0;&lt;br /&gt;            token.OrganizationName = "MicrosoftCRM";&lt;br /&gt;    &lt;br /&gt;            /* Create a CrmService end point */&lt;br /&gt;            CrmService crmService = new CrmService();&lt;br /&gt;            crmService.UnsafeAuthenticatedConnectionSharing = true;&lt;br /&gt;            crmService.Url = "http://localhost:5555/mscrmservices/2007/crmservice.asmx";&lt;br /&gt;            crmService.UseDefaultCredentials = true;&lt;br /&gt;            crmService.CrmAuthenticationTokenValue = token;&lt;br /&gt;    &lt;br /&gt;            /* Create a MetadataService end point */&lt;br /&gt;            MetadataService metaService = new MetadataService();&lt;br /&gt;            metaService.Url = "http://localhost:5555/mscrmservices/2007/metadataservice.asmx";&lt;br /&gt;            metaService.UseDefaultCredentials = true;&lt;br /&gt;            metaService.UnsafeAuthenticatedConnectionSharing = true;&lt;br /&gt;            metaService.CrmAuthenticationTokenValue = token;&lt;br /&gt;    &lt;br /&gt;            /* Retrieve the attribute metadata */&lt;br /&gt;            RetrieveAttributeRequest attributeRequest = new RetrieveAttributeRequest();&lt;br /&gt;            attributeRequest.EntityLogicalName = "account";&lt;br /&gt;            attributeRequest.LogicalName = "customertypecode"; //Relationship Type picklist&lt;br /&gt;       &lt;br /&gt;            RetrieveAttributeResponse attributeResponse = &lt;br /&gt;               (RetrieveAttributeResponse)metaService.Execute(attributeRequest);&lt;br /&gt;    &lt;br /&gt;            /* Cast the attribute metadata to a picklist metadata */&lt;br /&gt;            PicklistAttributeMetadata picklist = &lt;br /&gt;               (PicklistAttributeMetadata)attributeResponse.AttributeMetadata;&lt;br /&gt;    &lt;br /&gt;            /* set the default value to "customer" (3) */&lt;br /&gt;            picklist.DefaultValue = (object)3;&lt;br /&gt;    &lt;br /&gt;            /* update the attribute metadata */&lt;br /&gt;            UpdateAttributeRequest updateRequest = new UpdateAttributeRequest();&lt;br /&gt;            updateRequest.Attribute = picklist;&lt;br /&gt;            updateRequest.EntityName = "account";&lt;br /&gt;            updateRequest.MergeLabels = false;&lt;br /&gt;    &lt;br /&gt;            metaService.Execute(updateRequest);&lt;br /&gt;&lt;br /&gt;            /* Publish the changes */&lt;br /&gt;            PublishXmlRequest request = new PublishXmlRequest();&lt;br /&gt;&lt;br /&gt;            request.ParameterXml = @"&amp;lt;importexportxml&amp;gt;&lt;br /&gt;                                       &amp;lt;entities&amp;gt;&lt;br /&gt;                                          &amp;lt;entity&amp;gt;account&amp;lt;/entity&amp;gt;&lt;br /&gt;                                       &amp;lt;/entities&amp;gt;&lt;br /&gt;                                       &amp;lt;nodes/&amp;gt;&lt;br /&gt;                                       &amp;lt;securityroles/&amp;gt;&lt;br /&gt;                                       &amp;lt;settings/&amp;gt;&lt;br /&gt;                                       &amp;lt;workflows/&amp;gt;&lt;br /&gt;                                    &amp;lt;/importexportxml&amp;gt;";&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;            PublishXmlResponse response = &lt;br /&gt;               (PublishXmlResponse)crmService.Execute(request);&lt;br /&gt;         }&lt;br /&gt;         catch(System.Web.Services.Protocols.SoapException sex)&lt;br /&gt;         {&lt;br /&gt;               Console.WriteLine(sex.Detail.OuterXml);&lt;br /&gt;         }&lt;br /&gt;         catch(Exception ex)&lt;br /&gt;         {&lt;br /&gt;               Console.WriteLine(ex.ToString());&lt;br /&gt;         }&lt;br /&gt;      }&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3003801964503297388-1035984495871706995?l=mscrm4ever.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mscrm4ever.blogspot.com/feeds/1035984495871706995/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3003801964503297388&amp;postID=1035984495871706995&amp;isPopup=true' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3003801964503297388/posts/default/1035984495871706995'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3003801964503297388/posts/default/1035984495871706995'/><link rel='alternate' type='text/html' href='http://mscrm4ever.blogspot.com/2009/07/crm-40-setting-picklist-default-value.html' title='CRM 4.0 Setting a Picklist Default Value'/><author><name>Adi Katz</name><uri>http://www.blogger.com/profile/16783428437179825705</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3003801964503297388.post-3010521105234022228</id><published>2009-07-14T02:42:00.004+03:00</published><updated>2009-07-14T02:56:52.597+03:00</updated><title type='text'>CRM 4.0 Logging user login information</title><content type='html'>&lt;div&gt;&lt;/div&gt;&lt;br /&gt;Following is a simple example on how-to log a user interaction with CRM. The sample uses a simple plug-in that references the System.Web assembly. Once you create a reference to this assembly you’re able to read the HttpContext and consequently read the information you wish to log. &lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;The plug-in hooks into the execute message which in most cases is the first event to fire (grid event). The plug-in also writes a cookie back to the browser to mark the logging operation so it only happens once while the main crm application is opened.&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;In the example I also create a simple logInfo entity with fields I’m interested in logging. &lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;Please not that while reading information from the httpcontext and other objects like Request might be considered supported, reading information that CRM uses like httpcontext.items[“organizationName”] or writing information to a cookie might break your support. So when you write values back to the browser make sure you’re using a well defined naming convention that will always stay unique. And instead of reading the orgname from the items collection get it from the request url.&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;&lt;pre class="js" name="code"&gt;&lt;br /&gt;using System;&lt;br /&gt;using System.Collections.Generic;&lt;br /&gt;using System.Text;&lt;br /&gt;using Microsoft.Crm.Sdk;&lt;br /&gt;using Microsoft.Crm.SdkTypeProxy;&lt;br /&gt;using System.Web;&lt;br /&gt;&lt;br /&gt;namespace GI.SandBox&lt;br /&gt;{&lt;br /&gt;   public class LoginHandler : IPlugin&lt;br /&gt;   {&lt;br /&gt;      #region IPlugin Members&lt;br /&gt;&lt;br /&gt;      public void Execute(IPluginExecutionContext context)&lt;br /&gt;      {&lt;br /&gt;         if (context.MessageName != "Execute")&lt;br /&gt;         {&lt;br /&gt;            return;&lt;br /&gt;         }&lt;br /&gt;   &lt;br /&gt;         if (context.Stage != MessageProcessingStage.BeforeMainOperationOutsideTransaction)&lt;br /&gt;         {&lt;br /&gt;            return;&lt;br /&gt;         } &lt;br /&gt;   &lt;br /&gt;         if (!(context.CallerOrigin is ApplicationOrigin))&lt;br /&gt;         {&lt;br /&gt;            return;&lt;br /&gt;         }&lt;br /&gt;   &lt;br /&gt;         HttpContext webContext = HttpContext.Current;&lt;br /&gt;         HttpCookie  logInfo = webContext.Request.Cookies.Get("loginfo");&lt;br /&gt;  &lt;br /&gt;         if (logInfo == null)&lt;br /&gt;         {&lt;br /&gt;            DynamicEntity logInfoEntry = new DynamicEntity();&lt;br /&gt;            logInfoEntry.Name = "gi_loginfo";&lt;br /&gt;&lt;br /&gt;            logInfoEntry.Properties.Add&lt;br /&gt;            (&lt;br /&gt;               new StringProperty&lt;br /&gt;               (&lt;br /&gt;                  "gi_userhostaddress",&lt;br /&gt;                  webContext.Request.UserHostAddress&lt;br /&gt;               )&lt;br /&gt;            );&lt;br /&gt;&lt;br /&gt;            logInfoEntry.Properties.Add&lt;br /&gt;            (&lt;br /&gt;               new StringProperty&lt;br /&gt;               (&lt;br /&gt;                  "gi_useridentity",&lt;br /&gt;                  webContext.User.Identity.Name&lt;br /&gt;               )&lt;br /&gt;            );&lt;br /&gt;&lt;br /&gt;            logInfoEntry.Properties.Add&lt;br /&gt;            (&lt;br /&gt;               new StringProperty&lt;br /&gt;               (&lt;br /&gt;                  "gi_starturl",&lt;br /&gt;                  webContext.Request.Path&lt;br /&gt;               )&lt;br /&gt;            );&lt;br /&gt;&lt;br /&gt;            logInfoEntry.Properties.Add&lt;br /&gt;            (&lt;br /&gt;               new StringProperty&lt;br /&gt;               (&lt;br /&gt;                  "gi_hostname",&lt;br /&gt;                  webContext.Request.Url.Host&lt;br /&gt;               )&lt;br /&gt;            );&lt;br /&gt;&lt;br /&gt;            if (webContext.Items["organizationName"] != null)&lt;br /&gt;            {&lt;br /&gt;               logInfoEntry.Properties.Add&lt;br /&gt;               (&lt;br /&gt;                  new StringProperty&lt;br /&gt;                  (&lt;br /&gt;                     "gi_userhostaddress",&lt;br /&gt;                     webContext.Items["organizationName"].ToString()&lt;br /&gt;                  )&lt;br /&gt;               );&lt;br /&gt;            }&lt;br /&gt;    &lt;br /&gt;            logInfoEntry.Properties.Add&lt;br /&gt;            (&lt;br /&gt;               new CrmDateTimeProperty&lt;br /&gt;               (&lt;br /&gt;                  "gi_datetime",&lt;br /&gt;                  new CrmDateTime(webContext.Timestamp.ToString())&lt;br /&gt;               )&lt;br /&gt;            );&lt;br /&gt;    &lt;br /&gt;            TargetUpdateDynamic targetRecord = new TargetUpdateDynamic();&lt;br /&gt;            targetRecord.Entity = logInfoEntry;&lt;br /&gt;       &lt;br /&gt;            UpdateRequest updateRequest = new UpdateRequest();&lt;br /&gt;            updateRequest.Target = targetRecord;&lt;br /&gt;    &lt;br /&gt;            context.CreateCrmService(true).Execute(updateRequest);&lt;br /&gt;&lt;br /&gt;            logInfo = new HttpCookie("loginfo");&lt;br /&gt;            logInfo.Value = DateTime.Now.ToString();&lt;br /&gt;            webContext.Response.Cookies.Add(logInfo);&lt;br /&gt;      }   &lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  #endregion&lt;br /&gt; }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3003801964503297388-3010521105234022228?l=mscrm4ever.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mscrm4ever.blogspot.com/feeds/3010521105234022228/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3003801964503297388&amp;postID=3010521105234022228&amp;isPopup=true' title='6 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3003801964503297388/posts/default/3010521105234022228'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3003801964503297388/posts/default/3010521105234022228'/><link rel='alternate' type='text/html' href='http://mscrm4ever.blogspot.com/2009/07/crm-40-logging-user-login-information.html' title='CRM 4.0 Logging user login information'/><author><name>Adi Katz</name><uri>http://www.blogger.com/profile/16783428437179825705</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>6</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3003801964503297388.post-2941970867078311225</id><published>2009-07-12T03:48:00.006+03:00</published><updated>2010-05-05T07:51:24.489+03:00</updated><title type='text'>CRM 4.0 Cascading wizard</title><content type='html'>&lt;div&gt;&lt;/div&gt;&lt;br /&gt;&lt;a href="http://2.bp.blogspot.com/_M-mdv3Tfarg/Slk0y2nT7CI/AAAAAAAAANU/tDXlZS_L2T8/s1600-h/main.jpg"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 278px;border:1px solid black;" src="http://2.bp.blogspot.com/_M-mdv3Tfarg/Slk0y2nT7CI/AAAAAAAAANU/tDXlZS_L2T8/s400/main.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5357371279992417314" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;The Cascading wizard is a complementary add-on to dynamic mapping facility. Currently dynamics only supports mapping when a user creates a new child entity within a parent context (form). Knowing that many partners invest quite a bit of energy developing solutions that require cascading and having to deal with these types of requirements ourselves we decided to create a fully featured mapping / cascading wizard that enable us to complete these tasks with just a few clicks. &lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;The CAW wizard has the following features which can save you hours of tedious development.&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;1.Mapping fields from a parent entity when a child entity is created outside of the parent form. For example: A common requirement might be to inherit account information when you create a new contact from dynamics contacts grid (view). Currently the account fields are only mapped if you create the contact from the account form.&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;2.Mapping fields from multiple parents. For example: you might have an entity that has lookups to more the 1 entity. When you create the entity you might want to inherit fields from any parent.&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;3.Cascading parent modifications to all children. For example: you might want to update all contacts under a certain account when the account main phone field (contact business phone) changes. &lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;4.The ability to decide whether to inherit parent attributes only when the child field is empty. For example: you might want to inherit account information when the contact is created but want to enable the user to change the contacts fields afterwards. The CAW wizard enables you to decide whether field changes are cascaded when the child attribute is empty.&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;5.Cascading drilldown. One of the nicest features of this wizard is that it enables you to drill down the changes from the root entity to the bottom leaf.&lt;br /&gt;For example: if you define an account to contact cascading rules and then define a contact to invoice cascading rule on the same fields. And then change the field on the account form the change is carried out to the invoice entity.&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;6.The wizard integrates seamlessly with the current mapping facility. And you can choose to utilize both or just use the CAW wizard.&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;Common CAW features: The wizard supports – &lt;br /&gt;1. Both on-premise and partner hosted environments. &lt;br /&gt;2. IFD – Internet facing deployment&lt;br /&gt;3. 32 and 64 bit servers&lt;br /&gt;4. Multi-Tenancy&lt;br /&gt;5. All Languages&lt;br /&gt;6. All Rollups&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;We made a video to illustrate the above features.&lt;br /&gt;In order to play back the video, right click on the flash movie then click rewind and play. &lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;&lt;iframe src="http://www.upsite.co.il/uploaded/files/662_5d00861d7d4c0e900f160ab6aa6898c8.swf" width="680" height="534"&gt;&lt;/iframe&gt;&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;If you have question regarding the wizard functionality you can ask them here of send your enquiry to support@gicrm.com&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3003801964503297388-2941970867078311225?l=mscrm4ever.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mscrm4ever.blogspot.com/feeds/2941970867078311225/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3003801964503297388&amp;postID=2941970867078311225&amp;isPopup=true' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3003801964503297388/posts/default/2941970867078311225'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3003801964503297388/posts/default/2941970867078311225'/><link rel='alternate' type='text/html' href='http://mscrm4ever.blogspot.com/2009/07/crm-40-cascading-wizard.html' title='CRM 4.0 Cascading wizard'/><author><name>Adi Katz</name><uri>http://www.blogger.com/profile/16783428437179825705</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_M-mdv3Tfarg/Slk0y2nT7CI/AAAAAAAAANU/tDXlZS_L2T8/s72-c/main.jpg' height='72' width='72'/><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3003801964503297388.post-1029753781301476506</id><published>2009-07-05T15:26:00.007+03:00</published><updated>2010-05-05T07:51:49.405+03:00</updated><title type='text'>CRM 4.0 Record Filter Wizard</title><content type='html'>&lt;div&gt;&lt;/div&gt;&lt;br /&gt;&lt;a href="http://2.bp.blogspot.com/_M-mdv3Tfarg/SlCotng1RKI/AAAAAAAAANE/K6SgETLNZ8M/s1600-h/main.jpg"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 300px;border:1px solid black;" src="http://2.bp.blogspot.com/_M-mdv3Tfarg/SlCotng1RKI/AAAAAAAAANE/K6SgETLNZ8M/s400/main.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5354965458597790882" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;The idea behind the RFW wizard is to create a security filter in the context of each entity data and user role and expose / grant access to records only when a certain condition is met or under a certain restriction.  For example: an organization might want a specific salesperson role to see leads that are connected to a specific product family or allow a low-privileged role to only see accounts that their credit limit is between a certain range.The RFW wizard will always make sure that the returned data is within the security filter parameters. &lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;Another good example that we use is creating a severity or scoring attributes that enables the organization to categorize each entity and then create a security filter that allows users to see data depending on the scoring attribute value. &lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;Actually there is no limit to the type of security filters that you can create. You can construct any filter (simple or complex) using any business logic and utilizing existing or specialized attributes to achieve a high data aware security state.&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;The security filters are setup using the wizard User Interface without the need to write custom code. &lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;The following demonstration is a simple example of how you can take advantage of the RFW wizard.&lt;br /&gt;The first scenario creates a security restriction on the top business unit (a template) which hides accounts that their credit limit is between 100,000 and 1 million $. This means that all the views, quick create and advance find will adhere to this restriction and only return accounts that meet the security filter.&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;The second scenario overrides the template and creates a security filter on the system administrator role. This time the filter is setup on the credit hold attribute returning only accounts that their credit is not held. &lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;The last scenario defines a security filter for a specific marketing manager and allows him to only to view accounts that their category is set to standard.&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;RFW Technical Information:&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;1.Support for Internet Explorer 8,  Internet Facing Deployment - IFD, and all available rollups (1 – 5).&lt;br /&gt;2.Support for all searchable entities (entities that you can search using advanced find)&lt;br /&gt;3.Support for Security Hierarchy i.e. creating business unit templates, overriding the templates for each CRM role and creating specific security filters for users.&lt;br /&gt;4.Supports all views e.g. (public, private , associated , quick find , advanced find , print pages etc)&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;&lt;iframe src="http://www.upsite.co.il/uploaded/files/662_06a1b2cb64190d46acd67c5c0ede3b6a.swf" width="680" height="534" &gt;&lt;/iframe&gt;&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;If you have any questions regarding the RFW wizard post them here or send you enquiry to support@gicrm.com&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3003801964503297388-1029753781301476506?l=mscrm4ever.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mscrm4ever.blogspot.com/feeds/1029753781301476506/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3003801964503297388&amp;postID=1029753781301476506&amp;isPopup=true' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3003801964503297388/posts/default/1029753781301476506'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3003801964503297388/posts/default/1029753781301476506'/><link rel='alternate' type='text/html' href='http://mscrm4ever.blogspot.com/2009/07/crm-40-record-filter-wizard.html' title='CRM 4.0 Record Filter Wizard'/><author><name>Adi Katz</name><uri>http://www.blogger.com/profile/16783428437179825705</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_M-mdv3Tfarg/SlCotng1RKI/AAAAAAAAANE/K6SgETLNZ8M/s72-c/main.jpg' height='72' width='72'/><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3003801964503297388.post-8960349603247095312</id><published>2009-07-01T12:43:00.005+03:00</published><updated>2010-05-05T07:52:09.625+03:00</updated><title type='text'>CRM 4.0 Auto Sharing wizard</title><content type='html'>&lt;div&gt;&lt;/div&gt;&lt;br /&gt;&lt;a href="http://4.bp.blogspot.com/_M-mdv3Tfarg/SlDFU5CivzI/AAAAAAAAANM/jtwBowR3AAU/s1600-h/main.jpg"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 167px;border:1px solid black;" src="http://4.bp.blogspot.com/_M-mdv3Tfarg/SlDFU5CivzI/AAAAAAAAANM/jtwBowR3AAU/s400/main.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5354996919643062066" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;CRM 4.0 permission mechanism and access level rules can be quite limiting. This is especially true when privacy of data is required, visibility of records depends on business decisions or visibility depends on the source (role or user) which triggered the action. On other occasions you need to control visibility of record across sibling business units and business units that are not under the same branch. &lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;Most customers overcome the problem by granting organization access to roles which needs work with data. Of course this is very problematic and reduces the security to minimum and so they usually start looking for solution like field level security which enables the organization to hide certain entity fields from a specific business unit, role or user. &lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;Dynamics does address this problem using a mechanism called sharing. The problem with the sharing mechanism is that it’s a manual process which shifts the responsibility of sharing to the user. This behavior is rarely acceptable especially because sharing rules needs to adhere to specific business decisions and sharing logic. &lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;I’ve seen some very interesting solutions that use dynamics workflow engine to facilitate the sharing of records. However we needed a comprehensive solution and so we developed the Auto Sharing wizard which addresses most scenarios (all of ours anyway). &lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;Following is a technical description of the Auto Sharing wizard and what it can do for you.&lt;br /&gt;&lt;br /&gt;1.Creation of Global Sharing rules - Fired by anyone (We use them as sharing templates and override with specific rules).&lt;br /&gt;    a.When the record is created&lt;br /&gt;    b.When the record match a specific snapshot (state) &lt;br /&gt;&lt;br /&gt;2.Creation of Specific Sharing rules - Fired only when the current user or user role matches the sharing rule source (see video)&lt;br /&gt;    a.When the record is created&lt;br /&gt;    b.When the record match a specific snapshot (state)&lt;br /&gt;&lt;br /&gt;3.Creation of a Sharing hierarchy e.g. Rules setup as global fire first  Rule setup at the Role level may override or add more sharing rules  Rule setup at the User Level overrides the Role level rules and used as Sharing rule exceptions.&lt;br /&gt;&lt;br /&gt;4.Creation of Sharing rules that are integrated as a workflow step&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;Following is a list of scenarios that can be resolved using the wizard&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;Sharing of record only when a specific Role or User triggers the action&lt;br /&gt;Conditional sharing either by using a snapshot query or by integrating the sharing rule in an “IF” workflow routine.&lt;br /&gt;Sharing Record with lower non privileged business units, roles and users&lt;br /&gt;Sharing Records with sibling Business units and Business unit that are not under the same branch&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;ASW Presentation – in order to rewind click on the flash movie; then rewind and play.&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;&lt;iframe src="http://www.upsite.co.il/uploaded/files/662_a488ae0cee078f8fc2d95e5078c6eef9.swf" width="680" height="534"&gt;&lt;/iframe&gt;&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;If you have questions regarding the wizard feel free to ask them here or send them to support@gicrm.com&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3003801964503297388-8960349603247095312?l=mscrm4ever.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mscrm4ever.blogspot.com/feeds/8960349603247095312/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3003801964503297388&amp;postID=8960349603247095312&amp;isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3003801964503297388/posts/default/8960349603247095312'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3003801964503297388/posts/default/8960349603247095312'/><link rel='alternate' type='text/html' href='http://mscrm4ever.blogspot.com/2009/07/crm-40-auto-sharing-wizard.html' title='CRM 4.0 Auto Sharing wizard'/><author><name>Adi Katz</name><uri>http://www.blogger.com/profile/16783428437179825705</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_M-mdv3Tfarg/SlDFU5CivzI/AAAAAAAAANM/jtwBowR3AAU/s72-c/main.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3003801964503297388.post-8552582116361652126</id><published>2009-06-28T06:17:00.004+03:00</published><updated>2009-06-28T06:31:48.122+03:00</updated><title type='text'>CRM 4.0 Working with Strings</title><content type='html'>&lt;div&gt;&lt;/div&gt;&lt;br /&gt;Many CRM development tasks involve manipulation and concatenation of strings, whether it’s concatenating a Field DavaValue from 2 or more fields, building messages that involve dynamic data, constructing FetchXml Requests and Soap messages or constructing strings that contain CSS or other meaningful html information, in one way or another you end up writing a script that looks like the following examples:&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;&lt;pre class="js" name="code"&gt;&lt;br /&gt;//Exampe 1&lt;br /&gt;var someValue = “The account number: ” + crmForm.accountnumber.DataValue + “ is not valid\n”;&lt;br /&gt;    someValue+= “More helpful info on how to fill a valid account number here.”&lt;br /&gt;&lt;br /&gt;    alert(someValue);&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;or &lt;br /&gt;&lt;pre class="js" name="code"&gt;&lt;br /&gt;//Example 2&lt;br /&gt;var iLeft = 100;&lt;br /&gt;var iTop = 100; &lt;br /&gt;var cssText = “position:absolute;left:” + iLeft + “;top:” + iTop + “;”;&lt;br /&gt;document.all[“&lt;Some Element ID&gt;”].style.cssText = cssText;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;or&lt;br /&gt;&lt;pre class="js" name="code"&gt;&lt;br /&gt;//Example 3&lt;br /&gt;var iTop = 50;&lt;br /&gt;var iLeft = 50;&lt;br /&gt;var iWidth = 800;&lt;br /&gt;var iHeight = 600;&lt;br /&gt;var windowFeature = “toolbars=0;width=” + iWidth + “,height=” + iHeight + “,top=” + iTop + “,left=” + iLeft;&lt;br /&gt;window.open( “&lt;Valid Url&gt;” , “&lt;Window Name&gt;” , windowFeatures);&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;There are several disadvantages of using this simple concatenation technique: &lt;br /&gt;1.The first which is the most obvious and &lt;a href="http://mscrm4ever.blogspot.com/2009/06/crm-40-creating-js-resource-manager.html"&gt;discussed here&lt;/a&gt; is the lack of multi-lingual support for text messages. If for instance you need to translate the text that appears in the first example above, how can you accomplish the task and still keep the dynamic value concatenation in the correct context? &lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;2. Another major disadvantage involves construction of large strings such as when you build soap message or a large FetchXml queries which can dramatically decrease IE responsiveness and cause performance issues.&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;3. Large string that are constructed in this manner sometimes make it impossible to read and debug&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;The following JavaScript objects facilitates these types of tasks and will help you avoid the problems mentioned above.&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;The first code snippet facilitates the concatenation of strings using a class called StringBuilder (similar to C# System.Text.StringBuilder mechanism) &lt;br /&gt;&lt;pre class="js" name="code"&gt;&lt;br /&gt;/* JS String  Builder */&lt;br /&gt;StringBuilder = function()&lt;br /&gt;{&lt;br /&gt;      var parts     = [];&lt;br /&gt;      this.Append = function( text ){parts[ parts.length ] = text;return this;}&lt;br /&gt;      this.Reset    = function(){parts = [];}&lt;br /&gt;      this.ToString = function(){return parts.join( "" );}&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;//Usage&lt;br /&gt;function OnCrmPageLoad()&lt;br /&gt;{&lt;br /&gt;    //Solving example 2 &lt;br /&gt;    var iLeft = 100;&lt;br /&gt;    var iTop = 100; &lt;br /&gt;    var cssText = new StringBuilder();&lt;br /&gt;    cssText .Append(“position:absolute;left:”).Append(iLeft).Append( “;top:”).Append(iTop).Append( “;”);&lt;br /&gt;    document.all[“&lt;Some Element ID&gt;”].style.cssText = cssText.ToString();&lt;br /&gt;&lt;br /&gt;    //Solveing example 3&lt;br /&gt;    var iTop = 50;&lt;br /&gt;    var iLeft = 50;&lt;br /&gt;    var iWidth = 800;&lt;br /&gt;    var iHeight = 600;&lt;br /&gt;      &lt;br /&gt;    var windowFeature = new StringBuilder();&lt;br /&gt;    windowFeature.Append(“toolbars=0,”);&lt;br /&gt;    windowFeature.Append(“width=”).Append(iWidth).Append( “,”);&lt;br /&gt;    windowFeature.Append(“height=”).Append(iHeight).Append( “,”);&lt;br /&gt;    windowFeature Append(“top=”).Append(iTop).Append(“,”);&lt;br /&gt;    windowFeature.Append(“left=”).Append(iLeft);&lt;br /&gt;            &lt;br /&gt;    window.open( “&lt;Valid Url&gt;” , “&lt;Window Name&gt;” , windowFeatures.ToString());      &lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;OnCrmPageLaod(); &lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The second code snippet contains an extension function to the built-in JavaScript string object which helps you solve the problem mentioned in example 1 above and also makes it possible to write code similar to the String.Format c# method.&lt;br /&gt;&lt;pre class="js" name="code"&gt;&lt;br /&gt;String.prototype.Format = function( args )&lt;br /&gt;{&lt;br /&gt;    var result = this.replace( /\{(\d{1})\}/ig , &lt;br /&gt;        function match()&lt;br /&gt;        {&lt;br /&gt;            return args[arguments[1]];&lt;br /&gt;        } &lt;br /&gt;    );&lt;br /&gt; &lt;br /&gt;    return result;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;function OnCrmPageLoad()&lt;br /&gt;{&lt;br /&gt;    //Solve Exampe 1&lt;br /&gt;    var someValue =  new StringBuilder();&lt;br /&gt;    someValue.Append(“The account number: {0} is not valid\n”.Format([crmForm.accountnumber.DataValue]));&lt;br /&gt;    someValue.Append(“More helpful info on how to fill a valid account number here.”);&lt;br /&gt;&lt;br /&gt;    alert(someValue.ToString())     &lt;br /&gt;&lt;br /&gt;    //Solve Example 2&lt;br /&gt;    var iTop = 50;&lt;br /&gt;    var iLeft = 50;&lt;br /&gt;    var iWidth = 800;&lt;br /&gt;    var iHeight = 600;&lt;br /&gt;      &lt;br /&gt;    //Create string template with place holders&lt;br /&gt;    var windowFeature = “toolbars=0;top={0},left={1},width={2},height={3}”;&lt;br /&gt;    //Format the String &lt;br /&gt;    windowFeature = windowFeature.Format([iTop,iLeft,iWidht,iHeight]);&lt;br /&gt;    window.open( “&lt;Valid Url&gt;” , “&lt;Window Name&gt;” , windowFeatures);&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;OnCrmPageLoad();&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The last example extends the StringBuilder class and creates a StyleBulder that is used to concatenate CSS rules with ease&lt;br /&gt;&lt;pre class="js" name="code"&gt;&lt;br /&gt;StyleBuilder  = function()&lt;br /&gt;{&lt;br /&gt;    var cssText   = new StringBuilder();&lt;br /&gt;    this.Add  = function( key , value ){cssText.Append( key ).Append( ":" ).Append( value ).Append( ";" );}&lt;br /&gt;    this.ToString = function(){return cssText.ToString();} &lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;//Usage&lt;br /&gt;function OnCrmPageLoad()&lt;br /&gt;{&lt;br /&gt;    //Solve Example 2&lt;br /&gt;    var iLeft = 100;&lt;br /&gt;    var iTop = 100; &lt;br /&gt;    var cssText = new StyleBuilder();&lt;br /&gt;    cssText.Add(“position”,”absolute”);&lt;br /&gt;    cssText.Add(“left”, iLeft);&lt;br /&gt;    cssText.Add(“top”,iTop);&lt;br /&gt;      &lt;br /&gt;    document.all[“&lt;Some Element ID&gt;”].style.cssText = cssText.ToString();     &lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;OnCrmPageLoad();&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;I hope you find this interesting and helpful.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3003801964503297388-8552582116361652126?l=mscrm4ever.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mscrm4ever.blogspot.com/feeds/8552582116361652126/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3003801964503297388&amp;postID=8552582116361652126&amp;isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3003801964503297388/posts/default/8552582116361652126'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3003801964503297388/posts/default/8552582116361652126'/><link rel='alternate' type='text/html' href='http://mscrm4ever.blogspot.com/2009/06/crm-40-working-with-strings.html' title='CRM 4.0 Working with Strings'/><author><name>Adi Katz</name><uri>http://www.blogger.com/profile/16783428437179825705</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3003801964503297388.post-9172609207882819405</id><published>2009-06-27T18:15:00.006+03:00</published><updated>2009-06-27T20:41:57.555+03:00</updated><title type='text'>CRM 4.0 Adding a helper button to text fields</title><content type='html'>&lt;div&gt;&lt;/div&gt;&lt;br /&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 269px; height: 30px;border:1px solid black;" src="http://4.bp.blogspot.com/_M-mdv3Tfarg/SkY4K1MExBI/AAAAAAAAAM8/pqsIK_pDDYY/s400/texthelperbutton.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5352026965903983634" /&gt;&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;Sometimes you want to attach a click event to a text field. Since CRM does not provide a way to associate a button with a CRM field you need to implement the functionality using JavaScript. The following TextHelperButton object helps you achieve that goal for any given text field.&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;When creating an instance of the TextHelperButton set the following parameters:&lt;br /&gt;&lt;strong&gt;Image width&lt;/strong&gt; – This is used to adjust the image positioning. &lt;br /&gt;&lt;strong&gt;MouseOver image URL&lt;/strong&gt; – The image that is displayed when you go over the button.&lt;br /&gt;&lt;strong&gt;MouseOut Image URL&lt;/strong&gt; – The default image URL &lt;br /&gt;&lt;strong&gt;Click&lt;/strong&gt; – A function to call when the user clicks on the image.&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;Paste the code inside the entity onload event and enjoy...&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;&lt;pre class="js" name="code"&gt;&lt;br /&gt;TextHelperButton = function(fieldId)&lt;br /&gt;{&lt;br /&gt;    var fldButton = this;&lt;br /&gt;&lt;br /&gt;    fldButton.Field = crmForm.all[fieldId];&lt;br /&gt;&lt;br /&gt;    if (!fldButton.Field)&lt;br /&gt;    {&lt;br /&gt;        return alert("Unknown Field: " + fieldId);&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    fldButton.Click = null;&lt;br /&gt;    fldButton.Image = new ButtonImage();&lt;br /&gt;    fldButton.Paint = function()&lt;br /&gt;    {&lt;br /&gt;        var field_d = document.all[fldButton.Field.id + "_d"];&lt;br /&gt;        if (field_d)&lt;br /&gt;        {&lt;br /&gt;            field_d.style.whiteSpace = "nowrap";&lt;br /&gt;            field_d.appendChild(fldButton.Image.ToObject())&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt; &lt;br /&gt;    fldButton.MouseOver = function()&lt;br /&gt;    {&lt;br /&gt;        event.srcElement.src = fldButton.Image.MouseOver;&lt;br /&gt;    }&lt;br /&gt; &lt;br /&gt;    fldButton.MouseOut = function()&lt;br /&gt;    {&lt;br /&gt;        event.srcElement.src = fldButton.Image.MouseOut;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    function ButtonImage()&lt;br /&gt;    {&lt;br /&gt;        this.MouseOut  = "";&lt;br /&gt;        this.MouseOver = "";&lt;br /&gt;        this.Width = 21&lt;br /&gt;&lt;br /&gt;        this.ToObject = function()&lt;br /&gt;        {&lt;br /&gt;            var img = document.createElement("IMG");&lt;br /&gt;            img.onmouseover = fldButton.MouseOver;&lt;br /&gt;            img.onmouseout = fldButton.MouseOut;&lt;br /&gt;            img.onclick = fldButton.Click;&lt;br /&gt;            img.src = this.MouseOut;&lt;br /&gt;    &lt;br /&gt;            var cssText = "vertical-align:bottom;";&lt;br /&gt;            cssText+= "margin:1px;";&lt;br /&gt;            cssText+= "position:relative;";&lt;br /&gt;            cssText+= "right:" + (this.Width + 1) + "px";&lt;br /&gt;            img.style.cssText = cssText;&lt;br /&gt;            return img;&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;function OnCrmPageLoad()&lt;br /&gt;{&lt;br /&gt;    /* pass the name of the crm field as parameter */&lt;br /&gt;    var actnButton = new TextHelperButton("name");&lt;br /&gt;        /* set the image button width */&lt;br /&gt;        actnButton.Image.Width = 21; //integer&lt;br /&gt;        /* supply image rollover URLS */&lt;br /&gt;        actnButton.Image.MouseOver = "/_imgs/lookupOn.gif";&lt;br /&gt;        actnButton.Image.MouseOut  = "/_imgs/lookupOff.gif";&lt;br /&gt;        /* supply an function that is called when the image is clicked */&lt;br /&gt;        actnButton.Click = Accountnumber_Click;&lt;br /&gt;        /* add the image next to the field */&lt;br /&gt;        actnButton.Paint();&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;function Accountnumber_Click()&lt;br /&gt;{&lt;br /&gt;    alert('Account Number Field Clicked');&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;OnCrmPageLoad();&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3003801964503297388-9172609207882819405?l=mscrm4ever.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mscrm4ever.blogspot.com/feeds/9172609207882819405/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3003801964503297388&amp;postID=9172609207882819405&amp;isPopup=true' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3003801964503297388/posts/default/9172609207882819405'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3003801964503297388/posts/default/9172609207882819405'/><link rel='alternate' type='text/html' href='http://mscrm4ever.blogspot.com/2009/06/crm-40-adding-helper-button-to-text.html' title='CRM 4.0 Adding a helper button to text fields'/><author><name>Adi Katz</name><uri>http://www.blogger.com/profile/16783428437179825705</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_M-mdv3Tfarg/SkY4K1MExBI/AAAAAAAAAM8/pqsIK_pDDYY/s72-c/texthelperbutton.jpg' height='72' width='72'/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3003801964503297388.post-1282025452670392967</id><published>2009-06-27T04:47:00.004+03:00</published><updated>2009-06-27T05:01:40.518+03:00</updated><title type='text'>CRM 4.0 Creating a JS Resource Manager</title><content type='html'>The purpose of this post is to present a simple and effective way of handling multi-lingual text resources on the client (CRM) Form. &lt;br /&gt;&lt;br /&gt;In most projects an application is built to address a single language. Knowing that from the get go simplifies the way developers weave (hard code) the application messages into each entity form.&lt;br /&gt;&lt;br /&gt;Let’s assume, for example, that you need to validate that the account number starts with the letters “ACT ”. Your code might look like the following example:&lt;br /&gt;&lt;br /&gt;&lt;pre class="js" name="code"&gt;&lt;br /&gt;if (/^ACT\s{1}/.test(crmForm.accountnumber.DataValue) == false)&lt;br /&gt;{&lt;br /&gt;    alert(”Account number must begin with ACT ”);&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;This is a very simple and strait forward way to present messages to the user. However since CRM is a multi-lingual application using this approach is far from being a best practice. The reason is that your client might decide (eventually) to add another language to the system and once he does that you must rewrite you application to support the new language. Assuming you have a small amount of messages you might consider changing your code as follows:&lt;br /&gt;&lt;br /&gt;&lt;pre class="js" name="code"&gt;&lt;br /&gt;if (/^ACT\s{1}/.test(crmForm.accountnumber.DataValue) == false)&lt;br /&gt;{&lt;br /&gt;    if (USER_LANGUAGE_CODE == 1033) //English United States&lt;br /&gt;    {&lt;br /&gt;        alert(”Account number must begin with ACT ”);&lt;br /&gt;    }&lt;br /&gt;    else if (USER_LANGUAGE_CODE == 1043) //Dutch&lt;br /&gt;    {&lt;br /&gt;        alert(”Rekeningnummer moet beginnen met ACT ”);&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Now, as long as the application stays small this solution should hold. However, applications tend to grow over time and from a certain point the amount of messages will be too overwhelming to manage in this manner. &lt;br /&gt;&lt;br /&gt;Another reason why this approach is a bad practice is that is obligates or ties the client to an ever lasting development phase. The best approach is to shift the responsibility of handling multi-lingual tasks to the client and the best way to do that is to create a resource manager that enables you to support multi-lingual messages from your first line of code.&lt;br /&gt;&lt;br /&gt;So how do we shift responsibility of translating our application messages to the client?&lt;br /&gt;The simplest approach is to create a new text attribute for each message that you need to display. The text attribute display name can hold the message it self. For example:&lt;br /&gt;&lt;br /&gt;New Attribute: new_msginvalidaccountnumber &lt;br /&gt;Display Name: The account number must begin with “ACT “&lt;br /&gt;Searchable: No&lt;br /&gt;Required:No&lt;br /&gt;&lt;br /&gt;Now, put the new attribute on the CRM form under a new tab called Labels and hide it when the form loads. E.g.&lt;br /&gt;&lt;br /&gt;&lt;pre class="js" name="code"&gt;&lt;br /&gt;function OnCrmPageLoad()&lt;br /&gt;{ &lt;br /&gt;    document.all.tab4Tab.style.display = “none” //assuming that the fifth tab is the Labels tab.&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;OnCrmPageLoad();&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Now, let’s transform the above code so it would support any language&lt;br /&gt;&lt;br /&gt;&lt;pre class="js" name="code"&gt;&lt;br /&gt;/* --&lt; Resource Manager &gt;-- */&lt;br /&gt;ResourceManager = function()&lt;br /&gt;{&lt;br /&gt;        var rm = this;&lt;br /&gt;        rm.GetString = function( resId )&lt;br /&gt;        {&lt;br /&gt;            var resource = document.getElementById( resId ); &lt;br /&gt;            if ( !resource ) &lt;br /&gt;            {&lt;br /&gt;                /* Show missing label */&lt;br /&gt;                return "[" + resId + "]";&lt;br /&gt;            }&lt;br /&gt;                            &lt;br /&gt;            return crmForm.GetLabel( resource );&lt;br /&gt;        } &lt;br /&gt;}&lt;br /&gt;/* Create an instance of Resource Manager */&lt;br /&gt;RM = new ResourceManager();&lt;br /&gt;&lt;br /&gt;if (/^ACT\s{1}/.test(crmForm.accountnumber.DataValue) == false)&lt;br /&gt;{&lt;br /&gt;    alert(RM.GetString(“new_msginvalidaccountnumber”));&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Integrate the resource manager to each entity onload event.&lt;br /&gt;&lt;br /&gt;Good luck…&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3003801964503297388-1282025452670392967?l=mscrm4ever.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mscrm4ever.blogspot.com/feeds/1282025452670392967/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3003801964503297388&amp;postID=1282025452670392967&amp;isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3003801964503297388/posts/default/1282025452670392967'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3003801964503297388/posts/default/1282025452670392967'/><link rel='alternate' type='text/html' href='http://mscrm4ever.blogspot.com/2009/06/crm-40-creating-js-resource-manager.html' title='CRM 4.0 Creating a JS Resource Manager'/><author><name>Adi Katz</name><uri>http://www.blogger.com/profile/16783428437179825705</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3003801964503297388.post-5281552475465021656</id><published>2009-06-12T13:53:00.007+03:00</published><updated>2009-06-12T14:32:20.056+03:00</updated><title type='text'>Summarizing an Appointment field on a Parent Entity</title><content type='html'>&lt;div&gt;&lt;/div&gt;&lt;br /&gt;Imagine you have a custom field called number of participants on and appointment entity and you want to summarize this field for all the appointments under the same regarding parent.&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;This looks simple at first glance i.e. register a plug-in on the &lt;b&gt;parent&lt;/b&gt; post Create and Update messages and you’re done. However, since this entity is part of dynamics scheduling engine and affects rescheduling you also need to register your plug-in on the Book and Reschedule messages. &lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;Actually the Book and Reschedule messages somewhat replace the Create and Update messages. That means that when you create a new Appointment the Book message is fired and when you Update an existing appointment the reschedule message is fired. &lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;So why do we need to also register the plug-in on the Create and Update messages? &lt;br /&gt;The answer is that when you reschedule or book an appointment and the user is not available at that point in time the scheduling engine halts the execution and enables the user to cancel the operation. If the user decide to disregard (ignore) that warning and save anyway then the Create or Update are fired to complete the operation.&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;Since the summarizing of the field can be a long operation you should also register the plug-in for asynchronous execution. &lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;Another thing that is worth mentioning is code synchronization. Since CRM does not lock record and two users can update the same appointment at the same time it is also advisable to lock the process that updates the parent field to avoid data corruption.&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;Last Important remark: the regarding lookup (parent entity) is required for this operation. Since the Book and Reschedule messages don’t allow you to register Post Images you need to send the regarding field in each operation. E.g. &lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;//Put this code in the appointment onload event handler&lt;br /&gt;&lt;pre class='js' name='code'&gt;&lt;br /&gt;if (crmForm.regardingobjectid.DataValue != null)&lt;br /&gt;{&lt;br /&gt;    crmForm.regardingobjectid.ForceSumbit = true;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;Plug-in Registered on the Reschedule, Book, Create and Update Asynchronous Parent Pipeline&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;&lt;pre class='csharp' name='code'&gt;&lt;br /&gt;using System;&lt;br /&gt;using System.Collections.Generic;&lt;br /&gt;using System.Text;&lt;br /&gt;using Microsoft.Crm.Sdk;&lt;br /&gt;using Microsoft.Crm.SdkTypeProxy;&lt;br /&gt;using Microsoft.Crm.Sdk.Query;&lt;br /&gt;&lt;br /&gt;namespace GI.Crm.Sandbox&lt;br /&gt;{&lt;br /&gt; &lt;br /&gt; public class SummarizeAppointmentFieldHandler : IPlugin&lt;br /&gt; {&lt;br /&gt;     private Object SyncObject = new Object();&lt;br /&gt;  &lt;br /&gt;     public void Execute(IPluginExecutionContext context)&lt;br /&gt;     {&lt;br /&gt;          /* Validates that the user is available */&lt;br /&gt;          if (context.OutputParameters.Contains(ParameterName.ValidationResult))&lt;br /&gt;          {&lt;br /&gt;               ValidationResult validationResult = &lt;br /&gt;                    context.OutputParameters[ParameterName.ValidationResult] as ValidationResult;&lt;br /&gt;               if (validationResult.ValidationSuccess == false)&lt;br /&gt;               {&lt;br /&gt;                    return;&lt;br /&gt;               }&lt;br /&gt;          }&lt;br /&gt;            &lt;br /&gt;          /* Validate Target Entity */&lt;br /&gt;          if (!context.InputParameters.Contains(ParameterName.Target))&lt;br /&gt;          {&lt;br /&gt;               return;&lt;br /&gt;          }&lt;br /&gt;            &lt;br /&gt;          DynamicEntity Target = context.InputParameters[ParameterName.Target] as DynamicEntity;&lt;br /&gt;            &lt;br /&gt;          /* &lt;br /&gt;               We need both regarding (parent) entity id and number of participants field &lt;br /&gt;               Client side must Force Submit on regarding field  &lt;br /&gt;          */&lt;br /&gt;          if ( !Target.Properties.Contains("regardingobjectid") ||&lt;br /&gt;               !Target.Properties.Contains("gi_noofpart"))&lt;br /&gt;          {&lt;br /&gt;               return;&lt;br /&gt;          }&lt;br /&gt;            &lt;br /&gt;          Lookup regardingObjectId = Target.Properties["regardingobjectid"] as Lookup;&lt;br /&gt;          /* Validate that the Appointment is Regarding the Correct parent entity */&lt;br /&gt;          if (regardingObjectId.type != "gi_custom")&lt;br /&gt;          {&lt;br /&gt;               return;       &lt;br /&gt;          }&lt;br /&gt;&lt;br /&gt;          CrmNumber noofPart = Target.Properties["gi_noofpart"] as CrmNumber;&lt;br /&gt;            &lt;br /&gt;          /* Validate the number of Participants */&lt;br /&gt;          if (noofPart.Value == 0)&lt;br /&gt;          {&lt;br /&gt;               return;&lt;br /&gt;          }&lt;br /&gt;            &lt;br /&gt;          /* Synchronize access to this code block */&lt;br /&gt;          lock(SyncObject)&lt;br /&gt;          {&lt;br /&gt;               #region Retrieve all Regarding entity Appointments&lt;br /&gt;               QueryExpression allPartQuery = new QueryExpression();&lt;br /&gt;               allPartQuery.ColumnSet = new ColumnSet();&lt;br /&gt;               allPartQuery.ColumnSet.AddColumn("gi_noofpart");&lt;br /&gt;               allPartQuery.Criteria.AddCondition(&lt;br /&gt;                    "regardingobjectid" , ConditionOperator.Equal , regardingObjectId.Value&lt;br /&gt;               );&lt;br /&gt;               allPartQuery.Distinct = false;&lt;br /&gt;               allPartQuery.EntityName = EntityName.appointment.ToString();&lt;br /&gt;    &lt;br /&gt;               RetrieveMultipleRequest retMultiRequest = new RetrieveMultipleRequest();&lt;br /&gt;               retMultiRequest.Query = allPartQuery;&lt;br /&gt;               retMultiRequest.ReturnDynamicEntities = true;&lt;br /&gt;    &lt;br /&gt;               RetrieveMultipleResponse retMultiResponse = &lt;br /&gt;                    (RetrieveMultipleResponse)context.CreateCrmService(true).Execute(retMultiRequest);&lt;br /&gt;    &lt;br /&gt;               #endregion&lt;br /&gt;    &lt;br /&gt;               #region Summaries all Appointments Number of Participants&lt;br /&gt;               Int32 Summery = 0; &lt;br /&gt;               foreach(DynamicEntity appointment in retMultiResponse.BusinessEntityCollection.BusinessEntities)&lt;br /&gt;               {&lt;br /&gt;                    if (appointment.Properties.Contains("gi_noofpart"))&lt;br /&gt;                    {&lt;br /&gt;                         Summery += ((CrmNumber)appointment.Properties["gi_noofpart"]).Value; &lt;br /&gt;                    }&lt;br /&gt;               }&lt;br /&gt;               #endregion&lt;br /&gt;    &lt;br /&gt;               #region Update Parent entity Number of Participants&lt;br /&gt;               /* Key Property */&lt;br /&gt;               Key parentEntityKey = new Key(regardingObjectId.Value);&lt;br /&gt;               KeyProperty parentEntityKeyProp = new KeyProperty("gi_customid", parentEntityKey);&lt;br /&gt;               /* Number of participants Property */&lt;br /&gt;               CrmNumberProperty noofPartProp = new CrmNumberProperty("gi_noofpart",new CrmNumber(Summery));&lt;br /&gt;    &lt;br /&gt;               DynamicEntity parentEntity = new DynamicEntity(regardingObjectId.type);&lt;br /&gt;               parentEntity.Properties.Add(parentEntityKeyProp);&lt;br /&gt;               parentEntity.Properties.Add(noofPartProp);&lt;br /&gt;    &lt;br /&gt;               TargetUpdateDynamic targetEntity = new TargetUpdateDynamic();&lt;br /&gt;               targetEntity.Entity = parentEntity; &lt;br /&gt;               UpdateRequest updateRequest = new UpdateRequest();&lt;br /&gt;               updateRequest.Target = targetEntity;&lt;br /&gt;               context.CreateCrmService(true).Execute(updateRequest);&lt;br /&gt;               #endregion&lt;br /&gt;          }           &lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3003801964503297388-5281552475465021656?l=mscrm4ever.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mscrm4ever.blogspot.com/feeds/5281552475465021656/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3003801964503297388&amp;postID=5281552475465021656&amp;isPopup=true' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3003801964503297388/posts/default/5281552475465021656'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3003801964503297388/posts/default/5281552475465021656'/><link rel='alternate' type='text/html' href='http://mscrm4ever.blogspot.com/2009/06/summarizing-appointment-field-on-parent.html' title='Summarizing an Appointment field on a Parent Entity'/><author><name>Adi Katz</name><uri>http://www.blogger.com/profile/16783428437179825705</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3003801964503297388.post-5964324812507094785</id><published>2009-06-11T12:14:00.006+03:00</published><updated>2010-05-05T07:52:37.448+03:00</updated><title type='text'>CRM 4.0 Workflow Query Wizard</title><content type='html'>&lt;div&gt;&lt;/div&gt;&lt;br /&gt;The WQW wizard is a specialized query builder that we integrated into dynamics workflow engine. The wizard facilitates the creation of aggregate queries without the need to write custom code for each requirement that we have. This helps us create various complex workflow rules that are not available by the out of box workflow designer. A good usage example is when you need to create Monitoring / Alerting workflows or need to take some action depending on existing or non-existing amount of records that answer a specific set of conditions. &lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;The WQW also facilitates the creation of specific business counters on the parent entity. This is done by updating the parent entity with the WQW result using the workflow built in functionality (update record step). One of the biggest advantages of using the WQW and the business counters is that it can help you create not-in queries. &lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;Consider, for example, a scenario where you need to find accounts without contacts or open incidents.&lt;br /&gt;by creating a WQW workflow step and a new account attribute (counter attribute) called number of accounts / number of open incidents you can write the WQW result to each attribute counter. This enables the user to retrieve all accounts that have 0 contacts / or 0 open incidents in each respective account counter.&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;The video demonstration illustrates 2 simple scenarios that make the WQW such a wonderful workflow addition.&lt;br /&gt;The first scenario monitors the amount of long duration outgoing calls that are made by a user. The second scenario alerts a manager when too many on-hold cases are open for a specific user. These are just examples. There is no limit on the type of Alerts or Actions that you can take using the wizard. As long as you are able to build a query around your requirement the wizard will facilitate the actions and enable you to easily create workflow rules depending on the their results.&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;Another feature that the WQW has to offer is the dynamic binding of values in the current workflow context. If you take a look at the second scenario you’ll notice that the WQW uses the owner attribute dynamically. Since the owner is not known at design time the values are taken from the context at runtime and integrated into the query. &lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;I’m sure this addition will save you hours of tedious development effort. If you have question regarding the wizard fill free to post them here.&lt;br /&gt;The wizard will be available on our website next week. Enjoy…&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;In order to rewind right click on the flash movie and select play&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;&lt;iframe src="http://www.upsite.co.il/uploaded/files/662_161d7300581babbf835d76ad09225647.swf" width="670" height="526" &gt;&lt;/iframe&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3003801964503297388-5964324812507094785?l=mscrm4ever.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mscrm4ever.blogspot.com/feeds/5964324812507094785/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3003801964503297388&amp;postID=5964324812507094785&amp;isPopup=true' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3003801964503297388/posts/default/5964324812507094785'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3003801964503297388/posts/default/5964324812507094785'/><link rel='alternate' type='text/html' href='http://mscrm4ever.blogspot.com/2009/06/crm-40-workflow-query-wizard.html' title='CRM 4.0 Workflow Query Wizard'/><author><name>Adi Katz</name><uri>http://www.blogger.com/profile/16783428437179825705</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3003801964503297388.post-924839216550887090</id><published>2009-06-10T01:03:00.011+03:00</published><updated>2010-05-05T07:53:02.477+03:00</updated><title type='text'>CRM 4.0 Queue Security Manager Wizard</title><content type='html'>&lt;div&gt;&lt;/div&gt;&lt;br /&gt;&lt;a href="http://1.bp.blogspot.com/_M-mdv3Tfarg/Si7fk1iBJvI/AAAAAAAAAM0/Jgg1h44PpkM/s1600-h/Main.jpg"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 224px;border:1px solid black;" src="http://1.bp.blogspot.com/_M-mdv3Tfarg/Si7fk1iBJvI/AAAAAAAAAM0/Jgg1h44PpkM/s400/Main.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5345455631673403122" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;As you know, Queues in CRM are organizational entities. This means that if the user role has read access rights on the Queue entity he can see all Queues. Organization that uses Queues as a concept usually search for a way to moderate Queue access based on Business Unit, Role and sometimes specific Users. This type of requirement is especially important for call centers and application that use queues as intermediary facilities or what I call bus stops for work that spin many users and business processes. &lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;Our Product also makes substantial use of Queues throughout our solution. Knowing that the way CRM handles Queues will not serve the purpose we built a wizard that enables us to define Queue visibility for any security settings. If you’re looking for a wizard like solution that enables you to control Queue access with a few clicks (No Coding Required) then you’ll love this wizard. &lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;The QUM wizard, as all our wizards, supports rollup4, IE8, IFD and can be installed on a 64 bit environment. &lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;&lt;iframe height="502" width="670" src="http://www.upsite.co.il/uploaded/files/662_8b6bb99291f03e9c607a1ab354f9657b.swf"&gt;&lt;/iframe&gt;&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;The wizard can be obtained as a stand alone product however we also included it in our security package and partner starter pack for no additional cost. &lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;&lt;a href="http://www.gicrm.com/?categoryId=29264&amp;itemId=75349"&gt;The wizard is now available on GI online website&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3003801964503297388-924839216550887090?l=mscrm4ever.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mscrm4ever.blogspot.com/feeds/924839216550887090/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3003801964503297388&amp;postID=924839216550887090&amp;isPopup=true' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3003801964503297388/posts/default/924839216550887090'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3003801964503297388/posts/default/924839216550887090'/><link rel='alternate' type='text/html' href='http://mscrm4ever.blogspot.com/2009/06/crm-40-queue-manager-wizard.html' title='CRM 4.0 Queue Security Manager Wizard'/><author><name>Adi Katz</name><uri>http://www.blogger.com/profile/16783428437179825705</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_M-mdv3Tfarg/Si7fk1iBJvI/AAAAAAAAAM0/Jgg1h44PpkM/s72-c/Main.jpg' height='72' width='72'/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3003801964503297388.post-4958866193538935847</id><published>2009-05-31T01:36:00.014+03:00</published><updated>2010-03-09T18:14:31.862+02:00</updated><title type='text'>CRM 4.0 Embedding User Signature in CRM Web Client</title><content type='html'>One of the main features that the CRM email web client is missing is an automatic stationary signature. Although a user (or an administrator) can create a predefined signature and embed it before sending the email the web client requires the user to fill the TO (or CC) field in advance and then pick out the signature template using the Insert Template button. For most of us who gotten used to using advanced editors like outlook this seems like a major step backwards. &lt;br /&gt;&lt;br /&gt;In order to enhance the user experience and demonstrate to strength and simplicity of dynamics while I’m at it I wrote a post that automates the process.  The automated signature makes use of CRM’s email template feature. Once the Global email template (signature) is in place you need to extract its id (templateid) and use it in your code. &lt;br /&gt;&lt;br /&gt;In order to retrieve the template id you can run this following query against the filteredtemplate view:&lt;br /&gt;&lt;pre class='sql' name='code'&gt;&lt;br /&gt;SELECT TEMPLATEID FROM FILTEREDTEMPLATE WHERE TITLE = ‘USER SIGNATURE’ &lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The nice thing about dynamics is that most UI features also have an API manifestation. In this case it’s the ability to instantiate email templates by using the InstantiateTemplateRequest. The InstantiateTemplateRequest receives a templateid, objectid and objecttype. The objectid and type are used as context so the template is able to retrieve information that is specific to the recipient entity. Since we are only interested in the user information the objectid and type are filled with the email owner which is the current user.&lt;br /&gt;&lt;br /&gt;Copy the following code to the email onload event and enjoy…&lt;br /&gt;&lt;br /&gt;&lt;pre class='js' name='code'&gt;&lt;br /&gt;function Signature(companyTemplateId)&lt;br /&gt;{&lt;br /&gt;    var sig = this;&lt;br /&gt;    var emailIframe;&lt;br /&gt;    var emailBody;&lt;br /&gt;   &lt;br /&gt;    sig.TemplateId = companyTemplateId;&lt;br /&gt;   &lt;br /&gt;    sig.Load = function()&lt;br /&gt;    {&lt;br /&gt;        try&lt;br /&gt;        {   &lt;br /&gt;            var xml = '' + &lt;br /&gt;                '&lt;?xml version="1.0" encoding="utf-8"?&gt;' + &lt;br /&gt;                '&lt;soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  xmlns:xsd="http://www.w3.org/2001/XMLSchema"&gt;' + &lt;br /&gt;                GenerateAuthenticationHeader() +&lt;br /&gt;                '  &lt;soap:Body&gt;' + &lt;br /&gt;                '    &lt;Execute xmlns="http://schemas.microsoft.com/crm/2007/WebServices"&gt;' + &lt;br /&gt;                '      &lt;Request xsi:type="InstantiateTemplateRequest"  ReturnDynamicEntities="false"&gt;' + &lt;br /&gt;                '        &lt;TemplateId&gt;' + sig.TemplateId + '&lt;/TemplateId&gt;' + &lt;br /&gt;                '        &lt;ObjectType&gt;' + crmForm.ownerid.DataValue[0].typename + '&lt;/ObjectType&gt;' + &lt;br /&gt;                '        &lt;ObjectId&gt;' + crmForm.ownerid.DataValue[0].id + '&lt;/ObjectId&gt;' + &lt;br /&gt;                '      &lt;/Request&gt;' + &lt;br /&gt;                '    &lt;/Execute&gt;' + &lt;br /&gt;                '  &lt;/soap:Body&gt;' + &lt;br /&gt;                '&lt;/soap:Envelope&gt;' + &lt;br /&gt;                '';&lt;br /&gt;&lt;br /&gt;            var xmlHttpRequest = new ActiveXObject("Msxml2.XMLHTTP");&lt;br /&gt;            xmlHttpRequest.open("POST", "/mscrmservices/2007/CrmService.asmx", false);&lt;br /&gt;            xmlHttpRequest.setRequestHeader("SOAPAction","http://schemas.microsoft.com/crm/2007/WebServices/Execute");&lt;br /&gt;            xmlHttpRequest.setRequestHeader("Content-Type", "text/xml; charset=utf-8");&lt;br /&gt;            xmlHttpRequest.setRequestHeader("Content-Length", xml.length);&lt;br /&gt;            xmlHttpRequest.send(xml);&lt;br /&gt;     &lt;br /&gt;            var resultXml = xmlHttpRequest.responseXML;&lt;br /&gt;            if (xmlHttpRequest.status == 200)&lt;br /&gt;            {&lt;br /&gt;                emailBody = resultXml.selectSingleNode("//q1:description").text;    &lt;br /&gt;                emailIframeReady();&lt;br /&gt;            }&lt;br /&gt;        }&lt;br /&gt;        catch(err)&lt;br /&gt;        {&lt;br /&gt;            alert(err.description);&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;   &lt;br /&gt;    function emailIframeReady()&lt;br /&gt;    {&lt;br /&gt;        if (emailIframe.readyState != 'complete')&lt;br /&gt;        {&lt;br /&gt;            return;&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        emailIframe.contentWindow.document.body.innerHTML = emailBody;&lt;br /&gt;    } &lt;br /&gt;   &lt;br /&gt;    emailIframe = document.all.descriptionIFrame;&lt;br /&gt;    emailIframe.onreadystatechange = emailIframeReady;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;function OnCrmPageLoad()&lt;br /&gt;{&lt;br /&gt;    if (crmForm.FormType == 1)&lt;br /&gt;    {&lt;br /&gt;        var signature = new Signature("90886EF8-1A4D-DE11-9CF8-0003FF230264");&lt;br /&gt;        signature.Load();&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;OnCrmPageLoad();&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3003801964503297388-4958866193538935847?l=mscrm4ever.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mscrm4ever.blogspot.com/feeds/4958866193538935847/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3003801964503297388&amp;postID=4958866193538935847&amp;isPopup=true' title='27 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3003801964503297388/posts/default/4958866193538935847'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3003801964503297388/posts/default/4958866193538935847'/><link rel='alternate' type='text/html' href='http://mscrm4ever.blogspot.com/2009/05/crm-40-embedding-user-signature-in-crm.html' title='CRM 4.0 Embedding User Signature in CRM Web Client'/><author><name>Adi Katz</name><uri>http://www.blogger.com/profile/16783428437179825705</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>27</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3003801964503297388.post-196176235796026993</id><published>2009-05-30T15:34:00.002+03:00</published><updated>2009-05-30T15:41:51.564+03:00</updated><title type='text'>CRM 4.0 Finding Entity URL</title><content type='html'>This is a small trick you can use to find what URL is stored behind each CRM vanilla (customizable) entity. You can use as part of your code (see my post about cloning an entity using JavaScript) or just run it in IE address bar. Although it seems this function is not going anywhere there is a chance it won’t be there on the next version.&lt;br /&gt;&lt;br /&gt;Address bar function:&lt;br /&gt;&lt;pre class="js" name="code"&gt;javascript:void( alert( getObjUrl( 2 ) ) )&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The getObjUrl function utilizes the GetWindowInformation function which receives the entity object type code e.g. 2 – contact and return an object containing the entity Url and window size (width and height). Here is an example of how to use it inside your code:&lt;br /&gt;&lt;pre class="js" name="code"&gt;&lt;br /&gt;    var contactUrlInfo = GetWindowInformation(2);&lt;br /&gt;    var windowFeatures = “height=” + contactUrlInfo.Height + “,width=” + contactUrlInfo.Width + “,toolbars=0”;&lt;br /&gt;    window.open( prependOrgName( contactUrlInfo.Url ) , “contact window” , windowFeatures );&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;So how does this work?&lt;br /&gt;The GetWindowInformation function refers to “/_common/windowinformation/windowinformation.aspx” URL. If you open this page in the address bar and take a look at the page source you’ll see a script that look like this:&lt;br /&gt;&lt;br /&gt;&lt;pre class='js' name='code'&gt;&lt;br /&gt;function CRMWindowInfo(sUrl, iXOffset, iYOffset)&lt;br /&gt;{&lt;br /&gt; this.Width = parseInt(iXOffset, 10);&lt;br /&gt; this.Height = parseInt(iYOffset, 10);&lt;br /&gt; this.Url = sUrl;&lt;br /&gt;}&lt;br /&gt;function GetWindowInformation(iObjectType) {&lt;br /&gt;   switch (parseInt(iObjectType, 10)) &lt;br /&gt;   {&lt;br /&gt;      case Account: return new CRMWindowInfo("sfa/accts/edit.aspx",1000,560);&lt;br /&gt;      case List: return new CRMWindowInfo("ma/lists/edit.aspx",820,560);&lt;br /&gt;      //and so on…&lt;br /&gt;   }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;In order not to rely on ms functionality you can either work with static values or create your own page under the isv folder the returns similar js.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3003801964503297388-196176235796026993?l=mscrm4ever.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mscrm4ever.blogspot.com/feeds/196176235796026993/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3003801964503297388&amp;postID=196176235796026993&amp;isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3003801964503297388/posts/default/196176235796026993'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3003801964503297388/posts/default/196176235796026993'/><link rel='alternate' type='text/html' href='http://mscrm4ever.blogspot.com/2009/05/crm-40-finding-entity-url.html' title='CRM 4.0 Finding Entity URL'/><author><name>Adi Katz</name><uri>http://www.blogger.com/profile/16783428437179825705</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3003801964503297388.post-5673221179492972313</id><published>2009-05-29T10:17:00.003+03:00</published><updated>2009-07-15T05:49:48.337+03:00</updated><title type='text'>CRM 4.0 Show Associated-View in IFRAME (AssocViewer)</title><content type='html'>This post is complementary to the other posts that discuss presenting a CRM gird inside an IFRAME. As usually when you display an associated view inside an IFRME some of MS functionality breaks e.g. the automatic refresh when you add an existing member. This post uses the same technique as the N2NViewer but overrides the ms _locAssocOneToMany function in order to know when to refresh the grid.&lt;br /&gt;&lt;br /&gt;The object exposes the following properties:&lt;br /&gt;1. ParentId – this is the parent entity key. If not specified the viewer uses the current entity crmForm.ObjectId.&lt;br /&gt;2. ParentOtc – this is the parent object type code. Again if not specified the crmForm.ObjectTypeCode is used.&lt;br /&gt;3. RelationshipName – this is the relationship id as it appears in customization.&lt;br /&gt;4. Tabset – this is the name of the tabset parameter which is required for the associated view to work. Use the iedevtoolbar to extract this parameter.&lt;br /&gt;&lt;br /&gt;Enjoy...&lt;br /&gt;&lt;br /&gt;&lt;pre class='js' name='code'&gt;&lt;br /&gt;function OnCrmPageLoad() &lt;br /&gt;{ &lt;br /&gt;   var assocViewer = new AssocViewer("IFRAME_RelatedContacts");&lt;br /&gt;   /* Optional - crmForm.ObjectId */&lt;br /&gt;   assocViewer.ParentId = 'D74D44AD-36D0-DC11-AA32-0003FF33509E';&lt;br /&gt;   /* Optional - crmForm.ObjectTypeCode */&lt;br /&gt;   assocViewer.ParentOtc = 1;&lt;br /&gt;   /* Mandatory - relationship schema name */&lt;br /&gt;   assocViewer.RelationshipName = "contact_customer_accounts"&lt;br /&gt;   /* Mandatory - tabset query string parameter */&lt;br /&gt;   assocViewer.Tabset = "areaContacts";&lt;br /&gt;   assocViewer.Load(); &lt;br /&gt;} &lt;br /&gt;&lt;br /&gt;function AssocViewer(iframeId)&lt;br /&gt;{&lt;br /&gt;   var av = this;&lt;br /&gt;   if (crmForm.FormType == 1)&lt;br /&gt;   {&lt;br /&gt;      return;&lt;br /&gt;   }&lt;br /&gt; &lt;br /&gt;   av.IFrame = document.all[iframeId];&lt;br /&gt;   if (!av.IFrame)&lt;br /&gt;   {&lt;br /&gt;      alert("Iframe " + iframeId + " is missing!");&lt;br /&gt;   }&lt;br /&gt; &lt;br /&gt;   var _locAssocOneToMany = null;&lt;br /&gt;   av.ParentId = crmForm.ObjectId;&lt;br /&gt;   av.ParentOtc = crmForm.ObjectTypeCode;&lt;br /&gt;   av.Tabset = null;&lt;br /&gt;   av.RelationshipName = null;&lt;br /&gt; &lt;br /&gt;   av.Load = function()&lt;br /&gt;   {&lt;br /&gt;      if (av.ParentId == null || av.ParentOtc == null || av.Tabset == null || av.RelationshipName == null)&lt;br /&gt;      {&lt;br /&gt;         return alert("Missing Parameters: ParentId or ParentOtc or Tabset Name!");&lt;br /&gt;      }&lt;br /&gt;      var security = crmFormSubmit.crmFormSubmitSecurity.value;&lt;br /&gt;      av.IFrame.src = "areas.aspx?oId=" + av.ParentId + "&amp;oType=" + av.ParentOtc + "&amp;security=" + security + "&amp;tabSet=" + av.Tabset&lt;br /&gt;      av.IFrame.onreadystatechange = av.OnIframeReady;&lt;br /&gt;   }&lt;br /&gt; &lt;br /&gt;   av.OnIframeReady = function()&lt;br /&gt;   {&lt;br /&gt;      if (av.IFrame.readyState != 'complete')&lt;br /&gt;      {&lt;br /&gt;         return;&lt;br /&gt;      }&lt;br /&gt;  &lt;br /&gt;      av.IFrame.contentWindow.document.body.scroll = "no";&lt;br /&gt;      av.IFrame.contentWindow.document.body.childNodes[0].rows[0].cells[0].style.padding = "0px";&lt;br /&gt;&lt;br /&gt;      _locAssocOneToMany = locAssocOneToMany;&lt;br /&gt;      locAssocOneToMany  = av.locAssocOneToMany;   &lt;br /&gt;   }&lt;br /&gt; &lt;br /&gt;   av.locAssocOneToMany = function(iType, sRelationshipName)&lt;br /&gt;   {&lt;br /&gt;      _locAssocOneToMany(iType,sRelationshipName);&lt;br /&gt;      if (sRelationshipName == av.RelationshipName)&lt;br /&gt;      {&lt;br /&gt;         av.IFrame.contentWindow.document.all.crmGrid.Refresh();&lt;br /&gt;      }&lt;br /&gt;   }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;//Entry Point &lt;br /&gt;OnCrmPageLoad();&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3003801964503297388-5673221179492972313?l=mscrm4ever.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mscrm4ever.blogspot.com/feeds/5673221179492972313/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3003801964503297388&amp;postID=5673221179492972313&amp;isPopup=true' title='35 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3003801964503297388/posts/default/5673221179492972313'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3003801964503297388/posts/default/5673221179492972313'/><link rel='alternate' type='text/html' href='http://mscrm4ever.blogspot.com/2009/05/crm-40-show-associated-view-in-iframe.html' title='CRM 4.0 Show Associated-View in IFRAME (AssocViewer)'/><author><name>Adi Katz</name><uri>http://www.blogger.com/profile/16783428437179825705</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>35</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3003801964503297388.post-4462207419010528868</id><published>2009-05-26T11:59:00.003+03:00</published><updated>2009-05-26T12:03:23.828+03:00</updated><title type='text'>CRM 4.0 Multi Lingual Support in Plug-ins</title><content type='html'>The following code presents a simple yet very effective way to handle and return multi-lingual error messages form a plug-in.&lt;br /&gt;&lt;br /&gt;So how does it work?&lt;br /&gt;&lt;br /&gt;Basically I created a base class for Custom Exceptions. All custom exceptions that you use in your code derive from this class. The base class overrides the Exception class Message property and returns the error message from a resources dictionary.&lt;br /&gt;&lt;br /&gt;The resource dictionary holds the translations for each local id e.g. (1033, 3082). When an exception is thrown the user local id (LCID) is taken from the current running thread Culture Info object.&lt;br /&gt;&lt;br /&gt;The Inner translation dictionary manages the each error message depending on the exception type. This way when the code throws a specific exception the correct error message is fetched from the dictionary.&lt;br /&gt;&lt;br /&gt;Enjoy…&lt;br /&gt;&lt;br /&gt;&lt;pre class='csharp' name='code'&gt;&lt;br /&gt;using System;&lt;br /&gt;using System.Collections.Generic;&lt;br /&gt;using System.Text;&lt;br /&gt;using Microsoft.Crm.Sdk;&lt;br /&gt;using Microsoft.Crm.SdkTypeProxy;&lt;br /&gt;using Microsoft.Crm.Sdk.Query;&lt;br /&gt;using System.Web.Services.Protocols;&lt;br /&gt;&lt;br /&gt;namespace Empty.PlugIn&lt;br /&gt;{&lt;br /&gt; public class Handler : IPlugin&lt;br /&gt; {&lt;br /&gt;  #region IPlugin Members&lt;br /&gt;  /// &amp;lt;summary&amp;gt;&lt;br /&gt;  /// Resource Dictionary&lt;br /&gt;  /// &amp;lt;/summary&amp;gt;&lt;br /&gt;  public static Dictionary&amp;lt;Int32, Dictionary&amp;lt;Type, String&amp;gt;&amp;gt; Resources;&lt;br /&gt;  public void Execute(IPluginExecutionContext context)&lt;br /&gt;  {&lt;br /&gt;   if (Resources == null)&lt;br /&gt;   {&lt;br /&gt;    Resources = new Dictionary&amp;lt;Int32, Dictionary&amp;lt;Type, String&amp;gt;&amp;gt;();&lt;br /&gt;&lt;br /&gt;    Dictionary&amp;lt;Type, String&amp;gt; English = new Dictionary&amp;lt;Type, String&amp;gt;();&lt;br /&gt;    English.Add(typeof(Exception), "General Exception");&lt;br /&gt;    English.Add(typeof(SoapException), "CRM Exception");&lt;br /&gt;    English.Add(typeof(InvalidCustomException), "Invalid Custom Exception");&lt;br /&gt;    English.Add(typeof(UnKnownPluginException), "UnKnown Plugin Exception");&lt;br /&gt;    Resources.Add(1033, English);&lt;br /&gt;&lt;br /&gt;    Dictionary&amp;lt;Type, String&amp;gt; Spanish = new Dictionary&amp;lt;Type, String&amp;gt;();&lt;br /&gt;    Spanish.Add(typeof(Exception), "Excepciףn general");&lt;br /&gt;    Spanish.Add(typeof(SoapException), "Excepciףn de CRM");&lt;br /&gt;    Spanish.Add(typeof(InvalidCustomException), "Excepciףn personalizado no vבlido");&lt;br /&gt;    Spanish.Add(typeof(UnKnownPluginException), "Excepciףn Desconocida Plugin");&lt;br /&gt;    Resources.Add(3082, Spanish);&lt;br /&gt;   }&lt;br /&gt;  &lt;br /&gt;   try&lt;br /&gt;   {&lt;br /&gt;    //throw new Exception();&lt;br /&gt;    //throw new SoapException();&lt;br /&gt;    throw new UnKnownPluginException();&lt;br /&gt;   }&lt;br /&gt;   catch(Exception ex)&lt;br /&gt;   {&lt;br /&gt;    throw new InvalidPluginExecutionException(ex.Message); &lt;br /&gt;   }&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  public abstract class CustomException : Exception&lt;br /&gt;  {&lt;br /&gt;   private Int32 DefaultLanguage = 1033;&lt;br /&gt;   /// &amp;lt;summary&amp;gt;&lt;br /&gt;   /// override default message prop &lt;br /&gt;   /// &amp;lt;/summary&amp;gt;&lt;br /&gt;   public override string Message&lt;br /&gt;   {&lt;br /&gt;    get&lt;br /&gt;    {&lt;br /&gt;     return this.GetResource(this.GetType());&lt;br /&gt;    }&lt;br /&gt;   }&lt;br /&gt;   /// &amp;lt;summary&amp;gt;&lt;br /&gt;   /// Get the resource by exception type&lt;br /&gt;   /// &amp;lt;/summary&amp;gt;&lt;br /&gt;   /// &amp;lt;param name="type"&amp;gt;&amp;lt;/param&amp;gt;&lt;br /&gt;   /// &amp;lt;returns&amp;gt;&amp;lt;/returns&amp;gt;&lt;br /&gt;   protected string GetResource(Type type)&lt;br /&gt;   {&lt;br /&gt;    Int32 uiLcid = System.Threading.Thread.CurrentThread.CurrentUICulture.LCID;&lt;br /&gt;    &lt;br /&gt;    if (Resources.ContainsKey(uiLcid))&lt;br /&gt;    {&lt;br /&gt;     return Resources[uiLcid][type];&lt;br /&gt;    }&lt;br /&gt;    &lt;br /&gt;    return Resources[this.DefaultLanguage][type];&lt;br /&gt;   }  &lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  public class InvalidCustomException : CustomException&lt;br /&gt;  {&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  public class UnKnownPluginException : CustomException&lt;br /&gt;  {&lt;br /&gt;  }&lt;br /&gt;  &lt;br /&gt;  #endregion&lt;br /&gt; }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3003801964503297388-4462207419010528868?l=mscrm4ever.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mscrm4ever.blogspot.com/feeds/4462207419010528868/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3003801964503297388&amp;postID=4462207419010528868&amp;isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3003801964503297388/posts/default/4462207419010528868'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3003801964503297388/posts/default/4462207419010528868'/><link rel='alternate' type='text/html' href='http://mscrm4ever.blogspot.com/2009/05/crm-40-multi-lingual-support-in-plug.html' title='CRM 4.0 Multi Lingual Support in Plug-ins'/><author><name>Adi Katz</name><uri>http://www.blogger.com/profile/16783428437179825705</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3003801964503297388.post-1429502963313264383</id><published>2009-05-03T11:48:00.005+03:00</published><updated>2009-05-03T11:55:24.741+03:00</updated><title type='text'>CRM 4.0 Using attribute mapping in code</title><content type='html'>&lt;div&gt;&lt;/div&gt;&lt;br /&gt;This is a quick and dirty way of getting CRM attribute mapping. So what exactly can you do with it? One option would be to use it in order to retrieve parent context information when you use CrmService to create new entities. For example, when you create a new contact you might want to get the account information that would normally map to contact when used from the CRM UI.&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;One of the challenges of using SDK is to mimic some of the functionalities that exist in the UI e.g. required field and attribute mapping when a child entity is created in the context of a parent entity. This type of mapping does not exist out of the box and requires you to write custom code.&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;The following example is a generic way to extract mapping information from CRM. So what are the pros and cons of using this technique?&lt;br /&gt;The biggest advantage is that you’re able to you CRM customizations which make the configuration process worth while.&lt;br /&gt;The disadvantages are that the default CRM mapping (attributemap entity) does not hold the type of the attributes e.g picklist or lookup and the information returned form the CRM also contains inner mappings of picklist and lookups e.g. parentcustomeridname and parentcustomriddsc.&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;So how can we overcome these disadvantages?&lt;br /&gt;The attribute type can easily be retrieved using the metadata service RetrieveAttributeRequest. The problem is that this call to metadataservice is very slow so you also need to cache the data in order to use it efficiently.&lt;br /&gt;The inner mapping can be ignored if you exclude picklist and lookup attributes that end with name of dsc.&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;The code does not cache the mapping information and does not show you how to create a new contact with parent mapping but presents a poc of how to retrieve the mapping information for further use.&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;The following is what the program prints to the console:&lt;br /&gt;&lt;br /&gt;&lt;div style='border:1px solid navy;padding:2px 2px 2px 2px'&gt;paymenttermscode --&gt; paymenttermscode - Type: Picklist&lt;br /&gt;telephone1 --&gt; telephone1 - Type: String&lt;br /&gt;accountid --&gt; parentcustomerid - Type: Customer&lt;br /&gt;name --&gt; parentcustomeridname - Type: String&lt;br /&gt;deletionstatecode --&gt; parentcustomeriddsc - Type: Integer&lt;br /&gt;address1_addresstypecode --&gt; address1_addresstypecode - Type: Picklist&lt;br /&gt;address1_city --&gt; address1_city - Type: String&lt;br /&gt;address1_country --&gt; address1_country - Type: String&lt;br /&gt;address1_county --&gt; address1_county - Type: String&lt;br /&gt;address1_line1 --&gt; address1_line1 - Type: String&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;Here is the code, simply create a new console application and dump the class inside the program.cs file&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;&lt;pre class='csharp' name='code'&gt;&lt;br /&gt;using System;&lt;br /&gt;using System.Collections.Generic;&lt;br /&gt;using System.Text;&lt;br /&gt;using Microsoft.Crm.Sdk;&lt;br /&gt;using Microsoft.Crm.SdkTypeProxy;&lt;br /&gt;using Microsoft.Crm.Sdk.Query;&lt;br /&gt;using Microsoft.Crm.SdkTypeProxy.Metadata;&lt;br /&gt;using Microsoft.Crm.Sdk.Metadata;&lt;br /&gt;&lt;br /&gt;namespace EntityMapping&lt;br /&gt;{&lt;br /&gt; class Program&lt;br /&gt; {&lt;br /&gt;  static void Main(string[] args)&lt;br /&gt;  {&lt;br /&gt;   List&amp;lt;AttributeMapping&amp;gt; AcctContMap = GetAttributeMapping("account", "contact");&lt;br /&gt;   &lt;br /&gt;   foreach(AttributeMapping attributeMap in AcctContMap)&lt;br /&gt;   {&lt;br /&gt;    Console.WriteLine&lt;br /&gt;    (&lt;br /&gt;     String.Format&lt;br /&gt;     (&lt;br /&gt;      "{0} --&amp;gt; {1} - Type: {2}",&lt;br /&gt;      attributeMap.FromEntityName,&lt;br /&gt;      attributeMap.ToEntityName,&lt;br /&gt;      Enum.GetName(typeof(AttributeType),attributeMap.AttributeType)&lt;br /&gt;     )&lt;br /&gt;    );&lt;br /&gt;   }&lt;br /&gt;  }&lt;br /&gt;  &lt;br /&gt;  public class AttributeMapping&lt;br /&gt;  {&lt;br /&gt;   public String FromEntityName;&lt;br /&gt;   public String ToEntityName;&lt;br /&gt;   public AttributeType AttributeType;&lt;br /&gt;&lt;br /&gt;   public AttributeMapping(String fromEntityName, &lt;br /&gt;         String toEntityName, &lt;br /&gt;         AttributeType typeofAttribute)&lt;br /&gt;   {&lt;br /&gt;    this.FromEntityName = fromEntityName;&lt;br /&gt;    this.ToEntityName = toEntityName;&lt;br /&gt;    this.AttributeType = typeofAttribute;&lt;br /&gt;   }&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  private static List&amp;lt;AttributeMapping&amp;gt; GetAttributeMapping(String fromEntity, String toEntity)&lt;br /&gt;  {&lt;br /&gt;   DynamicEntity entityMap = GetEntityMapping(fromEntity, toEntity);&lt;br /&gt;   List&amp;lt;BusinessEntity&amp;gt; startMap = RetieveAttributeMappingByEntityMap(entityMap);&lt;br /&gt;   List&amp;lt;AttributeMapping&amp;gt; endMap = new List&amp;lt;AttributeMapping&amp;gt;(startMap.Count);&lt;br /&gt;   &lt;br /&gt;   foreach (DynamicEntity attributeMap in startMap)&lt;br /&gt;   {&lt;br /&gt;    String targetAttribute = attributeMap.Properties["targetattributename"].ToString();&lt;br /&gt;    String sourceAttribute = attributeMap.Properties["sourceattributename"].ToString();&lt;br /&gt;    endMap.Add(&lt;br /&gt;     new AttributeMapping(sourceAttribute,targetAttribute,&lt;br /&gt;           GetAttributeType(toEntity,targetAttribute))&lt;br /&gt;    );&lt;br /&gt;   }&lt;br /&gt;   &lt;br /&gt;   return endMap;&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  private static List&amp;lt;BusinessEntity&amp;gt; RetieveAttributeMappingByEntityMap(DynamicEntity entityMap)&lt;br /&gt;  {&lt;br /&gt;   Guid entityMapId = ((Key)entityMap.Properties["entitymapid"]).Value;&lt;br /&gt;   QueryExpression attributeQuery = GetAttributeQuery(entityMapId, new AllColumns());&lt;br /&gt;   return RetrieveMultiple(attributeQuery);&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  private static AttributeType GetAttributeType(string entityLogicalName, string attributeLogicalName)&lt;br /&gt;  {&lt;br /&gt;   RetrieveAttributeRequest attributeRequest = new RetrieveAttributeRequest();&lt;br /&gt;   attributeRequest.EntityLogicalName = entityLogicalName;&lt;br /&gt;   attributeRequest.LogicalName = attributeLogicalName;&lt;br /&gt;   RetrieveAttributeResponse attributeResponse = &lt;br /&gt;    (RetrieveAttributeResponse)GetMetaService("http://moss:5555/","MicrosoftCRM").Execute(attributeRequest);&lt;br /&gt;   return attributeResponse.AttributeMetadata.AttributeType.Value;&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  private static MetadataService GetMetaService(string serverUrl, string orgName)&lt;br /&gt;  {&lt;br /&gt;   CrmAuthenticationToken token = new CrmAuthenticationToken();&lt;br /&gt;   token.AuthenticationType = 0;&lt;br /&gt;   token.OrganizationName = orgName;&lt;br /&gt;&lt;br /&gt;   MetadataService service = new MetadataService();&lt;br /&gt;   service.Url = String.Format("{0}mscrmservices/2007/metadataservice.asmx", serverUrl);&lt;br /&gt;   service.PreAuthenticate = false;&lt;br /&gt;   service.UnsafeAuthenticatedConnectionSharing = true;&lt;br /&gt;   service.CrmAuthenticationTokenValue = token;&lt;br /&gt;   service.UseDefaultCredentials = true;&lt;br /&gt;   &lt;br /&gt;   return service;&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  private static List&amp;lt;BusinessEntity&amp;gt; RetrieveMultiple(QueryExpression query)&lt;br /&gt;  {&lt;br /&gt;   RetrieveMultipleRequest request = new RetrieveMultipleRequest();&lt;br /&gt;   request.Query = query;&lt;br /&gt;   request.ReturnDynamicEntities = true;&lt;br /&gt;   RetrieveMultipleResponse response = &lt;br /&gt;    (RetrieveMultipleResponse)GetCrmService(&lt;br /&gt;      "http://moss:5555/","MicrosoftCRM").Execute(request);&lt;br /&gt;   return response.BusinessEntityCollection.BusinessEntities;&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  private static CrmService GetCrmService(string serverUrl, string orgName)&lt;br /&gt;  {&lt;br /&gt;   CrmAuthenticationToken token = new CrmAuthenticationToken();&lt;br /&gt;   token.AuthenticationType = 0;&lt;br /&gt;   token.OrganizationName = orgName;&lt;br /&gt;&lt;br /&gt;   CrmService service = new CrmService();&lt;br /&gt;   service.Url = String.Format("{0}mscrmservices/2007/crmservice.asmx",serverUrl);&lt;br /&gt;   service.PreAuthenticate = false;&lt;br /&gt;   service.UnsafeAuthenticatedConnectionSharing = true;&lt;br /&gt;   service.CrmAuthenticationTokenValue = token;&lt;br /&gt;   service.UseDefaultCredentials = true;&lt;br /&gt;   &lt;br /&gt;   return service;&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  private static DynamicEntity GetEntityMapping(String fromEntity, String toEntity)&lt;br /&gt;  {&lt;br /&gt;   QueryExpression query = new QueryExpression(EntityName.entitymap.ToString());&lt;br /&gt;   query.ColumnSet.AddColumn("entitymapid");&lt;br /&gt;   query.Criteria.AddCondition(&lt;br /&gt;    new ConditionExpression("sourceentityname",ConditionOperator.Equal,&lt;br /&gt;    new object[]{fromEntity})&lt;br /&gt;   );&lt;br /&gt;   query.Criteria.AddCondition(&lt;br /&gt;    new ConditionExpression("targetentityname",ConditionOperator.Equal,&lt;br /&gt;    new object[]{toEntity})&lt;br /&gt;   );&lt;br /&gt;   &lt;br /&gt;   List&amp;lt;BusinessEntity&amp;gt; entityMapping = RetrieveMultiple(query);&lt;br /&gt;   return entityMapping[0] as DynamicEntity;&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  private static QueryExpression GetAttributeQuery(Guid entityMapId, ColumnSetBase columnSet)&lt;br /&gt;  {&lt;br /&gt;   QueryExpression query = new QueryExpression(EntityName.attributemap.ToString());&lt;br /&gt;   query.ColumnSet = columnSet;&lt;br /&gt;   query.Criteria.AddCondition(&lt;br /&gt;    new ConditionExpression("entitymapid",ConditionOperator.Equal,entityMapId.ToString())&lt;br /&gt;   );&lt;br /&gt;   return query;&lt;br /&gt;  }&lt;br /&gt; }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3003801964503297388-1429502963313264383?l=mscrm4ever.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mscrm4ever.blogspot.com/feeds/1429502963313264383/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3003801964503297388&amp;postID=1429502963313264383&amp;isPopup=true' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3003801964503297388/posts/default/1429502963313264383'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3003801964503297388/posts/default/1429502963313264383'/><link rel='alternate' type='text/html' href='http://mscrm4ever.blogspot.com/2009/05/crm-40-using-attribute-mapping-in-code.html' title='CRM 4.0 Using attribute mapping in code'/><author><name>Adi Katz</name><uri>http://www.blogger.com/profile/16783428437179825705</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3003801964503297388.post-3001880738823709623</id><published>2009-04-20T23:25:00.004+03:00</published><updated>2009-04-20T23:52:05.215+03:00</updated><title type='text'>CRM Form Script Loader</title><content type='html'>I wrote this post as a response to a question on ms forums about using XMLHTTP as a mechanism for loading remote scripts.&lt;br /&gt;&lt;br /&gt;It seems that using this technique causes a caching problem which can really slow things down while developing.&lt;br /&gt;&lt;br /&gt;Here is the notorious script:&lt;br /&gt;&lt;br /&gt;&lt;pre class="js" name="code"&gt;&lt;br /&gt;function load_script (url)&lt;br /&gt;{&lt;br /&gt;      var x = new ActiveXObject("Msxml2.XMLHTTP");&lt;br /&gt;      x.open('GET', url, false); x.send('');&lt;br /&gt;      eval(x.responseText);&lt;br /&gt;&lt;br /&gt;      var s = x.responseText.split(/\n/);&lt;br /&gt;    &lt;br /&gt;      var r = /^function\s*([a-z_]+)/i;&lt;br /&gt;      for (var i = 0; i &amp;lt; s.length; i++)&lt;br /&gt;      {&lt;br /&gt;          var m = r.exec(s[i]);&lt;br /&gt;          if (m != null)&lt;br /&gt;          {&lt;br /&gt;              window[m[1]] = eval(m[1]);&lt;br /&gt;          }&lt;br /&gt;      }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;load_script("/_customscript/customscript1.js");&lt;br /&gt;load_script("/_customscript/customscript2.js");&lt;br /&gt;load_script("/_customscript/customscript3.js");&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;In my opinion loading scripts using XMLHTTP should be avoided! Why?&lt;br /&gt;1. The eval() function is very slow and should only be used as a last resort i.e. if no other options are available.&lt;br /&gt;2. RegExp is also known to be slow especially with large documents.&lt;br /&gt;3. Using XMLHTTP synchronously hangs / blocks the entire page until each resource is fully loaded.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Here is a better choice. Why?&lt;br /&gt;1. Caching can be avoided easily.&lt;br /&gt;2. Works much faster.&lt;br /&gt;3. Does not block the page natural loading order and does not wait for other resources to finish loading.&lt;br /&gt;&lt;br /&gt;&lt;pre class="js" name="code"&gt;&lt;br /&gt;function ScriptLoader(func)&lt;br /&gt;{&lt;br /&gt;   var loader = this;&lt;br /&gt;  &lt;br /&gt;   /* if ScriptLoading &amp;gt; 0 then scripts are still loading */&lt;br /&gt;   loader.ScriptLoading = 0;&lt;br /&gt;   /* the script entity point e.g. OnCrmPageLoad */&lt;br /&gt;   loader.Init = func;&lt;br /&gt;   /*&lt;br /&gt;      url - script url&lt;br /&gt;      noc - include a nocache querystring parameter&lt;br /&gt;   */&lt;br /&gt;   loader.Load = function(url,noc)&lt;br /&gt;   {&lt;br /&gt;      var script = document.createElement("SCRIPT");&lt;br /&gt;      script.src = url + (noc ? "?nocache=" + Math.random() : "");&lt;br /&gt; &lt;br /&gt;      script.onreadystatechange = function()&lt;br /&gt;      {&lt;br /&gt;            if (!(script.readyState == 'loaded' || script.readyState == 'complete'))&lt;br /&gt;            {&lt;br /&gt;                  return;&lt;br /&gt;            }&lt;br /&gt;   &lt;br /&gt;            /* if loader.ScriptLoading &amp;gt; 0 true else false */&lt;br /&gt;            if (--loader.ScriptLoading)&lt;br /&gt;            {&lt;br /&gt;                  return;  &lt;br /&gt;            }&lt;br /&gt;   &lt;br /&gt;            /* finished loading (loader.ScriptLoading == 0) , call entry point */&lt;br /&gt;            loader.Init(); &lt;br /&gt;      }&lt;br /&gt;  &lt;br /&gt;      /* append the script to the head tag */&lt;br /&gt;      document.documentElement.childNodes[0].appendChild(script);&lt;br /&gt;      /* indicate that this script is loading */&lt;br /&gt;      loader.ScriptLoading++;&lt;br /&gt;   } &lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;/* &lt;br /&gt;      create a Script loader object + function to be called when the script has&lt;br /&gt;      finished loading &lt;br /&gt;*/&lt;br /&gt;window.ScriptLibrary = new ScriptLoader(OnCrmPageLoad);&lt;br /&gt;&lt;br /&gt;/* url , false - cache, true - no cache */&lt;br /&gt;ScriptLibrary.Load('/ISV/SCLIB/JScript1.js',false);&lt;br /&gt;ScriptLibrary.Load('/ISV/SCLIB/JScript2.js',true);&lt;br /&gt;ScriptLibrary.Load('/ISV/SCLIB/JScript3.js',false);&lt;br /&gt;&lt;br /&gt;/* Start Script Execution */&lt;br /&gt;function OnCrmPageLoad()&lt;br /&gt;{&lt;br /&gt;   var res = JS2Function(); &lt;br /&gt;   res += "," + JS1Function();&lt;br /&gt;   res += "," + JS3Function();&lt;br /&gt;   alert(res)&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The remote scripts functions can be written in such a way (e.g. window.FuncName = function(){ /* code */ }) which immediately exposes them to the window scope e.g.&lt;br /&gt;&lt;br /&gt;&lt;pre class='js' name='code'&gt;&lt;br /&gt;// JScript3.js File&lt;br /&gt;window.JS3Function = function()&lt;br /&gt;{&lt;br /&gt; return 'JS3Function';&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;// JScript2.js File&lt;br /&gt;window.JS2Function = function()&lt;br /&gt;{&lt;br /&gt; return 'JS2Function';&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;// JScript1.js File&lt;br /&gt;window.JS1Function = function()&lt;br /&gt;{&lt;br /&gt; return 'JS1Function';&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3003801964503297388-3001880738823709623?l=mscrm4ever.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mscrm4ever.blogspot.com/feeds/3001880738823709623/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3003801964503297388&amp;postID=3001880738823709623&amp;isPopup=true' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3003801964503297388/posts/default/3001880738823709623'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3003801964503297388/posts/default/3001880738823709623'/><link rel='alternate' type='text/html' href='http://mscrm4ever.blogspot.com/2009/04/crm-form-script-loader.html' title='CRM Form Script Loader'/><author><name>Adi Katz</name><uri>http://www.blogger.com/profile/16783428437179825705</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3003801964503297388.post-1119879951051867711</id><published>2009-04-19T00:57:00.008+03:00</published><updated>2010-05-05T07:56:30.118+03:00</updated><title type='text'>CRM 4.0 GI Partners Starter Pack</title><content type='html'>&lt;div&gt;&lt;/div&gt;&lt;br /&gt;The GI Partners Starter Pack (PSP) is the best value added and cost effective solution to jump start your CRM projects. Save time and effort by integrating wizards that are able to solve common development tasks such as Field Level Security and Bulk Delete Operations. Gap missing CRM functionalities and focus your effort on your client’s CRM objectives. Take advantage of our discount offer, Royalty free distribution license and ensure your project success.&lt;br /&gt;&lt;br /&gt;The Partner start pack contains the following wizards:&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Field Level Security wizard (&lt;a href="http://mscrm4ever.blogspot.com/2008/11/crm-40-field-level-security-wizard.html"&gt;FLS&lt;/a&gt;) &lt;/li&gt;&lt;li&gt;Public View Manager (&lt;a href="http://mscrm4ever.blogspot.com/2009/02/crm-40-public-view-manager-wizard.html"&gt;PVS&lt;/a&gt;) &lt;/li&gt;&lt;li&gt;State and Status Security wizard (&lt;a href="http://mscrm4ever.blogspot.com/2009/01/crm-40-state-and-status-code-manager.html"&gt;SMW&lt;/a&gt;) &lt;/li&gt;&lt;li&gt;Bulk Delete Wizard (&lt;a href="http://mscrm4ever.blogspot.com/2009/01/crm-40-bulk-delete-wizard.html"&gt;BDW&lt;/a&gt;) &lt;/li&gt;&lt;li&gt;Record and Page Counter (&lt;a href="http://mscrm4ever.blogspot.com/2008/12/crm-40-supported-record-page-counter.html"&gt;RCO&lt;/a&gt; &amp;amp; &lt;a href="http://mscrm4ever.blogspot.com/2009/03/crm-40-associated-view-record-page.html"&gt;AVCR&lt;/a&gt;)&lt;br /&gt;&lt;br /&gt;All wizards are compliant with &lt;u&gt;rollup3&lt;/u&gt;, Tested with &lt;u&gt;IE8&lt;/u&gt; and support &lt;u&gt;IFD&lt;/u&gt;.&lt;br /&gt;All wizards supports both &lt;u&gt;on-premise&lt;/u&gt; and &lt;u&gt;partner hosted&lt;/u&gt; environments.&lt;br /&gt;All wizards support &lt;u&gt;multi-tenancy&lt;/u&gt; and are available for &lt;u&gt;all languages&lt;/u&gt;.&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;Click here to order our &lt;a href="http://gicrm.upsite.co.il/?categoryId=29264&amp;itemId=74527"&gt;PSP package&lt;/a&gt;.&lt;br /&gt;&lt;p&gt;&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3003801964503297388-1119879951051867711?l=mscrm4ever.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mscrm4ever.blogspot.com/feeds/1119879951051867711/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3003801964503297388&amp;postID=1119879951051867711&amp;isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3003801964503297388/posts/default/1119879951051867711'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3003801964503297388/posts/default/1119879951051867711'/><link rel='alternate' type='text/html' href='http://mscrm4ever.blogspot.com/2009/04/crm-40-gi-partners-starter-pack.html' title='CRM 4.0 GI Partners Starter Pack'/><author><name>Adi Katz</name><uri>http://www.blogger.com/profile/16783428437179825705</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3003801964503297388.post-5427017603569488091</id><published>2009-04-18T18:00:00.013+03:00</published><updated>2010-05-05T07:56:53.828+03:00</updated><title type='text'>CRM 4.0 Global Interface Security Package</title><content type='html'>&lt;div&gt;&lt;/div&gt;&lt;br /&gt;&lt;strong&gt;GI Security Package&lt;/strong&gt; is a complementary security solution for Dynamics.  The package leverages the following wizards:&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;&lt;strong&gt;Field Level Security wizard (FLS)&lt;/strong&gt;&lt;br&gt;&lt;br /&gt;&lt;a href="http://1.bp.blogspot.com/_M-mdv3Tfarg/SeoQpyzl0mI/AAAAAAAAALc/GPGmdfAvTOY/s1600-h/contact_city.jpg"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 162px;border:1px solid black;" src="http://1.bp.blogspot.com/_M-mdv3Tfarg/SeoQpyzl0mI/AAAAAAAAALc/GPGmdfAvTOY/s400/contact_city.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5326087819517874786" /&gt;&lt;/a&gt;&lt;br /&gt;Enables your organization to decide which fields and data are available and visible for each business unit, role and user.&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;&lt;strong&gt;Public View Manager (PVS)&lt;/strong&gt;&lt;br&gt;&lt;br /&gt;&lt;a href="http://3.bp.blogspot.com/_M-mdv3Tfarg/SeoRGuM8m_I/AAAAAAAAALk/gSrexsRlDuY/s1600-h/Front.jpg"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 114px;border:1px solid black;" src="http://3.bp.blogspot.com/_M-mdv3Tfarg/SeoRGuM8m_I/AAAAAAAAALk/gSrexsRlDuY/s400/Front.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5326088316498254834" /&gt;&lt;/a&gt;&lt;br /&gt;Enables your organization to enhance and target business objectives by hiding, re-arranging and setting default views for each business unit, role and user.&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;&lt;strong&gt;State and Status Security wizard (SMW) &lt;/strong&gt;&lt;br&gt;&lt;br /&gt;&lt;a href="http://1.bp.blogspot.com/_M-mdv3Tfarg/SeoRYBys73I/AAAAAAAAALs/Pdv83xDxbg0/s1600-h/SMW.jpg"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 105px;border:1px solid black;" src="http://1.bp.blogspot.com/_M-mdv3Tfarg/SeoRYBys73I/AAAAAAAAALs/Pdv83xDxbg0/s400/SMW.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5326088613814660978" /&gt;&lt;/a&gt;&lt;br /&gt;Enables your organization to decide which CRM functionality (e.g. wining an opportunity, fulfilling an invoice) is available for each business unit, role and user.&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;Together the security package enables your organization to enforce common security requirements and achieve common business goals out of the box. The benefits of using the security package are substantial as it eliminates development effort, ensures a low TCO and a faster ROI. &lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;The Security Package is offered at a discounted price. Visit the Security Package &lt;a href='http://gicrm.upsite.co.il/?categoryId=29264&amp;itemId=67158'&gt;product page&lt;/a&gt; for Pricing Information and Video Demonatrations.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3003801964503297388-5427017603569488091?l=mscrm4ever.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mscrm4ever.blogspot.com/feeds/5427017603569488091/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3003801964503297388&amp;postID=5427017603569488091&amp;isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3003801964503297388/posts/default/5427017603569488091'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3003801964503297388/posts/default/5427017603569488091'/><link rel='alternate' type='text/html' href='http://mscrm4ever.blogspot.com/2009/04/crm-40-global-interface-security.html' title='CRM 4.0 Global Interface Security Package'/><author><name>Adi Katz</name><uri>http://www.blogger.com/profile/16783428437179825705</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_M-mdv3Tfarg/SeoQpyzl0mI/AAAAAAAAALc/GPGmdfAvTOY/s72-c/contact_city.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3003801964503297388.post-8124824426715957692</id><published>2009-04-12T13:49:00.005+03:00</published><updated>2009-07-22T05:48:52.398+03:00</updated><title type='text'>CRM 4.0 Many 2 Many IFrame Viewer</title><content type='html'>I’ve seen many posts about displaying a many 2 many grid inside an iframe so I didn’t post about it my self until now.&lt;br /&gt;&lt;br /&gt;One of the disadvantages of using such customization is that it does not follow ms initial execution path which breaks some of the functionality.&lt;br /&gt;&lt;br /&gt;In our case after you select an existing item the grid does not refresh / reflect your selection until you push the refresh button your self.&lt;br /&gt;&lt;br /&gt;I wrote a small utility class that solves that problem. It also takes care of the iframe padding.&lt;br /&gt;&lt;br /&gt;Basically a many to many iframe looks like this:&lt;br /&gt;&lt;br /&gt;areas.aspx?&lt;strong&gt;oId&lt;/strong&gt;={ObjectId}&amp;amp;&lt;strong&gt;oType&lt;/strong&gt;={ObjectTypeCode}&amp;amp;&lt;strong&gt;security&lt;/strong&gt;=852023&amp;amp;&lt;strong&gt;roleOrd&lt;/strong&gt;=1&amp;amp;&lt;strong&gt;tabSet&lt;/strong&gt;=area{relationship name}&lt;br /&gt;&lt;br /&gt;The viewer accepts the &lt;em&gt;RoleOrder&lt;/em&gt; and Relationship Name (&lt;em&gt;TabsetId&lt;/em&gt;) and constructs the iframe for you.&lt;br /&gt;&lt;br /&gt;Add the code in the entity onload event and Enjoy…&lt;br /&gt;&lt;br /&gt;&lt;pre class='js' name='code'&gt;&lt;br /&gt;function OnCrmPageLoad() &lt;br /&gt;{ &lt;br /&gt;    /* Create a N2NViewer and give it the IFRAME (container) id */&lt;br /&gt;    var n2nViewer = new N2NViewer('IFRAME_account_association');&lt;br /&gt;    /* Set the role order - use iedevtoolber for exact parameters */ &lt;br /&gt;    n2nViewer.RoleOrder = 1;&lt;br /&gt;    /* assing the relationship name (without the "area" word) */ &lt;br /&gt;    n2nViewer.TabsetId  = "gi_account_account";&lt;br /&gt;    /* Do the trick... */ &lt;br /&gt;    n2nViewer.Load();&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;function N2NViewer(iframeId)&lt;br /&gt;{&lt;br /&gt;    if (!document.all[iframeId])&lt;br /&gt;    {&lt;br /&gt;        alert(iframeId + " is missing!");&lt;br /&gt;        return;&lt;br /&gt;    }&lt;br /&gt; &lt;br /&gt;    var viewer = this;&lt;br /&gt;    var _locAssocObj = null;&lt;br /&gt; &lt;br /&gt;    viewer.IFRAME = document.all[iframeId];&lt;br /&gt;    viewer.RoleOrder;&lt;br /&gt;    viewer.TabsetId;&lt;br /&gt; &lt;br /&gt;    viewer.Load = function()&lt;br /&gt;    {&lt;br /&gt;        /* Construct a valid N2N IFRAME url */&lt;br /&gt;        viewer.IFRAME.src = "areas.aspx?oId=" + crmForm.ObjectId + "&amp;oType=" + crmForm.ObjectTypeCode + "&amp;security=" + crmFormSubmit.crmFormSubmitSecurity.value + "&amp;roleOrd=" +         viewer.RoleOrder + "&amp;tabSet=area" + viewer.TabsetId;&lt;br /&gt;        viewer.IFRAME.onreadystatechange = viewer.StateChanged;&lt;br /&gt;    }&lt;br /&gt; &lt;br /&gt;    viewer.StateChanged = function()&lt;br /&gt;    {&lt;br /&gt;        if (viewer.IFRAME.readyState != 'complete')&lt;br /&gt;        {&lt;br /&gt;            return;&lt;br /&gt;        }&lt;br /&gt;  &lt;br /&gt;        var iframeDoc = viewer.IFRAME.contentWindow.document;&lt;br /&gt;  &lt;br /&gt;        /* Reomve scrolling space */&lt;br /&gt;        iframeDoc.body.scroll = "no";&lt;br /&gt;        /* Remove crmGrid Default padding */&lt;br /&gt;        iframeDoc.body.childNodes[0].rows[0].cells[0].style.padding = 0;&lt;br /&gt;  &lt;br /&gt;        /* Save MS locAssocObj */&lt;br /&gt;        _locAssocObj = locAssocObj;&lt;br /&gt;        /* Override MS locAssocObj */&lt;br /&gt;        locAssocObj = viewer.locAssocObj;&lt;br /&gt;    }&lt;br /&gt; &lt;br /&gt;    viewer.locAssocObj = function(iType , sSubType, sAssociationName, iRoleOrdinal)&lt;br /&gt;    {&lt;br /&gt;        /* Open the Dialog */&lt;br /&gt;        _locAssocObj(iType , sSubType, sAssociationName, iRoleOrdinal);&lt;br /&gt;        /* Refresh only if our iframe contains the correnct tabset name */&lt;br /&gt;        if (sAssociationName == viewer.TabsetId)&lt;br /&gt;        {&lt;br /&gt;            viewer.IFRAME.contentWindow.document.all.crmGrid.Refresh(); &lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;} &lt;br /&gt;&lt;br /&gt;//Entry Point &lt;br /&gt;OnCrmPageLoad();&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3003801964503297388-8124824426715957692?l=mscrm4ever.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mscrm4ever.blogspot.com/feeds/8124824426715957692/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3003801964503297388&amp;postID=8124824426715957692&amp;isPopup=true' title='17 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3003801964503297388/posts/default/8124824426715957692'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3003801964503297388/posts/default/8124824426715957692'/><link rel='alternate' type='text/html' href='http://mscrm4ever.blogspot.com/2009/04/crm-40-many-2-many-iframe-viewer.html' title='CRM 4.0 Many 2 Many IFrame Viewer'/><author><name>Adi Katz</name><uri>http://www.blogger.com/profile/16783428437179825705</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>17</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3003801964503297388.post-3338107406758389346</id><published>2009-03-20T23:51:00.007+02:00</published><updated>2009-03-21T00:16:17.366+02:00</updated><title type='text'>Creating a Network Path Text Control</title><content type='html'>&lt;div&gt;&lt;/div&gt;&lt;br /&gt;&lt;a href="http://4.bp.blogspot.com/_M-mdv3Tfarg/ScQSrk8LTyI/AAAAAAAAALM/dGcJujxm9oA/s1600-h/networkexplorer.jpg"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 211px;border:1px solid black;" src="http://4.bp.blogspot.com/_M-mdv3Tfarg/ScQSrk8LTyI/AAAAAAAAALM/dGcJujxm9oA/s400/networkexplorer.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5315394000063778594" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;Dynamics supports several types of text fields. One type which is not available in the current version is a text field that supports a UNC path (\\) or a network drive (Y:) to a shared folder on the server or on the user local machine.&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;The following example converts a regular text field into a network path control. The control works in conjunction with an Explorer page which contains an IFRAME to the actual network share.&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;The Explorer(.aspx) should be deployed in the ISV folder. When the user double clicks on the field value a window is opened pointing to the shared folder. The control also provides a comfortable API for managing extra query parameters, changing control style, changing explorer window features and setting an initial network path. The last can be a handy feature if you require the explorer to point to an account personal folder within a shared file system tree.&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;Here is the Explorer.aspx implementation:&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;&lt;pre class='html' name='code'&gt;&lt;br /&gt;&amp;lt;%@ Page Language="C#" %&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;html xmlns="http://www.w3.org/1999/xhtml" &amp;gt;&lt;br /&gt;&amp;lt;head runat="server"&amp;gt;&lt;br /&gt;    &amp;lt;title&amp;gt;Dynamics File Explorer&amp;lt;/title&amp;gt;&lt;br /&gt;    &amp;lt;style&amp;gt;&lt;br /&gt;  .header&lt;br /&gt;  {&lt;br /&gt;   background-color:#639ace;&lt;br /&gt;   padding-left:10px;&lt;br /&gt;   color:White;&lt;br /&gt;   font-size:x-large;&lt;br /&gt;   font-family:arial;&lt;br /&gt;   height:30px;&lt;br /&gt;  }&lt;br /&gt;  &lt;br /&gt;  .iframe&lt;br /&gt;  {&lt;br /&gt;   border:1px solid #94b2de;&lt;br /&gt;   height:100%;&lt;br /&gt;   width:100%;&lt;br /&gt;  }&lt;br /&gt;  &lt;br /&gt;  body&lt;br /&gt;  {&lt;br /&gt;   margin:0px;&lt;br /&gt;   border:0px solid;&lt;br /&gt;  }&lt;br /&gt;    &amp;lt;/style&amp;gt;&lt;br /&gt;&amp;lt;/head&amp;gt;&lt;br /&gt;&amp;lt;body scroll="no"&amp;gt;&lt;br /&gt; &amp;lt;table border="0" cellpadding="1" cellspacing="0" style="width:100%;height:100%"&amp;gt;&lt;br /&gt;  &amp;lt;tr&amp;gt;&lt;br /&gt;   &amp;lt;td class="header"&amp;gt;File Explorer&amp;lt;/td&amp;gt;&lt;br /&gt;  &amp;lt;/tr&amp;gt;&lt;br /&gt;  &amp;lt;tr&amp;gt;&lt;br /&gt;   &amp;lt;td&amp;gt;&lt;br /&gt;    &amp;lt;iframe class="iframe" src="&amp;lt;%=Request["path"] %&amp;gt;"&amp;gt;&amp;lt;/iframe&amp;gt;&lt;br /&gt;   &amp;lt;/td&amp;gt;&lt;br /&gt;  &amp;lt;/tr&amp;gt;&lt;br /&gt; &amp;lt;/table&amp;gt;&lt;br /&gt;&amp;lt;/body&amp;gt;&lt;br /&gt;&amp;lt;/html&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;The following script should be added to the entity onload event:&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;&lt;pre class="js" name="code"&gt;&lt;br /&gt;// JScript File&lt;br /&gt;function OnCrmPageLoad()&lt;br /&gt;{&lt;br /&gt; var networkPath = new NetworkPath("fax");&lt;br /&gt; //Example Add query string parameter&lt;br /&gt;  networkPath.Params.Add("test","1");&lt;br /&gt; //Example Remove query string parameter&lt;br /&gt;  networkPath.Params.Remove("test");&lt;br /&gt; //Example change control default style &lt;br /&gt;  networkPath.Style.Add("color","red");&lt;br /&gt; //Transform the text field into a network path field &lt;br /&gt;  networkPath.Transform();&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;function NetworkPath(baseControlId)&lt;br /&gt;{&lt;br /&gt; nk = this;&lt;br /&gt; nk.ID = baseControlId;&lt;br /&gt; nk.Control  = document.all[nk.ID];&lt;br /&gt; /* Default Path */&lt;br /&gt; nk.Path     = "\\\\127.0.0.1\\Files"; //default path&lt;br /&gt; nk.Window   = null;&lt;br /&gt; /* File Explorer Features (window.open)*/&lt;br /&gt; nk.Features = new Dictionary(",","=");&lt;br /&gt; /* If you wish to extend the Explorer.aspx and add more querystring parameters */&lt;br /&gt; nk.Params   = new Dictionary("&amp;amp;","=");&lt;br /&gt; /* Network path control additional styling rules */&lt;br /&gt; nk.Style    = new Dictionary(";",":");&lt;br /&gt; &lt;br /&gt; nk.Transform = function()&lt;br /&gt; {&lt;br /&gt;  if (nk.Control)&lt;br /&gt;  {&lt;br /&gt;   /* Assign default path if control value is empty */&lt;br /&gt;   if (nk.Control.DataValue == null)&lt;br /&gt;   {&lt;br /&gt;    nk.Control.DataValue = nk.Path;&lt;br /&gt;   }&lt;br /&gt;   &lt;br /&gt;   nk.Control.title = "Double Click to Open Network Explorer";&lt;br /&gt;   /* Default Window Settings */&lt;br /&gt;   if (!nk.Features.Exist("width"))&lt;br /&gt;   {&lt;br /&gt;    nk.Features.Add("width",700);&lt;br /&gt;   }&lt;br /&gt;   if (!nk.Features.Exist("height"))&lt;br /&gt;   {&lt;br /&gt;    nk.Features.Add("height",400);&lt;br /&gt;   }&lt;br /&gt;   if (!nk.Features.Exist("resize"))&lt;br /&gt;   {&lt;br /&gt;    nk.Features.Add("resize","yes");&lt;br /&gt;   }&lt;br /&gt;   if (!nk.Features.Exist("toolbar"))&lt;br /&gt;   {&lt;br /&gt;    nk.Features.Add("toolbar","no");&lt;br /&gt;   }&lt;br /&gt;   if (!nk.Features.Exist("menubar"))&lt;br /&gt;   {&lt;br /&gt;    nk.Features.Add("menubar","no");&lt;br /&gt;   }&lt;br /&gt;   if (!nk.Features.Exist("titlebar"))&lt;br /&gt;   {&lt;br /&gt;    nk.Features.Add("titlebar","no");&lt;br /&gt;   }&lt;br /&gt;   /* Attach validation to change and save events */&lt;br /&gt;   nk.Control.attachEvent( "onchange" , nk.Validate );&lt;br /&gt;   crmForm.attachEvent( "onsave" , nk.Validate );&lt;br /&gt;   &lt;br /&gt;   /* Adjust control style */&lt;br /&gt;   nk.Style.Add("text-decoration","underline");&lt;br /&gt;   if (!nk.Style.Exist("color"))&lt;br /&gt;   {&lt;br /&gt;    nk.Style.Add("color","blue");&lt;br /&gt;   }&lt;br /&gt;   nk.Style.Add("cursor","hand");&lt;br /&gt;   nk.Control.style.cssText += ";" + nk.Style.ToString();&lt;br /&gt;   &lt;br /&gt;   /* Add double click functionality */&lt;br /&gt;   nk.Control.ondblclick = nk.Open;&lt;br /&gt;  }&lt;br /&gt; }&lt;br /&gt; &lt;br /&gt; nk.Validate = function()&lt;br /&gt; {&lt;br /&gt;  if (nk.Control.DataValue == null)&lt;br /&gt;  {&lt;br /&gt;   return true;&lt;br /&gt;  }&lt;br /&gt;  &lt;br /&gt;  /* validate \\ unc path or a netwrok drive Y: */&lt;br /&gt;  var regex = new RegExp("^(\\\\|[a-zA-Z]:)");&lt;br /&gt;  if (!regex.exec(nk.Control.DataValue))&lt;br /&gt;  {&lt;br /&gt;   alert("Invalid Network Path or Drive");&lt;br /&gt;   return (event.returnValue = false);&lt;br /&gt;  }&lt;br /&gt;  &lt;br /&gt;  return true;&lt;br /&gt; }&lt;br /&gt; &lt;br /&gt; nk.Open = function()&lt;br /&gt; {&lt;br /&gt;  if (nk.Control.DataValue != null)&lt;br /&gt;  {&lt;br /&gt;   nk.Window = window.open( SERVER_URL + "/isv/explorer.aspx?path=" + nk.Control.DataValue + nk.Params.ToString() , "" , nk.Features.ToString());&lt;br /&gt;  }&lt;br /&gt; }&lt;br /&gt; &lt;br /&gt; nk.Close = function()&lt;br /&gt; {&lt;br /&gt;  nk.Window.close();&lt;br /&gt; }&lt;br /&gt; &lt;br /&gt; /* Key Value Pair Collection */&lt;br /&gt; function Dictionary(sep,delim)&lt;br /&gt; {&lt;br /&gt;  this.list = [];&lt;br /&gt;  this.Seperator = sep;&lt;br /&gt;  this.Delimiter = delim&lt;br /&gt;  &lt;br /&gt;  this.Add = function(key , value)&lt;br /&gt;  { &lt;br /&gt;   this.list[key] = value; &lt;br /&gt;  }&lt;br /&gt;  &lt;br /&gt;  this.Remove = function(key)&lt;br /&gt;  {&lt;br /&gt;   this.list[key] = "";&lt;br /&gt;  }&lt;br /&gt;  &lt;br /&gt;  this.Exist = function(key)&lt;br /&gt;  {&lt;br /&gt;   return this.list[key] != null &amp;amp;&amp;amp; this.list[key] != "";&lt;br /&gt;  }&lt;br /&gt;  &lt;br /&gt;  this.ToString = function()&lt;br /&gt;  {&lt;br /&gt;   var result = new StringBuilder();&lt;br /&gt;   result.Append(this.Seperator);&lt;br /&gt;   for(var key in this.list)&lt;br /&gt;   {&lt;br /&gt;    if (this.list[key]!="")&lt;br /&gt;    {&lt;br /&gt;     result.Append(key).Append(this.Delimiter);&lt;br /&gt;     result.Append(this.list[key]).Append(this.Seperator);&lt;br /&gt;    }&lt;br /&gt;   }&lt;br /&gt;   var tmp = result.ToString();&lt;br /&gt;   return tmp.substring(0,tmp.length-1);&lt;br /&gt;  }&lt;br /&gt; }&lt;br /&gt; &lt;br /&gt; function StringBuilder()&lt;br /&gt; {&lt;br /&gt;  this.data = [];&lt;br /&gt;  &lt;br /&gt;  this.Append = function(text)&lt;br /&gt;  {&lt;br /&gt;   this.data[this.data.length] = text;&lt;br /&gt;   return this;&lt;br /&gt;  }&lt;br /&gt;  &lt;br /&gt;  this.Reset = function()&lt;br /&gt;  {&lt;br /&gt;   this.data = [];&lt;br /&gt;  } &lt;br /&gt;    &lt;br /&gt;  this.ToString = function()&lt;br /&gt;  {&lt;br /&gt;   return this.data.join("");&lt;br /&gt;  }&lt;br /&gt; }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;OnCrmPageLoad();&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3003801964503297388-3338107406758389346?l=mscrm4ever.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mscrm4ever.blogspot.com/feeds/3338107406758389346/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3003801964503297388&amp;postID=3338107406758389346&amp;isPopup=true' title='6 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3003801964503297388/posts/default/3338107406758389346'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3003801964503297388/posts/default/3338107406758389346'/><link rel='alternate' type='text/html' href='http://mscrm4ever.blogspot.com/2009/03/creating-network-path-text-control.html' title='Creating a Network Path Text Control'/><author><name>Adi Katz</name><uri>http://www.blogger.com/profile/16783428437179825705</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_M-mdv3Tfarg/ScQSrk8LTyI/AAAAAAAAALM/dGcJujxm9oA/s72-c/networkexplorer.jpg' height='72' width='72'/><thr:total>6</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3003801964503297388.post-2744160225402542359</id><published>2009-03-19T05:23:00.004+02:00</published><updated>2009-03-19T05:32:45.837+02:00</updated><title type='text'>Displaying a lookup with related entity fields</title><content type='html'>&lt;div&gt;&lt;/div&gt;&lt;br /&gt;&lt;a href="http://2.bp.blogspot.com/_M-mdv3Tfarg/ScG8KtYgd0I/AAAAAAAAALE/mHVJBcByCxo/s1600-h/lookuprelatedentityfields.jpg"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 54px;border:1px solid black;" src="http://2.bp.blogspot.com/_M-mdv3Tfarg/ScG8KtYgd0I/AAAAAAAAALE/mHVJBcByCxo/s400/lookuprelatedentityfields.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5314735927440996162" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;As you all know a CRM lookup displays its related entity primary field. Although this can not be changed using existing customizations; in most cases that suffices. However, there are occasions where you want to display more information in order to avoid opening the related entity form. One solution which I posted about was the lookup preview which builds a preview window for each lookup DataValue. I personally think it’s a great solution and we also have a wizard that facilitates the creation of the preview for us. This post offers a different solution which utilizes a plug-in that retrieves the extra information you wish to display and injects it inside the lookup text. The drawback of this solution is that the lookup can only occupy a certain amount of space. So you should consider expanding the lookup colspan before you use it.&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;The solution makes use of the post retrieve message on the incident entity. My goal in this demo is to show how to extend the customer lookup on the incident form so if you select an account the customer lookup will display the account name , number and primary contact and if you select a contact then the customer lookup displays the salutation , job title and company fields.&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;The solution is static but might give you a head start when other requirements of similar nature are in need.&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;&lt;pre class='csharp' name='code'&gt;&lt;br /&gt;using System;&lt;br /&gt;using System.Collections.Generic;&lt;br /&gt;using System.Text;&lt;br /&gt;using Microsoft.Crm.Sdk;&lt;br /&gt;using Microsoft.Crm.SdkTypeProxy;&lt;br /&gt;using Microsoft.Crm.Sdk.Query;&lt;br /&gt;&lt;br /&gt;namespace LookupTextPlugIn&lt;br /&gt;{&lt;br /&gt; public class LookupRetrieveHandler : IPlugin&lt;br /&gt; {&lt;br /&gt;  #region IPlugin Members&lt;br /&gt;&lt;br /&gt;  public void Execute(IPluginExecutionContext context)&lt;br /&gt;  {&lt;br /&gt;   try&lt;br /&gt;   {&lt;br /&gt;    LookupRetrieveProxy plugin = new LookupRetrieveProxy(context);&lt;br /&gt;    plugin.Execute();&lt;br /&gt;   }&lt;br /&gt;   catch&lt;br /&gt;   {&lt;br /&gt;   }&lt;br /&gt;  }&lt;br /&gt;  &lt;br /&gt;  #endregion&lt;br /&gt; }&lt;br /&gt; &lt;br /&gt; public class LookupRetrieveProxy&lt;br /&gt; {&lt;br /&gt;  private IPluginExecutionContext Context;&lt;br /&gt;  private Customer Customer;&lt;br /&gt;  private DynamicEntity CustomerInfo;&lt;br /&gt;  &lt;br /&gt;  public LookupRetrieveProxy(IPluginExecutionContext context)&lt;br /&gt;  {&lt;br /&gt;   this.Context = context;&lt;br /&gt;  }&lt;br /&gt;  &lt;br /&gt;  public void Execute()&lt;br /&gt;  {&lt;br /&gt;   if (!Context.OutputParameters.Contains(ParameterName.BusinessEntity))&lt;br /&gt;   {&lt;br /&gt;    return;&lt;br /&gt;   }&lt;br /&gt;&lt;br /&gt;   DynamicEntity entity = (DynamicEntity)Context.OutputParameters[ParameterName.BusinessEntity];&lt;br /&gt;&lt;br /&gt;   if (!entity.Properties.Contains("customerid"))&lt;br /&gt;   {&lt;br /&gt;    return;&lt;br /&gt;   }&lt;br /&gt;&lt;br /&gt;   this.Customer = (Customer)entity.Properties["customerid"];&lt;br /&gt;   if ((this.CustomerInfo = RetrieveCustomer()) != null)&lt;br /&gt;   {&lt;br /&gt;    this.Customer.name = ChangeCustomerName();&lt;br /&gt;   }&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  private DynamicEntity RetrieveCustomer()&lt;br /&gt;  {&lt;br /&gt;   ColumnSet customerColumns = new ColumnSet();&lt;br /&gt;   switch (this.Customer.type)&lt;br /&gt;   {&lt;br /&gt;    case "account":&lt;br /&gt;     customerColumns.AddColumn("accountnumber");&lt;br /&gt;     customerColumns.AddColumn("primarycontactid");&lt;br /&gt;     customerColumns.AddColumn("telephone1");&lt;br /&gt;     break;&lt;br /&gt;    case "contact":&lt;br /&gt;     customerColumns.AddColumn("salutation");&lt;br /&gt;     customerColumns.AddColumn("jobtitle");&lt;br /&gt;     customerColumns.AddColumn("parentcustomerid");&lt;br /&gt;     break;&lt;br /&gt;   }&lt;br /&gt;&lt;br /&gt;   ICrmService Service = this.Context.CreateCrmService(true);&lt;br /&gt;   &lt;br /&gt;   TargetRetrieveDynamic targetRetrieve = new TargetRetrieveDynamic();&lt;br /&gt;   targetRetrieve.EntityId = this.Customer.Value;&lt;br /&gt;   targetRetrieve.EntityName = this.Customer.type;&lt;br /&gt;   &lt;br /&gt;   RetrieveRequest retrieveRequest = new RetrieveRequest();&lt;br /&gt;   retrieveRequest.ColumnSet = customerColumns;&lt;br /&gt;   retrieveRequest.ReturnDynamicEntities = true;&lt;br /&gt;   retrieveRequest.Target = targetRetrieve;&lt;br /&gt;   &lt;br /&gt;   RetrieveResponse retrieveResponse = (RetrieveResponse)Service.Execute(retrieveRequest);&lt;br /&gt;   return retrieveResponse.BusinessEntity as DynamicEntity;&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  private string ChangeCustomerName()&lt;br /&gt;  {&lt;br /&gt;   StringBuilder lookupText = new StringBuilder();&lt;br /&gt;   lookupText.Append(this.Customer.name).Append(" ");&lt;br /&gt;   &lt;br /&gt;   switch (this.CustomerInfo.Name)&lt;br /&gt;   {&lt;br /&gt;    case "account":  &lt;br /&gt;     lookupText.Append(this.GetProperty("accountnumber")).Append(", ");&lt;br /&gt;     lookupText.Append(this.GetProperty("primarycontactid")).Append(", ");&lt;br /&gt;     lookupText.Append(this.GetProperty("telephone1"));&lt;br /&gt;     break;&lt;br /&gt;    case "contact":&lt;br /&gt;     lookupText.Append(this.GetProperty("salutation")).Append(", ");&lt;br /&gt;     lookupText.Append(this.GetProperty("jobtitle")).Append(", ");&lt;br /&gt;     lookupText.Append(this.GetProperty("primarycustomerid"));&lt;br /&gt;     break;&lt;br /&gt;   }&lt;br /&gt;   &lt;br /&gt;   return lookupText.ToString();&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  private String GetProperty(string propName)&lt;br /&gt;  {&lt;br /&gt;   if (!this.CustomerInfo.Properties.Contains(propName))&lt;br /&gt;   {&lt;br /&gt;    return "";&lt;br /&gt;   }&lt;br /&gt;   &lt;br /&gt;   Object property = this.CustomerInfo.Properties[propName];&lt;br /&gt;   &lt;br /&gt;   if (property is String)&lt;br /&gt;   {&lt;br /&gt;    return property.ToString();&lt;br /&gt;   }&lt;br /&gt;   else if (property is Customer)&lt;br /&gt;   {&lt;br /&gt;    return ((Customer)property).name;&lt;br /&gt;   } &lt;br /&gt;   else if (property is Lookup)&lt;br /&gt;   {&lt;br /&gt;    return ((Lookup)property).name;&lt;br /&gt;   }&lt;br /&gt;   &lt;br /&gt;   //not supproted&lt;br /&gt;   return String.Empty;&lt;br /&gt;  }&lt;br /&gt; }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3003801964503297388-2744160225402542359?l=mscrm4ever.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mscrm4ever.blogspot.com/feeds/2744160225402542359/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3003801964503297388&amp;postID=2744160225402542359&amp;isPopup=true' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3003801964503297388/posts/default/2744160225402542359'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3003801964503297388/posts/default/2744160225402542359'/><link rel='alternate' type='text/html' href='http://mscrm4ever.blogspot.com/2009/03/displaying-lookup-with-related-entity.html' title='Displaying a lookup with related entity fields'/><author><name>Adi Katz</name><uri>http://www.blogger.com/profile/16783428437179825705</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_M-mdv3Tfarg/ScG8KtYgd0I/AAAAAAAAALE/mHVJBcByCxo/s72-c/lookuprelatedentityfields.jpg' height='72' width='72'/><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3003801964503297388.post-5343654404474449251</id><published>2009-03-13T06:34:00.005+02:00</published><updated>2009-05-12T01:26:41.884+03:00</updated><title type='text'>CRM 4.0 Associated View Record / Page Counter</title><content type='html'>&lt;div&gt;&lt;/div&gt;&lt;br /&gt;&lt;a href="http://3.bp.blogspot.com/_M-mdv3Tfarg/SbnioMOzPaI/AAAAAAAAAK8/YyPfp1nmXYc/s1600-h/AssociatedViewCounter.jpg"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 139px;border:1px solid black" src="http://3.bp.blogspot.com/_M-mdv3Tfarg/SbnioMOzPaI/AAAAAAAAAK8/YyPfp1nmXYc/s400/AssociatedViewCounter.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5312526415566159266" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;Our Record and Page Counter now supports Associated Views. This AVCR extension complements the RCO solution and provides full record counting coverage. If you require such functionality it is now part of our solution. &lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;The Counter is available for All entities except Activities and All views that fire the RetrieveMultiple event.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3003801964503297388-5343654404474449251?l=mscrm4ever.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mscrm4ever.blogspot.com/feeds/5343654404474449251/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3003801964503297388&amp;postID=5343654404474449251&amp;isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3003801964503297388/posts/default/5343654404474449251'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3003801964503297388/posts/default/5343654404474449251'/><link rel='alternate' type='text/html' href='http://mscrm4ever.blogspot.com/2009/03/crm-40-associated-view-record-page.html' title='CRM 4.0 Associated View Record / Page Counter'/><author><name>Adi Katz</name><uri>http://www.blogger.com/profile/16783428437179825705</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_M-mdv3Tfarg/SbnioMOzPaI/AAAAAAAAAK8/YyPfp1nmXYc/s72-c/AssociatedViewCounter.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3003801964503297388.post-4397827330982884164</id><published>2009-03-12T15:31:00.002+02:00</published><updated>2009-03-12T15:35:11.109+02:00</updated><title type='text'>Controlling CRM window sizes</title><content type='html'>&lt;div&gt;&lt;/div&gt;&lt;br /&gt;A while back I posted a simple script for changing the window size. Although ms current window settings fit most CRM forms there are situations were a form contains only a few fields and it only seem right to fit the window to its content.&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;Following is a simple and generic solution which can be used on all entities. The idea is to create an aspx page under the ISV folder that accepts an entity name and returns the script which moves and resizes the window.&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;The WindowSize.aspx page functions as a repository or a configuration page and does not require compilation. Simply Add the entity names to the Settings Dictionary and you’re done.&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;The script that calls this page should reside in each entity form (onload event). You may also put the script inside the global.js (not supported) if you don’t want to repeat this action more then once.&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;&lt;strong&gt;WindowSize.aspx code&lt;/strong&gt;&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;&lt;pre class="csharp" name="code"&gt;&lt;br /&gt;&amp;lt;%@ Page Language="C#" %&amp;gt;&lt;br /&gt;&amp;lt;%@ IMPORT NAMESPACE="System.Collections.Generic" %&amp;gt;&lt;br /&gt;&amp;lt;script runat="server"&amp;gt;&lt;br /&gt; public static class Entity&lt;br /&gt; {&lt;br /&gt;  public static Dictionary&amp;lt;String,String&amp;gt; Settings;&lt;br /&gt; &lt;br /&gt;  static Entity()&lt;br /&gt;  {&lt;br /&gt;   Settings = new Dictionary&amp;lt;String,String&amp;gt;();&lt;br /&gt;   //Parameters Order: Width,Height,Center,posX,posY&lt;br /&gt;   Settings.Add("account", "800,600,true");&lt;br /&gt;   //Add more entity configuration&lt;br /&gt;   Settings.Add("contact", "850,550,false,10,10");&lt;br /&gt;  }&lt;br /&gt;  &lt;br /&gt;  public static String Script&lt;br /&gt;  {&lt;br /&gt;   get&lt;br /&gt;   {&lt;br /&gt;    return @"function adjWin(width,height,center,posX,posY){if(center==true){posX=(screen.availWidth-width)/2;posY=(screen.availHeight-height)/2;};window.resizeTo(width,height);window.moveTo(posX,posY);};";&lt;br /&gt;   }&lt;br /&gt;  }&lt;br /&gt; }&lt;br /&gt; &lt;br /&gt; public void Page_Load( object sender , EventArgs e)&lt;br /&gt; {&lt;br /&gt;  string entityName = Request.QueryString["etn"] + "";&lt;br /&gt;  if (Entity.Settings.ContainsKey(entityName))&lt;br /&gt;  {&lt;br /&gt;   Response.Write(&lt;br /&gt;    String.Format("adjWin({0});{1}",&lt;br /&gt;    Entity.Settings[entityName],&lt;br /&gt;    Entity.Script)&lt;br /&gt;   );&lt;br /&gt;  }&lt;br /&gt; }&lt;br /&gt; &lt;br /&gt; &lt;br /&gt;&amp;lt;/script&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;&lt;strong&gt;Entity onload script / global.js&lt;/strong&gt;&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;&lt;pre class="js" name="code"&gt;&lt;br /&gt;if (crmForm)&lt;br /&gt;{&lt;br /&gt; var wsScript = document.createElement("SCRIPT");&lt;br /&gt; wsScript.src = SERVER_URL + "/ISV/WindowSize.aspx?etn=" + crmForm.ObjectTypeName&lt;br /&gt; document.documentElement.childNodes[0].appendChild(wsScript);&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3003801964503297388-4397827330982884164?l=mscrm4ever.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mscrm4ever.blogspot.com/feeds/4397827330982884164/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3003801964503297388&amp;postID=4397827330982884164&amp;isPopup=true' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3003801964503297388/posts/default/4397827330982884164'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3003801964503297388/posts/default/4397827330982884164'/><link rel='alternate' type='text/html' href='http://mscrm4ever.blogspot.com/2009/03/controlling-crm-window-sizes.html' title='Controlling CRM window sizes'/><author><name>Adi Katz</name><uri>http://www.blogger.com/profile/16783428437179825705</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3003801964503297388.post-6133454458911052996</id><published>2009-03-12T04:26:00.008+02:00</published><updated>2009-07-15T05:38:58.555+03:00</updated><title type='text'>Display Fetch in IFRAME – Part 2</title><content type='html'>&lt;div&gt;&lt;/div&gt;&lt;br /&gt;My first post about displaying fetch inside an IFRAME has two disadvantages. The first is that the context of the parent window does not follow to the new entity window when you click on add new record (grid) button. This is might be annoying if you’re counting on the default mapping between the two entities. The second problem is that the grid does not refresh after you add (new button) or remove (delete button) a record. This means you are not able to see the changes unless you press on the grid refresh button your self.&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;I made piece with the fact that the parent context did not follow through to the child window since I was using advanced find. But the fact that the gird does not refresh automatically came as a surprise since I remember this feature working “AS IS” in v3.0. I guess ms did a few changes to the auto function which searched and refreshed the grid. Thanks to &lt;a href="http://6ix4our.blogspot.com/2009/03/microsoft-crm-embedding-advanced-find.html"&gt;Dave Berry who commented / Posted&lt;/a&gt; about this and brought it to my attention.&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;In order to overcome this issue I added two new properties to the FetchViewer class.&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;The first parameter is called WithParentContext. This property allows you to decide whether the parent window object id is passed to the child window. I made it a public (as opposed to internal) choice since I figured you can display any type of query inside the FetchViewer including a query that does not reflect a direct parent child relationship.&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;The second parameter is the EntityCode (ObjectTypeCode) of the entity being fetched. If you want the grid to refresh automatically you must supply this value. This is important since the value is eventually passed to MS original auto function which expects this value.&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;The rest of the fetch viewer class was not changed and you can read all about it &lt;a href="http://mscrm4ever.blogspot.com/2008/09/display-fetch-in-iframe.html"&gt;here&lt;/a&gt;.&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;&lt;pre class='js' name='code'&gt;&lt;br /&gt;function OnCrmPageLoad()&lt;br /&gt;{&lt;br /&gt;    window.fetchContacts = new FetchViewer("IFRAME_test");&lt;br /&gt;    fetchContacts.WithParentContext = true;&lt;br /&gt;    fetchContacts.EntityCode = 2;&lt;br /&gt;    fetchContacts.FetchXml  = getFetchXml();&lt;br /&gt;    fetchContacts.LayoutXml = getLayoutXml();&lt;br /&gt;    fetchContacts.Entity    = "contact";&lt;br /&gt;    fetchContacts.QueryId   = "{00000000-0000-0000-00AA-000000666400}";&lt;br /&gt;    fetchContacts.RegisterOnTab(0); //IFRAME ON THE FIRST TAB&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;function getFetchXml()&lt;br /&gt;{&lt;br /&gt;    return '&amp;lt;fetch version="1.0" output-format="xml-platform" mapping="logical" distinct="false"&amp;gt;&amp;lt;entity name="contact"&amp;gt;&amp;lt;attribute name="fullname"/&amp;gt;&amp;lt;attribute name="telephone1"/&amp;gt;&amp;lt;attribute name="contactid"/&amp;gt;&amp;lt;order attribute="fullname" descending="false"/&amp;gt;&amp;lt;filter type="and"&amp;gt;&amp;lt;condition attribute="parentcustomerid" operator="eq" uitype="account" value="' + crmForm.ObjectId + '"/&amp;gt;&amp;lt;/filter&amp;gt;&amp;lt;/entity&amp;gt;&amp;lt;/fetch&amp;gt;';&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;function getLayoutXml()&lt;br /&gt;{&lt;br /&gt;    return '&amp;lt;grid name="resultset" object="2" jump="lastname" select="1" icon="1" preview="1"&amp;gt;&amp;lt;row name="result" id="contactid"&amp;gt;&amp;lt;cell name="fullname" width="300" /&amp;gt;&amp;lt;cell name="telephone1" width="125" /&amp;gt;&amp;lt;/row&amp;gt;&amp;lt;/grid&amp;gt;';&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;function FetchViewer( iframeId )&lt;br /&gt;{&lt;br /&gt;    var Instance = this;&lt;br /&gt;    var vDynamicForm;&lt;br /&gt;    var m_iframeTab;&lt;br /&gt;    var m_iframeDoc;&lt;br /&gt;    var m_iframeShowModalDialogFunc = null;&lt;br /&gt;    var m_windowAutoFunc = null;&lt;br /&gt; &lt;br /&gt;    Instance.WithParentContext = false;&lt;br /&gt;    Instance.EntityCode = 0;&lt;br /&gt;    Instance.Entity    = "";&lt;br /&gt;    Instance.Iframe    = null;&lt;br /&gt;    Instance.FetchXml  = "";&lt;br /&gt;    Instance.QueryId   = "";&lt;br /&gt;    Instance.LayoutXml = "";&lt;br /&gt;&lt;br /&gt;    Instance.RegisterOnTab  = function( tabIndex )&lt;br /&gt;    { &lt;br /&gt;        Instance.Iframe = document.getElementById( iframeId );  &lt;br /&gt;&lt;br /&gt;        if( !Instance.Iframe )&lt;br /&gt;        {&lt;br /&gt;            return alert( "Iframe " + iframeId + " is undefined" );&lt;br /&gt;        }&lt;br /&gt;  &lt;br /&gt;        m_iframeDoc = getIframeDocument();&lt;br /&gt;        var loadingGifHTML  = "&amp;lt;table height='100%' width='100%' style='cursor:wait'&amp;gt;";&lt;br /&gt;        loadingGifHTML += "&amp;lt;tr&amp;gt;";&lt;br /&gt;        loadingGifHTML += "&amp;lt;td valign='middle' align='center'&amp;gt;";&lt;br /&gt;        loadingGifHTML += "&amp;lt;img alt='' src='/_imgs/AdvFind/progress.gif'/&amp;gt;";&lt;br /&gt;        loadingGifHTML += "&amp;lt;div/&amp;gt;&amp;lt;b&amp;gt;Loading View...&amp;lt;/b&amp;gt;";&lt;br /&gt;        loadingGifHTML += "&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&amp;lt;/table&amp;gt;";&lt;br /&gt;        m_iframeDoc.body.innerHTML = loadingGifHTML;&lt;br /&gt;&lt;br /&gt;        if( parseInt( "0" + tabIndex ) == 0 ) &lt;br /&gt;        {&lt;br /&gt;            Instance.Refresh();&lt;br /&gt;        }&lt;br /&gt;        else &lt;br /&gt;        {&lt;br /&gt;            Instance.Iframe.attachEvent( "onreadystatechange" , RefreshOnReadyStateChange ); &lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    function RefreshOnReadyStateChange()&lt;br /&gt;    {&lt;br /&gt;        if( Instance.Iframe.readyState != 'complete' )&lt;br /&gt;        {&lt;br /&gt;            return;&lt;br /&gt;        }&lt;br /&gt;  &lt;br /&gt;        Instance.Refresh();&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    Instance.Refresh = function()&lt;br /&gt;    {&lt;br /&gt;        if( !Instance.Iframe )&lt;br /&gt;        {&lt;br /&gt;            return alert( "Iframe " + iframeId + " is undefined" );&lt;br /&gt;        }&lt;br /&gt; &lt;br /&gt;        m_iframeDoc = getIframeDocument();  &lt;br /&gt;&lt;br /&gt;        Instance.Iframe.detachEvent( "onreadystatechange" , RefreshOnReadyStateChange );&lt;br /&gt;&lt;br /&gt;        var create  = m_iframeDoc.createElement;&lt;br /&gt;        var append1 = m_iframeDoc.appendChild; &lt;br /&gt;        vDynamicForm = create("&amp;lt;FORM name='vDynamicForm' method='post'&amp;gt;");&lt;br /&gt;&lt;br /&gt;        var append2 = vDynamicForm.appendChild;&lt;br /&gt;        append2(create("&amp;lt;INPUT type='hidden' name='FetchXml'&amp;gt;"));&lt;br /&gt;        append2(create("&amp;lt;INPUT type='hidden' name='LayoutXml'&amp;gt;"));&lt;br /&gt;        append2(create("&amp;lt;INPUT type='hidden' name='EntityName'&amp;gt;"));&lt;br /&gt;        append2(create("&amp;lt;INPUT type='hidden' name='DefaultAdvFindViewId'&amp;gt;"));&lt;br /&gt;        append2(create("&amp;lt;INPUT type='hidden' name='ViewType'&amp;gt;"));&lt;br /&gt;        append1( vDynamicForm );&lt;br /&gt;&lt;br /&gt;        vDynamicForm.action = "/" + ORG_UNIQUE_NAME + "/AdvancedFind/fetchData.aspx";&lt;br /&gt;        vDynamicForm.FetchXml.value   = Instance.FetchXml;&lt;br /&gt;        vDynamicForm.LayoutXml.value  = Instance.LayoutXml;&lt;br /&gt;        vDynamicForm.EntityName.value = Instance.Entity;&lt;br /&gt;        vDynamicForm.DefaultAdvFindViewId.value = Instance.QueryId;&lt;br /&gt;        vDynamicForm.ViewType.value = 1039;&lt;br /&gt;        vDynamicForm.submit();&lt;br /&gt;&lt;br /&gt;        Instance.Iframe.attachEvent( "onreadystatechange" , OnViewReady );&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    function OnViewReady()&lt;br /&gt;    {&lt;br /&gt;        if( Instance.Iframe.readyState != 'complete' ) &lt;br /&gt;        {&lt;br /&gt;            return;&lt;br /&gt;        }&lt;br /&gt;  &lt;br /&gt;        Instance.Iframe.style.border = 0;&lt;br /&gt;        Instance.Iframe.detachEvent( "onreadystatechange" , OnViewReady );&lt;br /&gt;   &lt;br /&gt;        if (Instance.WithParentContext == true)&lt;br /&gt;        {&lt;br /&gt;            getIframeWindow().open = OnWindowOpen;&lt;br /&gt;        }&lt;br /&gt;  &lt;br /&gt;        if (m_iframeShowModalDialogFunc == null)&lt;br /&gt;        {&lt;br /&gt;            m_iframeShowModalDialogFunc = getIframeWindow().showModalDialog;&lt;br /&gt;            getIframeWindow().showModalDialog = OnIframeShowModalDialog;&lt;br /&gt;        }&lt;br /&gt;  &lt;br /&gt;        if (Instance.EntityCode &amp;gt; 0)&lt;br /&gt;        { &lt;br /&gt;            if (m_windowAutoFunc == null)&lt;br /&gt;            {&lt;br /&gt;                m_windowAutoFunc = window.auto;&lt;br /&gt;                window.auto = OnWindowAuto;&lt;br /&gt;            }&lt;br /&gt;        }&lt;br /&gt;  &lt;br /&gt;        m_iframeDoc = getIframeDocument();&lt;br /&gt;        m_iframeDoc.body.scroll = "no";&lt;br /&gt;        m_iframeDoc.body.style.padding = "0px";   &lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    function OnWindowOpen(url, name, features) &lt;br /&gt;    {&lt;br /&gt;        //new window&lt;br /&gt;        if (url.indexOf('?') == -1)&lt;br /&gt;        {&lt;br /&gt;            if (url.indexOf('userdefined') == -1 ) &lt;br /&gt;            {&lt;br /&gt;                url = url + "?_CreateFromType=" + crmForm.ObjectTypeCode + "&amp;_CreateFromId=" + crmForm.ObjectId;&lt;br /&gt;            }&lt;br /&gt;            else &lt;br /&gt;            {&lt;br /&gt;                url = url + "?_CreateFromType=" + crmForm.ObjectTypeCode + "&amp;_CreateFromId=" + crmForm.ObjectId + "&amp;etc=" + Instance.EntityCode + "#";&lt;br /&gt;            }&lt;br /&gt;      } &lt;br /&gt;   &lt;br /&gt;        return window.open(url, name, features);&lt;br /&gt;    }&lt;br /&gt; &lt;br /&gt;    function OnIframeShowModalDialog(sUrl, vArguments, sFeatures)&lt;br /&gt;    {&lt;br /&gt;        m_iframeShowModalDialogFunc(sUrl, vArguments, sFeatures);&lt;br /&gt;        Instance.Refresh();&lt;br /&gt;    }&lt;br /&gt; &lt;br /&gt;    function OnWindowAuto(otc)&lt;br /&gt;    {&lt;br /&gt;        if ( otc == Instance.EntityCode )&lt;br /&gt;        {&lt;br /&gt;            getIframeDocument().all.crmGrid.Refresh();&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        m_windowAutoFunc(otc);&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    function getIframeDocument()&lt;br /&gt;    {&lt;br /&gt;        return getIframeWindow().document;&lt;br /&gt;    }&lt;br /&gt; &lt;br /&gt;    function getIframeWindow()&lt;br /&gt;    {&lt;br /&gt;        return Instance.Iframe.contentWindow;&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;OnCrmPageLoad();&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3003801964503297388-6133454458911052996?l=mscrm4ever.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mscrm4ever.blogspot.com/feeds/6133454458911052996/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3003801964503297388&amp;postID=6133454458911052996&amp;isPopup=true' title='53 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3003801964503297388/posts/default/6133454458911052996'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3003801964503297388/posts/default/6133454458911052996'/><link rel='alternate' type='text/html' href='http://mscrm4ever.blogspot.com/2009/03/display-fetch-in-iframe-part-2.html' title='Display Fetch in IFRAME – Part 2'/><author><name>Adi Katz</name><uri>http://www.blogger.com/profile/16783428437179825705</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>53</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3003801964503297388.post-9088193470068916170</id><published>2009-02-28T11:35:00.012+02:00</published><updated>2010-05-05T07:53:50.538+03:00</updated><title type='text'>CRM 4.0 Public View Manager Wizard (Hiding Views)</title><content type='html'>&lt;div&gt;&lt;/div&gt;&lt;br /&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 128px;border:1px solid black;" src="http://4.bp.blogspot.com/_M-mdv3Tfarg/SakO0w6llZI/AAAAAAAAAKs/BWQlcpEUn8c/s400/Front.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5307789935479461266" /&gt;&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;The Public View Manager Wizard is now available on GI’s company website. &lt;br /&gt;&lt;a href="http://www.gicrm.com/?categoryId=29264&amp;itemId=67157"&gt;Click here if you wish to acquire the source code&lt;/a&gt; &lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;I believe that beside FLS, hiding system views is one of the most requested features that are missing from CRM. The concept behind the CRM public views did not fit our product architecture. We needed to come up with a way to deliver pre defined public views with pre defined filtering and column display for different business units and roles in order to create a complete user experience and target specific business logic. Obviously, the idea of having a single default view set for the entire organization and the inability to hide specific views marks the current architecture as unacceptable. &lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;&lt;b&gt;&lt;u&gt;Following are the PVS Wizard features which I’m sure you’ll find very appealing:&lt;/u&gt;&lt;/b&gt;&lt;br /&gt;&lt;ol&gt;&lt;br /&gt;&lt;li&gt;Ability to set different public views for specific business units, roles and users.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Ability to set the public views order within the target’s (BU, ROLE, and USER) list of available views.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Ability to set a default public view at the business unit, role and user level.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Inheritable settings. Settings defined for a business unit affects all roles within that BU. Settings for a Role affects all users within that Role.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Ability to create exceptions at the user level by allowing users to request a specific view as their favorite default view.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Complements our security model (FLS and SMW) and enables us to completely secure CRM from all angles.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;An intuitive User interface that enables you to define a complete PVS settings for an entire organization in just a few minutes.&lt;/li&gt;&lt;br /&gt;&lt;/ol&gt;&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 302px;" src="http://1.bp.blogspot.com/_M-mdv3Tfarg/SakO03Va9HI/AAAAAAAAAK0/sw7Yl_chW84/s400/Technical.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5307789937202623602" /&gt;&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;If you are a Microsoft dynamics partner our sharing initiative enables you to save time and money by acquiring the source code once and distributing it to all your current and future clients. Find a client that will appreciate the PVS wizard or any of our other wizards and acquire the source code while you’re at it.  &lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;&lt;b&gt;&lt;u&gt;PVS Global features:&lt;/u&gt;&lt;/b&gt;&lt;br /&gt;&lt;ol&gt;&lt;br /&gt;&lt;li&gt;Available for all languages&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Supports multi tenancy&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Supports RTL (right to left) and LTR (left to right) displays&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Supports IFD&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Supports IE8&lt;/li&gt;&lt;br /&gt;&lt;/ol&gt;&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;In order to rewind the PVS video, right click on the flash movie, select rewind and then select play.&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;&lt;iframe src="http://www.upsite.co.il/uploaded/files/662_7b54fece5a6888221659c9e0f98b50ab.swf" width="650" height="511"&gt;&lt;/iframe&gt;&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3003801964503297388-9088193470068916170?l=mscrm4ever.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mscrm4ever.blogspot.com/feeds/9088193470068916170/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3003801964503297388&amp;postID=9088193470068916170&amp;isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3003801964503297388/posts/default/9088193470068916170'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3003801964503297388/posts/default/9088193470068916170'/><link rel='alternate' type='text/html' href='http://mscrm4ever.blogspot.com/2009/02/crm-40-public-view-manager-wizard.html' title='CRM 4.0 Public View Manager Wizard (Hiding Views)'/><author><name>Adi Katz</name><uri>http://www.blogger.com/profile/16783428437179825705</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_M-mdv3Tfarg/SakO0w6llZI/AAAAAAAAAKs/BWQlcpEUn8c/s72-c/Front.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3003801964503297388.post-7082785147082105164</id><published>2009-02-14T12:49:00.016+02:00</published><updated>2009-02-19T01:39:52.076+02:00</updated><title type='text'>CRM 4.0 Concatenating Fields Plug-In</title><content type='html'>&lt;div&gt;&lt;/div&gt;&lt;br /&gt;A few weeks back I carried out a boot-camp training course to a team of dot net developers. I have to say it’s quite amusing to see the frustration on experienced developers faces when they realize the amount of energy one needs to invest in order to implement a requirement as simple as the one covered in this post. No doubt dynamics requires getting use to, but the benefits of using such a vast platform makes it all worthwhile.&lt;br /&gt;&lt;br /&gt;Many CRM implementations require you to construct a field’s value by concatenating other fields. There are hands full of reasons for implementing this type of functionality and Most have to do with ways data is displayed or what I call data usability.&lt;br /&gt;&lt;br /&gt;There are three common approaches which one can utilize. If you find yourself in that crossroad consider the pros and cons of each approach before you start developing.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;The first solution is attaching a JavaScript event handler to the CRM form OnSave event. This allows you to set the target field before the form is saved and data is actually sent to the server. There is no arguing about the easiness of this approach since it only requires a few lines to get this done. &lt;p&gt;&lt;/p&gt;&lt;p&gt;Here is an example which you can follow: &lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre class="js" name="code"&gt;&lt;br /&gt;function OnCrmPageLoad()&lt;br /&gt;{&lt;br /&gt;    crmForm.attachEvent(“onsave”,OnCrmPageSave);&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;function OnCrmPageSave()&lt;br /&gt;{&lt;br /&gt;     var attribute1Value = (crmForm.new_attribute1.DataValue)? crmForm.new_attribute1.DataValue:””;&lt;br /&gt;     var attribute2Value = (crmForm.new_attribute2.DataValue)? crmForm.new_attribute2.DataValue:””;&lt;br /&gt;     crmForm.new_targetattribute.DataValue = attribute1Value + “ ” + attribute2Value;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;This method has obvious drawbacks which must be considered. One which is especially important is when the application needs to utilize clients other then Internet explorer such as excel when importing data or a web service when constructing data from code. Of course this is more of a general statement since writing client side code is never about a client but about all available ones. This is something many dynamics developers tend to oversee.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;The second approach is creating a workflow using the workflow designer. Although this provides a remedy to the dilemma presented above, the method is not bullet proof and has its own drawbacks. The first one has to do with the way the workflows operate. Since the workflow process is asynchronous the user won’t see the result concatenation until he reopens the record. A second disadvantage worth mentioning has to do with designer limitations. Consider a scenario where you need to concatenate the values of a lookup field and a text field. The workflow designer does not allow you to concatenate dynamic values which are not of the same type. This might convince you choose the third and final approach which is using a plug-in&lt;br /&gt;&lt;br /&gt;Although this might take longer to implement, planning and coding it once will no doubt save you plenty of time down the road. And since the following plug-in offers a generic solution you’ll get there even faster. The plug-in also offers a remedy to the workflow drawbacks mentioned above.&lt;br /&gt;&lt;br /&gt;Basically, in order to provide full coverage, the plug-in needs to run on both post create and post update events and also require post event images from which the values are read and concatenated. This approach seems unnecessary at first but since lookup and customer types don’t carry their names (just guids) to the plug-in the only way to retrieve the actual names is to use an image. &lt;br /&gt;&lt;br /&gt;In order to make the process friendlier I added a configuration xml which enables you to configure multiple target fields for each entity and set the desired format for each target field.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Bellow is a sample configuration xml. You must add it to the post-create and post-update unsecure configuration boxes. You might wonder why this is required for both events. The reason is that the plug-in needs to address both the creation of a new record and the update of existing records when it loads / cached and re-cached by CRM.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;This sample uses fields from the incident entity as a showcase. This is also the case for the plug-in solution xml file which is displayed at the bottom of the post.&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;&lt;a href="http://3.bp.blogspot.com/_M-mdv3Tfarg/SZyaj7nhe3I/AAAAAAAAAKU/P-D2FJLnKx0/s1600-h/RegTool1.jpg"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 309px; height: 126px;" src="http://3.bp.blogspot.com/_M-mdv3Tfarg/SZyaj7nhe3I/AAAAAAAAAKU/P-D2FJLnKx0/s400/RegTool1.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5304284403225164658" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;&lt;pre class="xml" name="code"&gt;&lt;br /&gt;&amp;lt;Entity&amp;gt;&lt;br /&gt; &amp;lt;Target ID="title" Format="{0} - {1}"&amp;gt;&lt;br /&gt;  &amp;lt;Field ID="subjectid" Type="Lookup"/&amp;gt;&lt;br /&gt;  &amp;lt;Field ID="customerid" Type="Customer"/&amp;gt;&lt;br /&gt; &amp;lt;/Target&amp;gt;&lt;br /&gt;&amp;lt;/Entity&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The Target node describes the field whose value is constructed from the inner Field nodes. The Target ID attribute must specify and existing attribute name. The plug-in code makes use of the String.Format method which receives a format string such as “{0} – {1}” and a list of optional parameters. Each Field Node must specify an existing entity attribute and correct Type. The Type attribute is obviously used to differentiate between CRM types.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;The Generic concatenation Plug-in is build on top of 3 classes. A Handler class which implements the IPlugIn Interface. A TargetField class which represent a single Target. And the ConcatPlugin proxy class which implements the concatenation process. I’ve also added remarks and regions to make the code more readable.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre class="csharp" name="code"&gt;&lt;br /&gt;using System;&lt;br /&gt;using System.Xml;&lt;br /&gt;using System.Collections.Generic;&lt;br /&gt;using System.Text;&lt;br /&gt;&lt;br /&gt;using Microsoft.Crm.Sdk;&lt;br /&gt;using Microsoft.Crm.SdkTypeProxy;&lt;br /&gt;&lt;br /&gt;namespace GenericConcatPlugIn&lt;br /&gt;{&lt;br /&gt; /// &lt;summary&gt;&lt;br /&gt; /// Plug-In Handler&lt;br /&gt; /// &lt;/summary&gt;&lt;br /&gt; public class Handler : IPlugin&lt;br /&gt; {&lt;br /&gt;  #region IPlugin Members&lt;br /&gt;  /// &lt;summary&gt;&lt;br /&gt;  /// Holds all fields that require concatenation&lt;br /&gt;  /// &lt;/summary&gt;&lt;br /&gt;  private Dictionary&lt;String, TargetField&gt; Targets;&lt;br /&gt;  /// &lt;summary&gt;&lt;br /&gt;  /// plug-in constructor&lt;br /&gt;  /// &lt;/summary&gt;&lt;br /&gt;  /// &lt;param name="config"&gt;&lt;/param&gt;&lt;br /&gt;  /// &lt;param name="secureConfig"&gt;&lt;/param&gt;&lt;br /&gt;  public Handler(String config, String secureConfig)&lt;br /&gt;  {&lt;br /&gt;   XmlDocument TargetsDocument = TryLoadTargetsXml(config);&lt;br /&gt;&lt;br /&gt;   #region Build Targets Settings&lt;br /&gt;&lt;br /&gt;   XmlNodeList TargetsNodeList = TargetsDocument.SelectNodes("//Target");&lt;br /&gt;   this.Targets = new Dictionary&lt;String, TargetField&gt;(TargetsNodeList.Count);&lt;br /&gt;&lt;br /&gt;   foreach (XmlElement ndField in TargetsNodeList)&lt;br /&gt;   {&lt;br /&gt;    TargetField Target = new TargetField(ndField);&lt;br /&gt;    if (this.Targets.ContainsKey(Target.ID))&lt;br /&gt;    {&lt;br /&gt;     this.Targets[Target.ID] = Target;&lt;br /&gt;    }&lt;br /&gt;    else&lt;br /&gt;    {&lt;br /&gt;     this.Targets.Add(Target.ID, Target);&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;   }&lt;br /&gt;&lt;br /&gt;   #endregion&lt;br /&gt;  }&lt;br /&gt;  /// &lt;summary&gt;&lt;br /&gt;  /// Attempt to load entity configuration xml&lt;br /&gt;  /// &lt;/summary&gt;&lt;br /&gt;  /// &lt;param name="configXml"&gt;&lt;/param&gt;&lt;br /&gt;  /// &lt;returns&gt;&lt;/returns&gt;&lt;br /&gt;  private XmlDocument TryLoadTargetsXml(String configXml)&lt;br /&gt;  {&lt;br /&gt;   #region Validate Plugin Configuration&lt;br /&gt;&lt;br /&gt;   if (configXml == null || configXml.Length == 0)&lt;br /&gt;   {&lt;br /&gt;    throw new InvalidPluginExecutionException("Initialize: Configuration Xml cannot be null");&lt;br /&gt;   }&lt;br /&gt;&lt;br /&gt;   #endregion&lt;br /&gt;&lt;br /&gt;   #region Return Configuratoin Document&lt;br /&gt;&lt;br /&gt;   try&lt;br /&gt;   {&lt;br /&gt;    XmlDocument document = new XmlDocument();&lt;br /&gt;    document.LoadXml(configXml);&lt;br /&gt;    return document;&lt;br /&gt;   }&lt;br /&gt;   catch (Exception ex)&lt;br /&gt;   {&lt;br /&gt;    throw new InvalidPluginExecutionException("Initialize: Error loading Configuration Xml Document", ex);&lt;br /&gt;   }&lt;br /&gt;&lt;br /&gt;   #endregion&lt;br /&gt;  }&lt;br /&gt;  /// &lt;summary&gt;&lt;br /&gt;  /// Plug-in Execute implementation&lt;br /&gt;  /// &lt;/summary&gt;&lt;br /&gt;  /// &lt;param name="context"&gt;&lt;/param&gt;&lt;br /&gt;  public void Execute(IPluginExecutionContext context)&lt;br /&gt;  {&lt;br /&gt;   &lt;br /&gt;   #region Execute Concatenation Plugin&lt;br /&gt;&lt;br /&gt;   ConcatPlugin contactPlugIn = new ConcatPlugin();&lt;br /&gt;   contactPlugIn.Context = context;&lt;br /&gt;   contactPlugIn.Execute(Targets);&lt;br /&gt;&lt;br /&gt;   #endregion&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  #endregion&lt;br /&gt; }&lt;br /&gt; /// &lt;summary&gt;&lt;br /&gt; /// A Field whose value is constructed (concatenated) from other fields&lt;br /&gt; /// &lt;/summary&gt;&lt;br /&gt; public class TargetField&lt;br /&gt; {&lt;br /&gt;  #region Target Field Members&lt;br /&gt;&lt;br /&gt;  public String ID = String.Empty;&lt;br /&gt;  public String Format = String.Empty;&lt;br /&gt;  public Dictionary&lt;String, String&gt; Related;&lt;br /&gt;&lt;br /&gt;  #endregion&lt;br /&gt;&lt;br /&gt;  public TargetField(XmlElement ndTarget)&lt;br /&gt;  {&lt;br /&gt;   #region Set Field Members&lt;br /&gt;&lt;br /&gt;   this.Related = new Dictionary&lt;String, String&gt;();&lt;br /&gt;   this.ID = ndTarget.Attributes["ID"].Value;&lt;br /&gt;   this.Format = ndTarget.Attributes["Format"].Value;&lt;br /&gt;&lt;br /&gt;   #endregion&lt;br /&gt;&lt;br /&gt;   #region Set Field Related (Concatenated) Fields&lt;br /&gt;&lt;br /&gt;   XmlNodeList RelatedFieldList = ndTarget.SelectNodes("Field");&lt;br /&gt;&lt;br /&gt;   foreach (XmlElement related in RelatedFieldList)&lt;br /&gt;   {&lt;br /&gt;    String relatedId = related.Attributes["ID"].Value;&lt;br /&gt;    String relatedType = related.Attributes["Type"].Value;&lt;br /&gt;&lt;br /&gt;    if (!Related.ContainsKey(relatedId))&lt;br /&gt;    {&lt;br /&gt;     Related.Add(relatedId, relatedType);&lt;br /&gt;    }&lt;br /&gt;   }&lt;br /&gt;&lt;br /&gt;   #endregion&lt;br /&gt;&lt;br /&gt;   #region Sample Target Xml Configuration&lt;br /&gt;   /* &lt;br /&gt;      &lt;Target ID="salutaion" Format="{0} {1} ({2})"&gt;&lt;br /&gt;    &lt;Field ID="firstname" Type="String"/&gt;&lt;br /&gt;    &lt;Field ID="lastname" Type="String"/&gt; &lt;br /&gt;    &lt;Field ID="middlename" Type="String"/&gt;&lt;br /&gt;      &lt;/Target&gt;&lt;br /&gt;      */&lt;br /&gt;   #endregion&lt;br /&gt;  }&lt;br /&gt; }&lt;br /&gt; /// &lt;summary&gt;&lt;br /&gt; /// Plug-in proxy&lt;br /&gt; /// &lt;/summary&gt;&lt;br /&gt; public class ConcatPlugin&lt;br /&gt; {&lt;br /&gt;  /// &lt;summary&gt;&lt;br /&gt;  /// Plug-in Original Context&lt;br /&gt;  /// &lt;/summary&gt;&lt;br /&gt;  public IPluginExecutionContext Context;&lt;br /&gt;  /// &lt;summary&gt;&lt;br /&gt;  /// Plug-in Main method&lt;br /&gt;  /// &lt;/summary&gt;&lt;br /&gt;  /// &lt;param name="Targets"&gt;&lt;/param&gt;&lt;br /&gt;  internal void Execute(Dictionary&lt;String, TargetField&gt; Targets)&lt;br /&gt;  {&lt;br /&gt;   #region Validate Target Entity&lt;br /&gt;&lt;br /&gt;   if (!this.Context.InputParameters.Contains(ParameterName.Target))&lt;br /&gt;   {&lt;br /&gt;    return;&lt;br /&gt;   }&lt;br /&gt;&lt;br /&gt;   #endregion&lt;br /&gt;&lt;br /&gt;   #region Concatenate Target Fields&lt;br /&gt;&lt;br /&gt;   DynamicEntity Current = null;&lt;br /&gt;   foreach (KeyValuePair&lt;String, TargetField&gt; Target in Targets)&lt;br /&gt;   {&lt;br /&gt;    Current = ((DynamicEntity)this.Context.InputParameters[ParameterName.Target]);&lt;br /&gt;    Current.Properties.Add(ConcatProperty(Target.Value));&lt;br /&gt;   }&lt;br /&gt;&lt;br /&gt;   if (Current == null)&lt;br /&gt;   {&lt;br /&gt;    return;&lt;br /&gt;   }&lt;br /&gt;&lt;br /&gt;   #region Update the target &lt;br /&gt;   &lt;br /&gt;   if (this.Context.MessageName == "Create")&lt;br /&gt;   {&lt;br /&gt;    if( !this.Context.OutputParameters.Contains("id") )&lt;br /&gt;    {&lt;br /&gt;     return;&lt;br /&gt;    }&lt;br /&gt;    &lt;br /&gt;    ICrmService Service = this.Context.CreateCrmService(true);&lt;br /&gt;    Key entityId = new Key( new Guid( this.Context.OutputParameters["id"].ToString() ) );&lt;br /&gt;    KeyProperty entityKey = new KeyProperty(this.Context.PrimaryEntityName + "id",entityId);&lt;br /&gt;    Current.Properties.Add(entityKey);&lt;br /&gt;    Service.Update(Current);&lt;br /&gt;   }&lt;br /&gt;   else if (this.Context.MessageName == "Update" &amp;&amp; this.Context.Depth == 1)&lt;br /&gt;   {&lt;br /&gt;    ICrmService Service = this.Context.CreateCrmService(true);&lt;br /&gt;    Service.Update(Current);&lt;br /&gt;   }&lt;br /&gt;   #endregion&lt;br /&gt;   &lt;br /&gt;   #endregion&lt;br /&gt;  }&lt;br /&gt;  /// &lt;summary&gt;&lt;br /&gt;  /// Reference to Target Dynamic Entity being created or updated&lt;br /&gt;  /// &lt;/summary&gt;&lt;br /&gt;  private DynamicEntity Target&lt;br /&gt;  {&lt;br /&gt;   get { return this.Context.InputParameters[ParameterName.Target] as DynamicEntity; }&lt;br /&gt;  }&lt;br /&gt;  /// &lt;summary&gt;&lt;br /&gt;  /// Reference to the Target Property Collection&lt;br /&gt;  /// &lt;/summary&gt;&lt;br /&gt;  private PropertyCollection PostImageProperties&lt;br /&gt;  {&lt;br /&gt;   get&lt;br /&gt;   {&lt;br /&gt;    return ((DynamicEntity)this.Context.PostEntityImages[ParameterName.Target]).Properties;&lt;br /&gt;   }&lt;br /&gt;  }&lt;br /&gt;  /// &lt;summary&gt;&lt;br /&gt;  /// Concatenates the target fields and returns the a StringProperty&lt;br /&gt;  /// &lt;/summary&gt;&lt;br /&gt;  /// &lt;param name="Target"&gt;&lt;/param&gt;&lt;br /&gt;  /// &lt;returns&gt;&lt;/returns&gt;&lt;br /&gt;  private StringProperty ConcatProperty(TargetField Target)&lt;br /&gt;  {&lt;br /&gt;   List&lt;String&gt; TargetValues = new List&lt;String&gt;();&lt;br /&gt;   StringProperty TargetProperty = new StringProperty();&lt;br /&gt;   TargetProperty.Name = Target.ID;&lt;br /&gt;&lt;br /&gt;   #region Retrieve Each Concatenated Field Value&lt;br /&gt;&lt;br /&gt;   foreach (KeyValuePair&lt;String, String&gt; related in Target.Related)&lt;br /&gt;   {&lt;br /&gt;    String fieldId = related.Key;&lt;br /&gt;    String fieldType = related.Value;&lt;br /&gt;&lt;br /&gt;    #region Get Field Value or Name by Type&lt;br /&gt;&lt;br /&gt;    PropertyCollection Properties = PostImageProperties;&lt;br /&gt;&lt;br /&gt;    switch (fieldType)&lt;br /&gt;    {&lt;br /&gt;     case "String":&lt;br /&gt;      TargetValues.Add(Properties[fieldId] + "");&lt;br /&gt;      break;&lt;br /&gt;     case "CrmNumber":&lt;br /&gt;      TargetValues.Add(((CrmNumber)Properties[fieldId]).Value + "");&lt;br /&gt;      break;&lt;br /&gt;     case "CrmFloat":&lt;br /&gt;      TargetValues.Add(((CrmFloat)Properties[fieldId]).Value + "");&lt;br /&gt;      break;&lt;br /&gt;     case "CrmDecimal":&lt;br /&gt;      TargetValues.Add(((CrmDecimal)Properties[fieldId]).Value + "");&lt;br /&gt;      break;&lt;br /&gt;     case "CrmMoney":&lt;br /&gt;      TargetValues.Add(((CrmMoney)Properties[fieldId]).Value + "");&lt;br /&gt;      break;&lt;br /&gt;     case "Lookup":&lt;br /&gt;      TargetValues.Add(((Lookup)Properties[fieldId]).name + "");&lt;br /&gt;      break;&lt;br /&gt;     case "Owner":&lt;br /&gt;      TargetValues.Add(((Owner)Properties[fieldId]).name + "");&lt;br /&gt;      break;&lt;br /&gt;     case "Customer":&lt;br /&gt;      TargetValues.Add(((Customer)Properties[fieldId]).name + "");&lt;br /&gt;      break;&lt;br /&gt;    }&lt;br /&gt;    #endregion&lt;br /&gt;   }&lt;br /&gt;&lt;br /&gt;   #endregion&lt;br /&gt;&lt;br /&gt;   TargetProperty.Value = String.Format(Target.Format, TargetValues.ToArray());&lt;br /&gt;   return TargetProperty;&lt;br /&gt;  }&lt;br /&gt; }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Following is the plug-in solutionxml. After you construct the plug-in project put the file inside the project debug folder where the plug-in dll resides and import the solution using the registration tool. The solution xml contains step information for the Incident post create and update events as described above.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre class="xml" name="code"&gt;&lt;br /&gt;&amp;lt;Register LogFile="Plug-in Registration Log.txt" Server="http://moss:5555" Org="MicrosoftCRM" Domain="" UserName="administrator"&amp;gt;&lt;br /&gt; &amp;lt;Solution SourceType="1" Assembly="GenericConcatPlugIn.dll" Id="fc137b56-936f-4711-852c-d5d4ca508f73"&amp;gt;&lt;br /&gt;  &amp;lt;PluginTypes&amp;gt;&lt;br /&gt;   &amp;lt;Plugin TypeName="GenericConcatPlugIn.Handler" FriendlyName="b283b2c5-0375-404e-b070-bbb350cb9d24" Id="2545c234-dee8-4a4c-979f-749748358295"&amp;gt;&lt;br /&gt;    &amp;lt;Steps&amp;gt;&lt;br /&gt;     &amp;lt;Step PluginTypeName="GenericConcatPlugIn.Handler" PluginTypeFriendlyName="b283b2c5-0375-404e-b070-bbb350cb9d24" CustomConfiguration="&amp;lt;Entity&amp;gt;&amp;#xD;&amp;#xA; &amp;lt;Target ID=&amp;quot;title&amp;quot; Format=&amp;quot;{0} - {1}&amp;quot;&amp;gt;&amp;#xD;&amp;#xA;  &amp;lt;Field ID=&amp;quot;subjectid&amp;quot; Type=&amp;quot;Lookup&amp;quot;/&amp;gt;&amp;#xD;&amp;#xA;  &amp;lt;Field ID=&amp;quot;customerid&amp;quot; Type=&amp;quot;Customer&amp;quot;/&amp;gt;&amp;#xD;&amp;#xA; &amp;lt;/Target&amp;gt;&amp;#xD;&amp;#xA;&amp;lt;/Entity&amp;gt;&amp;#xD;&amp;#xA;" SecureConfiguration="" Description="Create of incident in Parent Pipeline" FilteringAttributes="" ImpersonatingUserId="" InvocationSource="0" MessageName="Create" Mode="0" PrimaryEntityName="incident" SecondaryEntityName="none" Stage="50" SupportedDeployment="0" Rank="1" Id="c0fef31b-08fe-dd11-91f6-0003ff2d0264"&amp;gt;&lt;br /&gt;      &amp;lt;Images&amp;gt;&lt;br /&gt;       &amp;lt;Image EntityAlias="Target" ImageType="1" MessagePropertyName="Id" Attributes="" Id="f0f9d623-0bfe-dd11-91f6-0003ff2d0264" /&amp;gt;&lt;br /&gt;      &amp;lt;/Images&amp;gt;&lt;br /&gt;     &amp;lt;/Step&amp;gt;&lt;br /&gt;     &amp;lt;Step PluginTypeName="GenericConcatPlugIn.Handler" PluginTypeFriendlyName="b283b2c5-0375-404e-b070-bbb350cb9d24" CustomConfiguration="&amp;lt;Entity&amp;gt;&amp;#xD;&amp;#xA; &amp;lt;Target ID=&amp;quot;title&amp;quot; Format=&amp;quot;{0} - {1}&amp;quot;&amp;gt;&amp;#xD;&amp;#xA;  &amp;lt;Field ID=&amp;quot;subjectid&amp;quot; Type=&amp;quot;Lookup&amp;quot;/&amp;gt;&amp;#xD;&amp;#xA;  &amp;lt;Field ID=&amp;quot;customerid&amp;quot; Type=&amp;quot;Customer&amp;quot;/&amp;gt;&amp;#xD;&amp;#xA; &amp;lt;/Target&amp;gt;&amp;#xD;&amp;#xA;&amp;lt;/Entity&amp;gt;&amp;#xD;&amp;#xA;" SecureConfiguration="" Description="Update of incident in Parent Pipeline" FilteringAttributes="" ImpersonatingUserId="" InvocationSource="0" MessageName="Update" Mode="0" PrimaryEntityName="incident" SecondaryEntityName="none" Stage="50" SupportedDeployment="0" Rank="1" Id="c03e4a2e-08fe-dd11-91f6-0003ff2d0264"&amp;gt;&lt;br /&gt;      &amp;lt;Images&amp;gt;&lt;br /&gt;       &amp;lt;Image EntityAlias="Target" ImageType="0" MessagePropertyName="Target" Attributes="" Id="8065c335-08fe-dd11-91f6-0003ff2d0264" /&amp;gt;&lt;br /&gt;      &amp;lt;/Images&amp;gt;&lt;br /&gt;     &amp;lt;/Step&amp;gt;&lt;br /&gt;    &amp;lt;/Steps&amp;gt;&lt;br /&gt;   &amp;lt;/Plugin&amp;gt;&lt;br /&gt;  &amp;lt;/PluginTypes&amp;gt;&lt;br /&gt; &amp;lt;/Solution&amp;gt;&lt;br /&gt;&amp;lt;/Register&amp;gt;&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Incident Post Create And Post Entity Image&lt;/strong&gt;&lt;br /&gt;&lt;a href="http://3.bp.blogspot.com/_M-mdv3Tfarg/SZyajjDaj6I/AAAAAAAAAKM/M6766RXGjdU/s1600-h/CreateStep.jpg"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 218px;" src="http://3.bp.blogspot.com/_M-mdv3Tfarg/SZyajjDaj6I/AAAAAAAAAKM/M6766RXGjdU/s400/CreateStep.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5304284396631265186" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;&lt;a href="http://4.bp.blogspot.com/_M-mdv3Tfarg/SZyaj-5-HdI/AAAAAAAAAKc/l7hHtVP8Hqw/s1600-h/UpdateImageStep.jpg"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 93px;" src="http://4.bp.blogspot.com/_M-mdv3Tfarg/SZyaj-5-HdI/AAAAAAAAAKc/l7hHtVP8Hqw/s400/UpdateImageStep.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5304284404107845074" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;&lt;strong&gt;Incident Post Update And Post Entity Image&lt;/strong&gt;&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;&lt;a href="http://2.bp.blogspot.com/_M-mdv3Tfarg/SZyajzJE9wI/AAAAAAAAAKk/uXLI75r_Srk/s1600-h/UpdateStep.jpg"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 217px;" src="http://2.bp.blogspot.com/_M-mdv3Tfarg/SZyajzJE9wI/AAAAAAAAAKk/uXLI75r_Srk/s400/UpdateStep.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5304284400949982978" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;&lt;a href="http://4.bp.blogspot.com/_M-mdv3Tfarg/SZyaj-5-HdI/AAAAAAAAAKc/l7hHtVP8Hqw/s1600-h/UpdateImageStep.jpg"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 93px;" src="http://4.bp.blogspot.com/_M-mdv3Tfarg/SZyaj-5-HdI/AAAAAAAAAKc/l7hHtVP8Hqw/s400/UpdateImageStep.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5304284404107845074" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Good Luck&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3003801964503297388-7082785147082105164?l=mscrm4ever.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mscrm4ever.blogspot.com/feeds/7082785147082105164/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3003801964503297388&amp;postID=7082785147082105164&amp;isPopup=true' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3003801964503297388/posts/default/7082785147082105164'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3003801964503297388/posts/default/7082785147082105164'/><link rel='alternate' type='text/html' href='http://mscrm4ever.blogspot.com/2009/02/crm-40-concatenating-fields-plug-in.html' title='CRM 4.0 Concatenating Fields Plug-In'/><author><name>Adi Katz</name><uri>http://www.blogger.com/profile/16783428437179825705</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_M-mdv3Tfarg/SZyaj7nhe3I/AAAAAAAAAKU/P-D2FJLnKx0/s72-c/RegTool1.jpg' height='72' width='72'/><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3003801964503297388.post-205599262294650817</id><published>2009-02-10T14:05:00.006+02:00</published><updated>2009-02-10T14:20:59.263+02:00</updated><title type='text'>Displaying an Image in an IFRAME</title><content type='html'>&lt;div&gt;&lt;/div&gt;&lt;br /&gt;This seems like a very simple requirement. But when it comes to implementation, you might find that fitting this requirement into an already existing functionality such as the CRM the annotations (notes) facility, is not as strait forward as it sounds.  &lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;Wanting to display a user avatar, a company logo or even a product image is a very acceptable requirement. And since CRM does not allow such facility out of box, I figured it would be nice to show you how this could be done in just a few minutes.&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;The simplest solution would be to place all images under the ISV folder then create an IFRAME, where desirable, and set its SRC attribute to a specific URL through script. This also requires the user or application to adhere to a strict naming convention such as a contact FirstName + LastName.gif or an account  accountid.jpg or accountnumber.bmp and so forth. &lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;For the sake of discussion the rest of my code bits would refer to the account entity and presenting an account logo inside a dedicated iframe e.g. IFRAME_accountlogo.&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;Your code might look like the following lines:&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;&lt;pre class='js' name='code'&gt;&lt;br /&gt;var accountLogo = "/isv/images/accounts/missing.gif";&lt;br /&gt;If (crmForm.accountnumber.DataValue != null)&lt;br /&gt;{&lt;br /&gt;    accountLogo = "/isv/images/accounts/" + crmForm.accountnumber.DataValue + ".gif";&lt;br /&gt;}&lt;br /&gt;document.all.IFRAME_accountlogo.src = accountLogo;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;This process is quite limiting since it requires the involvement and communication with a power user or an administrator who need to put the images in their respective folders. They also need to manage the changes and deletions of images from that folder for obvious reasons. By now this looks like a bad strategy and we need to come up with a better and more manageable solution.&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;Can I upgrade the above solution? The answer is of course you can! You can automate this type of process by allowing the user to upload the image to a specified location and saving the image name to a new dedicated attribute you set on the entity e.g new_accountimagename.  &lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;The client side script might look like the following lines: &lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;&lt;pre class='js' name='code'&gt;&lt;br /&gt;var accountLogo = "/isv/images/accounts/missing.gif";&lt;br /&gt;if (crmForm.new_accountimagename.DataValue != null)&lt;br /&gt;{&lt;br /&gt;    accountLogo = "/isv/images/accounts/" + crmForm.new_accountimagename.DataValue;&lt;br /&gt;}&lt;br /&gt;document.all.IFRAME_accountlogo.src = accountLogo;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;You might argue, and rightly so, that this type of solution does not completely resolve the need for our administrator to manage these folders, and CRM already has an uploading facility (for annotations), so why not use that?! &lt;br /&gt;Obviously this type of solution only answers a partial requirement and we need to come up with a better one yet again.&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;So how do you take advantage of CRM’s annotation facility? And how can you bind the upload process to an IFRAME?&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;If you ask a developer, with no relevant knowledge of dynamics, how to do that with asp.net he would probably tell you that you need to read the binaries from the database and render them back to the calling image on the client. &lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;e.g. &lt;br /&gt;&lt;pre class='html' name='code'&gt;&lt;br /&gt;&lt;img src=”/MicrosoftCRM/isv/renderimage.aspx?someid={GUID}”/&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;Wait a minute, this looks very familiar. And indeed it is! as CRM already uses this type of functionality inside the email entity. When you track an email with an inline image from outlook client CRM saves the inline image as attachment and present it inside the email body. &lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;e.g. &lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;&lt;pre class='html' name='code'&gt;&lt;br /&gt;&lt;img width=978 height=630 id="Picture_x0020_1" alt="image001.png" src="/MicrosoftCRM/Activities/Attachment/download.aspx?AttachmentType=1001&amp;AttachmentId=4108f5f9-63f7-dd11-b2e0-0003ff2d0264"&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;So I asked my self why would ms send the attachment entity type if this type of functionality is only for attachments? And as it appears if you change the AttachmentType parameter from 1001 to 5 (annotation object type code) and set the attachmentid to an annotationid you receive the desired results.&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;e.g. &lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;&lt;pre class='html' name='code'&gt;&lt;br /&gt;&lt;img src="/MicrosoftCRM/Activities/Attachment/download.aspx?AttachmentType=5&amp;amp;AttachmentId=D02BFFF3-EFF1-DD11-A6A4-0003FF2D0264"&gt; &lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;And your onload script should look like:&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;&lt;pre class='js' name='code'&gt;&lt;br /&gt;var annotationid = getAccountLogoAnnotationId();&lt;br /&gt;var iframeDoc = document.all.IFRAME_accountlogo.contentWindow.document;&lt;br /&gt;var image = iframeDoc.createElement('img'); &lt;br /&gt;image.src = prependOrgName("/Activities/Attachment/download.aspx?AttachmentType=5&amp;amp;AttachmentId=") + annotationid;&lt;br /&gt;iframeDoc.body.appendChild(image);&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;The getAccountLogoAnnotationId function should use a fetchxml to retrieve the annotationid. The best way of doing that is telling the user to upload the image under a well known name and creating a constant fetchxml request as follows.&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;&lt;pre class='xml' name='code'&gt;&lt;br /&gt;&lt;fetch version="1.0" output-format="xml-platform" mapping="logical" distinct="false"&gt;&lt;br /&gt; &lt;entity name="annotation"&gt;&lt;br /&gt;  &lt;attribute name="annotationid"/&gt;&lt;br /&gt;  &lt;order attribute="subject" descending="false"/&gt;&lt;br /&gt;  &lt;filter type="and"&gt;&lt;br /&gt;   &lt;condition attribute="isdocument" operator="eq" value="1"/&gt;&lt;br /&gt;   &lt;condition attribute="filename" operator="like" value="[Account Number Value]&amp;amp;#37;"/&gt;&lt;br /&gt;  &lt;/filter&gt;&lt;br /&gt; &lt;/entity&gt;&lt;br /&gt;&lt;/fetch&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;Is this type of solution consider supported? My first gut feeling is actualy yes. I don’t see why ms would downgrade this type of solution to only support email attachment. The only thing that might change is the actual url. This seems a good enough solution for a 5 minute work.&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;Eventually you might mimic the entire functionality your self by creating a download.aspx page, reading the image binaries from the filtered annotation view and render the result your self.&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;For a complete example of how to use an Ajax fetch call to CRM &lt;a href="http://mscrm4ever.blogspot.com/2008/09/ajax-using-fetch-message.html"&gt;follow this post&lt;/a&gt;.&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3003801964503297388-205599262294650817?l=mscrm4ever.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mscrm4ever.blogspot.com/feeds/205599262294650817/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3003801964503297388&amp;postID=205599262294650817&amp;isPopup=true' title='18 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3003801964503297388/posts/default/205599262294650817'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3003801964503297388/posts/default/205599262294650817'/><link rel='alternate' type='text/html' href='http://mscrm4ever.blogspot.com/2009/02/displaying-image-in-iframe.html' title='Displaying an Image in an IFRAME'/><author><name>Adi Katz</name><uri>http://www.blogger.com/profile/16783428437179825705</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>18</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3003801964503297388.post-7994947086386082517</id><published>2009-01-26T09:04:00.012+02:00</published><updated>2010-05-05T07:54:17.851+03:00</updated><title type='text'>CRM 4.0 Bulk Delete Wizard</title><content type='html'>&lt;div&gt;&lt;/div&gt;&lt;br /&gt;&lt;a href="http://2.bp.blogspot.com/_M-mdv3Tfarg/SX1jNjiEjoI/AAAAAAAAAJk/upj7QEq5C9Y/s1600-h/BDW.jpg"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 239px;border:1px solid black;" src="http://2.bp.blogspot.com/_M-mdv3Tfarg/SX1jNjiEjoI/AAAAAAAAAJk/upj7QEq5C9Y/s400/BDW.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5295497821385166466" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;The BDW is another cool wizard which we developed. As you all know the only way to create a bulk delete job is to programmatically develop one using CRM SDK.I decided to automate this process for a number of reasons. Mainly since our products come with sample data which needs to be erased from the system before the customer go live. Secondly we have extensive auditing and logging which needs to be deleted periodically.  Of course there are other good enough reasons to having the ability to create bulk delete jobs in just a few seconds.&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;Global Features:&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;Supports all languages&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Supports multi-tenancy&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Supports IFD&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Supports IE8&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;For a complete list of entities which are valid for bulk delete please see CRM advanced find entity list.&lt;br /&gt;&lt;strong&gt;Note:&lt;/strong&gt; The BDW wizard enables you to delete all system jobs such as Bulk delete, Duplicate Delection, Bulk Email, Import, workflows jobs.&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;The wizard is &lt;a href="http://www.gicrm.com/?categoryId=29264&amp;itemId=65236"&gt;now available&lt;/a&gt;. You may also send your enquiries and questions to &lt;a href='mailto:support@gicrm.com'&gt;support@gicrm.com&lt;/a&gt;.&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;&lt;iframe src="http://www.upsite.co.il/uploaded/files/662_c8bc36eed02ab1c8b28f7342cb588f0c.swf" width="650" height="511"&gt;&lt;/iframe&gt;&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3003801964503297388-7994947086386082517?l=mscrm4ever.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mscrm4ever.blogspot.com/feeds/7994947086386082517/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3003801964503297388&amp;postID=7994947086386082517&amp;isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3003801964503297388/posts/default/7994947086386082517'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3003801964503297388/posts/default/7994947086386082517'/><link rel='alternate' type='text/html' href='http://mscrm4ever.blogspot.com/2009/01/crm-40-bulk-delete-wizard.html' title='CRM 4.0 Bulk Delete Wizard'/><author><name>Adi Katz</name><uri>http://www.blogger.com/profile/16783428437179825705</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_M-mdv3Tfarg/SX1jNjiEjoI/AAAAAAAAAJk/upj7QEq5C9Y/s72-c/BDW.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3003801964503297388.post-5796155154689863320</id><published>2009-01-21T04:57:00.005+02:00</published><updated>2009-01-21T05:07:42.708+02:00</updated><title type='text'>CRM 4.0 Creating Interactive Plug-ins</title><content type='html'>&lt;div&gt;&lt;/div&gt;&lt;br /&gt;&lt;a href="http://2.bp.blogspot.com/_M-mdv3Tfarg/SXaQd1LekTI/AAAAAAAAAJc/h64SqiN-Dug/s1600-h/DupDit.jpg"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 293px;border:1px solid black;" src="http://2.bp.blogspot.com/_M-mdv3Tfarg/SXaQd1LekTI/AAAAAAAAAJc/h64SqiN-Dug/s400/DupDit.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5293577254186422578" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;The Plug-ins model is a truly robust mechanism which makes it very easy to enforce business rules and control the way your organization data flows. &lt;br /&gt;However, this model does have a few limitations. One in specific which I find very annoying is the fact you can’t return interactive html to the user and must be satisfied with static textual information. I truly believe ms should consider changing this behavior in their next version.&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;In order to overcome this limitation I made a small yet unsupported modification to the error dialog page that pops up when you throw an &lt;br /&gt;Invalid plug-in exception.  The dialog resides in inside /CRMWeb/_common/error/dlg_error.aspx&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;In order to make the dialog support HTML messages I added the following function that transforms the ErrorMessage Text to HTML and also added an onload event in the html body tag.&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;&lt;pre class='js' name='code'&gt;&lt;br /&gt;function unsupported()&lt;br /&gt;{&lt;br /&gt; var ErrorMessage = document.getElementById("ErrorMessage");&lt;br /&gt;  ErrorMessage.innerHTML = ErrorMessage.innerText; &lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre class='html' name='code'&gt;&lt;br /&gt;&amp;lt;body onload="unsupported()"&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;To test this “theory” I created a simple duplicate detection check on the contact entity first and last name.  &lt;br /&gt;If a duplicate is found, I construct an HTML error message with a link to the duplicate record and &lt;br /&gt;pass it as an InvalidPluginExecutionException parameter.&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;To test this out simply register the plug-in on the contact pre create / update event.&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;&lt;pre class='csharp' name='code'&gt;&lt;br /&gt;&lt;br /&gt;using System;&lt;br /&gt;using System.Collections.Generic;&lt;br /&gt;using System.Text;&lt;br /&gt;using Microsoft.Crm.Sdk;&lt;br /&gt;using Microsoft.Crm.SdkTypeProxy;&lt;br /&gt;using Microsoft.Crm.Sdk.Query;&lt;br /&gt;&lt;br /&gt;namespace TestPlugin&lt;br /&gt;{&lt;br /&gt; public class DuplicateHandler : IPlugin&lt;br /&gt; {&lt;br /&gt;  #region IPlugin Members&lt;br /&gt;&lt;br /&gt;  public void Execute(IPluginExecutionContext context)&lt;br /&gt;  {&lt;br /&gt;   if (context.InputParameters.Properties.Contains(ParameterName.Target))&lt;br /&gt;   {&lt;br /&gt;    #region Check Duplicate Contact [FirstName LastName] &lt;br /&gt;    &lt;br /&gt;    DynamicEntity contact = context.InputParameters.Properties[ParameterName.Target] as DynamicEntity;&lt;br /&gt;    String firstName = String.Empty;&lt;br /&gt;    String lastName = String.Empty;&lt;br /&gt;&lt;br /&gt;    if (contact.Properties.Contains("firstname"))&lt;br /&gt;    {&lt;br /&gt;     firstName = contact.Properties["firstname"].ToString();&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    if (contact.Properties.Contains("lastname"))&lt;br /&gt;    {&lt;br /&gt;     lastName = contact.Properties["lastname"].ToString();&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;    QueryExpression query = new QueryExpression(context.PrimaryEntityName);&lt;br /&gt;    query.ColumnSet.AddColumn(context.PrimaryEntityName + "id");&lt;br /&gt;&lt;br /&gt;    ConditionExpression firstCondition = new ConditionExpression();&lt;br /&gt;    firstCondition.AttributeName = "firstname";&lt;br /&gt;    firstCondition.Operator = ConditionOperator.Equal;&lt;br /&gt;    firstCondition.Values = new object[] { firstName };&lt;br /&gt;&lt;br /&gt;    ConditionExpression lastCondition = new ConditionExpression();&lt;br /&gt;    lastCondition.AttributeName = "lastname";&lt;br /&gt;    lastCondition.Operator = ConditionOperator.Equal;&lt;br /&gt;    lastCondition.Values = new object[] { lastName };&lt;br /&gt;&lt;br /&gt;    FilterExpression whereFilter = new FilterExpression();&lt;br /&gt;    whereFilter.Conditions.Add(firstCondition);&lt;br /&gt;    whereFilter.Conditions.Add(lastCondition);&lt;br /&gt;    whereFilter.FilterOperator = LogicalOperator.And;&lt;br /&gt;&lt;br /&gt;    query.Criteria = whereFilter;&lt;br /&gt;&lt;br /&gt;    ICrmService Service = context.CreateCrmService(true);&lt;br /&gt;    BusinessEntityCollection resultCollection =&lt;br /&gt;     (BusinessEntityCollection)Service.RetrieveMultiple(query);&lt;br /&gt;&lt;br /&gt;    #endregion &lt;br /&gt;    &lt;br /&gt;    #region Retrieve Duplicate HTML &lt;br /&gt;    &lt;br /&gt;    if (resultCollection.BusinessEntities.Count &gt; 0)&lt;br /&gt;    {&lt;br /&gt;     contact dupContact = resultCollection.BusinessEntities[0] as contact;&lt;br /&gt;     &lt;br /&gt;     String linkStyle = "text-decoration:underline;color:blue;";&lt;br /&gt;     &lt;br /&gt;     String contactUrl = String.Format(&lt;br /&gt;      "&lt;a style='{0}' href='/{1}/sfa/conts/edit.aspx?id={2}'&gt;{3} {4}&lt;/a&gt;" , &lt;br /&gt;      linkStyle,&lt;br /&gt;      context.OrganizationName,&lt;br /&gt;      dupContact.contactid.Value.ToString(), &lt;br /&gt;      firstName, &lt;br /&gt;      lastName&lt;br /&gt;     );&lt;br /&gt;     &lt;br /&gt;     String errorMessage = String.Format("&lt;b&gt;Duplicate Contact:&lt;/b&gt; {0}",contactUrl);&lt;br /&gt;     &lt;br /&gt;     throw new InvalidPluginExecutionException(errorMessage);&lt;br /&gt;    &lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    #endregion&lt;br /&gt;   }&lt;br /&gt;&lt;br /&gt;  #endregion&lt;br /&gt;  }&lt;br /&gt; }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3003801964503297388-5796155154689863320?l=mscrm4ever.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mscrm4ever.blogspot.com/feeds/5796155154689863320/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3003801964503297388&amp;postID=5796155154689863320&amp;isPopup=true' title='14 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3003801964503297388/posts/default/5796155154689863320'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3003801964503297388/posts/default/5796155154689863320'/><link rel='alternate' type='text/html' href='http://mscrm4ever.blogspot.com/2009/01/crm-40-creating-interactive-plug-ins.html' title='CRM 4.0 Creating Interactive Plug-ins'/><author><name>Adi Katz</name><uri>http://www.blogger.com/profile/16783428437179825705</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_M-mdv3Tfarg/SXaQd1LekTI/AAAAAAAAAJc/h64SqiN-Dug/s72-c/DupDit.jpg' height='72' width='72'/><thr:total>14</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3003801964503297388.post-4167256386244456259</id><published>2009-01-19T00:23:00.014+02:00</published><updated>2010-05-05T07:54:52.355+03:00</updated><title type='text'>CRM 4.0 State Level Security Wizard</title><content type='html'>&lt;div&gt;&lt;/div&gt;&lt;br /&gt;Since the release of the FLS wizard I received a few enquiries about the FLS ability to control the change of an entity state and status codes. &lt;br /&gt;Although the FLS does not control this type of behavior we have built a complete state manager wizard that allows us to control the entire application state.&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;The motivation which has driven me to develop this type of functionality is the need to control application workflows that span different user roles.&lt;br /&gt;Consider a scenario where you have a project entity with logical statuses (e.g.  Draft, Prepared, Ready to launch, Approved etc) and which represent the status that your project is in. You need to enable or disable a specific role from changing the project status depending on his function within that project. &lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;Currently there is no way of doing that without specific plug-in development. &lt;br /&gt;The wizard aims to solve two major problems: &lt;br /&gt;1. Allowing you to control any type of entity status without the need to development.&lt;br /&gt;2. Allowing the organization to shift from one paradigm to another by changing roles responsibility without the need for further develop or change your workflows.&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;The nice thing about this wizard is that its foundation is completely generic and allows an organization to completely control the change of state for any given entity. If you’re looking for a supported way to control who deactivates or activates an entity, want to manage whether a user can win or lose an opportunity, need to decide if a service rep can cancel or resolve a case or any other “SetState” behavior, this wizard will do the job.&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;Think about the possibilities that this type of functionality uncover. The SMW wizard has certainly solved many of our business problems and is an inseparable part our overall solution. &lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;Similar to the FLS wizard, you can create a complete organization security hierarchy. This means you can set a setstate behavior at the business unit level , override those settings at the role level and create exceptions for individual users.&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;u&gt;The following presentation displays 4 simple scenarios.&lt;/u&gt;  &lt;br /&gt;&lt;u&gt;&lt;strong&gt;First Scenario:&lt;/strong&gt;&lt;/u&gt; Control who activates and deactivates the account entity.&lt;br /&gt;&lt;br /&gt;&lt;u&gt;&lt;strong&gt;Second Scenario:&lt;/strong&gt;&lt;/u&gt; Controls whether a user can cancel a case or set it state to waiting for details.&lt;br /&gt;&lt;br /&gt;&lt;u&gt;&lt;strong&gt;Third Scenario:&lt;/strong&gt;&lt;/u&gt; Controls whether a user can close an opportunity as won.&lt;br /&gt;&lt;br /&gt;&lt;u&gt;&lt;strong&gt;Forth Scenario:&lt;/strong&gt;&lt;/u&gt; Shows how to control custom status reasons added to a test entity.&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;I’m offering this wizard under the same sharing paradigm which I introduced a few weeks ago.&lt;br /&gt;The State manager wizard and the entire set of wizards will be available in the next few days.&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;If you have questions or enquiries you may send them to &lt;a href='mailto:support@gicrm.com'&gt;support@gicrm.com&lt;/a&gt;.&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;If you need to rewind the presentation simply right click the flash movie and click rewind, then click play.&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;The proposed property (source code) price is 1000$ USD.&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;&lt;iframe src="http://www.upsite.co.il/uploaded/files/662_e1cb9b32040d512a41660537763afaa2.swf" width="650" height="511" &gt;&lt;/iframe&gt;&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;Here is a short description of how the SMW wizard affects CRM OOB entities and the type of control you can gain by using it: &lt;br /&gt;&lt;br /&gt;Controlling activation and deactivation of any customizable and custom entities&lt;br /&gt;Controlling publication of kb articles form draft to unapproved to published&lt;br /&gt;Controlling case resolution&lt;br /&gt;Controlling contract activation and cancelation&lt;br /&gt;Controlling invoice cancelation and fulfillment&lt;br /&gt;Controlling lead qualification&lt;br /&gt;Controlling opportunity qualification (win, lose)&lt;br /&gt;Controlling order fulfillment and cancelation&lt;br /&gt;Controlling quote qualification (win, lose)&lt;br /&gt;Controlling custom status reasons&lt;br /&gt;&lt;br /&gt;Global Features:&lt;br /&gt;Support for all languages&lt;br /&gt;Supports Multi-tenancy&lt;br /&gt;Supports IFD&lt;br /&gt;Supports IE8&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3003801964503297388-4167256386244456259?l=mscrm4ever.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mscrm4ever.blogspot.com/feeds/4167256386244456259/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3003801964503297388&amp;postID=4167256386244456259&amp;isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3003801964503297388/posts/default/4167256386244456259'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3003801964503297388/posts/default/4167256386244456259'/><link rel='alternate' type='text/html' href='http://mscrm4ever.blogspot.com/2009/01/crm-40-state-and-status-code-manager.html' title='CRM 4.0 State Level Security Wizard'/><author><name>Adi Katz</name><uri>http://www.blogger.com/profile/16783428437179825705</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3003801964503297388.post-3953238290365982243</id><published>2008-12-28T14:18:00.014+02:00</published><updated>2010-07-20T10:33:06.260+03:00</updated><title type='text'>CRM 4.0 Supported Record / Page Counter for CRM views</title><content type='html'>&lt;div&gt;&lt;/div&gt;&lt;br /&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 121px; border:1px solid black" src="http://2.bp.blogspot.com/_M-mdv3Tfarg/SWlKKH1W1tI/AAAAAAAAAJU/3WLHO328LRI/s400/RecordCounter.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5289840775085348562" /&gt;&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;Are you a Microsoft dynamics partner? Find out more about how to obtain a copy of this Wizard through our &lt;a href="http://mscrm4ever.blogspot.com/2009/07/partner-opportunity-gi-reseller-program.html"&gt;reseller program&lt;/a&gt;.&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;This is certainly a feature that dynamics is missing. I had doubts about this solution before I developed it since I wanted to create a supported solution that would easily integrate into our product and upgrade seamlessly with dynamics. I was aware of dynamics limitations regarding this issue. Never the less, I found a cool way to integrate a record count and page count in any CRM grid in a totally supported way.&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;The Plug-in that manages the Counters is highly configurable. You can decide whether an entity displays the record counter and if it does you can also specify whether to display a total page count (the records counter is the default display).  You can also specify the labels for each language to support multiple languages. &lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;The Counter does not show if the record count is smaller then amount of records per page. This means that if the current page shows 25 records per page and the record count is 10 then you won’t see the counter since CRM already displays all the information you need. &lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;The images below Display how the plug-in works. I’m putting it for here as a shared Plug-in, I figure this is a feature that many CRM implementations require. You may try to develop it your self and spend the time and money doing so, or purchase it as your own company property under our T&amp;C and enjoy it immediately. &lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;It takes about 20 seconds to implement the solution. &lt;br /&gt;The configuration is made directly on top of the plug in registration tool.&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;If you have any further questions please send them to &lt;a href='mailto:support@gicrm.com'&gt;support@gicrm.com&lt;/a&gt; and specify the plug-in you’re interested in.&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;To play back the flash movie simply right click on the movie (*.SWF file) click rewind, and then click Play. &lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;&lt;strong&gt;The Plug-in Supports the following views&lt;/strong&gt;&lt;br /&gt;* Advanced Find&lt;br /&gt;&lt;a href="http://4.bp.blogspot.com/_M-mdv3Tfarg/SgQSZpfTr3I/AAAAAAAAAL0/SkCwPNySO60/s1600-h/Advancedfind.jpg"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 130px;" src="http://4.bp.blogspot.com/_M-mdv3Tfarg/SgQSZpfTr3I/AAAAAAAAAL0/SkCwPNySO60/s400/Advancedfind.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5333408090556706674" /&gt;&lt;/a&gt;&lt;br /&gt;* Single Lookup View&lt;br /&gt;&lt;a href="http://2.bp.blogspot.com/_M-mdv3Tfarg/SgQSj2JY-BI/AAAAAAAAAL8/TMNxt2xQ3M0/s1600-h/singlelookup.jpg"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 333px; height: 400px;" src="http://2.bp.blogspot.com/_M-mdv3Tfarg/SgQSj2JY-BI/AAAAAAAAAL8/TMNxt2xQ3M0/s400/singlelookup.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5333408265753131026" /&gt;&lt;/a&gt;&lt;br /&gt;* Multi Lookup View&lt;br /&gt;&lt;a href="http://3.bp.blogspot.com/_M-mdv3Tfarg/SgQStLpApKI/AAAAAAAAAME/HvJcjnnLHv4/s1600-h/multiplelookup.jpg"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 341px;" src="http://3.bp.blogspot.com/_M-mdv3Tfarg/SgQStLpApKI/AAAAAAAAAME/HvJcjnnLHv4/s400/multiplelookup.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5333408426141721762" /&gt;&lt;/a&gt;&lt;br /&gt;* Public and Private Views&lt;br /&gt;&lt;a href="http://1.bp.blogspot.com/_M-mdv3Tfarg/SgQS3J3KZ0I/AAAAAAAAAMM/3Q0ECiptmGI/s1600-h/publicview.jpg"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 107px;" src="http://1.bp.blogspot.com/_M-mdv3Tfarg/SgQS3J3KZ0I/AAAAAAAAAMM/3Q0ECiptmGI/s400/publicview.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5333408597462902594" /&gt;&lt;/a&gt;&lt;br /&gt;* Quick Find&lt;br /&gt;&lt;a href="http://2.bp.blogspot.com/_M-mdv3Tfarg/SgQS_vz5QRI/AAAAAAAAAMU/ocz_RGRqvtc/s1600-h/quickfind.jpg"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 109px;" src="http://2.bp.blogspot.com/_M-mdv3Tfarg/SgQS_vz5QRI/AAAAAAAAAMU/ocz_RGRqvtc/s400/quickfind.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5333408745088696594" /&gt;&lt;/a&gt;&lt;br /&gt;* Custom FetchXml and Advanced Find&lt;br /&gt;&lt;a href="http://2.bp.blogspot.com/_M-mdv3Tfarg/SgQTHRMKaCI/AAAAAAAAAMc/mAlJ2G4hKm0/s1600-h/Advancedfind.jpg"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 130px;" src="http://2.bp.blogspot.com/_M-mdv3Tfarg/SgQTHRMKaCI/AAAAAAAAAMc/mAlJ2G4hKm0/s400/Advancedfind.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5333408874307938338" /&gt;&lt;/a&gt;&lt;br /&gt;* Form Assistant&lt;br /&gt;&lt;a href="http://1.bp.blogspot.com/_M-mdv3Tfarg/SgQTS8ojcjI/AAAAAAAAAMk/oanN7xmzU0U/s1600-h/formassistant.jpg"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 192px; height: 400px;" src="http://1.bp.blogspot.com/_M-mdv3Tfarg/SgQTS8ojcjI/AAAAAAAAAMk/oanN7xmzU0U/s400/formassistant.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5333409074948305458" /&gt;&lt;/a&gt;&lt;br /&gt;* Auto resolve Lookup View&lt;br /&gt;&lt;a href="http://1.bp.blogspot.com/_M-mdv3Tfarg/SgQTc43jIjI/AAAAAAAAAMs/v_CwxLOZU3g/s1600-h/autoresolvelookup.jpg"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 295px; height: 304px;" src="http://1.bp.blogspot.com/_M-mdv3Tfarg/SgQTc43jIjI/AAAAAAAAAMs/v_CwxLOZU3g/s400/autoresolvelookup.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5333409245736149554" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;The Record counter plug-in in now available on &lt;a href="http://www.gicrm.com/?categoryId=29264&amp;itemId=63741"&gt;GI's company website&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3003801964503297388-3953238290365982243?l=mscrm4ever.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mscrm4ever.blogspot.com/feeds/3953238290365982243/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3003801964503297388&amp;postID=3953238290365982243&amp;isPopup=true' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3003801964503297388/posts/default/3953238290365982243'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3003801964503297388/posts/default/3953238290365982243'/><link rel='alternate' type='text/html' href='http://mscrm4ever.blogspot.com/2008/12/crm-40-supported-record-page-counter.html' title='CRM 4.0 Supported Record / Page Counter for CRM views'/><author><name>Adi Katz</name><uri>http://www.blogger.com/profile/16783428437179825705</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_M-mdv3Tfarg/SWlKKH1W1tI/AAAAAAAAAJU/3WLHO328LRI/s72-c/RecordCounter.jpg' height='72' width='72'/><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3003801964503297388.post-5510039698103530588</id><published>2008-12-19T09:40:00.021+02:00</published><updated>2010-05-05T07:55:11.069+03:00</updated><title type='text'>CRM 4.0 Tooltip Wizard</title><content type='html'>&lt;div&gt;&lt;/div&gt;&lt;br /&gt;When I first weighted the reasons for building an application wide tooltip solution I had a different idea in mind. Since many of our product entities are comprised of complex forms I needed a system that can deliver both informative text about the current field and much more importantly a next step trigger mechanism which will interact with the user and guide her through the next step or functions. &lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;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 &lt;a href='mailto:support@gicrm.com'&gt;support@gicrm.com&lt;/a&gt;).&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;In order to achieve the desired result we built the tooltip on top of ms createPopup window. That enabled us to add interactive next step scripts to each tooltip which eventually sets the focus to the next field or triggers a desired functionality.&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;The following images describe best the tooltips you can build using the tooltip wizard. You can create context menus and buttons, add custom logic or just be satisfied with simple informative html.&lt;br /&gt;&lt;br /&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 268px; height: 182px;border:1px solid black" src="http://4.bp.blogspot.com/_M-mdv3Tfarg/SU9-LvEoQoI/AAAAAAAAAJM/FkRE0ss3uMI/s400/NextField.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5282579628008424066" /&gt;&lt;br /&gt;&lt;br /&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 164px;border:1px solid black" src="http://4.bp.blogspot.com/_M-mdv3Tfarg/SU9-Le6FTHI/AAAAAAAAAJE/4tREGfuHOns/s400/FormatPhone.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5282579623669222514" /&gt;&lt;br /&gt;&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;Another reason I considered while contemplating on building such a wizard was that I could not know in advance where I would need to put interactive tooltips. In order to make this decision easy or eliminate the need to even think about it I came to a conclusion that such a wizard is the best way to go.&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;There are many more good enough reasons to implementing this type of functionality. Here is a partial list or incentives which I consider important and eventually made my decision easy:&lt;br /&gt;&lt;br /&gt;&lt;u&gt;Tooltips&lt;/u&gt; are considered to be preliminary validation steps. They teach the user how to interact with the system in the most efficient way.&lt;br /&gt;&lt;br /&gt;&lt;u&gt;Tooltips&lt;/u&gt; are pro-active mechanisms as opposed to validation alerts which are considered re-active since they only pop after the user makes her move.&lt;br /&gt;&lt;br /&gt;&lt;u&gt;Tooltips&lt;/u&gt; play a very important role in application acceptance. You don’t have to be Freud to understand that users like to be self sufficient. A robust  &lt;br /&gt;              tooltip mechanism helps the user solve most of her daily application stuff on her own.&lt;br /&gt;&lt;br /&gt;&lt;u&gt;Tooltips&lt;/u&gt; support organization shifts in regulations and accepted practice. Consider situations where the user’s daily routine of filling new account  &lt;br /&gt;              information changed due to a new regulation. What would be best if not revising the tooltip that guides the user with the new practice?&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;I’m sure there are more incentives then mentioned above. The bottom line is that such a system is more then just a nice to have feature.&lt;br /&gt;If you think the way I do then you’ll find the tooltip wizard a true companion.&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Specifications and technical stuff:&lt;/strong&gt;&lt;br /&gt;The tooltip wizard enables you to fully control the creation and editing of tooltips through out the Dynamics application.&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;The wizard supports dynamics multi-lingual environment and is available in all base languages. It’s also supports Dir LTR (Left to Right) and RTL (Right to Left).&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;To rewind the flash presentation simply right click on the flash movie and click rewind.&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;&lt;iframe src='http://www.upsite.co.il/uploaded/files/662_a4256a4686e4cfecbaf749f7f490a20b.swf' width='640' height='504'&gt;&lt;/iframe&gt;&lt;br /&gt;&lt;br /&gt;&lt;u&gt;&lt;strong&gt;The Tooltip Wizard Features&lt;/strong&gt;&lt;/u&gt;&lt;br /&gt;&lt;ol&gt;&lt;br /&gt;&lt;li&gt;Lets you manage and control application wide tooltips. &lt;/li&gt;&lt;br /&gt;&lt;li&gt;Allows you to cut design and development time.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Allows you to enrich application look and feel by designing highly elaborated and richly colored tooltips. &lt;/li&gt;&lt;/ol&gt;&lt;br /&gt;&lt;strong&gt;&lt;u&gt;Functionality&lt;/u&gt;&lt;/strong&gt;&lt;u&gt; &lt;/u&gt;&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;&lt;strong&gt;&lt;u&gt;Editing&lt;/u&gt; &lt;/strong&gt;&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;a href="http://4.bp.blogspot.com/_M-mdv3Tfarg/SUtjL9yHruI/AAAAAAAAAIc/AMlDyDPU8JQ/s1600-h/editor.jpg"&gt;&lt;img id="BLOGGER_PHOTO_ID_5281424045236530914" style="BORDER-RIGHT: black 1px solid; BORDER-TOP: black 1px solid; DISPLAY: block; MARGIN: 0px auto 10px; BORDER-LEFT: black 1px solid; WIDTH: 400px; CURSOR: hand; BORDER-BOTTOM: black 1px solid; HEIGHT: 170px; TEXT-ALIGN: center" alt="" src="http://4.bp.blogspot.com/_M-mdv3Tfarg/SUtjL9yHruI/AAAAAAAAAIc/AMlDyDPU8JQ/s400/editor.jpg" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;li&gt;The Tooltip Wizard contains a fully-featured easy to use HTML editor which lets you choose the font size face and color.&lt;br /&gt;&lt;/li&gt;&lt;br /&gt;&lt;a href="http://2.bp.blogspot.com/_M-mdv3Tfarg/SUtjL5DFpVI/AAAAAAAAAIk/btjOD-kv1yA/s1600-h/hyperlink.jpg"&gt;&lt;img id="BLOGGER_PHOTO_ID_5281424043965523282" style="BORDER-RIGHT: black 1px solid; BORDER-TOP: black 1px solid; DISPLAY: block; MARGIN: 0px auto 10px; BORDER-LEFT: black 1px solid; WIDTH: 397px; CURSOR: hand; BORDER-BOTTOM: black 1px solid; HEIGHT: 105px; TEXT-ALIGN: center" alt="" src="http://2.bp.blogspot.com/_M-mdv3Tfarg/SUtjL5DFpVI/AAAAAAAAAIk/btjOD-kv1yA/s400/hyperlink.jpg" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;li&gt;A link option lets you create various hyperlinks. You can switch between HTML source and Text modes at any time with a click of the mouse, and any changes you have made will instantly be reflected in both modes.&lt;br /&gt;&lt;/li&gt;&lt;br /&gt;&lt;a href="http://1.bp.blogspot.com/_M-mdv3Tfarg/SUtjLYZWs2I/AAAAAAAAAIM/ZRDnspIc7KU/s1600-h/Templates.jpg"&gt;&lt;img id="BLOGGER_PHOTO_ID_5281424035200545634" style="BORDER-RIGHT: black 1px solid; BORDER-TOP: black 1px solid; DISPLAY: block; MARGIN: 0px auto 10px; BORDER-LEFT: black 1px solid; WIDTH: 400px; CURSOR: hand; BORDER-BOTTOM: black 1px solid; HEIGHT: 304px; TEXT-ALIGN: center" alt="" src="http://1.bp.blogspot.com/_M-mdv3Tfarg/SUtjLYZWs2I/AAAAAAAAAIM/ZRDnspIc7KU/s400/Templates.jpg" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;li&gt;You can adjust the ToolTip's background to match the appearance of your application. Preview mode lets you instantly see what your edited tooltip will look like.&lt;br /&gt;&lt;/li&gt;&lt;br /&gt;&lt;a href="http://2.bp.blogspot.com/_M-mdv3Tfarg/SUtjLita35I/AAAAAAAAAIU/U8m890XkFt8/s1600-h/Compare.jpg"&gt;&lt;img id="BLOGGER_PHOTO_ID_5281424037969059730" style="BORDER-RIGHT: black 1px solid; BORDER-TOP: black 1px solid; DISPLAY: block; MARGIN: 0px auto 10px; BORDER-LEFT: black 1px solid; WIDTH: 400px; CURSOR: hand; BORDER-BOTTOM: black 1px solid; HEIGHT: 130px; TEXT-ALIGN: center" alt="" src="http://2.bp.blogspot.com/_M-mdv3Tfarg/SUtjLita35I/AAAAAAAAAIU/U8m890XkFt8/s400/Compare.jpg" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;li&gt;Compare option make translation convenient by letting you compare to your sibling tooltips in other system languages. &lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;strong&gt;&lt;u&gt;Displaying&lt;/u&gt;&lt;/strong&gt;&lt;strong&gt;&lt;/strong&gt;&lt;ul&gt;&lt;br /&gt;&lt;a href="http://1.bp.blogspot.com/_M-mdv3Tfarg/SUtjWCcniII/AAAAAAAAAI0/pghoWcA8PCk/s1600-h/Position.jpg"&gt;&lt;img id="BLOGGER_PHOTO_ID_5281424218287212674" style="BORDER-RIGHT: black 1px solid; BORDER-TOP: black 1px solid; DISPLAY: block; MARGIN: 0px auto 10px; BORDER-LEFT: black 1px solid; WIDTH: 273px; CURSOR: hand; BORDER-BOTTOM: black 1px solid; HEIGHT: 79px; TEXT-ALIGN: center" alt="" src="http://1.bp.blogspot.com/_M-mdv3Tfarg/SUtjWCcniII/AAAAAAAAAI0/pghoWcA8PCk/s400/Position.jpg" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;li&gt;The wizard lets you decide whether the tooltip will be displayed at the top, bottom, left or right of the control or the label.&lt;br /&gt;&lt;/li&gt;&lt;br /&gt;&lt;a href="http://1.bp.blogspot.com/_M-mdv3Tfarg/SUtjOLrLrzI/AAAAAAAAAIs/vEng4Il0PT4/s1600-h/Layout.jpg"&gt;&lt;img id="BLOGGER_PHOTO_ID_5281424083325267762" style="BORDER-RIGHT: black 1px solid; BORDER-TOP: black 1px solid; DISPLAY: block; MARGIN: 0px auto 10px; BORDER-LEFT: black 1px solid; WIDTH: 264px; CURSOR: hand; BORDER-BOTTOM: black 1px solid; HEIGHT: 79px; TEXT-ALIGN: center" alt="" src="http://1.bp.blogspot.com/_M-mdv3Tfarg/SUtjOLrLrzI/AAAAAAAAAIs/vEng4Il0PT4/s400/Layout.jpg" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;li&gt;Dynamic layout mode - adjust the tooltip size to the control or the label which it's attached to.&lt;br /&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Fixed layout mode - fine-tune (while previewing) of the tooltips size. Fadeout Interval - adjust when fading will start after hovering out of the tooltip control or label. &lt;/li&gt;&lt;u&gt;&lt;strong&gt;&lt;/ul&gt;&lt;/strong&gt;&lt;/u&gt;&lt;br /&gt;&lt;a href="http://3.bp.blogspot.com/_M-mdv3Tfarg/SUtjWS3Q9lI/AAAAAAAAAI8/A6QafHNgZsA/s1600-h/PublishClone.jpg"&gt;&lt;img id="BLOGGER_PHOTO_ID_5281424222693946962" style="BORDER-RIGHT: black 1px solid; BORDER-TOP: black 1px solid; DISPLAY: block; MARGIN: 0px auto 10px; BORDER-LEFT: black 1px solid; WIDTH: 400px; CURSOR: hand; BORDER-BOTTOM: black 1px solid; HEIGHT: 176px; TEXT-ALIGN: center" alt="" src="http://3.bp.blogspot.com/_M-mdv3Tfarg/SUtjWS3Q9lI/AAAAAAAAAI8/A6QafHNgZsA/s400/PublishClone.jpg" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;u&gt;&lt;strong&gt;&lt;p&gt;Publishing&lt;/strong&gt;&lt;/u&gt;&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;Publish and Un-publish are done at the entity level. While a single field tooltip will be viewed according to its Active \ De-active state. &lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;The wizard is &lt;a href="http://www.gicrm.com/?categoryId=29264&amp;itemId=63739"&gt;now available online&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Please send your comments / enquiries to &lt;a href='mailto:ttw@gicrm.com'&gt;ttw@gicrm.com&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3003801964503297388-5510039698103530588?l=mscrm4ever.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mscrm4ever.blogspot.com/feeds/5510039698103530588/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3003801964503297388&amp;postID=5510039698103530588&amp;isPopup=true' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3003801964503297388/posts/default/5510039698103530588'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3003801964503297388/posts/default/5510039698103530588'/><link rel='alternate' type='text/html' href='http://mscrm4ever.blogspot.com/2008/12/crm-40-tooltip-wizard.html' title='CRM 4.0 Tooltip Wizard'/><author><name>Adi Katz</name><uri>http://www.blogger.com/profile/16783428437179825705</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_M-mdv3Tfarg/SU9-LvEoQoI/AAAAAAAAAJM/FkRE0ss3uMI/s72-c/NextField.jpg' height='72' width='72'/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3003801964503297388.post-4680526242619020121</id><published>2008-12-03T21:14:00.004+02:00</published><updated>2008-12-03T23:54:27.849+02:00</updated><title type='text'>Expediting Plug-In Development Using VS Pre/Post Build Events</title><content type='html'>&lt;div&gt;&lt;/div&gt;&lt;br /&gt;No doubt, CRM 4.0s new plug-in engine is a giant leap compared to the callout facility that was available on the 3.0 version.&lt;br /&gt;However, a few things still hinder the development process. One of which is the fact that you need to reset the Internet information services (IIS) and copy your project files to the assembly bin folder each time you compile your code. &lt;br /&gt;&lt;br /&gt;In order to overcome and automate the process you can add Pre and/or Post Build events to you plug-in project.&lt;br /&gt;Build event are no more then command line instructions you need to run before and/or after you project builds. &lt;br /&gt;&lt;br /&gt;The nice thing about Post events is that you can set them to run only if the build is successful. Which means the iisreset won’t run and the files won’t be copied unless the build is complete. Following are the Post build command instruction I add to every plug-in project we build.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://3.bp.blogspot.com/_M-mdv3Tfarg/STbcC-hbqyI/AAAAAAAAAIE/2zNZkZ8nytg/s1600-h/BUILDEVENTS.jpg"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 261px;border:1px solid black" src="http://3.bp.blogspot.com/_M-mdv3Tfarg/STbcC-hbqyI/AAAAAAAAAIE/2zNZkZ8nytg/s400/BUILDEVENTS.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5275645957212187426" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;1.Open the Build Events Property Page: The Build events are located on the Project Properties page under the build events Tab.&lt;br /&gt;2.Add the following commands to the Post Build Event (make sure you run the command only on successful build)&lt;br /&gt;Resets Internet information Services&lt;br /&gt;&lt;pre class="js" name="code"&gt;&lt;br /&gt;call iisreset&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Copy the *.dll file to the assembly bin folder. &lt;br /&gt;&lt;pre class="js" name="code"&gt;&lt;br /&gt;xcopy "$(TargetPath)"  "C:\[CRM INSTALL FOLDER]\Server\bin\assembly" /i /d /y &lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Copy the *.pdb file to the assembly bin folder&lt;br /&gt;&lt;pre class="js" name="code"&gt;&lt;br /&gt;xcopy "$(TargetDir)$(TargetName).pdb" "C:\[CRM INSTALL FOLDER]\Server\bin\assembly" /i /d /y &lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;That’s it.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3003801964503297388-4680526242619020121?l=mscrm4ever.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mscrm4ever.blogspot.com/feeds/4680526242619020121/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3003801964503297388&amp;postID=4680526242619020121&amp;isPopup=true' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3003801964503297388/posts/default/4680526242619020121'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3003801964503297388/posts/default/4680526242619020121'/><link rel='alternate' type='text/html' href='http://mscrm4ever.blogspot.com/2008/12/expediting-plug-in-development-using-vs.html' title='Expediting Plug-In Development Using VS Pre/Post Build Events'/><author><name>Adi Katz</name><uri>http://www.blogger.com/profile/16783428437179825705</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_M-mdv3Tfarg/STbcC-hbqyI/AAAAAAAAAIE/2zNZkZ8nytg/s72-c/BUILDEVENTS.jpg' height='72' width='72'/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3003801964503297388.post-1552131959732985033</id><published>2008-12-03T13:52:00.016+02:00</published><updated>2008-12-03T16:10:58.643+02:00</updated><title type='text'>CRM 4.0 Supported Multi Select (Picklist) Control</title><content type='html'>&lt;div&gt;&lt;/div&gt;&lt;br /&gt;&lt;a href="http://3.bp.blogspot.com/_M-mdv3Tfarg/STZ0QNlSzGI/AAAAAAAAAFs/0j39raEFTLI/s1600-h/STEP0.jpg"&gt;&lt;img id="BLOGGER_PHOTO_ID_5275531835383925858" style="BORDER-RIGHT: black 1px solid; BORDER-TOP: black 1px solid; DISPLAY: block; MARGIN: 0px auto 10px; BORDER-LEFT: black 1px solid; WIDTH: 400px; CURSOR: hand; BORDER-BOTTOM: black 1px solid; HEIGHT: 210px; TEXT-ALIGN: center" alt="" src="http://3.bp.blogspot.com/_M-mdv3Tfarg/STZ0QNlSzGI/AAAAAAAAAFs/0j39raEFTLI/s400/STEP0.jpg" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;As you all know Dynamics does not support multi select controls. Of course this is not entirely true since from a design perspective having the ability to create many to many associations and using ms grid is a control none the less. Having that said, Customers will find unbelievable reasons to why they need or prefer to use a multi select control and not the out of the box grid.&lt;br /&gt;&lt;br /&gt;I was wondering what it would take to create a fully supported multi select control (in other words a listbox)&lt;br /&gt;using dynamics OOB/supported capabilities.&lt;br /&gt;&lt;br /&gt;Before I start let me say that the idea of creating such a control does not justify the amount of code needed to make this solution run inside ms supported boundaries. But then again, once you have it, it takes approximately 20 minutes (per listbox) to implement, which is fairly reasonable.&lt;br /&gt;&lt;br /&gt;In order to prove my point and satisfy my wonderings I devised a simple scenario of a credit card listbox on the contact entity.&lt;br /&gt;The idea was to be able to relate multiple credit card types to a single contact using a regular listbox.&lt;br /&gt;&lt;br /&gt;The first thing I did is create the necessary customizations needed to support the solution.&lt;br /&gt;&lt;strong&gt;STEP 1 - Customizations:&lt;/strong&gt;&lt;br /&gt;1. A new entity called credit card.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://1.bp.blogspot.com/_M-mdv3Tfarg/STZ0Qrm-3dI/AAAAAAAAAF0/thHrUgIT1f4/s1600-h/STEP1.1.jpg"&gt;&lt;img id="BLOGGER_PHOTO_ID_5275531843444071890" style="BORDER-RIGHT: black 1px solid; BORDER-TOP: black 1px solid; DISPLAY: block; MARGIN: 0px auto 10px; BORDER-LEFT: black 1px solid; WIDTH: 400px; CURSOR: hand; BORDER-BOTTOM: black 1px solid; HEIGHT: 311px; TEXT-ALIGN: center" alt="" src="http://1.bp.blogspot.com/_M-mdv3Tfarg/STZ0Qrm-3dI/AAAAAAAAAF0/thHrUgIT1f4/s400/STEP1.1.jpg" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;a href="http://4.bp.blogspot.com/_M-mdv3Tfarg/STZ0Qk0JTPI/AAAAAAAAAF8/DM-8-BvS4IQ/s1600-h/STEP1.2.jpg"&gt;&lt;img id="BLOGGER_PHOTO_ID_5275531841620233458" style="BORDER-RIGHT: black 1px solid; BORDER-TOP: black 1px solid; DISPLAY: block; MARGIN: 0px auto 10px; BORDER-LEFT: black 1px solid; WIDTH: 400px; CURSOR: hand; BORDER-BOTTOM: black 1px solid; HEIGHT: 176px; TEXT-ALIGN: center" alt="" src="http://4.bp.blogspot.com/_M-mdv3Tfarg/STZ0Qk0JTPI/AAAAAAAAAF8/DM-8-BvS4IQ/s400/STEP1.2.jpg" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;2. A new N:N relationship with the contact entity (e.g. gi_gi_creditcard_contact). When you create the relationship don’t change its name. The way ms represent its relationships (e.g. prefix_entity_entity) will help you to better understand this solution.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://4.bp.blogspot.com/_M-mdv3Tfarg/STZ0RA-7GwI/AAAAAAAAAGE/GeJUF65jCFY/s1600-h/STEP1.3.jpg"&gt;&lt;img id="BLOGGER_PHOTO_ID_5275531849181633282" style="BORDER-RIGHT: black 1px solid; BORDER-TOP: black 1px solid; DISPLAY: block; MARGIN: 0px auto 10px; BORDER-LEFT: black 1px solid; WIDTH: 400px; CURSOR: hand; BORDER-BOTTOM: black 1px solid; HEIGHT: 180px; TEXT-ALIGN: center" alt="" src="http://4.bp.blogspot.com/_M-mdv3Tfarg/STZ0RA-7GwI/AAAAAAAAAGE/GeJUF65jCFY/s400/STEP1.3.jpg" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;a href="http://1.bp.blogspot.com/_M-mdv3Tfarg/STZ0RfKHmiI/AAAAAAAAAGM/-otvQvAQqXA/s1600-h/STEP1.4.jpg"&gt;&lt;img id="BLOGGER_PHOTO_ID_5275531857281653282" style="BORDER-RIGHT: black 1px solid; BORDER-TOP: black 1px solid; DISPLAY: block; MARGIN: 0px auto 10px; BORDER-LEFT: black 1px solid; WIDTH: 400px; CURSOR: hand; BORDER-BOTTOM: black 1px solid; HEIGHT: 265px; TEXT-ALIGN: center" alt="" src="http://1.bp.blogspot.com/_M-mdv3Tfarg/STZ0RfKHmiI/AAAAAAAAAGM/-otvQvAQqXA/s400/STEP1.4.jpg" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;3. Added a few Credit card types to complete the list.&lt;br /&gt;&lt;a href="http://4.bp.blogspot.com/_M-mdv3Tfarg/STZ0ch21T7I/AAAAAAAAAGs/HtZp-KJxNSg/s1600-h/STEP1.9.jpg"&gt;&lt;img id="BLOGGER_PHOTO_ID_5275532046984630194" style="BORDER-RIGHT: black 1px solid; BORDER-TOP: black 1px solid; DISPLAY: block; MARGIN: 0px auto 10px; BORDER-LEFT: black 1px solid; WIDTH: 400px; CURSOR: hand; BORDER-BOTTOM: black 1px solid; HEIGHT: 191px; TEXT-ALIGN: center" alt="" src="http://4.bp.blogspot.com/_M-mdv3Tfarg/STZ0ch21T7I/AAAAAAAAAGs/HtZp-KJxNSg/s400/STEP1.9.jpg" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;4. On the contact entity I created a credit card IFRAME:&lt;br /&gt;&lt;br /&gt;&lt;a href="http://2.bp.blogspot.com/_M-mdv3Tfarg/STZ0cOzdfnI/AAAAAAAAAGU/b6V7DrXCJkI/s1600-h/STEP1.5.jpg"&gt;&lt;img id="BLOGGER_PHOTO_ID_5275532041870212722" style="BORDER-RIGHT: black 1px solid; BORDER-TOP: black 1px solid; DISPLAY: block; MARGIN: 0px auto 10px; BORDER-LEFT: black 1px solid; WIDTH: 327px; CURSOR: hand; BORDER-BOTTOM: black 1px solid; HEIGHT: 400px; TEXT-ALIGN: center" alt="" src="http://2.bp.blogspot.com/_M-mdv3Tfarg/STZ0cOzdfnI/AAAAAAAAAGU/b6V7DrXCJkI/s400/STEP1.5.jpg" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;a href="http://4.bp.blogspot.com/_M-mdv3Tfarg/STZ0cUCSvzI/AAAAAAAAAGk/9SmFUsPCbcs/s1600-h/STEP1.8.jpg"&gt;&lt;img id="BLOGGER_PHOTO_ID_5275532043274600242" style="BORDER-RIGHT: black 1px solid; BORDER-TOP: black 1px solid; DISPLAY: block; MARGIN: 0px auto 10px; BORDER-LEFT: black 1px solid; WIDTH: 400px; CURSOR: hand; BORDER-BOTTOM: black 1px solid; HEIGHT: 305px; TEXT-ALIGN: center" alt="" src="http://4.bp.blogspot.com/_M-mdv3Tfarg/STZ0cUCSvzI/AAAAAAAAAGk/9SmFUsPCbcs/s400/STEP1.8.jpg" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;5. Added a new contact attribute called creditcarditems; The reason for this attribute is that it is used as a transport that will hold the current selected options. &lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Note: &lt;/strong&gt;Notice that the attribute schema name (gi_creditcard) is the same as the credit card entity schema name. This is important since the multipicklist.aspx refers to its parent contact transport attribute by using the Entity2Name witch is gi_creditcard. You may also add a query string parameter to the contact onload event that opens the multipicklist.aspx and use that instead.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://3.bp.blogspot.com/_M-mdv3Tfarg/STZ0cb491zI/AAAAAAAAAGc/2I9MQootEsI/s1600-h/STEP1.6.jpg"&gt;&lt;img id="BLOGGER_PHOTO_ID_5275532045382965042" style="BORDER-RIGHT: black 1px solid; BORDER-TOP: black 1px solid; DISPLAY: block; MARGIN: 0px auto 10px; BORDER-LEFT: black 1px solid; WIDTH: 400px; CURSOR: hand; BORDER-BOTTOM: black 1px solid; HEIGHT: 186px; TEXT-ALIGN: center" alt="" src="http://3.bp.blogspot.com/_M-mdv3Tfarg/STZ0cb491zI/AAAAAAAAAGc/2I9MQootEsI/s400/STEP1.6.jpg" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;6. Added the following script to the contact entity. The script hides the transport attribute and points the iframe to an external multipicklist.aspx.&lt;br /&gt;The multipicklist.aspx receives the following querystring parameters:&lt;br /&gt;contact objected (crmForm.ObjectId)&lt;br /&gt;The name of the entity (crmForm.ObjectTypeName)&lt;br /&gt;The name of the related entity (gi_creditcard)&lt;br /&gt;The name of the storage (transport) attribute (gi_creditcard).&lt;br /&gt;Organization Name e.g. ORG_UNIQUE_NAME&lt;br /&gt;&lt;br /&gt;&lt;pre class="js" name="code"&gt;&lt;br /&gt;crmForm.gi_creditcard.style.display = "none";&lt;br /&gt;var url = prependOrgName("/isv/controls/multipicklist.aspx");&lt;br /&gt;url += "?toentity=gi_creditcard" ;&lt;br /&gt;url += "&amp;amp;toattribute=gi_card";&lt;br /&gt;url += "&amp;amp;fromentity=" + crmForm.ObjectTypeName;&lt;br /&gt;url += "&amp;amp;id=" + (crmForm.ObjectId?crmForm.ObjectId:"");&lt;br /&gt;url += "&amp;amp;orgname=" + ORG_UNIQUE_NAME;&lt;br /&gt;document.all["IFRAME_CreditCards"].src = url;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;STEP 2 – Build MultiPicklist.aspx Page (control)&lt;/strong&gt;&lt;br /&gt;1. Added a new asp.net web application and created a vdir under the isv folder.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://3.bp.blogspot.com/_M-mdv3Tfarg/STaOO0rndiI/AAAAAAAAAH8/-_Zac_EPDk0/s1600-h/STEP2.0.jpg"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 285px; height: 390px;border:1px solid black;" src="http://3.bp.blogspot.com/_M-mdv3Tfarg/STaOO0rndiI/AAAAAAAAAH8/-_Zac_EPDk0/s400/STEP2.0.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5275560398822012450" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;a href="http://4.bp.blogspot.com/_M-mdv3Tfarg/STZ0oD3b2UI/AAAAAAAAAG8/43S8-EsDwmI/s1600-h/STEP2.1.jpg"&gt;&lt;img id="BLOGGER_PHOTO_ID_5275532245092522306" style="BORDER-RIGHT: black 1px solid; BORDER-TOP: black 1px solid; DISPLAY: block; MARGIN: 0px auto 10px; BORDER-LEFT: black 1px solid; WIDTH: 379px; CURSOR: hand; BORDER-BOTTOM: black 1px solid; HEIGHT: 200px; TEXT-ALIGN: center" alt="" src="http://4.bp.blogspot.com/_M-mdv3Tfarg/STZ0oD3b2UI/AAAAAAAAAG8/43S8-EsDwmI/s400/STEP2.1.jpg" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;2. Changed the default.aspx page to multipicklist.aspx and added the following html. The html contains a runat server listbox ,a script that binds to the parent (contact) onsave event and fills the transport attribute with selected values and a few styling rules to make the listbox occupy the entire iframe space.&lt;br /&gt;&lt;br /&gt;&lt;pre class="html" name="code"&gt;&lt;br /&gt;&amp;lt;html xmlns="http://www.w3.org/1999/xhtml" &amp;gt;&lt;br /&gt;&amp;lt;head runat="server"&amp;gt;&lt;br /&gt;&amp;lt;style type="text/css"&amp;gt;&lt;br /&gt;.LISTBOX&lt;br /&gt;{&lt;br /&gt; width:100%;&lt;br /&gt; height:103%;&lt;br /&gt; border:0px solid;&lt;br /&gt;}&lt;br /&gt;&amp;lt;/style&amp;gt;&lt;br /&gt;&amp;lt;script language="javascript"&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre class="js" name="code"&gt;&lt;br /&gt;MultiPicklist = {};&lt;br /&gt;MultiPicklist.CrmForm = null;&lt;br /&gt;MultiPicklist.Storage = null;&lt;br /&gt;MultiPicklist.OnLoad  = function()&lt;br /&gt;{&lt;br /&gt; MultiPicklist.CrmForm = parent.document.all.crmForm;&lt;br /&gt; MultiPicklist.Storage = MultiPicklist.CrmForm.&amp;lt;%=Entity2Name%&amp;gt;;&lt;br /&gt; MultiPicklist.CrmForm.attachEvent( "onsave" , MultiPicklist.OnSave );&lt;br /&gt;}&lt;br /&gt;MultiPicklist.OnSave  = function()&lt;br /&gt;{&lt;br /&gt; var items = "";&lt;br /&gt; var MultiBox = document.all.MultiBox;&lt;br /&gt; for( var i = 0 ; i &amp;lt; MultiBox.options.length ; i++ )&lt;br /&gt; {&lt;br /&gt;  if( MultiBox.options[i].selected == true )&lt;br /&gt;   items += MultiBox.options[i].value + ",";&lt;br /&gt; } &lt;br /&gt; MultiPicklist.Storage.DataValue = items.replace(/,$/i,"");&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre class="html" name="code"&gt;&lt;br /&gt;&amp;lt;/script&amp;gt;&lt;br /&gt;&amp;lt;/head&amp;gt;&lt;br /&gt;&amp;lt;body leftmargin="0" onload="MultiPicklist.OnLoad()" scroll="no" topmargin="0"&amp;gt;&lt;br /&gt;    &amp;lt;form id="form1" runat="server"&amp;gt;&lt;br /&gt;  &amp;lt;ASP:LISTBOX CSSCLASS="listbox" ID="MultiBox" SELECTIONMODE="Multiple" RUNAT="server"&amp;gt;&lt;br /&gt;  &amp;lt;/ASP:LISTBOX&amp;gt;&lt;br /&gt;    &amp;lt;/form&amp;gt;&lt;br /&gt;&amp;lt;/body&amp;gt;&lt;br /&gt;&amp;lt;/html&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;3. In the code behind I added functionality which:&lt;br /&gt;a. Validates the query string parameters (Page_ValidateParameters())&lt;br /&gt;b. Retrieves the credit card list from CRM (Page_RetrievePicklistOptions())&lt;br /&gt;c. Marks the selected options if they exist (Page_SetSelectedOptions())&lt;br /&gt;&lt;br /&gt;&lt;pre class="csharp" name="code"&gt;&lt;br /&gt;using System;&lt;br /&gt;using System.Data;&lt;br /&gt;using System.Configuration;&lt;br /&gt;using System.Collections;&lt;br /&gt;using System.Web;&lt;br /&gt;using System.Web.Security;&lt;br /&gt;using System.Web.UI;&lt;br /&gt;using System.Web.UI.WebControls;&lt;br /&gt;using System.Web.UI.WebControls.WebParts;&lt;br /&gt;using System.Web.UI.HtmlControls;&lt;br /&gt;using System.Xml;&lt;br /&gt;using System.Text;&lt;br /&gt;using Microsoft.Crm.Sdk;&lt;br /&gt;using Microsoft.Crm.Sdk.Query;&lt;br /&gt;using Microsoft.Crm.SdkTypeProxy;&lt;br /&gt;&lt;br /&gt;namespace Controls&lt;br /&gt;{&lt;br /&gt; public partial class multipicklist : System.Web.UI.Page&lt;br /&gt; {&lt;br /&gt;  /// &amp;lt;summary&amp;gt;&lt;br /&gt;  /// Current Organizaton Name&lt;br /&gt;  /// &amp;lt;/summary&amp;gt;&lt;br /&gt;  private String OrganizationName;&lt;br /&gt;  /// &amp;lt;summary&amp;gt;&lt;br /&gt;  /// The Primary Entity (contact) ObjectId (crmForm.ObjectId)&lt;br /&gt;  /// &amp;lt;/summary&amp;gt;&lt;br /&gt;  private Guid ObjectId;&lt;br /&gt;  /// &amp;lt;summary&amp;gt;&lt;br /&gt;  /// The Primary Entity (contact) Name (typename)&lt;br /&gt;  /// &amp;lt;/summary&amp;gt;&lt;br /&gt;  private String Entity1Name;&lt;br /&gt;  /// &amp;lt;summary&amp;gt;&lt;br /&gt;  /// The Related N:N Entity (gi_creditcard) Name&lt;br /&gt;  /// This should also be the name of the transport attribute&lt;br /&gt;  /// on the Primary Entity (gi_creditcard attribute on contact entity)&lt;br /&gt;  /// &amp;lt;/summary&amp;gt;&lt;br /&gt;  protected String Entity2Name;&lt;br /&gt;  /// &amp;lt;summary&amp;gt;&lt;br /&gt;  /// The Related Entity PrimaryField Name (gi_card)&lt;br /&gt;  /// This is the Picklist Option Text&lt;br /&gt;  /// &amp;lt;/summary&amp;gt;&lt;br /&gt;  private String Entity2TextAttribute;&lt;br /&gt;  /// &amp;lt;summary&amp;gt;&lt;br /&gt;  /// The Primary Entity PrimaryKey Name (contactid)&lt;br /&gt;  /// &amp;lt;/summary&amp;gt;&lt;br /&gt;  private String Entity1KeyAttribute;&lt;br /&gt;  /// &amp;lt;summary&amp;gt;&lt;br /&gt;  /// The Related N:N Entity PrimaryKey Name (gi_creditcardid)&lt;br /&gt;  /// &amp;lt;/summary&amp;gt;&lt;br /&gt;  private String Entity2KeyAttribute;&lt;br /&gt;  /// &amp;lt;summary&amp;gt;&lt;br /&gt;  /// CrmService&lt;br /&gt;  /// &amp;lt;/summary&amp;gt;&lt;br /&gt;  private CrmService Service;&lt;br /&gt; &lt;br /&gt;  protected void Page_Load(object sender, EventArgs e)&lt;br /&gt;  {&lt;br /&gt;   Page_ValidateParameters();&lt;br /&gt;   Page_CreateService();&lt;br /&gt;   Page_RetrievePicklistOptions();&lt;br /&gt;   Page_SetSelectedOptions();&lt;br /&gt;  }&lt;br /&gt;  /// &amp;lt;summary&amp;gt;&lt;br /&gt;  /// Retrieve Options From Relationship Entity Using Fetch Message&lt;br /&gt;  /// And Set the Listbox Selected Items&lt;br /&gt;  /// &amp;lt;/summary&amp;gt;&lt;br /&gt;  private void Page_SetSelectedOptions()&lt;br /&gt;  {&lt;br /&gt;   if (!ObjectId.Equals(Guid.Empty))&lt;br /&gt;   {&lt;br /&gt;    String relationShipName = String.Format("gi_{0}_{1}", Entity2Name, Entity1Name);&lt;br /&gt;    StringBuilder fetchXml = new StringBuilder();&lt;br /&gt;    fetchXml.Append("&amp;lt;fetch mapping='logical'&amp;gt;");&lt;br /&gt;    fetchXml.Append("&amp;lt;entity name='").Append(relationShipName).Append("'&amp;gt;");&lt;br /&gt;    fetchXml.Append("&amp;lt;all-attributes /&amp;gt;");&lt;br /&gt;    fetchXml.Append("&amp;lt;filter&amp;gt;");&lt;br /&gt;    fetchXml.Append("&amp;lt;condition attribute='contactid' operator='eq' value ='");&lt;br /&gt;    fetchXml.Append(ObjectId).Append("' /&amp;gt;");&lt;br /&gt;    fetchXml.Append("&amp;lt;/filter&amp;gt;");&lt;br /&gt;    fetchXml.Append("&amp;lt;/entity&amp;gt;");&lt;br /&gt;    fetchXml.Append("&amp;lt;/fetch&amp;gt;");&lt;br /&gt;&lt;br /&gt;    String resultXml = Service.Fetch(fetchXml.ToString());&lt;br /&gt;    XmlDocument fetchResult = new XmlDocument();&lt;br /&gt;    fetchResult.LoadXml(resultXml);&lt;br /&gt;    XmlNodeList relatedResults = fetchResult.SelectNodes("resultset/result");&lt;br /&gt;&lt;br /&gt;    foreach (XmlElement item in relatedResults)&lt;br /&gt;    {&lt;br /&gt;     XmlElement Entity2KeyAttributeNode = (XmlElement)item.SelectSingleNode(Entity2KeyAttribute);&lt;br /&gt;     ListItem CurrentItem = MultiBox.Items.FindByValue(Entity2KeyAttributeNode.InnerText.ToLower());&lt;br /&gt;     if( CurrentItem != null ) CurrentItem.Selected = true;&lt;br /&gt;    }&lt;br /&gt;   }&lt;br /&gt;  }&lt;br /&gt;  /// &amp;lt;summary&amp;gt;&lt;br /&gt;  /// Retrieve the listbox options from the related entity using&lt;br /&gt;  /// queryexpression and fill the listbox.&lt;br /&gt;  /// &amp;lt;/summary&amp;gt;&lt;br /&gt;  private void Page_RetrievePicklistOptions()&lt;br /&gt;  {&lt;br /&gt;   QueryExpression creditQuery = new QueryExpression(Entity2Name);&lt;br /&gt;   creditQuery.ColumnSet.AddColumn(Entity2TextAttribute);&lt;br /&gt;   creditQuery.ColumnSet.AddColumn(Entity2KeyAttribute);&lt;br /&gt;   creditQuery.Distinct = true;&lt;br /&gt;   creditQuery.Orders.Add(new OrderExpression(Entity2TextAttribute, OrderType.Ascending));&lt;br /&gt;&lt;br /&gt;   RetrieveMultipleRequest retrieveMultipleRequest = new RetrieveMultipleRequest();&lt;br /&gt;   retrieveMultipleRequest.Query = creditQuery;&lt;br /&gt;   retrieveMultipleRequest.ReturnDynamicEntities = true;&lt;br /&gt;   RetrieveMultipleResponse retrieveMultipleResponse =&lt;br /&gt;    (RetrieveMultipleResponse)Service.Execute(retrieveMultipleRequest);&lt;br /&gt;&lt;br /&gt;   MultiBox.Items.Add( "" );&lt;br /&gt;   foreach (DynamicEntity creditcard in retrieveMultipleResponse.BusinessEntityCollection.BusinessEntities)&lt;br /&gt;   {&lt;br /&gt;    Key key = creditcard.Properties[Entity2KeyAttribute] as Key;&lt;br /&gt;    ListItem item = new ListItem(&lt;br /&gt;       creditcard.Properties[Entity2TextAttribute] + "",&lt;br /&gt;       key.Value.ToString("B"));&lt;br /&gt;    MultiBox.Items.Add(item);&lt;br /&gt;   }&lt;br /&gt;  }&lt;br /&gt;  /// &amp;lt;summary&amp;gt;&lt;br /&gt;  /// Create the crmservice&lt;br /&gt;  /// &amp;lt;/summary&amp;gt;&lt;br /&gt;  private void Page_CreateService()&lt;br /&gt;  {&lt;br /&gt;   CrmAuthenticationToken token = new CrmAuthenticationToken();&lt;br /&gt;   token.AuthenticationType = 0;&lt;br /&gt;   token.OrganizationName = OrganizationName;&lt;br /&gt;&lt;br /&gt;   Service = new CrmService();&lt;br /&gt;   Service.CrmAuthenticationTokenValue = token;&lt;br /&gt;   Service.PreAuthenticate = false;&lt;br /&gt;   Service.UnsafeAuthenticatedConnectionSharing = true;&lt;br /&gt;   Service.Credentials = System.Net.CredentialCache.DefaultCredentials;&lt;br /&gt;&lt;br /&gt;   Service.Url = String.Format("{0}://{1}:{2}/mscrmservices/2007/crmservice.asmx", Request.Url.Scheme, Request.Url.Host, Request.Url.Port);&lt;br /&gt;  }&lt;br /&gt;  /// &amp;lt;summary&amp;gt;&lt;br /&gt;  /// Validate the listbox control parameters&lt;br /&gt;  /// &amp;lt;/summary&amp;gt;&lt;br /&gt;  private void Page_ValidateParameters()&lt;br /&gt;  {&lt;br /&gt;   Entity1Name = Request.QueryString["fromentity"];&lt;br /&gt;   if (Entity1Name + "" == String.Empty)&lt;br /&gt;    throw new Exception("Entity1 Name is Missing");&lt;br /&gt;&lt;br /&gt;   Entity2Name = Request.QueryString["toentity"];&lt;br /&gt;   if (Entity2Name + "" == String.Empty)&lt;br /&gt;    throw new Exception("Entity2 Name is Missing");&lt;br /&gt;&lt;br /&gt;   OrganizationName = Request.QueryString["orgname"];&lt;br /&gt;   if (OrganizationName + "" == String.Empty)&lt;br /&gt;    throw new Exception("Organization Name is Missing");&lt;br /&gt;&lt;br /&gt;   ObjectId = Request.QueryString["id"] + "" == String.Empty ? Guid.Empty :&lt;br /&gt;    new Guid(Request.QueryString["id"]);&lt;br /&gt;&lt;br /&gt;   Entity2TextAttribute = Request.QueryString["toattribute"];&lt;br /&gt;   if (Entity2TextAttribute + "" == String.Empty)&lt;br /&gt;    throw new Exception("Picklist Text Attribute is Missing");&lt;br /&gt;&lt;br /&gt;   Entity1KeyAttribute = Entity1Name + "id";&lt;br /&gt;   Entity2KeyAttribute = Entity2Name + "id";&lt;br /&gt;  }&lt;br /&gt; }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;STEP 3 – Closing the Cycle Using a Plug-in&lt;/strong&gt;&lt;br /&gt;So why do we need a Plug-in? The main reason is that from the client side perspective CRM will create the contact id (GUID) only after the record is saved! This leaves us with no other option than to associate selected options on the server using a Plug-in.&lt;br /&gt;&lt;br /&gt;1. Added a new Class Library application&lt;br /&gt;&lt;br /&gt;&lt;a href="http://4.bp.blogspot.com/_M-mdv3Tfarg/STZ0xA__fiI/AAAAAAAAAH0/F8NAY0Z6KYg/s1600-h/STEP3.6.jpg"&gt;&lt;img id="BLOGGER_PHOTO_ID_5275532398941928994" style="BORDER-RIGHT: black 1px solid; BORDER-TOP: black 1px solid; DISPLAY: block; MARGIN: 0px auto 10px; BORDER-LEFT: black 1px solid; WIDTH: 285px; CURSOR: hand; BORDER-BOTTOM: black 1px solid; HEIGHT: 340px; TEXT-ALIGN: center" alt="" src="http://4.bp.blogspot.com/_M-mdv3Tfarg/STZ0xA__fiI/AAAAAAAAAH0/F8NAY0Z6KYg/s400/STEP3.6.jpg" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;2. Signed the application by creating a Strong Name key&lt;br /&gt;&lt;br /&gt;3. Added Plug-In code that removes existing options (Disassociates) and saves (Re-Associates) the current contact with the selected listbox (credit card) options. The plug-in receives the existing transport attribute saved options from the PreImage and disassociates these options then gets the newly selected options from the PostImage and Re-Associate them with the contact. The Plug-in needs to run on both Post Create and Post Update events. Following is the Plug-in Specifications, SolutionXml and Code&lt;br /&gt;&lt;br /&gt;&lt;a href="http://2.bp.blogspot.com/_M-mdv3Tfarg/STZ0oJEtjNI/AAAAAAAAAHE/GRqjVlYBEMk/s1600-h/STEP3.0.jpg"&gt;&lt;img id="BLOGGER_PHOTO_ID_5275532246490385618" style="BORDER-RIGHT: black 1px solid; BORDER-TOP: black 1px solid; DISPLAY: block; MARGIN: 0px auto 10px; BORDER-LEFT: black 1px solid; WIDTH: 400px; CURSOR: hand; BORDER-BOTTOM: black 1px solid; HEIGHT: 190px; TEXT-ALIGN: center" alt="" src="http://2.bp.blogspot.com/_M-mdv3Tfarg/STZ0oJEtjNI/AAAAAAAAAHE/GRqjVlYBEMk/s400/STEP3.0.jpg" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;a href="http://2.bp.blogspot.com/_M-mdv3Tfarg/STZ0oZmELTI/AAAAAAAAAHM/aPo8_Vs6gTE/s1600-h/STEP3.1.jpg"&gt;&lt;img id="BLOGGER_PHOTO_ID_5275532250925247794" style="BORDER-RIGHT: black 1px solid; BORDER-TOP: black 1px solid; DISPLAY: block; MARGIN: 0px auto 10px; BORDER-LEFT: black 1px solid; WIDTH: 354px; CURSOR: hand; BORDER-BOTTOM: black 1px solid; HEIGHT: 400px; TEXT-ALIGN: center" alt="" src="http://2.bp.blogspot.com/_M-mdv3Tfarg/STZ0oZmELTI/AAAAAAAAAHM/aPo8_Vs6gTE/s400/STEP3.1.jpg" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;a href="http://3.bp.blogspot.com/_M-mdv3Tfarg/STZ0oWgZFfI/AAAAAAAAAHU/7esVWHV0auk/s1600-h/STEP3.2.jpg"&gt;&lt;img id="BLOGGER_PHOTO_ID_5275532250096145906" style="BORDER-RIGHT: black 1px solid; BORDER-TOP: black 1px solid; DISPLAY: block; MARGIN: 0px auto 10px; BORDER-LEFT: black 1px solid; WIDTH: 400px; CURSOR: hand; BORDER-BOTTOM: black 1px solid; HEIGHT: 217px; TEXT-ALIGN: center" alt="" src="http://3.bp.blogspot.com/_M-mdv3Tfarg/STZ0oWgZFfI/AAAAAAAAAHU/7esVWHV0auk/s400/STEP3.2.jpg" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;a href="http://3.bp.blogspot.com/_M-mdv3Tfarg/STZ0ozcfknI/AAAAAAAAAHc/OceWgIIBuZw/s1600-h/STEP3.3.jpg"&gt;&lt;img id="BLOGGER_PHOTO_ID_5275532257864422002" style="BORDER-RIGHT: black 1px solid; BORDER-TOP: black 1px solid; DISPLAY: block; MARGIN: 0px auto 10px; BORDER-LEFT: black 1px solid; WIDTH: 400px; CURSOR: hand; BORDER-BOTTOM: black 1px solid; HEIGHT: 94px; TEXT-ALIGN: center" alt="" src="http://3.bp.blogspot.com/_M-mdv3Tfarg/STZ0ozcfknI/AAAAAAAAAHc/OceWgIIBuZw/s400/STEP3.3.jpg" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;a href="http://2.bp.blogspot.com/_M-mdv3Tfarg/STZ0wpH7iJI/AAAAAAAAAHk/wnk6yeXlxtw/s1600-h/STEP3.4.jpg"&gt;&lt;img id="BLOGGER_PHOTO_ID_5275532392532773010" style="BORDER-RIGHT: black 1px solid; BORDER-TOP: black 1px solid; DISPLAY: block; MARGIN: 0px auto 10px; BORDER-LEFT: black 1px solid; WIDTH: 400px; CURSOR: hand; BORDER-BOTTOM: black 1px solid; HEIGHT: 217px; TEXT-ALIGN: center" alt="" src="http://2.bp.blogspot.com/_M-mdv3Tfarg/STZ0wpH7iJI/AAAAAAAAAHk/wnk6yeXlxtw/s400/STEP3.4.jpg" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;a href="http://3.bp.blogspot.com/_M-mdv3Tfarg/STZ0wwAXdXI/AAAAAAAAAHs/OpRactYf9iM/s1600-h/STEP3.5.jpg"&gt;&lt;img id="BLOGGER_PHOTO_ID_5275532394380096882" style="BORDER-RIGHT: black 1px solid; BORDER-TOP: black 1px solid; DISPLAY: block; MARGIN: 0px auto 10px; BORDER-LEFT: black 1px solid; WIDTH: 400px; CURSOR: hand; BORDER-BOTTOM: black 1px solid; HEIGHT: 94px; TEXT-ALIGN: center" alt="" src="http://3.bp.blogspot.com/_M-mdv3Tfarg/STZ0wwAXdXI/AAAAAAAAAHs/OpRactYf9iM/s400/STEP3.5.jpg" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Plug-In Solution XML&lt;/strong&gt;&lt;br /&gt;&lt;pre class='xml' name='code'&gt;&lt;br /&gt;&amp;lt;Register LogFile="Plug-in Registration Log.txt" Server="http://moss:5555" Org="MicrosoftCRM" Domain="" UserName="administrator"&amp;gt;&lt;br /&gt; &amp;lt;Solution SourceType="1" Assembly="MPPlugIn.dll" Id="28f2c17f-2a16-4ce7-9a98-8e627ad2aecf"&amp;gt;&lt;br /&gt;  &amp;lt;PluginTypes&amp;gt;&lt;br /&gt;   &amp;lt;Plugin TypeName="MPPlugIn.MPHandler" FriendlyName="3fe8c6c8-f928-43e3-b2e1-ccb55d651d6c" Id="93ecf707-c794-47df-a76e-9df3a3ee8e00"&amp;gt;&lt;br /&gt;    &amp;lt;Steps&amp;gt;&lt;br /&gt;     &amp;lt;Step PluginTypeName="MPPlugIn.MPHandler" PluginTypeFriendlyName="3fe8c6c8-f928-43e3-b2e1-ccb55d651d6c" CustomConfiguration="" SecureConfiguration="" Description="Create of contact in Parent Pipeline" FilteringAttributes="" ImpersonatingUserId="" InvocationSource="0" MessageName="Create" Mode="0" PrimaryEntityName="contact" SecondaryEntityName="none" Stage="50" SupportedDeployment="0" Rank="1" Id="a0631824-7fc0-dd11-b17c-0003ff2f0264"&amp;gt;&lt;br /&gt;      &amp;lt;Images&amp;gt;&lt;br /&gt;       &amp;lt;Image EntityAlias="gi_gi_creditcard_contact" ImageType="1" MessagePropertyName="Id" Attributes="contactid,gi_creditcard" Id="405a617d-7fc0-dd11-b17c-0003ff2f0264" /&amp;gt;&lt;br /&gt;      &amp;lt;/Images&amp;gt;&lt;br /&gt;     &amp;lt;/Step&amp;gt;&lt;br /&gt;     &amp;lt;Step PluginTypeName="MPPlugIn.MPHandler" PluginTypeFriendlyName="3fe8c6c8-f928-43e3-b2e1-ccb55d651d6c" CustomConfiguration="" SecureConfiguration="" Description="Update of contact in Parent Pipeline" FilteringAttributes="" ImpersonatingUserId="" InvocationSource="0" MessageName="Update" Mode="0" PrimaryEntityName="contact" SecondaryEntityName="none" Stage="50" SupportedDeployment="0" Rank="1" Id="a0d1a8fe-82c0-dd11-b17c-0003ff2f0264"&amp;gt;&lt;br /&gt;      &amp;lt;Images&amp;gt;&lt;br /&gt;       &amp;lt;Image EntityAlias="gi_gi_creditcard_contact" ImageType="2" MessagePropertyName="Target" Attributes="contactid,gi_creditcard" Id="f0d85819-83c0-dd11-b17c-0003ff2f0264" /&amp;gt;&lt;br /&gt;      &amp;lt;/Images&amp;gt;&lt;br /&gt;     &amp;lt;/Step&amp;gt;&lt;br /&gt;    &amp;lt;/Steps&amp;gt;&lt;br /&gt;   &amp;lt;/Plugin&amp;gt;&lt;br /&gt;  &amp;lt;/PluginTypes&amp;gt;&lt;br /&gt; &amp;lt;/Solution&amp;gt;&lt;br /&gt;&amp;lt;/Register&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;&lt;strong&gt;Plug-In Code:&lt;/strong&gt;&lt;/strong&gt;&lt;br /&gt;&lt;pre class="csharp" name="code"&gt;&lt;br /&gt;using System;&lt;br /&gt;using System.Collections.Generic;&lt;br /&gt;using System.Text;&lt;br /&gt;using Microsoft.Crm.Sdk;&lt;br /&gt;using Microsoft.Crm.SdkTypeProxy;&lt;br /&gt;&lt;br /&gt;namespace MPPlugIn&lt;br /&gt;{&lt;br /&gt; public class MPHandler : IPlugin&lt;br /&gt; {&lt;br /&gt;  #region IPlugin Members&lt;br /&gt; &lt;br /&gt;  /// &amp;lt;summary&amp;gt;&lt;br /&gt;  /// A Multipicklist context Object&lt;br /&gt;  /// &amp;lt;/summary&amp;gt;&lt;br /&gt;  public class MPContext&lt;br /&gt;  {&lt;br /&gt;   /// &amp;lt;summary&amp;gt;&lt;br /&gt;   /// MS Context&lt;br /&gt;   /// &amp;lt;/summary&amp;gt;&lt;br /&gt;   public IPluginExecutionContext context;&lt;br /&gt;   /// &amp;lt;summary&amp;gt;&lt;br /&gt;   /// The Entity 1 (Primary: contact in this case) Primary Key e.g. contactid&lt;br /&gt;   /// &amp;lt;/summary&amp;gt;&lt;br /&gt;   public String Entity1PrimaryAttributeName;&lt;br /&gt;   /// &amp;lt;summary&amp;gt;&lt;br /&gt;   /// The Entity 2 (related: gi_creditcard in this case) Name&lt;br /&gt;   /// &amp;lt;/summary&amp;gt;&lt;br /&gt;   public String Entity2Name;&lt;br /&gt;   /// &amp;lt;summary&amp;gt;&lt;br /&gt;   /// An Entity 1 Attribute that is used as a transport form selected options.&lt;br /&gt;   /// in this case i called it gi_creditcard (same and the related entity name)&lt;br /&gt;   /// You can give it any name you like.&lt;br /&gt;   /// &amp;lt;/summary&amp;gt;&lt;br /&gt;   public String Entity1PicklistOptionsAttribute;&lt;br /&gt;   /// &amp;lt;summary&amp;gt;&lt;br /&gt;   /// The relationship name of contact and gi_creditcard&lt;br /&gt;   /// e.g. gi_gi_creditcard_contact. this is also used as the&lt;br /&gt;   /// PreImage and PostImage ALIAS names.&lt;br /&gt;   /// &amp;lt;/summary&amp;gt;&lt;br /&gt;   public String N2NRelationShipName;&lt;br /&gt;   /// &amp;lt;summary&amp;gt;&lt;br /&gt;   /// Entity 1 (Primary) Name&lt;br /&gt;   /// Primary means the entity where the multipicklist is put.&lt;br /&gt;   /// Related means the entity from which the multipicklist options are taken.&lt;br /&gt;   /// &amp;lt;/summary&amp;gt;&lt;br /&gt;   private String _Entity1Name;&lt;br /&gt;   public  String  Entity1Name&lt;br /&gt;   {&lt;br /&gt;    get&lt;br /&gt;    {&lt;br /&gt;     return this._Entity1Name;&lt;br /&gt;    }&lt;br /&gt;    set&lt;br /&gt;    {&lt;br /&gt;     this._Entity1Name = value;&lt;br /&gt;     this.Entity1PrimaryAttributeName = value + "id";&lt;br /&gt;    }&lt;br /&gt;   }&lt;br /&gt;   /// &amp;lt;summary&amp;gt;&lt;br /&gt;   /// Initialize a new MP context&lt;br /&gt;   /// &amp;lt;/summary&amp;gt;&lt;br /&gt;   /// &amp;lt;param name="context"&amp;gt;&amp;lt;/param&amp;gt;&lt;br /&gt;   public MPContext(IPluginExecutionContext context) {&lt;br /&gt;    this.context = context;&lt;br /&gt;   }&lt;br /&gt;  }&lt;br /&gt;  /// &amp;lt;summary&amp;gt;&lt;br /&gt;  /// PlugIn Implementation:&lt;br /&gt;  /// 1. Create a new MPContext&lt;br /&gt;  /// 2. Fill the MPContext parameters&lt;br /&gt;  /// 3. Disassociate old options&lt;br /&gt;  /// 4. Associate new options&lt;br /&gt;  /// &amp;lt;/summary&amp;gt;&lt;br /&gt;  /// &amp;lt;param name="context"&amp;gt;&amp;lt;/param&amp;gt;&lt;br /&gt;  public void Execute(IPluginExecutionContext context)&lt;br /&gt;  {&lt;br /&gt;   MPContext mpContext = new MPContext(context);&lt;br /&gt;   mpContext.Entity1Name = "contact";&lt;br /&gt;   mpContext.Entity2Name = "gi_creditcard";&lt;br /&gt;   mpContext.Entity1PicklistOptionsAttribute = "gi_creditcard";&lt;br /&gt;   mpContext.N2NRelationShipName = "gi_gi_creditcard_contact";&lt;br /&gt;&lt;br /&gt;   DisAssociateMultiplePicklist(mpContext);&lt;br /&gt;   AssociateMultiplePicklist(mpContext);&lt;br /&gt;  }&lt;br /&gt;  /// &amp;lt;summary&amp;gt;&lt;br /&gt;  /// Retrieves the existing options from the storage (transport) attribute&lt;br /&gt;  /// on the primary entity (contact) and call DisassociateEntitiesRequest&lt;br /&gt;  /// to remove the association&lt;br /&gt;  /// &amp;lt;/summary&amp;gt;&lt;br /&gt;  /// &amp;lt;param name="context"&amp;gt;&amp;lt;/param&amp;gt;&lt;br /&gt;  private void DisAssociateMultiplePicklist(MPContext context)&lt;br /&gt;  {&lt;br /&gt;   IPluginExecutionContext msContext = context.context;&lt;br /&gt;   /*&lt;br /&gt;    Validates that the relationship name (e.g. gi_gi_creditcard_contact)&lt;br /&gt;    is the name you gave the PreImage Alias.&lt;br /&gt;   */&lt;br /&gt;   if (msContext.PreEntityImages.Properties.Contains(context.N2NRelationShipName) == true)&lt;br /&gt;   {&lt;br /&gt;    //Retrieve the PreImage dynamic (Primary) entity 1 (contact)&lt;br /&gt;    DynamicEntity Entity1 = msContext.PreEntityImages.Properties[context.N2NRelationShipName] as DynamicEntity;&lt;br /&gt;    /*&lt;br /&gt;     Validates that the image contains the primarykey (contactid) in the property bag.&lt;br /&gt;     the contactid represents one side of the link (Moniker).&lt;br /&gt;    */&lt;br /&gt;    if (Entity1.Properties.Contains(context.Entity1PrimaryAttributeName) == false)&lt;br /&gt;     return;&lt;br /&gt;    /* Extract the id (contactid) */&lt;br /&gt;    Guid primaryEntityId = ((Key)Entity1.Properties[context.Entity1PrimaryAttributeName]).Value;&lt;br /&gt;    /*&lt;br /&gt;     If the options (transport) attribute is missing&lt;br /&gt;     meaning that the its value is null then exit&lt;br /&gt;    */&lt;br /&gt;    if( Entity1.Properties.Contains( context.Entity1PicklistOptionsAttribute ) == false )&lt;br /&gt;     return;&lt;br /&gt;    /* get the existing options */&lt;br /&gt;    String strOptions = Entity1.Properties[context.Entity1PicklistOptionsAttribute] + "";&lt;br /&gt;    if (strOptions.Equals(String.Empty)) return;&lt;br /&gt;   &lt;br /&gt;    /* create an option array */&lt;br /&gt;    String[] mpOptions  = strOptions.Split(',');&lt;br /&gt;    /* create a crmservice */&lt;br /&gt;    ICrmService Service = msContext.CreateCrmService(true);&lt;br /&gt;   &lt;br /&gt;    /* for each option disassociate (remove) the link */&lt;br /&gt;    foreach (String option in mpOptions)&lt;br /&gt;    {&lt;br /&gt;     Guid relatedEntityId = new Guid(option);&lt;br /&gt;     DisassociateEntitiesRequest assocRequest = new DisassociateEntitiesRequest();&lt;br /&gt;     assocRequest.Moniker1 = new Moniker(context.Entity1Name, primaryEntityId);&lt;br /&gt;     assocRequest.Moniker2 = new Moniker(context.Entity2Name, relatedEntityId);&lt;br /&gt;     assocRequest.RelationshipName = context.N2NRelationShipName;&lt;br /&gt;     Service.Execute(assocRequest);&lt;br /&gt;    }&lt;br /&gt;   }&lt;br /&gt;  }&lt;br /&gt;  /// &amp;lt;summary&amp;gt;&lt;br /&gt;  /// Associates the new selected options with the primary entity (contact)&lt;br /&gt;  /// using the selected options taken from the PostImage Propery bag.&lt;br /&gt;  /// See DisAssociateMultiplePicklist method for code details.&lt;br /&gt;  /// &amp;lt;/summary&amp;gt;&lt;br /&gt;  /// &amp;lt;param name="context"&amp;gt;&amp;lt;/param&amp;gt;&lt;br /&gt;  private void AssociateMultiplePicklist( MPContext context )&lt;br /&gt;  {&lt;br /&gt;   IPluginExecutionContext msContext = context.context;&lt;br /&gt;  &lt;br /&gt;   if (msContext.PostEntityImages.Properties.Contains(context.N2NRelationShipName) == true)&lt;br /&gt;   {&lt;br /&gt;    DynamicEntity Entity1 = msContext.PostEntityImages.Properties[context.N2NRelationShipName] as DynamicEntity;&lt;br /&gt;&lt;br /&gt;    if (Entity1.Properties.Contains(context.Entity1PrimaryAttributeName) == false)&lt;br /&gt;     return;&lt;br /&gt;&lt;br /&gt;    Guid primaryEntityId = ((Key)Entity1.Properties[context.Entity1PrimaryAttributeName]).Value;&lt;br /&gt;&lt;br /&gt;    if (Entity1.Properties.Contains(context.Entity1PicklistOptionsAttribute) == false)&lt;br /&gt;     return;&lt;br /&gt;    &lt;br /&gt;    String strOptions = Entity1.Properties[context.Entity1PicklistOptionsAttribute] + "";&lt;br /&gt;    if (strOptions.Equals(String.Empty)) return;&lt;br /&gt;&lt;br /&gt;    String[] mpOptions  = strOptions.Split(',');&lt;br /&gt;    ICrmService Service = msContext.CreateCrmService(true);&lt;br /&gt;&lt;br /&gt;    foreach (String option in mpOptions)&lt;br /&gt;    {    &lt;br /&gt;     Guid relatedEntityId  = new Guid(option);&lt;br /&gt;     AssociateEntitiesRequest assocRequest = new AssociateEntitiesRequest();&lt;br /&gt;     assocRequest.Moniker1 = new Moniker(context.Entity1Name, primaryEntityId);&lt;br /&gt;     assocRequest.Moniker2 = new Moniker(context.Entity2Name, relatedEntityId);&lt;br /&gt;     assocRequest.RelationshipName = context.N2NRelationShipName;&lt;br /&gt;     Service.Execute( assocRequest );&lt;br /&gt;    }&lt;br /&gt;   }&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  #endregion&lt;br /&gt; }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;In order to add another relationship multi select control you only need to repeat step 1 ( customizations ) and part of step 3 ( Plug-In Registration and duplicating the Execute Method).&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3003801964503297388-1552131959732985033?l=mscrm4ever.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mscrm4ever.blogspot.com/feeds/1552131959732985033/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3003801964503297388&amp;postID=1552131959732985033&amp;isPopup=true' title='15 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3003801964503297388/posts/default/1552131959732985033'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3003801964503297388/posts/default/1552131959732985033'/><link rel='alternate' type='text/html' href='http://mscrm4ever.blogspot.com/2008/12/crm-40-supported-multi-select-picklist.html' title='CRM 4.0 Supported Multi Select (Picklist) Control'/><author><name>Adi Katz</name><uri>http://www.blogger.com/profile/16783428437179825705</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_M-mdv3Tfarg/STZ0QNlSzGI/AAAAAAAAAFs/0j39raEFTLI/s72-c/STEP0.jpg' height='72' width='72'/><thr:total>15</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3003801964503297388.post-7694117393827056295</id><published>2008-11-21T01:55:00.003+02:00</published><updated>2009-03-19T02:12:00.426+02:00</updated><title type='text'>Change Cases Associated Default View</title><content type='html'>This is a complementary post to the “&lt;a href="http://mscrm4ever.blogspot.com/2008/07/changing-activity-history-default-view.html"&gt;Changing Activity / History Default View&lt;/a&gt;”.&lt;br /&gt;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.&lt;br /&gt;&lt;br /&gt;&lt;pre class='js' name='code'&gt;&lt;br /&gt;//Case History statecode options &lt;br /&gt;var ServiceOptions =&lt;br /&gt;{&lt;br /&gt;   Active : "0",&lt;br /&gt;   Resolved : "1",&lt;br /&gt;   Canceled : "2",&lt;br /&gt;   All : "All"&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;var _loadarea = loadArea;&lt;br /&gt;loadArea = function(sArea, sParams, sUrl, bIsvMode)&lt;br /&gt;{&lt;br /&gt;   //load the iframe&lt;br /&gt;   _loadarea(sArea, sParams, sUrl, bIsvMode);&lt;br /&gt;&lt;br /&gt;   if( sArea != "areaService" ) return;&lt;br /&gt;&lt;br /&gt;   //create the iframe object&lt;br /&gt;   var iframe = document.getElementById(sArea + "Frame");&lt;br /&gt;   //wait until the iframe is fully loaded ("complete")&lt;br /&gt;   iframe.onreadystatechange = function()&lt;br /&gt;   {&lt;br /&gt;      if( iframe.readyState == "complete")&lt;br /&gt;      {&lt;br /&gt;         var picklist,option;&lt;br /&gt;         //reference to the iframe document&lt;br /&gt;         var iframeDoc = iframe.contentWindow.document;&lt;br /&gt;         switch(sArea)&lt;br /&gt;         {&lt;br /&gt;            case "areaService":&lt;br /&gt;               picklist = iframeDoc.all.statecode[0];&lt;br /&gt;               option = ServiceOptions.All;&lt;br /&gt;            break;&lt;br /&gt;         }&lt;br /&gt;         picklist.value = option;&lt;br /&gt;         picklist.FireOnChange();&lt;br /&gt;      }&lt;br /&gt;   }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3003801964503297388-7694117393827056295?l=mscrm4ever.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mscrm4ever.blogspot.com/feeds/7694117393827056295/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3003801964503297388&amp;postID=7694117393827056295&amp;isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3003801964503297388/posts/default/7694117393827056295'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3003801964503297388/posts/default/7694117393827056295'/><link rel='alternate' type='text/html' href='http://mscrm4ever.blogspot.com/2008/11/change-cases-associated-default-view.html' title='Change Cases Associated Default View'/><author><name>Adi Katz</name><uri>http://www.blogger.com/profile/16783428437179825705</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3003801964503297388.post-4134017500833757963</id><published>2008-11-14T20:01:00.003+02:00</published><updated>2008-11-14T20:08:48.928+02:00</updated><title type='text'>Creating an UpDown Control</title><content type='html'>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.&lt;br /&gt;&lt;br /&gt;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.&lt;br /&gt;&lt;br /&gt;&lt;img style="border:1px solid black;display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 374px; height: 31px;" src="http://2.bp.blogspot.com/_M-mdv3Tfarg/SR2-aR3VstI/AAAAAAAAAFk/E0Eu3ZOjZG0/s400/UpDownControl.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5268576497774736082" /&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre class='js' name='code'&gt;&lt;br /&gt;&lt;br /&gt;/* &lt;br /&gt; parameters: fieldId - crm field id &lt;br /&gt; minValue - positive number &lt;br /&gt; maxValue - positive number &lt;br /&gt;*/&lt;br /&gt;function UpDownControl( fieldId , minValue , maxValue )&lt;br /&gt;{&lt;br /&gt; var Instance   = this;&lt;br /&gt; /* crm field */&lt;br /&gt; var upDownCtrlBox = crmForm.all[ fieldId ];&lt;br /&gt; /* field parent element field_d */&lt;br /&gt; var parent        = upDownCtrlBox.parentElement;&lt;br /&gt; /* this span affects the spanUpDown scroll */&lt;br /&gt; var span          = document.createElement( "&amp;lt;SPAN style='visibility:hidden;width:1;'&amp;gt;" );&lt;br /&gt; var spanUpDownHeight = upDownCtrlBox.clientHeight - 2;&lt;br /&gt; /* this span is the scroller */&lt;br /&gt; var spanUpDown     = document.createElement( "&amp;lt;SPAN style='margin-left:-18px;overflow-y:scroll;width:1;height:" + spanUpDownHeight +  ";'&amp;gt;" );&lt;br /&gt; /* max range */&lt;br /&gt; Instance.Max = maxValue;&lt;br /&gt; /* min range */&lt;br /&gt; Instance.Min = minValue;&lt;br /&gt; /* fires when the scroll is pressed */&lt;br /&gt; function onScrollChange()&lt;br /&gt; {&lt;br /&gt;  /* get the current scroll position */&lt;br /&gt;  var scrollTop = parseInt( spanUpDown.scrollTop );&lt;br /&gt;  /* adjust size if value is outside of the scroll limits */&lt;br /&gt;  if( scrollTop &amp;lt; Instance.Min ){ &lt;br /&gt;   scrollTop = Instance.Min;&lt;br /&gt;   spanUpDown.scrollTop = scrollTop;&lt;br /&gt;  }&lt;br /&gt;  else if( scrollTop &amp;gt; Instance.Max ){&lt;br /&gt;   scrollTop = Instance.Max;&lt;br /&gt;   spanUpDown.scrollTop = scrollTop;&lt;br /&gt;  }&lt;br /&gt;  /* assign the scroll value back to the field */&lt;br /&gt;  upDownCtrlBox.DataValue = scrollTop;  &lt;br /&gt; }&lt;br /&gt; /* fires when the field value changes */&lt;br /&gt; function onValueChange()&lt;br /&gt; {&lt;br /&gt;  /* if the value is valid */&lt;br /&gt;  if( upDownCtrlBox.DataValue != null )&lt;br /&gt;  {&lt;br /&gt;    /* get the value */&lt;br /&gt;    var dataValue = parseInt( upDownCtrlBox.DataValue );&lt;br /&gt;    /* check that the vlaue is within permitted limits */&lt;br /&gt;    if( dataValue &amp;lt; Instance.Min ){&lt;br /&gt;     dataValue = Instance.Min;&lt;br /&gt;     upDownCtrlBox.DataValue = dataValue;&lt;br /&gt;     return false;&lt;br /&gt;    }&lt;br /&gt;    else if( dataValue &amp;gt; Instance.Max ){&lt;br /&gt;     dataValue = Instance.Max;&lt;br /&gt;     upDownCtrlBox.DataValue = dataValue;&lt;br /&gt;     return false;&lt;br /&gt;    }&lt;br /&gt;    /* assign the field value back to the scroll */&lt;br /&gt;       spanUpDown.scrollTop = dataValue; &lt;br /&gt;  }&lt;br /&gt;  /* assign min value is the datavalue is null */&lt;br /&gt;  else spanUpDown.scrollTop = Instance.Min;&lt;br /&gt; }&lt;br /&gt;  &lt;br /&gt; /* fire onValueChange when the field is changed */&lt;br /&gt; upDownCtrlBox.attachEvent( "onchange" , onValueChange );&lt;br /&gt; /* adjust inner span height = maxValue + spanHeight */&lt;br /&gt; span.style.height = maxValue + spanUpDownHeight; &lt;br /&gt; /* construct the updown control */&lt;br /&gt; spanUpDown.appendChild( span );&lt;br /&gt; /* fire onScrollChange when the user presses the scrollbar */&lt;br /&gt; spanUpDown.attachEvent( "onscroll" , onScrollChange );&lt;br /&gt; parent.appendChild( spanUpDown );  &lt;br /&gt; /* call onValueChange when the form loads / control is created */&lt;br /&gt; onValueChange();&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;widthUpDownCtrl = new UpDownControl("gi_width" , 20 , 10000 ); &lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3003801964503297388-4134017500833757963?l=mscrm4ever.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mscrm4ever.blogspot.com/feeds/4134017500833757963/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3003801964503297388&amp;postID=4134017500833757963&amp;isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3003801964503297388/posts/default/4134017500833757963'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3003801964503297388/posts/default/4134017500833757963'/><link rel='alternate' type='text/html' href='http://mscrm4ever.blogspot.com/2008/11/creating-updown-control.html' title='Creating an UpDown Control'/><author><name>Adi Katz</name><uri>http://www.blogger.com/profile/16783428437179825705</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_M-mdv3Tfarg/SR2-aR3VstI/AAAAAAAAAFk/E0Eu3ZOjZG0/s72-c/UpDownControl.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3003801964503297388.post-3311759097052915484</id><published>2008-11-13T08:35:00.004+02:00</published><updated>2008-11-25T21:36:00.771+02:00</updated><title type='text'>Creating a Dynamic Picklist Control</title><content type='html'>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.&lt;br /&gt;&lt;br /&gt;This post offers a different and more robust solution to the creation of dynamic Picklists.&lt;br /&gt;I call the control a TextList which obviously is a hybridization of a text and a Picklist fields.&lt;br /&gt;&lt;br /&gt;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.&lt;br /&gt;&lt;br /&gt;I’ve added comments which explain the methods functionality.&lt;br /&gt;&lt;br /&gt;&lt;pre class="js" name="code"&gt;&lt;br /&gt;// JScript File&lt;br /&gt;function OnCrmPageLoad()&lt;br /&gt;{&lt;br /&gt; /* Change gi_name text field to a textlist */&lt;br /&gt; var nameTextList = new TextList( "gi_name" );&lt;br /&gt;  /* Set 2 dependent fields */&lt;br /&gt;  nameTextList.DependentFields.Add( "gi_dependentfield1" , "JustANumber" );&lt;br /&gt;  nameTextList.DependentFields.Add( "gi_dependentfield2" , "Color" );&lt;br /&gt;  /* Add blank option */&lt;br /&gt;  nameTextList.Add( "" , "" );&lt;br /&gt;  for( var i = 0 ; i &amp;lt; 6 ; i++ )&lt;br /&gt;  {&lt;br /&gt;   /* Fill some values */&lt;br /&gt;   var option = nameTextList.Add( "Text " + i , "Value " + i );&lt;br /&gt;   /* create 2 new attributes on each option (Color,JustANumber) */ &lt;br /&gt;    option.Color = "A" + i + "B" + i + "C" + i;&lt;br /&gt;    option.JustANumber = i.toString();&lt;br /&gt;  }&lt;br /&gt;  /* Set the initial value */&lt;br /&gt;  nameTextList.SetInitValue(true);&lt;br /&gt;  /* Finish the job */&lt;br /&gt;  nameTextList.Transform();&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;/* &lt;br /&gt; Holds a list of all dependent fields. &lt;br /&gt; Each textlist has a dependent fields collection &lt;br /&gt;*/&lt;br /&gt;DependentFieldCollection = function()&lt;br /&gt;{&lt;br /&gt; this.List = [];&lt;br /&gt; this.Add = function( name , prop ){&lt;br /&gt;  this.List.push( new DependentField( name , prop ) );&lt;br /&gt; }&lt;br /&gt;}&lt;br /&gt;/* &lt;br /&gt; Single Dependent Field. &lt;br /&gt; The Object maps between an option attribute an a crmForm field &lt;br /&gt;*/&lt;br /&gt;DependentField = function( name , prop )&lt;br /&gt;{&lt;br /&gt; this.Property  = prop;&lt;br /&gt; this.Name = name;&lt;br /&gt;}&lt;br /&gt;/* Text list control */&lt;br /&gt;TextList = function( txtId ) &lt;br /&gt;{&lt;br /&gt; var textList = this;&lt;br /&gt; /* Referech the original text field */&lt;br /&gt; var textCtrl = crmForm.all[ txtId ];&lt;br /&gt; if (!textCtrl) return alert("TextList: " + txtId + " Is Missing");&lt;br /&gt; &lt;br /&gt; /* Create a new Picklist control */&lt;br /&gt; textList.Picklist   = document.createElement("SELECT"); &lt;br /&gt; textList.Picklist.id  = textCtrl.id;&lt;br /&gt; textList.Picklist.req = textCtrl.req;&lt;br /&gt; textList.Picklist.tabIndex = textCtrl.tabIndex;&lt;br /&gt; textList.Picklist.className = "ms-crm-SelectBox ";&lt;br /&gt; textList.Picklist.defaultSelected = textCtrl.DataValue;&lt;br /&gt; textList.DependentFields = new DependentFieldCollection();&lt;br /&gt; &lt;br /&gt; /* Add the new picklist and remove the text control */&lt;br /&gt; textCtrl.parentElement.appendChild( textList.Picklist );&lt;br /&gt; textCtrl.parentElement.removeChild( textCtrl );&lt;br /&gt; &lt;br /&gt; /* &lt;br /&gt;  Finish the textlist transformation&lt;br /&gt;  Create inline dependent fields properties on the textlist&lt;br /&gt;  Attach an update function to the picklist onchange event&lt;br /&gt; */&lt;br /&gt; textList.Transform = function()&lt;br /&gt; {&lt;br /&gt;  for( var i = 0;i &amp;lt; textList.DependentFields.List.length;i++ ){&lt;br /&gt;   var depField = textList.DependentFields.List[ i ]; &lt;br /&gt;    textList[ depField.Name ] = crmForm.all[ depField.Name ]; &lt;br /&gt;  } &lt;br /&gt;  textList.Picklist.attachEvent( "onchange" , UpdateDependentFields );&lt;br /&gt; }&lt;br /&gt; /* Sets the picklist value  */&lt;br /&gt; textList.SetValue = function( value )&lt;br /&gt; {&lt;br /&gt;  textList.Picklist.DataValue = value;&lt;br /&gt;  textList.Picklist.FireOnChange();&lt;br /&gt; }&lt;br /&gt; /* &lt;br /&gt;  Sets the picklist initial value. &lt;br /&gt;  This should be called when the page load after you fill the picklist options &lt;br /&gt; */&lt;br /&gt; textList.SetInitValue = function( fireEvent )&lt;br /&gt; {&lt;br /&gt;  textList.Picklist.DataValue = textList.Picklist.defaultSelected;&lt;br /&gt;  if(fireEvent) textList.Picklist.FireOnChange();&lt;br /&gt; }&lt;br /&gt; /* Add a new picklist Option */&lt;br /&gt; textList.Add = function( text , value ) &lt;br /&gt; {&lt;br /&gt;  return textList.Picklist.AddOption( text , value );&lt;br /&gt; }&lt;br /&gt; /* Remove a picklist Option by its value */&lt;br /&gt; textList.Remove = function( value )&lt;br /&gt; {&lt;br /&gt;  textList.Picklist.DeleteOption( value );&lt;br /&gt; }&lt;br /&gt; /* Clear all Picklist Options */&lt;br /&gt; textList.Clear = function()&lt;br /&gt; {&lt;br /&gt;  textList.Picklist.length = 0;&lt;br /&gt; }&lt;br /&gt; /* Helper property for attaching to an event from within the picklist control */&lt;br /&gt; textList.Attach = function( targetId , eventName , callback )&lt;br /&gt; {&lt;br /&gt;  var target = document.all[ targetId ];&lt;br /&gt;  if ( target ) target.attachEvent( eventName , callback );&lt;br /&gt; }&lt;br /&gt; /* Updates the dependent fields when the picklist onchange fires */&lt;br /&gt; function UpdateDependentFields()&lt;br /&gt; {&lt;br /&gt;  if ( textList.Picklist.selectedIndex &amp;gt; -1 )&lt;br /&gt;  for( var i = 0;i &amp;lt; textList.DependentFields.List.length;i++ )&lt;br /&gt;  {&lt;br /&gt;   var depField = textList.DependentFields.List[ i ]; &lt;br /&gt;   textList[ depField.Name ].DataValue = textList.Picklist.SelectedOption[ depField.Property ];&lt;br /&gt;   textList[ depField.Name ].FireOnChange();&lt;br /&gt;  }&lt;br /&gt; }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;OnCrmPageLoad();&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3003801964503297388-3311759097052915484?l=mscrm4ever.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mscrm4ever.blogspot.com/feeds/3311759097052915484/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3003801964503297388&amp;postID=3311759097052915484&amp;isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3003801964503297388/posts/default/3311759097052915484'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3003801964503297388/posts/default/3311759097052915484'/><link rel='alternate' type='text/html' href='http://mscrm4ever.blogspot.com/2008/11/creating-dynamic-picklist-control.html' title='Creating a Dynamic Picklist Control'/><author><name>Adi Katz</name><uri>http://www.blogger.com/profile/16783428437179825705</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3003801964503297388.post-848362783834845867</id><published>2008-11-12T17:22:00.005+02:00</published><updated>2008-11-12T17:32:20.406+02:00</updated><title type='text'>Creating Mini Form Sections</title><content type='html'>&lt;div&gt;&lt;/div&gt;&lt;br /&gt;The mini sections are a great way to create smaller logical areas inside a CRM section.&lt;br /&gt;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.&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;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.&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;The end result looks like this:&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 399px; height: 295px;border:1px solid black" src="http://3.bp.blogspot.com/_M-mdv3Tfarg/SRr2bkEATGI/AAAAAAAAAFc/FmRruzPSpFQ/s400/MINISections.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5267793667560590434" /&gt;&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;&lt;pre class="js" name="code"&gt;&lt;br /&gt;function OnCrmPageLoad()&lt;br /&gt;{&lt;br /&gt; /* Convert the text attribute ‘gi_ministep1’ to a mini section */&lt;br /&gt; CreateMiniSection("gi_ministep1");&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;function CreateMiniSection( ctrlId )&lt;br /&gt;{&lt;br /&gt; var Control = crmForm.all[ ctrlId ];&lt;br /&gt; if ( !Control ) return;&lt;br /&gt; Control.DataValue = " " + crmForm.GetLabel(Control);&lt;br /&gt; Control.Disabled  = true;&lt;br /&gt; Control.style.cssText = "font-weight:900;border:1px solid #94b2de;filter: progid:DXImageTransform.Microsoft.Gradient(GradientType=0,StartColorStr=#eff3ff,EndColorStr=#cedfff)";&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;OnCrmPageLoad();&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3003801964503297388-848362783834845867?l=mscrm4ever.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mscrm4ever.blogspot.com/feeds/848362783834845867/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3003801964503297388&amp;postID=848362783834845867&amp;isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3003801964503297388/posts/default/848362783834845867'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3003801964503297388/posts/default/848362783834845867'/><link rel='alternate' type='text/html' href='http://mscrm4ever.blogspot.com/2008/11/creating-mini-form-sections.html' title='Creating Mini Form Sections'/><author><name>Adi Katz</name><uri>http://www.blogger.com/profile/16783428437179825705</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_M-mdv3Tfarg/SRr2bkEATGI/AAAAAAAAAFc/FmRruzPSpFQ/s72-c/MINISections.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3003801964503297388.post-211791632890565650</id><published>2008-11-12T16:04:00.002+02:00</published><updated>2008-11-12T16:18:23.297+02:00</updated><title type='text'>Restricting access to IFRAME (read-only IFRAME)</title><content type='html'>&lt;div&gt;&lt;/div&gt;&lt;br /&gt;This is a complementary post to my original post about creating a &lt;a href="http://mscrm4ever.blogspot.com/2008/08/make-iframe-disabled-readonly.html"&gt;read-only iframe&lt;/a&gt;. 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.&lt;br /&gt;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.&lt;br /&gt;The following code tells the tail.&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;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.&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;&lt;pre class="js" name="code"&gt;&lt;br /&gt;function OnCrmPageLoad()&lt;br /&gt;{&lt;br /&gt;    RestrictAccessToIFrame("IFRAME_Test");&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;function RestrictAccessToIFrame(iframeId)&lt;br /&gt;{&lt;br /&gt; /* Reference the relevant IFRAME */&lt;br /&gt; var testIframe = document.all[iframeId];&lt;br /&gt; /* get the iframe parent container */&lt;br /&gt; var testIframeParent = testIframe.parentElement;&lt;br /&gt; &lt;br /&gt; /* if the IFRAME exist */&lt;br /&gt; if (testIframe)&lt;br /&gt; {&lt;br /&gt;  /* Save the Original Iframe HTML */&lt;br /&gt;  var testIframeHTML = testIframe.outerHTML;&lt;br /&gt;  /* Remove the Original IFRAME    */&lt;br /&gt;  testIframeParent.removeChild(testIframe);&lt;br /&gt;  /* Create a new IFRAME Instead   */&lt;br /&gt;  testIframe = document.createElement(testIframeHTML);&lt;br /&gt;  /* Add an IFRAME restriction attribute to the new IFRAME Element */&lt;br /&gt;  testIframe.setAttribute("SECURITY","restricted");&lt;br /&gt;  /* IF needed rebuild the IFRAME url */&lt;br /&gt;  testIframe.src = "/MicrosoftCRM/_root/homepage.aspx?etc=1";&lt;br /&gt;  /* Append the IFRAME to its original parent element */&lt;br /&gt;  testIframeParent.appendChild(testIframe);&lt;br /&gt; }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;OnCrmPageLoad();&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3003801964503297388-211791632890565650?l=mscrm4ever.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mscrm4ever.blogspot.com/feeds/211791632890565650/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3003801964503297388&amp;postID=211791632890565650&amp;isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3003801964503297388/posts/default/211791632890565650'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3003801964503297388/posts/default/211791632890565650'/><link rel='alternate' type='text/html' href='http://mscrm4ever.blogspot.com/2008/11/restricting-access-to-iframe-read-only.html' title='Restricting access to IFRAME (read-only IFRAME)'/><author><name>Adi Katz</name><uri>http://www.blogger.com/profile/16783428437179825705</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3003801964503297388.post-6341209619868993957</id><published>2008-11-11T07:26:00.035+02:00</published><updated>2010-05-05T07:55:30.253+03:00</updated><title type='text'>CRM 4.0 Field Level Security Wizard</title><content type='html'>&lt;div&gt;&lt;/div&gt;&lt;br /&gt;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. &lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;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. &lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;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 &lt;a href="mailto:support@gicrm.com"&gt;support@gicrm.com&lt;/a&gt;).&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;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.&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;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. &lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;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.&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;In order to rewind the video right click on the flash movie, click rewind and then play.&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;&lt;iframe src="http://www.upsite.co.il/uploaded/files/662_ff6a55fd97f77f36e988ad7b82e9ab53.swf" width="670" height="502" scrolling="yes"&gt;&lt;/iframe&gt;&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;&lt;u&gt;&lt;strong&gt;FLS Features and information:&lt;/strong&gt;&lt;/u&gt;&lt;br /&gt;1. &lt;u&gt;Supported Entities&lt;/u&gt; - All entity types (System, Customizable and Custom). &lt;br /&gt;    You can find the entire entity list inside CRM advanced find window.&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;2. &lt;u&gt;Supports both online and offline modes&lt;/u&gt;.&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;3. &lt;u&gt;Supports the following CRM modules&lt;/u&gt;:&lt;br /&gt;&amp;nbsp;&amp;nbsp;1. Entity Form (Can be extended using client side API)&lt;br /&gt;&amp;nbsp;&amp;nbsp;2. Entity Print Form &lt;br /&gt;&amp;nbsp;&amp;nbsp;3. Entity Views&lt;br /&gt;&amp;nbsp;&amp;nbsp;4. Entity Views Print page &lt;br /&gt;&amp;nbsp;&amp;nbsp;5. Static Export to Excel &lt;br /&gt;&amp;nbsp;&amp;nbsp;6. Advanced Find&lt;br /&gt;&amp;nbsp;&amp;nbsp;7. Mail Merge&lt;br /&gt;&amp;nbsp;&amp;nbsp;8. Merge&lt;br /&gt;&amp;nbsp;&amp;nbsp;9. Filtered View (For Reports) &lt;br /&gt;&amp;nbsp;&amp;nbsp;10. Entity Associated views&lt;br /&gt;&amp;nbsp;&amp;nbsp;11. Lookup views&lt;br /&gt;&amp;nbsp;&amp;nbsp;12. CRM Workflows&lt;br /&gt;&lt;br /&gt;4. &lt;u&gt;Supported Languages&lt;/u&gt; - FLS Customizations are available in all base languages – no language packs needed, But you’ll need to translate them your self.&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;5. &lt;u&gt;Supported Layouts&lt;/u&gt; - Dir RTL ( Right to Left ) and LTR ( Left to Right ) layouts&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;6. &lt;u&gt;Supports the following field level security modes&lt;/u&gt;:&lt;br /&gt;&lt;nobr&gt;&amp;nbsp;&amp;nbsp;1. &lt;img style="cursor:pointer; cursor:hand;width: 16px; height: 16px;" src="http://4.bp.blogspot.com/_M-mdv3Tfarg/SRmWp7wxfkI/AAAAAAAAAEY/U_D_v4hGOu4/s200/ico_18_role_x.gif" border="0" alt=""id="BLOGGER_PHOTO_ID_5267406886347570754" /&gt;&amp;nbsp;&lt;strong&gt;Default &lt;/strong&gt;– Field is not set&lt;/nobr&gt;&lt;br /&gt;&lt;nobr&gt;&amp;nbsp;&amp;nbsp;2. &lt;img style="cursor:pointer; cursor:hand;width: 16px; height: 16px;" src="http://1.bp.blogspot.com/_M-mdv3Tfarg/SRmWh6jyjHI/AAAAAAAAAD4/jF6FJMwWk3U/s200/ico_18_role_b.gif" border="0" alt=""id="BLOGGER_PHOTO_ID_5267406748585725042" /&gt;&amp;nbsp;&lt;strong&gt;Hidden &lt;/strong&gt;– Field is hidden on both crm form and views &lt;/nobr&gt;&lt;br /&gt;&lt;nobr&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;a. &lt;img style="cursor:pointer; cursor:hand;width: 16px; height: 16px;" src="http://4.bp.blogspot.com/_M-mdv3Tfarg/SRmWh_HpG_I/AAAAAAAAADw/EX_P5Ipqf2Y/s200/ico_18_4418.gif" border="0" alt=""id="BLOGGER_PHOTO_ID_5267406749809843186" /&gt;&amp;nbsp;Keep layout – the field space is kept &lt;/nobr&gt;&lt;br /&gt;&lt;nobr&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;b. &lt;img style="cursor:pointer; cursor:hand;width: 16px; height: 16px;" src="http://4.bp.blogspot.com/_M-mdv3Tfarg/SRmWh3IR2cI/AAAAAAAAADo/x5AD4n0pCzQ/s200/ico_18_4417.gif" border="0" alt=""id="BLOGGER_PHOTO_ID_5267406747665029570" /&gt;&amp;nbsp;Collapse layout – the field space is collapsed for better presentation &lt;/nobr&gt;&lt;br /&gt;&lt;nobr&gt;&amp;nbsp;&amp;nbsp;3. &lt;img style="cursor:pointer; cursor:hand;width: 16px; height: 16px;" src="http://4.bp.blogspot.com/_M-mdv3Tfarg/SRmWp8XZibI/AAAAAAAAAEQ/3gVanDqpOJ4/s200/ico_18_role_l.gif" border="0" alt=""id="BLOGGER_PHOTO_ID_5267406886509578674" /&gt;&amp;nbsp;&lt;strong&gt;Missing &lt;/strong&gt;– Field is disabled , the user can not see the data &lt;/nobr&gt;&lt;br /&gt;&lt;nobr&gt;&amp;nbsp;&amp;nbsp;4. &lt;img style="cursor:pointer; cursor:hand;width: 16px; height: 16px;" src="http://2.bp.blogspot.com/_M-mdv3Tfarg/SRmWiKaS8dI/AAAAAAAAAEA/NDQrf2VCUvg/s200/ico_18_role_d.gif" border="0" alt=""id="BLOGGER_PHOTO_ID_5267406752840872402" /&gt;&amp;nbsp;&lt;strong&gt;Disabled &lt;/strong&gt;– Field is disabled , the user can see the data &lt;/nobr&gt;&lt;br /&gt;&lt;nobr&gt;&amp;nbsp;&amp;nbsp;5. &lt;img style="cursor:pointer; cursor:hand;width: 16px; height: 16px;" src="http://2.bp.blogspot.com/_M-mdv3Tfarg/SRmWiIHCmdI/AAAAAAAAAEI/TUxRRsoDsug/s200/ico_18_role_g.gif" border="0" alt=""id="BLOGGER_PHOTO_ID_5267406752223238610" /&gt;&amp;nbsp;&lt;strong&gt;Enabled &lt;/strong&gt;– Override existing settings, the user has full permissions on the field. &lt;/nobr&gt; &lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;7. &lt;u&gt;Supports “Formless” Entities&lt;/u&gt; – Entities that do not have a form like activity&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;8. &lt;u&gt;Supports Security Hierarchy:&lt;/u&gt; &lt;div&gt;&lt;/div&gt;&lt;br /&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:left;cursor:pointer; cursor:hand;width: 395px; height: 107px;border:1px solid black" src="http://4.bp.blogspot.com/_M-mdv3Tfarg/SRpzUD5r_9I/AAAAAAAAAE4/d068-ZYasM8/s400/FLS_SecuritySettings.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5267649502645059538" /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;1. &lt;strong&gt;Business Unit level:&lt;/strong&gt; Creation of FLS Templates that affect the entire business unit.&lt;br /&gt;&amp;nbsp;&amp;nbsp;2. &lt;strong&gt;Role Level:&lt;/strong&gt; Inheritable security roles with an option to extend or override business unit settings&lt;br /&gt;&amp;nbsp;&amp;nbsp;3. &lt;strong&gt;User Level:&lt;/strong&gt; Inheritable User settings with the option to create exceptions for users within the same role&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;9. &lt;u&gt;Fields View - Intelligent Orientation.&lt;/u&gt; &lt;div&gt;&lt;/div&gt;&lt;br /&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:left;cursor:pointer; cursor:hand;width: 392px; height: 316px;border:1px solid black" src="http://1.bp.blogspot.com/_M-mdv3Tfarg/SRpzUAWU64I/AAAAAAAAAEw/5r8F4KzTsjM/s400/FLS_FieldsControl.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5267649501691440002" /&gt;&lt;br /&gt;&lt;strong&gt;The user can filter by:&lt;/strong&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;1. &lt;strong&gt;Field Type:&lt;/strong&gt; Presents all available field types&lt;br /&gt;&amp;nbsp;&amp;nbsp;2. &lt;strong&gt;Existing:&lt;/strong&gt; Presents existing Settings&lt;br /&gt;&amp;nbsp;&amp;nbsp;3. &lt;strong&gt;Placement:&lt;/strong&gt; Presents fields that exist ON and OFF the CRM Form &lt;br /&gt;&amp;nbsp;&amp;nbsp;4. &lt;strong&gt;Tabs:&lt;/strong&gt; Presents All Fields Categorized by Tab Name&lt;br /&gt;&amp;nbsp;&amp;nbsp;5. &lt;strong&gt;Sections:&lt;/strong&gt; Presents All Fields Categorized by Section Name&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;The wizard also supports Internet Facing Deployment (IFD) and IE8&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;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. &lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;The wizard is &lt;a href="http://www.gicrm.com/?categoryId=29264&amp;itemId=63714"&gt;now available online&lt;/a&gt;. I’m sure you’ll find it worthwhile. Feel free to comment here or send your questions to &lt;a href="mailto:support@gicrm.com"&gt;support@gicrm.com&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3003801964503297388-6341209619868993957?l=mscrm4ever.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mscrm4ever.blogspot.com/feeds/6341209619868993957/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3003801964503297388&amp;postID=6341209619868993957&amp;isPopup=true' title='12 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3003801964503297388/posts/default/6341209619868993957'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3003801964503297388/posts/default/6341209619868993957'/><link rel='alternate' type='text/html' href='http://mscrm4ever.blogspot.com/2008/11/crm-40-field-level-security-wizard.html' title='CRM 4.0 Field Level Security Wizard'/><author><name>Adi Katz</name><uri>http://www.blogger.com/profile/16783428437179825705</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_M-mdv3Tfarg/SRmWp7wxfkI/AAAAAAAAAEY/U_D_v4hGOu4/s72-c/ico_18_role_x.gif' height='72' width='72'/><thr:total>12</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3003801964503297388.post-4349760959880860380</id><published>2008-10-08T11:19:00.010+02:00</published><updated>2008-10-10T12:31:43.677+02:00</updated><title type='text'>Objectizing Ajax (XML) results</title><content type='html'>&lt;div&gt;&lt;/div&gt;&lt;br /&gt;&lt;a href="http://4.bp.blogspot.com/_M-mdv3Tfarg/SOx9j5pylcI/AAAAAAAAAC8/HwBhZh06Eoc/s1600-h/objectizer.jpg"&gt;&lt;img id="BLOGGER_PHOTO_ID_5254712920959522242" style="BORDER-RIGHT: black 1px solid; BORDER-TOP: black 1px solid; DISPLAY: block; MARGIN: 0px auto 10px; BORDER-LEFT: black 1px solid; CURSOR: hand; BORDER-BOTTOM: black 1px solid; TEXT-ALIGN: center" alt="" src="http://4.bp.blogspot.com/_M-mdv3Tfarg/SOx9j5pylcI/AAAAAAAAAC8/HwBhZh06Eoc/s400/objectizer.jpg" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Definition: Objectize - To make an object of; to regard as an object; to place in the position of an object.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Objectizing XML documents is a very common concept incorporated into dot.net server side framework. Whether you’re creating a web service proxy or desterilizing objects what you’re doing is objectizing xml. Wouldn’t it be the coolest thing if you could do the same on the client side, regard xml results coming back from Ajax requests as objects and manipulating then with ease. So instead on dealing with Xml DOM and writing code that looks like this:&lt;br /&gt;&lt;br /&gt;&lt;pre class="js" name="code"&gt;&lt;br /&gt;Root.childNodes[0].childNodes[0].firstChild.attributes[“name”]&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;You can actually write:&lt;br /&gt;&lt;br /&gt;&lt;pre class="js" name="code"&gt;&lt;br /&gt;Root.resultset.result.businessunitid.name&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;While creating my own objectizer I came across &lt;a href="http://www.kawa.net/works/js/xml/objtree-e.html"&gt;Yusuke Kawasaki ObjTree.js&lt;/a&gt; free class library and decided to check it out.&lt;br /&gt;I modified it slightly to support ms resulting xml schema and made it a bit leaner to so only the objectizing part is included.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;The OnCrmPageLoad includes a very simple example of how to create the ObjTree object, passing it an xml document and receiving an object representation of that document.The post uses the “&lt;a href="http://mscrm4ever.blogspot.com/2008/09/ajax-using-fetch-message.html"&gt;Ajax using Fetch Message&lt;/a&gt;” to demonstrate the usage.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;&lt;u&gt;Part of Yusuke Kawasaki ObjTree.js Library&lt;/u&gt;&lt;/b&gt;&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;&lt;pre class="js" name="code"&gt;&lt;br /&gt;if ( typeof(XML) == 'undefined' ) XML = function() {};&lt;br /&gt;&lt;br /&gt;//  constructor&lt;br /&gt;&lt;br /&gt;XML.ObjTree = function () {&lt;br /&gt;    return this;&lt;br /&gt;};&lt;br /&gt;&lt;br /&gt;//  object prototype&lt;br /&gt;XML.ObjTree.prototype.xmlDecl = '&amp;lt;?xml version="1.0" encoding="UTF-8" ?&amp;gt;\n';&lt;br /&gt;XML.ObjTree.prototype.attr_prefix = '';&lt;br /&gt;&lt;br /&gt;//  method: parseXML( xmlsource )&lt;br /&gt;XML.ObjTree.prototype.parseXML = function ( xml ) {&lt;br /&gt;    var root;&lt;br /&gt;    if ( window.ActiveXObject ) {&lt;br /&gt;        xmldom = new ActiveXObject('Microsoft.XMLDOM');&lt;br /&gt;        xmldom.async = false;&lt;br /&gt;        xmldom.loadXML( xml );&lt;br /&gt;        root = xmldom.documentElement;&lt;br /&gt;    }&lt;br /&gt;    if ( ! root ) return;&lt;br /&gt;    return this.parseDOM( root );&lt;br /&gt;};&lt;br /&gt;&lt;br /&gt;//  method: parseDOM( documentroot )&lt;br /&gt;XML.ObjTree.prototype.parseDOM = function ( root ) {&lt;br /&gt;    if ( ! root ) return;&lt;br /&gt;&lt;br /&gt;    this.__force_array = {};&lt;br /&gt;    if ( this.force_array ) {&lt;br /&gt;        for( var i=0; i&amp;lt;this.force_array.length; i++ ) {&lt;br /&gt;            this.__force_array[this.force_array[i]] = 1;&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    var json = this.parseElement( root );   // parse root node&lt;br /&gt;    if ( this.__force_array[root.nodeName] ) {&lt;br /&gt;        json = [ json ];&lt;br /&gt;    }&lt;br /&gt;    if ( root.nodeType != 11 ) {            // DOCUMENT_FRAGMENT_NODE&lt;br /&gt;        var tmp = {};&lt;br /&gt;        tmp[root.nodeName] = json;          // root nodeName&lt;br /&gt;        json = tmp;&lt;br /&gt;    }&lt;br /&gt;    return json;&lt;br /&gt;};&lt;br /&gt;&lt;br /&gt;//  method: parseElement( element )&lt;br /&gt;XML.ObjTree.prototype.parseElement = function ( elem ) {&lt;br /&gt;    //  COMMENT_NODE&lt;br /&gt;    if ( elem.nodeType == 7 ) {&lt;br /&gt;        return;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    //  TEXT_NODE CDATA_SECTION_NODE&lt;br /&gt;    if ( elem.nodeType == 3 || elem.nodeType == 4 ) {&lt;br /&gt;        var bool = elem.nodeValue.match( /[^\x00-\x20]/ );&lt;br /&gt;        if ( bool == null ) return;     // ignore white spaces&lt;br /&gt;        return elem.nodeValue;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    var retval;&lt;br /&gt;    var cnt = {};&lt;br /&gt;&lt;br /&gt;    //  parse attributes&lt;br /&gt;    if ( elem.attributes &amp;amp;&amp;amp; elem.attributes.length ) {&lt;br /&gt;        retval = {};&lt;br /&gt;        for ( var i=0; i&amp;lt;elem.attributes.length; i++ ) {&lt;br /&gt;            var key = elem.attributes[i].nodeName;&lt;br /&gt;            if ( typeof(key) != "string" ) continue;&lt;br /&gt;            var val = elem.attributes[i].nodeValue;&lt;br /&gt;            if ( ! val ) continue;&lt;br /&gt;            key = this.attr_prefix + key;&lt;br /&gt;            if ( typeof(cnt[key]) == "undefined" ) cnt[key] = 0;&lt;br /&gt;            cnt[key] ++;&lt;br /&gt;            this.addNode( retval, key, cnt[key], val );&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    //  parse child nodes (recursive)&lt;br /&gt;    if ( elem.childNodes &amp;amp;&amp;amp; elem.childNodes.length ) {&lt;br /&gt;        var textonly = true;&lt;br /&gt;        if ( retval ) textonly = false;        // some attributes exists&lt;br /&gt;        for ( var i=0; i&amp;lt;elem.childNodes.length &amp;amp;&amp;amp; textonly; i++ ) {&lt;br /&gt;            var ntype = elem.childNodes[i].nodeType;&lt;br /&gt;            if ( ntype == 3 || ntype == 4 ) continue;&lt;br /&gt;            textonly = false;&lt;br /&gt;        }&lt;br /&gt;        if ( textonly ) {&lt;br /&gt;            if ( ! retval ) retval = "";&lt;br /&gt;            for ( var i=0; i&amp;lt;elem.childNodes.length; i++ ) {&lt;br /&gt;                retval += elem.childNodes[i].nodeValue;&lt;br /&gt;            }&lt;br /&gt;        } else {&lt;br /&gt;            if ( ! retval ) retval = {};&lt;br /&gt;            for ( var i=0; i&amp;lt;elem.childNodes.length; i++ ) {&lt;br /&gt;                var key = elem.childNodes[i].nodeName.replace("#","");&lt;br /&gt;                if ( typeof(key) != "string" ) continue;&lt;br /&gt;                var val = this.parseElement( elem.childNodes[i] );&lt;br /&gt;                if ( ! val ) continue;&lt;br /&gt;                if ( typeof(cnt[key]) == "undefined" ) cnt[key] = 0;&lt;br /&gt;                cnt[key] ++;&lt;br /&gt;                this.addNode( retval, key, cnt[key], val );&lt;br /&gt;            }&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;    return retval;&lt;br /&gt;};&lt;br /&gt;&lt;br /&gt;//  method: addNode( hash, key, count, value )&lt;br /&gt;XML.ObjTree.prototype.addNode = function ( hash, key, cnts, val ) {&lt;br /&gt; key = this.key_qualify(key);&lt;br /&gt;    if ( this.__force_array[key] ) {&lt;br /&gt;        if ( cnts == 1 ) hash[key] = [];&lt;br /&gt;        hash[key][hash[key].length] = val;      // push&lt;br /&gt;    } else if ( cnts == 1 ) {                   // 1st sibling&lt;br /&gt;        hash[key] = val;&lt;br /&gt;    } else if ( cnts == 2 ) {                   // 2nd sibling&lt;br /&gt;        hash[key] = [ hash[key], val ];&lt;br /&gt;    } else {                                    // 3rd sibling and more&lt;br /&gt;        hash[key][hash[key].length] = val;&lt;br /&gt;    }&lt;br /&gt;};&lt;br /&gt;&lt;br /&gt;XML.ObjTree.prototype.key_qualify = function( key ){&lt;br /&gt; return key.replace(/\W/gi,"");&lt;br /&gt;}&lt;br /&gt;//  method: xml_escape( text )&lt;br /&gt;XML.ObjTree.prototype.xml_escape = function ( text ) {&lt;br /&gt;    return String(text).replace(/&amp;amp;/g,'&amp;amp;amp;').replace(/&amp;lt;/g,'&amp;amp;lt;').replace(/&amp;gt;/g,'&amp;amp;gt;').replace(/"/g,'&amp;amp;quot;');&lt;br /&gt;};&lt;br /&gt;&lt;br /&gt;/*&lt;br /&gt;Copyright (c) 2005-2006 Yusuke Kawasaki. All rights reserved.&lt;br /&gt;This program is free software; you can redistribute it and/or&lt;br /&gt;modify it under the Artistic license. Or whatever license I choose,&lt;br /&gt;which I will do instead of keeping this documentation like it is.&lt;br /&gt;*/&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;&lt;u&gt;The original xml result looks like this&lt;/u&gt;&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre class='xml' name='code'&gt;&lt;br /&gt;&amp;lt;resultset morerecords=\"0\" paging-cookie=\"&amp;amp;lt;cookie page=&amp;amp;quot;1&amp;amp;quot;&amp;amp;gt;&amp;amp;lt;fullname last=&amp;amp;quot;SYSTEM&amp;amp;quot; first=&amp;amp;quot;INTEGRATION&amp;amp;quot; /&amp;amp;gt;&amp;amp;lt;systemuserid last=&amp;amp;quot;{D874E288-2C8C-43D5-AEBA-5404888BC185}&amp;amp;quot; first=&amp;amp;quot;{B2C53269-CFF5-4F26-A4E5-669284EA6E96}&amp;amp;quot; /&amp;amp;gt;&amp;amp;lt;/cookie&amp;amp;gt;\"&amp;gt;&lt;br /&gt;  &amp;lt;result&amp;gt;&lt;br /&gt;     &amp;lt;fullname&amp;gt;INTEGRATION&amp;lt;/fullname&amp;gt;&lt;br /&gt;     &amp;lt;businessunitid dsc=\"0\" name=\"MicrosoftCRM\"&amp;gt;&lt;br /&gt;          {80E4E0DF-65C1-DC11-B67A-0003FFBB057D}&lt;br /&gt;     &amp;lt;/businessunitid&amp;gt;&lt;br /&gt;     &amp;lt;systemuserid&amp;gt;{B2C53269-CFF5-4F26-A4E5-669284EA6E96}&amp;lt;/systemuserid&amp;gt;&lt;br /&gt;  &amp;lt;/result&amp;gt;&lt;br /&gt;  &amp;lt;result&amp;gt;&lt;br /&gt;    &amp;lt;fullname&amp;gt;LitwareInc Administrator&amp;lt;/fullname&amp;gt;&lt;br /&gt;    &amp;lt;businessunitid dsc=\"0\" name=\"MicrosoftCRM\"&amp;gt;&lt;br /&gt;      {80E4E0DF-65C1-DC11-B67A-0003FFBB057D}&lt;br /&gt;    &amp;lt;/businessunitid&amp;gt;&lt;br /&gt;    &amp;lt;systemuserid&amp;gt;{9BF5E0DF-65C1-DC11-B67A-0003FFBB057D}&amp;lt;/systemuserid&amp;gt;&lt;br /&gt;    &amp;lt;/result&amp;gt;&lt;br /&gt;&amp;lt;/resultset&amp;gt;&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;&lt;u&gt;Usage Example:&lt;/u&gt;&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre class="js" name="code"&gt;&lt;br /&gt;&lt;br /&gt;function OnCrmPageLoad()&lt;br /&gt;{&lt;br /&gt;/* Get All Users */&lt;br /&gt; var fetchXml = '&amp;lt;fetch version="1.0" output-format="xml-platform" mapping="logical" distinct="false"&amp;gt;';&lt;br /&gt;  fetchXml += '&amp;lt;entity name="systemuser"&amp;gt;';&lt;br /&gt;  fetchXml += '&amp;lt;attribute name="fullname"/&amp;gt;';&lt;br /&gt;  fetchXml += '&amp;lt;attribute name="businessunitid"/&amp;gt;';&lt;br /&gt;  fetchXml += '&amp;lt;attribute name="title"/&amp;gt;';&lt;br /&gt;  fetchXml += '&amp;lt;attribute name="address1_telephone1"/&amp;gt;';&lt;br /&gt;  fetchXml += '&amp;lt;attribute name="systemuserid"/&amp;gt;';&lt;br /&gt;  fetchXml += '&amp;lt;order attribute="fullname" descending="false"/&amp;gt;';&lt;br /&gt;  fetchXml += '&amp;lt;/entity&amp;gt;';&lt;br /&gt;  fetchXml += '&amp;lt;/fetch&amp;gt;';&lt;br /&gt; &lt;br /&gt;  /* Make the fetch and retrieve xml result */&lt;br /&gt;  var resxml = Fetch(fetchXml);&lt;br /&gt;  /* Create an ObjTree Object */&lt;br /&gt;  var xotree = new XML.ObjTree();&lt;br /&gt;  /* Objectize xml result  */&lt;br /&gt;  var tree = xotree.parseXML( resxml );       &lt;br /&gt;  &lt;br /&gt;  /*&lt;br /&gt;  if the original node contains only data then the property is treated as string&lt;br /&gt;  */&lt;br /&gt;  alert( tree.resultset.result[0].fullname )&lt;br /&gt;  /*&lt;br /&gt;  if the original node contains attributes or children then you should treat it as &lt;br /&gt;  object and reference its properties e.g. object.text and object.propertyName&lt;br /&gt;  */&lt;br /&gt;  alert( tree.resultset.result[0].businessunitid.text ); //GUID&lt;br /&gt;  alert( tree.resultset.result[0].businessunitid.name ); //OrgName&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;function Fetch( xml )&lt;br /&gt;{&lt;br /&gt; var Xml = "&amp;lt;soap:Envelope xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\"&amp;gt;"&lt;br /&gt;  Xml += GenerateAuthenticationHeader()&lt;br /&gt;  Xml += "&amp;lt;soap:Body&amp;gt;";&lt;br /&gt;  Xml += "&amp;lt;Fetch xmlns=\"http://schemas.microsoft.com/crm/2007/WebServices\"&amp;gt;";&lt;br /&gt;  Xml += "&amp;lt;fetchXml&amp;gt;";&lt;br /&gt;  Xml += _HtmlEncode(xml); // Microsoft _HtmlEncode function&lt;br /&gt;  Xml += "&amp;lt;/fetchXml&amp;gt;";&lt;br /&gt;  Xml += "&amp;lt;/Fetch&amp;gt;";&lt;br /&gt;  Xml += "&amp;lt;/soap:Body&amp;gt;";&lt;br /&gt;  Xml += "&amp;lt;/soap:Envelope&amp;gt;";&lt;br /&gt;&lt;br /&gt; var XmlHttp = CreateXmlHttp(); // Microsot CreateXmlHttp function&lt;br /&gt;  XmlHttp.open("POST", "/mscrmservices/2007/crmservice.asmx", false ); //Sync Request&lt;br /&gt;  XmlHttp.setRequestHeader("Content-Type", "text/xml; charset=utf-8");&lt;br /&gt;  XmlHttp.setRequestHeader("SOAPAction", "http://schemas.microsoft.com/crm/2007/WebServices/Fetch");&lt;br /&gt;  XmlHttp.send(Xml);&lt;br /&gt;&lt;br /&gt; return XmlHttp.responseXML.text&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;OnCrmPageLoad();&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3003801964503297388-4349760959880860380?l=mscrm4ever.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mscrm4ever.blogspot.com/feeds/4349760959880860380/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3003801964503297388&amp;postID=4349760959880860380&amp;isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3003801964503297388/posts/default/4349760959880860380'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3003801964503297388/posts/default/4349760959880860380'/><link rel='alternate' type='text/html' href='http://mscrm4ever.blogspot.com/2008/10/objectizing-ajax-xml-results.html' title='Objectizing Ajax (XML) results'/><author><name>Adi Katz</name><uri>http://www.blogger.com/profile/16783428437179825705</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_M-mdv3Tfarg/SOx9j5pylcI/AAAAAAAAAC8/HwBhZh06Eoc/s72-c/objectizer.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3003801964503297388.post-2189713554980148927</id><published>2008-10-03T21:57:00.006+03:00</published><updated>2008-10-03T22:09:22.725+03:00</updated><title type='text'>Playing with Notes</title><content type='html'>&lt;div&gt;&lt;/div&gt;&lt;br /&gt;This post is more of an example about how one can utilize the “&lt;a href="http://mscrm4ever.blogspot.com/2008/09/display-fetch-in-iframe.html"&gt;Show Fetch in IFRAME&lt;/a&gt;” post in order to consolidate customer information in a focal location. &lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;Consider a scenario where a contact center service rep needs to see the customer’s related notes (annotations) as part of the case service workflow. In order to achieve that using out of box functionality the rep needs to open the customer’s account page, navigate to the notes tab and select the desired document out of all the notes (text and annotations). Most customers find it an unacceptable behavior as do I.&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;In order to transform the above behavior into a “One Click Process” procedure I’ve added a new IFRAME to the case entity called IFRAME_relatednotes and attached the iframe results to the customer’s lookup onchange event. Each time the customer lookup changes the iframe is filled with the selected customer annotations.&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;Follow the “&lt;a href="http://mscrm4ever.blogspot.com/2008/09/display-fetch-in-iframe.html"&gt;Display Fetch in IFRAME&lt;/a&gt;” post in order to assign valid FetchViewer Notes Parameters.I’ve also added the FetchViewer Class functionally as part of this example for the sake of completeness.&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;&lt;a href="http://1.bp.blogspot.com/_M-mdv3Tfarg/SOZrpthKN9I/AAAAAAAAACs/dadmho92-a4/s1600-h/customerrelatednotes.jpg"&gt;&lt;img id="BLOGGER_PHOTO_ID_5253004379711748050" style="DISPLAY: block; MARGIN: 0px auto 10px; CURSOR: hand;border:1px solid black; TEXT-ALIGN: center" alt="" src="http://1.bp.blogspot.com/_M-mdv3Tfarg/SOZrpthKN9I/AAAAAAAAACs/dadmho92-a4/s400/customerrelatednotes.jpg" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;&lt;a href="http://3.bp.blogspot.com/_M-mdv3Tfarg/SOZruhgAaGI/AAAAAAAAAC0/3SO5Cm-thno/s1600-h/customerrelatednotes1.jpg"&gt;&lt;img id="BLOGGER_PHOTO_ID_5253004462385031266" style="DISPLAY: block; MARGIN: 0px auto 10px;border:1px solid black; CURSOR: hand; TEXT-ALIGN: center" alt="" src="http://3.bp.blogspot.com/_M-mdv3Tfarg/SOZruhgAaGI/AAAAAAAAAC0/3SO5Cm-thno/s400/customerrelatednotes1.jpg" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;&lt;pre class='js' name='code'&gt;&lt;br /&gt;var customerLookup;&lt;br /&gt;function OnCrmPageLoad()&lt;br /&gt;{&lt;br /&gt;  customerLookup = crmForm.all.customerid;&lt;br /&gt;  &lt;br /&gt;  window.CustomerRelatedNotes = new FetchViewer("IFRAME_relatednotes"); &lt;br /&gt;  CustomerRelatedNotes.LayoutXml = getLayoutXml();&lt;br /&gt;  CustomerRelatedNotes.Entity    = "annotation";&lt;br /&gt;  CustomerRelatedNotes.QueryId   = "{DDDFF6AE-2F52-4640-B2BB-2BA59DA0777C}";&lt;br /&gt;  //First Time&lt;br /&gt;  SetRelatedNotesFetchXml();&lt;br /&gt;  CustomerRelatedNotes.RegisterOnTab(0); //IFRAME ON THE DEFAULT TAB&lt;br /&gt;  //Consequent lookup selections&lt;br /&gt;  crmForm.all.customerid.attachEvent( "onchange" , function(){ &lt;br /&gt;  SetRelatedNotesFetchXml();&lt;br /&gt;  CustomerRelatedNotes.Refresh();&lt;br /&gt;  });&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;/*&lt;br /&gt;The function construct a valid fetchexml depending on the customer selection taking the customer type into account.&lt;br /&gt;If the customer lookup does not contain data then the fetch uses an empty GUID&lt;br /&gt;*/&lt;br /&gt;function SetRelatedNotesFetchXml(){&lt;br /&gt;&lt;br /&gt; var customerGuid = "{00000000-0000-0000-0000-000000000000}"&lt;br /&gt; var customerType = "account" &lt;br /&gt; &lt;br /&gt; if( customerLookup.DataValue != null )&lt;br /&gt; { &lt;br /&gt;  customerGuid = customerLookup.DataValue[0].id;&lt;br /&gt;  customerType = customerLookup.DataValue[0].typename;&lt;br /&gt; } &lt;br /&gt; &lt;br /&gt; var fetchXml  = '&amp;lt;fetch version="1.0" output-format="xml-platform" mapping="logical" distinct="true"&amp;gt;';&lt;br /&gt;     fetchXml += '&amp;lt;entity name="annotation"&amp;gt;';&lt;br /&gt;     fetchXml += '&amp;lt;attribute name="subject"/&amp;gt;';&lt;br /&gt;     fetchXml += '&amp;lt;attribute name="notetext"/&amp;gt;';&lt;br /&gt;     fetchXml += '&amp;lt;attribute name="filename"/&amp;gt;';&lt;br /&gt;     fetchXml += '&amp;lt;attribute name="annotationid"/&amp;gt;';&lt;br /&gt;     fetchXml += '&amp;lt;order attribute="subject" descending="false"/&amp;gt;';&lt;br /&gt;     fetchXml += '&amp;lt;filter type="and"&amp;gt;';&lt;br /&gt;     fetchXml += '&amp;lt;condition attribute="isdocument" operator="eq" value="1"/&amp;gt;';&lt;br /&gt;     fetchXml += '&amp;lt;/filter&amp;gt;';&lt;br /&gt;     fetchXml += '&amp;lt;link-entity name="' + customerType + '" from="' + customerType + 'id" to="objectid" alias="aa"&amp;gt;';&lt;br /&gt;     fetchXml += '&amp;lt;filter type="and"&amp;gt;';&lt;br /&gt;     fetchXml += '&amp;lt;condition attribute="' + customerType + 'id" operator="eq" uitype="' + customerType + '" value="' + customerGuid + '"/&amp;gt;';&lt;br /&gt;     fetchXml += '&amp;lt;/filter&amp;gt;';&lt;br /&gt;     fetchXml += '&amp;lt;/link-entity&amp;gt;';&lt;br /&gt;     fetchXml += '&amp;lt;/entity&amp;gt;&amp;lt;/fetch&amp;gt;';&lt;br /&gt; &lt;br /&gt; CustomerRelatedNotes.FetchXml = fetchXml;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;function getLayoutXml(){&lt;br /&gt; return '&amp;lt;grid name="resultset" object="5" jump="" select="1" icon="1" preview="1"&amp;gt;&amp;lt;row name="result" id="annotationid"&amp;gt;&amp;lt;cell name="subject" width="200" /&amp;gt;&amp;lt;cell name="notetext" width="200" /&amp;gt;&amp;lt;cell name="filename" width="80" /&amp;gt;&amp;lt;/row&amp;gt;&amp;lt;/grid&amp;gt;';&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;function FetchViewer( iframeId )&lt;br /&gt;{&lt;br /&gt; var Instance = this;&lt;br /&gt; var vDynamicForm;&lt;br /&gt; var m_iframeTab;&lt;br /&gt; var m_iframeDoc;&lt;br /&gt;  &lt;br /&gt;  Instance.Entity    = "";&lt;br /&gt;  Instance.Iframe    = null;&lt;br /&gt;  Instance.FetchXml  = "";&lt;br /&gt;  Instance.QueryId   = "";&lt;br /&gt;  Instance.LayoutXml = "";&lt;br /&gt;  &lt;br /&gt;  Instance.RegisterOnTab  = function( tabIndex )&lt;br /&gt;  { &lt;br /&gt;   Instance.Iframe = document.getElementById( iframeId );  &lt;br /&gt;  &lt;br /&gt;   if( !Instance.Iframe )&lt;br /&gt;    return alert( "Iframe " + iframeId + " is undefined" );&lt;br /&gt;&lt;br /&gt;   m_iframeDoc = getIframeDocument();&lt;br /&gt;   var loadingGifHTML  = "&amp;lt;table height='100%' width='100%' style='cursor:wait'&amp;gt;";&lt;br /&gt;    loadingGifHTML += "&amp;lt;tr&amp;gt;";&lt;br /&gt;    loadingGifHTML += "&amp;lt;td valign='middle' align='center'&amp;gt;";&lt;br /&gt;    loadingGifHTML += "&amp;lt;img alt='' src='/_imgs/AdvFind/progress.gif'/&amp;gt;";&lt;br /&gt;    loadingGifHTML += "&amp;lt;div/&amp;gt;&amp;lt;b&amp;gt;Loading View...&amp;lt;/b&amp;gt;";&lt;br /&gt;    loadingGifHTML += "&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&amp;lt;/table&amp;gt;";&lt;br /&gt;   m_iframeDoc.body.innerHTML = loadingGifHTML;&lt;br /&gt;&lt;br /&gt;   if( parseInt( "0" + tabIndex ) == 0 ) Instance.Refresh();&lt;br /&gt;   else Instance.Iframe.attachEvent( "onreadystatechange" , RefreshOnReadyStateChange ); &lt;br /&gt; }&lt;br /&gt; &lt;br /&gt; function RefreshOnReadyStateChange()&lt;br /&gt; {&lt;br /&gt; if( Instance.Iframe.readyState != 'complete' )&lt;br /&gt;  return;&lt;br /&gt;  &lt;br /&gt; Instance.Refresh();&lt;br /&gt; }&lt;br /&gt;  &lt;br /&gt; Instance.Refresh = function()&lt;br /&gt; {&lt;br /&gt; if( !Instance.Iframe )&lt;br /&gt;  return alert( "Iframe " + iframeId + " is undefined" );&lt;br /&gt;  &lt;br /&gt; m_iframeDoc = getIframeDocument();  &lt;br /&gt;  &lt;br /&gt; Instance.Iframe.detachEvent( "onreadystatechange" , RefreshOnReadyStateChange );&lt;br /&gt;     &lt;br /&gt; var create  = m_iframeDoc.createElement;&lt;br /&gt; var append1 = m_iframeDoc.appendChild; &lt;br /&gt;  vDynamicForm = create("&amp;lt;FORM name='vDynamicForm' method='post'&amp;gt;");&lt;br /&gt;   &lt;br /&gt; var append2 = vDynamicForm.appendChild;&lt;br /&gt;  append2(create("&amp;lt;INPUT type='hidden' name='FetchXml'&amp;gt;"));&lt;br /&gt;  append2(create("&amp;lt;INPUT type='hidden' name='LayoutXml'&amp;gt;"));&lt;br /&gt;  append2(create("&amp;lt;INPUT type='hidden' name='EntityName'&amp;gt;"));&lt;br /&gt;  append2(create("&amp;lt;INPUT type='hidden' name='DefaultAdvFindViewId'&amp;gt;"));&lt;br /&gt;  append2(create("&amp;lt;INPUT type='hidden' name='ViewType'&amp;gt;"));&lt;br /&gt;  append1( vDynamicForm );&lt;br /&gt;&lt;br /&gt;  vDynamicForm.action = "/" + ORG_UNIQUE_NAME + "/AdvancedFind/fetchData.aspx";&lt;br /&gt;  vDynamicForm.FetchXml.value   = Instance.FetchXml;&lt;br /&gt;  vDynamicForm.LayoutXml.value  = Instance.LayoutXml;&lt;br /&gt;  vDynamicForm.EntityName.value = Instance.Entity;&lt;br /&gt;  vDynamicForm.DefaultAdvFindViewId.value = Instance.QueryId;&lt;br /&gt;  vDynamicForm.ViewType.value = 1039;&lt;br /&gt;  vDynamicForm.submit();&lt;br /&gt;   &lt;br /&gt;  Instance.Iframe.attachEvent( "onreadystatechange" , OnViewReady );&lt;br /&gt;  }&lt;br /&gt;  &lt;br /&gt;  function OnViewReady()&lt;br /&gt;  {&lt;br /&gt;   if( Instance.Iframe.readyState != 'complete' ) return;&lt;br /&gt;   &lt;br /&gt;   Instance.Iframe.style.border = 0;&lt;br /&gt;   Instance.Iframe.detachEvent( "onreadystatechange" , OnViewReady );&lt;br /&gt;   m_iframeDoc = getIframeDocument();&lt;br /&gt;   m_iframeDoc.body.scroll = "no";&lt;br /&gt;   m_iframeDoc.body.style.padding = "0px";   &lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  function getIframeDocument(){&lt;br /&gt;   return Instance.Iframe.contentWindow.document;&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;OnCrmPageLoad(); &lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3003801964503297388-2189713554980148927?l=mscrm4ever.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mscrm4ever.blogspot.com/feeds/2189713554980148927/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3003801964503297388&amp;postID=2189713554980148927&amp;isPopup=true' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3003801964503297388/posts/default/2189713554980148927'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3003801964503297388/posts/default/2189713554980148927'/><link rel='alternate' type='text/html' href='http://mscrm4ever.blogspot.com/2008/10/playing-with-notes.html' title='Playing with Notes'/><author><name>Adi Katz</name><uri>http://www.blogger.com/profile/16783428437179825705</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_M-mdv3Tfarg/SOZrpthKN9I/AAAAAAAAACs/dadmho92-a4/s72-c/customerrelatednotes.jpg' height='72' width='72'/><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3003801964503297388.post-664077110306680017</id><published>2008-10-01T07:07:00.005+03:00</published><updated>2008-11-14T13:42:06.881+02:00</updated><title type='text'>Finding crmForm elements By Css Class Names</title><content type='html'>&lt;div&gt;&lt;/div&gt;&lt;br /&gt;There are situations where you need to change form elements text or appearance dynamically, i.e. changing a section name or page title, but find it “impossible” since the form html DOM doesn’t include identifiers for those elements.&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;Most examples I’ve seen regarding similar requests use a simple drill down (or up) mechanism from a certain anchor element, usually the closest element the target.&lt;br /&gt;For example:&lt;br /&gt;&lt;pre class='js' name='code'&gt;&lt;br /&gt;var anchorElement = document.getElementById(“An Element WithID”);&lt;br /&gt;var targetElement = anchorElement.parentElement.parentElement.parentElement;&lt;br /&gt;    targetElement.innerText = "New Text";&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;This type of method is less then elegant and is a highly unmanageable technique.&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;In order to address this issue I’ve created a simple getElementsByClassName function which is a method similar to the already known getElementsByTagName function. The function receives an anchor element ( or null ) and a css class Name and return an array of html element that match the class Name. This type of mechanism is actually very popular in most JavaScript libraries like jquery and prototype.&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;The following code includes getElementsByClassName function and two common requests that are handled by that function.&lt;br /&gt;&lt;br /&gt;&lt;pre class='js' name='code'&gt;&lt;br /&gt;&lt;br /&gt;function OnCrmPageLoad()&lt;br /&gt;{&lt;br /&gt; //Example #1: Change Address Section Text&lt;br /&gt; var anchorNode = document.getElementById( "tab0" );&lt;br /&gt; var secBarCss  = "ms-crm-Form-SectionBar";&lt;br /&gt; var addrSecTxt = "Address";&lt;br /&gt; var addrSecElm = null;&lt;br /&gt; &lt;br /&gt; var results = getElementsByClassName( secBarCss , anchorNode );&lt;br /&gt; for( var i = 0 ; i &amp;lt; results.length ; i++ )&lt;br /&gt; {&lt;br /&gt;  if( results[i].innerText == addrSecTxt ){&lt;br /&gt;   addrSecElm = results[i]; break;&lt;br /&gt;  }&lt;br /&gt; }&lt;br /&gt; &lt;br /&gt; addrSecElm.innerText = "Partial Address Information";&lt;br /&gt; &lt;br /&gt; //Example #2: Change Account title form account: name to account: name (accountnumber)&lt;br /&gt; anchorNode = null;&lt;br /&gt; titleCss   = "ms-crm-Form-Title";&lt;br /&gt; titleTag   = "SPAN";&lt;br /&gt; titleElem  = null;&lt;br /&gt; &lt;br /&gt; results = getElementsByClassName( titleCss , anchorNode );&lt;br /&gt; for( var i = 0 ; i &amp;lt; results.length ; i++ )&lt;br /&gt; {&lt;br /&gt;  if( results[i].tagName == titleTag ){&lt;br /&gt;   titleElem = results[i]; break;&lt;br /&gt;  }&lt;br /&gt; }&lt;br /&gt; &lt;br /&gt; var accountNumber = crmForm.all.accountnumber.DataValue;&lt;br /&gt; if (accountNumber == null) &lt;br /&gt;  accountNumber = "Not Supplied";&lt;br /&gt; &lt;br /&gt; titleElem.innerText = titleElem.innerText + " (" + accountNumber + ")";&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;function getElementsByClassName(className, anchorNode)  &lt;br /&gt;{    &lt;br /&gt; if(!anchorNode) anchorNode = document.body;    &lt;br /&gt; var result = [];    &lt;br /&gt; var regEx  = new RegExp("\\b" + className + "\\b");    &lt;br /&gt; var children = anchorNode.getElementsByTagName("*");    &lt;br /&gt; for( var i = 0 ; i &amp;lt; children.length ; i++ )        &lt;br /&gt; {&lt;br /&gt;  if( regEx.test( children[i].className ) )&lt;br /&gt;   result.push( children[i] );    &lt;br /&gt; }&lt;br /&gt; return result;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;OnCrmPageLoad();&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3003801964503297388-664077110306680017?l=mscrm4ever.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mscrm4ever.blogspot.com/feeds/664077110306680017/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3003801964503297388&amp;postID=664077110306680017&amp;isPopup=true' title='8 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3003801964503297388/posts/default/664077110306680017'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3003801964503297388/posts/default/664077110306680017'/><link rel='alternate' type='text/html' href='http://mscrm4ever.blogspot.com/2008/10/finding-crmform-elements-by-css-class.html' title='Finding crmForm elements By Css Class Names'/><author><name>Adi Katz</name><uri>http://www.blogger.com/profile/16783428437179825705</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>8</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3003801964503297388.post-761462007516758505</id><published>2008-09-29T02:51:00.004+03:00</published><updated>2008-09-29T02:58:25.714+03:00</updated><title type='text'>Embedding related record link in Email</title><content type='html'>&lt;div&gt;&lt;/div&gt;&lt;br /&gt;The code below is not a replacement to the well known workflow + plug-in solution. This is a simple service function that I usually add to the email entity onload event, so when a user clicks on the send email toolbar button the opener ( related entity ) link is embedded in the email body.&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;Users find this extremely comfortable since even if you don’t send the email to a co-worker as a direct reference when the email comes back it very handy to be able to open the record from within outlook like a bookmark.&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;&lt;pre class='js' name='code'&gt;&lt;br /&gt;function OnCrmPageLoad()&lt;br /&gt;{&lt;br /&gt; if( crmForm.FormType == 1 &amp;&amp; opener )&lt;br /&gt; {&lt;br /&gt;  var opnrLoc = opener.location;&lt;br /&gt;  var relUrl  = "http://" + opnrLoc.host + opnrLoc.pathname + opnrLoc.search;&lt;br /&gt;  var ifrmDoc = document.all.descriptionIFrame.contentWindow.document;&lt;br /&gt;  &lt;br /&gt;  var relLink = iframeDoc.createElement( "A" );&lt;br /&gt;      relLink.href  = url;&lt;br /&gt;      relLink.innerText = "Related Record";&lt;br /&gt;      ifrmDoc.body.appendChild( relLink ); &lt;br /&gt; }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;OnCrmPageLoad();&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3003801964503297388-761462007516758505?l=mscrm4ever.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mscrm4ever.blogspot.com/feeds/761462007516758505/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3003801964503297388&amp;postID=761462007516758505&amp;isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3003801964503297388/posts/default/761462007516758505'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3003801964503297388/posts/default/761462007516758505'/><link rel='alternate' type='text/html' href='http://mscrm4ever.blogspot.com/2008/09/embedding-related-record-link-in-email.html' title='Embedding related record link in Email'/><author><name>Adi Katz</name><uri>http://www.blogger.com/profile/16783428437179825705</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3003801964503297388.post-6696094346534955158</id><published>2008-09-26T19:07:00.007+03:00</published><updated>2008-09-26T19:29:56.519+03:00</updated><title type='text'>Decorating CrmGrid Columns</title><content type='html'>&lt;div&gt;&lt;/div&gt;&lt;br /&gt;This How-To Post is about decorating grid columns. Once you understand the mechanics behind the code the rest is a simple (or not) manifest of your / customer’s imagination. You should consider the ramifications of using this type of functionality since this might not upgrade to the next version.&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;The general idea is to override a sitemap link, contacts SubArea link in the workplace Area customers Group in this case, with a new url which contains an IFRAME to the original / Desired CrmGrid.&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;The JavaScript code in the new aspx page has 2 functionalities:&lt;br /&gt;1. Handles the IFRAME and CrmGird loads and between Refreshes.&lt;br /&gt;2. Contains the code you provide to Decorate the column cells.&lt;br /&gt;&lt;br /&gt;So basically if you need to clone this solution for other purposes you need only change the second (2) part.&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;Steps to follow:&lt;br /&gt;1. Export the sitemap from customization (Settings → customization → export customizations).&lt;br /&gt;2. Save the exported sitemap.zip and extract its contents.&lt;br /&gt;3. Open the constomization.xml file in Visual Studio or Notepad.&lt;br /&gt;4. Locate the contacts (nav_conts) SubArea Node. Here is the actual sitemap fragment&lt;br /&gt;&lt;br /&gt;&lt;pre class="xml" name="code"&gt;&lt;br /&gt;&amp;lt;Group Id="Customers" &lt;br /&gt;          ResourceId="Group_Customers"  &lt;br /&gt;          DescriptionResourceId="Customers_Description"&amp;gt;&lt;br /&gt;             &lt;br /&gt;    &amp;lt;SubArea Id="nav_accts" &lt;br /&gt;                Entity="account"   &lt;br /&gt;                DescriptionResourceId="Account_SubArea_Description" /&amp;gt;        &lt;br /&gt;&lt;br /&gt;    &amp;lt;SubArea Id="nav_conts"&lt;br /&gt;                Entity="contact"&lt;br /&gt;                DescriptionResourceId="Contact_SubArea_Description"&lt;br /&gt;                Url="/ISV/GridVIews/Contacts.aspx" /&amp;gt;&lt;br /&gt;&amp;lt;/Group&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;5. Add Url attribute to the following url: /ISV/GridVIews/Contacts.aspx&lt;br /&gt;6. Import the customization.xml file back to crm (Settings → Customization → Import Customizations)&lt;br /&gt;7. Add a new aspx under the isv folder, I put it inside ISV/GridViews folder. The aspx does not need a code behind.&lt;br /&gt;&lt;br /&gt;Note! If you need to run server side code then you must create your own IIS application to be able to compile your code.&lt;br /&gt;&lt;br /&gt;8. Copy the following page html to the Contacts.aspx and Save.&lt;br /&gt;9. In order for the sitemap changes to take effect close and reopen crm in IE.&lt;br /&gt;&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;&lt;pre class='xml' name='code'&gt;&lt;br /&gt;&amp;lt;%@ Page Language="C#" %&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;html xmlns="http://www.w3.org/1999/xhtml" &amp;gt;&lt;br /&gt;&amp;lt;head runat="server"&amp;gt;&lt;br /&gt;    &amp;lt;title&amp;gt;Untitled Page&amp;lt;/title&amp;gt;&lt;br /&gt;    &amp;lt;script language="javascript"&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;pre class='js' name='code'&gt;&lt;br /&gt;  attachEvent( "onload" , OnPageLoad );&lt;br /&gt;  //IFrame ID&lt;br /&gt;  var contsGrid;&lt;br /&gt;  //IFrame document object&lt;br /&gt;  var iframeDoc;&lt;br /&gt;  //Views picklist &lt;br /&gt;  var SavedQuerySelector;&lt;br /&gt;  //The grid object&lt;br /&gt;         var crmGrid;&lt;br /&gt;   &lt;br /&gt;  /*&lt;br /&gt;   Load the contacts grid view,&lt;br /&gt;   You may change the IFRAME url to suit your needs&lt;br /&gt;  */&lt;br /&gt;  function OnPageLoad()&lt;br /&gt;  {&lt;br /&gt;   contsGrid = document.all.contsGrid;&lt;br /&gt;   var contsUrl  = "/" + top.ORG_UNIQUE_NAME + "/_root/homepage.aspx?etc=2"; &lt;br /&gt;   contsGrid.src = contsUrl;&lt;br /&gt;   contsGrid.onreadystatechange = OnGridViewReady;&lt;br /&gt;  }&lt;br /&gt;  &lt;br /&gt;  /*&lt;br /&gt;   When the IFRAME is ready then:&lt;br /&gt;   Attach to the grid refresh and selector change events.&lt;br /&gt;   Since the grid is already loaded call it for the first time.&lt;br /&gt;  */&lt;br /&gt;  function OnGridViewReady()&lt;br /&gt;  {&lt;br /&gt;   if( contsGrid.readyState != "complete" ) &lt;br /&gt;    return;&lt;br /&gt;   iframeDoc = contsGrid.contentWindow.document;&lt;br /&gt;   &lt;br /&gt;   //make sure the selector exists&lt;br /&gt;   SavedQuerySelector = iframeDoc.all.SavedQuerySelector;&lt;br /&gt;   if( SavedQuerySelector )&lt;br /&gt;    iframeDoc.all.SavedQuerySelector.attachEvent( "onchange" , OnGridReadyChangeLayout );&lt;br /&gt;   &lt;br /&gt;   //make sure the grid exists&lt;br /&gt;   crmGrid = iframeDoc.all.crmGrid;&lt;br /&gt;   if( crmGrid )&lt;br /&gt;   {&lt;br /&gt;    iframeDoc.all.crmGrid.attachEvent( "onrefresh" , OnGridReadyChangeLayout );&lt;br /&gt;    //change the layout for the first time&lt;br /&gt;    OnGridReadyChangeLayout();&lt;br /&gt;   }  &lt;br /&gt;  }&lt;br /&gt;  &lt;br /&gt;  /*&lt;br /&gt;   This function (callback) is called every time the &lt;br /&gt;   grid view refreshes either by the selector or refresh button.&lt;br /&gt;   &lt;br /&gt;   IF the Data( InnerGrid ) is not ready then the setTimeout is called.      &lt;br /&gt;  */&lt;br /&gt;  function OnGridReadyChangeLayout()&lt;br /&gt;  {&lt;br /&gt;   if( !crmGrid.InnerGrid ) &lt;br /&gt;    return setTimeout( OnGridReadyChangeLayout , 100 );&lt;br /&gt;   &lt;br /&gt;         /* Put your implementations under this line */&lt;br /&gt;   DecorateEmailColumn();&lt;br /&gt;  }&lt;br /&gt;  &lt;br /&gt;  /*&lt;br /&gt;   This is an example of how to transform the email column into an &lt;br /&gt;   Active mailto link&lt;br /&gt;&lt;br /&gt;   IF the Column you want to Decorate is found then:&lt;br /&gt;    FOR each row in the grid&lt;br /&gt;     IF the column has text&lt;br /&gt;      1. SAVE the column text&lt;br /&gt;      2. CLEAR the column inner NOBR element html&lt;br /&gt;      3. CREATE a link element&lt;br /&gt;      4. APPEND the link element to the NOBR element&lt;br /&gt;&lt;br /&gt;  */&lt;br /&gt;  function DecorateEmailColumn()&lt;br /&gt;  {&lt;br /&gt;   var emailColIndex = crmGrid.InnerGrid.FindColumnIndex("emailaddress1");&lt;br /&gt;   if( emailColIndex == -1 ) &lt;br /&gt;    return;&lt;br /&gt;&lt;br /&gt;   for( var i = 0 ; i &amp;lt; crmGrid.InnerGrid.AllRecords.length ; i++ )&lt;br /&gt;   {&lt;br /&gt;    emailCell = crmGrid.InnerGrid.AllRecords[ i ][3].cells[ emailColIndex ];        &lt;br /&gt;    if( emailCell.innerText == "" ) continue;&lt;br /&gt;    &lt;br /&gt;    var emailText = emailCell.innerText;&lt;br /&gt;     emailCell.childNodes[0].innerHTML = "";&lt;br /&gt;    var link = iframeDoc.createElement("&amp;lt;A style='text-decoration:underline'&amp;gt;");&lt;br /&gt;     link.href = "mailto:" + emailText;&lt;br /&gt;     link.innerText = emailText;&lt;br /&gt;    emailCell.childNodes[0].appendChild( link );&lt;br /&gt;   }&lt;br /&gt;  }&lt;br /&gt;&lt;/pre&gt;&lt;pre class='xml' name='code'&gt;&lt;br /&gt; &amp;lt;/script&amp;gt;&lt;br /&gt;&amp;lt;/head&amp;gt;&lt;br /&gt;&amp;lt;body scroll="no" style="margin:0px"&amp;gt;&lt;br /&gt;    &amp;lt;iframe id="contsGrid" &lt;br /&gt;   src="about:blank" &lt;br /&gt;   style="width:100%;height:100%" &lt;br /&gt;   frameborder="0" &lt;br /&gt;   scrolling="no"&amp;gt;&lt;br /&gt; &amp;lt;/iframe&amp;gt;&lt;br /&gt;&amp;lt;/body&amp;gt;&lt;br /&gt;&amp;lt;/html&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3003801964503297388-6696094346534955158?l=mscrm4ever.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mscrm4ever.blogspot.com/feeds/6696094346534955158/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3003801964503297388&amp;postID=6696094346534955158&amp;isPopup=true' title='8 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3003801964503297388/posts/default/6696094346534955158'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3003801964503297388/posts/default/6696094346534955158'/><link rel='alternate' type='text/html' href='http://mscrm4ever.blogspot.com/2008/09/decorating-crmgrid-columns.html' title='Decorating CrmGrid Columns'/><author><name>Adi Katz</name><uri>http://www.blogger.com/profile/16783428437179825705</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>8</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3003801964503297388.post-3345945365762162098</id><published>2008-09-25T20:41:00.002+03:00</published><updated>2008-09-25T20:47:49.090+03:00</updated><title type='text'>Enhancing user experience using Shortcuts</title><content type='html'>&lt;div&gt;&lt;/div&gt;&lt;br /&gt;If you get around the blog you’ll notice I targeted a wide range of dynamics usability issues. I’ve done so since I my experience taught me that application “interactivity” plays a major role in end users satisfaction which most agree to be one of the important key factors in successful dynamics implementations.&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;Most customers will identify the need to shorten the time and complexity (in clicks) it takes to accomplish a certain task and of course invest some of the project founds in building comfortable shortcuts like adding buttons on the grids or even replacing menu items with toolbar buttons. It seems odd that the whole shortcut mechanism is hardly ever used.&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;Up to a certain point users won’t mind switching between keyboard and mouse in order to fill out an entire form but as users get more and more acquainted with dynamics they search for better and more productive ways to interact with the system. Suddenly switching between devices seems like a waist of good energy. I asked my self that same question! Way can’t I just press (Alt + tab #) to navigate between tabs, or (Alt + S) to send out an email when I finish filling out the form. What about reassigning the form to another user, why do I need to navigate to the third tab to do so, can’t I just ( Alt + w ) to open the assignment window and of course the list goes on and on.&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;In order to address this I’ve created a very handy utility class which facilitates the creation of shortcuts on all types of crm functions i.e menu items, toolbar buttons, navigation links, fields and tabs. The nice thing about this feature is that it teaches the user to use those shortcuts from a very early stage. Each time the user uses the mouse to click on a “function” with a shortcut the shortcut combination is presented to him and soon he learns to use that instead.&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;In order to create shortcuts you need to create an instance of the ShortcutMaker class. This class facilitates the creation of all shortcuts on the form.&lt;br /&gt;To create a new shortcut simply use the Add method by passing it a “function” id then use the return Shortcut object SetKeys method to set the special key (Alt or Ctrl), the accompanying letter (Capital) and the type of the function (Button, Menu item, Navigation link and so on). To finish the job use the ShortcutMaker Create method by passing the Shortcut object.&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;&lt;pre class='js' name='code'&gt;&lt;br /&gt;function OnCrmPageLoad()&lt;br /&gt;{&lt;br /&gt; //Create an instance of the ShortcutMaker class&lt;br /&gt; var scMaker = new ShortcutMaker();&lt;br /&gt; &lt;br /&gt; //Create a shortcut on the Send Email Button&lt;br /&gt; var FollowUpSC = scMaker.Add( "_MBdocumentallRelatedInformationPaneExpanddocumentallRelatedInformationPaneLoadContextDatafollowup" );&lt;br /&gt;  FollowUpSC.SetKeys( scMaker.Keys.Alt , "O" , scMaker.UIType.Button );&lt;br /&gt;  scMaker.Create( FollowUpSC );&lt;br /&gt; &lt;br /&gt; //Create a shortcut on the toolbar Follow up button&lt;br /&gt; var SendEmailSC = scMaker.Add( "_MBlocAddActTo4202" );&lt;br /&gt;  SendEmailSC.SetKeys( scMaker.Keys.Alt , "E" , scMaker.UIType.Button );&lt;br /&gt;  scMaker.Create( SendEmailSC );&lt;br /&gt; &lt;br /&gt; //Create a shortcut on the History naviation link &lt;br /&gt; var actHistorySC = scMaker.Add( "navActivityHistory" );&lt;br /&gt;  actHistorySC.SetKeys( scMaker.Keys.Alt , "H" , scMaker.UIType.Navigation );&lt;br /&gt;  scMaker.Create( actHistorySC );&lt;br /&gt; &lt;br /&gt; //Create a shortcut on the owner lookup field &lt;br /&gt; var owneridSC = scMaker.Add( "ownerid" );&lt;br /&gt;  owneridSC.SetKeys( scMaker.Keys.Alt , "W" , scMaker.UIType.Field );&lt;br /&gt;  scMaker.Create( owneridSC );&lt;br /&gt; &lt;br /&gt; //Create shortcuts on all form Tabs&lt;br /&gt; var generalTabSC = scMaker.Add( "tab0Tab" ); //General Tab&lt;br /&gt;  generalTabSC.SetKeys( scMaker.Keys.Alt , "1" , scMaker.UIType.Tab );&lt;br /&gt;  scMaker.Create( generalTabSC );&lt;br /&gt; var notesTabSC = scMaker.Add( "tab1Tab" ); //Notes Tab&lt;br /&gt;  notesTabSC.SetKeys( scMaker.Keys.Alt , "2" , scMaker.UIType.Tab );&lt;br /&gt;  scMaker.Create( notesTabSC );&lt;br /&gt; var moreTabSC = scMaker.Add( "tab2Tab" ); //Another Tab&lt;br /&gt;  moreTabSC.SetKeys( scMaker.Keys.Alt , "3" , scMaker.UIType.Tab );&lt;br /&gt;  scMaker.Create( moreTabSC );&lt;br /&gt; &lt;br /&gt; //Create shortcut on the AddNote menu item&lt;br /&gt; var addnoteSC = scMaker.Add( "_MIlocAddObjTo5" ); &lt;br /&gt;  addnoteSC.SetKeys( scMaker.Keys.Alt , "G" , scMaker.UIType.Menu );&lt;br /&gt;  scMaker.Create( addnoteSC );&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;function ShortcutMaker()&lt;br /&gt;{&lt;br /&gt; var Instance  = this;&lt;br /&gt; var ShortCuts = [];&lt;br /&gt;&lt;br /&gt; Instance.Keys =   {&lt;br /&gt;  Alt   : "18",&lt;br /&gt;  Ctrl  : "17"&lt;br /&gt; }&lt;br /&gt; &lt;br /&gt; Instance.UIType = {&lt;br /&gt;  Navigation : 1,&lt;br /&gt;  Button    : 2 ,&lt;br /&gt;  Field      : 3,&lt;br /&gt;  Menu       : 4,&lt;br /&gt;  Tab     : 5 &lt;br /&gt; }&lt;br /&gt; &lt;br /&gt; Instance.Add = function( controlId ){&lt;br /&gt;  return new ShortCut( controlId );  &lt;br /&gt; }&lt;br /&gt; &lt;br /&gt; Instance.Create = function( shortcut )&lt;br /&gt; {&lt;br /&gt;  if( !shortcut ) &lt;br /&gt;    return;&lt;br /&gt;  &lt;br /&gt;  var hashName = shortcut.SpecialKey + shortcut.KeyCode;&lt;br /&gt;  if( shortcut.Control )&lt;br /&gt;  {&lt;br /&gt;   var controlWithTitle = shortcut.Control;&lt;br /&gt;   if( shortcut.UiType == Instance.UIType.Navigation )&lt;br /&gt;    controlWithTitle = controlWithTitle.childNodes[1];  &lt;br /&gt;   controlWithTitle.title = controlWithTitle.title + " (" + shortcut.SpecialKeyName + " + " + shortcut.Letter + ")";&lt;br /&gt;   controlWithTitle.title = controlWithTitle.title.replace( /^\s/ , "" ); &lt;br /&gt;  }&lt;br /&gt;  ShortCuts[ hashName ] = shortcut;&lt;br /&gt; }&lt;br /&gt;   &lt;br /&gt; Instance.OnKeyDownCheckShortcuts = function()&lt;br /&gt; {&lt;br /&gt;  var hashName = getSpecialKey( event ) + event.keyCode;&lt;br /&gt;  var shortcut = ShortCuts[ hashName ];&lt;br /&gt;  if( shortcut )  &lt;br /&gt;  { &lt;br /&gt;   if( shortcut.UiType == Instance.UIType.Menu )&lt;br /&gt;    window.execScript( shortcut.Control.action );&lt;br /&gt;   else&lt;br /&gt;    shortcut.Control.click();&lt;br /&gt;   &lt;br /&gt;   event.cancelBubble = true;&lt;br /&gt;   event.returnValue = false;&lt;br /&gt;   return false;&lt;br /&gt;  }&lt;br /&gt; }&lt;br /&gt; &lt;br /&gt; function getSpecialKey( evt ) {&lt;br /&gt;  if( evt.altKey ) return Instance.Keys.Alt;&lt;br /&gt;  else if( evt.ctrlKey  ) return Instance.Keys.Ctrl;&lt;br /&gt;  return "0";&lt;br /&gt; }&lt;br /&gt; &lt;br /&gt; function ShortCut( controlId )&lt;br /&gt; {&lt;br /&gt;  this.Control = document.getElementById( controlId ); &lt;br /&gt;  this.Letter  = "";&lt;br /&gt;  this.SpecialKey = 0;&lt;br /&gt;  this.KeyCode = 0;&lt;br /&gt;  this.UiType  = 0; &lt;br /&gt;  &lt;br /&gt;  this.SetKeys = function( spKey , letter , uiType )&lt;br /&gt;  {&lt;br /&gt;   if( !spKey || !letter )&lt;br /&gt;    return;&lt;br /&gt;    &lt;br /&gt;   this.Letter  = letter;&lt;br /&gt;   this.KeyCode = letter.charCodeAt(0); &lt;br /&gt;   this.SpecialKey = spKey; &lt;br /&gt;   this.SpecialKeyName = getSpecialKey(spKey);&lt;br /&gt;   this.UiType  = uiType;&lt;br /&gt;  }&lt;br /&gt;  &lt;br /&gt;  function getSpecialKey( spKey ) {&lt;br /&gt;   if( spKey == Instance.Keys.Alt ) return "Alt";&lt;br /&gt;   else if( spKey == Instance.Keys.Ctrl  )  return "Ctrl";&lt;br /&gt;  }&lt;br /&gt; }&lt;br /&gt; &lt;br /&gt; function Initialize(){&lt;br /&gt;  document.attachEvent( "onkeydown" , Instance.OnKeyDownCheckShortcuts );&lt;br /&gt; }&lt;br /&gt; &lt;br /&gt; Initialize();&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;OnCrmPageLoad(); &lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3003801964503297388-3345945365762162098?l=mscrm4ever.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mscrm4ever.blogspot.com/feeds/3345945365762162098/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3003801964503297388&amp;postID=3345945365762162098&amp;isPopup=true' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3003801964503297388/posts/default/3345945365762162098'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3003801964503297388/posts/default/3345945365762162098'/><link rel='alternate' type='text/html' href='http://mscrm4ever.blogspot.com/2008/09/enhancing-user-experience-using.html' title='Enhancing user experience using Shortcuts'/><author><name>Adi Katz</name><uri>http://www.blogger.com/profile/16783428437179825705</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3003801964503297388.post-5843456579087514965</id><published>2008-09-22T01:11:00.001+03:00</published><updated>2008-09-22T01:14:02.892+03:00</updated><title type='text'>CRM Master Detail Page – Part 1</title><content type='html'>&lt;div&gt;&lt;/div&gt;&lt;br /&gt;This is a nice and simple solution you can use to enable inline navigation between CRM entities OR in other word a master detail display. This is also considered an excellent alternative to not having the ability to edit the crm grid since the user doesn’t have to leave the current entity window which completely eliminates the open - close page problem and the entity fields are always available for editing. &lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;The idea behind the solution is to create an iframe using customization, set the iframe src attribute to any view you like to show and use this code in the onload event. The only thing the code does is override the iframe window.open function which means that when the user double clicks on a record, instead of the opening a new window, the current window url is replaced with to the selected record url.&lt;br /&gt;&lt;br /&gt;&lt;pre class='js' name='code'&gt;&lt;br /&gt;function OnCrmPageLoad()&lt;br /&gt;{&lt;br /&gt; /* Reference the IFrame */&lt;br /&gt; var IFRAME_test = document.all.IFRAME_test;&lt;br /&gt;  /* Assign the IFrame URL */&lt;br /&gt;  IFRAME_test.src = DecideOnTheIFrameGridUrl();&lt;br /&gt;  /* Override the onreadystatechange Event */&lt;br /&gt;  IFRAME_test.onreadystatechange = function ()&lt;br /&gt;  {&lt;br /&gt;   /* if the IFrame is ready == 'complete' */&lt;br /&gt;   if( IFRAME_test.readyState != 'complete' ) &lt;br /&gt;    return;&lt;br /&gt;   /* Remove the IFrame border since the grid already has one */&lt;br /&gt;   IFRAME_test.style.border = "0px";&lt;br /&gt;   &lt;br /&gt;   /* Remove the IFrame padding and scroll */&lt;br /&gt;   var IframeWindo = IFRAME_test.contentWindow;&lt;br /&gt;    IframeWindo.document.body.scroll = "no";&lt;br /&gt;    IframeWindo.document.body.style.padding = "0px";&lt;br /&gt;   &lt;br /&gt;   /* &lt;br /&gt;    This is what you really after!&lt;br /&gt;    Override the IFrame window.open function&lt;br /&gt;    So the selected record url replaces the current one. &lt;br /&gt;   */&lt;br /&gt;   IframeWindo.open = function( url )&lt;br /&gt;   { &lt;br /&gt;    location.href = url;&lt;br /&gt;    return false;&lt;br /&gt;   }&lt;br /&gt;  }&lt;br /&gt;  &lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;function DecideOnTheIFrameGridUrl()&lt;br /&gt;{&lt;br /&gt; //This will show the entity default view&lt;br /&gt; return "/" + ORG_UNIQUE_NAME + "/_root/homepage.aspx?etc=" + crmForm.ObjectTypeCode;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;OnCrmPageLoad();&lt;br /&gt; &lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3003801964503297388-5843456579087514965?l=mscrm4ever.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mscrm4ever.blogspot.com/feeds/5843456579087514965/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3003801964503297388&amp;postID=5843456579087514965&amp;isPopup=true' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3003801964503297388/posts/default/5843456579087514965'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3003801964503297388/posts/default/5843456579087514965'/><link rel='alternate' type='text/html' href='http://mscrm4ever.blogspot.com/2008/09/crm-master-detail-page-part-1.html' title='CRM Master Detail Page – Part 1'/><author><name>Adi Katz</name><uri>http://www.blogger.com/profile/16783428437179825705</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3003801964503297388.post-525014654188141868</id><published>2008-09-21T19:21:00.003+03:00</published><updated>2008-09-21T19:36:31.109+03:00</updated><title type='text'>JavaScript Ajax Wrapper</title><content type='html'>The idea behind this post is not about calling a specific crmservice but a general idea of how you can facilitate calls to any webservice, whether it’s a custom one you built your self or one of crm’s existing services. Although the code introduces working classes they can be further develop to support more advanced requirement.&lt;br /&gt;&lt;br /&gt;The OnCrmPageLoad contains several calls using the WebServiceStartInfo and the WebRequest classes both to crmservice and a custom service I made to demonstrate the usage. The concept I follow is comprised of 2 steps. The first step is about filling the information required to make an Ajax call. The second and last step just executes the requests and hands over the results back to you.&lt;br /&gt;&lt;br /&gt;The nice thing about the WebServiceStartInfo is that it builds the entire soap message for you. This makes life very easy since you can focus more on the business requirement and less on the technology. Never the less, a good grasp over Ajax is needed in order to fill the StartInfo with the correct data.&lt;br /&gt;&lt;br /&gt;Note. The easiest way of getting the soap needed to make a request is to open the asmx file in Internet explorer. This will assist you when you construct complex webmethod parameters using the Parameters.Add method of the WebServiceStartInfo and Parameter classes.&lt;br /&gt;&lt;br /&gt;The fist part describes the WebService.aspx code behind:&lt;br /&gt;&lt;br /&gt;&lt;pre class="js" name="code"&gt;&lt;br /&gt;namespace MyAppServices&lt;br /&gt;{&lt;br /&gt;   /// &amp;lt;summary&amp;gt;&lt;br /&gt;   /// Summary description for WebService1&lt;br /&gt;   /// &amp;lt;/summary&amp;gt;&lt;br /&gt;   [WebService(Namespace = "http://www.mycompany.com/demo")]&lt;br /&gt;   [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]&lt;br /&gt;   [ToolboxItem(false)]&lt;br /&gt;   public class WebService1 : System.Web.Services.WebService&lt;br /&gt;   {&lt;br /&gt;&lt;br /&gt;   [WebMethod]&lt;br /&gt;   public string Hello()&lt;br /&gt;   {&lt;br /&gt;      return "Hello";&lt;br /&gt;   }&lt;br /&gt;&lt;br /&gt;   [WebMethod]&lt;br /&gt;   public string HelloUser(String userName)&lt;br /&gt;   {&lt;br /&gt;      return String.Format("Hello {0}", userName);&lt;br /&gt;   }&lt;br /&gt;  &lt;br /&gt;   [WebMethod]&lt;br /&gt;   public String HelloOnceMore( String[] Parameters )&lt;br /&gt;   {&lt;br /&gt;      return String.Join( "," , Parameters );&lt;br /&gt;   }&lt;br /&gt;&lt;br /&gt;   [WebMethod]&lt;br /&gt;   public string HelloAgain(ComplexParameter userInfo)&lt;br /&gt;   {&lt;br /&gt;      return String.Format("Hello {0} {1}", userInfo.FirstName, userInfo.LastName);&lt;br /&gt;   }&lt;br /&gt;  &lt;br /&gt;   [Serializable]&lt;br /&gt;   public class ComplexParameter&lt;br /&gt;   {&lt;br /&gt;      public String FirstName;&lt;br /&gt;      public String LastName;&lt;br /&gt;   }&lt;br /&gt;   }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;This part describes the client side usage called from crm onload event / external file.&lt;br /&gt;&lt;br /&gt;&lt;pre class='js' name='code'&gt;&lt;br /&gt;function OnCrmPageLoad ()&lt;br /&gt;{  &lt;br /&gt; /* call the CrmService Fetch method. */&lt;br /&gt; var fetchXml = _HtmlEncode('&amp;lt;fetch version="1.0" output-format="xml-platform" mapping="logical" distinct="false"&amp;gt;&amp;lt;entity name="systemuser"&amp;gt;&amp;lt;attribute name="fullname"/&amp;gt;&amp;lt;attribute name="businessunitid"/&amp;gt;&amp;lt;attribute name="title"/&amp;gt;&amp;lt;attribute name="address1_telephone1"/&amp;gt;&amp;lt;attribute name="systemuserid"/&amp;gt;&amp;lt;order attribute="fullname" descending="false"/&amp;gt;&amp;lt;filter type="and"&amp;gt;&amp;lt;condition attribute="systemuserid" operator="eq-userid"/&amp;gt;&amp;lt;/filter&amp;gt;&amp;lt;/entity&amp;gt;&amp;lt;/fetch&amp;gt;');&lt;br /&gt; &lt;br /&gt; var crmInfo = new WebServiceStartInfo();&lt;br /&gt;  /* machine.domain:port */&lt;br /&gt;     crmInfo.Host     = "http://moss.litwareinc.com:5555";&lt;br /&gt;  /* crm service default namespace */&lt;br /&gt;     crmInfo.XmlNS    = "http://schemas.microsoft.com/crm/2007/WebServices";&lt;br /&gt;  /* crmservice url */&lt;br /&gt;     crmInfo.Asmx     = "/mscrmservices/2007/crmservice.asmx"&lt;br /&gt;     crmInfo.Method   = "Fetch";&lt;br /&gt;  /* include a CrmAuthentication Token */&lt;br /&gt;     crmInfo.CrmToken = true;&lt;br /&gt;     crmInfo.Async    = true; &lt;br /&gt;  /* call this function when done */&lt;br /&gt;     crmInfo.Callback = OnEndRequest;&lt;br /&gt;  /* the Fetch method parameter  */&lt;br /&gt;     crmInfo.Parameters.Add( "fetchXml" , fetchXml );&lt;br /&gt;  &lt;br /&gt;  /* Assign the start info to the webrequest */&lt;br /&gt;     var request = new WebRequest( crmInfo );&lt;br /&gt;         request.Execute();&lt;br /&gt;  &lt;br /&gt; // --------------------------------------------------------------&lt;br /&gt;  /* this part calls the custom web method above */&lt;br /&gt;    var customInfo = new WebServiceStartInfo();&lt;br /&gt;  /* the webservice resided on the default website on port 80 */&lt;br /&gt;        customInfo.Host     = "http://moss.litwareinc.com";&lt;br /&gt;  /* the default namespace I gave the WebService1 class */&lt;br /&gt;        customInfo.XmlNS    = "http://www.mycompany.com/demo";&lt;br /&gt;  /* the webservice url */&lt;br /&gt;        customInfo.Asmx     = "/MyAppServices/WebService1.asmx"&lt;br /&gt;        customInfo.Method   = "Hello";&lt;br /&gt;  /* we don’t need a token since we are not calling crm services */&lt;br /&gt;        customInfo.CrmToken = false;&lt;br /&gt;        customInfo.Async    = true; &lt;br /&gt;        customInfo.Callback = OnEndRequest;&lt;br /&gt;  &lt;br /&gt;        request = new WebRequest( customInfo );&lt;br /&gt;        request.Execute();&lt;br /&gt;  &lt;br /&gt; // -------------------------------------------------------------&lt;br /&gt;  /* &lt;br /&gt;        since we already have a startinfo object I just changed the property&lt;br /&gt;        values to show more advance usage in this case the HelloUser method &lt;br /&gt;        receives a parameter.&lt;br /&gt;         &lt;br /&gt;        Signature:&lt;br /&gt;        [WebMethod]&lt;br /&gt;        public string HelloUser( string username ); &lt;br /&gt;  */&lt;br /&gt;        customInfo.Method = "HelloUser";&lt;br /&gt;        customInfo.Parameters.Add( "userName" , "Adi Katz" );&lt;br /&gt;  &lt;br /&gt;        request = new WebRequest( customInfo );&lt;br /&gt;        request.Execute();&lt;br /&gt;   &lt;br /&gt; // -------------------------------------------------------------&lt;br /&gt;  /*&lt;br /&gt;        The following method signature is comprised of a string array &lt;br /&gt;        [WebMethod]&lt;br /&gt;        public string HelloOnceMore( string[] Parameters );&lt;br /&gt;  */&lt;br /&gt;        customInfo.Method = "HelloOnceMore";&lt;br /&gt;        var StringArray = customInfo.Parameters.Add( "Parameters" );&lt;br /&gt;            StringArray.Parameters.Add( "string" , "Adi" );&lt;br /&gt;            StringArray.Parameters.Add( "string" , "Katz" );&lt;br /&gt;            request = new WebRequest( customInfo );&lt;br /&gt;            request.Execute();&lt;br /&gt;  &lt;br /&gt; // -------------------------------------------------------------&lt;br /&gt;  /*&lt;br /&gt;        The following web method signature is comprised of a complex type &lt;br /&gt;        called ComplexParameter which is decorated with the [Serializable] &lt;br /&gt;        attribute and contains a FirstName and LastName.&lt;br /&gt;&lt;br /&gt;        public class ComplexParameter&lt;br /&gt;        {&lt;br /&gt;            public string FirstName;&lt;br /&gt;            public string LastName;&lt;br /&gt;        }&lt;br /&gt;         &lt;br /&gt;        Signature:&lt;br /&gt;        [WebMethod]&lt;br /&gt;        public string HelloAgain( ComplexParameter complexParameter );&lt;br /&gt;         &lt;br /&gt;  */&lt;br /&gt;        customInfo.Method = "HelloAgain";&lt;br /&gt;        customInfo.Async  = false;&lt;br /&gt;  &lt;br /&gt;        var ComplexParameter = customInfo.Parameters.Add( "userInfo" );&lt;br /&gt;            ComplexParameter.Parameters.Add( "FirstName" , "Bill" );&lt;br /&gt;            ComplexParameter.Parameters.Add( "LastName" , "Gates" );&lt;br /&gt;  &lt;br /&gt;            request = new WebRequest( customInfo );&lt;br /&gt;        var result = request.Execute();&lt;br /&gt;            alert(result.text);&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;function OnEndRequest( requestObj ) &lt;br /&gt;{&lt;br /&gt;    alert( requestObj.responseXML.text );&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;/* Holds all the information needed to call a webservice */&lt;br /&gt;function WebServiceStartInfo()&lt;br /&gt;{&lt;br /&gt; /* WebMethod Name */&lt;br /&gt; this.Method   = "";&lt;br /&gt; /* Server Name + Port */&lt;br /&gt; this.Host     = ""&lt;br /&gt; /* The Default XML Namespace */&lt;br /&gt; this.XmlNS    = "";&lt;br /&gt; /* The WebService Url */&lt;br /&gt; this.Asmx     = "";&lt;br /&gt; /* Holds the soap envelop */&lt;br /&gt; this.Soap     = new StringBuilder();&lt;br /&gt; this.Async    = false;&lt;br /&gt; /* Holds the function (pointer) to call if Async is true */&lt;br /&gt; this.Callback = null;&lt;br /&gt; /* Flag which specify whether to include the crm token */&lt;br /&gt; this.CrmToken = false;&lt;br /&gt; /* Holds the WebMothod parameters list */&lt;br /&gt; this.Parameters = new ParameterCollection();&lt;br /&gt; /* Returns a valid SoapAction */&lt;br /&gt; this.GetSoapAction = function() {&lt;br /&gt;  return this.XmlNS + "/" + this.Method;&lt;br /&gt; }&lt;br /&gt; /* Gets the complete url to call */&lt;br /&gt; this.GetUrl = function() {&lt;br /&gt;  return this.Host + this.Asmx;&lt;br /&gt; }&lt;br /&gt; /* return the entire soap envelop xml */&lt;br /&gt; this.ToString = function()&lt;br /&gt; {&lt;br /&gt;  /* clear the soap former calls */&lt;br /&gt;  this.Soap.Clear();&lt;br /&gt;  &lt;br /&gt;  this.Soap.Append( '&amp;lt;soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"&amp;gt;' );&lt;br /&gt;  if( this.CrmToken )&lt;br /&gt;      this.Soap.Append( GenerateAuthenticationHeader() );&lt;br /&gt;  &lt;br /&gt;  this.Soap.Append( '&amp;lt;soap:Body&amp;gt;' ); &lt;br /&gt;  this.Soap.Append('&amp;lt;').Append(this.Method);&lt;br /&gt;  this.Soap.Append(' xmlns="').Append(this.XmlNS).Append('"');&lt;br /&gt;  &lt;br /&gt;  if( this.Parameters.List.length &amp;gt; 0 )&lt;br /&gt;  {&lt;br /&gt;   this.Soap.Append('&amp;gt;');&lt;br /&gt;   for( var i = 0 ; i &amp;lt; this.Parameters.List.length ; i++ )&lt;br /&gt;     this.Soap.Append( this.Parameters.List[i].ToString() );&lt;br /&gt;   this.Soap.Append('&amp;lt;/').Append(this.Method).Append('&amp;gt;');&lt;br /&gt;  }&lt;br /&gt;  else&lt;br /&gt;  {&lt;br /&gt;   this.Soap.Append(' /&amp;gt;');&lt;br /&gt;  }&lt;br /&gt;  &lt;br /&gt;  this.Soap.Append( '&amp;lt;/soap:Body&amp;gt;' );&lt;br /&gt;  this.Soap.Append( '&amp;lt;/soap:Envelope&amp;gt;' );&lt;br /&gt;  &lt;br /&gt;  return this.Soap.ToString();&lt;br /&gt; }&lt;br /&gt; /* A collection of parameters */&lt;br /&gt; function ParameterCollection()&lt;br /&gt; {&lt;br /&gt;  /* Parameters list */&lt;br /&gt;  this.List = [];&lt;br /&gt;  /* &lt;br /&gt;   Adds a new parameter and returns a reference so &lt;br /&gt;   further child parameters can be added.&lt;br /&gt;   &lt;br /&gt;   for example:&lt;br /&gt;   var ComplexParam = Object.Parameters.Add( "ComplexParameter" )&lt;br /&gt;       ComplexParam.Parameters.Add( "FirstName" , "Adi" );&lt;br /&gt;   &lt;br /&gt;   Result Xml&lt;br /&gt;   &amp;lt;ComplexParam&amp;gt;&lt;br /&gt;    &amp;lt;FirstName&amp;gt;Adi&amp;lt;/FirstName&amp;gt;&lt;br /&gt;   &amp;lt;/ComplexParam&amp;gt; &lt;br /&gt;   &lt;br /&gt;   Signature&lt;br /&gt;   [WebMethod]&lt;br /&gt;   public string MethodName( ComplexParam complexParam );&lt;br /&gt;  */&lt;br /&gt;  this.Add = function( name , value )&lt;br /&gt;  {&lt;br /&gt;   var parameter = new Parameter( name , value );&lt;br /&gt;   this.List[ this.List.length ] = parameter;&lt;br /&gt;   return parameter;&lt;br /&gt;  }&lt;br /&gt; }&lt;br /&gt; /* A single parameter */&lt;br /&gt; function Parameter( name , value )&lt;br /&gt; {&lt;br /&gt;  /* &lt;br /&gt;   Holds either the Property Name or type if this is an array &lt;br /&gt;   for example:&lt;br /&gt;   var StringArray = Object.Parameters.Add( "StringArray" );&lt;br /&gt;       StringArray.Parameters.Add( "string" , "Adi" );&lt;br /&gt;       StringArray.Parameters.Add( "string" , "Katz" );&lt;br /&gt;   &lt;br /&gt;   Result Xml&lt;br /&gt;   &amp;lt;StringArray&amp;gt;&lt;br /&gt;    &amp;lt;string&amp;gt;Adi&amp;lt;/string&amp;gt;&lt;br /&gt;    &amp;lt;string&amp;gt;Katz&amp;lt;/string&amp;gt;&lt;br /&gt;   &amp;lt;/StringArray&amp;gt;&lt;br /&gt;  &lt;br /&gt;   Signature:&lt;br /&gt;   [WebMethod]&lt;br /&gt;   public string MethodName( string[] StringArray );&lt;br /&gt;  */&lt;br /&gt;  this.Name  = name;&lt;br /&gt;  this.Value = value;&lt;br /&gt;  /* List of child parameters */&lt;br /&gt;  this.Parameters = new ParameterCollection();&lt;br /&gt;  /* Internal string builder */&lt;br /&gt;  this.Xml = new StringBuilder();&lt;br /&gt;  &lt;br /&gt;  this.ToString = function()&lt;br /&gt;  {&lt;br /&gt;   this.Xml.Append('&amp;lt;').Append(this.Name).Append('&amp;gt;');&lt;br /&gt;   if( this.Value != null ) this.Xml.Append(this.Value); &lt;br /&gt;   else if( this.Parameters.List.length &amp;gt; 0 )&lt;br /&gt;       for( var i = 0 ; i &amp;lt; this.Parameters.List.length ; i++ )&lt;br /&gt;       this.Xml.Append( this.Parameters.List[i].ToString() );&lt;br /&gt;   this.Xml.Append('&amp;lt;/').Append(this.Name).Append('&amp;gt;');&lt;br /&gt;   return this.Xml.ToString();&lt;br /&gt;  }&lt;br /&gt; }&lt;br /&gt; /*&lt;br /&gt;  Utility class that uses an array to append new strings &lt;br /&gt;  and uses the join method to concatenate them&lt;br /&gt; */&lt;br /&gt; function StringBuilder()&lt;br /&gt; {&lt;br /&gt;  /* String elements*/&lt;br /&gt;  this.Parts = [];&lt;br /&gt;  /* &lt;br /&gt;   Appends a new string into the array and &lt;br /&gt;   returns a reference to the String Builder so you can write:&lt;br /&gt;   Object.Append('String A').Append('String B'); &lt;br /&gt;  */&lt;br /&gt;  this.Append = function( text ) {&lt;br /&gt;   this.Parts[this.Parts.length] = text;&lt;br /&gt;   return this;&lt;br /&gt;  }&lt;br /&gt;  /* Re-Initialized the parts array for consequent calls */&lt;br /&gt;  this.Clear = function(){&lt;br /&gt;   this.Parts = [];&lt;br /&gt;  }&lt;br /&gt;  /* returns the parts as string */&lt;br /&gt;  this.ToString = function() {&lt;br /&gt;   return this.Parts.join("");&lt;br /&gt;  }&lt;br /&gt; }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;function WebRequest( startInfo )&lt;br /&gt;{&lt;br /&gt; var WebInfo = this;&lt;br /&gt; /* Structure that holds the WebService information */&lt;br /&gt; WebInfo.StartInfo = startInfo;&lt;br /&gt; /* Executes the ws call */&lt;br /&gt; WebInfo.Execute = function ( startInfo )&lt;br /&gt; {&lt;br /&gt;  if( !isNullOrEmpty( startInfo ) )&lt;br /&gt;   WebInfo.StartInfo = startInfo;&lt;br /&gt;   &lt;br /&gt;  var xmlHttp = new ActiveXObject("Msxml2.XMLHTTP"); &lt;br /&gt;  xmlHttp.open("POST", WebInfo.StartInfo.GetUrl() , WebInfo.StartInfo.Async ); //Sync Request&lt;br /&gt;  xmlHttp.setRequestHeader("Content-Type", "text/xml; charset=utf-8");&lt;br /&gt;  xmlHttp.setRequestHeader("SOAPAction", WebInfo.StartInfo.GetSoapAction() ); &lt;br /&gt;  &lt;br /&gt;  if( WebInfo.StartInfo.Async )&lt;br /&gt;  { &lt;br /&gt;   /* Calls the callback */&lt;br /&gt;   xmlHttp.onreadystatechange = function() {&lt;br /&gt;    /* &lt;br /&gt;     Calls the user callback function only when the readystate is complete &lt;br /&gt;     so the user does not have to check the readyState him self.&lt;br /&gt;    */&lt;br /&gt;    if( xmlHttp.readyState != 4 ) return; &lt;br /&gt;    WebInfo.StartInfo.Callback( xmlHttp ) &lt;br /&gt;   } ;&lt;br /&gt;   /* Execute */&lt;br /&gt;   xmlHttp.send( WebInfo.StartInfo.ToString() );&lt;br /&gt;  }&lt;br /&gt;  else&lt;br /&gt;  {&lt;br /&gt;   xmlHttp.send( WebInfo.StartInfo.ToString() );&lt;br /&gt;   return xmlHttp.responseXML;&lt;br /&gt;  }&lt;br /&gt; }&lt;br /&gt; &lt;br /&gt; function isNullOrEmpty( o ){&lt;br /&gt;  return o == null || typeof( o ) == "undefined";&lt;br /&gt; }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;OnCrmPageLoad();&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3003801964503297388-525014654188141868?l=mscrm4ever.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mscrm4ever.blogspot.com/feeds/525014654188141868/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3003801964503297388&amp;postID=525014654188141868&amp;isPopup=true' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3003801964503297388/posts/default/525014654188141868'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3003801964503297388/posts/default/525014654188141868'/><link rel='alternate' type='text/html' href='http://mscrm4ever.blogspot.com/2008/09/javascript-ajax-wrapper.html' title='JavaScript Ajax Wrapper'/><author><name>Adi Katz</name><uri>http://www.blogger.com/profile/16783428437179825705</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3003801964503297388.post-3530211734978587160</id><published>2008-09-19T13:27:00.017+03:00</published><updated>2008-09-24T18:21:06.804+03:00</updated><title type='text'>CRM 4.0 inline spell checker</title><content type='html'>I was looking integrate an inline spellchecker with Input and Textarea fields in crm. The idea was to enable spelling checks on any nvarchar field that users can activate by pressing shortcut combination keys e.g. Alt + Z. During my quest I stumbled on jianwang blog and saw the &lt;a href="http://jianwang.blogspot.com/2008/04/add-client-side-microsoft-office-word.html"&gt;following post&lt;/a&gt; which uses the local machine (user) Microsoft word spell checker. I really liked the idea but hated the clumsy UI. So eventually I decided to try integrating my &lt;a href="http://mscrm4ever.blogspot.com/2008/06/crm-custom-tooltip.html"&gt;custom tool tip post&lt;/a&gt; with a simple retrieval of the Spelling Errors suggestion list. And, as always, what started as a simple programming task turned out to be a pain in the butt since I had to mimic the spell checker UI functionality.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;I think the result came out pretty cool and since there are plenty of free spell checker out there why not add another one.&lt;br /&gt;The usage is very simple! You create an instance of the InlineSpellChecker class decide on a shortcut combination key (default keyCode is 90 [z]) add the field or fields you what to enable spell checking on and you’re done.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;The spell checker object is not called until the user presses Alt + z which mean the code does not affect load time. The first time might take a second or 2 but after that it works pretty quickly. I didn’t test it with large data so that might have an impact as well.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;The UI have 4 buttons, the first 2 allow you to navigate between the list of spelling errors and the last 2 enables you to replace the error(s) with one of the selected suggestions. The field focus is never lost so you can open the spell checker and continue writing while it is open. The user can press Alt + z while writing to refresh the spelling errors suggestion list to accommodate the new written words.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;When you select a suggestion it is painted in red. If you replace 1 occurrence then the suggestion is painter in orange. If all occurrences have been replaced the suggestion is painted in green. The current spelling error is also highlighted so the user can keep track. The image bellow displays a graphical representation of the above.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;My only disclaimer is that you give me your feedback should you choose to use it in your customer’s application.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;a href="http://3.bp.blogspot.com/_M-mdv3Tfarg/SNN_fY5oeQI/AAAAAAAAACU/TkmnHiIujUE/s1600-h/spellchecker.jpg"&gt;&lt;img id="BLOGGER_PHOTO_ID_5247678168053610754" style="DISPLAY: block; MARGIN: 0px auto 10px; CURSOR: hand; TEXT-ALIGN: center" alt="" src="http://3.bp.blogspot.com/_M-mdv3Tfarg/SNN_fY5oeQI/AAAAAAAAACU/TkmnHiIujUE/s400/spellchecker.jpg" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;div&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre class='js' name='code'&gt; &lt;br /&gt;var Spellcheker;&lt;br /&gt;&lt;br /&gt;function OnCrmPageLoad()&lt;br /&gt;{ &lt;br /&gt; Spellcheker = new InlineSpellcheker();&lt;br /&gt; Spellcheker.KeyCode = 90;&lt;br /&gt; Spellcheker.AddField("gi_name"); &lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;function InlineSpellcheker()&lt;br /&gt;{&lt;br /&gt; /* Private Fields */&lt;br /&gt; var Instance = this;&lt;br /&gt; var mswordApp;&lt;br /&gt; var activeDoc;&lt;br /&gt; &lt;br /&gt; /* Public Fields */&lt;br /&gt; Instance.KeyCode = 90; //Z&lt;br /&gt; Instance.Fields  = [];&lt;br /&gt; Instance.SBox = new SuggestionBox();&lt;br /&gt; &lt;br /&gt; /* Public Methods */&lt;br /&gt; /* Add Fields with spell checking */&lt;br /&gt; Instance.AddFields = function()&lt;br /&gt; {&lt;br /&gt;  for( var i = 0 ; i &amp;lt; arguments.length; i++ )&lt;br /&gt;  {&lt;br /&gt;   var field   = document.getElementById( arguments[ i ] ); &lt;br /&gt;   if( isNullOrEmpty( field )) return;&lt;br /&gt;   &lt;br /&gt;   Instance.Fields[ field.id ] = field;&lt;br /&gt;   field.title  = "Press Alt + ";&lt;br /&gt;   field.title += String.fromCharCode(Instance.KeyCode);&lt;br /&gt;   field.title += " to use SpellChecker";&lt;br /&gt;   &lt;br /&gt;   field.attachEvent( "onkeydown" , onSpellCheckingTest );&lt;br /&gt;   field.attachEvent( "onfocusout" , onLostFocusCleanUp ); &lt;br /&gt;  }&lt;br /&gt; }&lt;br /&gt; /* Add a Field with spell checking*/&lt;br /&gt; Instance.AddField = function( fieldId ){&lt;br /&gt;  Instance.AddFields( fieldId );&lt;br /&gt; }&lt;br /&gt; /* Kill word application */&lt;br /&gt; Instance.Quite = function()&lt;br /&gt; {&lt;br /&gt;  if( !isNullOrEmpty( mswordApp ) )&lt;br /&gt;  {&lt;br /&gt;   mswordApp.ActiveDocument.Close( 0 );&lt;br /&gt;   mswordApp.Quit( 0 );&lt;br /&gt;  }&lt;br /&gt;  activeDoc = null;&lt;br /&gt;  mswordApp = null;&lt;br /&gt; }&lt;br /&gt; &lt;br /&gt; /* Event Callbacks */&lt;br /&gt; function onLostFocusCleanUp()&lt;br /&gt; {&lt;br /&gt;  var field = Instance.Fields[ event.srcElement.id ];&lt;br /&gt;  if( isNullOrEmpty( field ) ) return;&lt;br /&gt;  &lt;br /&gt;  field.SpellingErrors = null;&lt;br /&gt;  field.Box = null;&lt;br /&gt;  Instance.SBox.Clean(); &lt;br /&gt; }&lt;br /&gt; &lt;br /&gt; function onSpellCheckingTest()&lt;br /&gt; {&lt;br /&gt;  &lt;br /&gt;  var scKeyStroke = event.altKey &amp;&amp; event.keyCode == Instance.KeyCode;&lt;br /&gt;  if( scKeyStroke == true )&lt;br /&gt;  { &lt;br /&gt;   var field = Instance.Fields[ event.srcElement.id ];&lt;br /&gt;   if( isNullOrEmpty( field ) || isNullOrEmpty( field.DataValue ) )&lt;br /&gt;    return Instance.SBox.Hide();&lt;br /&gt;   &lt;br /&gt;   Instance.SBox.Load( field );&lt;br /&gt;   &lt;br /&gt;   var result = InitializeWordObject();&lt;br /&gt;   if( result != "" ) return alert( "ok" + result );&lt;br /&gt;   &lt;br /&gt;   field.SpellingErrors = new SpellingErrors();&lt;br /&gt;   &lt;br /&gt;   var textParts = field.DataValue.split( /\W/ );&lt;br /&gt;   var foundSuggestions = false;&lt;br /&gt;   &lt;br /&gt;   for( var i = 0 ; i &amp;lt; textParts.length ; i++ )&lt;br /&gt;   {&lt;br /&gt;    var word  = textParts[ i ].replace(/\W*/gi,"");&lt;br /&gt;    if( field.SpellingErrors.GetByValue( word ) != null )&lt;br /&gt;     continue;&lt;br /&gt;    &lt;br /&gt;    try&lt;br /&gt;    { &lt;br /&gt;     activeDoc.Content = textParts[i];&lt;br /&gt;     activeDoc.LanguageDetected = false;&lt;br /&gt;     var range = activeDoc.Range(0,activeDoc.Range().End);&lt;br /&gt;      range.DetectLanguage();&lt;br /&gt;       &lt;br /&gt;     if( range.SpellingErrors.Count &amp;gt; 0 )&lt;br /&gt;     {&lt;br /&gt;      var result = range.GetSpellingSuggestions();&lt;br /&gt;      if( result.Count &amp;gt; 0 )&lt;br /&gt;      { &lt;br /&gt;       var suggestion = new Suggestion(word);&lt;br /&gt;       for( var k = 1 ; k &amp;lt; result.Count ; k++ )&lt;br /&gt;         suggestion.AddOption( result.Item( k ).name );   &lt;br /&gt;       field.SpellingErrors.Add( suggestion );&lt;br /&gt;       foundSuggestions = true;&lt;br /&gt;      }&lt;br /&gt;     }&lt;br /&gt;    }&lt;br /&gt;    catch( e )&lt;br /&gt;    {&lt;br /&gt;     Instance.Quite();&lt;br /&gt;     return alert( e.message );&lt;br /&gt;    } &lt;br /&gt;   }&lt;br /&gt;   &lt;br /&gt;   if( !foundSuggestions ) Instance.SBox.Hide();&lt;br /&gt;   else Instance.SBox.UpdateUI( 0 );&lt;br /&gt;   &lt;br /&gt;   return false;&lt;br /&gt;  }&lt;br /&gt; }&lt;br /&gt;  &lt;br /&gt; /* Private Members */&lt;br /&gt; function InitializeWordObject()&lt;br /&gt; {&lt;br /&gt;  try&lt;br /&gt;  {&lt;br /&gt;   if( !isNullOrEmpty( mswordApp ) ) return "";&lt;br /&gt;   attachEvent( "onunload" , Instance.Quite );&lt;br /&gt;   mswordApp = new ActiveXObject( "Word.Application" );&lt;br /&gt;   mswordApp.Visible = false;&lt;br /&gt;   mswordApp.Application.Visible = false;&lt;br /&gt;   activeDoc = mswordApp.Documents.Add();&lt;br /&gt;   return "";&lt;br /&gt;  }&lt;br /&gt;  catch( wordErr )&lt;br /&gt;  {&lt;br /&gt;   Instance.Quite();&lt;br /&gt;   return wordErr.message;&lt;br /&gt;  }&lt;br /&gt; }&lt;br /&gt; &lt;br /&gt; /* Classes */&lt;br /&gt; function Suggestion( word )&lt;br /&gt; {&lt;br /&gt;  /* Public Fields */&lt;br /&gt;  this.Word = word;&lt;br /&gt;  this.Replacement = "";&lt;br /&gt;  this.Options = [];&lt;br /&gt;  this.IsReplacedOnce = false;&lt;br /&gt;  this.IsReplacedAll  = false;&lt;br /&gt;  &lt;br /&gt;  /* Public Methods */&lt;br /&gt;  this.AddOption = function( name ){&lt;br /&gt;   this.Options[ this.Options.length ] = name;  &lt;br /&gt;  }&lt;br /&gt; }&lt;br /&gt; &lt;br /&gt; /* Collection of Spelling Errors Suggestions */&lt;br /&gt; function SpellingErrors()&lt;br /&gt; {&lt;br /&gt;  /* Public Fields */&lt;br /&gt;  this.Keys = [];&lt;br /&gt;  this.Vals = [];&lt;br /&gt;  &lt;br /&gt;  /* Public Methods */&lt;br /&gt;  this.Add = function( suggestion )&lt;br /&gt;  {&lt;br /&gt;   if( this.Vals[ suggestion.Word ] == null )&lt;br /&gt;   {&lt;br /&gt;    this.Keys[ this.Keys.length ] = suggestion;&lt;br /&gt;    this.Vals[ suggestion.Word  ] = suggestion;&lt;br /&gt;   }&lt;br /&gt;  }&lt;br /&gt;  /* Retrieve suggestion by word */&lt;br /&gt;  this.GetByValue = function( word ){&lt;br /&gt;   return this.Vals[ word ];&lt;br /&gt;  }&lt;br /&gt;  /* Retrieve suggestion by position */&lt;br /&gt;  this.GetByIndex = function( index )&lt;br /&gt;  {&lt;br /&gt;   if( this.Keys[ index ] != null )&lt;br /&gt;    return this.Vals[ this.Keys[ index ].Word ];&lt;br /&gt;   return null;&lt;br /&gt;  }&lt;br /&gt; }&lt;br /&gt; &lt;br /&gt; /* Suggestion Box Popup */ &lt;br /&gt; function SuggestionBox()&lt;br /&gt; {&lt;br /&gt;  /* Private Fields */&lt;br /&gt;  var Box = this;&lt;br /&gt;  var SBoxPopup;&lt;br /&gt;  &lt;br /&gt;  /* Public Fields  */&lt;br /&gt;  Box.Field = null;&lt;br /&gt;  &lt;br /&gt;  Box.Load = function( field )&lt;br /&gt;  {&lt;br /&gt;   Box.Field = field;&lt;br /&gt;   &lt;br /&gt;   var loadingHTML  = "&amp;lt;table height='100%' width='100%' style='cursor:wait'&amp;gt;";&lt;br /&gt;    loadingHTML += "&amp;lt;tr&amp;gt;&amp;lt;td valign='middle' align='center'&amp;gt;";&lt;br /&gt;    loadingHTML += "&amp;lt;img alt='' src='/_imgs/AdvFind/progress.gif'/&amp;gt;";&lt;br /&gt;    loadingHTML += "&amp;lt;div/&amp;gt;&amp;lt;b&amp;gt;Loading...&amp;lt;/b&amp;gt;";&lt;br /&gt;    loadingHTML += "&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;";&lt;br /&gt;    loadingHTML += "&amp;lt;/table&amp;gt;";&lt;br /&gt;    &lt;br /&gt;   SBoxPopup.document.all.divList.innerHTML = loadingHTML;&lt;br /&gt;   &lt;br /&gt;   var Width  = 241;     &lt;br /&gt;   var Height = 140;     &lt;br /&gt;   var Position = GetControlPostion(field);&lt;br /&gt;   var Left   = Position.X + 1;&lt;br /&gt;   var Top    = Position.Y + 1;&lt;br /&gt;       SBoxPopup.show( Left , Top , Width , Height , null ); &lt;br /&gt;  }&lt;br /&gt;  &lt;br /&gt;  /* Public Methods */&lt;br /&gt;  &lt;br /&gt;  /* Hide the popup is it exists */&lt;br /&gt;  Box.Hide = function() {     &lt;br /&gt;   if( SBoxPopup ) SBoxPopup.hide();&lt;br /&gt;  }&lt;br /&gt;  /* Clean popup control expando objects and html*/&lt;br /&gt;  Box.Clean = function()&lt;br /&gt;  {&lt;br /&gt;   with( SBoxPopup.document.all )&lt;br /&gt;   {&lt;br /&gt;    btnReplace.SelectedOption    = null;&lt;br /&gt;    btnReplaceAll.SelectedOption = null;&lt;br /&gt;    spnReplace.innerHTML = "";&lt;br /&gt;   } &lt;br /&gt;  }&lt;br /&gt;  /* Update the current selection */&lt;br /&gt;  Box.UpdateSelection = function( index , newWord )&lt;br /&gt;  {&lt;br /&gt;   var suggestion = Box.Field.SpellingErrors.GetByIndex(index);&lt;br /&gt;   if( !isNullOrEmpty( suggestion ) )&lt;br /&gt;   {&lt;br /&gt;    suggestion.Replacement = newWord;&lt;br /&gt;    with( SBoxPopup.document.all )&lt;br /&gt;    {&lt;br /&gt;     spnReplace.innerHTML = "&amp;lt;B style='color:red'&amp;gt;" + newWord + "&amp;lt;/B&amp;gt;";&lt;br /&gt;     btnReplace.SelectedOption = suggestion;&lt;br /&gt;     btnReplaceAll.SelectedOption = suggestion;&lt;br /&gt;    }&lt;br /&gt;   }&lt;br /&gt;  }&lt;br /&gt;  /* Update the Popup UI with current word selection */&lt;br /&gt;  Box.UpdateUI = function( index )&lt;br /&gt;  {&lt;br /&gt;   var spellingErrors = Box.Field.SpellingErrors;&lt;br /&gt;   if( !isNullOrEmpty( spellingErrors ) &amp;&amp; spellingErrors.Keys.length &amp;gt; 0)&lt;br /&gt;   {&lt;br /&gt;    var create = SBoxPopup.document.createElement;&lt;br /&gt;    with( SBoxPopup.document.all )&lt;br /&gt;    {&lt;br /&gt;      divList.innerHTML = "";&lt;br /&gt;     var suggestion = spellingErrors.GetByIndex( index );&lt;br /&gt;      spnWord.innerText    = suggestion.Word;&lt;br /&gt;      spnReplace.innerHTML = getReplacementHTML( suggestion );&lt;br /&gt;&lt;br /&gt;     for( var i = 0 ; i &amp;lt; suggestion.Options.length ; i++ )&lt;br /&gt;     {&lt;br /&gt;      var n = create( "SPAN" );&lt;br /&gt;       n.innerHTML = "&amp;amp;nbsp;" + ( i + 1 ) + ".&amp;amp;nbsp;";&lt;br /&gt;      &lt;br /&gt;      var a = create( "&amp;lt;A style='width:85%;padding:1px'&amp;gt;" );&lt;br /&gt;       a.href = "#";&lt;br /&gt;       a.onclick     = function(){ Box.UpdateSelection( index , this.innerText ); };&lt;br /&gt;       a.onmouseover = function(){ this.style.backgroundColor = 'gold' ; };&lt;br /&gt;       a.onmouseout  = function(){ this.style.backgroundColor = 'white'; };&lt;br /&gt;       a.Box = Box;&lt;br /&gt;       a.innerText = suggestion.Options[ i ];&lt;br /&gt;       &lt;br /&gt;      var br = create("&amp;lt;BR style='line-height:2px'&amp;gt;");&lt;br /&gt;       &lt;br /&gt;       divList.appendChild( n );&lt;br /&gt;       divList.appendChild( a );&lt;br /&gt;       divList.appendChild( br );&lt;br /&gt;     }&lt;br /&gt;     &lt;br /&gt;     var spLen = Box.Field.SpellingErrors.Keys.length;&lt;br /&gt;     var iPrev = index - 1 &amp;lt; 0 ? 0 : index - 1;&lt;br /&gt;     var iNext = index + 1 &amp;gt; spLen - 1 ? spLen - 1 : index + 1;&lt;br /&gt;     &lt;br /&gt;     btnPrev.Box    = Box;&lt;br /&gt;     btnNext.Box    = Box;&lt;br /&gt;     btnReplace.Box   = Box;&lt;br /&gt;     btnReplaceAll.Box = Box;&lt;br /&gt;     &lt;br /&gt;     btnReplace.SelectedOption = null;&lt;br /&gt;     btnReplaceAll.SelectedOption = null;&lt;br /&gt;     &lt;br /&gt;     btnPrev.onclick       = function(){ this.Box.UpdateUI( iPrev ); }&lt;br /&gt;     btnNext.onclick    = function(){ this.Box.UpdateUI( iNext ); }&lt;br /&gt;     btnReplace.onclick   = function(){ this.Box.Replace( this ); }&lt;br /&gt;     btnReplaceAll.onclick = function(){ this.Box.ReplaceAll( this );}&lt;br /&gt;    &lt;br /&gt;     var range = Box.Field.createTextRange();&lt;br /&gt;     var found = range.findText(suggestion.Word,1,2);&lt;br /&gt;     if( found )&lt;br /&gt;     {&lt;br /&gt;      range.expand(suggestion.Word);&lt;br /&gt;      range.select();&lt;br /&gt;     }&lt;br /&gt;    }&lt;br /&gt;   }&lt;br /&gt;  }&lt;br /&gt;  /* Replace a single occurance of the current error */&lt;br /&gt;  Box.Replace = function( btn )&lt;br /&gt;  {&lt;br /&gt;   var suggestion = btn.SelectedOption;&lt;br /&gt;   if( suggestion )&lt;br /&gt;   {&lt;br /&gt;    var re = new RegExp( "\\b" + btn.SelectedOption.Word + "\\b" , "i" );&lt;br /&gt;    var result = replace( btn , re );&lt;br /&gt;     suggestion.IsReplacedOnce = result != Box.Field.DataValue;&lt;br /&gt;     suggestion.IsReplacedAll  = result == Box.Field.DataValue;&lt;br /&gt;     Box.Field.DataValue = result;&lt;br /&gt;     setReplacementHTML( suggestion );&lt;br /&gt;   }&lt;br /&gt;   else&lt;br /&gt;   {&lt;br /&gt;    setNullSuggestionHTML();&lt;br /&gt;   }&lt;br /&gt;  }&lt;br /&gt;  /* Replace all occurances of the current error */&lt;br /&gt;  Box.ReplaceAll = function( btn )&lt;br /&gt;  {&lt;br /&gt;   var suggestion = btn.SelectedOption;&lt;br /&gt;   if( suggestion )&lt;br /&gt;   {&lt;br /&gt;    var re = new RegExp( "\\b" + btn.SelectedOption.Word + "\\b" , "ig" );&lt;br /&gt;    var result = replace( btn , re );&lt;br /&gt;     suggestion.IsReplacedAll = true; &lt;br /&gt;     Box.Field.DataValue = result;&lt;br /&gt;     setReplacementHTML( suggestion );&lt;br /&gt;   }&lt;br /&gt;   else&lt;br /&gt;   {&lt;br /&gt;    setNullSuggestionHTML();&lt;br /&gt;   }&lt;br /&gt;  }&lt;br /&gt;  &lt;br /&gt;  /* Private */&lt;br /&gt;  &lt;br /&gt;  function replace( btn , re )&lt;br /&gt;  {&lt;br /&gt;   var suggestion = btn.SelectedOption;&lt;br /&gt;   if( !isNullOrEmpty( Box.Field.DataValue ) &amp;&amp; &lt;br /&gt;    !isNullOrEmpty( suggestion ) )&lt;br /&gt;   {&lt;br /&gt;    return Box.Field.DataValue.replace( re , suggestion.Replacement );&lt;br /&gt;   }&lt;br /&gt;  }&lt;br /&gt;  &lt;br /&gt;  function GetControlPostion( control )&lt;br /&gt;  {     &lt;br /&gt;    var Position = new Object();     &lt;br /&gt;    var controlHeight = control.offsetHeight;     &lt;br /&gt;    var iY = 0, iX = 0;     &lt;br /&gt;    while( control != null )     &lt;br /&gt;    {           &lt;br /&gt;    iY += control.offsetTop;&lt;br /&gt;    iX += control.offsetLeft;&lt;br /&gt;    control = control.offsetParent;&lt;br /&gt;    }     &lt;br /&gt;    Position.X = iX + screenLeft;     &lt;br /&gt;    Position.Y = iY + screenTop + controlHeight;     &lt;br /&gt;    return Position;&lt;br /&gt;  }&lt;br /&gt;  &lt;br /&gt;  function Initialize()&lt;br /&gt;  {&lt;br /&gt;   SBoxPopup = window.createPopup();     &lt;br /&gt;          &lt;br /&gt;   var baseStyleUrl = "/" + ORG_UNIQUE_NAME + "/_common/styles";&lt;br /&gt;   with( SBoxPopup.document )&lt;br /&gt;   { &lt;br /&gt;    createStyleSheet( baseStyleUrl + "/global.css.aspx?lcid=" + USER_LANGUAGE_CODE );  &lt;br /&gt;    createStyleSheet( baseStyleUrl + "/fonts.aspx?lcid=" + USER_LANGUAGE_CODE );&lt;br /&gt;    createStyleSheet( baseStyleUrl + "/controls.css.aspx?lcid=" + USER_LANGUAGE_CODE );&lt;br /&gt;    body.innerHTML = getSBoxHTML();  &lt;br /&gt;   }&lt;br /&gt;  }&lt;br /&gt;  &lt;br /&gt;  function getSBoxHTML()&lt;br /&gt;  {&lt;br /&gt;   var html = "";&lt;br /&gt;    html += "&amp;lt;DIV style='width:100%;height:100%;border:1px solid gray;background-color: #d8e8ff;filter: progid:DXImageTransform.Microsoft.Gradient(GradientType=0,StartColorStr=#eff3ff,EndColorStr=#84aed6);padding-left:2px;font:12 px tahoma'&amp;gt;";&lt;br /&gt;    &lt;br /&gt;    html += "&amp;lt;TABLE cellpadding='1' cellspacing='1' style='width:100%'&amp;gt;";&lt;br /&gt;    html += "&amp;lt;TR&amp;gt;";&lt;br /&gt;    html += "&amp;lt;TD width='40%' height='20' valign='middle'&amp;gt;";&lt;br /&gt;    html += "&amp;lt;NOBR&amp;gt;";&lt;br /&gt;    html += "&amp;lt;IMG src='/_imgs/ico_mailmerge.gif' align='top'/&amp;gt;&amp;amp;nbsp;";&lt;br /&gt;    html += "&amp;lt;B&amp;gt;&amp;lt;SPAN style='margin-top:5px' id='spnWord'&amp;gt;&amp;lt;/SPAN&amp;gt;&amp;lt;/B&amp;gt;";&lt;br /&gt;    html += "&amp;lt;/NOBR&amp;gt;";&lt;br /&gt;    html += "&amp;lt;/TD&amp;gt;";&lt;br /&gt;    &lt;br /&gt;    html += "&amp;lt;TD&amp;gt;";&lt;br /&gt;    html += "&amp;lt;NOBR&amp;gt;";&lt;br /&gt;    html += "&amp;lt;IMG src='/_imgs/SetRegarding.gif' align='top'/&amp;gt;&amp;amp;nbsp;";&lt;br /&gt;    html += "&amp;lt;B&amp;gt;&amp;lt;SPAN id='spnReplace'&amp;gt;&amp;lt;/SPAN&amp;gt;&amp;lt;/B&amp;gt;";&lt;br /&gt;    html += "&amp;lt;/NOBR&amp;gt;";&lt;br /&gt;    html += "&amp;lt;/TD&amp;gt;";&lt;br /&gt;    &lt;br /&gt;    html += "&amp;lt;TR&amp;gt;";&lt;br /&gt;    html += "&amp;lt;TD&amp;gt;";&lt;br /&gt;    html += "&amp;lt;BUTTON id='btnPrev' class='ms-crm-Button' style='height:25'&amp;gt;Previous&amp;lt;/BUTTON&amp;gt;";&lt;br /&gt;    html += "&amp;lt;/TD&amp;gt;";&lt;br /&gt;    html += "&amp;lt;TD rowspan='4' width='65%' height='20'&amp;gt;";&lt;br /&gt;    html += "&amp;lt;DIV id='divList' style='overflow-y:scroll;width:95%;height:110;border:1px solid black;background-color:white'&amp;gt;";&lt;br /&gt;    html += "&amp;lt;/DIV&amp;gt;";&lt;br /&gt;    html += "&amp;lt;/TD&amp;gt;";&lt;br /&gt;    html += "&amp;lt;/TR&amp;gt;";&lt;br /&gt;    &lt;br /&gt;    html += "&amp;lt;TR&amp;gt;";&lt;br /&gt;    html += "&amp;lt;TD&amp;gt;";&lt;br /&gt;    html += "&amp;lt;BUTTON id='btnNext' class='ms-crm-Button' style='height:25'&amp;gt;Next&amp;lt;/BUTTON&amp;gt;";&lt;br /&gt;    html += "&amp;lt;/TD&amp;gt;";&lt;br /&gt;    html += "&amp;lt;/TR&amp;gt;";&lt;br /&gt;    &lt;br /&gt;    html += "&amp;lt;TR&amp;gt;";&lt;br /&gt;    html += "&amp;lt;TD&amp;gt;";&lt;br /&gt;    html += "&amp;lt;BUTTON id='btnReplace' class='ms-crm-Button' style='height:25'&amp;gt;Replace&amp;lt;/BUTTON&amp;gt;";&lt;br /&gt;    html += "&amp;lt;/TD&amp;gt;";&lt;br /&gt;    html += "&amp;lt;/TR&amp;gt;";&lt;br /&gt;    &lt;br /&gt;    html += "&amp;lt;TR&amp;gt;";&lt;br /&gt;    html += "&amp;lt;TD&amp;gt;";&lt;br /&gt;    html += "&amp;lt;BUTTON id='btnReplaceAll' class='ms-crm-Button' style='height:25'&amp;gt;Replace All&amp;lt;/BUTTON&amp;gt;";&lt;br /&gt;    html += "&amp;lt;/TD&amp;gt;";&lt;br /&gt;    html += "&amp;lt;/TR&amp;gt;";&lt;br /&gt;    &lt;br /&gt;    html += "&amp;lt;/DIV&amp;gt;"; &lt;br /&gt;   return html;&lt;br /&gt;  }&lt;br /&gt;  &lt;br /&gt;  function getReplacementColor( suggestion ){&lt;br /&gt;   if( suggestion.IsReplacedAll ) return "green";&lt;br /&gt;   else if( suggestion.IsReplacedOnce ) return "orange";&lt;br /&gt;   else return "red";&lt;br /&gt;  } &lt;br /&gt;  &lt;br /&gt;  function getReplacementHTML( suggestion ){&lt;br /&gt;   var color = getReplacementColor( suggestion );&lt;br /&gt;   return "&amp;lt;B style='color:" + color + "'&amp;gt;" + suggestion.Replacement + "&amp;lt;/B&amp;gt;";&lt;br /&gt;  }&lt;br /&gt;  &lt;br /&gt;  function setReplacementHTML( suggestion ){&lt;br /&gt;   SBoxPopup.document.all.spnReplace.innerHTML = getReplacementHTML( suggestion );&lt;br /&gt;  }&lt;br /&gt; &lt;br /&gt;  function setNullSuggestionHTML(){&lt;br /&gt;   SBoxPopup.document.all.spnReplace.innerHTML = "&amp;lt;B&amp;gt;Select a suggestion&amp;lt;/b&amp;gt;"; &lt;br /&gt;  }&lt;br /&gt;  &lt;br /&gt;  Initialize();&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; //Utility&lt;br /&gt; function isNullOrEmpty( o ){&lt;br /&gt;  return o == null || typeof( o ) == "undefined" || o == "";&lt;br /&gt; }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;OnCrmPageLoad();&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3003801964503297388-3530211734978587160?l=mscrm4ever.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://mscrm4ever.blogspot.com/feeds/3530211734978587160/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3003801964503297388&amp;postID=3530211734978587160&amp;isPopup=true' title='27 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3003801964503297388/posts/default/3530211734978587160'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3003801964503297388/posts/default/3530211734978587160'/><link rel='alternate' type='text/html' href='http://mscrm4ever.blogspot.com/2008/09/crm-40-inline-spell-checker.html' title='CRM 4.0 inline spell checker'/><author><name>Adi Katz</name><uri>http://www.blogger.com/profile/16783428437179825705</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_M-mdv3Tfarg/SNN_fY5oeQI/AAAAAAAAACU/TkmnHiIujUE/s72-c/spellchecker.jpg' height='72' width='72'/><thr:total>27</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3003801964503297388.post-2205831084857472963</id><published>2008-09-17T02:39:00.008+03:00</published><updated>2008-10-27T17:47:03.202+02:00</updated><title type='text'>Cloning an Entity Part 3 – using Ajax</title><content type='html'>This is a complementary post to my two previous post regarding how to &lt;a href="http://mscrm4ever.blogspot.com/2008/06/cloning-entity-using-javascript.html"&gt;clone an entity using JavaScript&lt;/a&gt;.The main idea, as the title suggests, is to build a soap envelope utilizing the create message gather the entity values and send them to &lt;a href="http://msdn.microsoft.com/en-us/library/cc677070.aspx"&gt;CrmService.Create&lt;/a&gt; web method. The method returns a create result of the newly created (cloned) entity which is used to create a valid entity url&lt;br /&gt;e.g. edit.aspx?id={cloned entity id}.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;I also included a dynamic popup similar to ms yellow customization popup since this is a server side operation. If an error is returned then the popup inner message is changed and displays the error information.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;I added a simple prompt (OnCrmPageLoad) instead of an ISV button. You can check the “&lt;a href="http://mscrm4ever.blogspot.com/2008/06/cloning-entity-using-javascript.html"&gt;cloning an entity using JavaScript&lt;/a&gt;” for details on how to implement the button.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;if you don’t want to include the popup in your solution , should you choose this method for cloning, then simply set the ajaxCloner.Progress.Visible to false. If you need to clone specific fields then use the AddField method. If you don’t use the method the cloner will send all non null fields to CrmService.Create. Finally use the Clone method to rap up the process.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;I bet some of you are asking your self why not send the entity id and selected attributes names to a custom web method and do every thing there. The answer is that you can. The major advantage this method has to offer over a custom web method is that it saves you a Query(expression) to crm since the entity values are already available to you. On the other hand you're sending more information over the network&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;This might seem a lot, i hope you finod this to be self explanatory.&lt;br /&gt;I added general summery remarks on top of each functional code block and direct links to ms sdk files above.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;a href="http://2.bp.blogspot.com/_M-mdv3Tfarg/SNBFRlz5xKI/AAAAAAAAACM/dT5Erc2Sg2U/s1600-h/cloningentityajax.jpg"&gt;&lt;img id="BLOGGER_PHOTO_ID_5246769734396527778" style="BORDER-RIGHT: black 1px solid; BORDER-TOP: black 1px solid; DISPLAY: block; MARGIN: 0px auto 10px; BORDER-LEFT: black 1px solid; CURSOR: hand; BORDER-BOTTOM: black 1px solid; TEXT-ALIGN: center" alt="" src="http://2.bp.blogspot.com/_M-mdv3Tfarg/SNBFRlz5xKI/AAAAAAAAACM/dT5Erc2Sg2U/s400/cloningentityajax.jpg" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre class="js" name="code"&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;/* simulate ISV button */&lt;br /&gt;function OnCrmPageLoad()&lt;br /&gt;{&lt;br /&gt; if( prompt("Execute?") == "ok" )&lt;br /&gt;     CloneEntity(); &lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;/*&lt;br /&gt;Call this method from the ISV button.&lt;br /&gt;Create a new cloner, set whether to show a progress popup.&lt;br /&gt;Set the popup default message.&lt;br /&gt;Add specific candidate fields (optional). Otherwise all the form is cloned&lt;br /&gt;Start the cloning process&lt;br /&gt;*/&lt;br /&gt;function CloneEntity()&lt;br /&gt;{&lt;br /&gt; var ajaxCloner = new AjaxCloner();&lt;br /&gt;     ajaxCloner.Progress.Visible = true;&lt;br /&gt;     ajaxCloner.Progress.Message = "Cloning Account";&lt;br /&gt;     //ajaxCloner.AddField(“firstname”);&lt;br /&gt;     ajaxCloner.Clone();&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;/*&lt;br /&gt;Cloner class.&lt;br /&gt;Sets the progress popup default values.&lt;br /&gt;AddField – add new candidate fields to be cloned&lt;br /&gt;Clone – Start the cloning process&lt;br /&gt;*/&lt;br /&gt;function AjaxCloner()&lt;br /&gt;{&lt;br /&gt; var Instance = this;&lt;br /&gt;&lt;br /&gt; Instance.Progress = new ProgressPopup();&lt;br /&gt; Instance.Progress.Visi
