Monday, May 31, 2010

Making your SharePoint 2010 applications ECM aware (Part two – Document Sets)

This is part two of a multipart series of how to take advantage of the new ECM features in SharePoint 2010. Part one of this series talked about how you enable your applications to use the SharePoint Content Organizer feature. In this post I will discuss how to do the same with the new Document Set feature in SharePoint .

Document sets are basically SharePoint folders on steroids. The original concept was to enhance team collaboration on a set of documents without having to set up an additional SharePoint site. You could also look at Document Sets as mini SharePoint document libraries. They allow you to set permissions, assign workflows, share common properties and send an email link.  However, the real power of Document Sets is contained in their ability to define a repeatable process. A custom Document Set content type can be defined and reused across a site. In addition to assigning site columns to a document set content type, you can set common shareable properties that will be given to each new document that is added to the document set. This helps group common documents by common metadata, making it easy to search and retrieve these documents. Another, great feature is the ability to define what content types are allowed to be added to the document set. This gives administrators the ability to control the metadata assigned to documents within the set. Finally, the most important feature is the ability to assign default documents to be added automatically to the document set when it is created.

Document sets are a great way to increase productivity. For example, if a loan processing company receives a home loan application, then it can be scanned into a new  custom document set which will automatically have other required documents added. For instance, a property appraisal form or a house inspection report. Also, the document set itself has required metadata that needs to be entered before any documents are added. Document sets gives businesses the ability to put a process around content, ergo “content management”.

So, lets say you have a document scanning application and you want your application to create new types document sets dynamically. How can the application emulate being able to set up a new document set like the SharePoint UI? The following code shows how to use the server side object model to set up a new document set including adding shared properties, allowable content types, and default documents. The key is the DocumentSetTemplate class which contains all the properties needed.

 

public static void CreateDocumentSetContentType(string destinationUrl,
            string documentSetTypeName, string defaultDocumentPath)
{

            using (SPSite site = new SPSite(destinationUrl))
            {
                using (SPWeb web = site.OpenWeb())
                {

                    //create the new document set contenttype
                    SPContentType newDocumentSetContentType = web.ContentTypes.Add
                        (new SPContentType(web.ContentTypes["Document Set"],
                            web.ContentTypes,
                            documentSetTypeName));

                    //get a DocumentSetTemplate for the new document set
                    DocumentSetTemplate newDocumentSetTemplate =
                        DocumentSetTemplate.GetDocumentSetTemplate
                        (newDocumentSetContentType);

                    //add allowable content types
                    newDocumentSetTemplate.AllowedContentTypes.Add
                        (web.ContentTypes["Document"].Id);

                    newDocumentSetTemplate.AllowedContentTypes.Add
                        (web.ContentTypes["Picture"].Id);

                    //get the default document's binary
                    FileInfo fi = new FileInfo(defaultDocumentPath);
                    byte[] defaultDocumentBytes = new byte[fi.Length];
                    FileStream fs = fi.OpenRead();
                    fs.Read(defaultDocumentBytes, 0, (int)fi.Length);
                    //add the default document
                    newDocumentSetTemplate.DefaultDocuments.Add("defaultDocument",
                        web.ContentTypes["Document"].Id, defaultDocumentBytes);

                    //add a shareable property
                    newDocumentSetTemplate.SharedFields.Add
                        (newDocumentSetContentType.Fields["Description"]);

                    //make sure to add the document set name to the default documents
                    newDocumentSetTemplate.DefaultDocuments.AddSetName = true;

                    newDocumentSetTemplate.Update(true);
                    newDocumentSetContentType.Update();
                    web.Update();
                }
            }
}

After running this code you can verify that it works by going to “Site Settings—>Site Content Types” . Find your new content type and select it.  Then you can view your settings by clicking on “Document Set Settings”. Now you can use this new document set content type to create new project folders in your document libraries.

While I was testing this code I was curious as to where the actual default documents are stored in SharePoint. They must be made available to all content types inheriting from it. At first I thought they might be stored in a site level hidden list. However they are actually stored within the SPWeb’s folder collection under “_cts”. So if you want to see the documents programatically you use the this as example to follow:

