2009-10-29

How to determine on which pipeline a plugin is executing - Parent or Childe

The InvocationSource property is an integer value that you can use to determine whether the current plug-in is running in a child pipeline.

MessageInvocationSource Values
FieldValueDescription
Child 1 Specifies a child pipeline
Parent 0 Specifies a parent pipeline

2009-10-20

MS CRM Fetch and Retrieve from javascript

This code snippet was writing exactly like recommended in MSCRM SDK 4.09.
I was put it in OnLoad() event of my account form, and in the lookup field(at my example - new_postalcodeid) at OnChange() event I was calling my function.

There is two function that are doing the same work but with different methods.

var authenticationHeader = GenerateAuthenticationHeader();
crmForm.all.address1_city.ForceSubmit = true;

window.GetRegionByIndex_Retrieve = function()  
{  
 
if (crmForm.all.new_postalcodeid.DataValue == null)
{
  crmForm.all.address1_city.Disabled = false;
 crmForm.all.address1_city.DataValue = "";
 return;
}

var lookupItem = new Array;

lookupItem = crmForm.all.new_postalcodeid.DataValue;
if(lookupItem[0] != null)
{
 // Prepare the SOAP message.
 var xml = "<?xml version='1.0' encoding='utf-8'?>"+ 
 "<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'>"+ 
 authenticationHeader+ 
 "<soap:Body>"+ 
 "<Retrieve xmlns='http://schemas.microsoft.com/crm/2007/WebServices'>"+ 
 "<entityName>new_postalcode</entityName>"+ 
 "<id>"+lookupItem[0].id+"</id>"+ 
 "<columnSet xmlns:q1='http://schemas.microsoft.com/crm/2006/Query' xsi:type='q1:ColumnSet'>"+ 
 "<q1:Attributes>"+ 
 "<q1:Attribute>new_regcityarea</q1:Attribute>"+ 
 "</q1:Attributes>"+ 
 "</columnSet>"+ 
 "</Retrieve>"+ 
 "</soap:Body>"+ 
 "</soap:Envelope>";
 // Prepare the xmlHttpObject and send the request.
 var xHReq = new ActiveXObject("Msxml2.XMLHTTP");
 xHReq.Open("POST", "/mscrmservices/2007/CrmService.asmx", false);
 xHReq.setRequestHeader("SOAPAction","http://schemas.microsoft.com/crm/2007/WebServices/Retrieve");
 xHReq.setRequestHeader("Content-Type", "text/xml; charset=utf-8");
 xHReq.setRequestHeader("Content-Length", xml.length);
 xHReq.send(xml);
 // Capture the result.
 var resultXml = xHReq.responseXML;

 // Check for errors.
 var errorCount = resultXml.selectNodes('//error').length;
 if (errorCount != 0)
 {
 var msg = resultXml.selectSingleNode('//description').nodeTypedValue;
 alert(msg);
 }
 // Display the retrieved value.
 else
 {
 crmForm.all.address1_city.DataValue = resultXml.selectSingleNode("//q1:new_regcityarea").nodeTypedValue;
 crmForm.all.address1_city.Disabled = true;
 }
}
}



window.GetRegionByIndex_Fetch = function (){
if (crmForm.all.new_postalcodeid.DataValue == null)
{
  crmForm.all.address1_city.Disabled = false;
 crmForm.all.address1_city.DataValue = "";
 return;
}

var lookupItem = new Array;

lookupItem = crmForm.all.new_postalcodeid.DataValue;
if(lookupItem[0] != null)
{
 var xml = "<?xml version='1.0' encoding='utf-8'?>" +
 "<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'>" +
 authenticationHeader +
 "<soap:Body>" +
 "<Fetch xmlns='http://schemas.microsoft.com/crm/2007/WebServices'>" +
 "<fetchXml>" +
 "&lt;fetch mapping='logical'&gt;" +
 "&lt;entity name='new_postalcode'&gt;" +
 "&lt;attribute name='new_postalcodeid'/&gt;"+
 "&lt;attribute name='new_regcityarea'/&gt;"+
 "&lt;filter type='and'&gt;"+
 "&lt;condition attribute='new_postalcodeid' operator='eq' value='"+lookupItem[0].id+"'/&gt;"+
 "&lt;/filter&gt;&lt;/entity&gt;&lt;/fetch&gt;" +
 "</fetchXml>" +
 "</Fetch>" +
 " </soap:Body>" +
 "</soap:Envelope>";

 var xmlHttpRequest = new ActiveXObject("Msxml2.XMLHTTP");
 xmlHttpRequest.Open("POST", "/mscrmservices/2007/CrmService.asmx", false);
 xmlHttpRequest.setRequestHeader("SOAPAction","http://schemas.microsoft.com/crm/2007/WebServices/Fetch");
 xmlHttpRequest.setRequestHeader("Content-Type", "text/xml; charset=utf-8");
 xmlHttpRequest.setRequestHeader("Content-Length", xml.length);
 xmlHttpRequest.send(xml);
 
 var resultXml = xmlHttpRequest.responseXML;
 // Check for errors.
 var errorCount = resultXml.selectNodes('//error').length;
 if (errorCount != 0)
 {
  var msg = resultXml.selectSingleNode('//description').nodeTypedValue;
  alert(msg);
 }
 // Process and display the results.
 else
 {

  // Capture the result and UnEncode it.
  var resultSet = new String();
  resultSet = resultXml.text;
  resultSet.replace('&lt;','<');
  resultSet.replace('&gt;','>');

  var xmlDoc = new ActiveXObject("Microsoft.XMLDOM");
  xmlDoc.async = false;
  xmlDoc.loadXML(resultSet);
  var results = xmlDoc.getElementsByTagName('result');
  
  for(i=0;i<results.length;i++)
  {
   crmForm.all.address1_city.DataValue = results[i].selectSingleNode('//new_regcityarea').nodeTypedValue;
  }
   crmForm.all.address1_city.Disabled = true;
 }
}
}

