Content Deployment and Audience Targeting in MOSS

Content Deployment and Audience Targeting in sharepoint 2007 (specifically AUDIENCEID the GUID)

We had a requirement (and I am sure most of the folks using content deployment and Audience targeting have this requirement) to manage the Audiences on the Publishing site (target site) after the content deployment is done from the Staging/Authoring site.

1. Staging/Authoring site has Audience set for the webpart and this Audience is defined in the SSP used by the Staging/Authoring Farm and I am sure you all know that the AUDIENCE IDs are stored and not the AUDIENCE Names in the web part's Audience Targeting property. So the GUID stored for the web parts AUDIENCE ID is the GUID from the staging farm.

2. Now you run Content Deployment and the Publishing/Production farm has all the contents from Staging farm. Please note here that the Publising Farm is using a different SSP and hence the GUIDS are not the same for the AUDIENCES in the Publishing farm. Also note that the AudienceID property is readonly and there is no way to set it. So Audiences will always fail or to say Audience Targeting will always fail when you have different SSPs for Staging/Publishing Farm. So do we modify the Publishing Farm to make use of the new Audiences? If we do so will it break the readonly nature of the Publishing farm, if yes that means future content deployments will fail, correct? Well no that is not correct modifying the Audiences on the web part of the Pages will not break the readonly nature of the Publishing site.

Following are some of my findings:

1. There is no way to change the GUIDs of the AUDIENCEID (in any Farm's SSP ).

2. So content deployment will only carry over the OLD GUIDs for web parts/Pages that are targetted in the Publishing/Target site.

3. Hence a utility needs to be created that will export all the Audience information for all the web-parts and Pages in a site/site-collection. This information will only fetch us GUIDs of the Audiences. Now using these Audience GUIDs we find the name of the Audiences (from the Source Farm's SSP), AUDIENCENAME property.

4. Now we go to the Target/Publishing site and run the import utility that will have all the Audience information about all the web-parts and Pages in a site (or the entire site-collection).

5. This will replace the old GUIDs (on the Publishing site's web-part/pages) with the new GUIDs (from the new SSP of Publishing), the mapping will be done on the basis of AUDIENCENAME property (Basically we will have the AudienceName from the old SSP from step-3 and we will have the same AudienceNames created in the Publishing SSP, GUIDs will be different but now we have the Table/mapping that will help us replace the old GUIDs on the publishing/target site with the new GUIDs by matching the name).

So all and all no straight way to replicate the Audiences and also I am not sure if we would break the readonly nature of the Publishing/Target site when we change the AUDIENCE-TARGET property for the web parts on pages. Also a web-part doesn't have a AUDIENCETARGET property, it is the AuthorizationFilter property that is used to set the Audiences for the web-part.

Following are some result of the test that we conducted both for Security modification and Audience modification on the Publishing site:

Test 1: Security

 

1. Changed the Permission on the Publishing/Target site's Page to add a USER (basically I broke the Parent Inheritance for the Page form Pages library) and then ran the content deployment again from Source to Target, this SUCCESSDED. Content deployment changed the contents of the Target Page to match the Source Page and didnt change the Permissions i.e. it retained the change I made to the Target site (i.e. additional user still exists, content deployment didn't change the permissioning).

 

Bottom line, this works the way we want for SECURITY.

 

Test 2: AUDIENCE TARGETING

 

1. Added an Audience (from Publishing SSP) to the web-part's AUDIENCE TARGET property (for e.g. AUD_PUBSSP).

2. Ran content deployment and it didn't fail, it retained the AUDIENCE that was set (thing to note is the source page didn't have any AUDIENCE Target set for the web-part).

3. Then I set the AUDIENCE TARGET property for the web-part on the Source Site's Page (for e.g. AUD_SOURCSSP). Then re-ran the CONTENT DEPLOYMENT.

4. CONTENT Deployment SUCCEEDED, but the AUDIENCE on the TARGET site was set to the GUID of the SOURCE SSP.

5. I removed the old GUID from the Publishing/Target site and set it to the Audience of the Publishing Farm SSP (for e.g. AUD_PUBSSP).

6. Re-ran the CONTENT DEPLOYMENT, CD again SUCCEEDED no issues at all and Audience ID were the new one ( Please note that the content that contained the audiences was not changed since the previous deployment, and hence the Audiences were not overwritten but when I changed the content fields the AUDIECNE IDs for the web parts on that content page were set back to the OLD AUDIENCE GUIDs(Source/Authoring SSP Audience ID/GUID).

 

 

Bottom line, this also works the way we want for AUDIENCE, it doesn't break the readonly nature of the Publishing site.

 

 

So now remains the point on how to move/modify so many Audiences defined in the webparts in different sites/sub-sites. Simple answer is do it programmatically thru the command line parameter for ths site-collection, confusing? Well following code will make it simple (note that we had a requirement only for web part audience targeting and not Page/List item targeting, so the code is only limited to web part targeting and can be extended if required).

 

PLEASE DEFINE An OLDNEWGUID as a DATASET with a DATATABLE (DATATABLE1) with 2 columns OLD_GUID and NEW_GUID and in the buildNodes() method define the OLD_GUID and NEW_GUID IDs to create your list of Audience GUIDs. This method can be changed to read from an External File. I have just Hardcoded the AUDIENCE IDs, you can change it. All it does is to create a List of OLD_GUID and NEW_GUID (for find and replace of GUIDs).

 

using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.SharePoint.Administration;
using Microsoft.Office.Server.Audience;
using Microsoft.SharePoint;
using Microsoft.SharePoint.WebControls;
using Microsoft.SharePoint.WebPartPages;
using Microsoft.Office.Server;
using Microsoft.SharePoint.Utilities;
using System.Web;
using System.Collections;
using Microsoft.SharePoint.Publishing;

namespace Ketaanh.Sharepoint.Administration.AudienceChanger
{
    class Program
    {
        static OldNewGUID guidCollection;  
        static void Main(string[] args)
        {
            try
            {
                string strSiteURL = args[0];
                //building Nodes in a collection
                buildNodes();
               
                SPSecurity.RunWithElevatedPrivileges(delegate()
                {
                    using (SPSite siteColl = new SPSite(strSiteURL))
                    {

                        foreach (SPWeb web in siteColl.AllWebs)
                        {
                            if (PublishingWeb.IsPublishingWeb(web))
                            {
                                 PublishingWeb publishingWeb = PublishingWeb.GetPublishingWeb(web);
                                 PublishingPageCollection pageColl = publishingWeb.GetPublishingPages();
                               
                                foreach (PublishingPage page in pageColl)
                                {
                                 //only need to do this for ASPX pages (i.e. webpart pages) not html
                                    if (page.Url.Contains(".aspx"))
                                    {
                                        string strCheckInMsg = string.Empty;
                                        page.CheckOut();
                                        SPLimitedWebPartManager wm = web.GetLimitedWebPartManager(page.Url , System.Web.UI.WebControls.WebParts.PersonalizationScope.Shared);
                                        foreach (WebPart wp in wm.WebParts)
                                        {
                                           string[] strAudiences =  wp.AuthorizationFilter.Split(new char[] {';'});
                                           for (int i = 0; i < strAudiences.Length; i++)
                                           {
                                               if (!string.IsNullOrEmpty(strAudiences[i]))
                                               {
                                                   string newAUDGUID = ReplaceOldGuidWithNew(strAudiences[i]);
                                                   if (!string.IsNullOrEmpty(newAUDGUID))
                                                   {
                                                       wp.AuthorizationFilter = newAUDGUID;
                                                       wm.SaveChanges(wp);
                                                       strCheckInMsg = "Updated Audience";
                                                   }
                                               }
                                           }
                                        }
                                        page.CheckIn(strCheckInMsg);
                                        SPListItem pageForPublishing = page.ListItem;
                                        pageForPublishing.File.Publish(strCheckInMsg);
                                    }
                                }
                             }
                               
                           }
                        }
                });
            }
            catch (Exception ex)
            {
                Console.WriteLine("Error Occured, Details:=> {0}", ex.Message);
            }
            Console.WriteLine();
        }

        public static void buildNodes()
        {
            //building GUIDS first value is old GUID, second value is New GUID.
            guidCollection = new OldNewGUID();
            guidCollection.DataTable1.AddDataTable1Row("e7359169-48cd-4470-894a-dfd48b3f4452", "26a99c67-c30d-48fc-b126-910d7f4e5cdc");
            guidCollection.DataTable1.AddDataTable1Row("0f0648b2-cd9a-4f72-8242-57e99ba4a8fc", "51800193-6592-405e-a2e2-43ad96f0716b");
 
        }

        //This method returns the New GUID for the OLD GUID
        public static string ReplaceOldGuidWithNew(string AudienceGUID)
        {

            string strNewGUID = string.Empty;
            OldNewGUID.DataTable1Row row = guidCollection.DataTable1.FindByOLD_GUID(AudienceGUID);
            if (row != null)
            {

                return row.NEW_GUID;
            }
            else

           return strNewGUID;
        }
    }
}

 

Update : Also, the “read only” nature is applied to data that lives Content DB’s only (since content deployment doesn’t take care of other DBs at all) and the Audience ID is a reference to the SSP data. So, I don’t think that the nature of Content Deployment would not be preserved