dev #1

Merged
Torsten merged 10 commits from dev into main 2026-07-19 21:57:45 +00:00
11 changed files with 102 additions and 86 deletions
Showing only changes of commit 85c56a1153 - Show all commits

View File

@@ -4,7 +4,7 @@ Barrierearme, dreistufige Portalnavigation auf Basis von SharePoint Managed Meta
| Eigenschaft | Wert | | Eigenschaft | Wert |
|---|---| |---|---|
| Version | 1.1.4 | | Version | 2.0.0 |
| SharePoint Framework | SPFx 1.4.1 | | SharePoint Framework | SPFx 1.4.1 |
| Node.js | 8.17.0 | | Node.js | 8.17.0 |
| Zieloberfläche | Moderne SharePoint-Seiten | | Zieloberfläche | Moderne SharePoint-Seiten |
@@ -43,7 +43,7 @@ MegaMenuApplicationCustomizer
└── responsive Darstellung └── responsive Darstellung
``` ```
Das MegaMenu liest seine Laufzeitkonfiguration aus der site-scoped UserCustomAction. Es enthält keine eigene Administrationsoberfläche. Die zentrale Pflege soll über die separate Solution **PortalSettings** erfolgen. Das MegaMenu wird einmal pro Site Collection konfiguriert. PortalSettings speichert die zentrale Konfiguration im Property Bag des Root Webs und synchronisiert eine identische, web-scoped Runtime-Bindung auf das Root Web und alle vorhandenen Unterwebs. Das MegaMenu selbst enthält keine eigene Administrationsoberfläche.
## Voraussetzungen ## Voraussetzungen
@@ -126,10 +126,10 @@ sharepoint/solution/mega-menu.sppkg
1. `mega-menu.sppkg` in den App Catalog laden. 1. `mega-menu.sppkg` in den App Catalog laden.
2. Die Solution freigeben. 2. Die Solution freigeben.
3. Die App im Root Web der gewünschten Site Collection installieren. Der MegaMenu Application Customizer wird dabei automatisch web-scoped registriert und von PortalSettings erkannt. 3. Die App im Root Web der gewünschten Site Collection installieren. Der MegaMenu Application Customizer wird dort zunächst web-scoped registriert und von PortalSettings erkannt.
4. Das Termset in PortalSettings konfigurieren. 4. Das Termset in PortalSettings konfigurieren.
Das folgende Skript ist nur erforderlich, wenn ausdrücklich eine site-scoped Registrierung oder eine vollständig skriptbasierte Vorkonfiguration gewünscht ist. Das folgende Skript ist nur erforderlich, wenn PortalSettings nicht verwendet wird oder eine vollständig skriptbasierte Vorkonfiguration gewünscht ist.
Empfohlen mit Termset-GUID: Empfohlen mit Termset-GUID:
@@ -149,7 +149,7 @@ Abwärtskompatibel nur mit Namen:
-TermSetName 'Global Navigation' -TermSetName 'Global Navigation'
``` ```
Das Skript entfernt ältere web-scoped Registrierungen derselben Komponente und erstellt beziehungsweise aktualisiert eine site-scoped UserCustomAction mit dem Description-Tag `MSFT-Custom-Solution:MegaMenu`. Dieses Tag wird von PortalSettings zur Erkennung verwendet. Das Skript speichert die Konfiguration zentral unter `PortalSettings.MegaMenu.Configuration`, entfernt alte beziehungsweise doppelte Component IDs und synchronisiert die neue web-scoped Runtime-Bindung idempotent auf alle vorhandenen Webs. Fuer spaeter angelegte Unterwebs kann es ohne Nebenwirkungen erneut ausgefuehrt werden.
## Individuelles Branding ## Individuelles Branding
@@ -261,6 +261,13 @@ MegaMenu/
## Versionshistorie ## Versionshistorie
### 2.0.0
- Neue Application-Customizer-ID `c0abbffb-355d-4d4e-bd38-9e15fb811506`, um alte On-Premises-Manifest- und Loader-Zuordnungen sicher zu umgehen.
- Eine zentrale MegaMenu-Konfiguration pro Site Collection im Property Bag des Root Webs.
- Web-scoped Runtime-Bindungen werden auf Root Web und alle vorhandenen Unterwebs synchronisiert.
- Aktivierungsskript entfernt alte Component IDs und kann fuer neue Unterwebs idempotent erneut ausgefuehrt werden.
### 1.1.4 ### 1.1.4
- Unnötige externe Abhängigkeit zu `@microsoft/sp-lodash-subset` entfernt. - Unnötige externe Abhängigkeit zu `@microsoft/sp-lodash-subset` entfernt.

View File

@@ -113,7 +113,7 @@ Use one of these methods:
````powershell ````powershell
# Connect to SharePoint site # Connect to SharePoint site
Add-PnPCustomAction -Name "MegaMenuConfig" -Location "ClientSideExtension.ApplicationCustomizer" ` Add-PnPCustomAction -Name "MegaMenuConfig" -Location "ClientSideExtension.ApplicationCustomizer" `
-ClientSideComponentId "abc3361f-bb2d-491f-aba3-cd51c19a299b" ` -ClientSideComponentId "c0abbffb-355d-4d4e-bd38-9e15fb811506" `
-ClientSideComponentProperties '{"termSetName":"Your Navigation Termset","cssUrl":"/SiteAssets/custom-menu.css"}' -ClientSideComponentProperties '{"termSetName":"Your Navigation Termset","cssUrl":"/SiteAssets/custom-menu.css"}'
```` ````

View File

@@ -19,7 +19,7 @@
(function() { (function() {
'use strict'; 'use strict';
var MEGAMENU_UCA_ID = 'abc3361f-bb2d-491f-aba3-cd51c19a299b'; // Same as in MegaMenuApplicationCustomizer.ts var MEGAMENU_UCA_ID = 'c0abbffb-355d-4d4e-bd38-9e15fb811506'; // Same as in MegaMenuApplicationCustomizer.ts
var config = window.MegaMenuConfig || { var config = window.MegaMenuConfig || {
containerId: 's4-titlerow', containerId: 's4-titlerow',

View File

@@ -3,7 +3,7 @@
"solution": { "solution": {
"name": "mega-menu-client-side-solution", "name": "mega-menu-client-side-solution",
"id": "f4660e06-ce08-43ee-bfb7-5c4464e01133", "id": "f4660e06-ce08-43ee-bfb7-5c4464e01133",
"version": "1.1.4.0", "version": "2.0.0.0",
"includeClientSideAssets": true, "includeClientSideAssets": true,
"skipFeatureDeployment": false, "skipFeatureDeployment": false,
"features": [ "features": [
@@ -11,7 +11,7 @@
"title": "MegaMenu extension registration", "title": "MegaMenu extension registration",
"description": "Registers the MegaMenu Application Customizer in the host web.", "description": "Registers the MegaMenu Application Customizer in the host web.",
"id": "1e4fdc74-8053-48b2-a15b-e975d61eb71f", "id": "1e4fdc74-8053-48b2-a15b-e975d61eb71f",
"version": "1.1.4.0", "version": "2.0.0.0",
"assets": { "assets": {
"elementManifests": [ "elementManifests": [
"elements.xml" "elements.xml"

View File

@@ -6,7 +6,7 @@
"default": { "default": {
"pageUrl": "https://sharepoint.contoso.local/SitePages/Neues-Zuhause.aspx", "pageUrl": "https://sharepoint.contoso.local/SitePages/Neues-Zuhause.aspx",
"customActions": { "customActions": {
"abc3361f-bb2d-491f-aba3-cd51c19a299b": { "c0abbffb-355d-4d4e-bd38-9e15fb811506": {
"location": "ClientSideExtension.ApplicationCustomizer", "location": "ClientSideExtension.ApplicationCustomizer",
"properties": { "properties": {
"termSetName": "Horizontale Navigation", "termSetName": "Horizontale Navigation",
@@ -18,7 +18,7 @@
"megaMenu": { "megaMenu": {
"pageUrl": "https://contoso.sharepoint.com/sites/mySite/SitePages/myPage.aspx", "pageUrl": "https://contoso.sharepoint.com/sites/mySite/SitePages/myPage.aspx",
"customActions": { "customActions": {
"abc3361f-bb2d-491f-aba3-cd51c19a299b": { "c0abbffb-355d-4d4e-bd38-9e15fb811506": {
"location": "ClientSideExtension.ApplicationCustomizer", "location": "ClientSideExtension.ApplicationCustomizer",
"properties": { "properties": {
"testMessage": "Test message" "testMessage": "Test message"

View File

@@ -17,90 +17,99 @@ param(
Add-PSSnapin Microsoft.SharePoint.PowerShell -ErrorAction SilentlyContinue | Out-Null Add-PSSnapin Microsoft.SharePoint.PowerShell -ErrorAction SilentlyContinue | Out-Null
$componentId = 'abc3361f-bb2d-491f-aba3-cd51c19a299b' $componentId = [Guid]'c0abbffb-355d-4d4e-bd38-9e15fb811506'
$legacyComponentId = [Guid]'abc3361f-bb2d-491f-aba3-cd51c19a299b'
$location = 'ClientSideExtension.ApplicationCustomizer' $location = 'ClientSideExtension.ApplicationCustomizer'
$name = 'MegaMenu' $configurationProperty = 'PortalSettings.MegaMenu.Configuration'
$title = 'Mega Menu'
function Get-MegaMenuPropertiesJson { $properties = @{
param( termSetName = $TermSetName
[string]$CurrentTermSetName, termSetId = $TermSetId
[string]$CurrentTermSetId, cacheMinutes = $CacheMinutes
[int]$CurrentCacheMinutes, debug = [bool]$EnableDebug.IsPresent
[bool]$CurrentDebug } | ConvertTo-Json -Depth 10 -Compress
$site = Get-SPSite -Identity $SiteUrl
try {
$rootWeb = $site.RootWeb
$rootWeb.AllProperties[$configurationProperty] = $properties
$rootWeb.Update()
$siteActions = @(
$site.UserCustomActions |
Where-Object {
$_.ClientSideComponentId -eq $componentId -or
$_.ClientSideComponentId -eq $legacyComponentId
}
) )
return (@{ foreach ($siteAction in $siteActions) {
termSetName = $CurrentTermSetName $siteAction.Delete()
termSetId = $CurrentTermSetId }
cacheMinutes = $CurrentCacheMinutes
debug = $CurrentDebug
} | ConvertTo-Json -Depth 10 -Compress)
}
function Remove-WebScopedComponentActions { $created = 0
param( $updated = 0
[Microsoft.SharePoint.SPSite]$CurrentSite, $removedLegacy = 0
[string]$CurrentComponentId,
[string]$CurrentLocation
)
$removedCount = 0 foreach ($web in $site.AllWebs) {
foreach ($web in $CurrentSite.AllWebs) {
try { try {
$webActions = @($web.UserCustomActions | Where-Object { $actions = @(
$_.Location -eq $CurrentLocation -and $web.UserCustomActions |
$_.ClientSideComponentId -and Where-Object {
$_.ClientSideComponentId.ToString().ToLower() -eq $CurrentComponentId $_.ClientSideComponentId -eq $componentId -or
}) $_.ClientSideComponentId -eq $legacyComponentId
}
)
foreach ($webAction in $webActions) { $currentAction = $actions |
$web.UserCustomActions.Delete($webAction.Id) Where-Object {
$removedCount++ $_.ClientSideComponentId -eq $componentId
} |
Select-Object -First 1
foreach ($existingAction in $actions) {
if ($currentAction -and $existingAction.Id -eq $currentAction.Id) {
continue
}
$existingAction.Delete()
$removedLegacy++
} }
if ($webActions.Count -gt 0) { if (-not $currentAction) {
$web.Update() $currentAction = $web.UserCustomActions.Add()
$currentAction.Name = [Guid]::NewGuid().ToString('B')
$created++
} }
else {
$updated++
}
$currentAction.Title = 'MegaMenu Application Customizer'
$currentAction.Description = $Description
$currentAction.Location = $location
$currentAction.ClientSideComponentId = $componentId
$currentAction.ClientSideComponentProperties = $properties
$currentAction.Sequence = 100
$currentAction.Update()
$web.Update()
Write-Host ('MegaMenu synchronisiert: ' + $web.Url) -ForegroundColor Green
} }
finally { finally {
$web.Dispose() $web.Dispose()
} }
} }
return $removedCount Write-Host ''
} Write-Host 'MegaMenu wurde fuer die gesamte Site Collection konfiguriert.' -ForegroundColor Green
Write-Host ('Zentrale Konfiguration: ' + $configurationProperty)
$site = Get-SPSite -Identity $SiteUrl Write-Host ('Neu registrierte Webs: ' + $created)
try { Write-Host ('Aktualisierte Webs: ' + $updated)
$removedWebScopedActions = Remove-WebScopedComponentActions -CurrentSite $site -CurrentComponentId $componentId -CurrentLocation $location Write-Host ('Entfernte alte oder doppelte Registrierungen: ' + $removedLegacy)
Write-Host ('Properties: ' + $properties)
$existingAction = $site.UserCustomActions | Where-Object { Write-Host 'Neue Unterwebs koennen durch erneutes Ausfuehren idempotent synchronisiert werden.' -ForegroundColor Yellow
$_.Location -eq $location -and $_.ClientSideComponentId -and $_.ClientSideComponentId.ToString().ToLower() -eq $componentId
} | Select-Object -First 1
if ($existingAction) {
$action = $existingAction
Write-Host 'Aktualisiere vorhandene site-scoped Mega Menu UserCustomAction...' -ForegroundColor Yellow
}
else {
$action = $site.UserCustomActions.Add()
Write-Host 'Erstelle neue site-scoped Mega Menu UserCustomAction...' -ForegroundColor Yellow
}
$action.Name = $name
$action.Title = $title
$action.Description = $Description
$action.Location = $location
$action.ClientSideComponentId = [Guid]$componentId
$action.ClientSideComponentProperties = Get-MegaMenuPropertiesJson -CurrentTermSetName $TermSetName -CurrentTermSetId $TermSetId -CurrentCacheMinutes $CacheMinutes -CurrentDebug ([bool]$EnableDebug.IsPresent)
$action.Update()
Write-Host 'Mega Menu wurde site-scoped registriert.' -ForegroundColor Green
Write-Host ('Entfernte web-scoped Eintraege: ' + $removedWebScopedActions)
Write-Host ('Beschreibung: ' + $Description)
Write-Host ('Properties: ' + $action.ClientSideComponentProperties)
} }
finally { finally {
if ($site) { if ($site) {

2
package-lock.json generated
View File

@@ -1,6 +1,6 @@
{ {
"name": "mega-menu", "name": "mega-menu",
"version": "1.1.4", "version": "2.0.0",
"lockfileVersion": 1, "lockfileVersion": 1,
"requires": true, "requires": true,
"dependencies": { "dependencies": {

View File

@@ -1,6 +1,6 @@
{ {
"name": "mega-menu", "name": "mega-menu",
"version": "1.1.4", "version": "2.0.0",
"private": true, "private": true,
"main": "lib/index.js", "main": "lib/index.js",
"engines": { "engines": {

View File

@@ -3,6 +3,6 @@
<CustomAction <CustomAction
Title="MegaMenu Application Customizer" Title="MegaMenu Application Customizer"
Location="ClientSideExtension.ApplicationCustomizer" Location="ClientSideExtension.ApplicationCustomizer"
ClientSideComponentId="abc3361f-bb2d-491f-aba3-cd51c19a299b" ClientSideComponentId="c0abbffb-355d-4d4e-bd38-9e15fb811506"
ClientSideComponentProperties="{}" /> ClientSideComponentProperties="{}" />
</Elements> </Elements>

View File

@@ -1,7 +1,7 @@
{ {
"$schema": "https://developer.microsoft.com/json-schemas/spfx/client-side-extension-manifest.schema.json", "$schema": "https://developer.microsoft.com/json-schemas/spfx/client-side-extension-manifest.schema.json",
"id": "abc3361f-bb2d-491f-aba3-cd51c19a299b", "id": "c0abbffb-355d-4d4e-bd38-9e15fb811506",
"alias": "MegaMenuApplicationCustomizer", "alias": "MegaMenuApplicationCustomizer",
"componentType": "Extension", "componentType": "Extension",
"extensionType": "ApplicationCustomizer", "extensionType": "ApplicationCustomizer",

View File

@@ -19,7 +19,7 @@ import { debugError, debugLog, debugWarn } from '../../services/MegaMenuDebug';
import './MegaMenu.module.scss'; import './MegaMenu.module.scss';
const LOG_SOURCE: string = 'MegaMenuApplicationCustomizer'; const LOG_SOURCE: string = 'MegaMenuApplicationCustomizer';
export const UserCustomActionMegaMenuId: string = 'abc3361f-bb2d-491f-aba3-cd51c19a299b'; export const UserCustomActionMegaMenuId: string = 'c0abbffb-355d-4d4e-bd38-9e15fb811506';
export interface IMegaMenuApplicationCustomizerProperties { export interface IMegaMenuApplicationCustomizerProperties {
termSetName?: string; termSetName?: string;
@@ -40,7 +40,7 @@ export default class MegaMenuApplicationCustomizer
const debug: boolean = this._isDebugEnabled(); const debug: boolean = this._isDebugEnabled();
debugLog(debug, LOG_SOURCE, 'onInit started.', { debugLog(debug, LOG_SOURCE, 'onInit started.', {
componentId: UserCustomActionMegaMenuId, componentId: UserCustomActionMegaMenuId,
version: '1.1.4', version: '2.0.0',
properties: this.properties || {}, properties: this.properties || {},
pageUrl: window.location.href, pageUrl: window.location.href,
webUrl: this.context.pageContext.web.absoluteUrl, webUrl: this.context.pageContext.web.absoluteUrl,