SPWeb.Folders["_cts"].SubFolders["document set name"]

Another thing your application may want to determine is if a particular file is part of a document set. The code below will take a url to a SharePoint file and return whether it is part of a document set.

       public static bool IsPartOfDocumentSet(string url)
       {

           DocumentSet ds = null;

           using (SPSite site = new SPSite(url))
           {
               using (SPWeb web = site.OpenWeb())
               {
                   object value = web.GetFileOrFolderObject(url);

                   if(typeof(SPFile).IsAssignableFrom(value.GetType()))
                   {
                       SPFile file = value as SPFile;
                       ds = DocumentSet.GetDocumentSet(file.ParentFolder);
                   }
                   else
                   {
                       if(typeof(SPFolder).IsAssignableFrom(value.GetType()))
                       {
                           SPFolder folder = value as SPFolder;
                           ds = DocumentSet.GetDocumentSet(folder);
                       }
                       else
                           return false;
                   }

                   if (ds != null)
                       return ds.ContentTypeTemplate.AllowedContentTypes.Count > 0? true : false;
                   else
                       return false;

               }
           }
         }

 

This code uses the static GetDocumentSet method of the DocumentSet class. All the code above makes use of the Microsoft.Office.DocumentManagement assembly located in the 14 hive ISAPI folder. Once you have a reference to the DocumentSet object you can interrogate it’s properties to work with it in your application. If you need to access the files in the document set you can access the Folder property of the document set which contains the Files collection.

The biggest problem with SharePoint document sets is the lack of any remote access to them. There are no client object model methods or web services to access them. It is up to the developer to create a custom web service to manipulate them remotely from the application.  You can create a document set content type through the client object model:

Creating document sets remotely

Unfortunately you cannot add the default documents and other features described above. It might be possible by manipulating the Schema property of the client object model content type by adding the xml declarations for the allowable content types and other information. Further research needs to be done on that. If I am able to do this, then I will be sure to post it here.

Document sets are a great new feature in SP2010 and can add a lot to your content management application. Unfortunately, Microsoft needs to give us access to them remotely.

Saturday, May 8, 2010

Making your SharePoint 2010 Applications ECM Aware (Part One)

SharePoint 2010 has many new Enterprise Content Management (ECM) features. Document sets, which give you the ability to aggregate documents into a cohesive set based on common metadata attributes. “In Place” records management, giving users the ability to declare a document located anywhere as a record and apply corporate policies to that document.  “Document ID” service, which applies a unique ID to all documents which can be used to locate that document within a site collection. Finally, the “Content Organizer” which enables companies to centrally apply rules to incoming documents so they can be routed to the appropriate document libraries within a site collection.

This multi-part series will explain how you can use new and existing coding techniques to enable your SharePoint applications to take advantage of these new SharePoint ECM capabilities. The series will illustrate both server side and client side code to accomplish this.

The Content Organizer

The content organizer feature in SP2010 allows administrators to set up routing rules based on content types and metadata to organize incoming documents. This feature entails the use of a “drop off library” where incoming documents are submitted and evaluated using the rules.  So if your application submits documents to a document library then you must determine if the site collection is using a content organizer and whether the document library is a target of a content organizer rule. Once this determination has been made you must then upload your document to the “drop off library” so it can be routed correctly. The first thing your application must determine is if the content organizer is enabled and if users are allowed to bypass the “drop off library” . These settings can be seen in “Content Organizer Settings” under “Site Administration” in “Site Settings”.

Below is a server side code which determines if the content organizer is enabled and if it is being enforced. The code uses the Microsoft.Office.Policy.dll and the new EcmDocumentRoutingWeb class. You want to first determine if content organizing is being enforced for a particular SharePoint site. The code will check the “client_routerEnforceRouting” SPWeb property. Once this is determined that the user does not have the option to override the drop off library, then the second piece of code determines if the document library is a target of a content organizer rule. If the code determines it is, then the third piece of code will return the url to the “drop off library” so you can submit the document there.

public static bool IsContentOrganizerEnforcedForWeb(string webUrl)
{

            bool enforced = false;

            using (SPSite site = new SPSite(webUrl))
            {
                using (SPWeb web = site.OpenWeb())
                {
                    if (web.Properties.ContainsKey("client_routerEnforceRouting"))
                    {
                        object enforce = web.Properties["client_routerEnforceRouting"];
                        if (enforce.ToString().Equals(bool.TrueString,
                            StringComparison.OrdinalIgnoreCase))
                            enforced = true;
                    }

                }

            }

            return enforced;

}

 

public static bool IsContentOrganizerEnforcedForLibrary(string webUrl, string libraryName)
{

            bool enforced = false;

            if (IsContentOrganizerEnforcedForWeb(webUrl))
            {

                using (SPSite site = new SPSite(webUrl))
                {
                    using (SPWeb web = site.OpenWeb())
                    {
                        EcmDocumentRoutingWeb edrw =
                            new EcmDocumentRoutingWeb(web);

                        enforced =
                            edrw.RoutingRuleCollection.IsTargetLibrary("/" + libraryName);

                    }

                }

            }

            return enforced;

}

 

public static string GetDropOffLibraryUrl(string webUrl)
{

           string dropOffUrl = string.Empty;

           using (SPSite site = new SPSite(webUrl))
           {
               using (SPWeb web = site.OpenWeb())
               {
                   EcmDocumentRoutingWeb edrw =
                       new EcmDocumentRoutingWeb(web);

                   dropOffUrl = edrw.DropOffZoneUrl;
               }

           }

           return dropOffUrl;

}

 

The Content Organizer and remote applications

The previous code will work with web parts or asp.net applications running on a SharePoint server. However, you may have a desktop application not running on a SharePoint server or a silverlight application that requires you to access SharePoint remotely. If this is the case, then you can use a combination of the SharePoint client object model and the Official web service to accomplish the same thing. The first example of code uses the Microsoft.SharePoint.Client.dll which enables you to access a subset of the SharePoint server object model remotely. It basically does the same thing as the previous IsContentOrganizerEnforedForWeb method.

public static bool IsContentOrganizerEnforced(string webUrl)
{

           bool enforced = false;

           ClientContext context =
               new ClientContext(webUrl);

           Web web = context.Web;
           context.Load(web);

           PropertyValues pv = web.AllProperties;
           context.Load(pv);
           context.ExecuteQuery();

           if (pv.FieldValues.ContainsKey("client_routerenforcerouting"))
           {
               object enforce = pv.FieldValues["client_routerenforcerouting"];
               if (enforce.ToString().Equals(bool.TrueString, StringComparison.OrdinalIgnoreCase))
                   enforced = true; 
           }

           return enforced;

}

This code would be called first to determine if the content organizer is being enforced. In order to determine if the document library is a target of content organizer rule and get the url to the “drop off library”, then you can call the the Official web service’s GetFinalRoutingDestinationFolderUrl method. It takes the target document library’s full url along with a file name, for example, “http://basesmc2008/shared documents/whatever.txt” and will return either “http://basesmc2008/shared documents/whatever.txt” if it is not a target or “http://basesmc2008/dropofflibrary/whatever.txt” (localized) if it is a target. The strange thing is, the method needs a complete url to an item or document for it to work. This was a change from RC to RTM.

public static string GetDropOffLibraryUrll(string documentLibraryUrl)
{
            official.RecordsRepository rr = new official.RecordsRepository();
            rr.Url = "http://basesmc2008/_vti_bin/officialfile.asmx";
            rr.UseDefaultCredentials = true;
            official.DocumentRoutingResult dr =
                rr.GetFinalRoutingDestinationFolderUrl(null, null, documentLibraryUrl);

            string url = dr.Url;
            return url;

}

This all can be accomplished the same way with a silverlight application by just using the Microsoft.SharePoint.Silverlight.dll along with the Official web service. The only change is the code must be called asynchronously.

So now you have all the code you need to make your applications “Content Organizer” aware and to make sure you upload your documents to the appropriate document library, so your application can participate in a company’s taxonomy rules.

In the next part of this multi-part post I will explain how you can determine if a document is part of a document set and how to add a document to a document set in SharePoint 2010.