chore: update version to 2.0.1 and enhance Classic functionality with dynamic field discovery

This commit is contained in:
Torsten Brendgen
2026-07-18 20:02:49 +02:00
parent 0d6768e24c
commit 0d6da3451d
11 changed files with 71 additions and 20 deletions

View File

@@ -336,7 +336,7 @@ neu laden.
Version 2.0 legt bei der App-Installation `ExpiryIndicatorClassic.js` unter Version 2.0 legt bei der App-Installation `ExpiryIndicatorClassic.js` unter
`SiteAssets/ExpiryIndicator` ab. Das Aktivierungsskript registriert standardmäßig für die angegebene Liste: `SiteAssets/ExpiryIndicator` ab. Das Aktivierungsskript registriert standardmäßig für die angegebene Liste:
- einen listenspezifischen `ScriptLink` für CSR/JSLink, - einen einmaligen Web-`ScriptLink` für CSR/JSLink, der das gebundene Feld der aktuellen Liste selbst erkennt,
- die Berechnung, Beschriftung und den Zeilenverlauf in der klassischen Ansicht, - die Berechnung, Beschriftung und den Zeilenverlauf in der klassischen Ansicht,
- den Ribbon-Befehl **Ablaufdatum +1 Jahr**, - den Ribbon-Befehl **Ablaufdatum +1 Jahr**,
- den Einstellungsdialog über denselben URL-Parameter `expiryIndicatorSettings=1`. - den Einstellungsdialog über denselben URL-Parameter `expiryIndicatorSettings=1`.
@@ -393,11 +393,12 @@ Das Skript ist in einer SharePoint Management Shell beziehungsweise auf einem Re
### Upgrade von Version 1.x auf 2.0 ### Upgrade von Version 1.x auf 2.0
1. Das vorhandene Paket im App Catalog durch Version `2.0.0.0` ersetzen. 1. Das vorhandene Paket im App Catalog durch Version `2.0.1.0` ersetzen.
2. Die App in der Site aktualisieren. Die enthaltene Feature-UpgradeAction provisioniert ausschließlich das 2. Die App in der Site aktualisieren. Die enthaltene Feature-UpgradeAction provisioniert ausschließlich das
neue Classic-Asset und legt keine fachliche Spalte an. neue Classic-Asset und legt keine fachliche Spalte an.
3. `Enable-ExpiryIndicator.ps1` für jede bereits angebundene Liste erneut ausführen. Das Skript erhält die 3. `Enable-ExpiryIndicator.ps1` für jede bereits angebundene Liste erneut ausführen. Das Skript erhält die
vorhandene Feldkonfiguration und ergänzt idempotent die Classic-ScriptLink- und Ribbon-Registrierungen. vorhandene Feldkonfiguration und ergänzt idempotent den gemeinsamen Web-ScriptLink sowie die
listenspezifische Ribbon-Registrierung.
4. Modern- und Classic-Ansicht mit geleertem Browsercache neu laden und prüfen. 4. Modern- und Classic-Ansicht mit geleertem Browsercache neu laden und prüfen.
## Deaktivierung ## Deaktivierung
@@ -409,6 +410,8 @@ Das Skript ist in einer SharePoint Management Shell beziehungsweise auf einem Re
-ExpiryFieldInternalName 'CustomerExpiryDate' -ExpiryFieldInternalName 'CustomerExpiryDate'
``` ```
Das Skript entfernt die Field-Customizer-Verknüpfung sowie die listenspezifischen Classic-ScriptLink- und Das Skript entfernt die Field-Customizer-Verknüpfung sowie die listenspezifische Classic-Ribbon-Registrierung.
Ribbon-Registrierungen. Die Spalte und alle fachlichen Daten bleiben erhalten. Die modernen Command-Set- und Die Spalte und alle fachlichen Daten bleiben erhalten. Der gemeinsam auf Web-Ebene verwendete ScriptLink bleibt
standardmäßig bestehen, da ihn weitere Listen benötigen können. Wenn keine weitere Liste den ExpiryIndicator
verwendet, kann er mit `-RemoveClassicWebScriptLink` entfernt werden. Die modernen Command-Set- und
Application-Customizer-Registrierungen werden durch das Entfernen der App aus der Site beseitigt. Application-Customizer-Registrierungen werden durch das Entfernen der App aus der Site beseitigt.

