Monday, November 28, 2011

Dynamics crm 2011 n to n lookup

N:N lookup in Dynamics CRM 2011.

For to 4.0 version: http://dynamicslollipops.blogspot.com/2011/07/mscrm-40-nn-lookup-field.html

Prerequest customization:
1) 1:N relation, and lookup placed on the form.
2) N:N relationship for holding the values.

Walkthrough:
1) Create new javascript webresource with the following code:

var CRM_FORM_TYPE_CREATE = 1;

RetreiveAssociatedEntities = function(relationshipSchemaName, entity1SchemaName, entity1KeyValue, retreiveAttribute) {
    var fetchXml = "<fetch mapping='logical'>"
    + "  <entity name='" + relationshipSchemaName + "'>"
    + "    <all-attributes />"
    + "    <filter>"
    + "      <condition attribute='" + entity1SchemaName + "id' operator='eq' value ='" + entity1KeyValue + "' />"
    + "    </filter>"
    + "  </entity>"
    + "</fetch>";

    var fetchResults = Fetch(fetchXml);

    var nodeList = fetchResults.selectNodes("resultset/result");

    var returnList = new Array();
    if (nodeList == null || nodeList.length == 0) {
        return returnList;
    } else {
        for (i = 0; i < nodeList.length; i++) {
            var idValue = nodeList[i].selectSingleNode('./' + retreiveAttribute).nodeTypedValue;
            returnList[i] = idValue;
        }
        return returnList;
    }
}
MischiefMayhemSOAP = function(serviceUrl, xmlSoapBody, soapActionHeader, suppressError) {
    var xmlReq = "<?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>"
    + xmlSoapBody
    + "  </soap:Body>"
    + "</soap:Envelope>";

    var httpObj = new ActiveXObject("Msxml2.XMLHTTP");

    httpObj.open("POST", serviceUrl, false);

    httpObj.setRequestHeader("SOAPAction", soapActionHeader);
    httpObj.setRequestHeader("Content-Type", "text/xml; charset=utf-8");
    httpObj.setRequestHeader("Content-Length", xmlReq.length);

    httpObj.send(xmlReq);

    var resultXml = httpObj.responseXML;

    var errorCount = resultXml.selectNodes("//error").length;
    if (errorCount != 0) {
        var msg = resultXml.selectSingleNode("//description").nodeTypedValue;

        if (typeof (suppressError) == "undefined" || suppressError == null) {
            alert("The following error was encountered: " + msg);
        }

        return null;
    } else {
        return resultXml;
    }
}

Fetch = function(fetchXml) {
    var xmlSoapBody = "<Fetch xmlns='http://schemas.microsoft.com/crm/2007/WebServices'>"
    + "  <fetchXml>"
    + FetchEncode(fetchXml)
    + "  </fetchXml>"
    + "</Fetch>";

    var fetchResponse = MischiefMayhemSOAP("/MSCRMServices/2007/CrmService.asmx", xmlSoapBody, "http://schemas.microsoft.com/crm/2007/WebServices/Fetch");

    if (fetchResponse != null) {
        var fetchResults = new ActiveXObject("Msxml2.DOMDocument");

        fetchResults.async = false;
        fetchResults.resolveExternals = false;
        fetchResults.loadXML(fetchResponse.text);

        return fetchResults;
    } else {
        return null;
    }
}
FetchEncode = function(strInput) //_HtmlEncode
{
    var c;
    var HtmlEncode = '';

    if (strInput == null) {
        return null;
    }
    if (strInput == '') {
        return '';
    }

    for (var cnt = 0; cnt < strInput.length; cnt++) {
        c = strInput.charCodeAt(cnt);

        if (((c > 96) && (c < 123)) ||
  ((c > 64) && (c < 91)) ||
  (c == 32) ||
  ((c > 47) && (c < 58)) ||
  (c == 46) ||
  (c == 44) ||
  (c == 45) ||
  (c == 95)) {
            HtmlEncode = HtmlEncode + String.fromCharCode(c);
        }
        else {
            HtmlEncode = HtmlEncode + '&#' + c + ';';
        }
    }

    return HtmlEncode;
}

AssociateEntities = function(moniker1name, moniker1id, moniker2name, moniker2id, RelationshipName) {
    var authenticationHeader = GenerateAuthenticationHeader();
    // Prepare the SOAP message.
    var xml = "<?xml version='1.0' encoding='utf-8'?>";
    xml += "<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\'>";
    xml += authenticationHeader;
    xml += "<soap:Body><Execute xmlns='http://schemas.microsoft.com/crm/2007/WebServices'><Request xsi:type='AssociateEntitiesRequest'>";
    xml += "<Moniker1><Id xmlns='http://schemas.microsoft.com/crm/2006/CoreTypes'>" + moniker1id + "</Id>";
    xml += "<Name xmlns='http://schemas.microsoft.com/crm/2006/CoreTypes'>" + moniker1name + "</Name></Moniker1>";
    xml += "<Moniker2><Id xmlns='http://schemas.microsoft.com/crm/2006/CoreTypes'>" + moniker2id + "</Id>";
    xml += "<Name xmlns='http://schemas.microsoft.com/crm/2006/CoreTypes'>" + moniker2name + "</Name></Moniker2>";
    xml += "<RelationshipName>" + RelationshipName + "</RelationshipName>";
    xml += "</Request></Execute></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/Execute");
    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);
    }
}

DisassociateEntities = function(moniker1name, moniker1id, moniker2name, moniker2id, RelationshipName) {
    var authenticationHeader = GenerateAuthenticationHeader();
    // Prepare the SOAP message.
    var xml = "<?xml version='1.0' encoding='utf-8'?>";
    xml += "<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\'>";
    xml += authenticationHeader;
    xml += "<soap:Body><Execute xmlns='http://schemas.microsoft.com/crm/2007/WebServices'><Request xsi:type='DisassociateEntitiesRequest'>";
    xml += "<Moniker1><Id xmlns='http://schemas.microsoft.com/crm/2006/CoreTypes'>" + moniker1id + "</Id>";
    xml += "<Name xmlns='http://schemas.microsoft.com/crm/2006/CoreTypes'>" + moniker1name + "</Name></Moniker1>";
    xml += "<Moniker2><Id xmlns='http://schemas.microsoft.com/crm/2006/CoreTypes'>" + moniker2id + "</Id>";
    xml += "<Name xmlns='http://schemas.microsoft.com/crm/2006/CoreTypes'>" + moniker2name + "</Name></Moniker2>";
    xml += "<RelationshipName>" + RelationshipName + "</RelationshipName>";
    xml += "</Request></Execute></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/Execute");
    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);
    }
}

//returns the item location in array if not found -1
GetIndexFromArray = function(item, recordArr) {
    for (var i = 0; i < recordArr.length; i++) {
        if (recordArr[i] != null && recordArr[i].id == item) {
            return i;
        }
    }
    return -1;
}

/*"new_new_entity1_new_entity2new","new_multynewid", "new_entity2", "new_name"*/
FillMultiLookup = function(relationshipSchemaName, lookupSchemaName, relatedEntitySchemaName, relatedEntityPrimaryAttributeSchemaName) {
    var relatedValues = RetreiveAssociatedEntities(relationshipSchemaName, Xrm.Page.data.entity.getEntityName(), Xrm.Page.data.entity.getId(), relatedEntitySchemaName + "id");
    var value = new Array();
    for (var i = 0; i < relatedValues.length; i++) {
        value[i] = new Object();
        value[i].id = relatedValues[i];
        value[i].name = RetreiveAssociatedEntities(relatedEntitySchemaName, relatedEntitySchemaName, relatedValues[i], relatedEntityPrimaryAttributeSchemaName)[0];
        value[i].typename = relatedEntitySchemaName;
    }
 crmForm.all[lookupSchemaName].DataValue = value;
}

