param( [Parameter(Mandatory = $true)] [Alias('WebUrl')] [string]$SiteUrl, [string]$CssPath = '', [string[]]$AllowedCssHost = @(), [switch]$EnableDebug, [switch]$Disable, [switch]$ResetConfiguration, [string]$ClassicScriptUrl = '', [switch]$RemoveClassicScriptLink, [string]$Description = 'MSFT-Custom-Solution:CustomBranding' ) if (-not (Get-Command -Name Get-SPSite -ErrorAction SilentlyContinue)) { if (Get-Module -ListAvailable -Name Microsoft.SharePoint.PowerShell) { Import-Module Microsoft.SharePoint.PowerShell -DisableNameChecking } elseif (Get-PSSnapin -Registered -Name Microsoft.SharePoint.PowerShell -ErrorAction SilentlyContinue) { Add-PSSnapin Microsoft.SharePoint.PowerShell } else { throw 'Die SharePoint PowerShell-Komponenten wurden nicht gefunden. Bitte in der SharePoint Management Shell ausfuehren.' } } $componentId = [Guid]'035ba968-6488-4d42-86b3-0470ffcc95b9' $componentIdText = $componentId.ToString().ToLowerInvariant() $componentLocation = 'ClientSideExtension.ApplicationCustomizer' $componentName = 'CustomBranding' $componentTitle = 'Custom Branding Application Customizer' $classicActionName = 'CustomBranding.Classic.ScriptLink' function New-DefaultCustomBrandingConfiguration { return @{ schemaVersion = 2 enabled = $true debug = $false allowedCssHosts = @() cssfiles = @() placeholdertop = @{ elements = @() } placeholderbottom = @{ elements = @() } } } function ConvertTo-Hashtable { param([object]$InputObject) if ($null -eq $InputObject) { return $null } if ($InputObject -is [System.Collections.IDictionary]) { $result = @{} foreach ($key in $InputObject.Keys) { $result[$key] = ConvertTo-Hashtable $InputObject[$key] } return $result } if ($InputObject -is [System.Collections.IEnumerable] -and -not ($InputObject -is [string])) { return @($InputObject | ForEach-Object { ConvertTo-Hashtable $_ }) } if ($InputObject.PSObject -and $InputObject.PSObject.Properties.Count -gt 0 -and -not ($InputObject -is [string]) -and -not ($InputObject.GetType().IsPrimitive)) { $result = @{} foreach ($property in $InputObject.PSObject.Properties) { $result[$property.Name] = ConvertTo-Hashtable $property.Value } return $result } return $InputObject } function Get-ExistingConfiguration { param([Microsoft.SharePoint.SPUserCustomAction]$Action) if ($ResetConfiguration -or -not $Action -or [string]::IsNullOrWhiteSpace($Action.ClientSideComponentProperties)) { return New-DefaultCustomBrandingConfiguration } try { $parsed = $Action.ClientSideComponentProperties | ConvertFrom-Json $configuration = ConvertTo-Hashtable $parsed if (-not $configuration) { return New-DefaultCustomBrandingConfiguration } return $configuration } catch { Write-Warning 'Vorhandene ClientSideComponentProperties waren ungueltig und werden durch Standardwerte ersetzt.' return New-DefaultCustomBrandingConfiguration } } function Remove-WebScopedComponentActions { param([Microsoft.SharePoint.SPSite]$CurrentSite) $removed = 0 foreach ($web in $CurrentSite.AllWebs) { try { $actions = @($web.UserCustomActions | Where-Object { $_.Location -eq $componentLocation -and $_.ClientSideComponentId -and $_.ClientSideComponentId.ToString().ToLowerInvariant() -eq $componentIdText }) foreach ($action in $actions) { $action.Delete() $removed++ } if ($actions.Count -gt 0) { $web.Update() } } finally { $web.Dispose() } } return $removed } $site = Get-SPSite -Identity $SiteUrl try { $siteActions = @($site.UserCustomActions | Where-Object { $_.Location -eq $componentLocation -and $_.ClientSideComponentId -and $_.ClientSideComponentId.ToString().ToLowerInvariant() -eq $componentIdText }) $action = $siteActions | Select-Object -First 1 $configuration = Get-ExistingConfiguration -Action $action $configuration.schemaVersion = 2 if (-not $configuration.ContainsKey('placeholdertop')) { $configuration.placeholdertop = @{ elements = @() } } if (-not $configuration.ContainsKey('placeholderbottom')) { $configuration.placeholderbottom = @{ elements = @() } } if (-not $configuration.ContainsKey('cssfiles')) { $configuration.cssfiles = @() } if (-not $configuration.ContainsKey('allowedCssHosts')) { $configuration.allowedCssHosts = @() } if ($PSBoundParameters.ContainsKey('EnableDebug')) { $configuration.debug = [bool]$EnableDebug.IsPresent } if ($PSBoundParameters.ContainsKey('Disable')) { $configuration.enabled = -not [bool]$Disable.IsPresent } if (-not [string]::IsNullOrWhiteSpace($CssPath)) { $trimmedPath = $CssPath.Trim() $existingPaths = @($configuration.cssfiles | ForEach-Object { if ($_ -is [System.Collections.IDictionary]) { [string]$_['path'] } else { [string]$_.path } }) if ($existingPaths -notcontains $trimmedPath) { $configuration.cssfiles = @($configuration.cssfiles) + @(@{ path = $trimmedPath; media = 'all' }) } } if ($AllowedCssHost.Count -gt 0) { $configuration.allowedCssHosts = @($AllowedCssHost | ForEach-Object { $_.Trim().ToLowerInvariant() } | Where-Object { $_ } | Select-Object -Unique) } if (-not $action) { $action = $site.UserCustomActions.Add() Write-Host 'Erstelle zentrale CustomBranding Site-Collection-Action...' -ForegroundColor Yellow } else { Write-Host 'Aktualisiere zentrale CustomBranding Site-Collection-Action...' -ForegroundColor Yellow } $action.Name = $componentName $action.Title = $componentTitle $action.Description = $Description $action.Location = $componentLocation $action.Sequence = 100 $action.ClientSideComponentId = $componentId $action.ClientSideComponentProperties = [string]($configuration | ConvertTo-Json -Depth 20 -Compress) $action.Update() @($siteActions | Select-Object -Skip 1) | ForEach-Object { Write-Host ('Entferne doppelte Site-Collection-Action: ' + $_.Id) -ForegroundColor Yellow $_.Delete() } $removedWebActions = Remove-WebScopedComponentActions -CurrentSite $site $classicActions = @($site.UserCustomActions | Where-Object { $_.Location -eq 'ScriptLink' -and $_.Name -eq $classicActionName }) if ($RemoveClassicScriptLink) { foreach ($classicAction in $classicActions) { $classicAction.Delete() } Write-Host 'Classic ScriptLink wurde entfernt.' -ForegroundColor Yellow } elseif (-not [string]::IsNullOrWhiteSpace($ClassicScriptUrl)) { $classicAction = $classicActions | Select-Object -First 1 if (-not $classicAction) { $classicAction = $site.UserCustomActions.Add() } $classicAction.Name = $classicActionName $classicAction.Title = 'CustomBranding Classic Runtime' $classicAction.Description = $Description $classicAction.Location = 'ScriptLink' $classicAction.Sequence = 101 $classicAction.ScriptSrc = $ClassicScriptUrl.Trim() $classicAction.Update() @($classicActions | Select-Object -Skip 1) | ForEach-Object { $_.Delete() } Write-Host ('Classic ScriptLink: ' + $classicAction.ScriptSrc) -ForegroundColor Green } Write-Host 'CustomBranding wurde zentral fuer die Site Collection registriert.' -ForegroundColor Green Write-Host ('Entfernte web-scoped Actions: ' + $removedWebActions) Write-Host ('Properties: ' + $action.ClientSideComponentProperties) } finally { if ($site) { $site.Dispose() } }