dev-3.0 #1
@@ -6,7 +6,7 @@ PortalSettings V3 ist ein providerbasiertes SPFx-WebPart für SharePoint Server
|
|||||||
|
|
||||||
| Eigenschaft | Wert |
|
| Eigenschaft | Wert |
|
||||||
|---|---|
|
|---|---|
|
||||||
| Version | 3.0.1 |
|
| Version | 3.0.2 |
|
||||||
| SharePoint Framework | 1.4.1 |
|
| SharePoint Framework | 1.4.1 |
|
||||||
| Build-Node | 8.17.0 |
|
| Build-Node | 8.17.0 |
|
||||||
| npm | 6.x |
|
| npm | 6.x |
|
||||||
@@ -155,7 +155,7 @@ sharepoint/solution/portal-settings.sppkg
|
|||||||
|
|
||||||
## Installation
|
## 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.
|
2. Die App im Root Web der gewünschten Site Collection installieren oder aktualisieren.
|
||||||
3. Eine moderne Seite erstellen.
|
3. Eine moderne Seite erstellen.
|
||||||
4. Das WebPart **Portal Settings** einmal auf der Seite hinzufügen.
|
4. Das WebPart **Portal Settings** einmal auf der Seite hinzufügen.
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
Stand: 21.07.2026
|
Stand: 21.07.2026
|
||||||
Branch: `dev-3.0`
|
Branch: `dev-3.0`
|
||||||
Zielversion: 3.0.1
|
Zielversion: 3.0.2
|
||||||
|
|
||||||
## Architektur
|
## Architektur
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
"solution": {
|
"solution": {
|
||||||
"name": "portal-settings-client-side-solution",
|
"name": "portal-settings-client-side-solution",
|
||||||
"id": "1c98e32d-bb7b-43b7-9625-074d6e4ea286",
|
"id": "1c98e32d-bb7b-43b7-9625-074d6e4ea286",
|
||||||
"version": "3.0.1.0",
|
"version": "3.0.2.0",
|
||||||
"includeClientSideAssets": true,
|
"includeClientSideAssets": true,
|
||||||
"skipFeatureDeployment": false
|
"skipFeatureDeployment": false
|
||||||
},
|
},
|
||||||
|
|||||||
Generated
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "portal-settings",
|
"name": "portal-settings",
|
||||||
"version": "3.0.1",
|
"version": "3.0.2",
|
||||||
"lockfileVersion": 1,
|
"lockfileVersion": 1,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "portal-settings",
|
"name": "portal-settings",
|
||||||
"version": "3.0.1",
|
"version": "3.0.2",
|
||||||
"private": true,
|
"private": true,
|
||||||
"main": "lib/index.js",
|
"main": "lib/index.js",
|
||||||
"engines": {
|
"engines": {
|
||||||
|
|||||||
@@ -1,6 +1,12 @@
|
|||||||
import { ISettingsProvider } from '../core/ProviderContracts';
|
import { ISettingsProvider } from '../core/ProviderContracts';
|
||||||
|
|
||||||
export interface IMegaMenuSettings {
|
export interface IMegaMenuSettings {
|
||||||
|
schemaVersion: number;
|
||||||
|
portalSettings: {
|
||||||
|
providerKey: string;
|
||||||
|
contractVersion: number;
|
||||||
|
minimumPortalSettingsVersion: string;
|
||||||
|
};
|
||||||
termSetId: string;
|
termSetId: string;
|
||||||
termSetName: string;
|
termSetName: string;
|
||||||
cacheMinutes: number;
|
cacheMinutes: number;
|
||||||
@@ -21,7 +27,16 @@ export const MegaMenuSettingsProvider: ISettingsProvider<IMegaMenuSettings> = {
|
|||||||
'c0abbffb-355d-4d4e-bd38-9e15fb811506',
|
'c0abbffb-355d-4d4e-bd38-9e15fb811506',
|
||||||
'abc3361f-bb2d-491f-aba3-cd51c19a299b'
|
'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: [
|
sections: [
|
||||||
{
|
{
|
||||||
key: 'navigation',
|
key: 'navigation',
|
||||||
@@ -89,6 +104,12 @@ export const MegaMenuSettingsProvider: ISettingsProvider<IMegaMenuSettings> = {
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
createDefault: (): IMegaMenuSettings => ({
|
createDefault: (): IMegaMenuSettings => ({
|
||||||
|
schemaVersion: 1,
|
||||||
|
portalSettings: {
|
||||||
|
providerKey: 'megamenu',
|
||||||
|
contractVersion: 1,
|
||||||
|
minimumPortalSettingsVersion: '3.0.0'
|
||||||
|
},
|
||||||
termSetId: '', termSetName: '', cacheMinutes: 15, cacheVersion: '1', menuMode: 'megaMenu', debug: false
|
termSetId: '', termSetName: '', cacheMinutes: 15, cacheVersion: '1', menuMode: 'megaMenu', debug: false
|
||||||
}),
|
}),
|
||||||
normalize: (value: any): IMegaMenuSettings => { // tslint:disable-line:no-any
|
normalize: (value: any): IMegaMenuSettings => { // tslint:disable-line:no-any
|
||||||
@@ -96,6 +117,12 @@ export const MegaMenuSettingsProvider: ISettingsProvider<IMegaMenuSettings> = {
|
|||||||
const minutes: number = Number(source.cacheMinutes);
|
const minutes: number = Number(source.cacheMinutes);
|
||||||
const result: any = {}; // tslint:disable-line:no-any
|
const result: any = {}; // tslint:disable-line:no-any
|
||||||
Object.keys(source).forEach((key: string): void => { result[key] = source[key]; });
|
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.termSetId = typeof source.termSetId === 'string' ? source.termSetId.trim() : '';
|
||||||
result.termSetName = typeof source.termSetName === 'string' ? source.termSetName.trim() : '';
|
result.termSetName = typeof source.termSetName === 'string' ? source.termSetName.trim() : '';
|
||||||
result.cacheMinutes = isFinite(minutes) ? Math.min(Math.max(Math.floor(minutes), 1), 1440) : 15;
|
result.cacheMinutes = isFinite(minutes) ? Math.min(Math.max(Math.floor(minutes), 1), 1440) : 15;
|
||||||
|
|||||||
@@ -267,7 +267,7 @@ export class PortalSettingsDataService {
|
|||||||
return this.context.spHttpClient.post(this.siteUrl + '/_api/site/UserCustomActions', SPHttpClient.configurations.v1, {
|
return this.context.spHttpClient.post(this.siteUrl + '/_api/site/UserCustomActions', SPHttpClient.configurations.v1, {
|
||||||
headers: this.writeHeaders(), body: JSON.stringify(body)
|
headers: this.writeHeaders(), body: JSON.stringify(body)
|
||||||
}).then((response: SPHttpClientResponse): Promise<any> => this.ensureJson(response,
|
}).then((response: SPHttpClientResponse): Promise<any> => 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 => {
|
.then((payload: any): IUserCustomActionInfo => {
|
||||||
const source: any = payload.d || payload;
|
const source: any = payload.d || payload;
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -125,7 +125,7 @@ export class PortalSettingsApp {
|
|||||||
if (!item.registered) {
|
if (!item.registered) {
|
||||||
const registrationNotice: HTMLElement = createElement('div', 'ps3-status ps3-status-warning');
|
const registrationNotice: HTMLElement = createElement('div', 'ps3-status ps3-status-warning');
|
||||||
registrationNotice.setAttribute('role', 'status');
|
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);
|
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;
|
const reset: HTMLButtonElement = createElement('button', 'ps3-button ps3-button-secondary', 'Änderungen verwerfen') as HTMLButtonElement;
|
||||||
reset.type = 'button'; reset.addEventListener('click', (): void => this.resetCurrent());
|
reset.type = 'button'; reset.addEventListener('click', (): void => this.resetCurrent());
|
||||||
this.saveButton = createElement('button', 'ps3-button ps3-button-primary',
|
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());
|
this.saveButton.type = 'button'; this.saveButton.addEventListener('click', (): void => this.saveCurrent());
|
||||||
actions.appendChild(reset); actions.appendChild(this.saveButton); this.contentHost.appendChild(actions);
|
actions.appendChild(reset); actions.appendChild(this.saveButton); this.contentHost.appendChild(actions);
|
||||||
this.updateSaveState();
|
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('h2', '', failed ? 'PortalSettings konnte nicht geladen werden' : 'Keine aktiven Provider gefunden'));
|
||||||
empty.appendChild(createElement('p', '', failed
|
empty.appendChild(createElement('p', '', failed
|
||||||
? 'Prüfen Sie die Statusmeldung und die REST-Berechtigungen.'
|
? '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);
|
this.contentHost.appendChild(empty);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -164,13 +164,13 @@ export class PortalSettingsApp {
|
|||||||
if (errors.length) { this.showStatus(errors.join(' '), 'danger', true); return; }
|
if (errors.length) { this.showStatus(errors.join(' '), 'danger', true); return; }
|
||||||
const question: string = wasRegistered
|
const question: string = wasRegistered
|
||||||
? 'Konfiguration für „' + item.provider.displayName + '“ speichern?'
|
? '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; }
|
if (!window.confirm(question)) { return; }
|
||||||
this.saveButton.disabled = true; this.showStatus('Konfiguration wird gespeichert …', 'info', true);
|
this.saveButton.disabled = true; this.showStatus('Konfiguration wird gespeichert …', 'info', true);
|
||||||
this.dataService.save(item).then((): void => {
|
this.dataService.save(item).then((): void => {
|
||||||
this.showStatus(wasRegistered
|
this.showStatus(wasRegistered
|
||||||
? 'Die Konfiguration wurde erfolgreich gespeichert.'
|
? '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();
|
this.renderTabs(); this.renderActive();
|
||||||
}).catch((error: Error): void => {
|
}).catch((error: Error): void => {
|
||||||
this.showStatus(error.message || String(error), 'danger', true); this.updateSaveState();
|
this.showStatus(error.message || String(error), 'danger', true); this.updateSaveState();
|
||||||
|
|||||||
@@ -18,7 +18,6 @@ $green: "[theme: green, default: #107c10]";
|
|||||||
.ps3-root {
|
.ps3-root {
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
background: $neutral-lighter-alt;
|
|
||||||
color: $neutral-primary;
|
color: $neutral-primary;
|
||||||
font-family: 'Segoe UI', 'Segoe UI Web (West European)', sans-serif;
|
font-family: 'Segoe UI', 'Segoe UI Web (West European)', sans-serif;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,10 +21,14 @@ providers.forEach((provider: ISettingsProvider<any>): void => { // tslint:disabl
|
|||||||
});
|
});
|
||||||
|
|
||||||
const mega: ISettingsProvider<any> = getSettingsProvider('megamenu'); // tslint:disable-line:no-any
|
const mega: ISettingsProvider<any> = 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
|
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.cacheMinutes === 1440, 'MegaMenu Cache-Maximum greift nicht.');
|
||||||
assert(normalizedMega.cacheVersion === '1', 'MegaMenu Cache-Fallback fehlt.');
|
assert(normalizedMega.cacheVersion === '1', 'MegaMenu Cache-Fallback fehlt.');
|
||||||
assert(normalizedMega.menuMode === 'megaMenu', 'MegaMenu Modus-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.');
|
assert(normalizedMega.unknownFuture === 'keep', 'Unbekannte MegaMenu-Properties gehen verloren.');
|
||||||
|
|
||||||
const branding: ISettingsProvider<any> = getSettingsProvider('custombranding'); // tslint:disable-line:no-any
|
const branding: ISettingsProvider<any> = getSettingsProvider('custombranding'); // tslint:disable-line:no-any
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ if ($errors.Count -gt 0) {
|
|||||||
$jsonFiles = @(
|
$jsonFiles = @(
|
||||||
'package.json',
|
'package.json',
|
||||||
'config\config.json',
|
'config\config.json',
|
||||||
|
'config\copy-assets.json',
|
||||||
'config\package-solution.json',
|
'config\package-solution.json',
|
||||||
'config\serve.json',
|
'config\serve.json',
|
||||||
'src\webparts\portalSettings\PortalSettingsWebPart.manifest.json'
|
'src\webparts\portalSettings\PortalSettingsWebPart.manifest.json'
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ function assert(condition, message) { if (!condition) { throw new Error(message)
|
|||||||
var packageJson = json('package.json');
|
var packageJson = json('package.json');
|
||||||
var solution = json('config/package-solution.json').solution;
|
var solution = json('config/package-solution.json').solution;
|
||||||
var config = json('config/config.json');
|
var config = json('config/config.json');
|
||||||
|
var copyAssets = json('config/copy-assets.json');
|
||||||
var manifest = json('src/webparts/portalSettings/PortalSettingsWebPart.manifest.json');
|
var manifest = json('src/webparts/portalSettings/PortalSettingsWebPart.manifest.json');
|
||||||
var styles = read('src/webparts/portalSettings/PortalSettings.scss');
|
var styles = read('src/webparts/portalSettings/PortalSettings.scss');
|
||||||
var sourceFiles = [
|
var sourceFiles = [
|
||||||
@@ -20,10 +21,11 @@ var sourceFiles = [
|
|||||||
'src/ui/PortalSettingsApp.ts'
|
'src/ui/PortalSettingsApp.ts'
|
||||||
].map(read).join('\n');
|
].map(read).join('\n');
|
||||||
|
|
||||||
assert(packageJson.version === '3.0.1', 'Package-Version ist nicht 3.0.1.');
|
assert(packageJson.version === '3.0.2', 'Package-Version ist nicht 3.0.2.');
|
||||||
assert(solution.version === '3.0.1.0', 'Solution-Version ist nicht 3.0.1.0.');
|
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(!solution.features, 'Alte ASPX-Feature-Assets sind noch registriert.');
|
||||||
assert(!!config.bundles['portal-settings-web-part'], 'WebPart-Bundle fehlt.');
|
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(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/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.');
|
assert(!fs.existsSync(path.join(root, 'src/extensions/portalSettings/PortalSettingsApplicationCustomizer.ts')), 'Alter Application Customizer ist noch vorhanden.');
|
||||||
|
|||||||
Reference in New Issue
Block a user