2009-10-13

How to retrieve from many-to-many entity.

If you need to retrieve something from many-to-many relationship entity, and there is no such entity in CRM, then you should use the FetchXml, instead of Query.
// Base bodel of FetchXml for get relationship entity
string fetchXML =
"<fetch distinct='false' mapping='logical'>" +
"<entity name='" + relationshipName + "'><filter type='and'>" +
"<condition attribute='" + relatedEntityName + "id' operator='eq' value='" + relatedId + "' />" +
"<condition attribute='" + typeEntityName + "id' operator='eq' value='" + profileId + "' />" +
"</filter></entity>" + "</fetch>";

For example, next code snippet is checking a count of relations between the Account entity and New_Industry entity, wich relationship is many-to-many. To achieve this, I need to check the relationship entity - New_industry_account, wich is contains all of relation of our two entities.
ICrmService crmService = context.CreateCrmService(true);

// check for relation
#region FetchXML
string fetchXML =
"<fetch distinct='false' mapping='logical'>" +
"<entity name='new_industry_account'><filter type='and'>" +
"<condition attribute='accountid' operator='eq' value='" + accountid.ToString() + "' />" +
"</filter></entity></fetch>";
#endregion

string fetchresult = crmService.Fetch(fetchXML);
XmlDocument xmldoc = new XmlDocument();
xmldoc.LoadXml(fetchresult);
XmlNodeList xnodlist = xmldoc.SelectNodes("resultset/result");
if (xnodlist.Count > 0) { return; }

Update a number of custom attributes of DynamicEntity with TargetUpdateDynamic.

This code I was used in plugins for updating the custom attributes using Microsoft.Crm.SDK.

using System;
using System.Collections.Generic;
using System.Collections;

// Microsoft Dynamics CRM namespaces
using Microsoft.Crm.Sdk;
using Microsoft.Crm.Sdk.Query;
using Microsoft.Crm.SdkTypeProxy;

...

// Extract the DynamicEntity from the request.
entity = (DynamicEntity)retrieved.BusinessEntity;

// declare a property array
ArrayList arrProps = new ArrayList();

// create some property
CrmBooleanProperty new_withindustry = new CrmBooleanProperty();
new_withindustry.Name="new_withindustry";
new_withindustry.Value= new CrmBoolean();
new_withindustry.Value.Value=true;
arrProps.Add(new_withindustry);

// create another property
CrmMoneyProperty new_industrydeal = new CrmMoneyProperty();
new_industrydeal.Name = "new_industrydeal";
new_industrydeal.Value = new CrmMoney();
new_industrydeal.Value.Value = 12345.0m;
arrProps.Add(new_industrydeal);

//   Update the properties array on the DynamicEntity.
entity.Properties.AddRange((Property[])arrProps.ToArray(typeof(Property)));

// Create the update target.
TargetUpdateDynamic updateDynamic = new TargetUpdateDynamic();

// Set the properties of the target.
updateDynamic.Entity = entity;

//   Create the update request object.
UpdateRequest update = new UpdateRequest();

//   Set request properties.
update.Target = updateDynamic;

//   Execute the request.
UpdateResponse updated = (UpdateResponse)crmService.Execute(update);

Dynamic hyperlink in Reporting Services 2008

I had a custom entity - project and was needed to open a project card from the report.
So I had done exactly as saying in http://msdn.microsoft.com/en-us/library/ms157159.aspx and in the expression field of hyperlink I was typed next

="http://SERVERNAME/ORGANIZATIONNAME/userdefined/edit.aspx?id=%7b" & Fields!projectid.Value.ToString() & "%7d&etc=10005"

SERVERNAME and ORGANIZATIONNAME must be replaced with your crm values.

2009-10-12

Registering plugins for event on many-to-many entities

I found that from the box MS CRM 4.0 are not supported the events on many-to-many relationship entities(bridge entities). But Aaron Elder has hack it.

All you need is just register it at AssociateEntities or DiassociateEntities event with empty Primary and Secondary Entity fields.

-- ============================================================================
-- Enable Associate and Disassociate Plug-in Events Script v1.0
-- ----------------------------------------------------------------------------
-- (c) 2009 Aaron Elder
-- ============================================================================
-- DISCLAIMER:
-- This script is provided "AS IS" with no warranties, and confers no rights.
-- ============================================================================
-- While this is obviously "unsupported", I think the fact that these events
-- are not available is a bug and hopefully it will be fixed in a rollup.
-- ============================================================================

USE AscentiumCrmDev_MSCRM
GO

-- Find the deployments SDK Filter ID for the
-- Associate and Disassociate Entity SDK Messages
DECLARE @DisassociateEntitiesFilterId uniqueidentifier
DECLARE @AssociateEntitiesFilterId uniqueidentifier
SET @DisassociateEntitiesFilterId = (SELECT SdkMessageId FROM SdkMessageBase WHERE [Name] = 'DisassociateEntities')
SET @AssociateEntitiesFilterId = (SELECT SdkMessageId FROM SdkMessageBase WHERE [Name] = 'AssociateEntities')

-- Enable the Associate and Disassociate Filters to be valid for custom processing
-- Custom Processing means "you register plug-ins against it"
-- Note: We only do this for the "generic" (OTC == 0) case, just to be safer
UPDATE SdkMessageFilterBase SET IsCustomProcessingStepAllowed = 1
       WHERE SdkMessageId = @DisassociateEntitiesFilterId AND PrimaryObjectTypeCode = 0


UPDATE SdkMessageFilterBase SET IsCustomProcessingStepAllowed = 1
       WHERE SdkMessageId = @AssociateEntitiesFilterId AND PrimaryObjectTypeCode = 0
(C)
http://consulting.ascentium.com/blog/crm/Post533.aspx

2009-10-07

Window redirect and close

This was the window that was opened from CRM form menu (ISV.Config.xml). The window has received some parameters and then redirected to another window, which is preparing and opening the MS Word document, but the pop-up window was still open - this is rude. So, I've closed it.

// call this code on body onload event
function redirect()
{
    // needed URL of pop-up window or modify current location URL
    window.opener.location = window.location.href.replace(/Default2/, "Default");
    window.top.close();
}

Close window from server-side

// closethe page if successful update
RegisterStartupScript("load", "<" + "script type=\"text/javascript\">\n" +
"self.close();\n" +
"<" + "/script>");

Dim strscript As String = "<"+"script language=javascript>window.top.close();<"+"/script>"
If (Not ClientScript.IsStartupScriptRegistered("clientScript")) Then
ClientScript.RegisterStartupScript(Page.GetType(), "clientScript", strscript)
End If

System.Text.StringBuilder sb = System.Text.StringBuilder ();

sb.Append("");

if(!IsClientScriptRegistered("myScript",sb.ToString())
   this.RegisterClientScriptBlock("myScript",sb.ToString());

All of code was found in Internet

2009-10-05

Mask the phone fields

I was need some mask on the "mobilephone" field in contact card. So I was combine some google search result with my experience and got next code:
//base maskphone function 
window.applyMask = function()
{
    // var of fields wich must be masked
    var ele = document.getElementById("mobilephone");

var FormatPhoneHandler=function()
    {
      var PH = this.value;
      var re = /\D/g;
      // +7 - this is country code of Russia, so this is decision only for one country
      PH = PH.replace("+7","");
      PH = PH.replace(re,"");
     
      if(PH.length <1)
      {
          this.value = "";
      }
      else if(PH.length <= 3)
      {
            this.value = "+7 (" + PH;
      }
      else if(PH.length > 3 && PH.length <= 6)
      {
            this.value = "+7 (" + PH.substring(0,3) + ") " + PH.substring(3,6);
      }
      else if(PH.length > 6)
      {
            this.value = "+7 (" + PH.substring(0,3) + ") " + PH.substring(3,6) + "-" + PH.substring(6,10);
      }
    }

    ele.onkeyup = FormatPhoneHandler;
}

// turn it on
applyMask();

Place this code in onLoad event in the entity wich you need and don't forget change names of the field for the right one.