Friday, November 26, 2010

Read QueryString in Jscript

function PageQuery( q )
{
    if( q.length > 1 ) this.q = q.substring( 1, q.length );
    else this.q = null;
    this.keyValuePairs = new Array();
    if( q )
    {
        for( var i=0; i < this.q.split( "&" ).length; i++ )
        {
            this.keyValuePairs[i] = this.q.split( "&" )[i];
        }
    }
    this.getKeyValuePairs = function() { return this.keyValuePairs; }
    this.getValue = function( s )
    {
        for( var j=0; j < this.keyValuePairs.length; j++ )
        {
            if( this.keyValuePairs[j].split( "=" )[0] == s )
            return this.keyValuePairs[j].split( "=" )[1];
        }
        return false;
    }
    this.getParameters = function()
    {
        var a = new Array( this.getLength() );
        for( var j=0; j < this.keyValuePairs.length; j++ )
        {
            a[j] = this.keyValuePairs[j].split( "=" )[0];
        }
        return a;
    }
    this.getLength = function() { return this.keyValuePairs.length; }
}


function queryString( key )
{
    var page = new PageQuery( window.location.search );
    return unescape( page.getValue( key ) );
}

alert(queryString('id'));

This will provide the record Guid as alert, reading the value from queryString.

Thursday, November 25, 2010

Email Validation

//To Check for proper email address
if(crmForm.all.emailaddress1!=null && crmForm.all.emailaddress1.DataValue!=null)
{
                    var isval = validateEmail(crmForm.all.emailaddress1.DataValue);
                     if(isval==false){
                     event.returnValue = false;
                     crmForm.all.emailaddress1.DataValue = null;
return false;}
}
if(crmForm.all.emailaddress2!=null && crmForm.all.emailaddress2.DataValue!=null)
{
                     isval = validateEmail(crmForm.all.emailaddress2.DataValue);
                     if(isval==false){
                     event.returnValue = false;
                     crmForm.all.emailaddress2.DataValue = null;
return false;}

}

if(crmForm.all.emailaddress3!=null && crmForm.all.emailaddress3.DataValue!=null)
{
                   isval =  validateEmail(crmForm.all.emailaddress3.DataValue);
                     if(isval==false){
                     event.returnValue = false;
                     crmForm.all.emailaddress3.DataValue = null;
return false;}

}

function validateEmail(mailstring)
{
     var searchStr='@';
     var isEmail = mailstring.indexOf(searchStr);
     var searchStr2 = '.';
     var isValid = mailstring.lastIndexOf(searchStr2);
if(isEmail == '-1' || isValid == '-1' || (isValid < isEmail) || (isValid == (mailstring.length-1)) || (isValid > (mailstring.length-3)))
{
    alert('Please enter proper email address');
     return false;
}


}

Restrict Future date in CRM and restrict save event

To avoid future date in a date field in CRM Form and restrict save in such cases:

var today = new Date();

var filledDate = crmForm.all.new_testdate.DataValue;

if(filledDate > today)
{
   alert('filled in date is a future date. please enter appropriate date.');

 event.returnValue = false;
     return false;
}


To restrict save of crmfrom, we need to execute the following script onsave of the form.

 event.returnValue = false;
     return false; // if this line is not added, the rest of the script that follows this will get executed. 

Monday, November 22, 2010

Social Networking accelerator for Microsoft dynamics CRM

MS released a SNA for twitter integration with MS CRM.SNA has stopped working since august, because twitter has changed the authentication mechanism. But SNA still uses the old basic authentication mechanism to authenticate to twitter. Hence SNA doesn't work.

To make this work, we need to modify the authentication mechanism. i wasn't successful in trying this with workflows, as in SNA. hence, i made it out as a webpage. The application uses the same entities for twitter integartion as in existing SNA. 

If any help is required, in this regard, pl get in touch. 

Thursday, November 18, 2010

Retrieve roles of loggedin user in Jscript

var roleName="SalesRep";


var isSalesrep = UserHasRole(roleName);





