Showing posts with label dynamics crm 2011. Show all posts
Showing posts with label dynamics crm 2011. Show all posts

Sunday, 7 September 2014

CRM 2011 - JavaScript - Access Is Denied

While testing a CRM 2011 instance we ran into a issue where the JavaScript kept throwing an "Access is Denied" message. This only occurred on one system and no others. What we discovered was the user was looked in using the fully qualified URL ie:- http://crmtroubleshoot.blogspot.com.au While everone else was using the short URL something like the following http://crmtroubleshoot When calling the service through JavaScript we use the following getClientUrl() + "/XRMServices/2011/Organization.svc/web"; This was returning the short URL which the server thought was a different server to the long name. The user re logged in with the Short URL and the problem went away. If you have DNS suffixes set then you need to log in to CRM using the short name. I suspect (unconfirmed) if you don't have the DNS suffixes set then using the long URL should be fine.

Tuesday, 26 August 2014

CRM 2011 - Changing the color of Text fields

We had a scenario where we wanted to clearly identify the differences between two addresses on a form.

To facilitate this we thought we would change the text color however we hit two issues
1) Most of the examples showed an unsupported example.
2) It didn't seem to work when the field was read only

The examples seem to use the standard JavaScript style
document.getElementById("fieldname").style.color = "red";
While my final solution might still not be Microsoft supported, it is a bit more "CRM'd" then the above.
var field = Xrm.Page.ui.controls.get("fieldname");
if (field != null) {
     field._control.get_element().firstChild.style.color= 'red';
}

HOWEVER as the display of read only fields is defined by the client browser/css modifying this would be tricky at best. So instead we changed the background color which will work on a disabled field.
var field = Xrm.Page.ui.controls.get("fieldname");
if (field != null) {
     field._control.get_element().firstChild.style.backgroundColor = 'red';
}
Let me know if you have a more elegant solution!

Thursday, 3 October 2013

0x8004F016 - A managed solution cannot overwrite the LocalizedLabel component with Id=XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX which has an unmanaged base instance. The most likely scenario for this error is that an unmanaged solution has installed a new unmanaged LocalizedLabel component on the target system, and now a managed solution from the same publisher is trying to install that same LocalizedLabel component as managed. This will cause an invalid layering of solutions on the target system and is not allowed.

We had a client who had a very old version of our CRM customizations managed solution. When we attempted to update we received the following error.

ErrorCode:
0x8004F016

ErrorText:
A managed solution cannot overwrite the LocalizedLabel component with Id=XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX which has an unmanaged base instance.  The most likely scenario for this error is that an unmanaged solution has installed a new unmanaged LocalizedLabel component on the target system, and now a managed solution from the same publisher is trying to install that same LocalizedLabel component as managed.  This will cause an invalid layering of solutions on the target system and is not allowed.

Checking the internet the responses seemed to be based around deleting data from the SQL database. This is not recommended for two reasons. 1) Its not microsoft supported  2) It doesn't work for CRM Online.

The item that was failing was:
ItemType       
Name Localized Name
Form Incident Case
Investigating the forms related to Case I found that the name of forms had been changed i suspect by an unmanaged customization.  By changing the name back to the correct name I could then import my managed solution correctly.

Wednesday, 2 October 2013

Dynamics CRM 2011 - Filter a lookup

Filtering a lookup in CRM 2011 is quite straight forward. Below is a sample that I used to filter one lookup based on the value of another.



function filterSecondLookup()
{
    try {
        var lookupObject = Xrm.Page.getAttribute("lookup_one").getValue();

        if (lookupObject != null)
        {

            lookup_one_name = $('').text(lookupObject[0].name).html();  // text of lookup
            lookup_one_id = lookupObject[0].id; // Guid of lookup

            var viewId = '{1ACB2B35-B07C-44D1-868D-258DEEAB88E2}'; //Random GUID
            var entityName = 'account';
            var viewDisplayName = 'Filtered Lookup Name';

            var fetchXml = "" +
                              "" +
                                "" +
                                "" +
                                "" +
                                "" +
                                "" +
                                "" +
                                "" +
                                  "" +
                                    "" +
                                  "" +
                                "" +
                              "" +
                            "";

            var layoutXml = "" +
                          "" +
                            "" +
                            "" +
                            "" +
                            "" +
                          "" +
                        "";

            Xrm.Page.getControl('lookup_two').addCustomView(viewId, entityName, viewDisplayName, fetchXml, layoutXml, true);
        }
        else
        {
            Xrm.Page.getControl("lookup_two").setDefaultView(window.defaultSiteViewId );
        }
    }
    catch (ex)
    {
        alert("Error in filterSecondLookup: " + ex);
    }
}

Thursday, 25 July 2013

Dynamics CRM 2011 - PHP and SOAP Calls

I put the first little section regarding CRM 2011, PHP, and SOAP here ->  http://crmtroubleshoot.blogspot.com.au/2013/07/dynamics-crm-2011-php-and-soap-using.html

I went into some basic information about authentication so in this second part I thought I would give people the SOAP calls for talking to CRM 2011.

NOTE: I am not suggesting the following are the only ways to connect using SOAP to CRM and I know its not a complete list (Delete for example) however I hope this assists you in your investigation it is not intended to do all the work for you! ;-)

SOAP CALLS


CREATE
Creates a single CRM record


    
      
  
      
      
      
      00000000-0000-0000-0000-000000000000
      
      
    



RETRIEVE
Returns a single CRM record based on the GUID.


  
  
  
 false
 
   
 
  



RETRIEVE ALL ENTITIES
This returns all all entities. I don't really recommend this because it is incredibly slow!


 
   
    
  EntityFilters
  Entity
    
    
  RetrieveAsIfPublished
  true
    
   
   
   RetrieveAllEntities
 



RETRIEVE MULTIPLE
Returns a collection of CRM Records based on the Criteria list passed


 
  
    false
    
   
    
  
  
  false
  
  
  
  
    
    
    
    false
  
 



UPDATE
Updates a single record based on the GUID


 
  
   
  
  
  
  
  
  
 



WHO AM I
Returns the User Id, Business Unit Id, and Organization Id for the logged in user


 
  
  
  WhoAmI
 



CHILD ITEMS


COLUMN
This should be repeated for each column required for the Retrieve Requests


 



ATTRIBUTES
This should be repeated for each attribute required for a Create or Update Request. There is various types for attributes.

EntityReference

 
 
  
  
  
  


OptionSetValue

 
 
  
 


Money

 
 
  
 


String

        
 


Boolean

        
 


Integer

        
 


Date Time

        
 




CRITERIA
Used to filter the query results


 
 
  
  
   
   
   
    
    
   
  
 



ORDER
Used to define the sort order of the result set


 
 
  
  
 



Tuesday, 16 July 2013

Dynamics CRM 2011 - PHP and SOAP using Office 365