View File

@@ -144,7 +144,7 @@ Beispiel einer Laufzeitkonfiguration:
### Release 2.0 ### Release 2.0
- [x] Versionsstände von Solution, Feature und SPFx-Komponenten auf `2.0.0` anheben. - [x] Versionsstände von Solution, Feature und SPFx-Komponenten auf `2.0.1` anheben.
- [x] README um V2-Regeln, Classic-Aktivierung und Deaktivierung ergänzen. - [x] README um V2-Regeln, Classic-Aktivierung und Deaktivierung ergänzen.
- [x] Pakete mit Node.js 8.17.0 erstellen und lokale Package-Validierung erfolgreich ausführen. - [x] Pakete mit Node.js 8.17.0 erstellen und lokale Package-Validierung erfolgreich ausführen.
- [ ] Feature-Upgrade in SharePoint Server Subscription Edition testen. - [ ] Feature-Upgrade in SharePoint Server Subscription Edition testen.

View File

@@ -3,7 +3,7 @@
"solution": { "solution": {
"name": "expiry-indicator-client-side-solution", "name": "expiry-indicator-client-side-solution",
"id": "68bb6d2d-9895-45b4-88e8-b98835faa981", "id": "68bb6d2d-9895-45b4-88e8-b98835faa981",
"version": "2.0.0.0", "version": "2.0.1.0",
"includeClientSideAssets": true, "includeClientSideAssets": true,
"skipFeatureDeployment": false, "skipFeatureDeployment": false,
"features": [ "features": [
@@ -11,7 +11,7 @@
"title": "ExpiryIndicator extension registration", "title": "ExpiryIndicator extension registration",
"description": "Registers ExpiryIndicator commands and its settings Application Customizer. Does not provision ExpiryDate.", "description": "Registers ExpiryIndicator commands and its settings Application Customizer. Does not provision ExpiryDate.",
"id": "913402af-ab9a-4974-9f86-5c2159ae41db", "id": "913402af-ab9a-4974-9f86-5c2159ae41db",
"version": "2.0.0.0", "version": "2.0.1.0",
"assets": { "assets": {
"elementManifests": [ "elementManifests": [
"elements.xml", "elements.xml",

2
package-lock.json generated
View File

@@ -1,6 +1,6 @@
{ {
"name": "expiry-indicator", "name": "expiry-indicator",
"version": "2.0.0", "version": "2.0.1",
"lockfileVersion": 1, "lockfileVersion": 1,
"requires": true, "requires": true,
"dependencies": { "dependencies": {

View File

@@ -1,6 +1,6 @@
{ {
"name": "expiry-indicator", "name": "expiry-indicator",
"version": "2.0.0", "version": "2.0.1",
"private": true, "private": true,
"engines": { "engines": {
"node": ">=6.9.0 <9.0.0" "node": ">=6.9.0 <9.0.0"

View File

@@ -9,7 +9,9 @@ param(
[Parameter(Mandatory = $true)] [Parameter(Mandatory = $true)]
[string]$ExpiryFieldInternalName, [string]$ExpiryFieldInternalName,
[Guid]$FieldCustomizerComponentId = 'a555f4fc-d6a6-4421-8189-457449d9bbde' [Guid]$FieldCustomizerComponentId = 'a555f4fc-d6a6-4421-8189-457449d9bbde',
[switch]$RemoveClassicWebScriptLink
) )
$isapiPath = Join-Path ([Environment]::GetFolderPath('CommonProgramFiles')) 'microsoft shared\Web Server Extensions\16\ISAPI' $isapiPath = Join-Path ([Environment]::GetFolderPath('CommonProgramFiles')) 'microsoft shared\Web Server Extensions\16\ISAPI'
@@ -23,9 +25,11 @@ try {
$list = $context.Web.Lists.GetByTitle($ListTitle) $list = $context.Web.Lists.GetByTitle($ListTitle)
$field = $list.Fields.GetByInternalNameOrTitle($ExpiryFieldInternalName) $field = $list.Fields.GetByInternalNameOrTitle($ExpiryFieldInternalName)
$customActions = $list.UserCustomActions $customActions = $list.UserCustomActions
$webCustomActions = $context.Web.UserCustomActions
$context.Load($list) $context.Load($list)
$context.Load($field) $context.Load($field)
$context.Load($customActions) $context.Load($customActions)
$context.Load($webCustomActions)
$context.ExecuteQuery() $context.ExecuteQuery()
if ($field.ClientSideComponentId -ne $FieldCustomizerComponentId) { if ($field.ClientSideComponentId -ne $FieldCustomizerComponentId) {
@@ -44,8 +48,19 @@ try {
$_.DeleteObject() $_.DeleteObject()
} }
if ($RemoveClassicWebScriptLink) {
$webCustomActions | Where-Object {
$_.Name -eq 'ExpiryIndicator.Classic.ScriptLink'
} | ForEach-Object {
$_.DeleteObject()
}
}
$context.ExecuteQuery() $context.ExecuteQuery()
Write-Host 'ExpiryIndicator-Verknüpfung und Classic-Registrierungen wurden entfernt. Feld und Daten bleiben unverändert.' Write-Host 'ExpiryIndicator-Verknüpfung und listenspezifische Classic-Registrierungen wurden entfernt. Feld und Daten bleiben unverändert.'
if (-not $RemoveClassicWebScriptLink) {
Write-Host 'Der gemeinsam verwendete Classic-Web-ScriptLink bleibt bestehen. Verwende -RemoveClassicWebScriptLink, wenn keine weitere Liste ihn benötigt.'
}
} }
} }
finally { finally {

View File

@@ -27,9 +27,11 @@ try {
$list = $context.Web.Lists.GetByTitle($ListTitle) $list = $context.Web.Lists.GetByTitle($ListTitle)
$field = $list.Fields.GetByInternalNameOrTitle($ExpiryFieldInternalName) $field = $list.Fields.GetByInternalNameOrTitle($ExpiryFieldInternalName)
$customActions = $list.UserCustomActions $customActions = $list.UserCustomActions
$webCustomActions = $context.Web.UserCustomActions
$context.Load($list) $context.Load($list)
$context.Load($field) $context.Load($field)
$context.Load($customActions) $context.Load($customActions)
$context.Load($webCustomActions)
$context.ExecuteQuery() $context.ExecuteQuery()
if ($field.InternalName -ne $ExpiryFieldInternalName) { if ($field.InternalName -ne $ExpiryFieldInternalName) {
@@ -55,15 +57,25 @@ try {
$_.DeleteObject() $_.DeleteObject()
} }
if (-not $SkipClassic) {
$webCustomActions | Where-Object {
$_.Name -eq 'ExpiryIndicator.Classic.ScriptLink'
} | ForEach-Object {
$_.DeleteObject()
}
}
$context.ExecuteQuery() $context.ExecuteQuery()
if (-not $SkipClassic) { if (-not $SkipClassic) {
$scriptAction = $list.UserCustomActions.Add() # SharePoint permits Location=ScriptLink only on Web/Site custom
# actions, not on List.UserCustomActions. The runtime detects the
# current list and its bound expiry field by itself.
$scriptAction = $context.Web.UserCustomActions.Add()
$scriptAction.Name = 'ExpiryIndicator.Classic.ScriptLink' $scriptAction.Name = 'ExpiryIndicator.Classic.ScriptLink'
$scriptAction.Title = 'ExpiryIndicator Classic runtime' $scriptAction.Title = 'ExpiryIndicator Classic runtime'
$scriptAction.Location = 'ScriptLink' $scriptAction.Location = 'ScriptLink'
$separator = if ($ClassicScriptUrl.Contains('?')) { '&' } else { '?' } $scriptAction.ScriptSrc = $ClassicScriptUrl
$scriptAction.ScriptSrc = $ClassicScriptUrl + $separator + 'expiryField=' + [Uri]::EscapeDataString($field.InternalName)
$scriptAction.Sequence = 650 $scriptAction.Sequence = 650
$scriptAction.Update() $scriptAction.Update()
@@ -103,7 +115,7 @@ try {
$ribbonAction.Update() $ribbonAction.Update()
$context.ExecuteQuery() $context.ExecuteQuery()
Write-Host 'Classic-Ansicht: CSR/JSLink und Ribbon-Befehl wurden registriert.' Write-Host 'Classic-Ansicht: Web-ScriptLink und listenspezifischer Ribbon-Befehl wurden registriert.'
} }
Write-Host "ExpiryIndicator wurde für '$($list.Title)' an '$($field.InternalName)' gebunden." Write-Host "ExpiryIndicator wurde für '$($list.Title)' an '$($field.InternalName)' gebunden."

View File

@@ -15,6 +15,27 @@
return ''; return '';
} }
function discoverExpiryField() {
var id = listId(null);
if (!id) { return ''; }
try {
var xhr = new XMLHttpRequest();
xhr.open('GET', listUrl(id) +
'/fields?$select=InternalName,ClientSideComponentId', false);
xhr.setRequestHeader('Accept', 'application/json;odata=verbose');
xhr.setRequestHeader('OData-Version', '3.0');
xhr.send(null);
if (xhr.status < 200 || xhr.status >= 300) { return ''; }
var data = JSON.parse(xhr.responseText || '{}');
var fields = data.d ? data.d.results : (data.value || []);
for (var index = 0; index < fields.length; index++) {
var componentId = String(fields[index].ClientSideComponentId || '').replace(/[{}]/g, '').toLowerCase();
if (componentId === FIELD_CUSTOMIZER_ID) { return fields[index].InternalName; }
}
} catch (ignore) { }
return '';
}
function pageContext() { function pageContext() {
return window._spPageContextInfo || {}; return window._spPageContextInfo || {};
} }
@@ -473,7 +494,7 @@
function register() { function register() {
if (window.SPClientTemplates && SPClientTemplates.TemplateManager) { if (window.SPClientTemplates && SPClientTemplates.TemplateManager) {
var overrides = { OnPostRender: render }; var overrides = { OnPostRender: render };
var expiryField = configuredExpiryField(); var expiryField = configuredExpiryField() || discoverExpiryField();
if (expiryField) { if (expiryField) {
overrides.Templates = { Fields: {} }; overrides.Templates = { Fields: {} };
overrides.Templates.Fields[expiryField] = { overrides.Templates.Fields[expiryField] = {

View File

@@ -4,7 +4,7 @@
"alias": "ExpiryIndicatorFieldCustomizer", "alias": "ExpiryIndicatorFieldCustomizer",
"componentType": "Extension", "componentType": "Extension",
"extensionType": "FieldCustomizer", "extensionType": "FieldCustomizer",
"version": "2.0.0", "version": "2.0.1",
"manifestVersion": 2, "manifestVersion": 2,
"requiresCustomScript": false "requiresCustomScript": false
} }

View File

@@ -4,7 +4,7 @@
"alias": "ExpiryIndicatorApplicationCustomizer", "alias": "ExpiryIndicatorApplicationCustomizer",
"componentType": "Extension", "componentType": "Extension",
"extensionType": "ApplicationCustomizer", "extensionType": "ApplicationCustomizer",
"version": "2.0.0", "version": "2.0.1",
"manifestVersion": 2, "manifestVersion": 2,
"requiresCustomScript": false "requiresCustomScript": false
} }

View File

@@ -4,7 +4,7 @@
"alias": "ExpiryIndicatorCommandSet", "alias": "ExpiryIndicatorCommandSet",
"componentType": "Extension", "componentType": "Extension",
"extensionType": "ListViewCommandSet", "extensionType": "ListViewCommandSet",
"version": "2.0.0", "version": "2.0.1",
"manifestVersion": 2, "manifestVersion": 2,
"requiresCustomScript": false, "requiresCustomScript": false,
"items": { "items": {