Monday, March 9, 2009

Web Part Audience Targeting RoleProvider-roles through SharePoint Groups

I've managed to spend some days to understand the Audience Targeting functionality of SharePoint Server 2007 (MOSS). I wish to share my conclusions:

* Global Audiences (rule-based, SSP) is OOTB only possible when using SharePoint UserProfiles. I wanted to bind it to an ASP.NET Profile Provider, but that's not possible.

* The Global Audiences (rule-based) functionality is realized directly in-database via stored-procedures.

* It is possible to target (changing visibility) Web Parts to SharePoint Groups.

* If you target a Web Part to a "ASP.NET RoleProvider"-role by adding the role to a Sharepoint Group does not work. (Forms Based Authentication)

* It is possible to override the "TargetTo"-FieldType by replacing the 12\Template\xml\fldtypes_TargetTo.xml file with your own version. This way you are able to hook your own FieldControl into the Audience-functionality.

* Most code which is related to Targeting is hardwired to use "Microsoft.Office.Server.Audience.AudienceManager". It is thus not easy replaceable or extendable

* The ContentByQuery-WebPart is hardwired to use field with ID "{61cbb965-1e04-4273-b658-eedaa662f48d}" as Audience-field.

* The AuthorizationFilter.Editor of Web Parts is buggy. When removing Sharepoint Groups as Target, many strange bugs become apparent.

* It is not possible to manually AudienceFilter the ContentByQuery-WebPart by selecting the "Audience"-field as filter. It gave me an error because it was trying to compare an ntext- with an nvarchar-column.


I wrote the following class to be able to correctly use RoleProvider-roles as Sharepoint-groups for targeting Web Parts. You should register the class as ResourceFilter in the web.config of your SharePoint-site.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
 
using Microsoft.Office.Server.Audience;
using System.Collections;
using System.Globalization;
using Microsoft.SharePoint.WebPartPages;
using Microsoft.SharePoint;
 
namespace AudienceManagerUI
{
    /// <summary>
    /// Replace <RuntimeFilter Assembly="Microsoft.Office.Server, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" Class="Microsoft.Office.Server.Audience.AudienceManager" BuilderURL="audience_chooser.aspx" />
    /// With <RuntimeFilter Assembly="AudienceManagerUI, Version=1.0.0.0, Culture=neutral, PublicKeyToken=6cb89fff80058439" Class="AudienceManagerUI.PatchedResourceManager" BuilderURL="audience_chooser.aspx" />
    /// </summary>
    public class PatchedResourceManager : Microsoft.SharePoint.WebPartPages.IRuntimeFilter2
    {
        private Microsoft.Office.Server.Audience.AudienceManager audienceManager = new Microsoft.Office.Server.Audience.AudienceManager();
 
        public ArrayList InitializeStrings(CultureInfo cultureInfO)
        {
            return audienceManager.InitializeStrings(cultureInfO);
        }
 
        public IToolPaneControl GetToolPaneControl()
        {
            return audienceManager.GetToolPaneControl();
        }
 
        public bool IsFilterActive()
        {
            return audienceManager.IsFilterActive();
        }
 
        /// <summary>
        /// Checks that the values in the IsIncludedFilter property are valid before they are saved
        /// </summary>
        /// <param name="PersistedString"></param>
        /// <returns></returns>
        public string ValidateIsIncludedFilter(string PersistedString)
        {
            return audienceManager.ValidateIsIncludedFilter(PersistedString);
        }
 
        public bool CheckRuntimeRender(string isIncludedFilter)
        {
            bool result = audienceManager.CheckRuntimeRender(isIncludedFilter);
            if (result) return result;
            string[] targets = isIncludedFilter.Split(new string[] {";;" }, StringSplitOptions.None);
            if (targets.Length == 3)
            {
                string[] groups = targets[2].Split(new char[] {';'}, StringSplitOptions.RemoveEmptyEntries);
                foreach (string target in groups)
                {
                    try
                    {
                        SPGroup group = SPContext.Current.Site.RootWeb.SiteGroups[target];
                        if (group.ContainsCurrentUser) return true;
                    }
                    catch (SPException)
                    {
                    }
                }
            }
            return false;
        }
    }
}

No comments:

Post a Comment