From 64a157f7a88db452cc5132b2dc5dee4ea66774da Mon Sep 17 00:00:00 2001 From: Torsten Brendgen Date: Tue, 18 Aug 2026 14:53:03 +0200 Subject: [PATCH] =?UTF-8?q?Aktualisiere=20Versionsnummer=20auf=203.0.2=20i?= =?UTF-8?q?n=20README.md,=20package.json,=20package-lock.json=20und=20pack?= =?UTF-8?q?age-solution.json.=20F=C3=BCge=20neue=20Eigenschaften=20in=20IM?= =?UTF-8?q?egaMenuSettings=20hinzu=20und=20verbessere=20die=20Logik=20zur?= =?UTF-8?q?=20Handhabung=20von=20CustomBranding.=20Aktualisiere=20Tests=20?= =?UTF-8?q?zur=20Validierung=20der=20=C3=84nderungen.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 4 +-- ToDo.md | 2 +- config/package-solution.json | 2 +- package-lock.json | 2 +- package.json | 2 +- src/providers/MegaMenuSettingsProvider.ts | 29 ++++++++++++++++++- src/services/PortalSettingsDataService.ts | 2 +- src/ui/PortalSettingsApp.ts | 10 +++---- .../portalSettings/PortalSettings.scss | 1 - tests/ProviderRegistry.test.ts | 4 +++ tests/validate-project.ps1 | 1 + tests/validate-static-assets.js | 6 ++-- 12 files changed, 49 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index f7feb34..3753a29 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ PortalSettings V3 ist ein providerbasiertes SPFx-WebPart für SharePoint Server | Eigenschaft | Wert | |---|---| -| Version | 3.0.1 | +| Version | 3.0.2 | | SharePoint Framework | 1.4.1 | | Build-Node | 8.17.0 | | npm | 6.x | @@ -155,7 +155,7 @@ sharepoint/solution/portal-settings.sppkg ## Installation -1. `portal-settings.sppkg` im App Catalog durch Version `3.0.1.0` ersetzen. +1. `portal-settings.sppkg` im App Catalog durch Version `3.0.2.0` ersetzen. 2. Die App im Root Web der gewünschten Site Collection installieren oder aktualisieren. 3. Eine moderne Seite erstellen. 4. Das WebPart **Portal Settings** einmal auf der Seite hinzufügen. diff --git a/ToDo.md b/ToDo.md index 907729e..dbd7d66 100644 --- a/ToDo.md +++ b/ToDo.md @@ -2,7 +2,7 @@ Stand: 21.07.2026 Branch: `dev-3.0` -Zielversion: 3.0.1 +Zielversion: 3.0.2 ## Architektur diff --git a/config/package-solution.json b/config/package-solution.json index dfaf853..31725ee 100644 --- a/config/package-solution.json +++ b/config/package-solution.json @@ -3,7 +3,7 @@ "solution": { "name": "portal-settings-client-side-solution", "id": "1c98e32d-bb7b-43b7-9625-074d6e4ea286", - "version": "3.0.1.0", + "version": "3.0.2.0", "includeClientSideAssets": true, "skipFeatureDeployment": false }, diff --git a/package-lock.json b/package-lock.json index e012525..f95df1c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "portal-settings", - "version": "3.0.1", + "version": "3.0.2", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 4eaa65c..c074eb9 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "portal-settings", - "version": "3.0.1", + "version": "3.0.2", "private": true, "main": "lib/index.js", "engines": { diff --git a/src/providers/MegaMenuSettingsProvider.ts b/src/providers/MegaMenuSettingsProvider.ts index e4d1a6c..44acdd4 100644 --- a/src/providers/MegaMenuSettingsProvider.ts +++ b/src/providers/MegaMenuSettingsProvider.ts @@ -1,6 +1,12 @@ import { ISettingsProvider } from '../core/ProviderContracts'; export interface IMegaMenuSettings { + schemaVersion: number; + portalSettings: { + providerKey: string; + contractVersion: number; + minimumPortalSettingsVersion: string; + }; termSetId: string; termSetName: string; cacheMinutes: number; @@ -21,7 +27,16 @@ export const MegaMenuSettingsProvider: ISettingsProvider = { 'c0abbffb-355d-4d4e-bd38-9e15fb811506', 'abc3361f-bb2d-491f-aba3-cd51c19a299b' ], - storage: { kind: 'siteUserCustomAction' }, + storage: { + kind: 'siteUserCustomAction', + action: { + name: 'MegaMenu', + title: 'MegaMenu Application Customizer', + description: 'MSFT-Custom-Solution:MegaMenu', + location: 'ClientSideExtension.ApplicationCustomizer', + sequence: 100 + } + }, sections: [ { key: 'navigation', @@ -89,6 +104,12 @@ export const MegaMenuSettingsProvider: ISettingsProvider = { } ], createDefault: (): IMegaMenuSettings => ({ + schemaVersion: 1, + portalSettings: { + providerKey: 'megamenu', + contractVersion: 1, + minimumPortalSettingsVersion: '3.0.0' + }, termSetId: '', termSetName: '', cacheMinutes: 15, cacheVersion: '1', menuMode: 'megaMenu', debug: false }), normalize: (value: any): IMegaMenuSettings => { // tslint:disable-line:no-any @@ -96,6 +117,12 @@ export const MegaMenuSettingsProvider: ISettingsProvider = { const minutes: number = Number(source.cacheMinutes); const result: any = {}; // tslint:disable-line:no-any Object.keys(source).forEach((key: string): void => { result[key] = source[key]; }); + result.schemaVersion = 1; + result.portalSettings = { + providerKey: 'megamenu', + contractVersion: 1, + minimumPortalSettingsVersion: '3.0.0' + }; result.termSetId = typeof source.termSetId === 'string' ? source.termSetId.trim() : ''; result.termSetName = typeof source.termSetName === 'string' ? source.termSetName.trim() : ''; result.cacheMinutes = isFinite(minutes) ? Math.min(Math.max(Math.floor(minutes), 1), 1440) : 15; diff --git a/src/services/PortalSettingsDataService.ts b/src/services/PortalSettingsDataService.ts index de08a97..eb22779 100644 --- a/src/services/PortalSettingsDataService.ts +++ b/src/services/PortalSettingsDataService.ts @@ -267,7 +267,7 @@ export class PortalSettingsDataService { return this.context.spHttpClient.post(this.siteUrl + '/_api/site/UserCustomActions', SPHttpClient.configurations.v1, { headers: this.writeHeaders(), body: JSON.stringify(body) }).then((response: SPHttpClientResponse): Promise => this.ensureJson(response, - 'CustomBranding konnte nicht für die Site Collection registriert werden.')) + provider.displayName + ' konnte nicht für die Site Collection registriert werden.')) .then((payload: any): IUserCustomActionInfo => { const source: any = payload.d || payload; return { diff --git a/src/ui/PortalSettingsApp.ts b/src/ui/PortalSettingsApp.ts index b0153d2..78815ec 100644 --- a/src/ui/PortalSettingsApp.ts +++ b/src/ui/PortalSettingsApp.ts @@ -125,7 +125,7 @@ export class PortalSettingsApp { if (!item.registered) { const registrationNotice: HTMLElement = createElement('div', 'ps3-status ps3-status-warning'); registrationNotice.setAttribute('role', 'status'); - registrationNotice.textContent = 'CustomBranding ist noch nicht zentral registriert. Beim ersten Speichern legt PortalSettings die Site-Collection-Action an. Das CustomBranding-Paket muss zuvor im App Catalog bereitgestellt sein.'; + registrationNotice.textContent = item.provider.displayName + ' ist noch nicht zentral registriert. Beim ersten Speichern legt PortalSettings die Site-Collection-Action an. Das zugehörige SPFx-Paket muss zuvor im App Catalog bereitgestellt sein.'; this.contentHost.appendChild(registrationNotice); } @@ -140,7 +140,7 @@ export class PortalSettingsApp { const reset: HTMLButtonElement = createElement('button', 'ps3-button ps3-button-secondary', 'Änderungen verwerfen') as HTMLButtonElement; reset.type = 'button'; reset.addEventListener('click', (): void => this.resetCurrent()); this.saveButton = createElement('button', 'ps3-button ps3-button-primary', - item.registered ? 'Konfiguration speichern' : 'CustomBranding aktivieren') as HTMLButtonElement; + item.registered ? 'Konfiguration speichern' : item.provider.displayName + ' aktivieren') as HTMLButtonElement; this.saveButton.type = 'button'; this.saveButton.addEventListener('click', (): void => this.saveCurrent()); actions.appendChild(reset); actions.appendChild(this.saveButton); this.contentHost.appendChild(actions); this.updateSaveState(); @@ -152,7 +152,7 @@ export class PortalSettingsApp { empty.appendChild(createElement('h2', '', failed ? 'PortalSettings konnte nicht geladen werden' : 'Keine aktiven Provider gefunden')); empty.appendChild(createElement('p', '', failed ? 'Prüfen Sie die Statusmeldung und die REST-Berechtigungen.' - : 'MegaMenu wird über seine zentrale Site-Collection-Action erkannt. CustomBranding kann direkt aktiviert werden; ExpiryIndicator wird über seinen Descriptor oder vorhandene Bindungen erkannt.')); + : 'MegaMenu und CustomBranding können direkt für die Site Collection aktiviert werden; ExpiryIndicator wird über seinen Descriptor oder vorhandene Bindungen erkannt.')); this.contentHost.appendChild(empty); } @@ -164,13 +164,13 @@ export class PortalSettingsApp { if (errors.length) { this.showStatus(errors.join(' '), 'danger', true); return; } const question: string = wasRegistered ? 'Konfiguration für „' + item.provider.displayName + '“ speichern?' - : 'CustomBranding zentral für diese Site Collection aktivieren?'; + : item.provider.displayName + ' zentral für diese Site Collection aktivieren?'; if (!window.confirm(question)) { return; } this.saveButton.disabled = true; this.showStatus('Konfiguration wird gespeichert …', 'info', true); this.dataService.save(item).then((): void => { this.showStatus(wasRegistered ? 'Die Konfiguration wurde erfolgreich gespeichert.' - : 'CustomBranding wurde zentral aktiviert und gespeichert.', 'success'); + : item.provider.displayName + ' wurde zentral aktiviert und gespeichert.', 'success'); this.renderTabs(); this.renderActive(); }).catch((error: Error): void => { this.showStatus(error.message || String(error), 'danger', true); this.updateSaveState(); diff --git a/src/webparts/portalSettings/PortalSettings.scss b/src/webparts/portalSettings/PortalSettings.scss index 1e9755b..397ed49 100644 --- a/src/webparts/portalSettings/PortalSettings.scss +++ b/src/webparts/portalSettings/PortalSettings.scss @@ -18,7 +18,6 @@ $green: "[theme: green, default: #107c10]"; .ps3-root { box-sizing: border-box; width: 100%; - background: $neutral-lighter-alt; color: $neutral-primary; font-family: 'Segoe UI', 'Segoe UI Web (West European)', sans-serif; } diff --git a/tests/ProviderRegistry.test.ts b/tests/ProviderRegistry.test.ts index 4adbfa7..8e46f67 100644 --- a/tests/ProviderRegistry.test.ts +++ b/tests/ProviderRegistry.test.ts @@ -21,10 +21,14 @@ providers.forEach((provider: ISettingsProvider): void => { // tslint:disabl }); const mega: ISettingsProvider = getSettingsProvider('megamenu'); // tslint:disable-line:no-any +assert(!!mega.storage.action, 'MegaMenu-Aktivierungsdefinition fehlt.'); +assert(mega.storage.action.name === 'MegaMenu', 'MegaMenu Action-Name ist nicht stabil.'); +assert(mega.storage.action.location === 'ClientSideExtension.ApplicationCustomizer', 'MegaMenu Action-Location ist ungültig.'); const normalizedMega: any = mega.normalize({ cacheMinutes: 5000, cacheVersion: '', unknownFuture: 'keep', menuMode: 'other' }); // tslint:disable-line:no-any assert(normalizedMega.cacheMinutes === 1440, 'MegaMenu Cache-Maximum greift nicht.'); assert(normalizedMega.cacheVersion === '1', 'MegaMenu Cache-Fallback fehlt.'); assert(normalizedMega.menuMode === 'megaMenu', 'MegaMenu Modus-Fallback fehlt.'); +assert(normalizedMega.portalSettings.providerKey === 'megamenu', 'MegaMenu-Vertragsmarker fehlt.'); assert(normalizedMega.unknownFuture === 'keep', 'Unbekannte MegaMenu-Properties gehen verloren.'); const branding: ISettingsProvider = getSettingsProvider('custombranding'); // tslint:disable-line:no-any diff --git a/tests/validate-project.ps1 b/tests/validate-project.ps1 index b7fb6a7..a0e0f46 100644 --- a/tests/validate-project.ps1 +++ b/tests/validate-project.ps1 @@ -15,6 +15,7 @@ if ($errors.Count -gt 0) { $jsonFiles = @( 'package.json', 'config\config.json', + 'config\copy-assets.json', 'config\package-solution.json', 'config\serve.json', 'src\webparts\portalSettings\PortalSettingsWebPart.manifest.json' diff --git a/tests/validate-static-assets.js b/tests/validate-static-assets.js index 472660d..60ddb4e 100644 --- a/tests/validate-static-assets.js +++ b/tests/validate-static-assets.js @@ -12,6 +12,7 @@ function assert(condition, message) { if (!condition) { throw new Error(message) var packageJson = json('package.json'); var solution = json('config/package-solution.json').solution; var config = json('config/config.json'); +var copyAssets = json('config/copy-assets.json'); var manifest = json('src/webparts/portalSettings/PortalSettingsWebPart.manifest.json'); var styles = read('src/webparts/portalSettings/PortalSettings.scss'); var sourceFiles = [ @@ -20,10 +21,11 @@ var sourceFiles = [ 'src/ui/PortalSettingsApp.ts' ].map(read).join('\n'); -assert(packageJson.version === '3.0.1', 'Package-Version ist nicht 3.0.1.'); -assert(solution.version === '3.0.1.0', 'Solution-Version ist nicht 3.0.1.0.'); +assert(packageJson.version === '3.0.2', 'Package-Version ist nicht 3.0.2.'); +assert(solution.version === '3.0.2.0', 'Solution-Version ist nicht 3.0.2.0.'); assert(!solution.features, 'Alte ASPX-Feature-Assets sind noch registriert.'); assert(!!config.bundles['portal-settings-web-part'], 'WebPart-Bundle fehlt.'); +assert(copyAssets.deployCdnPath === 'temp/deploy', 'Ship-Assets werden nicht nach temp/deploy geschrieben.'); assert(manifest.componentType === 'WebPart', 'Komponente ist kein WebPart.'); assert(!fs.existsSync(path.join(root, 'src/assets/PortalSettings.aspx')), 'Alte ASPX-Seite ist noch vorhanden.'); assert(!fs.existsSync(path.join(root, 'src/extensions/portalSettings/PortalSettingsApplicationCustomizer.ts')), 'Alter Application Customizer ist noch vorhanden.');