Pages

Sunday, October 31, 2010

Tito-Z’s Iframe-embedded Image Code

[Full Disclosure: The following, true story happened within the CRM Development forums.]

With blessings of the author, Tito-Z, whose real name I don’t know, I’d like to present his code for taking an image file attachment from an Annotation record and displaying it within an Iframe dynamically.  Since I helped him locate the solutions that he used in his code, Tito-Z graciously granted me permission to reproduce his work here.

Originally, Tito-Z started with Adi Katz’ code.  However, he soon discovered that Update Rollup 7 (and later) thwarted its use.  Unfortunately, the work-around left him with a file-stream and not a valid URL to place in a src attribute for an image element.  When I did a little searching, I found some resources that showed how to assemble an inline-data URI.

I then left Tito-Z to do the dirty work—not my usual style, but since he had demonstrated proficiency in CRM-hacking, I figured the project was in good hands.  He did not disappoint.  The following, unaltered code is his example for reproducing an image within a form’s Iframe, after retrieving the image data from an Annotation (file attachment) record:

[Caveat:  The following code works only for IE8, as earlier versions of IE do not support the necessary inline-data URIs employed by the code.]

// *********************************************************
// To fetch the picture file from the Notes Entity 
// *********************************************************

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>" +
"<Fetch xmlns='http://schemas.microsoft.com/crm/2007/WebServices'>" +
"<fetchXml>" +
"&lt;fetch version='1.0' output-format='xml-platform' mapping='logical' distinct='false'&gt;"+
"&lt;entity name='annotation'&gt;"+
"&lt;attribute name='filename'/&gt;"+
"&lt;attribute name='documentbody'/&gt;"+
"&lt;attribute name='annotationid'/&gt;"+
"&lt;order attribute='subject' descending='false'/&gt;"+
"&lt;filter type='and'&gt;"+
"&lt;condition attribute='isdocument' operator='eq' value='1'/&gt;"+
"&lt;condition attribute='filename' operator='eq' value='mex9.jpg'/&gt;"+
"&lt;/filter&gt;"+
"&lt;/entity&gt;"+
"&lt;/fetch&gt;"+
" </fetchXml>" +
" </Fetch>" +
" </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/Fetch");
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);
}

// 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;','>');

 


// Create an XML document that you can parse.
   var oXmlDoc = new ActiveXObject("Microsoft.XMLDOM");
   oXmlDoc.async = false;
// Load the XML document that has the UnEncoded results.
   oXmlDoc.loadXML(resultSet);

 

// Display the results.
var results = oXmlDoc.getElementsByTagName('result');

for (i=0;i < results.length;i++)
    {var docBody= results[i].selectSingleNode('./documentbody').nodeTypedValue;}


crmForm.all.IFRAME_Picture.src="";

// BE CAREFULL WITH THE QUOTES

var image = "<img alt='', src= 'data:image/jpeg;base64," + docBody + "'> </img>";

ff = document.getElementById("IFRAME_Picture");

ff.contentWindow.document.write(image);

}

Thank you, Tito-Z, for your success in this endeavor and for allowing me to host it here, on my blog.

12 comments:

  1. Hi,

    I'm trying to use your code to display a Picture of a Contact in CRM, but the picture is cropped !

    Do you have ant idea ?

    thx.

    ReplyDelete
  2. It's not my code, it's Tito-Z's. And to be honest, I haven't yet used it. I posted it here because the author doesn't have a blog resource of his own. That said, I'm uncertain why the picture would be cropped; but I assume that it's probably the bounds of the Iframe that's causing it. You may need to perform some DOM-modifications in order to size the Iframe appropriately.

    ReplyDelete
  3. I have the exact same issue, looks like the size of content allowed to be returned by the webservice call is limited to a certian size (as image is retuend as a base64 string).

    ReplyDelete
  4. That makes sense, although I have trouble understanding how you could submit the attachment through the web-service, and not retrieve it from the same. This assumes, logically, that the contents are base64-encoded bidirectionally--which I see no reason to disbelieve. If the file hasn't broached any limitations being sent to CRM, why would it broach some when retrieved?

    I'll be interested to hear additional insight on this matter.

    ReplyDelete
  5. According to your own referenced link:
    http://www.websiteoptimization.com/speed/tweak/inline-images/

    IE8 limits URI length: 32 kilobytes

    This is why images may appear cropped, or not appear at all.

    FYI, IE9 allows large URI using the base64 method.

    ReplyDelete
  6. Excellent finding! I'm open to any ideas for circumventing the URI method for this.

    ReplyDelete
  7. Dear Dave,

    At first: I'm new to CRM...so sorry if I ask too easy :)

    So I've tried to use this code, but I have some issue..

    1: I've made an IFrame on the form...what should be the URL of the Iframe?
    2: Just paste this code to the OnLoad() event of the form?

    I'm using IE 9...and the CRM instance (http://crm.[something].hu:5555/Demo) is running on a remote server...and I'm trying to customize a form via my pc.

    Thanks for your help in advance!
    Regards,
    tyu67

    ReplyDelete
  8. 1. It doesn't matter what the IFrame's URL is, the contents are replaced by the Javascript. The best thing to do, is reference a blank document. Either "/_root/Blank.aspx" or "about:blank" should suffice.

    2. The code above uses a FetchXML query to look for an annotation with a file attachment, where the file is named "mex9.jpg". You'll probably need to change that, if you intend to use an alternate file name. It then dumps the contents into an Iframe control called "IFRAME_Picture". If yours is different, update the code as appropriate. Otherwise, yes, that code should work with a straight copy-paste.

    ReplyDelete
  9. Sorry, you asked if "OnLoad()" was the right place for it, and that would be a good place for it if you want the picture to load with the form.

    ReplyDelete
  10. Ohhh...I was careless about the picture's filename. I didn't see it in the FetchXML query. Sorry :)

    Now it works fine! Thx

    Now I just have to figure out how to set the filename parameter dinamically.
    So what to do if I don't know in advance the name of the attached picture?

    Do you have any idea?

    Thx!

    tyu67

    ReplyDelete
  11. Well, it's really the FetchXML query that sets the parameters for discovering the attachment. So, you can setup the query to look for any value either on the annotation entity itself, or through its relationship to another record (via FetchXML "link-entity" nodes). Do some research on FetchXML, or use the FetchXML Wizard by Michael Hoehne over at Stunnware.

    ReplyDelete
  12. Ok!

    I'm going to try it.

    Thank you 1x again!

    ReplyDelete

Unrelated comments to posts may be summarily disposed at the author's discretion.