Migrating Audience Targeted Sites


Sometimes you’ll encounter a scenario where you need to move a site from one environment to another and the site is using Audiences. Now I’m personally a bit of a fan of Audiences for simple out of the box targeting of information. However, it has one pretty major flaw. Audiences are fundamentally environment specific. There is no out of the box method for remapping or moving audience targeted information and have it still work properly on the other side. This is due to a number of reasons which I’m not going to go in to in this blog post. However, here is a tool that can help with this :)

This tool is pretty straight forward with only two command, import and export.

  • Export – Generates a file with a mapping of audiences to be used when updating the new environment
  • Import – Based on your mapping file updates the content in the new environment to use the new environments audiences

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Office.Server;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Navigation;
using Microsoft.Office.Server.WebControls.FieldTypes;
using Microsoft.Office.Server.UserProfiles;
using Microsoft.Office.Server.Audience;
using System.IO;

namespace WithinSharePoint.MigrateAudiences
{
    class Program
    {
        internal static List<Audience> Audiences = null;
        internal static Dictionary<string, Audience> RemappedAudiences = null;
        static void Main(string[] args)
        {
            try
            {
                if (args.Count() == 2)
                {
                    SPSite site = new SPSite(args[1]);
                    foreach (Audience a in GetAudiences(site))
                    {
                        Console.WriteLine(a.AudienceName + ","+ a.AudienceID.ToString());
                    }
                }
                if (args.Count() == 3)
                {
                    SPSite site = new SPSite(args[1]);
                    SPWeb RootWeb = site.OpenWeb();
                    Audiences = GetAudiences(site);
                    RemappedAudiences = AudienceMap(args[2]);
                    ScanWeb(RootWeb);
                }
                else if ((args.Count() < 2) | (args.Count() > 3))
                {
                    WriteUsage();
                }
            }
            catch(Exception ex)
            {
                Console.WriteLine("ERROR:" + ex.Message);
                WriteUsage();
            }
        }

        internal static void WriteUsage()
        {
            Console.WriteLine("Usage: WithinSharePoint.MigrateAudiences -EXPORT [URL TO RETRIEVE AUDIENCES] >> AudienceExport.csv");
            Console.WriteLine("Usage: WithinSharePoint.MigrateAudiences -IMPORT [URL TO SCAN AND UPDATE] [FILEPATH]");
            Console.WriteLine("File is a text document with this format (Audience ID is the ID of the audience in the SOURCE environment, not the destination):");
            Console.WriteLine("AudienceName,AudienceID");
        }

        internal static void ScanWeb(SPWeb web)
        {
            web.AllowUnsafeUpdates = true;
            ScanLists(web.Lists);
            try
            {
                ScanNavigation(web.Navigation.GlobalNodes);
                ScanNavigation(web.Navigation.QuickLaunch);
            }
            catch (Exception ex)
            {
                Console.WriteLine("Error updating navigation. " + ex.Message);
                Console.WriteLine(web.Url);
            }
            foreach (SPWeb child in web.Webs)
            {
                ScanWeb(child);
            }
            web.AllowUnsafeUpdates = false;
        }

        internal static void ScanLists(SPListCollection Lists)
        {
            foreach (SPList list in Lists)
            {
                if (list.Fields.ContainsField("Target_x0020_Audiences"))
                {
                    ScanItems(list.Items, "Target_x0020_Audiences");
                }
                else if (list.Fields.ContainsField("Audience"))
                {
                    ScanItems(list.Items, "Audience");
                }
            }
        }

        /// <summary>
        /// Scans and updates all audience targetted sharepoint navigation nodes with audiences from the new environment
        /// </summary>
        /// <param name="Nodes"></param>
        internal static void ScanNavigation(SPNavigationNodeCollection Nodes)
        {
            string value = "";
            string[] values;
            bool pendingupdate = false;
            Char[] splitter = new Char[] { ';' };
            SPNavigationNode node;
            for (int i = 0; i < Nodes.Count; i++ )
            {
                node = Nodes[i];
                string newvalue = "";
                if (node.Properties.Contains("Audience"))
                {
                    value = node.Properties["Audience"].ToString();
                    value = value.Replace(',', ';');
                    values = value.Split(splitter, StringSplitOptions.RemoveEmptyEntries);
                    foreach (string val in values)
                    {
                        if (RemappedAudiences.ContainsKey(val))
                        {
                            //update with new audiences
                            pendingupdate = true;
                            newvalue += RemappedAudiences[val].AudienceID + ",";
                        }
                        else
                        {
                            //this is to preserve existing unknown audiences
                            newvalue += val + ",";
                        }
                    }
                    if (pendingupdate)
                    {
                        node.Properties["Audience"] = newvalue;
                        node.Update();
                    }
                }
            }
        }

        /// <summary>
        /// Scans all items in an audience targetted list and updates them with new environments audiences
        /// </summary>
        /// <param name="items"></param>
        /// <param name="AudienceField"></param>
        internal static void ScanItems(SPListItemCollection items, string AudienceField)
        {
            Console.WriteLine("Scanning and updating list " + items.List.Title);
            bool ListUpdate = false;
            Char[] splitter = new Char[] { ';' };
            SPListItem item;
            for(int i = 0; i < items.Count;i++)
            {
                try
                {
                    item = items[i];
                    string value = "";
                    if(item[AudienceField] != null)
                    {
                        if (!String.IsNullOrEmpty(item[AudienceField].ToString()))
                        {
                            bool PendingUpdate = false;
                            string NewValue = "";
                            value = item[AudienceField].ToString();
                            if (value.Contains(","))
                            {
                                value = value.Replace(',', ';');
                            }
                            //Console.WriteLine(value);
                            string[] audiences = value.Split(splitter, StringSplitOptions.RemoveEmptyEntries);
                            foreach (string a in audiences)
                            {
                                //Console.WriteLine(a);
                                if (RemappedAudiences.ContainsKey(a))
                                {
                                    //add remapped audience to update
                                    PendingUpdate = true;
                                    NewValue += RemappedAudiences[a].AudienceID + ",";
                                }
                                else
                                {
                                    //keep unknown audiences in the item
                                    NewValue += a + ",";
                                }
                            }
                            if (PendingUpdate)
                            {
                                //don't ask why sharepoint uses csv for audience id's and then appends ;;; at the end
                                item[AudienceField] = NewValue + ";;;;";
                                ListUpdate = true;
                                item.UpdateOverwriteVersion();
                            }
                        }
                    }
                }
                catch(Exception ex)
                {
                    Console.WriteLine("Error reading line item:" + ex.Message);
                    Console.WriteLine(items[1][AudienceField].ToString());
                    Console.WriteLine(ex.StackTrace);
                }
            }
            if (ListUpdate)
            {
                items.List.Update();
            }
        }

        /// <summary>
        /// Reads the contents of the audience export file to generate a mapping of equivalent audiences in the target environment
        /// </summary>
        /// <param name="FilePath"></param>
        /// <returns>
        /// String - AudienceID of Source Environment
        /// Audience - Audience in New/Target Environment
        /// </returns>
        internal static Dictionary<string, Audience> AudienceMap(string FilePath)
        {
            Dictionary<string, Audience> map = new Dictionary<string, Audience>();
            StreamReader reader = new StreamReader(FilePath);
            string input = null;
            while ((input = reader.ReadLine()) != null)
            {
                string[] line = input.Split(',');
                if(line.Count() == 2)
                {
                    var match = from a in Audiences where a.AudienceName == line[0] select a;
                    foreach (Audience m in match)
                    {
                        map.Add(line[1], m);
                    }
                }
            }
            return map;
        }

        /// <summary>
        /// Returns a list of all audiences from the target site collection
        /// </summary>
        /// <param name="site"></param>
        /// <returns></returns>
        internal static List<Audience> GetAudiences(SPSite site)
        {
            List<Audience> audiences = new List<Audience>();
            SPServiceContext context = SPServiceContext.GetContext(site);
            AudienceManager aMan = new AudienceManager(context);
            foreach (Audience a in aMan.Audiences)
            {
                audiences.Add(a);
            }
            return audiences;
        }
    }
}

  1. No comments yet.
(will not be published)


%d bloggers like this: