- Introduced custom branding CSS styles in `custom-branding.css`. - Created example JSON configuration for custom branding in `custom-branding.example.json`. - Implemented branding configuration logic in `BrandingConfig.ts` to normalize and validate branding settings. - Developed CSS loader to manage loading and unloading of custom stylesheets in `BrandingCssLoader.ts`. - Added DOM rendering capabilities for branding elements in `BrandingDomRenderer.ts`. - Defined types and interfaces for branding elements and configurations in `BrandingTypes.ts`. - Included localization support for German in `de-de.js`. - Added unit tests for branding configuration, CSS loader, and DOM renderer. - Validated project structure and static assets with new validation scripts.
195 lines
7.8 KiB
PowerShell
195 lines
7.8 KiB
PowerShell
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() }
|
|
}
|