Thursday, September 18, 2008

Working with versions in List programmatically

Hi All,

Our SharepointKings team really wanted to share this because when we needed this kind of facility we did not find many articles on this topic. so we thought to dig in this and find out something.

Ok, let's get to the business straight forward. The requirement was to have History for the List. Yes, a history for the list.

So bottom line is working with versions in List programmatically.

So we thought that there is a default facility in Document Library for Version and it is very famous for versions, but we actually never came across list history. so we decided to look on the List Settings and yes we were right, we found that there is an option to have version for the List also.

To Enable the Versions, Go to List Settings and go to Version Settings and then Select Create a version each time you edit an item in this list? to true. there you go, you have just made versoning option on.

Now, Try to edit any item from that list and then Select the ECB of that Item, see you will find Version History option, Click on that and you will see all changes that you have just performed. So this is very nice for the End User.

But, What about the Developer if they have to do it by code and display the result of any previous version????
So this article focuses on this.

Let me give you a brief introduction on what all classes we will require to get this thing done.

Let's consider a scenario where you need to check all versions of all ListItems in the List.

First you take an object of the list in which you need to find our the versions of ListItems.

Considering that i am writing code in Web Part, change the code accordingly (If Needed).

SPList objList = SPContext.Current.Web.Lists["{your_list}"];

SPQuery objQuery = new SPQuery();

objQuery.Query = "<Where><Gt><FieldRef Name=\"ID\"/><Value Type=\"Counter\">0</Value></Gt></Where>";

SPListItemCollection objItemColl = objList.GetItems(objQuery);


Note that update your code accordingly in single line. here code is written in multiple lines only for readability.

Here what we have done is we got all ListItems so far for the list.

Now, we will proceed in getting the versions of each ListItem.

foreach (SPListItem objItem in objItemColl)
{

SPListItemVersionCollection objVerisionColl = objItem.Versions;

//Above Line gets all versions of the ListItem. If it returns 1, that means nothing is modified. no version //for this item.

if (objVerisionColl.Count > 1)
{
//Navigate to each version of the ListItem
foreach (SPListItemVersion objVersion in objVerisionColl)
{
int versionID = objVersion.VersionId;

//Returns incremental IDs starting from 512,1024,1536... for each version

DateTime timeofcreation = objVersion.Created;

//VersionLabel - Gets 1.0,2.0,3.0 as default versions for each iteration

string strVersionLabel = objVersion.VersionLabel;

SPListItem objLstItm = objVersion.ListItem;

}
}
}


This means If the ListItem is modified for 3 times, the above loop for that ListItem will execute 3 times and you will get the VersionId as 512,1024,1536 Likewise. VersionLabel as 3.0, 2.0, 1.0. It returns by default from latest to oldest.

So This is the way to get all versions of the ListItem. So now the question is how to get the Data of old versions.Check out the last line in the loop which actually gets the SpListItem of that Version.

So first get the SPListItem that you want and then check for all versions just like the above code and use simple technique to retrieve the data. Consider i have three columns. FirstName, LastName and Address in the List.

So all i have to do is, once i get the Version that i want to fetch take its SPListItem object and then access its fields,

string strFirstName = Convert.ToString(objLstItm["FirstName"]);
string strLastName = Convert.ToString(objLstItm["LastName"]);
string strAddress = Convert.ToString(objLstItm["Address"]);


Now you have the old version data on your hand. Play with it on your own way!!!!

28 comments:

Anonymous said...

Get Version Data
string strFirstName = Convert.ToString(objLstItm.Version[{VersionCount}]["FirstName"]);

Anonymous said...

string strFirstName = Convert.ToString(objLstItm.Version[{VersionCount}]["FirstName"]);

K.D.Kadyan said...

Thank u so much. Personally i had seen any blog on verions data of list item.

It was a great help.

Kuldeep Kadyan

Blake said...

While looping through items and versions like you are, only the most recent versions field values are being returned for me.

Is there a setting that needs to be set to allow you to programatically get previous versions field values?

SharePoint Kings said...

Blake,

you can also get any previous version from mentioned method or below method.

string strValue = listItem.Versions.GetVersionFromLabel("Version Lable")["FielName"].ToString();