NOTE: If you are using this code I found a small issue!!
Microsoft appears to have changed the SSL version they are using. Due to this the following function needs to be modified!
 public static function GetSOAPResponse($url, $request) {
    // Set up headers.
    $headers = array(
      "POST " . "/Organization.svc" . " HTTP/1.1",
      "Host: yourorganisation.api.crm5.dynamics.com",
      'Connection: Keep-Alive',
      "Content-type: application/soap+xml; charset=UTF-8",
      "Content-length: " . strlen($request),
    );

    $cURLHandle = curl_init();
    curl_setopt($cURLHandle, CURLOPT_URL, $url);
    curl_setopt($cURLHandle, CURLOPT_RETURNTRANSFER, 1);
    curl_setopt($cURLHandle, CURLOPT_TIMEOUT, 60);
    curl_setopt($cURLHandle, CURLOPT_SSL_VERIFYPEER, FALSE);
    curl_setopt($cURLHandle, CURLOPT_FOLLOWLOCATION, TRUE);
    //COMMENT OUT THIS LINE!!curl_setopt($cURLHandle, CURLOPT_SSLVERSION, 3);
    curl_setopt($cURLHandle, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
    curl_setopt($cURLHandle, CURLOPT_HTTPHEADER, $headers);
    curl_setopt($cURLHandle, CURLOPT_POST, 1);
    curl_setopt($cURLHandle, CURLOPT_POSTFIELDS, $request);
    //COMMENT OUT THIS LINE!!$response = curl_exec($cURLHandle);
    // ADD THE FOLLOWING FOUR LINES INSTEAD (and a try/catch wouldn't hurt!)
    if( ! $response = curl_exec($cURLHandle)) 
    { 
        trigger_error(curl_error($cURLHandle)); 
    }
    curl_close($cURLHandle);

    return $response;
  }


Now onto the post!!
I recently found myself in a position where I needed to connect to CRM Online from PHP.  As I didn't have anywhere to put my code except for the client side creating my own wrapper for the CRM Web services didn't seem like a great option.

I found a few examples of CRM, PHP, and SOAP however many seemed to have some issue along the way (In particular around Office 365 authentication).

So firstly we will look at Office 365 authentication. To connect you need to request so tokens from the CRM Web Service as follows:

class Authentication
{
 public $username;
 public $password;
 public $keyIdentifier;
    public $securityToken0;
    public $securityToken1;

 public function Authentication($_username, $_password)
 {
  $this->username = $_username;
  $this->password = $_password;
  $this->AuthenticateUser();
 }
 
 function BuildOCPSoap()
 {
  /*
  Select the right region for your CRM
  crmna:dynamics.com - North America
  crmemea:dynamics.com - Europe, the Middle East and Africa
  crmapac:dynamics.com - Asia Pacific
  */
  $region = 'crmapac:dynamics.com';

  $OCPRequest = '<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope" xmlns:a="http://www.w3.org/2005/08/addressing" xmlns:u="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
       <s:Header>
      <a:Action s:mustUnderstand="1">http://schemas.xmlsoap.org/ws/2005/02/trust/RST/Issue</a:Action>
      <a:MessageID>urn:uuid:%s</a:MessageID>
      <a:ReplyTo>
        <a:Address>http://www.w3.org/2005/08/addressing/anonymous</a:Address>
      </a:ReplyTo>
      <a:To s:mustUnderstand="1">%s</a:To>
      <o:Security s:mustUnderstand="1" xmlns:o="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
        <u:Timestamp u:Id="_0">
       <u:Created>%sZ</u:Created>
       <u:Expires>%sZ</u:Expires>
        </u:Timestamp>
        <o:UsernameToken u:Id="uuid-cdb639e6-f9b0-4c01-b454-0fe244de73af-1">
       <o:Username>%s</o:Username>
       <o:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">%s</o:Password>
        </o:UsernameToken>
      </o:Security>
       </s:Header>
       <s:Body>
      <t:RequestSecurityToken xmlns:t="http://schemas.xmlsoap.org/ws/2005/02/trust">
        <wsp:AppliesTo xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy">
       <a:EndpointReference>
         <a:Address>'. $region .'</a:Address>
       </a:EndpointReference>
        </wsp:AppliesTo>
        <t:RequestType>http://schemas.xmlsoap.org/ws/2005/02/trust/Issue</t:RequestType>
      </t:RequestSecurityToken>
       </s:Body>
     </s:Envelope>';
  
  $OCPRequest = sprintf($OCPRequest, gen_uuid(), 'https://login.microsoftonline.com/RST2.srf',  getCurrentTime(), getNextDayTime(), $this->username, $this->password);
  return $OCPRequest;
 }
 
 public static function GetSOAPResponse($url, $request) {
    // Set up headers.
    $headers = array(
      "POST " . "/Organization.svc" . " HTTP/1.1",
      "Host: yourorganisation.api.crm5.dynamics.com",
      'Connection: Keep-Alive',
      "Content-type: application/soap+xml; charset=UTF-8",
      "Content-length: " . strlen($request),
    );

    $cURLHandle = curl_init();
    curl_setopt($cURLHandle, CURLOPT_URL, $url);
    curl_setopt($cURLHandle, CURLOPT_RETURNTRANSFER, 1);
    curl_setopt($cURLHandle, CURLOPT_TIMEOUT, 60);
    curl_setopt($cURLHandle, CURLOPT_SSL_VERIFYPEER, FALSE);
    curl_setopt($cURLHandle, CURLOPT_FOLLOWLOCATION, TRUE);
    curl_setopt($cURLHandle, CURLOPT_SSLVERSION, 3);
    curl_setopt($cURLHandle, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
    curl_setopt($cURLHandle, CURLOPT_HTTPHEADER, $headers);
    curl_setopt($cURLHandle, CURLOPT_POST, 1);
    curl_setopt($cURLHandle, CURLOPT_POSTFIELDS, $request);
    $response = curl_exec($cURLHandle);
    curl_close($cURLHandle);

    return $response;
  }
  
  public function AuthenticateUser()
 {
  $SOAPresult = GetSOAPResponse('https://login.microsoftonline.com/RST2.srf', $this->BuildOCPSoap());
  $responsedom = new DomDocument();
        $responsedom->loadXML($SOAPresult);
  
        $cipherValues = $responsedom->getElementsbyTagName("CipherValue");
       
        if( isset ($cipherValues) && $cipherValues->length>0){
            $this->securityToken0 =  $cipherValues->item(0)->textContent;
   $this->securityToken1 =  $cipherValues->item(1)->textContent;
   $this->keyIdentifier = $responsedom->getElementsbyTagName("KeyIdentifier")->item(0)->textContent;
        }else{
            return null;
        }
 }
      


gen_uuid returns a random guid, getCurrentTime gets the current date/time, getNextDayTime gets tomorrow (or any expiry date/time you wish)

We then send this to the authentication srf to get the required tokens

You should now have  the required tokens which you then place in your header for any requests as shown below:


public static function getHeader($_action, $_authentication) {

  $header = '
  <s:Header>
   <a:Action s:mustUnderstand="1">http://schemas.microsoft.com/xrm/2011/Contracts/Services/IOrganizationService/'.$_action .'</a:Action>
   <a:MessageID>
    urn:uuid:'.self::gen_uuid().'
   </a:MessageID>
   <a:ReplyTo><a:Address>http://www.w3.org/2005/08/addressing/anonymous</a:Address></a:ReplyTo>
   <a:To s:mustUnderstand="1">
    https://yourorganisation.api.crm5.dynamics.com/XRMServices/2011/Organization.svc
   </a:To>
   <o:Security s:mustUnderstand="1" xmlns:o="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
   <u:Timestamp u:Id="_0">
    <u:Created>'.self::getCurrentTime().'Z</u:Created>
    <u:Expires>'.self::getNextDayTime().'Z</u:Expires>
   </u:Timestamp>
   <EncryptedData Id="Assertion0" Type="http://www.w3.org/2001/04/xmlenc#Element" xmlns="http://www.w3.org/2001/04/xmlenc#">
    <EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#tripledes-cbc"></EncryptionMethod>
    <ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
     <EncryptedKey>
      <EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p"></EncryptionMethod>
      <ds:KeyInfo Id="keyinfo">
       <wsse:SecurityTokenReference xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
        <wsse:KeyIdentifier EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary" ValueType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509SubjectKeyIdentifier">
         '.$_authentication->keyIdentifier.'
        </wsse:KeyIdentifier>
       </wsse:SecurityTokenReference>
      </ds:KeyInfo>
      <CipherData>
       <CipherValue>
        '.$_authentication->securityToken0.'
       </CipherValue>
      </CipherData>
     </EncryptedKey>
    </ds:KeyInfo>
    <CipherData>
     <CipherValue>
      '.$_authentication->securityToken1.'
     </CipherValue>
    </CipherData>
   </EncryptedData>
   </o:Security>
  </s:Header>';

  return $header;

 }

public static function WhoAmIRequest($request){

 $authentication = new Authentication('Username', 'Password');
 $xml = '
 <s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope" xmlns:a="http://www.w3.org/2005/08/addressing" xmlns:u="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
  'getHeader('Execute', $authentication).'
  <s:Body>
   <Execute xmlns="http://schemas.microsoft.com/xrm/2011/Contracts/Services" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
    <request i:type="b:WhoAmIRequest" xmlns:a="http://schemas.microsoft.com/xrm/2011/Contracts" xmlns:b="http://schemas.microsoft.com/crm/2011/Contracts">
     <a:Parameters xmlns:c="http://schemas.datacontract.org/2004/07/System.Collections.Generic" />
     <a:RequestId i:nil="true" />
     <a:RequestName>WhoAmI</a:RequestName>
    </request>
   </Execute>
  </s:Body>
 </s:Envelope>'; 

 return self::GetSOAPResponse('https://yourorganisation.api.crm5.dynamics.com/XRMServices/2011/Organization.svc', $xml);
}

This code is slightly modified obviously from what I am using however I have the above working. The above is not intended to be the entire solution it is just expected that it might assist you in your own work.

Thursday, 11 July 2013

CRM2011 - "A currency is required if a value exists in a money field. Select a currency and try again."

I was writing a javascript function and during testing kept getting "A currency is required if a value exists in a money field. Select a currency and try again." while attempting to enter data into the field.

Basically what had happened was a currency field was added to an existing CRM entity.  CRM doesn't like this for some weird reason.

After googling alot of people suggested that you write javascript to set the currency onload.  This to me seemed like a bit of a hack which i'd prefer to avoid.  Instead do the following:

Step 1 - Go to File -> Options
Then in the first tab General go down to "Select a default currency". Set it to the appropriate value.
NOTE: This is a user setting so this needs to be repeated for all users however HOPEFULLY this is already set.

Step 2 - Add the "Currency" field to the form for the entity you are having issues with. Save and Publish

Step 3 - Select all existing records for the entity and "Edit"

Step 4 - Click on the currency lookup and select the required currency. Save the changes

You will now find the required currency symbol appears in the field and you can happily enter data into the field!

Monday, 17 June 2013

CRM 2011 OData XMLHttpRequest Maximum Length

I had a requirement where I wanted to know if a user had a particular system role.  I created a function which returned all User Roles with a particular name (System Administrator for example) and compared this to what gets returned from Xrm.Page.context.getUserRoles().

This worked in the Development and Testing environments so it was pushed to Production.  When in Production the function failed to recognise the correct Roles which made no sense.

After wasting several hours of my life I discovered that the issue was related to the relationship between Business Units and Roles.

The Production environment had 60+ business entities where Development and Testing had much less.  The 60+ business units meant that CRM created 60+ versions of the Security Roles so searching for "System Administrator" returned over 60 records.

The page length for XMLHttpRequest OData requests through the CRM Web Service is fixed at 50.

Rather than repeat the solution here I suggest you look at Lakshman Reddy's blog which is what lead me to my fix.

http://lakshmanindian.wordpress.com/2012/12/07/retrieving-more-than-50-records-using-odata-in-crm-2011/

The only issue I had was the process of returning all those Roles slowed down my CRM page.  So I instead reversed my query for example.

Original
Return all Roles with 'System Administrator' name then compare with User Roles.

Updated
Return all Roles where Guid is in User Roles

Dynamics CRM 2011 Authentication Types

Connecting to CRM 2011 (in particular CRM Online) can be tricky due to the different authentication methods on offer. If you are allowing users to connect to CRM using different authentication types then you need to be aware of the different requirements


  • Windows Live Id (WLID)
    • Device Registration
    • Username
    • Password
  • Office 365 - (Online Commerce Platform) (OCP)
    • Username
    • Password
    • Security Token using Username/Password
  • Active Directory
    • Domain
    • Username
    • Password
  • Federated
    • Username
    • Password

Dynamics CRM 2011 - "An error occurred when verifying security for the message"

If you attempt to connect to the Dynamics CRM 2011 Web Services using SOAP you may come across this message "An error occurred when verifying security for the message".

In most cases this will be an issue of the server and client times not being close enough so they web server rejects the request.

However I was receiving this message for a completely different reason.

I was using the "CRMOnlineSOAPRequests" example written in C# as a base reference point however I wanted to write my code in PHP so I googled and found a couple of good examples.

Both the C# code and my PHP code returned the required tokens when authenticating however the second token was always 6764 characters in length while the PHP second token was 6796 in length.

I then discovered that I was attempting to connect to the wrong region for my authentication!

If you are receiving this error then ensure that you have selected the correct URN for your region.

  • crmna:dynamics.com - North America
  • crmemea:dynamics.com - Europe, the Middle East and Africa
  • crmapac:dynamics.com - Asia Pacific