UpdateN2N = function(nnId, relatedEntitySchemaName, relatedEntitySchemaId, lookupSchemaName) {
    var oldValues = RetreiveAssociatedEntities(nnId, Xrm.Page.data.entity.getEntityName(), Xrm.Page.data.entity.getId(), relatedEntitySchemaId);
 var value = crmForm.all[lookupSchemaName].DataValue;
    //if there's records in lookup
    if (value != null) {
        //go over all the related records and remove them if not in the new list (lookup)
        var temp = value;
        for (var i = 0; i < oldValues.length; i++) {
            //if not in the new list disassociate them
            var index = GetIndexFromArray(oldValues[i], temp);
            if (index == -1) {
                DisassociateEntities(Xrm.Page.data.entity.getEntityName(), Xrm.Page.data.entity.getId(), relatedEntitySchemaName, oldValues[i], nnId);
            }
            else { // if in the list remove them from the list
                temp[index] = null;
            }
        } //ends for
        //go over all the remaining records and associate them
        for (var i = 0; i < temp.length; i++) {
            if (temp[i] != null) {
                AssociateEntities(Xrm.Page.data.entity.getEntityName(), Xrm.Page.data.entity.getId(), relatedEntitySchemaName, temp[i].id, nnId);
            }
        }
    }
    else if (oldValues != null) {
        for (var i = 0; i < oldValues.length; i++) {
            DisassociateEntities(Xrm.Page.data.entity.getEntityName(), Xrm.Page.data.entity.getId(), relatedEntitySchemaName, oldValues[i], nnId);
        }
    }
}

FilterN2NLookup = function(filterByLookup, filterLookup, entityName, nnName, nnFrom, linkedEntityName, linkedAttributeName) {
    var filter = '';
    var values = filterByLookup.DataValue;
    if (values != null) {
        //filter = '<filters><filter entity="' + entityName + '"><condition attribute="' + attributeName + '" operator="in">';
        //for (var i = 0; i < values.length; i++) {
        //    filter += '<value uiname="' + values[i].name + '">' + values[i].id + '</value>';
        // }
        //filter += '</condition></filter></filters>';

        filter = '<link-entity entity="' + entityName + '" name="' + nnName + '" from="' + nnFrom + '" to="' + nnFrom + '" visible="false" intersect="true"><link-entity name="' + linkedEntityName + '" from="' + linkedAttributeName + '" to="' + linkedAttributeName + '"><filter type="and"><condition attribute="' + linkedAttributeName + '" operator="in">';
        for (var i = 0; i < values.length; i++) {
            filter += '<value uiname="' + values[i].name + '">' + values[i].id + '</value>';
        }
        filter += '</condition></filter></link-entity></link-entity>';

    }
    filterLookup.AddParam('filters', filter);
}

/*"new_new_entity1_new_entity2new","new_multynewid", "new_entity2", "new_name"*/
function ConvertN2N(relationshipSchemaName, lookupSchemaName, relatedEntitySchemaName, relatedEntityPrimaryAttributeSchemaName)
{
 document.getElementById(lookupSchemaName).setAttribute("lookupstyle", "multi");
 document.getElementById(lookupSchemaName).setAttribute("_lookupstyle", "multi");

 document.getElementById(lookupSchemaName).onchange = function(){UpdateN2N(relationshipSchemaName, relatedEntitySchemaName, relatedEntitySchemaName + "id", lookupSchemaName)};

 Xrm.Page.getAttribute(lookupSchemaName).setSubmitMode("never");

 if (crmForm.FormType != CRM_FORM_TYPE_CREATE) {
  FillMultiLookup(relationshipSchemaName, lookupSchemaName, relatedEntitySchemaName, relatedEntityPrimaryAttributeSchemaName);
 }
 else {
  Xrm.Page.getControl(lookupSchemaName).setDisabled(true);
 }
}

2) Add it to the form
3) Add to the onload 
   3.1) Function: ConvertN2N
   3.2) Parameters: N:N Relationship name, lookup attribute name, Related Entity Schema Name, Related Entity Primary Attribute Schema Name
(Example: "new_incident_new_problem", "new_problemid", "new_problem", "new_name")



That's it, everything else is inside the code. 
Plug and Play.

Sunday, November 27, 2011

Microsoft Dynamics CRM 2011 Performs better on IE 9

Microsoft Dynamics CRM 2011 Performs better on IE 9.
After few tests we ran on Microsoft Dynamics CRM 2011 vanilla, we have found that IE 9 performance improves by 1-2~ sec per form.

Open Create Account form:
IE 8  - 3~ sec.
IE 9 - 1-1.5 ~ sec.

