evont-software.com

Email: info@evont-software.com

Powershell - Create Navigation

Category:Administration
Date:
Author: Mathias Osterkamp

Managed Navigation with Powershell

Goal

Our goal is a complete setup for a test site collection with managed metadata navigation and existing pages with different levels. The challenge is the correct configuration for the site with powershell and also create the correct terms. With the script you can set the amount of navigation entries.

Preparation

We use SharePoint PnP Powershell Package to get help with some tasks.

  • PnP Powershell 2019 Download
  • CSOM dlls "Microsoft.SharePoint.Client.dll" "Microsoft.SharePoint.Client.Runtime.dll" "Microsoft.SharePoint.Client.Taxonomy.dll" "Microsoft.SharePoint.Client.Publishing.dll" from "\Program Files\Common Files\Microsoft Shared\Web Server Extensions\15\isapi\" , more details here
  • Create a classic team site collection

Key Points

The Invoke-SetupFeatures enables needed features for publishing, what is the requirement.

The Invoke-SetupTermSet creates your termset and Invoke-SetupNavigation enables navigation features. We also use Invoke-EnableTagging to allow tagging, that means we also override the default permission behaviour. (Metadata navigation is not visible to users with read permissions) more information.

Furthermore we collect the page layout item Get-PageLayoutItem and create our terms Invoke-CreateTerms. For each term we create also first a new site page Add-PublishingPageWithContent and connect this page with the new created term.

The Invoke-UpdateTaxonomyCache checks changes for taxonomy and avoids save conflicts.

Content not crawled by search

We also had some issues to crawl a big amount (over 3000) of friendly pages from search. That means you can create the pages, but the search index does not collect it any more - at least the friendly url version.

The official limit for "Number of terms in managed navigation term set" from Microsoft is at 2000.

After some research, on a full crawl for example the complete termset is crawled with all pages. That leads to a timeout. You can override this in search central administration here. We had to set it to a high value, the crawl tooks over 12 minutes - so that is maybe in some cases no optimal solution.

Complete Script


1# -------------------------------------------------------------------------------------
2# Configuration
3# -------------------------------------------------------------------------------------
4$siteurl = "https://contoso.com/sites/sitecollection1"
5$termSetName = "GlobalNav"
6
7# -------------------------------------------------------------------------------------
8# Functions
9# -------------------------------------------------------------------------------------
10function Invoke-CreateTerms($context, $PublishingWeb, $PageLayoutItem, $termStore, $termObject, $path, $level, $maxlevel, $maxItemsPerLevel, $overallMax) {
11
12 $max = $maxItemsPerLevel
13 $level = $level + 1
14
15 for ($i = 1; $i -lt $max + 1; $i++) {
16 if ($global:countTerms -lt $overallMax) {
17 if ($path -eq "") {
18 $newPath = "$i"
19 }
20 else {
21 $newPath = "$($path)_$($i)"
22 }
23
24 $termName = "Term$newPath"
25
26 $newpage = Add-PublishingPageWithContent $context $PublishingWeb $PageLayoutItem "$($termName).aspx" $termName $termName
27 $url = $newpage["FileRef"]
28 $global:countTerms++
29 Write-Host "Create term $($termName) - $($global:countTerms)/$overallMax..." -f Yellow -NoNewline
30 $newTerm = $termObject.CreateTerm($termName, 1033, [System.Guid]::NewGuid().toString())
31 $newTerm.SetLocalCustomProperty('_Sys_Nav_TargetUrl', $url)
32 Write-Host "Done" -f Green
33
34 if ($level -lt $maxlevel ) {
35 Invoke-CreateTerms $context $PublishingWeb $PageLayoutItem $termStore $newTerm $newPath $level $maxlevel $maxItemsPerLevel $overallMax
36 }
37 }
38 }
39 $termStore.CommitAll()
40 Invoke-PnPQuery
41
42}
43function Get-PageLayoutItem($PageLayoutName) {
44 $Ctx = Get-PnPContext
45 Write-host -f Yellow "Getting Page Layout..." -NoNewline
46 #Get the Page Layout
47 $RootWeb = $Ctx.Site.RootWeb
48 $MasterPageList = $RootWeb.Lists.GetByTitle('Master Page Gallery')
49 $CAMLQuery = New-Object Microsoft.SharePoint.Client.CamlQuery
50 $CAMLQuery.ViewXml = "<View><Query><Where><Eq><FieldRef Name='FileLeafRef' /><Value Type='Text'>$PageLayoutName</Value></Eq></Where></Query></View>"
51 $PageLayouts = $MasterPageList.GetItems($CAMLQuery)
52 $Ctx.Load($PageLayouts)
53 $Ctx.ExecuteQuery()
54
55 $PageLayoutItem = $PageLayouts[0]
56 $Ctx.Load($PageLayoutItem)
57 $Ctx.ExecuteQuery()
58 Write-host -f Green "Done"
59 return $PageLayoutItem
60}
61function Add-PublishingPageWithContent($Ctx, $PublishingWeb , $PageLayoutItem , $PageName, $PageTitle, $PageContent) {
62
63
64 #Create Publishing page
65 Write-host -f Yellow "Creating New Page $PageName ..." -NoNewline
66 $PageInfo = New-Object Microsoft.SharePoint.Client.Publishing.PublishingPageInformation
67 $PageInfo.Name = $PageName
68 $PageInfo.PageLayoutListItem = $PageLayoutItem
69 $Page = $PublishingWeb.AddPublishingPage($PageInfo)
70 $Ctx.ExecuteQuery()
71
72 $ListItem = $Page.ListItem
73 $Ctx.Load($ListItem)
74 $Ctx.ExecuteQuery()
75
76 #Update Page Contents
77 $ListItem["Title"] = $PageTitle
78 $ListItem["PublishingPageContent"] = $PageContent
79 $ListItem.Update()
80 $Ctx.ExecuteQuery()
81
82
83 #Publish the page
84
85 $ListItem.File.CheckIn([string]::Empty, [Microsoft.SharePoint.Client.CheckinType]::MajorCheckIn)
86 $ListItem.File.Publish([string]::Empty)
87 $Ctx.ExecuteQuery()
88 Write-host -f Green "Done"
89
90
91 return $ListItem
92}
93function Invoke-SetupFeatures() {
94
95 #publihsing Infrastructure site feature check
96 $FeaturePublishingInfraSiteId = "f6924d36-2fa8-4f0b-b16d-06b7250180fa" #Site Scoped Publishing Feature
97 $Feature = Get-PnPFeature -Scope Site -Identity $FeaturePublishingInfraSiteId
98 If ($null -eq $Feature.DefinitionId) {
99 Write-host -f Yellow "Activating Publishing Infrastructure Site Feature..." -NoNewline
100 Enable-PnPFeature -Scope Site -Identity $FeaturePublishingInfraSiteId -Force
101 Write-host -f Green "Done"
102 }
103 Else {
104 Write-host -f Yellow "Publishing Infrastructure Site Feature already activated..." -NoNewline
105 Write-host -f Green "Done"
106 }
107
108 #publishing Infrastructure web feature check
109 $FeaturePublishingInfraWebId = "94c94ca6-b32f-4da9-a9e3-1f3d343d7ecb"
110 $Feature = Get-PnPFeature -Scope Web -Identity $FeaturePublishingInfraWebId
111 If ($null -eq $Feature.DefinitionId) {
112 Write-host -f Yellow "Activating Publishing Infrastructure Web Feature..."
113 Enable-PnPFeature -Scope Web -Identity $FeaturePublishingInfraWebId -Force
114 Write-host -f Green "Done"
115 }
116 Else {
117 Write-host -f Yellow "Publishing Infrastructure Web Feature already activated..." -NoNewline
118 Write-host -f Green "Done"
119 }
120
121 #Wait complete all
122 Start-Sleep -Seconds 10
123}
124function Invoke-SetupTermSet($termSetName) {
125
126 $context = Get-PnPContext
127 $CurrentSite = Get-PnPSite
128 $taxonomySession = [Microsoft.SharePoint.Client.Taxonomy.TaxonomySession]::GetTaxonomySession($context)
129 $TermStore = $taxonomySession.GetDefaultSiteCollectionTermStore();
130 $SiteCollectionTermGroup = $TermStore.GetSiteCollectionGroup($CurrentSite, $false)
131 $context.Load($taxonomySession)
132 $context.Load($SiteCollectionTermGroup)
133 $context.Load($TermStore)
134 Invoke-PnPQuery
135 $termgroupname = $SiteCollectionTermGroup.Name
136
137 $termSets = Get-PnPTermSet -TermGroup $termgroupname
138 $exists = ($termSets | Where-Object { $_.Name -eq $termSetName } | Measure-Object).Count -gt 0
139 if ($exists -eq $false ) {
140 Write-Host "Created termset $termSetName ..." -f Yellow -NoNewline
141 $termSet = New-PnPTermSet -Name $termSetName -TermGroup $SiteCollectionTermGroup -Lcid 1033 -IsOpenForTermCreation
142 $TermStore.CommitAll()
143 Write-Host "Done" -f Green
144 }
145 else {
146
147 Start-Sleep -Seconds 5
148 Write-Host "Termset already exists $($termSet.Name)" -f Yellow -NoNewline
149 $termSet = Get-PnPTermSet -Identity $termSetName -TermGroup $termgroupname
150 Write-Host "Done" -f Green
151 }
152
153}
154function Invoke-SetupNavigation($termSetName) {
155
156 Write-Host "Reset Navigation..." -f Yellow -NoNewline
157 $context = Get-PnPContext
158 $CurrentSite = Get-PnPSite
159 $taxonomySession = [Microsoft.SharePoint.Client.Taxonomy.TaxonomySession]::GetTaxonomySession($context)
160
161 $TermStore = $taxonomySession.GetDefaultSiteCollectionTermStore();
162 $SiteCollectionTermGroup = $TermStore.GetSiteCollectionGroup($CurrentSite, $false)
163 $termsets = $SiteCollectionTermGroup.TermSets
164 $context.Load($taxonomySession)
165 $context.Load($SiteCollectionTermGroup)
166 $context.Load($termsets)
167 $context.Load($TermStore)
168 Invoke-PnPQuery
169
170 $navigationTermSet = $termsets | Where-Object { $_.Name -eq $termSetName }
171
172 $context.Load($navigationTermSet)
173 Invoke-PnPQuery
174 Write-Host "Done" -f Green
175
176 Write-Host "Set taxonomy navigation..." -f Yellow -NoNewline
177 $context = Get-PnPContext
178 $Web = Get-PnPWeb
179 $navigationSettings = New-Object Microsoft.SharePoint.Client.Publishing.Navigation.WebNavigationSettings $context, $Web
180 $navigationSettings.ResetToDefaults()
181 $navigationSettings.GlobalNavigation.Source = 1
182 $navigationSettings.CurrentNavigation.Source = 1
183 $navigationSettings.Update($taxonomySession)
184 Invoke-PnPQuery
185 Start-Sleep -Seconds 2
186
187
188 $context = Get-PnPContext
189 $Web = Get-PnPWeb
190 $navigationSettings = New-Object Microsoft.SharePoint.Client.Publishing.Navigation.WebNavigationSettings $context, $Web
191 $navigationSettings.CurrentNavigation.Source = "taxonomyProvider"
192 $navigationSettings.CurrentNavigation.TermStoreId = $TermStore.Id
193 $navigationSettings.CurrentNavigation.TermSetId = $navigationTermSet.Id
194 $navigationSettings.GlobalNavigation.Source = "taxonomyProvider"
195 $navigationSettings.GlobalNavigation.TermStoreId = $TermStore.Id
196 $navigationSettings.GlobalNavigation.TermSetId = $navigationTermSet.Id
197 $navigationSettings.Update($taxonomySession)
198
199 $Web.AllProperties["__IncludeSubSitesInNavigation"] = $True
200 #Show pages in global navigation
201 $Web.AllProperties["__IncludePagesInNavigation"] = $False
202
203 #Update Settings
204 $Web.Update()
205 $TermStore.CommitAll()
206 Invoke-PnPQuery
207
208 Write-Host "Done" -f Green
209
210}
211function Invoke-UpdateTaxonomyCache() {
212 $context = Get-PnPContext
213 Write-Host "Update taxonomy cache..." -f Yellow -NoNewline
214 $TaxonomySession = Get-PnPTaxonomySession
215 $TaxonomySession.UpdateCache()
216 $context.Load($TaxonomySession)
217 Invoke-PnPQuery
218 Write-Host "Done" -f Green
219}
220function Invoke-EnableTagging($termSetName) {
221
222 Write-Host "Enable tagging for termset..." -f Yellow -NoNewline
223 Start-Sleep -Seconds 5
224 $context = Get-PnPContext
225 $CurrentSite = Get-PnPSite
226 $taxonomySession = [Microsoft.SharePoint.Client.Taxonomy.TaxonomySession]::GetTaxonomySession($context)
227 $TermStore = $taxonomySession.GetDefaultSiteCollectionTermStore();
228 $SiteCollectionTermGroup = $TermStore.GetSiteCollectionGroup($CurrentSite, $false)
229 $termsets = $SiteCollectionTermGroup.TermSets
230 $context.Load($taxonomySession)
231 $context.Load($SiteCollectionTermGroup)
232 $context.Load($termsets)
233 $context.Load($TermStore)
234 Invoke-PnPQuery
235
236 $navigationTermSet = $termsets | Where-Object { $_.Name -eq $termSetName }
237
238 $context.Load($navigationTermSet)
239 Invoke-PnPQuery
240
241 $navigationTermSet.IsOpenForTermCreation = $true
242 $navigationTermSet.IsAvailableForTagging = $true
243 $TermStore.CommitAll()
244
245 Start-Sleep -Seconds 2
246 Write-Host "Done" -f Green
247}
248
249function Invoke-LoadPnp() {
250 Write-Host "Load Libraries..." -f Yellow -NoNewline
251
252 $rootpath = $PSScriptRoot
253 $path = Join-Path -Path $rootpath -ChildPath "SharePointPnPPowerShell2019\3.29.2101.0\"
254 $sharepointPowershellModulePath = Join-Path -Path $path -ChildPath "SharePointPnPPowerShell2019.psd1"
255 if ($null -eq (Get-Module -Name "SharePointPnPPowerShell2019")) {
256 Import-Module $sharepointPowershellModulePath -DisableNameChecking
257 Disable-PnPPowerShellTelemetry -Force | Out-Null
258 }
259 #Load SharePoint CSOM Assemblies
260 Add-Type -Path "$path\Microsoft.SharePoint.Client.dll"
261 Add-Type -Path "$path\Microsoft.SharePoint.Client.Runtime.dll"
262 Add-Type -Path "$path\Microsoft.SharePoint.Client.Taxonomy.dll"
263 Add-Type -Path "$path\Microsoft.SharePoint.Client.Publishing.dll"
264 Write-Host "Done" -f Green
265}
266
267# -------------------------------------------------------------------------------------
268# Load PnP and Client Libraries
269# -------------------------------------------------------------------------------------
270Invoke-LoadPnp
271# -------------------------------------------------------------------------------------
272# Connect
273# -------------------------------------------------------------------------------------
274$global:countTerms = 0
275
276Connect-PnPOnline -Url $siteurl -CurrentCredentials
277$Web = Get-PnPWeb -Includes Title, WebTemplate, Configuration
278
279# -------------------------------------------------------------------------------------
280# Test Template
281# -------------------------------------------------------------------------------------
282Write-Host "Site $($Web.Title): $($Web.WebTemplate)#$($Web.Configuration) it should be STS#0"
283if ($Web.WebTemplate -ne "STS" -and $Web.Configuration -ne 0) {
284 Write-Host "Wrong template" -ForegroundColor Red
285}
286
287# -------------------------------------------------------------------------------------
288# Setup Features
289# -------------------------------------------------------------------------------------
290Invoke-SetupFeatures
291
292# -------------------------------------------------------------------------------------
293# Setup Termset
294# -------------------------------------------------------------------------------------
295Invoke-SetupTermSet $termSetName
296Invoke-UpdateTaxonomyCache
297# -------------------------------------------------------------------------------------
298# Setup Navigation
299# -------------------------------------------------------------------------------------
300Invoke-SetupNavigation $termSetName
301Invoke-UpdateTaxonomyCache
302
303# -------------------------------------------------------------------------------------
304# Setup Tagging
305# -------------------------------------------------------------------------------------
306Invoke-EnableTagging $termSetName
307
308# -------------------------------------------------------------------------------------
309# Create Content
310# -------------------------------------------------------------------------------------
311$Ctx = Get-PnPContext
312$PublishingWeb = [Microsoft.SharePoint.Client.Publishing.PublishingWeb]::GetPublishingWeb($Ctx, $Ctx.Web)
313$Ctx.Load($PublishingWeb)
314$Ctx.ExecuteQuery()
315$PageLayoutItem = Get-PageLayoutItem 'ArticleLeft.aspx'
316$context = Get-PnPContext
317$CurrentSite = Get-PnPSite
318$taxonomySession = [Microsoft.SharePoint.Client.Taxonomy.TaxonomySession]::GetTaxonomySession($context)
319
320$termStore = $taxonomySession.GetDefaultSiteCollectionTermStore();
321$SiteCollectionTermGroup = $TermStore.GetSiteCollectionGroup($CurrentSite, $false)
322$termsets = $SiteCollectionTermGroup.TermSets
323$context.Load($taxonomySession)
324$context.Load($SiteCollectionTermGroup)
325$context.Load($termsets)
326$context.Load($TermStore)
327Invoke-PnPQuery
328
329$termSet = $termsets | Where-Object { $_.Name -eq $termSetName }
330
331$context.Load($termSet)
332Invoke-PnPQuery
333Invoke-CreateTerms -context $context -PublishingWeb $PublishingWeb -PageLayoutItem $PageLayoutItem -termStore $termStore -termObject $termSet -path "" -level 0 -maxlevel 3 -maxItemsPerLevel 30 -overallMax 3300
334
335Invoke-UpdateTaxonomyCache