Friday, April 23, 2010

SharePoint Feature List Page

In some cases, once the site is created and properly configured, system admins don't want end users, not even site collection administrators, to touch the SharePoint Features for security and other reasons. We built a custom SharePoint solution with custom site definition. System admin requested to set all our custom Features to be hidden so that those Features won't be able to be activated or deactivated by users. Admins themselves like to use stsadm or some other tools to control those features.

All set and everything is okay, except you can not see those Feature status in site settings' pages. So I created a Feature List application page specific for this:



Code:
<%@ Page Language="C#" Inherits="Microsoft.SharePoint.WebControls.LayoutsPageBase" %>

<%@ Assembly Name="System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" %>
<%@ Assembly Name="System.Web, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" %>
<%@ Assembly Name="Microsoft.SharePoint, Version=12.0.0.0, Culture=neutral,PublicKeyToken=71e9bce111e9429c" %>
<%@ Assembly Name="Microsoft.SharePoint.ApplicationPages, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c"%>

<%@ Import Namespace="System.Web.UI" %>
<%@ Import Namespace="System.Collections.Generic" %>
<%@ Import Namespace="System.Collections.Specialized" %>
<%@ Import Namespace="Microsoft.SharePoint" %>
<%@ Import Namespace="Microsoft.SharePoint.Utilities" %>
<%@ Import Namespace="Microsoft.SharePoint.Administration" %>

<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>SharePoint Feature List</title>
<meta name="ROBOTS" content="NOINDEX, NOFOLLOW">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<meta http-equiv="Content-language" content="en">

<script runat="server">
public class FeatureItem
{
private string _name, _scope, _id;
private bool _hidden;
public string Name { get { return _name; } set { _name = value; } }
public string Scope { get { return _scope; } set { _scope = value; } }
public string ID { get { return _id; } set { _id = value; } }
public bool Hidden { get { return _hidden; } set { _hidden = value; } }
public FeatureItem(string name, string scope, string id, bool hidden)
{
Name = name;
Scope = scope;
ID = id;
Hidden = hidden;
}
}

protected override bool AllowNullWeb { get { return false; } }
protected override bool RequireSiteAdministrator { get { return false; } }

void Page_Load(object sender, System.EventArgs e)
{
if (SPContext.Current == null || SPContext.Current.Site == null || SPContext.Current.Web == null)
{
lblInfo.Text = "Invalid SPContext. Please sign in and try again.";
return;
}

PopulateFeatures();
}