function UserHasRole(roleName)
{
var oXml = GetCurrentUserRoles();
                     var found = false;
                     if(oXml != null)
{
var roles = oXml.selectNodes("//BusinessEntity/q1:name");
if(roles != null)
{

for( i = 0; i < roles.length; i++)
{
if(roles[i].text == roleName)
{
found = true;
return found;
}
}

}
}
return found;
}
function GetCurrentUserRoles()
{
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\">" + 


GenerateAuthenticationHeader() + 


" <soap:Body>" + 


" <RetrieveMultiple xmlns=\"http://schemas.microsoft.com/crm/2007/WebServices\">" + 


" <query xmlns:q1=\"http://schemas.microsoft.com/crm/2006/Query\" xsi:type=\"q1:QueryExpression\">" + 


" <q1:EntityName>role</q1:EntityName>" + 


" <q1:ColumnSet xsi:type=\"q1:ColumnSet\">" + 


" <q1:Attributes>" + 


" <q1:Attribute>name</q1:Attribute>" + 


" </q1:Attributes>" + 


" </q1:ColumnSet>" + 


" <q1:Distinct>false</q1:Distinct>" + 


" <q1:LinkEntities>" + 


" <q1:LinkEntity>" + 


" <q1:LinkFromAttributeName>roleid</q1:LinkFromAttributeName>" + 


" <q1:LinkFromEntityName>role</q1:LinkFromEntityName>" + 


" <q1:LinkToEntityName>systemuserroles</q1:LinkToEntityName>" + 


" <q1:LinkToAttributeName>roleid</q1:LinkToAttributeName>" + 


" <q1:JoinOperator>Inner</q1:JoinOperator>" + 


" <q1:LinkEntities>" + 


" <q1:LinkEntity>" + 


" <q1:LinkFromAttributeName>systemuserid</q1:LinkFromAttributeName>" + 


" <q1:LinkFromEntityName>systemuserroles</q1:LinkFromEntityName>" + 


" <q1:LinkToEntityName>systemuser</q1:LinkToEntityName>" + 


" <q1:LinkToAttributeName>systemuserid</q1:LinkToAttributeName>" + 


" <q1:JoinOperator>Inner</q1:JoinOperator>" + 


" <q1:LinkCriteria>" + 


" <q1:FilterOperator>And</q1:FilterOperator>" + 


" <q1:Conditions>" + 


" <q1:Condition>" + 


" <q1:AttributeName>systemuserid</q1:AttributeName>" + 


" <q1:Operator>EqualUserId</q1:Operator>" + 


" </q1:Condition>" + 


" </q1:Conditions>" + 


" </q1:LinkCriteria>" + 


" </q1:LinkEntity>" + 


" </q1:LinkEntities>" + 


" </q1:LinkEntity>" + 


" </q1:LinkEntities>" + 


" </query>" + 


" </RetrieveMultiple>" + 


" </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/RetrieveMultiple");
xmlHttpRequest.setRequestHeader("Content-Type", "text/xml; charset=utf-8");
xmlHttpRequest.setRequestHeader("Content-Length", xml.length);
xmlHttpRequest.send(xml);
var resultXml = xmlHttpRequest.responseXML;
                     return(resultXml);
}

To get the loggedin user id through Jscript

   var userId=getCurrentUser().toString ().toLowerCase();
   userId=userId.replace('{','').replace('}','').toLowerCase();