Open update Account form
IE 8 - 3~ sec.
IE 9 - 1-1.5 ~ sec.

* Average time for 3 tests.

Share your results

Taken form http://xkcd.com/979/

Sunday, November 20, 2011

Microsoft Dynamics CRM 2011 crmForm workaround


There is a problem when checking attributes value, after the user clicks on ribbon button without losing focus of the attribute.

Description:

1) I've register the ribbon button event to something like:
if(Xrm.Page.getAttribute("new_test").getValue() != null)
{
alert("aaaaa");
2) User clicks the ribbon button when the attribute is empty
3) Get's alert
4) User insert text to the attribute and clicks again 
5) Get's alert

When user isn't getting out (clicks out side) of the attribute's textbox the value, gets from Xrm.Page.getAttribute("xxxx").getValue(), remains the old value but if he does (just click out side the textbox) everything works.

After changing the code to the old (CRM 4) javascript syntact:
if(crmForm.all.new_test.DataValue != null)
{
     alert("AAAA");
}

Everything worked great! Even when the focus still on the textbox.

This is a workaround.

Tuesday, November 15, 2011

MSCRM 2011 Set Guid (Any Entity Id) Plugin

MSCRM 2011 Set Guid (Any Entity Id) Plugin

http://dynamicslollipops.blogspot.com/2011/10/mscrm-2011-workflow-assembly-get.html

Because it's impossiable to use workflow assembly in Microsoft Dynamics CRM 2011,
This is the piece of code needed for creating plugin that's sets any entity GUID into any test field you choose.


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xrm.Sdk;

namespace MSCRM2011SetGuidPlugin
{
    public class MSCRM2011SetGuidPlugin : IPlugin
    {
        public readonly string rAttributeSchemaName = "new_guid";
        public string m_SecureConfig { get; set; }
        public string m_Config { get; set; }

        public MSCRM2011SetGuidPlugin(string i_Config, string i_SecureConfig)
        {
            m_Config = i_Config;
            m_SecureConfig = i_SecureConfig;
        }


        /// <summary>
        /// A plugin that creates a follow-up task activity when a new account is created.
        /// </summary>
        /// <remarks>Register this plug-in on the Create message, account entity,
        /// and asynchronous mode.
        /// </remarks>
        public void Execute(IServiceProvider serviceProvider)
        {
            //Extract the tracing service for use in debugging sandboxed plug-ins.
            ITracingService tracingService =
                (ITracingService)serviceProvider.GetService(typeof(ITracingService));

            // Obtain the execution context from the service provider.
            IPluginExecutionContext context = (IPluginExecutionContext)
                serviceProvider.GetService(typeof(IPluginExecutionContext));
            IOrganizationServiceFactory serviceFactory = (IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOrganizationServiceFactory));
            IOrganizationService service = serviceFactory.CreateOrganizationService(context.UserId);

            tracingService.Trace("Config: " + m_Config);
            string attr = rAttributeSchemaName;
            if (!String.IsNullOrEmpty(m_Config))
            {
                attr = m_Config;
            }
            tracingService.Trace("attr: " + attr);
            tracingService.Trace("id: " + context.PrimaryEntityId);
            Guid id = context.PrimaryEntityId;

            Entity entity = new Entity(context.PrimaryEntityName);
            entity.Id = id;
            entity.Attributes.Add(attr, id.ToString());
            service.Update(entity);
        }
    }

}

If no attribute is in the configuration, then it tries to place the GUID into new_guid.

The complate code project and dll

Friday, November 4, 2011

MSCRM 2011 Debug plugin

I've just read it and wanted to share it.
http://msdn.microsoft.com/en-us/library/hh372952.aspx

It helps debuging plugins even when the Dynamics CRM is ONLINE.
It is great for unit testing.

It is what i was looking for.

Thursday, November 3, 2011

Dynamics CRM 2011 record hyperlink

Dynamics CRM 2011 record hyperlink.

Finally
Microsoft added Insert Hyperlink to send email / email templates in MSCRM 2011 Rollup 5.

The workarounds are finally uneeded anymore.

Walkthrough:
1) Go to send email in workflow
2) Insert text for display
3) insert the Record URLto the url field.
4) Click on OK.