Pages

Tuesday, July 27, 2010

Working with Versions in SharePoint List programmatically

There are very few posts on working with Versions in SharePoint Lists Programmatically. While browsing through those posts I ran in to a question saying "How to get the Columns which changed in the SharePoint List?"

Problem Description:

How to get only the Updated Columns in a SharePoint List with respect to Versions? or maybe get the columns changed from the previous version with respect to the current Version?

Solution:

ArrayList alUpdatedFields = new ArrayList();

bool bItemChanged = false;

SPListItemCollection objItemColl = objList.GetItems(objQuery);

SPListItem objItem = objItemColl[0];

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 it with the actually needed version

Below 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;
}

*Note: I have executed the code for the first item in the List, instead we can have it as a loop for multiple items.

Monday, July 26, 2010

Resolved: Grouping Issues in SharePoint Data View WebPart - Part2

SharePoint Data View WebPart grouping and Filtering:

In continuation to our previous blog l'll further demonstrate on how to add a message under each group if no records are found: something of this sort "No records found for this Group"

Issue:

If we use the default filtering available in the SharePoint Data View WebPart or the XSLT filtering we cannot display this message under each group which has no records satisfying the criteria provided. Say for example under Group1 we have 3 SubGroups and under Group2 we have 5 SubGroups When the parameters passed are say "Group1", "Group2", "SubGroup4" and "SubGroup5" then we get the records displayed for "Group2" and nothing would displayed for "Group1" not even the header. Also as per our example in the previous Post (parameters passed as above) we get the Group Header (Group1) but nothing under it. Please refer the image below:


Solution:

  1. Find the Group Header Template (dvt_1.groupheader0) in this at the end of the "tr" tag add another "tr" tag with ID as "trNoRecords"
  2. Inside that "tr" add the required text: for example "No records found for this Group"
  3. Now lets add some JavaScript Code to get the desired results: I have clubbed the whole JavaScript in to one function that is from Previous Blog (Green) and the Current Blog (Red)
_spBodyOnLoadFunctionNames.push("HideGroup");
function HideGroup()
{
var vTable = document.getElementById('tblRows');
var vTRelement = vTable.getElementsByTagName("TR");
for(i=0;i < vTRelement.length;i++)
{
if(vTRelement[i].id == "group1")

{
var vNextSibling = vTRelement[i].nextSibling;
if(vNextSibling != null)
{

if(vNextSibling.id != "displayRows")
{
vTRelement[i].style.display = "none";
}
}
else
{

vTRelement[i].style.display = "none";
}
}

else if(vTRelement[i].id == "trNoRecords")
{
var vNextSibling = vTRelement[i].nextSibling;

var vCount = 0;
while(vNextSibling.id != "group0")
{
if(vNextSibling != null)
{
if(vNextSibling.id == "displayRows")
{
vCount++;
break;
}
}

vNextSibling = vNextSibling.nextSibling;
if(vNextSibling == null)
{
break;
}
}
if(vCount>0)
{
vTRelement[i].style.display = "none";
}

}

}

}

Once this is added we can see the desired result as shown in the below figure:


Monday, July 19, 2010

Resolved: Grouping Issues in SharePoint Data View WebPart - Part1

SharePoint Data View WebPart grouping and Filtering:

Not always we can use the filtering conditions in a Data View WebPart, say for example we have few parameters (Group, SubGroup, level etc..) passed in Query String and based on these parameters we need to filter our Data View WebPart.

One of the Parameters (say SubGroup) may be sent as "All", instead of selecting individual items. In this particular scenario we cannot use the default Filters available in Data View WebPart, hence we go for individual Row verification against these parameters. Till this point everything works fine and looks great. Lets now discuss about the problem:

Issue:

If we set grouping to the results we see that the data is filtered properly according to our requirement but then , we can also all other Group Headers, which are does not meet the criteria (though there will not be any data available under these headers). Please refer the screenshot below:


Solution:

The first possible solution anyone can think of is to add the same filtering condition where the Group header (in XSLT) is being added and I did the same too and found that it resolved the issue to some extent. It would remove the main Group Headers but the second level grouping (if exists would still be there)Please refer below screenshot for the resultant Data after the modifications.


But applying the same filter to the next Grouping level wouldn't work instead it would add additional issues, I would not go in to its details. So I opted to get it done with JavaScript:

Steps to be followed:
  1. Find the Row where the actual data is being rendering by default in the dvt_1.rowview template.
  2. For its TR add an ID attribute and assign its value to "displayRows"
  3. Add the below script to you page:
_spBodyOnLoadFunctionNames.push("HideGroup");
function HideGroup()
{
var vTable = document.getElementById('tblRows');
var vTRelement = vTable.getElementsByTagName("TR");
for(i=0;i <vTRelement.length;i++)
{
if(vTRelement[i].id == "group1")
{
var vNextSibling = vTRelement[i].nextSibling;
if(vNextSibling != null)
{
if(vNextSibling.id != "displayRows")
{
vTRelement[i].style.display = "none";
}
}
else
{
vTRelement[i].style.display = "none";
}
}
}
}

This would hide all the Second Level Groups which does not have data under its category*. Now this may look like the issue is resolved. Consider say under Main Group (Group1) we have three Sub Groups (SubGroup1, SubGroup2, SubGroup3). This Group1 may have data for SubGroup1, SubGroup3 but not for SubGroup2. In this case SubGroup2 would also be displayed with no data under its section based on the filtering Condition. With the above the SubGroup2 would be hidden and cannot be seen. Here comes one more issue, that is while using the expand collapse button the hidden TR that is "SubGroup2" would be visible again (Refer below Image: "Before").

Find the Expand Collapse Image for Group Header (that is the main Group) which contains an OnClick function: onclick="javascript:ExpGroupBy(this);return false;" change this to onclick="javascript:ExpGroupBy(this);HideGroup();return false;" and now we are all set to see the expected results(Refer below Image: "After")

--> Before --> After

More on this in my Next Blog

*Note: Since its the Second level group its value is "group1" as rendered by the browser, change it to appropriate value wherever necessary (first level "group0", third level "group2" etc...)