I occasionally have to use the SharePoint .Net Client-Side Object Model (CSOM) API to reach into SharePoint and poke around a little bit. It's all pretty straightforward, but something that I have not found to be straightforward is reliably determining which version of a file is the latest major/published version.
In this context, I'm usually working with a SharePoint Document Library where versioning is enabled with Major and Minor versions. The application is also using a user with administrator access to the site, which means it can view all versions of a document, including minor versions which would not normally be visible to the average end user. Lastly, I'm checking the properties of a ListItem
, which may be part of a Document Library (ListItemCollection
) through which I'm iterating, or which may have been loaded individually by some property (name, Id, URL, etc.)
Checking Latest Version Is Easy
If the latest version of a file happens to be a published major version, it's easy: ListItem.File.Level
will be FileLevel.Published
. So always start by checking that property of an item. If it's something other than Published
, then you have to dig through the older versions to find the latest major/published version (if one exists).
Checking Older Versions Gets Tricky
To see if an older version of the file is the latest major version, you can iterate through either ListItem.Versions
(collection of ListItemVersion
) or Item.File.Versions
(collection of FileVersion
). It's important to note that Item.Versions
includes all versions, including the current version, while Item.File.Versions
includes all file versions except the current version, which is already accessible through Item.File
.
But what properties should you check?
The IsCurrentVersion Fallacy
Most online resources indicate that you can simply check an older version of an item/file to see if FileVersion.IsCurrentVersion == True
or ListItemVersion.IsCurrentVersion == True
, and if so, this is your latest published major version. This is not the case! I have found that IsCurrentVersion
can be True
on more than one minor version of a document.
Before I explain that, I should point out that the current version of a document always has IsCurrentVersion==True
, even if it's a minor version. This is especially important to keep in mind when you choose to iterate through the versions in Item.Versions
, because the latest version (which I assume is not major/published, otherwise you wouldn't be iterating through the versions) will have IsCurrentVersion==True
even though it's not published. If iterating through Item.File.Versions
, this point is moot because the latest version is not included in that collection.
So, other than it being true on the latest version of a file (expected), and being true on the latest Major version of a file (expected), when else can it be true?
CrazyTalk: When a document is checked out, IsCurrentVersion == True on the second-latest version of the file.
Now we have THREE possible versions in an item's version history where IsCurrentVersion
can be true:
- Latest version (minor or major) always has
IsCurrentVersion==True
← (expected) - Latest major version has
IsCurrentVersion==True
← (expected) - 2nd latest version has IsCurrentVersion==True ← (WHAT?)
Even if that second-latest version of the file is minor, or a rejected draft, etc., it will always have IsCurrentVersion==True
. And there may be more scenarios where that property is unexpectedly true. As a result, I've given up on using it to help me find the latest major/published version of a document.
So What's The Solution Already?
I've switched to using the VersionId
property, which is an integer that can tell you a lot about the version you're dealing with. When you're looking at a ListItemVersion
(from ListItem.Versions
), the property is VersionId
. And when you're looking at a FileVersion
(from ListItem.File.Versions
), the property is Id
.
VersionId
uses multiples of 512 for every major version and multiples of 1 for every minor version. The first minor version of a new document will have a VersionId
of 1. Adding two more minor versions will bring the VersionId
up to 3. Publishing the first major version will bring the VersionId
up to 512, and adding one more minor version will bring it up to 513. The second major version will be 1024, and the next minor version will be 10125, and so on. This means there can be 511 minor versions of an item between every major version. The item with the highest VersionId
that is > 0 and a multiple of 512 is the latest major/published version.
Alternatively, instead of the properties ListItemVersion.VersionId
and FileVersion.Id
, you could also look at the properties ListItemVersion.VersionLabel
or FileVersion.VersionLabel
. These are decimal values formatted as <Major>.<Minor>, e.g. 3.12 (Major=3, Minor=12). If the Major version > 0 and the Minor version is 0, it is a major/published version of the file. Finding the highest non-0 Major version where the Minor version is 0 gives you the latest major (published) version of the file.
Because ListItem.Versions
returns versions in descending order, starting with the latest version, I prefer to use this (over ListItem.File.Versions
). Check if each item's VersionId
is > 0 and a multiple of 512 (or VersionLabel
has non-0 Major version with 0 Minor version), and the first one you find matching that criteria is the latest major/published version. You can ignore the first result if you've already checked if ListItem.File.Level
is FileLevel.Published
, because you already know the latest version is not major/published. Alternatively, you could use ListItem.File.Versions
, but because it returns versions in ascending order, it's a little less performant; you would have to iterate through all versions to know which is the highest.