Source code available through Github.
See also: Importing CSV term sets into SharePoint 2010 using PowerShell.
Here I provide an F# implementation of a Powershell script for populating the SharePoint 2010 term store with a term set. Each term set is stored in a CSV file (whose format is defined by Microsoft) that drives the calls made to the SharePoint .NET APIs to import it:
| | 0 | 5 | 6 | ... | 11 | |---|---------------|--------------|--------------|-----|--------------| | 0 | Term Set Name | Level 1 Term | Level 2 Term | ... | Level 7 Term | | 1 | TermSetName | | | ... | | | 2 | | Term1 | | ... | | | 3 | | Term2 | Term2.2 | ... | |
First the function signatures:
// Program.fsi
module Bugfree.SharePoint.TermSetImporter
open Microsoft.SharePoint.Taxonomy
val importCsv : string -> seq<string[]>
val removeTermGroup : TermStore -> string -> unit
val getOrCreate : 'b -> seq<'a> -> ('a -> bool) -> ('b -> 'a) -> 'a
val getOrCreateGroup : string -> TermStore -> Group
val getOrCreateSet : string -> Group -> TermSet
val getOrCreateTerm : string -> TermSetItem -> Term
val importTerm : TermSetItem -> string list -> TermSetItem
val importTermSet : TermStore -> string -> seq<string[]> -> unit
val main : string[] -> int
Then the implementation:
// Program.fs
module Bugfree.SharePoint.TermSetImporter
open System.IO
open System.Collections.Generic
open Microsoft.SharePoint
open Microsoft.SharePoint.Taxonomy
let importCsv path =
seq { use sr = File.OpenText(path)
while not sr.EndOfStream do
let line = sr.ReadLine()
let tokens = line.Split [|','|]
yield tokens }
let removeTermGroup (store : TermStore) name =
store.Groups |> Seq.filter(fun g -> g.Name = name)
|> Seq.iter(fun g -> g.TermSets |> Seq.iter(fun t -> t.Delete())
g.Delete()
store.CommitAll())
let getOrCreate name children predicate create =
match (Seq.tryFind predicate children) with
| Some c -> c
| None -> create name
let getOrCreateGroup name (store : TermStore) =
getOrCreate name store.Groups (fun g -> g.Name = name)
(fun name -> store.CreateGroup(name))
let getOrCreateSet name (group : Group) =
getOrCreate name group.TermSets (fun s -> s.Name = name)
(fun name -> group.CreateTermSet(name))
let getOrCreateTerm name (item : TermSetItem) =
getOrCreate name item.Terms (fun t -> t.Name = name)
(fun name -> item.CreateTerm(name, 1033))
let rec importTerm (parent : TermSetItem) levels =
match levels with
| [] -> parent
| head::tail -> let t = getOrCreateTerm head parent
importTerm t tail
let importTermSet (store : TermStore) groupName (rows : seq<string[]>) =
let termSetName = (rows |> Seq.nth 1).[0].Replace("\"", "")
let termSet = getOrCreateGroup groupName store |> getOrCreateSet termSetName
rows |> Seq.skip 2
|> Seq.iter(fun r -> r.[5..] |> Array.filter(fun i -> i <> "")
|> Array.map(fun i -> i.Replace("\"", ""))
|> Array.toList
|> importTerm termSet
|> ignore)
store.CommitAll()
[<EntryPoint>]
let main args =
let siteCollection = new SPSite("http://sp2010")
let session = new TaxonomySession(siteCollection)
let store = session.TermStores.["Managed Metadata Service"]
let rows = importCsv "C:\Test.csv"
let groupName = "MyGroup"
removeTermGroup store groupName
importTermSet store groupName rows
0
Maybe it's implementing the population algorithm twice, but to me the F# implementation seems the easiest one to implement and follow. Not because PowerShell is a bad language, but because F#'s static typing and type inferencing makes it better suited for implementing something of this relative complexity.