void PopulateFeatures()
{
List<FeatureItem> featureList = new List<FeatureItem>();
string scope = ddlScope.SelectedValue;

Dictionary<string, SPFeatureCollection> activeFeatures = new Dictionary<string, SPFeatureCollection>();
activeFeatures.Add(SPFeatureScope.Farm.ToString(), Microsoft.SharePoint.Administration.SPWebService.ContentService.Features);
activeFeatures.Add(SPFeatureScope.WebApplication.ToString(), SPContext.Current.Site.WebApplication.Features);
activeFeatures.Add(SPFeatureScope.Site.ToString(), SPContext.Current.SiteFeatures);
activeFeatures.Add(SPFeatureScope.Web.ToString(), SPContext.Current.WebFeatures);

if (ddlActive.SelectedValue == "Active")
{
if (scope == "All")
{
foreach (SPFeatureCollection features in activeFeatures.Values)
{
foreach (SPFeature item in features)
{
try
{
string name = string.IsNullOrEmpty(item.Definition.DisplayName) ? item.Definition.Id.ToString() : item.Definition.DisplayName;
FeatureItem fi = new FeatureItem(name,
item.Definition.Scope.ToString(), item.Definition.Id.ToString(), item.Definition.Hidden);
featureList.Add(fi);
}
catch (Exception ex)
{
string name = string.IsNullOrEmpty(item.Definition.DisplayName) ? item.Definition.Id.ToString() : item.Definition.DisplayName;
FeatureItem fi = new FeatureItem(ex.Message, string.Empty, string.Empty, false);
featureList.Add(fi);
}

}
}
}
else
{
SPFeatureCollection features = activeFeatures[scope];
foreach (SPFeature item in features)
{
try
{
string name = string.IsNullOrEmpty(item.Definition.DisplayName) ? item.Definition.Id.ToString() : item.Definition.DisplayName;
FeatureItem fi = new FeatureItem(name,
item.Definition.Scope.ToString(), item.Definition.Id.ToString(), item.Definition.Hidden);
featureList.Add(fi);
}
catch (Exception ex)
{
string name = string.IsNullOrEmpty(item.Definition.DisplayName) ? item.Definition.Id.ToString() : item.Definition.DisplayName;
FeatureItem fi = new FeatureItem(ex.Message, string.Empty, string.Empty, false);
featureList.Add(fi);
}
}
}
}
else //Inavtive
{
foreach (SPFeatureDefinition definition in SPFarm.Local.FeatureDefinitions)
{
Guid featureID = Guid.NewGuid();
string name = string.Empty;
try
{
featureID = definition.Id;
name = string.IsNullOrEmpty(definition.DisplayName) ? featureID.ToString() : definition.DisplayName;
bool isActive = false;
if (activeFeatures[definition.Scope.ToString()] != null)
isActive = (activeFeatures[definition.Scope.ToString()][featureID] != null);

if (!isActive && (scope == "All" || definition.Scope.ToString() == scope))
{
FeatureItem fi = new FeatureItem(name, definition.Scope.ToString(), definition.Id.ToString(), definition.Hidden);
featureList.Add(fi);
}
}
catch (Exception e)
{
FeatureItem fi = new FeatureItem(name + " : " + e.Message, "Invalid", featureID.ToString(), true);
featureList.Add(fi);
}
}
}

featureList.Sort(delegate(FeatureItem item1, FeatureItem item2)
{
return item1.Name.CompareTo(item2.Name);
});
lblTotal.Text = string.Format("Total count: {0}", featureList.Count);

gvFeature.DataSource = featureList;
gvFeature.DataBind();
}
</script>

</head>
<body>
<form id="form1" runat="server">
<div style="margin: 15">
<p>
<asp:Label ID="Label2" runat="server" Text="Feature Status:"></asp:Label>
<asp:DropDownList ID="ddlActive" runat="server" AutoPostBack="true">
<asp:ListItem Selected="True">Active</asp:ListItem>
<asp:ListItem>Inactive</asp:ListItem>
</asp:DropDownList>&nbsp;
<asp:Label ID="Label3" runat="server" Text="Feature Scope:"></asp:Label>
<asp:DropDownList ID="ddlScope" runat="server" AutoPostBack="true">
<asp:ListItem>All</asp:ListItem>
<asp:ListItem>Farm</asp:ListItem>
<asp:ListItem>WebApplication</asp:ListItem>
<asp:ListItem>Site</asp:ListItem>
<asp:ListItem Selected="True">Web</asp:ListItem>
</asp:DropDownList>&nbsp;
<asp:Label ID="lblTotal" runat="server"></asp:Label>
</p>
<p>
<asp:Label ID="lblInfo" runat="server"></asp:Label>
<asp:GridView ID="gvFeature" runat="server" AutoGenerateColumns="False" BorderWidth="1px"
BackColor="White" CellPadding="4" BorderStyle="Solid" BorderColor="#3366CC"
Font-Size="Small" AlternatingRowStyle-ForeColor="ActiveCaption">
<HeaderStyle ForeColor="White" BackColor="#003399" HorizontalAlign="Left">
</HeaderStyle>
<Columns>
<asp:BoundField HeaderText="Feature Name" DataField="Name" ReadOnly="true" ItemStyle-Width="380px">
</asp:BoundField>
<asp:BoundField HeaderText="Scope" DataField="Scope" ReadOnly="true" ItemStyle-Width="120px">
</asp:BoundField>
<asp:BoundField HeaderText="Hidden" DataField="Hidden" ReadOnly="true" ItemStyle-Width="120px">
</asp:BoundField>
<asp:BoundField HeaderText="FeatureID" DataField="ID" ReadOnly="true" ItemStyle-Font-Size="X-Small" ItemStyle-Width="240px">
</asp:BoundField>
</Columns>
<EmptyDataTemplate>
<asp:Label ID="lblEmptyMessage" runat="server" Text="No feature found."></asp:Label>
</EmptyDataTemplate>
</asp:GridView>
</p>
</div>
</form>
</body>
</html>