Monday, March 22, 2010

Feature * for list template * is not installed in this farm error

So I got this error few times now I thought it is about time to post on it.

I always fall for the same "trap", I get this error when trying to access the list.Forms collection, which is useful for getting the display, edit or new form of a list or library.

But - if some one happened to delete, move or rename one of the default forms of your list - you will get this exception trying to get to this collection:
"Feature * for list template * is not installed in this farm error"

So, keep that in mind and call this collection in a try catch block, and feel free to display this error message in the "catch" block:
"Don't delete built-in list forms!"

What you should do it add new custom forms and redirect to them - but never touch the default ones in SharePoint Designer!

Good luck to you guys,

Thursday, March 11, 2010

How to get SharePoint KPI value in C#

It's been a while since I had anything smart to say, and then I had to go and play with KPIs...

KPIs in SharePoint 2007 are pretty cool, they allow you to work in several ways basically: Either you enter the KPI info manually, or you connect it to some sort of a data source like a list, excel file or other providers.

in the end you find your self with a nice list item that has some KPI fields to hold the goal, warning and value levels (in C#: double).

the problem is that when you try to access these fields in code you find that only the manual KPI actually holds the information for you to display:

value = (double)item[list.Fields.GetFieldByInternalName(MobileConstants.KPIHelper.Field_Value).Id];
goal = (double)item[list.Fields.GetFieldByInternalName(MobileConstants.KPIHelper.Field_Goal).Id];
warning = (double)item[list.Fields.GetFieldByInternalName(MobileConstants.KPIHelper.Field_Warning).Id];

Meaning, if you have a KPI that loads from a SharePoint list or an excel file... don't hope to get anything from these fields...

Well, after hours of diggin into SharePoint dll's, I have found the solution finally.

It appears that the entire KPI API ( :) ) is either internal or private to these DLL's, which means of course - reflection!

So I dug in and found some cool examples in MS code that loads KPIs and display them, which in turn exposed some key classes and methods that would save the day.

First one - is the KpiFactory. This guy will in turn get us a Kpi object based on a KPI list item (yes, simple SPList.Item object).

Once we got the Kpi - it is safe to call GetKpiData with no parametes to get another object named KpiData, which finally holds a Value, Goal and Warning properties!

So the final code should look like so:
Assembly asm = System.Reflection.Assembly.Load("Microsoft.SharePoint.Portal, Version=, Culture=neutral, PublicKeyToken=71e9bce111e9429c");
Type t = asm.GetType("Microsoft.SharePoint.Portal.WebControls.KpiFactory");
MethodInfo mi = t.GetMethod("GetKpi", BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static, null, new Type[] { item.GetType() }, null);
object kpi = mi.Invoke(null, new object[] { item });
mi = kpi.GetType().GetMethod("GetKpiData", BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance, null, new Type[] { }, null);
object kpidata = mi.Invoke(kpi, new object[] { });

value = (double)ReflectionUtility.GetPropertyValue(kpidata, "Value");//double
warning = (double)ReflectionUtility.GetPropertyValue(kpidata, "Warning");//double
goal = (double)ReflectionUtility.GetPropertyValue(kpidata, "Goal");//double

Of course there are some unclear parameters that have to do with filter and other things, I will update as soon as i figure out what they are for... no idea for now - but at least our mobile solution will have nice KPI's to show after all!