function getCurrentUser()
{
    var systemUserId="";
    if(AUTHENTICATION_TYPE==0)//
    {
        var soapBody = "<soap:Body>"+"<Execute xmlns='http://schemas.microsoft.com/crm/2007/WebServices'>"+"<Request xsi:type='WhoAmIRequest' />"+"</Execute></soap:Body>";
        var soapXml = "<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'>";
        soapXml += "<soap:Header><CrmAuthenticationToken xmlns='http://schemas.microsoft.com/crm/2007/WebServices'><AuthenticationType xmlns='http://schemas.microsoft.com/crm/2007/CoreTypes'>0</AuthenticationType><OrganizationName xmlns='http://schemas.microsoft.com/crm/2007/CoreTypes'>"+ORG_UNIQUE_NAME+"</OrganizationName><CallerId xmlns='http://schemas.microsoft.com/crm/2007/CoreTypes'>00000000-0000-0000-0000-000000000000</CallerId></CrmAuthenticationToken></soap:Header>";
        soapXml += soapBody;
        soapXml += "</soap:Envelope>";
        // Create the XMLHTTP object for the execute method.
        var xmlhttp = new ActiveXObject("Msxml2.XMLHTTP");
        xmlhttp.open("POST", "/mscrmservices/2007/CrmService.asmx", false);
        xmlhttp.setRequestHeader("Content-Type", "text/xml; charset=utf-8");
        xmlhttp.setRequestHeader("SOAPAction", "http://schemas.microsoft.com/crm/2007/WebServices/Execute");
        //Send the XMLHTTP object.
        xmlhttp.send(soapXml);
        // Create an XML object to parse the results.
        xmlDoc=new ActiveXObject("Microsoft.XMLDOM");
        xmlDoc.async=false;
        xmlDoc.loadXML(xmlhttp.responseXML.xml);
        // Get the user's ID.
        var userid = xmlDoc.getElementsByTagName("UserId")[0].childNodes[0].nodeValue;
        systemUserId=userid;
    }
    else
    if(AUTHENTICATION_TYPE==2)//IFD
    {
        //Create the XML that will fetch the required info.
        //You can inspect this web service call using a tool called FIDDLER.
        var XMLRequest = "" + 
        "<?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\">" + GenerateAuthenticationHeader() +
        " <soap:Body>" + 
        " <RetrieveMultiple xmlns=\"http://schemas.microsoft.com/crm/2007/WebServices\">" + 
        " <query xmlns:q1=\"http://schemas.microsoft.com/crm/2006/Query\" xsi:type=\"q1:QueryExpression\">" + 
        " <q1:EntityName>systemuser</q1:EntityName>" + 
        " <q1:ColumnSet xsi:type=\"q1:ColumnSet\">" + 
        " <q1:Attributes>" + 
        " <q1:Attribute>systemuserid</q1:Attribute>" + 
        " <q1:Attribute>fullname</q1:Attribute>" + 
        " </q1:Attributes>" + 
        " </q1:ColumnSet>" + 
        " <q1:Distinct>false</q1:Distinct>" + 
        " <q1:Criteria>" + 
        " <q1:FilterOperator>And</q1:FilterOperator>" + 
        " <q1:Conditions>" + 
        " <q1:Condition>" + 
        " <q1:AttributeName>systemuserid</q1:AttributeName>" + 
        " <q1:Operator>EqualUserId</q1:Operator>" + 
        " </q1:Condition>" + 
        " </q1:Conditions>" + 
        " </q1:Criteria>" + 
        " </query>" + 
        " </RetrieveMultiple>" + 
        " </soap:Body>" + 
        "</soap:Envelope>" + 
        "";


        try
        {
            //Create Http request object
            var xmlHttpRequest = new ActiveXObject("Msxml2.XMLHTTP");
            xmlHttpRequest.Open("POST", "/mscrmservices/2007/CrmService.asmx", false);
            xmlHttpRequest.setRequestHeader("SOAPAction","http://schemas.microsoft.com/crm/2007/WebServices/RetrieveMultiple");
            xmlHttpRequest.setRequestHeader("Content-Type", "text/xml; charset=utf-8");
            xmlHttpRequest.setRequestHeader("Content-Length", XMLRequest.length);
            xmlHttpRequest.send(XMLRequest);
            //Store the response which would be XML
            var Result = xmlHttpRequest.responseXML;
            /*
            The return is of type "BusinessEntity" if you were using similar code one server side.
            Hence we need to select node of type "BusinessEntity"
            In our case It should be not more one than one node
            */
            var BusinessEntityNodes = Result.selectNodes("//RetrieveMultipleResult/BusinessEntities/BusinessEntity");


            // Check If data was retrived
            if (BusinessEntityNodes.length != 0)
            {
                var BusinessEntityNode = BusinessEntityNodes[0]; 
                var SystemUserId = BusinessEntityNode.selectSingleNode("q1:systemuserid");
                var FullName = BusinessEntityNode.selectSingleNode("q1:fullname");
                var SystemUserId = (SystemUserId == null) ? null : SystemUserId.text;
                var FullName = (FullName == null) ? null : FullName.text;
            }
               systemUserId=SystemUserId ;
        }
        catch (e)
        {
            alert(e.message);
        }
    }//Else for IFD end
    systemUserId=systemUserId.replace('{','');
    systemUserId=systemUserId.replace('}','');
    systemUserId=systemUserId.toLowerCase();
    return systemUserId
}

Impersonation in custom aspx page for logged in user

To execute crmservice calls from the logged in user context




CrmService crmservice = new CrmService();
            string orgName = "contoso";

            using (new CrmImpersonator())
            {
                CrmAuthenticationToken token = CrmAuthenticationToken.ExtractCrmAuthenticationToken(Context, orgName);
                token.AuthenticationType = 0;
                crmservice.Url = "http://crm//MSCRMServices//2007//crmservice.asmx";
                crmservice.CrmAuthenticationTokenValue = token;
                crmservice.Credentials = CredentialCache.DefaultCredentials;




                try
                {
                    //Any crmservice call 
                   //crmservice.Execute
                }
                catch (Exception ex)
                {
                }


            }


Remember that the calls have to be executed within the using block.
Else, it results in an exception. 

Fetchxml through advanced find

Run advanced find, fire your query.. i.e. click find.. once you get the result, press F11 and paste this in addressbar.

javascript:alert(document.all.FetchXml.value);


you can ctrl+c and ctrl+v . you have the fetchxml ready.

Filtered lookup for N:N relation

Blog has been moved to here.

Refresh IFRAME loaded in grid

This is a very common problem when we load an area in IFrame. On addition of some records using 'Add existing' button, the grid doesn't refresh.
To help in generalisation the IFrame naming convention has to be "IFRAME_"+relationshipname.


eg: relation name is -- account_opportunity
Iframe name must be IFRAME_account_opportunity


This method is unsupported.


Script changes to be made in C:\Inetpub\wwwroot\CRM\_static\_grid\action.js :


/* In the function AssociateObjects,  we need to add the following code in two places.



var IFrame = "IFRAME_" + sAssociationName;
if (window != null && window.document != null && window.document.getElementById(IFrame) != null) {
    window.document.getElementById(IFrame).src = window.document.getElementById(IFrame).src;
}

In the two places - If and else condition ------  if ( parentInitiated ) condition and the else part. 



*/


CODE:



if ( parentInitiated )
{
for ( i=0; i < iLength; i++ )
{
commandAssociate.SetParameter("objectType", type1);
commandAssociate.SetParameter("parentObjectType", type2);
commandAssociate.SetParameter("objectId", id1);
commandAssociate.SetParameter("parentId", objs[i].id);
commandAssociate.SetParameter("associationName", sAssociationName);
if (!commandAssociate.Execute().Success)
{
break;
}
var IFrame = "IFRAME_" + sAssociationName;
if (window != null && window.document != null && window.document.getElementById(IFrame) != null) {
    window.document.getElementById(IFrame).src = window.document.getElementById(IFrame).src;
}
}
}
else
{
for ( i=0; i < iLength; i++ )
{
commandAssociate.SetParameter("objectType", type2);
commandAssociate.SetParameter("parentObjectType", type1);
commandAssociate.SetParameter("objectId", objs[i].id);
commandAssociate.SetParameter("parentId", id1);
commandAssociate.SetParameter("associationName", sAssociationName);
if (!commandAssociate.Execute().Success)
{
break;
}


var IFrame = "IFRAME_" + sAssociationName;
if (window != null && window.document != null && window.document.getElementById(IFrame) != null) {
    window.document.getElementById(IFrame).src = window.document.getElementById(IFrame).src;
}
}
}








Filter Lookup for N:N relation




My Scenario is i have loaded an area in an IFrame. 

Three entities are invloved in this:
Account, demo, testmodel.

Account <--> demo N:N relation
Account <--> testmodel N:N relation
testmodel <--> demo 1:N relation


Now, N:N between account and testmodel is loaded in an iframe and,
N:N between account and demo is loaded in another iframe, both in account form. 

Scenario is we need to filter the lookup such as demo lookup gives us only those demo records which are associated to the selected testmodel record in testmodel iframe in account form. 

Filtering this N:N relation lookup invloves two steps:
  1. customization in the onload script of the primary entity.
  2. registering a plugin on execute event
Note that this method is unsupported because we will be calling internal js in the server to associate the records.
JScript:


///CODE TO LOAD CUSTOM LOOKUP FROM AREA IN IFRAME

var relId = "new_new_demo_account";

var lookupId = crmForm.ObjectId ;  

var lookupEntityTypeCode;
var navId = document.getElementById("IFRAME_" + relId);

//navId has to be the IFrame. NAme accordingly
if (navId != null)
{
    
    navId.onreadystatechange = function()
    {

     if (navId.readyState == "complete"){

var areaId = document.getElementById("mnuBar1");
        if(areaId != null)
        {
                    var frame = frames[window.event.srcElement.id];  
                    var li = frame.document.getElementsByTagName("li");    

                    for (var i = 0; i < li.length; i++)
                    {
                        var action = li[i].getAttribute("action");
                        if(action != null && action.indexOf(relId) > 1)
                        {
                            lookupEntityTypeCode = action.substring(action.indexOf("\(")+1, action.indexOf(","));
                            li[i].onclick = CustomLookup;
                            break;
                        }
                    }
          }
        }
    }
}

