How to Use Audience Targeting to Filter List Items in SharePoint 2010


In a Building an Accordion with jQuery and SharePoint 2010 | dig sharepoint, I showed you how to build an accordion style menu using SharePoint 2010 and jQuery. I recently had a request to expand on this idea by personalizing the items shown based on which SharePoint Group users belonged to. What came to mind was to use Audience Targeting.
Here is another custom webpart I built that shows items from a SharePoint list, and using an ASP.Net Repeater control, I display them in a specified order:

I was approached with a challenge of only showing items from the list for certain users. At first I thought I could have used item level permissions, but then I thought using Audience Targeting would be a better option. When researching Audience Targeting, most searches I found involved setting up the User Profile Service and then using a Content Query Webpart to filter who sees the data. In my case, I didn’t need a CQWP as I already had a custom webpart to display list content. Initially I thought that by removing the “RunWithElevatedPrivileges” context when retrieving list data would filter list items in a list/library view by the Target Audience specified in that column. However, this did not produce the desired outcome, as the item priviledges are based on item level permissions.
Here is what I ended up doing to get Audience Targeting to work for me:
The first step is to setup a method to retrieve list data. The following shows a standard way of getting all list data without regard to item level permissions:


public static DataTable GetListData(string listName, string xmlQuery)
{
 
    DataTable dt = new DataTable();
    DataTable dtNew = new DataTable();
    SPSite site = SPContext.Current.Site;
    SPWeb web = SPContext.Current.Web;
 
    SPSecurity.RunWithElevatedPrivileges(delegate()
    {
        using (SPSite ElevatedSite = new SPSite(site.ID))
        {
            using (SPWeb ElevatedWeb = ElevatedSite.OpenWeb(web.ID))
            {
                SPList theList = ElevatedWeb.Lists[listName];
 
                SPQuery query = new SPQuery();
                query.Query = xmlQuery;
 
                SPListItemCollection items = theList.GetItems(query);
                dt = items.GetDataTable();
 
                // Factor in those items for specific audiences
                dtNew = GetListDataIncludingTargetAudience(dt);
            }
        }
    });
 
    return dtNew;
}
Next you’ll want to create a SharePoint Group that will hold all the users that will see the additional list items:

Add users to the group. I usually have a test Active Directory account, so that I can see the affects of logging into the site as myself or as the test user.
The next step involves setting up your list with Audience Targeting:
  • Site Actions –> View All Site Content –> <List Name>
  • List Settings –> Audience Targeting Settings
  • Enable audience targeting
Add the new column to the default list view:
  • List Settings –> <View> –> <Click to Modify>
  • Add the new “Target Audiences” to the current view.
Now when you go back to view your list, you will see a new column called “Target Audiences”:
Now that your list is setup, whenever you edit a list item, you can add the SharePoint Group to the “Target Audiences” field using the People Picker:

  • Click on the ‘Browse’ icon, search for your new SharePoint Group, and click ‘OK’.
  • Click ‘Save’ on the item, and you should now see it listed in the “Target Audience” column for this item.
Now that you have your list setup and a method that retrieves list data, we need a way to factor in those users that need to see the additional item. I created this new method:


public static DataTable GetListDataIncludingTargetAudience(DataTable dt)
{
    // Perform audience targeting on list items:  http://msdn.microsoft.com/en-us/library/ms563693.aspx
    // Fill dtNew with Audience specific list items
    AudienceLoader audienceLoader = AudienceLoader.GetAudienceLoader();
    DataTable dtNew = new DataTable();
    dtNew = dt.Clone();
 
    for (int z = 0; z <= dt.Rows.Count - 1; z++)
    {
        // Check to see if this is a valid column
        if (dt.Columns.Contains("Target_x0020_Audiences"))
        {
            string audienceFieldValue = dt.Rows[z]["Target_x0020_Audiences"].ToString();  // (string)listItem[k_AudienceColumn];
 
            // If the value is empy, then assume it is for every audience
            if (audienceFieldValue == string.Empty)
                dtNew.ImportRow(dt.Rows[z]);
            else
            {
                // quickly check if the user belongs to any of those roles.
                // Add the data row if user is a member of the audience targeted item.
                if (AudienceManager.IsCurrentUserInAudienceOf(audienceLoader, audienceFieldValue, false))
                    dtNew.ImportRow(dt.Rows[z]);
            }
        }
        else
        {
            // Add the item if audience targeted column does not exist
            dtNew.ImportRow(dt.Rows[z]);
        }
    }
 
    return dtNew;
}
As you can see in the code sample at the beginning of the post, the method “GetListData” makes a call to the method “GetListDataIncludingTargetAudience”, passing the resulting DataTable of content from the “GetListData” method. This new method loops through the rows of the DataTable passed in and does 3 things:
  1. It first looks to see if there is a column called “Target_x0020_Audiences”.
  2. If it does not exist, then we want to continue on and still import the row into a new DataTable. By doing it this way, we won’t be filtering out data…we will be filtering “in” data if the column exists.
  3. If the column exists, it then performs a check to see if the currently logged in user is a part of the SharePoint Group found for the particular DataTable Row. It does this using the AudienceManager class. With this class you can retrieve individual types, lists of audiences, and lists of users who are associated with an audience. If the user a part of the Audience, this item will be imported into a new DataTable. If the user does not belong to the Audience, it will not be imported.
The goal with this method is not to exclude data for users where the Target Audience column is blank. I actually want to include all data but simply include more data if the Target Audience has been specified. This way it is scalable to any list that does or does not have the Target Audience feature enabled.
I found that using the Target Audience feature on a list is a great built-in feature to SharePoint 2010. Using it to include more data for a specific group adds a little personalization that can be easily implemented. Please test it out for yourself and let me know how it works out for you!