Enhance ExpiryIndicator binding dialog with status display and validation improvements

- Added a status display area in the ExpiryIndicator binding dialog to show messages related to binding actions.
- Cleared the status display when opening the binding dialog.
- Updated the binding registration function to normalize expiry bindings for better handling.
- Improved error handling and user feedback during the binding process.
- Adjusted the showStatus function to update the status in both the main status area and the binding dialog.
This commit is contained in:
Torsten Brendgen
2026-07-18 22:23:01 +02:00
parent 730b07439e
commit 4713299ce1
6 changed files with 17 additions and 15 deletions

View File

@@ -17,7 +17,7 @@
<section id="panel-branding" class="ps-panel" hidden><div class="ps-panel-head"><div><h2>Custom Branding</h2><p>Pflegt die ClientSideComponentProperties des aktiven Custom Branding Application Customizers.</p></div><span id="brandingChip" class="ps-chip">0</span></div><div class="ps-grid"><div class="ps-card ps-span-4"><h3>CSS-Dateien</h3><p>Lokale SiteAssets-Pfade oder externe Stylesheets.</p><div class="ps-meta"><div><label>Component Id</label><strong id="brandingComponentId">-</strong></div><div><label>Registrierung</label><strong id="brandingLocation">-</strong></div></div><label class="ps-label" for="brandingCssPath">Neue CSS-Datei</label><div class="ps-inline"><input type="text" id="brandingCssPath" class="ps-input" placeholder="/sites/root/SiteAssets/custom-branding.css oder https://cdn.example.com/style.css" /><button type="button" class="ps-btn ps-btn-primary" onclick="addBrandingCssFile()">Hinzufuegen</button></div><div id="brandingCssList" class="ps-list"></div></div><div class="ps-card ps-span-4"><h3>Placeholder Top</h3><p>JSON-Array fuer den oberen Placeholder.</p><label class="ps-label" for="brandingTopJson">Top JSON</label><textarea id="brandingTopJson" class="ps-textarea" spellcheck="false"></textarea></div><div class="ps-card ps-span-4"><h3>Placeholder Bottom</h3><p>JSON-Array fuer den unteren Placeholder.</p><label class="ps-label" for="brandingBottomJson">Bottom JSON</label><textarea id="brandingBottomJson" class="ps-textarea" spellcheck="false"></textarea></div><div class="ps-card ps-span-12"><h3>ClientSideComponentProperties Preview</h3><p>Dieses JSON wird direkt in die aktive UserCustomAction fuer Custom Branding geschrieben.</p><pre id="brandingPreview" class="ps-preview"></pre><div class="ps-note"><strong>Schema:</strong> <code>{ cssfiles, placeholdertop: { elements: [] }, placeholderbottom: { elements: [] } }</code></div></div></div><div class="ps-actions-bar"><div class="ps-actions"><button type="button" class="ps-btn ps-btn-quiet" onclick="reloadSolutions('branding')">Neu laden</button><button type="button" class="ps-btn ps-btn-quiet" onclick="validateBrandingEditors()">JSON validieren</button><button type="button" class="ps-btn ps-btn-quiet" onclick="formatBrandingEditors()">JSON formatieren</button></div><div class="ps-actions"><button type="button" class="ps-btn ps-btn-primary" onclick="applyBrandingConfig()">Konfiguration anwenden</button></div></div></section>
<section id="panel-megamenu" class="ps-panel" hidden><div class="ps-panel-head"><div><h2>Mega Menu</h2><p>Pflegt die ClientSideComponentProperties des aktiven Mega Menu Application Customizers.</p></div><span id="megaMenuChip" class="ps-chip">OK</span></div><div class="ps-grid"><div class="ps-card ps-span-6"><h3>Navigation</h3><p>Pflegt Term Set, optionales CSS und Debug-Verhalten.</p><div class="ps-meta"><div><label>Component Id</label><strong id="megaMenuComponentId">-</strong></div><div><label>Registrierung</label><strong id="megaMenuLocation">-</strong></div></div><label class="ps-label" for="megaMenuTermSetName">Term Set Name</label><input type="text" id="megaMenuTermSetName" class="ps-input" placeholder="Global Navigation" /><label class="ps-label" for="megaMenuCssUrl" style="margin-top:16px;">Zusatz-CSS URL</label><input type="text" id="megaMenuCssUrl" class="ps-input" placeholder="/sites/root/SiteAssets/megamenu.css oder leer lassen" /><label class="ps-label" style="margin-top:16px;">Debug-Modus</label><div class="ps-check"><input type="checkbox" id="megaMenuDebug" /><label for="megaMenuDebug" style="margin:0;font-weight:600;">Debug-Ausgaben in der Browser-Konsole aktivieren</label></div></div><div class="ps-card ps-span-6"><h3>ClientSideComponentProperties Preview</h3><p>Dieses JSON wird direkt in die aktive UserCustomAction fuer Mega Menu geschrieben.</p><pre id="megaMenuPreview" class="ps-preview"></pre><div class="ps-note"><strong>Schema:</strong> <code>{ termSetName, cssUrl, debug }</code></div></div></div><div class="ps-actions-bar"><div class="ps-actions"><button type="button" class="ps-btn ps-btn-quiet" onclick="reloadSolutions('megamenu')">Neu laden</button></div><div class="ps-actions"><button type="button" class="ps-btn ps-btn-primary" onclick="applyMegaMenuConfig()">Konfiguration anwenden</button></div></div></section>
<section id="panel-expiryindicator" class="ps-panel" hidden><div class="ps-panel-head"><div><h2>ExpiryIndicator 2.1</h2><p>Zentrale Standards fuer die Site Collection und lokale Ausnahmen fuer einzelne Listen oder Bibliotheken.</p></div><span id="expiryIndicatorChip" class="ps-chip">V2.1</span></div><div class="ps-grid"><div class="ps-card ps-span-4"><h3>Site-Collection-Standard</h3><p>Gilt im Root Web und in allen Subwebs, sofern eine Bindung auf Vererbung steht.</p><div class="ps-actions" style="margin-top:16px;"><button type="button" class="ps-btn ps-btn-quiet" onclick="loadExpirySiteDefaults()">Standard laden</button><button type="button" class="ps-btn ps-btn-primary" onclick="saveExpirySiteDefaults()">Standard speichern</button></div><div class="ps-note"><strong>Speicherort:</strong> Property Bag des Root Webs. Es wird keine Konfigurationsliste verwendet.</div></div><div class="ps-card ps-span-8"><h3>Registrierte Bindungen</h3><p>Listen und Bibliotheken, die per PortalSettings oder Aktivierungsskript registriert wurden.</p><div id="expiryBindingsList" class="ps-list"><div class="ps-list-empty">Keine Bindungen registriert.</div></div><div class="ps-actions" style="margin-top:16px;"><button type="button" class="ps-btn ps-btn-quiet" onclick="openExpiryBindingDialog()">Bindung hinzufuegen</button><button type="button" class="ps-btn ps-btn-quiet" onclick="setAllExpiryBindingsToInheritance()">Alle auf Vererbung stellen</button></div></div><div class="ps-card ps-span-4"><h3>Ausgewaehltes Ziel</h3><p>Web, Liste und interner Name des gebundenen Ablaufdatumsfeldes.</p><label class="ps-label" for="expiryWebUrl" style="margin-top:16px;">Web URL</label><input type="text" id="expiryWebUrl" class="ps-input" /><label class="ps-label" for="expiryListTitle" style="margin-top:16px;">Listentitel</label><input type="text" id="expiryListTitle" class="ps-input" placeholder="Dokumente" /><label class="ps-label" for="expiryFieldName" style="margin-top:16px;">Interner Feldname</label><input type="text" id="expiryFieldName" class="ps-input" placeholder="ExpiryDate" /><div class="ps-actions" style="margin-top:16px;"><button type="button" class="ps-btn ps-btn-primary" onclick="loadExpiryIndicatorConfig()">Lokale Konfiguration laden</button><button type="button" class="ps-btn ps-btn-quiet" onclick="setSelectedExpiryBindingToInheritance()">Standard erben</button></div></div><div class="ps-card ps-span-8"><h3>Konfigurationseditor</h3><p>Unterstuetzt V1-Regeln sowie verschachtelte V2-<code>condition</code>-Gruppen mit <code>and</code>/<code>or</code>.</p><textarea id="expiryIndicatorJson" class="ps-textarea" style="min-height:430px" spellcheck="false"></textarea><div class="ps-note"><strong>Prioritaet:</strong> lokale Feldkonfiguration &gt; Subweb-Override &gt; Site-Collection-Standard &gt; eingebauter Standard.</div></div></div><div class="ps-actions-bar"><div class="ps-actions"><button type="button" class="ps-btn ps-btn-quiet" onclick="validateExpiryIndicatorConfig()">JSON validieren</button><button type="button" class="ps-btn ps-btn-quiet" onclick="formatExpiryIndicatorConfig()">JSON formatieren</button></div><div class="ps-actions"><button type="button" class="ps-btn ps-btn-primary" onclick="applyExpiryIndicatorConfig()">Als lokale Ausnahme speichern</button></div></div></section>
<div id="expiryBindingDialog" class="ps-modal-backdrop" hidden><div class="ps-modal" role="dialog" aria-modal="true" aria-labelledby="expiryBindingDialogTitle"><h2 id="expiryBindingDialogTitle">ExpiryIndicator anbinden</h2><p>Verbindet ein bereits vorhandenes Datumsfeld mit dem Field Customizer und registriert die Liste zentral.</p><label class="ps-label" for="bindExpiryWebUrl">Web URL</label><input type="text" id="bindExpiryWebUrl" class="ps-input" /><label class="ps-label" for="bindExpiryListTitle" style="margin-top:16px;">Listentitel</label><input type="text" id="bindExpiryListTitle" class="ps-input" placeholder="Documents" /><label class="ps-label" for="bindExpiryFieldName" style="margin-top:16px;">Interner Name des vorhandenen Datumsfeldes</label><input type="text" id="bindExpiryFieldName" class="ps-input" placeholder="ExpiryDate" /><div class="ps-check" style="margin-top:14px;"><input type="checkbox" id="bindExpiryClassic" checked="checked" /><label for="bindExpiryClassic" style="margin:0;font-weight:600;">Classic ScriptLink und Ribbon-Befehl registrieren</label></div><div class="ps-note">Die Solution legt keine Spalte an. Bei bereits vorhandener lokaler Konfiguration bleiben die Component Properties erhalten.</div><div class="ps-actions"><button type="button" class="ps-btn ps-btn-quiet" onclick="closeExpiryBindingDialog()">Abbrechen</button><button type="button" id="bindExpirySubmit" class="ps-btn ps-btn-primary" onclick="bindExpiryIndicatorFromDialog()">Bindung anlegen</button></div></div></div>
<div id="expiryBindingDialog" class="ps-modal-backdrop" hidden><div class="ps-modal" role="dialog" aria-modal="true" aria-labelledby="expiryBindingDialogTitle"><h2 id="expiryBindingDialogTitle">ExpiryIndicator anbinden</h2><p>Verbindet ein bereits vorhandenes Datumsfeld mit dem Field Customizer und registriert die Liste zentral.</p><div id="bindExpiryStatus"></div><label class="ps-label" for="bindExpiryWebUrl">Web URL</label><input type="text" id="bindExpiryWebUrl" class="ps-input" /><label class="ps-label" for="bindExpiryListTitle" style="margin-top:16px;">Listentitel</label><input type="text" id="bindExpiryListTitle" class="ps-input" placeholder="Documents" /><label class="ps-label" for="bindExpiryFieldName" style="margin-top:16px;">Interner Name des vorhandenen Datumsfeldes</label><input type="text" id="bindExpiryFieldName" class="ps-input" placeholder="ExpiryDate" /><div class="ps-check" style="margin-top:14px;"><input type="checkbox" id="bindExpiryClassic" checked="checked" /><label for="bindExpiryClassic" style="margin:0;font-weight:600;">Classic ScriptLink und Ribbon-Befehl registrieren</label></div><div class="ps-note">Die Solution legt keine Spalte an. Bei bereits vorhandener lokaler Konfiguration bleiben die Component Properties erhalten.</div><div class="ps-actions"><button type="button" class="ps-btn ps-btn-quiet" onclick="closeExpiryBindingDialog()">Abbrechen</button><button type="button" id="bindExpirySubmit" class="ps-btn ps-btn-primary" onclick="bindExpiryIndicatorFromDialog()">Bindung anlegen</button></div></div></div>
<script type="text/javascript">
var CUSTOM_SOLUTION_TAG='MSFT-Custom-Solution';var EXPIRY_FIELD_COMPONENT_ID='a555f4fc-d6a6-4421-8189-457449d9bbde';var EXPIRY_DESCRIPTOR_PROPERTY='PortalSettings.Solutions.ExpiryIndicator';var EXPIRY_DEFAULTS_PROPERTY='PortalSettings.ExpiryIndicator.Defaults';var EXPIRY_BINDINGS_PROPERTY='PortalSettings.ExpiryIndicator.Bindings';var SUPPORTED_SOLUTIONS={branding:{key:'branding',label:'Custom Branding',componentId:'035ba968-6488-4d42-86b3-0470ffcc95b9',panelId:'panel-branding',descriptionTag:'MSFT-Custom-Solution:CustomBranding',providerType:'applicationCustomizer'},megamenu:{key:'megamenu',label:'Mega Menu',componentId:'abc3361f-bb2d-491f-aba3-cd51c19a299b',panelId:'panel-megamenu',descriptionTag:'MSFT-Custom-Solution:MegaMenu',providerType:'applicationCustomizer'},expiryindicator:{key:'expiryindicator',label:'ExpiryIndicator',componentId:EXPIRY_FIELD_COMPONENT_ID,panelId:'panel-expiryindicator',providerType:'rootPropertyBag'}};
var state={booted:false,activeKey:'',solutions:{}};
@@ -45,16 +45,17 @@ function buildMegaMenuConfig(){var solution=state.solutions.megamenu;if(!solutio
function updateMegaMenuPreview(){var preview=document.getElementById('megaMenuPreview');try{var config=buildMegaMenuConfig();preview.textContent=JSON.stringify(config,null,2);document.getElementById('megaMenuChip').textContent=config.debug?'DBG':'OK';}catch(error){preview.textContent='Fehler: '+error.message;document.getElementById('megaMenuChip').textContent='!';}}
function applyMegaMenuConfig(){try{var config=buildMegaMenuConfig();showStatus('Aktualisiere Mega Menu...','info',true);updateSolutionAction('megamenu',config,function(){state.solutions.megamenu.config=normalizeMegaMenuConfig(config);populateMegaMenuPanel(state.solutions.megamenu);showStatus('Mega Menu wurde erfolgreich aktualisiert.','success');},function(message){showStatus('Mega Menu konnte nicht aktualisiert werden: '+message,'error',true);});}catch(error){showStatus(error.message,'error');}}
function defaultExpiryIndicatorConfig(expiryField){return{baseField:'Created',expiryField:expiryField||'ExpiryDate',default:{lifeTime:{value:2,unit:'years'},columnRule:[{daysUntilExpiry:0,operator:'lessOrEqual',backgroundColor:'#a4262c',textColor:'#ffffff',label:'Abgelaufen'},{daysUntilExpiry:30,operator:'lessOrEqual',backgroundColor:'#ffaa44',textColor:'#000000',label:'Laeuft bald ab'},{daysUntilExpiry:90,operator:'lessOrEqual',backgroundColor:'#fff4ce',textColor:'#000000',label:'Beobachten'}]},rules:[],nullText:'Kein Ablaufdatum',confirmExtension:true};}
function openExpiryBindingDialog(){document.getElementById('bindExpiryWebUrl').value=getWebUrl();document.getElementById('bindExpiryListTitle').value='';document.getElementById('bindExpiryFieldName').value='ExpiryDate';document.getElementById('expiryBindingDialog').hidden=false;window.setTimeout(function(){document.getElementById('bindExpiryListTitle').focus();},0);}
function openExpiryBindingDialog(){document.getElementById('bindExpiryWebUrl').value=getWebUrl();document.getElementById('bindExpiryListTitle').value='';document.getElementById('bindExpiryFieldName').value='ExpiryDate';document.getElementById('bindExpiryStatus').innerHTML='';document.getElementById('bindExpirySubmit').disabled=false;document.getElementById('expiryBindingDialog').hidden=false;window.setTimeout(function(){document.getElementById('bindExpiryListTitle').focus();},0);}
function closeExpiryBindingDialog(){document.getElementById('expiryBindingDialog').hidden=true;}
function getExpiryBindingInput(){var webUrl=document.getElementById('bindExpiryWebUrl').value.trim().replace(/\/$/,'');var listTitle=document.getElementById('bindExpiryListTitle').value.trim();var fieldName=document.getElementById('bindExpiryFieldName').value.trim();if(!webUrl){throw new Error('Bitte eine Web URL angeben.');}if(!listTitle){throw new Error('Bitte einen Listentitel angeben.');}if(!/^[A-Za-z_][A-Za-z0-9_]*$/.test(fieldName)){throw new Error('Der interne Feldname ist ungueltig.');}return{webUrl:webUrl,listTitle:listTitle,fieldName:fieldName,classic:document.getElementById('bindExpiryClassic').checked===true};}
function bindingListUrl(target){return target.webUrl+"/_api/web/lists/getbytitle('"+target.listTitle.replace(/'/g,"''")+"')";}
function bindExpiryIndicatorFromDialog(){var target;try{target=getExpiryBindingInput();}catch(error){showStatus(error.message,'error',true);return;}var button=document.getElementById('bindExpirySubmit');button.disabled=true;showStatus('Pruefe Liste und Ablaufdatumsfeld...','info',true);var listUrl=bindingListUrl(target);getJson(listUrl+'?$select=Id,Title,BaseType',function(listPayload){var list=listPayload.d||listPayload;getJson(listUrl+"/fields/getbyinternalnameortitle('"+target.fieldName+"')?$select=Id,InternalName,TypeAsString,ClientSideComponentId,ClientSideComponentProperties",function(fieldPayload){var field=fieldPayload.d||fieldPayload;var currentComponent=normalizeGuid(field.ClientSideComponentId);if(currentComponent&&currentComponent!==normalizeGuid(EXPIRY_FIELD_COMPONENT_ID)){button.disabled=false;showStatus('Das Feld ist bereits mit einem anderen Field Customizer verbunden. Die Bindung wurde nicht geaendert.','error',true);return;}if(field.TypeAsString&&field.TypeAsString!=='DateTime'){button.disabled=false;showStatus('Das angegebene Feld ist kein DateTime-Feld.','error',true);return;}var properties=field.ClientSideComponentProperties;if(!properties||!String(properties).trim()){properties=JSON.stringify({schemaVersion:2,expiryField:field.InternalName,inheritSiteDefaults:true});}var payload={__metadata:{type:field.__metadata&&field.__metadata.type?field.__metadata.type:'SP.Field'},ClientSideComponentId:EXPIRY_FIELD_COMPONENT_ID,ClientSideComponentProperties:properties};postJsonMerge(listUrl+"/fields(guid'"+normalizeGuid(field.Id)+"')",payload,function(){var info={target:target,list:list,field:field,listUrl:listUrl};function finish(classicWarning){registerExpiryBinding(info,function(){button.disabled=false;closeExpiryBindingDialog();reloadSolutions('expiryindicator');if(classicWarning){showStatus('Die moderne Bindung wurde angelegt. Classic konnte nicht vollstaendig registriert werden: '+classicWarning,'warning',true);}else{showStatus('ExpiryIndicator wurde erfolgreich angebunden und zentral registriert.','success');}},function(message){button.disabled=false;showStatus('Das Feld wurde gebunden, aber das zentrale Inventar konnte nicht aktualisiert werden: '+message,'warning',true);});}if(target.classic){ensureExpiryClassicRegistration(info,function(warning){finish(warning);});}else{finish('');}},function(message){button.disabled=false;showStatus('Das Feld konnte nicht mit dem ExpiryIndicator verbunden werden: '+message,'error',true);});},function(message){button.disabled=false;showStatus('Das Ablaufdatumsfeld konnte nicht gelesen werden: '+message,'error',true);});},function(message){button.disabled=false;showStatus('Die Liste oder Bibliothek konnte nicht gelesen werden: '+message,'error',true);});}
function registerExpiryBinding(info,onSuccess,onError){getJson(getSiteUrl().replace(/\/$/,'')+'/_api/web/AllProperties',function(payload){var properties=payload.d||payload||{};var bindings=propertyBagJson(properties,EXPIRY_BINDINGS_PROPERTY,[]);if(!Array.isArray(bindings)){bindings=[];}var webUrl=info.target.webUrl.replace(/\/$/,'');var listId=normalizeGuid(info.list.Id);bindings=bindings.filter(function(item){return !(String(item.webUrl||'').replace(/\/$/,'').toLowerCase()===webUrl.toLowerCase()&&normalizeGuid(item.listId)===listId);});bindings.push({webUrl:webUrl,listId:listId,listTitle:info.list.Title,fieldInternalName:info.field.InternalName,fieldId:normalizeGuid(info.field.Id)});var descriptor={key:'ExpiryIndicator',displayName:'Expiry Indicator',version:'2.1.0',provider:'expiryIndicator',componentIds:[EXPIRY_FIELD_COMPONENT_ID,'cd58f5d9-ffc8-4df7-a910-3054842d7abf','95691218-bcb1-4fad-adda-02bb12014d15'],settingsScope:['siteCollection','web','list']};setRootPropertyBagValue(EXPIRY_DESCRIPTOR_PROPERTY,JSON.stringify(descriptor),function(){setRootPropertyBagValue(EXPIRY_BINDINGS_PROPERTY,JSON.stringify(bindings),onSuccess,onError);},onError);},onError);}
function registerExpiryBinding(info,onSuccess,onError){getJson(getSiteUrl().replace(/\/$/,'')+'/_api/web/AllProperties',function(payload){var properties=payload.d||payload||{};var bindings=normalizeExpiryBindings(propertyBagJson(properties,EXPIRY_BINDINGS_PROPERTY,[]));var webUrl=info.target.webUrl.replace(/\/$/,'');var listId=normalizeGuid(info.list.Id);bindings=bindings.filter(function(item){return !(String(item.webUrl||'').replace(/\/$/,'').toLowerCase()===webUrl.toLowerCase()&&normalizeGuid(item.listId)===listId);});bindings.push({webUrl:webUrl,listId:listId,listTitle:info.list.Title,fieldInternalName:info.field.InternalName,fieldId:normalizeGuid(info.field.Id)});var descriptor={key:'ExpiryIndicator',displayName:'Expiry Indicator',version:'2.1.1',provider:'expiryIndicator',componentIds:[EXPIRY_FIELD_COMPONENT_ID,'cd58f5d9-ffc8-4df7-a910-3054842d7abf','95691218-bcb1-4fad-adda-02bb12014d15'],settingsScope:['siteCollection','web','list']};setRootPropertyBagValue(EXPIRY_DESCRIPTOR_PROPERTY,JSON.stringify(descriptor),function(){setRootPropertyBagValue(EXPIRY_BINDINGS_PROPERTY,JSON.stringify(bindings),onSuccess,onError);},onError);},onError);}
function ensureExpiryClassicRegistration(info,onComplete){var webActionsUrl=info.target.webUrl+'/_api/web/UserCustomActions';getJson(webActionsUrl,function(webPayload){var webActions=getResultArray(webPayload);var hasScript=webActions.some(function(action){return action.Name==='ExpiryIndicator.Classic.ScriptLink';});function ensureRibbon(){var listActionsUrl=info.listUrl+'/UserCustomActions';getJson(listActionsUrl,function(listPayload){var listActions=getResultArray(listPayload);var hasRibbon=listActions.some(function(action){return action.Name==='ExpiryIndicator.Classic.Ribbon';});if(hasRibbon){onComplete('');return;}var ribbonLocation=Number(info.list.BaseType)===1?'Ribbon.Library.Actions.Controls._children':'Ribbon.List.Actions.Controls._children';var ribbonXml='<CommandUIExtension xmlns="http://schemas.microsoft.com/sharepoint/"><CommandUIDefinitions><CommandUIDefinition Location="'+ribbonLocation+'"><Button Id="ExpiryIndicator.Classic.ExtendOneYear.Button" Command="ExpiryIndicator.Classic.ExtendOneYear" Sequence="90" LabelText="Ablaufdatum +1 Jahr" Description="Verlaengert das Ablaufdatum der ausgewaehlten Elemente um ein Kalenderjahr." TemplateAlias="o1" /></CommandUIDefinition></CommandUIDefinitions><CommandUIHandlers><CommandUIHandler Command="ExpiryIndicator.Classic.ExtendOneYear" CommandAction="javascript:ExpiryIndicatorClassic.extendSelected();" EnabledScript="javascript:ExpiryIndicatorClassic.canExtend();" /></CommandUIHandlers></CommandUIExtension>';postJson(listActionsUrl,{__metadata:{type:'SP.UserCustomAction'},Name:'ExpiryIndicator.Classic.Ribbon',Title:'ExpiryIndicator Classic ribbon command',Location:'CommandUI.Ribbon',CommandUIExtension:ribbonXml,Sequence:651},function(){onComplete('');},onComplete);},onComplete);}if(hasScript){ensureRibbon();return;}postJson(webActionsUrl,{__metadata:{type:'SP.UserCustomAction'},Name:'ExpiryIndicator.Classic.ScriptLink',Title:'ExpiryIndicator Classic runtime',Location:'ScriptLink',ScriptSrc:'~sitecollection/SiteAssets/ExpiryIndicator/ExpiryIndicatorClassic.js',Sequence:650},ensureRibbon,onComplete);},onComplete);}
function normalizePropertyBagKey(value){return String(value||'').replace(/_x002e_/gi,'.').toLowerCase();}
function propertyBagJson(properties,key,fallback){if(!properties){return fallback;}var value;var expected=normalizePropertyBagKey(key);Object.keys(properties).some(function(name){if(normalizePropertyBagKey(name)===expected){value=properties[name];return true;}return false;});if(typeof value==='undefined'||value===null||value===''){return fallback;}if(typeof value==='object'){return value;}try{return JSON.parse(value);}catch(ignore){return fallback;}}
function readExpiryProviderInfo(onSuccess,onError){getJson(getSiteUrl().replace(/\/$/,'')+'/_api/web/AllProperties',function(payload){var properties=payload.d||payload||{};var descriptor=propertyBagJson(properties,EXPIRY_DESCRIPTOR_PROPERTY,null);var bindings=propertyBagJson(properties,EXPIRY_BINDINGS_PROPERTY,[]);var siteDefaults=propertyBagJson(properties,EXPIRY_DEFAULTS_PROPERTY,null);if(!Array.isArray(bindings)){bindings=[];}if(!descriptor&&!siteDefaults&&!bindings.length){onSuccess(null);return;}onSuccess({descriptor:descriptor,bindings:bindings,siteDefaults:siteDefaults||defaultExpiryIndicatorConfig('ExpiryDate'),rootProperties:properties,selected:null});},onError);}
function normalizeExpiryBindings(value){var result=[];function visit(item){if(Array.isArray(item)){item.forEach(visit);return;}if(!item||typeof item!=='object'){return;}if(item.webUrl&&item.listId&&item.fieldInternalName){result.push(item);}}visit(value);return result;}
function readExpiryProviderInfo(onSuccess,onError){getJson(getSiteUrl().replace(/\/$/,'')+'/_api/web/AllProperties',function(payload){var properties=payload.d||payload||{};var descriptor=propertyBagJson(properties,EXPIRY_DESCRIPTOR_PROPERTY,null);var bindings=normalizeExpiryBindings(propertyBagJson(properties,EXPIRY_BINDINGS_PROPERTY,[]));var siteDefaults=propertyBagJson(properties,EXPIRY_DEFAULTS_PROPERTY,null);if(!descriptor&&!siteDefaults&&!bindings.length){onSuccess(null);return;}onSuccess({descriptor:descriptor,bindings:bindings,siteDefaults:siteDefaults||defaultExpiryIndicatorConfig('ExpiryDate'),rootProperties:properties,selected:null});},onError);}
function renderExpiryBindings(){var solution=state.solutions.expiryindicator;var host=document.getElementById('expiryBindingsList');if(!solution||!solution.info||!solution.info.bindings.length){host.innerHTML='<div class="ps-list-empty">Keine Bindungen registriert. Aktivieren Sie ExpiryIndicator 2.1 erneut an den gewuenschten Listen.</div>';return;}var html='';for(var i=0;i<solution.info.bindings.length;i++){var binding=solution.info.bindings[i];html+='<div class="ps-item"><div><strong>'+escapeHtml(binding.listTitle||binding.listId)+'</strong><div class="ps-path">'+escapeHtml(binding.webUrl)+' | '+escapeHtml(binding.fieldInternalName)+'</div></div><button type="button" class="ps-btn ps-btn-quiet" onclick="selectExpiryBinding('+i+')">Auswaehlen</button></div>';}host.innerHTML=html;document.getElementById('expiryIndicatorChip').textContent=String(solution.info.bindings.length);}
function selectExpiryBinding(index){var solution=state.solutions.expiryindicator;if(!solution||!solution.info||!solution.info.bindings[index]){return;}var binding=solution.info.bindings[index];document.getElementById('expiryWebUrl').value=binding.webUrl||getWebUrl();document.getElementById('expiryListTitle').value=binding.listTitle||'';document.getElementById('expiryFieldName').value=binding.fieldInternalName||'ExpiryDate';loadExpiryIndicatorConfig();}
function loadExpirySiteDefaults(){var solution=state.solutions.expiryindicator;if(!solution||!solution.info){return;}var config=solution.info.siteDefaults||defaultExpiryIndicatorConfig('ExpiryDate');document.getElementById('expiryIndicatorJson').value=JSON.stringify(config,null,2);showStatus('Site-Collection-Standard wurde in den Editor geladen.','success');}
@@ -91,7 +92,7 @@ function getAvailableSolutionKeys(){return Object.keys(SUPPORTED_SOLUTIONS).filt
function getActionLocationText(info){if(!info||!info.action){return '-';}var scopeLabel=info.scopeLabel?info.scopeLabel+' | ':'';return scopeLabel+(info.action.Location||'ClientSideExtension.ApplicationCustomizer');}
function getWebUrl(){if(window._spPageContextInfo&&_spPageContextInfo.webAbsoluteUrl){return _spPageContextInfo.webAbsoluteUrl;}return window.location.origin||'';}
function getSiteUrl(){if(window._spPageContextInfo&&_spPageContextInfo.siteAbsoluteUrl){return _spPageContextInfo.siteAbsoluteUrl;}return getWebUrl();}
function showStatus(message,type,keepVisible){var host=document.getElementById('statusMessage');host.innerHTML='<div class="ps-status ps-status-'+type+'">'+escapeHtml(message)+'</div>';if(!keepVisible){window.clearTimeout(showStatus._timer);showStatus._timer=window.setTimeout(function(){host.innerHTML='';},4200);}}
function showStatus(message,type,keepVisible){var html='<div class="ps-status ps-status-'+type+'">'+escapeHtml(message)+'</div>';var host=document.getElementById('statusMessage');host.innerHTML=html;var dialog=document.getElementById('expiryBindingDialog');var dialogHost=document.getElementById('bindExpiryStatus');if(dialog&&dialog.hidden===false&&dialogHost){dialogHost.innerHTML=html;}if(!keepVisible){window.clearTimeout(showStatus._timer);showStatus._timer=window.setTimeout(function(){host.innerHTML='';if(dialogHost){dialogHost.innerHTML='';}},4200);}}
function escapeHtml(text){var div=document.createElement('div');div.textContent=text===null||text===undefined?'':String(text);return div.innerHTML;}
</script></div></div>
</asp:Content>