diff --git a/README.md b/README.md index 712cb0a..3aa9b40 100644 --- a/README.md +++ b/README.md @@ -336,7 +336,7 @@ neu laden. Version 2.0 legt bei der App-Installation `ExpiryIndicatorClassic.js` unter `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, - den Ribbon-Befehl **Ablaufdatum +1 Jahr**, - 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 -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 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 - 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. ## Deaktivierung @@ -409,6 +410,8 @@ Das Skript ist in einer SharePoint Management Shell beziehungsweise auf einem Re -ExpiryFieldInternalName 'CustomerExpiryDate' ``` -Das Skript entfernt die Field-Customizer-Verknüpfung sowie die listenspezifischen Classic-ScriptLink- und -Ribbon-Registrierungen. Die Spalte und alle fachlichen Daten bleiben erhalten. Die modernen Command-Set- und +Das Skript entfernt die Field-Customizer-Verknüpfung sowie die listenspezifische Classic-Ribbon-Registrierung. +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. diff --git a/ToDo.md b/ToDo.md index 8a3dbfc..c0bfc31 100644 --- a/ToDo.md +++ b/ToDo.md @@ -144,7 +144,7 @@ Beispiel einer Laufzeitkonfiguration: ### 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] Pakete mit Node.js 8.17.0 erstellen und lokale Package-Validierung erfolgreich ausführen. - [ ] Feature-Upgrade in SharePoint Server Subscription Edition testen. diff --git a/config/package-solution.json b/config/package-solution.json index b153563..e7ca6f6 100644 --- a/config/package-solution.json +++ b/config/package-solution.json @@ -3,7 +3,7 @@ "solution": { "name": "expiry-indicator-client-side-solution", "id": "68bb6d2d-9895-45b4-88e8-b98835faa981", - "version": "2.0.0.0", + "version": "2.0.1.0", "includeClientSideAssets": true, "skipFeatureDeployment": false, "features": [ @@ -11,7 +11,7 @@ "title": "ExpiryIndicator extension registration", "description": "Registers ExpiryIndicator commands and its settings Application Customizer. Does not provision ExpiryDate.", "id": "913402af-ab9a-4974-9f86-5c2159ae41db", - "version": "2.0.0.0", + "version": "2.0.1.0", "assets": { "elementManifests": [ "elements.xml", diff --git a/package-lock.json b/package-lock.json index 5b30892..843cce2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "expiry-indicator", - "version": "2.0.0", + "version": "2.0.1", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index c31b855..2914c78 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "expiry-indicator", - "version": "2.0.0", + "version": "2.0.1", "private": true, "engines": { "node": ">=6.9.0 <9.0.0" diff --git a/scripts/Disable-ExpiryIndicator.ps1 b/scripts/Disable-ExpiryIndicator.ps1 index 2792e06..bd52e21 100644 --- a/scripts/Disable-ExpiryIndicator.ps1 +++ b/scripts/Disable-ExpiryIndicator.ps1 @@ -9,7 +9,9 @@ param( [Parameter(Mandatory = $true)] [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' @@ -23,9 +25,11 @@ try { $list = $context.Web.Lists.GetByTitle($ListTitle) $field = $list.Fields.GetByInternalNameOrTitle($ExpiryFieldInternalName) $customActions = $list.UserCustomActions + $webCustomActions = $context.Web.UserCustomActions $context.Load($list) $context.Load($field) $context.Load($customActions) + $context.Load($webCustomActions) $context.ExecuteQuery() if ($field.ClientSideComponentId -ne $FieldCustomizerComponentId) { @@ -44,8 +48,19 @@ try { $_.DeleteObject() } + if ($RemoveClassicWebScriptLink) { + $webCustomActions | Where-Object { + $_.Name -eq 'ExpiryIndicator.Classic.ScriptLink' + } | ForEach-Object { + $_.DeleteObject() + } + } + $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 { diff --git a/scripts/Enable-ExpiryIndicator.ps1 b/scripts/Enable-ExpiryIndicator.ps1 index cba95c1..725e90d 100644 --- a/scripts/Enable-ExpiryIndicator.ps1 +++ b/scripts/Enable-ExpiryIndicator.ps1 @@ -27,9 +27,11 @@ try { $list = $context.Web.Lists.GetByTitle($ListTitle) $field = $list.Fields.GetByInternalNameOrTitle($ExpiryFieldInternalName) $customActions = $list.UserCustomActions + $webCustomActions = $context.Web.UserCustomActions $context.Load($list) $context.Load($field) $context.Load($customActions) + $context.Load($webCustomActions) $context.ExecuteQuery() if ($field.InternalName -ne $ExpiryFieldInternalName) { @@ -55,15 +57,25 @@ try { $_.DeleteObject() } + if (-not $SkipClassic) { + $webCustomActions | Where-Object { + $_.Name -eq 'ExpiryIndicator.Classic.ScriptLink' + } | ForEach-Object { + $_.DeleteObject() + } + } + $context.ExecuteQuery() 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.Title = 'ExpiryIndicator Classic runtime' $scriptAction.Location = 'ScriptLink' - $separator = if ($ClassicScriptUrl.Contains('?')) { '&' } else { '?' } - $scriptAction.ScriptSrc = $ClassicScriptUrl + $separator + 'expiryField=' + [Uri]::EscapeDataString($field.InternalName) + $scriptAction.ScriptSrc = $ClassicScriptUrl $scriptAction.Sequence = 650 $scriptAction.Update() @@ -103,7 +115,7 @@ try { $ribbonAction.Update() $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." diff --git a/sharepoint/assets/ExpiryIndicatorClassic.js b/sharepoint/assets/ExpiryIndicatorClassic.js index 14dd73c..99b00e7 100644 --- a/sharepoint/assets/ExpiryIndicatorClassic.js +++ b/sharepoint/assets/ExpiryIndicatorClassic.js @@ -15,6 +15,27 @@ 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() { return window._spPageContextInfo || {}; } @@ -473,7 +494,7 @@ function register() { if (window.SPClientTemplates && SPClientTemplates.TemplateManager) { var overrides = { OnPostRender: render }; - var expiryField = configuredExpiryField(); + var expiryField = configuredExpiryField() || discoverExpiryField(); if (expiryField) { overrides.Templates = { Fields: {} }; overrides.Templates.Fields[expiryField] = { diff --git a/src/extensions/expiryIndicator/ExpiryIndicatorFieldCustomizer.manifest.json b/src/extensions/expiryIndicator/ExpiryIndicatorFieldCustomizer.manifest.json index 1aab302..7b8172e 100644 --- a/src/extensions/expiryIndicator/ExpiryIndicatorFieldCustomizer.manifest.json +++ b/src/extensions/expiryIndicator/ExpiryIndicatorFieldCustomizer.manifest.json @@ -4,7 +4,7 @@ "alias": "ExpiryIndicatorFieldCustomizer", "componentType": "Extension", "extensionType": "FieldCustomizer", - "version": "2.0.0", + "version": "2.0.1", "manifestVersion": 2, "requiresCustomScript": false } diff --git a/src/extensions/expiryIndicatorApplicationCustomizer/ExpiryIndicatorApplicationCustomizer.manifest.json b/src/extensions/expiryIndicatorApplicationCustomizer/ExpiryIndicatorApplicationCustomizer.manifest.json index 49245ea..7963ad3 100644 --- a/src/extensions/expiryIndicatorApplicationCustomizer/ExpiryIndicatorApplicationCustomizer.manifest.json +++ b/src/extensions/expiryIndicatorApplicationCustomizer/ExpiryIndicatorApplicationCustomizer.manifest.json @@ -4,7 +4,7 @@ "alias": "ExpiryIndicatorApplicationCustomizer", "componentType": "Extension", "extensionType": "ApplicationCustomizer", - "version": "2.0.0", + "version": "2.0.1", "manifestVersion": 2, "requiresCustomScript": false } diff --git a/src/extensions/expiryIndicatorCommandSet/ExpiryIndicatorCommandSet.manifest.json b/src/extensions/expiryIndicatorCommandSet/ExpiryIndicatorCommandSet.manifest.json index f3b7b43..e5e5872 100644 --- a/src/extensions/expiryIndicatorCommandSet/ExpiryIndicatorCommandSet.manifest.json +++ b/src/extensions/expiryIndicatorCommandSet/ExpiryIndicatorCommandSet.manifest.json @@ -4,7 +4,7 @@ "alias": "ExpiryIndicatorCommandSet", "componentType": "Extension", "extensionType": "ListViewCommandSet", - "version": "2.0.0", + "version": "2.0.1", "manifestVersion": 2, "requiresCustomScript": false, "items": {