Sunday, August 8, 2010

Making your SharePoint 2010 applications ECM aware (Part Four – Records Management)

This is the final installment in my “Making your SharePoint 2010 applications ECM aware” series and it deals with SP2010 Records Management. In this posting I will show you how you can make your server based solutions interact with the built in records management features now available in SP2010. I will do this by showing you the code you need to implement and the SharePoint object model to use.

Records management is a required feature in any ECM system. SP2010 has enhanced SP2007 capabilities by allowing users to declare documents as records at any time, also known as “In place” records management. So why let users manually declare records instead of just relying on SharePoint’s “Document Center”? One reason, is that many companies may have documents outside of the “Document Center” and want to place a restriction or an information policy on a document based on a user review process. As documents are scanned into different document libraries, users (Record managers, lawyers, compliance officers) can not only apply metadata, but also review the document for sensitive information.  The user can then make the decision to declare or even un-declare a document as a record. This feature adds a lot of flexibility to your records review process.

Determine if users can manually declare records

When creating SharePoint records management solutions, one of the most difficult tasks will be programmatically determining if manual record declaration is enabled for a file or a list. You can allow manual records declaration at the site collection level under the “Record declaration settings”.  In the “Record Declaration Availability” section you can select an option to make record declaration available.  This affects what is the site default that is displayed in the document library record declaration settings. In the “Declaration Roles” section where you set what user roles can declare records, you can set “Only policy actions” then manual declarations are not allowed. Selecting the other two roles allows manual declaration.

 

An administrator can override the site records declaration settings by setting a document library’s record declaration settings shown below.

In order to determine if manual records declaration is available for a file or a list you must find where the above settings are stored. These settings are stored in the SPWeb.Properties and the SPList.RootFolder.Properties collections. The code below shows a method that reads the appropriate properties and makes the determination. All the code listed in this posting requires a reference to the Microsoft. Office.Policy assembly located in the GAC or in the ISAPI folder of the 14 hive.

public static bool IsManualDeclarationEnabled(SPList list)
{
    bool isFeatureActive = Records.IsInPlaceRecordsEnabled(list.ParentWeb.Site);

    bool enabledInSite =
        list.ParentWeb.Properties.ContainsKey("ecm_SiteRecordDeclarationDefault")
        && list.ParentWeb.Properties["ecm_SiteRecordDeclarationDefault"].ToString()
        .Equals(bool.TrueString, StringComparison.OrdinalIgnoreCase);

    bool useListSpecific =
        list.RootFolder.Properties.ContainsKey("ecm_IPRListUseListSpecific")
        && list.RootFolder.Properties["ecm_IPRListUseListSpecific"].ToString()
        .Equals(bool.TrueString, StringComparison.OrdinalIgnoreCase);

    bool enabledInList =
        list.RootFolder.Properties.ContainsKey("ecm_AllowManualDeclaration")
        && list.RootFolder.Properties["ecm_AllowManualDeclaration"].ToString()
        .Equals(bool.TrueString, StringComparison.OrdinalIgnoreCase);

    if (isFeatureActive)
    {
        if (!useListSpecific && enabledInSite) return true;
        if (useListSpecific && enabledInList) return true;
    }

    return false;
}

Now that we can determine if manual record declaration is available we must also check to see if the user falls within a role that allows it. In the image above for the site collection settings you can allow only list administrators, contributors and list administrators, or only policy actions. Manual record declaration can only be done by either a list administrator or a list contributor. The code below shows how to determine this.

public static bool CanDeclareRecord(SPFile file)
{
    bool declare = false;

    RecordDeclarationPermissions perms =
        Records.GetDeclareByPermissionsForSite(file.Web.Site);

    if (perms == RecordDeclarationPermissions.AllListContributors)
    {
        declare =
            file.Item.ParentList.DoesUserHavePermissions(SPBasePermissions.EditListItems);
    }
    else
        declare = ((perms == RecordDeclarationPermissions.OnlyAdmins)
            && file.Item.ParentList.
            DoesUserHavePermissions(SPBasePermissions.EmptyMask
            | SPBasePermissions.ManageLists));

    return declare;

}

public static bool CanUnDeclareRecord(SPFile file)
{
    bool undeclare = false;

    RecordDeclarationPermissions perms =
        Records.GetUndeclareByPermissionsForSite(file.Web.Site);

    if (perms == RecordDeclarationPermissions.AllListContributors)
    {
        undeclare =
            file.Item.ParentList.DoesUserHavePermissions(SPBasePermissions.EditListItems);
    }
    else
        undeclare = ((perms == RecordDeclarationPermissions.OnlyAdmins)
            && file.Item.ParentList.
            DoesUserHavePermissions(SPBasePermissions.EmptyMask
            | SPBasePermissions.ManageLists));

    return undeclare;

}

Declaring a file as a record

So now that we know the user can declare a file as a record, lets look at how to actually declare it. It is one line of code using the static DelcareItemAsRecord method of the Records class. This method takes a SPListItem as an argument. However, many times I work with url’s and SPFiles. So the code below shows how to use these as arguments.

public static void DeclareFileAsRecord(string url)
{

    using (SPSite site = new SPSite(url))
    {
        using (SPWeb web = site.OpenWeb())
        {
            SPFile file = web.GetFile(url);

            if (file.Exists)
            {
                DeclareFileAsRecord(file);
            }
            else
            {
                throw new Exception("File does not exist");
            }
        }

    }

}

public static void DeclareFileAsRecord(SPFile file)
{

    Records.DeclareItemAsRecord(file.Item);

}

If you want to un-declare a file as a record then just call the Records.UndeclareItemAsRecord static method.

Determining if a file is a record

Another piece of information your solution will need is if a file has been declared a record. Your solution needs to know this so it can take appropriate action. For example, you may not want to allow users to edit or delete the document. Once again it is a call to a static method on the Records class.

public static bool IsFileRecord(string url)
{
    bool record = false;

    using (SPSite site = new SPSite(url))
    {
        using (SPWeb web = site.OpenWeb())
        {
            SPFile file = web.GetFile(url);
            record = Records.IsRecord(file.Item);
        }
    }

    return record;

}

public static bool IsFileRecord(SPFile file)
{
    bool record = false;

    record = Records.IsRecord(file.Item);

    return record;

}

Determining record restrictions

Remember you could apply restrictions to a record in the Site Collection “Records Declaration Settings” page.

Here you can block a record from being deleted, or edited and deleted. Your solution may want to check this regardless of whether a user can manually declare a file as a record. You can use the Records class and some more static methods. The IsLocked method determines if a file cannot be edited or has been put on hold. The IsDeleteBlocked method checks to see if the file can be deleted. You should check if the file is locked first, and if it is not then check if it can be deleted.

public static bool IsFileLocked(string url)
{
    bool locked = false;

    using (SPSite site = new SPSite(url))
    {
        using (SPWeb web = site.OpenWeb())
        {
            SPFile file = web.GetFile(url);
            locked = Records.IsLocked(file.Item);
        }
    }

    return locked;

}

public static bool IsFileLocked(SPFile file)
{

    return Records.IsLocked(file.Item);

}

public static bool IsDeleteBlocked(string url)
{
    bool locked = false;

    using (SPSite site = new SPSite(url))
    {
        using (SPWeb web = site.OpenWeb())
        {
            SPFile file = web.GetFile(url);
            locked = Records.IsDeleteBlocked(file.Item);
        }
    }

    return locked;

}

public static bool IsDeleteBlocked(SPFile file)
{
    return Records.IsDeleteBlocked(file.Item);
}

 

Is your SharePoint application ECM aware?

I hope the above code helps make your solutions active participants in SP2010 ECM features. One limitation is that your solution must reside on the SharePoint server. This code can easily be wrapped into a custom web service where your remote solution could utilize it. However, as we all know, this makes for more difficult installations, and some corporate environments do not allow custom web services. Unfortunately, Microsoft did not expose any of this functionality in the client object model per se. But as I have demonstrated previously with document sets where there is a will there is a way. If you would like to see another posting about how to accomplish records management with the client object model, then leave a comment to this post and I will research how difficult it may be.

This series of postings has shown how to add value to your applications by integrating with the new ECM features within SP2010, including the content organizer and document sets. If your applications are not leveraging these features, then of course users will look else where for better solutions.

11 comments:

Anonymous said...

I would like to see a " posting about how to accomplish records management with the client object model".

Nicholas said...

Hi,
Thanks for your informative post. I am attempting to declare a record on items added to a list, however when I access the List's RootFolder's properties HashTable, there are no keys for ecm_AllowManualDeclaration, ecm_IPRListUseListSpecific, nor ecm_SiteRecordDeclarationDefault in the Parent web's Properties. I have Record Declaration Availability set at the site collection level, and Manual Record Declaration Availability at the list level, however these properties are null?

Steve Curran said...

Nicholas, I updated the code to check to see if the feature is enabled, and if manual declaration is enabled on the site. If it is enabled on the site and the list is using the site default, it will return true.

waran said...

is it possible to deploy a record to all sitecollection using API?

snreddy said...

hi urs Making your SharePoint 2010 applications ECM awareNice post. The way you written is appreciated

http://sharepointsolution2010.blogspot.com/2011/01/sharepoint-2010-record-undeclare-how.html

Anonymous said...

Thanks !

records management said...

Thanks for the instructions! I was having a real problem keeping records of work done by team membbers at different times, and this seems to solve it perfectly.

Angama Jeff said...

Hi, could you help me, when i try to declare the SPListItem as a record with Record.Declare... it goes in catch exception without displaying any message ? Any idea ? Thank you

Angama Jeff said...

Actually it tells me : "Attempted to perform an unauthorized operation". My code is running under RunWithElevatedPriviledge in a console application.
Any idea ? thx

Anonymous said...

Awesome post! Two-and-a-half years later, and still a very valuable post; thanks for taking the time!

Kaboodle Colin said...

Now 3-and-a-half years on and still an awesomely useful post.

One thing I have noticed is that the Records.IsInPlaceRecordsEnabled method has an overload which takes an SPList object and you can use that to determine whether in place RM is available on the list rather than having to check the properties of the root folder yourself.

Maybe this was added by a service pack at some point between when the post was first written and now?

Post a Comment