4

I need to be able to query old field values from items in a SharePoint list. I can't execute code on the server (it needs to work with SharePoint Online/O365 for a start). So far the ONLY API I've that lets me do this is the lists.asmx GetVersionCollection SOAP call. This lets me specify a single field name and returns an XML structure with the values for the various versions, along with the modification time and who made the change - but NO reliable way of actually identifying which version (i.e. an ID or label). That is, if I know I need to fetch the Title value from version 512 of item 1 in list "Documents", I don't see how to reliably parse the results to determine which entry is version 512. While they may be returned in order, in many cases the entries are actually missing when there was no field value present (or perhaps when the field hadn't been created yet). I've tried comparing the Modified date to the Created date of the corresponding FileVersion item (which I can get via CSOM or REST), and while it works some of the time, it's not reliable. I've also looked at the output from the lists.asmx GetVersion API but I don't see how that's useful either, as the Created property for all versions always seems to be just the date the file was originally created. It does seem odd to me that there's not a neat way of doing this - if I need to return information for several fields but just for a single version, I have to make a whole lot of requests that return far more info than I need, and then I need to figure out how to parse the returned text in the case of, say, multiple-value taxonomy fields etc. Anyone tried doing anything similar here?

Thanks

Dylan

Dylan Nicholson
  • 1,235
  • 14
  • 20

2 Answers2

5

I would recommend to consider the following options:

Consume SharePoint Web Services

Probably this is the best bet to utilize Lists.GetVersionCollection Method but as you correctly mentioned in the question you could only request version info per field. The following example demonstrates how to retrieve version info for multiple fields and determine version info.

C# example

public static Dictionary<string, List<VersionProperty>> GetVersionHistory(Uri webUri, string listName, int itemId, string[] fieldsToRetrieve)
{

        var listsProxy = new Lists.Lists();
        listsProxy.Url = webUri + "/_vti_bin/Lists.asmx";

        //Retrieve version labels
        var versionsResult = listsProxy.GetVersionCollection(listName, itemId.ToString(), "Version");
        var versionsXml = XElement.Parse(versionsResult.OuterXml);
        XNamespace xmlns = "http://schemas.microsoft.com/sharepoint/soap/";
        var versionLabels = versionsXml.Descendants(xmlns + "Version").Select(e => new VersionLabel()
        {
            VersionNo = e.Attribute("Version").Value,
            Modified = e.Attribute("Modified").Value,
            ModifiedBy = e.Attribute("Editor").Value
        }).ToList();

        //Retrieve properties
        var versionHistory = new Dictionary<string, List<VersionProperty>>();
        foreach (var f in fieldsToRetrieve)
        {
            versionsResult = listsProxy.GetVersionCollection(listName, itemId.ToString(), f);
            versionsXml = XElement.Parse(versionsResult.OuterXml);
            var properties = versionsXml.Descendants(xmlns + "Version").Select((e, i) => new VersionProperty { Label = versionLabels[i], Value = e.Attribute(f).Value });
            versionHistory[f] = properties.ToList(); 
        }
        return versionHistory;
}

where

public class VersionLabel
{
    public string VersionNo { get; set; }

    public string Modified { get; set; }

    public string ModifiedBy { get; set; }

}

public class VersionProperty
{
    public VersionLabel Label { get; set; }

    public string Value { get; set; }
}

Usage

var listName = "Documents";
var itemId = 1;
var fieldsToRetrieve = new[] { "Title", "TaxKeyword" };
var versionHistory = GetVersionHistory(webUri, listName, itemId, fieldsToRetrieve);

Result

Extract version history from Versions.aspx application page

Another option to request Versions.aspx out-of-the-box SharePoint application page and extract version history information from html content.

Versions.aspx page

enter image description here

The following example demonstrates how to load Versions.aspx page content (the part responsible for extracting version info is omitted).

C# example

 public static  Dictionary<string, List<VersionProperty>> GetVersionsPageInfo(string webUrl,ICredentials credentials, Guid listId,int itemId)
 {
        var versionsPageUrl = string.Format("{0}/_layouts/versions.aspx?list={1}&ID={2}",webUrl, listId,itemId);
        using (var client = new WebClient())
        {
            client.Credentials = credentials;
            client.Headers.Add("X-FORMS_BASED_AUTH_ACCEPTED", "f");
            var content = client.DownloadString(versionsPageUrl);
            //extract version history info goes here.. 
        }
 }
Vadim Gremyachev
  • 57,952
  • 20
  • 129
  • 193
  • Very detailed answer, thanks, but doesn't actual address the performance issue when querying O365 in particular so many times, which is my main problem. – Dylan Nicholson Feb 08 '15 at 02:23
  • I agree about performance issue in case of SharePoint web services, but what about the second approach, getting version history from versions.aspx page? In that case only a single request should be performed to the server, after that you just need to extract versions info from HTML content. – Vadim Gremyachev Feb 08 '15 at 13:26
  • That doesn't have all the field data, and what data it has is often not in a format that can be used to reliably extract the underlying value. – Dylan Nicholson Feb 08 '15 at 20:45
0

I answered almost a similar question regarding versions here. I hope this somehow leads you to the right path but basically what this solution does is it goes through all FileVersion of a ListItem. From there you can get the version numbers. As for how to use the correct version number if its 512, 1048 and so on you can read up on the details regarding that here.

Community
  • 1
  • 1
lem.mallari
  • 1,274
  • 12
  • 25
  • 1
    That doesn't help with retrieving all field values for a specific version, which I have code that does actually work for now but is hopelessly slow for O365 (each field has to be queried one by one and each call pulls back all values from the entire history). – Dylan Nicholson Feb 06 '15 at 00:57