function CustomLookup()
{

    var lookupSrc = "/" + ORG_UNIQUE_NAME + "/_controls/lookup/lookupmulti.aspx?class=&objecttypes=" + lookupEntityTypeCode + "&browse=0";
    if(lookupId != null )
    {
        lookupSrc = lookupSrc + "&id=" + lookupId;
    }

    var lookupItems = window.showModalDialog(lookupSrc, null);
    if (lookupItems)  
    {
        if ( lookupItems.items.length > 0 )
        {
// This is the CRM internal JS funciton on \_static\_grid\action.js  This is for N:N non self referential relationships. UNSUPPORTED
            AssociateObjects( crmFormSubmit.crmFormSubmitObjectType.value, crmFormSubmit.crmFormSubmitId.value, lookupEntityTypeCode, lookupItems, true, null, relId);
//Incase of selfreferential relationships, you need to pass use the below coxde.
// AssociateObjects( crmFormSubmit.crmFormSubmitObjectType.value, crmFormSubmit.crmFormSubmitId.value, lookupEntityTypeCode, lookupItems, false, null, relId);
        }
    }
}




Plugin


Two fetchxmls are used in this plugin. First, we retrieve the id from the url of the customlookup and execute one fetchxml for retrieving the testmodel records associated with the account. 


second fetchxml is for getting demo records associated with the result of first fetchxml testmodels. 


Ultimately, we will just replace the fetchxml present in the execute event with the second xml. The plugin has to be registered on Pre-Stage of execute with primaryentity none and secondaryentity none. 


Note: Execute is the common event used for many CRM service calls. So the catch here is that we need to check for the existence of the parameter 'id' in the context before proceeding with the execution. 


An additional check as to whether the id belongs to the particular objecttype for which you are executing the plugin would also help. 




CODE:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Crm.Sdk;
using Microsoft.Crm.SdkTypeProxy;
using System.Web;
using System.Xml;
using System.Net;
using System.Web.Caching;


namespace ExecuteClassLibrary
{
    public class Class1:IPlugin
    {    
        string lookupId;

        public void Execute(IPluginExecutionContext context)
        {

            lookupId = HttpContext.Current.Request.QueryString["id"] == null ? null : HttpContext.Current.Request.QueryString["id"].ToString();

            
            if (lookupId == null)   return;

            try
            {
                if (context.InputParameters.Contains("FetchXml"))
                {
                    string beforeXml = (String)context.InputParameters["FetchXml"];

                    if (beforeXml.Contains("<entity name=\"new_demo\">") && beforeXml.Contains("xml-platform"))
                    {
                        //GET VALUES FROM N:N TABLE FIRST

                        string fetchXml = "<fetch version='1.0' page='1' count='100' output-format='xml-platform' mapping='logical'> " +
                                        "<entity name='new_account_new_testmodel'> " +
                                            "<attribute name='new_testmodelid' /> " +
                                            "<filter type='and'> " +
                                                    "<condition attribute = 'accountid' operator='eq' value='"+ lookupId+ "'/>" +  //lookupId + "'/> " +
                            //"<condition attribute='new_name' operator='like' value='%' /> " +
                                            "</filter> " +
                                        "</entity> " +
                                    "</fetch>";
                        CrmService crmservice = context.getCrmService();

                        ExecuteFetchRequest req = new ExecuteFetchRequest();
                        req.FetchXml = fetchXml;
                        ExecuteFetchResponse resp = (ExecuteFetchResponse)crmservice.Execute(req);
                        // string retXml = crmservice.Fetch(fetchXml);
                        XmlDocument fxml = new XmlDocument();
                        fxml.LoadXml(resp.FetchXmlResult);
                        string value = string.Empty;

                        foreach (XmlNode node in fxml.ChildNodes)
                        {
                            foreach (XmlNode childnode in node.ChildNodes)
                            {
                                foreach (XmlNode resultnode in childnode)
                                {
                                    if (resultnode.Name == "new_testmodelid")
                                    {
                                        value += "<value>" + resultnode.ChildNodes.Item(0).InnerText + "</value>";
                                    }
                                }
                            }
                            //node
                        }


                        //Customise the FetchXml query string
                        string afterXml =
                        "<fetch version='1.0' page='1' count='100' output-format='xml-platform' mapping='logical'> " +
                            "<entity name='account'> " +
                                "<attribute name='name' /> " +
                                "<attribute name='accountid' /> " +
                                "<attribute name='createdon' /> " +
                                "<order attribute='name' /> " +
                                "<filter type='and'> " +
                                        "<condition attribute = 'new_soperator' operator='neq'>" + value + "</condition> " +
                                        "<condition attribute='statecode' operator='eq' value='0' /> " +
                                "</filter> " +
                            "</entity> " +
                        "</fetch>";

                        //Replace the FetchXml query string
                        context.InputParameters["FetchXml"] = beforeXml.Replace(beforeXml, afterXml);

                    }
                }
            }

            catch (System.Web.Services.Protocols.SoapException ex)
            {
                throw new InvalidPluginExecutionException("An error occurred in the CRM plug-in.", ex);
            }
        }

    }
}