Friday, May 15, 2009

View SharePoint Documents in an I-Frame in CRM 4.0

Many CRM 4.0 users want to be able to store documents against entities. While attaching documents is one approach a better solution is to use SharePoint as the document store. You don't have to use full blown SharePoint, WSS is perfectly adequate for this solution.
The approach I've taken is to use a SharePoint document library and create folders for each instance of a CRM entity (in my case service requests). You'll need some code in CRM 4.0 that will create a new folder in SharePoint when a service request is created. And you'll need some way of labelling the folder so it's obvious which service request its referring to (the Case Reference appended with the Customer name might be a good solution). I've been lazy and just used the incidentid as the folder name for this example.
I created a 'Correspondence' tab on the Service Request form. I added an I-Frame and the url to my web page. I checked the option to "Pass record object type code and unique identifier as parameters".



Obvioulsy if you click on the link, it opens the document from SharePoint.

The ASP.net page just has a literal on it which will render a table of documents from the relevant document library folder. It passes the incident id onto a web service.
So the Page_Load event code is
string CRMGUID = "";
if (Request.QueryString["id"] != null)
{

CRMGUID = Request.QueryString["id"].ToString();
Service ws = new Service();
XmlDocument xmldom = new XmlDocument();
xmldom = ws.GetSPSDocs(CRMGUID);

Literal1.Text = xmldom.InnerXml;

}

The Web Service code is given below. Note that I am returning a fragment of HTML wrapped up in a top level DIV tag so I can return it as XML.
Also note that this solution assumes that the folders are all in a flat structure underneath the document library. You'll need a more scalable solution if the number of documents will be large. It would be better to use a CAML query to return just the documents you are interested in rather than the approach I use in this example, but I was in a hurry. It also needs to be changed to support other file types than the three I chose.

[WebMethod]
public XmlDocument GetSPSDocs(string CRMGUID) {

SPLists.Lists ws = new SPLists.Lists();
ws.Url = "http://biztalkcrm:8080/sites/complaints";

string sharePointSiteUrl = "http://biztalkcrm:8080/sites/complaints";
string strListName = "SharedDocuments";
string m_UserName = "Administrator";
string m_Domain = "BIZTALKCRM.local";
string m_Pword = "Password1";
XmlDocument xmldom = new XmlDocument();
string html = "";
// {} not used in folder names
CRMGUID = CRMGUID.Replace("{", "");
CRMGUID = CRMGUID.Replace("}", "");


NameTable nt = new NameTable();
XmlNamespaceManager nsmgr = new XmlNamespaceManager(nt);

nsmgr.AddNamespace("rs", "urn:schemas-microsoft-com:rowset");
nsmgr.AddNamespace("z", "#RowsetSchema");
string filename = "";
string fileurl = "";
XmlNodeList results = null;
DateTime lastmodified ;
html += "<div><table width=\"80%\" border=\"1\" style=\"border-collapse: collapse;\" title=\"Document list\">";
html += "<tr><th class=\"tableheader\">Type</th><th class=\"tableheader\">Name</th><th class=\"tableheader\">Last modified</th></tr>";
try
{

XmlNode nodeList = GetListByName(sharePointSiteUrl, strListName, null, m_UserName, m_Domain, m_Pword);
if (nodeList != null)
{
results = nodeList.SelectNodes("//z:row[@ows_FileRef[contains(.,'" + CRMGUID + "')]]", nsmgr);
//results = nodeList.SelectNodes("//z:row", nsmgr);
}
if (results != null)
{
foreach (XmlNode nodeDoc in results)
{
XmlDocument xDoc = new XmlDocument();

xDoc.LoadXml(nodeDoc.OuterXml);

html += "<tr>";
html += "<td>";
if (GetXmlAttribute(xDoc, "//@ows_LinkFilename", nsmgr).IndexOf("pdf") > 0)
{
html += "<img src=\"http://localhost:8080/_layouts/images/pdf.jpg\" />";
}
if (GetXmlAttribute(xDoc, "//@ows_LinkFilename", nsmgr).IndexOf("msg") > 0)
{
html += "<img src=\"http://localhost:8080/_layouts/images/icmsg.gif\" />";
}
if (GetXmlAttribute(xDoc, "//@ows_LinkFilename", nsmgr).IndexOf("doc") > 0)
{
html += "<img src=\"http://localhost:8080/_layouts/images/icdoc.gif\" />";
}
html += "</td>";
filename = GetXmlAttribute(xDoc, "//@ows_LinkFilename", nsmgr);
fileurl = GetXmlAttribute(xDoc, "//@ows_FileRef", nsmgr);
fileurl = fileurl.Substring(fileurl.IndexOf("#") + 1, fileurl.Length - fileurl.IndexOf("#") - 1);
fileurl = "http://biztalkcrm:8080/" + fileurl;
html += "<td>";
html += "<a href='" + fileurl + "'>" + filename + "</a>";
html += "</td>";
lastmodified = Convert.ToDateTime(GetXmlAttribute(xDoc, "//@ows_Modified", nsmgr));
html += "<td>";
html += lastmodified.ToString("dd/MM/yyyy HH:mm");
html += "</td>";
html += "</tr>";
}
}

}

catch (Exception ex)
{

}

html += "</table></div>";
xmldom.LoadXml(html);

return xmldom;
}

The two other procedures are GetListByName and GetXmlAttribute. Please note that you must change the line in GetListByName where I have hard-coded the Guid of the 'All documents' view on my document library.
string viewName = "{FAD1A412-359A-4FF6-BC1E-0A53934791CD}";


public XmlNode GetListByName(string sharePointSiteUrl, string strListName, XmlNode query, string m_UserName, string m_Domain, string m_Pword)
{
try
{
SPLists.Lists lists = new SPLists.Lists();
SPViews.Views views = new SPViews.Views();

lists.Url = sharePointSiteUrl + "/_vti_bin/lists.asmx";
views.Url = sharePointSiteUrl + "/_vti_bin/views.asmx";

System.Net.NetworkCredential cred = new System.Net.NetworkCredential(m_UserName, m_Pword, m_Domain);
lists.Credentials = cred;
views.Credentials = cred;

XmlNode nodeList = null;

string strRealListName = "";

try
{

switch (strListName)
{
case "SharedDocuments":
strRealListName = "Shared Documents";
break;

}
// string query = "";
XmlDocument temp = new XmlDocument();
// search thro all folders
XmlNode queryOptions = temp.CreateNode(XmlNodeType.Element,"QueryOptions","");
queryOptions.InnerXml = "";

// cheat for view name of All documents
string viewName = "{FAD1A412-359A-4FF6-BC1E-0A53934791CD}";
nodeList = lists.GetListItems(strRealListName, viewName, query, null, "",queryOptions, null);
}

catch (Exception ex)
{
// record error message
}
return nodeList;
}
catch (Exception ex)
{
throw ex;
}
}


public string GetXmlAttribute(XmlNode zrowOuterNode, string XPath, XmlNamespaceManager nsmgr)
{

string strInnerText = "";
XmlNode thisNode = zrowOuterNode.SelectSingleNode(XPath, nsmgr);
if (thisNode != null)
{
strInnerText = thisNode.InnerText;
}
return strInnerText;

}

No comments: