I recently posted about exporting audiences using my gl-exportaudiences STSADM command. Of course an export wouldn’t be of much use if you didn’t also have an import so I give you gl-importaudiences.

Developing this was really easy as I already had code that created an audience and its associated rules. All I had to do was read in the source XML file, do a little refactoring of the audience creation code and then call the rules creation code. One cool thing I added was the ability to output a mapping file which provides the search and replace XML necessary to use my gl-replacefieldvalues command so that you can replace the old GUIDs used in audience targeting with the new GUID of the new audience. The code can be seen below:

  1#if MOSS
  2using System;
  3using System.Collections;
  4using System.Collections.Specialized;
  5using System.Collections.Generic;
  6using System.IO;
  7using System.Text;
  8using System.Xml;
  9using Lapointe.SharePoint.STSADM.Commands.OperationHelpers;
 10using Lapointe.SharePoint.STSADM.Commands.SPValidators;
 11using Microsoft.Office.Server;
 12using Microsoft.Office.Server.Audience;
 13 
 14namespace Lapointe.SharePoint.STSADM.Commands.Audiences
 15{
 16    public class ImportAudiences : SPOperation
 17    {
 18        /// <summary>
 19        /// Initializes a new instance of the <see cref="ImportAudiences"/> class.
 20        /// </summary>
 21        public ImportAudiences()
 22        {
 23            SPParamCollection parameters = new SPParamCollection();
 24            parameters.Add(new SPParam("ssp", "ssp", false, null, new SPNonEmptyValidator()));
 25            parameters.Add(new SPParam("deleteexisting", "delete"));
 26            parameters.Add(new SPParam("inputfile", "input", false, null, new SPFileExistsValidator()));
 27            parameters.Add(new SPParam("compile", "c"));
 28            parameters.Add(new SPParam("mapfile", "map", false, null, new SPDirectoryExistsAndValidFileNameValidator()));
 29 
 30            StringBuilder sb = new StringBuilder();
 31            sb.Append("\r\n\r\nImports all audiences given the provided input file.\r\n\r\nParameters:");
 32            sb.Append("\r\n\t-inputfile <file to input results from>");
 33            sb.Append("\r\n\t[-deleteexisting <delete existing audiences>]");
 34            sb.Append("\r\n\t[-ssp <SSP name>]");
 35            sb.Append("\r\n\t[-compile]");
 36            sb.Append("\r\n\t[-mapfile <generate a map file to use for search and replace of Audience IDs>]");
 37            Init(parameters, sb.ToString());
 38        }
 39 
 40        /// <summary>
 41        /// Gets the help message.
 42        /// </summary>
 43        /// <param name="command">The command.</param>
 44        /// <returns></returns>
 45        public override string GetHelpMessage(string command)
 46        {
 47            return HelpMessage;
 48        }
 49 
 50        /// <summary>
 51        /// Executes the specified command.
 52        /// </summary>
 53        /// <param name="command">The command.</param>
 54        /// <param name="keyValues">The key values.</param>
 55        /// <param name="output">The output.</param>
 56        /// <returns></returns>
 57        public override int Execute(string command, StringDictionary keyValues, out string output)
 58        {
 59            output = string.Empty;
 60 
 61            string inputFile = Params["inputfile"].Value;
 62            bool deleteExisting = Params["deleteexisting"].UserTypedIn;
 63            bool compile = Params["compile"].UserTypedIn;
 64            string mapFile = default(string);
 65            if (Params["mapfile"].UserTypedIn)
 66                mapFile = Params["mapfile"].Value;
 67 
 68            string xml = File.ReadAllText(inputFile);
 69 
 70            Import(xml, Params["ssp"].Value, deleteExisting, compile, mapFile);
 71 
 72            return OUTPUT_SUCCESS;
 73        }
 74 
 75        /// <summary>
 76        /// Imports the specified XML.
 77        /// </summary>
 78        /// <param name="xml">The XML.</param>
 79        /// <param name="sspName">Name of the SSP.</param>
 80        /// <param name="deleteExisting">if set to <c>true</c> [delete existing].</param>
 81        /// <param name="compile">if set to <c>true</c> [compile].</param>
 82        /// <param name="mapFile">The map file.</param>
 83        private void Import(string xml, string sspName, bool deleteExisting, bool compile, string mapFile)
 84        {
 85            ServerContext context;
 86            if (string.IsNullOrEmpty(sspName))
 87                context = ServerContext.Default;
 88            else
 89                context = ServerContext.GetContext(sspName);
 90 
 91            AudienceManager manager = new AudienceManager(context);
 92 
 93            XmlDocument audiencesDoc = new XmlDocument();
 94            audiencesDoc.LoadXml(xml);
 95 
 96            XmlNodeList audienceElements = audiencesDoc.SelectNodes("//Audience");
 97            if (audienceElements == null)
 98                throw new ArgumentException("The input file does not contain any audience elements.");
 99 
100            StringBuilder sb = new StringBuilder();
101            XmlTextWriter xmlWriter = new XmlTextWriter(new StringWriter(sb));
102            xmlWriter.Formatting = Formatting.Indented;
103            xmlWriter.WriteStartElement("Replacements");
104 
105            Dictionary<Guid, string> ids = new Dictionary<Guid, string>();
106            if (deleteExisting)
107            {
108                Log("Progrss: Deleting existing audiences.");
109                foreach (Audience au in manager.Audiences)
110                    ids.Add(au.AudienceID, au.AudienceName);
111 
112                foreach (Guid id in ids.Keys)
113                {
114                    if (id == Guid.Empty)
115                        continue;
116 
117                    string name = manager.Audiences[id].AudienceName;
118                    manager.Audiences.Remove(id);
119                }
120            }
121 
122            foreach (XmlElement audienceElement in audienceElements)
123            {
124                string audienceName = audienceElement.GetAttribute("AudienceName");
125                string audienceDesc = audienceElement.GetAttribute("AudienceDescription");
126 
127                Audience audience;
128                bool updatedAudience = false;
129                if (manager.Audiences.AudienceExist(audienceName))
130                {
131                    Log("Progress: Updating audience {0}.", audienceName);
132                    audience = manager.Audiences[audienceName];
133                    audience.AudienceDescription = audienceDesc ?? "";
134                    updatedAudience = true;
135                }
136                else
137                {
138                    // IMPORTANT: the create method does not do a null check but the methods that load the resultant collection assume not null.
139                    Log("Progress: Creating audience {0}.", audienceName);
140                    audience = manager.Audiences.Create(audienceName, audienceDesc ?? "");
141                }
142 
143                audience.GroupOperation = (AudienceGroupOperation)Enum.Parse(typeof (AudienceGroupOperation),
144                                                     audienceElement.GetAttribute("GroupOperation"));
145 
146                audience.OwnerAccountName = audienceElement.GetAttribute("OwnerAccountName");
147 
148                audience.Commit();
149 
150                if (updatedAudience && audience.AudienceID != Guid.Empty)
151                {
152                    // We've updated an existing audience.
153                    xmlWriter.WriteStartElement("Replacement");
154                    xmlWriter.WriteElementString("SearchString", (new Guid(audienceElement.GetAttribute("AudienceID")).ToString().ToUpper()));
155                    xmlWriter.WriteElementString("ReplaceString", string.Format("(?i:{0})", audience.AudienceID.ToString().ToUpper()));
156                    xmlWriter.WriteEndElement(); // Replacement
157                }
158                else if (!updatedAudience && audience.AudienceID != Guid.Empty && ids.ContainsValue(audience.AudienceName))
159                {
160                    // We've added a new audience which we just previously deleted.
161                    xmlWriter.WriteStartElement("Replacement");
162                    foreach (Guid id in ids.Keys)
163                    {
164                        if (ids[id] == audience.AudienceName)
165                        {
166                            xmlWriter.WriteElementString("SearchString", id.ToString().ToUpper());
167                            break;
168                        }
169                    }
170                    xmlWriter.WriteElementString("ReplaceString", string.Format("(?i:{0})", audience.AudienceID.ToString().ToUpper()));
171                    xmlWriter.WriteEndElement(); // Replacement
172                }
173 
174                XmlElement rulesElement = (XmlElement)audienceElement.SelectSingleNode("rules");
175                if (rulesElement == null |  rulesElement.ChildNodes.Count == 0)
176                {
177                    audience.AudienceRules = new ArrayList();
178                    audience.Commit();
179                    continue;
180                }
181 
182 
183                string rules = rulesElement.OuterXml;
184                Log("Progress: Adding rules to audience {0}.", audienceName);
185                AddAudienceRule.AddRules(sspName, audienceName, rules, true, compile, false, AddAudienceRule.AppendOp.AND);
186            }
187 
188            xmlWriter.WriteEndElement(); // Replacements
189 
190            if (!string.IsNullOrEmpty(mapFile))
191            {
192                xmlWriter.Flush();
193                File.WriteAllText(mapFile, sb.ToString());
194            }
195        }
196 
197    }
198}
199#endif

The help for the command is shown below:

C:\>stsadm -help gl-importaudiences

stsadm -o gl-importaudiences

Imports all audiences given the provided input file.

Parameters:
        -inputfile <file to input results from>
        [-deleteexisting <delete existing audiences>]
        [-ssp <SSP name>]
        [-compile]
        [-mapfile <generate a map file to use for search and replace of Audience IDs>]

The following table summarizes the command and its various parameters:

Command NameAvailabilityBuild Date
gl-importaudiencesMOSS 2007Released: 4/24/2009
Parameter NameShort FormRequiredDescriptionExample Usage
inputfileinputYesThe path to the input file obtained via the gl-exportaudiences command.-inputfile c:\audiences.xml, -input c:\audiences.xml
deleteexistingdeleteNoIf specified then all existing audiences will be deleted prior to importing the audiences. Note that the “All site users” audience will not be deleted or updated.-deleteexisting, -delete
sspNoThe name of the SSP to import the audiences into. If omitted then the default SSP will be used.-ssp SSP1
compilecNoIf specified then compile the audience after creation/update.-compile, -c
mapfilemapNoIf specified then an XML file will be generated providing the search and replace strings to use in order to update the GUIDs in your site collections.-mapfile c:\map.xml, -map c:\map.xml

The following is an example of how to import all audiences contained in the audiences.xml file:

stsadm -o gl-importaudiences -inputfile c:\audiences.xml -ssp SSP1 -mapfile c:\map.xml -compile

The following shows an example output of the map file after running the above command:

1<Replacements>
2  <Replacement>
3    <SearchString>98E29BEF-8B1E-4113-BB15-6FAF1E6FB8D0</SearchString>
4    <ReplaceString>(?i:1CA1F37E-A50A-4F84-BDCD-8C1279BADB3E)</ReplaceString>
5  </Replacement>
6</Replacements>