OR

string strValue = listItem.Versions[1]["FieldName"].ToString();
//use 1 for previous version

Anonymous said...

Can I use the same piece of code to get previous versions for a document in a document library.

SharePoint Kings said...

sure, you can use it for doc lib also

doc lib is also inherited by base class of list.

Anonymous said...

Excellent writeup!

Anonymous said...

Excellent writeup!

JD said...

Is there a way to Reject a previous version of a document ?

There is only one method called Deny which I assume will Reject the document itself.

However, I would like to Reject a specific version of a document.

Is this possible?

SharePoint Kings said...

JD,

actually how deny will behave to your document. whether it is same as reject or not? and specifically rejecting particular version is more confusing? you can try a custom workflow if applicable but again don't make it so complected to manage.

JD said...

The SharePoint UI does allow one to Reject a previous approved version of a document.

So there has to be a mechanism that will allow one to programmatically Reject a version of a file as well (at least I am hoping hard).

I do not have the luxury of a workflow as this is going to be a one time activity.

If SharePoint allows to Reject a version from the UI, then why cannot we do it through code?

Deny is the closest method that I could find to Reject functionality. But unfortunately it Rejects the latest version of a document and not the version one that you want .. :(

Any thoughts .....

SharePoint Kings said...

JD,

we tried your scenario with below steps

1) create doc lib

2) doc lib setting --> version setting --> Require content approval for submitted items? == yes

3) doc lib setting --> version setting --> Create a version each time you edit a file in this document library? == Create major versions

other options are not relevant.

now

Upload a document
approve it
change it/upload another version
approve it
change it/upload another version

so current version is 3.0
go to version history of document

if you check ECB menu of latest version there are only 3 option
a. view
b. restore
c. reject this version

and if you check ECB menu of the previous version there are also 3 option
a. view
b. restore
c. delete

now please let me know how you able to reject previous version from front end.

Anonymous said...

Being a newbie at this i have two questions:

What Webpart are you writing this code into?
How are you displaying this information once you have collected it?

Thanks,
RD

SharePoint Kings said...

RD,

You can use this code depend upon your requirement.

like may be in win application for admin kind of stuff or may be on webpart where you can show user the previous version depend upon some business logic.

Mai Omar said...

dear all;
thanks for the post, it's great but
i tried this

Get Version Data
string strFirstName = Convert.ToString(objLstItm.Version[{VersionCount}]["FirstName"]);

i get all versions but for specific column which hard coded as string but i have lots of columns in my list, i don't know which changed, i want to get the changed column only with it's versions..

here we r able to get versions but only for certain column,

how can it be without specifing column in code and get it dynamic?

like for example, title changed in version 1 and then e-mail column changed in versioin 2?

i get only changes and not all columns because in version 1, e-mail column didn't change

i hope u got me, i need also this fast.. u hope u can help me


Thanks.
Mai Omar
Senior Solutions Developer
ITEgyptCorp
Website: http://www.itegyptcorp.com
Blog: http://maiomar.itegyptcorp.com

SharePoint Kings said...

Omer,

we got your question.

you want previous version of selected column like not the list item old version
but the old version of the column you want whether it can be different then list item version number.

so we got your question.

its quite genuine but confusing.
we are trying to find your answer and soon try to reply back.

Venkat Karri said...

Its a good post. However, I have few questions:

SPListItem objLstItm = objVersion.ListItem;

I don't think this would give the values corresponding to that particular version instead, it gives item values of the most latest Version.

Omar,

Are you looking for something which allows you to capture the columns names whose values have changed? Do you want to compare between any two versions? or compare with the latest version if anything has changed? If you can provide me with these details then I'll try to post the code for your query, provided if you are still waiting for a solution.

-Venkat

SharePoint Kings said...

Hay Venkat,
Thanks. it would be great if you post related to Omar's problem.

Venkat Karri said...

bool bItemChanged = false;
ArrayList alUpdatedFields = new ArrayList();

SPListItemVersionCollection objVersionColl = objItem.Versions;

if (objVersionColl != null && objVersionColl.Count > 0)
{
foreach (SPListItemVersion item in objVersionColl)
{
if (item.VersionLabel.ToString() != objItem["_UIVersionString"].ToString())
{
foreach (SPField objField in objItem.Fields)
{
if (!objField.ReadOnlyField && IsValueChanged(objField.Type, objItem[objField.InternalName], item[objField.InternalName]))
{
bItemChanged = true; alUpdatedFields.Add(objField.Title);

}
}
if (bItemChanged)
{ break;
}
}
}
}

In the above code I am looking for the previously modified version compared to the current version and adding those fields to the Array List, instead we can have an HashTable to add the Field and its value as a key value pair.

if (item.VersionLabel.ToString() != objItem["_UIVersionString"].ToString()) //This condition is used to ignore the comparison of the current version with itself. you can add your own logic to compare with the actually needed version

Here is the function to check if the value for a particular field has changed compared to other version:

private bool IsValueChanged(SPFieldType type, object FirstValue, object SecondValue)
{
if (string.IsNullOrEmpty(Convert.ToString(FirstValue)) && string.IsNullOrEmpty(Convert.ToString(SecondValue)))
{
return false;
}
else if (string.IsNullOrEmpty(Convert.ToString(FirstValue)))
{
return true;
}
else if (string.IsNullOrEmpty(Convert.ToString(SecondValue)))
{
return true;
}

switch (type)
{
case SPFieldType.DateTime:
return !Convert.ToDateTime(FirstValue).Date.Equals(Convert.ToDateTime(Convert.ToString(SecondValue)).Date);
case SPFieldType.User:
break;
case SPFieldType.Text:
case SPFieldType.Note:
return !Convert.ToString(FirstValue).ToUpper().Equals(Convert.ToString(SecondValue).ToUpper());
case SPFieldType.Boolean:
return !Convert.ToBoolean(FirstValue).Equals(Convert.ToBoolean(SecondValue));
case SPFieldType.Attachments:
break;
default:
return !FirstValue.Equals(SecondValue);
}

return false;
}

Hope this helps:

Regards,
Venkat

http://tips-in-sharepoint.blogspot.com/
http://tipsinsharepoint.wordpress.com/

Venkat Karri said...

Also, you can refer to my blog for this solution and also let me know if you were looking for something else:


http://tips-in-sharepoint.blogspot.com/2010/07/working-with-versions-in-sharepoint.html

http://tipsinsharepoint.wordpress.com/2010/07/28/working-with-versions-in-sharepoint-list-programmatically/


Regards,
Venkat

SharePoint Kings said...

Venkat,

this is great help.

Omar and other user should look at this.

thanks Venkat

Thanks,
SharepointKings Team

Anonymous said...

How can I update the data of old version ?

SharePoint Kings said...

Anonymous,

its great question...if you can change the old data then what it means to save old data and if history is editable then its not called history, right?

if you change the data it will create new version so rather then changing old it create another one.

still you can try changing it by item.systemupdate it might work.

share your experience as we had no idea weather it is possible or not or how is it behave.

Anonymous said...

Great Blog!
One more question about version:
Is it possible make a real roll back? That means, if my current version is version n, I want to set version n-1 as current version and delete the version n. Restore always creates a new version n+1.

Go ahhead with your Blog. It iss aways great help.

SharePoint Kings said...

yes it is possible to rollback.

Anonymous said...

Hi SharePoint Kings,
can you please give me an short example how to do a rollback.
Trying to delete the current version gets me an error that I can't do so (delete the cuurent version). Trying to set version n-1 as the currrent version gets me an error that this field is read only.

SharePoint Kings said...

what we suggest is do not delete current item.

just recover the version you required.




Share your SharePoint Experiences with us...
As good as the SharePointKings is, we want to make it even better. One of our most valuable sources of input for our Blog Posts comes from ever enthusiastic Visitors/Readers. We welcome every Visitor/Reader to contribute their experiences with SharePoint. It may be in the form of a code stub, snippet, any tips and trick or any crazy thing you have tried with SharePoint.
Send your Articles to sharepointkings@gmail.com with your Profile Summary. We will Post them. The idea is to act as a bridge between you Readers!!!

If anyone would like to have their advertisement posted on this blog, please send us the requirement details to sharepointkings@gmail.com