Add SharePoint element manifests for PortalSettings pages

- Created elements-v1.1.xml, elements-v2.xml, and elements.xml to define the PortalSettingsPage modules for different versions.
- Added upgrade actions for v1.1 and v2 to apply the respective element manifests.
- Ensured that the PortalSettings.aspx file is included in the SitePages library with appropriate settings.
This commit is contained in:
Torsten Brendgen
2026-07-18 20:41:57 +02:00
parent 592e46805b
commit 5eedbe6ed5
15 changed files with 279 additions and 42 deletions

View File

@@ -0,0 +1,86 @@
<%@ Page language="C#" MasterPageFile="~masterurl/default.master" Inherits="Microsoft.SharePoint.WebPartPages.WebPartPage, Microsoft.SharePoint, Version=15.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<%@ Register Tagprefix="SharePoint" Namespace="Microsoft.SharePoint.WebControls" Assembly="Microsoft.SharePoint, Version=15.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<%@ Register Tagprefix="Utilities" Namespace="Microsoft.SharePoint.Utilities" Assembly="Microsoft.SharePoint, Version=15.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<%@ Register Tagprefix="WebPartPages" Namespace="Microsoft.SharePoint.WebPartPages" Assembly="Microsoft.SharePoint, Version=15.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<asp:Content ContentPlaceHolderID="PlaceHolderAdditionalPageHead" runat="server">
<SharePoint:ScriptLink language="javascript" name="sp.js" runat="server" Defer="true" LoadAfterUI="true" Localizable="false" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<style type="text/css">
:root{--ps-bg:#f4f7fb;--ps-surface:#fff;--ps-border:#d8e1ec;--ps-ink:#16263d;--ps-muted:#607086;--ps-accent:#0f6cbd;--ps-soft:#eaf2fb;--ps-success:#107c10;--ps-warning:#a15c00;--ps-danger:#b42318;--ps-shadow:0 16px 34px rgba(20,35,59,.08);--ps-radius:18px}
.ps-page{background:linear-gradient(180deg,#f9fbfe 0%,var(--ps-bg) 100%);padding:28px 0 40px}.ps-shell{width:min(1240px,calc(100% - 32px));margin:0 auto}.ps-hero{background:linear-gradient(135deg,#123660 0%,#0f6cbd 60%,#58a3e0 100%);color:#fff;border-radius:24px;padding:28px 30px;box-shadow:var(--ps-shadow)}.ps-hero h1{margin:0 0 10px;font-size:34px;line-height:1.1}.ps-hero p{margin:0;max-width:860px;line-height:1.6;color:rgba(255,255,255,.92)}.ps-eyebrow{font-size:12px;text-transform:uppercase;letter-spacing:.16em;opacity:.85;margin-bottom:12px}.ps-status-host{min-height:56px;margin-bottom:18px}.ps-status{padding:14px 16px;border-radius:12px;border:1px solid transparent;background:#fff;box-shadow:0 10px 24px rgba(20,35,59,.06)}.ps-status-info{color:var(--ps-accent);background:#f3f8fe;border-color:rgba(15,108,189,.2)}.ps-status-success{color:var(--ps-success);background:#eff8ef;border-color:rgba(16,124,16,.2)}.ps-status-warning{color:var(--ps-warning);background:#fff6eb;border-color:rgba(161,92,0,.2)}.ps-status-error{color:var(--ps-danger);background:#fff1f0;border-color:rgba(180,35,24,.2)}.ps-metrics,.ps-grid{display:grid;gap:16px;margin-top:18px}.ps-metrics{grid-template-columns:repeat(4,minmax(0,1fr))}.ps-grid{grid-template-columns:repeat(12,minmax(0,1fr))}.ps-card,.ps-panel,.ps-metric,.ps-empty{background:var(--ps-surface);border:1px solid var(--ps-border);border-radius:var(--ps-radius);box-shadow:0 10px 24px rgba(20,35,59,.05)}.ps-metric{padding:16px 18px}.ps-metric label{display:block;font-size:12px;text-transform:uppercase;letter-spacing:.08em;color:var(--ps-muted);margin-bottom:8px}.ps-metric strong{display:block;font-size:18px;line-height:1.45;word-break:break-word}.ps-tabs{display:flex;flex-wrap:wrap;gap:10px;margin-top:22px}.ps-tab,.ps-btn{height:42px;padding:0 18px;border-radius:999px;border:1px solid var(--ps-border);background:#fff;color:var(--ps-ink);font-size:14px;font-weight:600;cursor:pointer}.ps-tab.is-active{border-color:var(--ps-accent);background:var(--ps-soft);color:var(--ps-accent)}.ps-empty{margin-top:18px;padding:24px;color:var(--ps-muted);line-height:1.7}.ps-panel{margin-top:18px;padding:22px}.ps-panel-head{display:flex;justify-content:space-between;align-items:flex-start;gap:14px;margin-bottom:18px}.ps-panel-head h2,.ps-card h3{margin:0 0 8px}.ps-panel-head p,.ps-card p,.ps-note,.ps-help{margin:0;color:var(--ps-muted);line-height:1.6}.ps-chip{display:inline-flex;align-items:center;justify-content:center;min-width:36px;height:28px;padding:0 10px;border-radius:999px;background:var(--ps-soft);color:var(--ps-accent);font-size:12px;font-weight:700}.ps-card{padding:20px}.ps-span-4{grid-column:span 4}.ps-span-6{grid-column:span 6}.ps-span-8{grid-column:span 8}.ps-span-12{grid-column:span 12}.ps-meta{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:10px;margin:14px 0}.ps-meta div{padding:12px;border:1px solid var(--ps-border);border-radius:12px;background:#f8fbff}.ps-meta label{display:block;font-size:12px;text-transform:uppercase;letter-spacing:.08em;color:var(--ps-muted);margin-bottom:6px}.ps-meta strong{display:block;font-size:14px;line-height:1.55;word-break:break-word}.ps-label{display:block;margin:0 0 8px;font-size:13px;font-weight:600}.ps-input,.ps-textarea,.ps-preview{width:100%;box-sizing:border-box;border:1px solid var(--ps-border);border-radius:12px;background:#fbfdff;color:var(--ps-ink)}.ps-input{height:44px;padding:0 14px;font-size:14px}.ps-textarea,.ps-preview{min-height:260px;padding:14px;font:13px/1.6 Consolas,'Courier New',monospace;white-space:pre-wrap;overflow:auto}.ps-inline{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:10px;align-items:center}.ps-list{display:grid;gap:10px;margin-top:14px}.ps-item,.ps-list-empty{border:1px solid var(--ps-border);border-radius:12px;background:#fbfdff}.ps-item{display:flex;justify-content:space-between;align-items:center;gap:12px;padding:14px}.ps-list-empty{padding:18px;text-align:center;color:var(--ps-muted)}.ps-path{font:13px/1.6 Consolas,'Courier New',monospace;word-break:break-all}.ps-actions{display:flex;gap:10px;flex-wrap:wrap}.ps-actions-bar{position:sticky;bottom:16px;z-index:4;display:flex;justify-content:space-between;align-items:center;gap:14px;margin-top:18px;padding:16px 18px;border:1px solid rgba(15,108,189,.14);border-radius:20px;background:rgba(255,255,255,.92);box-shadow:0 16px 32px rgba(20,35,59,.12)}.ps-btn-primary{background:linear-gradient(135deg,var(--ps-accent) 0%,#1b86e0 100%);border-color:var(--ps-accent);color:#fff}.ps-btn-quiet{background:#f7faff}.ps-btn-danger{background:#fff6f5;border-color:rgba(180,35,24,.18);color:var(--ps-danger)}.ps-check{display:flex;align-items:center;gap:10px;min-height:44px}.ps-check input{width:18px;height:18px}.ps-note{margin-top:14px;padding:14px 16px;border-radius:12px;background:#f8fbff;border:1px solid var(--ps-border)}
@media (max-width:1080px){.ps-metrics{grid-template-columns:repeat(2,minmax(0,1fr))}.ps-span-4,.ps-span-6,.ps-span-8{grid-column:span 12}.ps-meta{grid-template-columns:minmax(0,1fr)}}@media (max-width:720px){.ps-shell{width:calc(100% - 20px)}.ps-metrics{grid-template-columns:minmax(0,1fr)}.ps-inline,.ps-actions-bar{display:grid;grid-template-columns:minmax(0,1fr)}.ps-actions .ps-btn,.ps-inline .ps-btn,.ps-tab{width:100%}.ps-hero h1{font-size:28px}}
</style>
</asp:Content>
<asp:Content ContentPlaceHolderID="PlaceHolderMain" runat="server">
<div class="ps-page"><div class="ps-shell"><div id="statusMessage" class="ps-status-host"></div><section class="ps-hero"><div class="ps-eyebrow">Portal Settings</div><h1>Zentrale Verwaltung fuer aktive Portal-Erweiterungen</h1><p>Diese Seite erkennt installierte Einstellungsanbieter automatisch und zeigt nur deren Tabs. Konfigurationen werden in UserCustomActions, Feld-Properties oder im Property Bag des Root Webs gespeichert.</p></section><section class="ps-metrics"><div class="ps-metric"><label>Aktive Erweiterungen</label><strong id="activeSolutionCount">0</strong></div><div class="ps-metric"><label>Aktiver Tab</label><strong id="activeTabValue">-</strong></div><div class="ps-metric"><label>Site Collection</label><strong id="siteUrlValue">-</strong></div><div class="ps-metric"><label>Erkannte Erweiterungen</label><strong id="detectedSolutionsValue">Keine</strong></div></section><div id="tabsHost" class="ps-tabs" role="tablist" aria-label="Portal Settings Tabs"></div><section id="emptyState" class="ps-empty" hidden>Keine unterstuetzten Erweiterungen wurden aktiv gefunden. Aktuell werden <strong>Custom Branding</strong>, <strong>Mega Menu</strong> und <strong>ExpiryIndicator</strong> unterstuetzt.</section>
<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 mit dem 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="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>
<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:{}};
document.addEventListener('DOMContentLoaded',function(){bootPage();});
function bootPage(){if(state.booted){return;}if(window.SP&&SP.ClientContext){state.booted=true;bindPage();reloadSolutions();return;}window.setTimeout(bootPage,150);}
function bindPage(){document.getElementById('siteUrlValue').textContent=getSiteUrl();document.getElementById('expiryWebUrl').value=getWebUrl();document.getElementById('expiryIndicatorJson').value=JSON.stringify(defaultExpiryIndicatorConfig('ExpiryDate'),null,2);document.getElementById('brandingCssPath').addEventListener('keypress',function(event){if(event.key==='Enter'){event.preventDefault();addBrandingCssFile();}});document.getElementById('brandingTopJson').addEventListener('input',updateBrandingPreview);document.getElementById('brandingBottomJson').addEventListener('input',updateBrandingPreview);document.getElementById('megaMenuTermSetName').addEventListener('input',updateMegaMenuPreview);document.getElementById('megaMenuCssUrl').addEventListener('input',updateMegaMenuPreview);document.getElementById('megaMenuDebug').addEventListener('change',updateMegaMenuPreview);}
function reloadSolutions(preferredKey){showStatus('Lade aktive Einstellungsanbieter...','info',true);state.solutions={};loadSolutionsSequentially(Object.keys(SUPPORTED_SOLUTIONS),0,preferredKey||state.activeKey,[],[],function(labels,errors,preferred){var keys=getAvailableSolutionKeys();if(!keys.length){state.activeKey='';renderTabs();renderPanels();updateOverview();showStatus('Keine unterstuetzten Erweiterungen wurden aktiv registriert gefunden.','warning',true);return;}state.activeKey=preferred&&state.solutions[preferred]?preferred:keys[0];hydratePanels();renderTabs();renderPanels();updateOverview();var message='Aktive Erweiterungen geladen: '+labels.join(', ')+'.';if(errors.length){showStatus(message+' Hinweise: '+errors.join(' | '),'warning',true);return;}showStatus(message,'success');});}
function loadSolutionsSequentially(keys,index,preferred,labels,errors,done){if(index>=keys.length){done(labels,errors,preferred);return;}var key=keys[index];var definition=SUPPORTED_SOLUTIONS[key];if(definition.providerType==='rootPropertyBag'){readExpiryProviderInfo(function(info){if(info){state.solutions[key]={definition:definition,info:info,config:info.siteDefaults};labels.push(definition.label);}loadSolutionsSequentially(keys,index+1,preferred,labels,errors,done);},function(message){errors.push(definition.label+': '+message);loadSolutionsSequentially(keys,index+1,preferred,labels,errors,done);});return;}readCustomizerActionInfo(definition,function(info){if(info&&info.action){state.solutions[key]={definition:definition,info:info,config:parseSolutionConfig(key,info.action.ClientSideComponentProperties)};labels.push(definition.label);}loadSolutionsSequentially(keys,index+1,preferred,labels,errors,done);},function(message){errors.push(definition.label+': '+message);loadSolutionsSequentially(keys,index+1,preferred,labels,errors,done);});}
function hydratePanels(){if(state.solutions.branding){populateBrandingPanel(state.solutions.branding);}if(state.solutions.megamenu){populateMegaMenuPanel(state.solutions.megamenu);}if(state.solutions.expiryindicator){renderExpiryBindings();}}
function renderTabs(){var host=document.getElementById('tabsHost');var keys=getAvailableSolutionKeys();if(!keys.length){host.innerHTML='';host.hidden=true;document.getElementById('emptyState').hidden=false;return;}host.hidden=false;document.getElementById('emptyState').hidden=true;var html='';for(var i=0;i<keys.length;i++){var key=keys[i];var def=SUPPORTED_SOLUTIONS[key];var isActive=state.activeKey===key;html+='<button type="button" class="ps-tab'+(isActive?' is-active':'')+'" role="tab" aria-selected="'+(isActive?'true':'false')+'" aria-controls="'+def.panelId+'" onclick="switchTab(\''+key+'\')">'+escapeHtml(def.label)+'</button>'; }host.innerHTML=html;}
function renderPanels(){document.getElementById('panel-branding').hidden=!(state.activeKey==='branding'&&state.solutions.branding);document.getElementById('panel-megamenu').hidden=!(state.activeKey==='megamenu'&&state.solutions.megamenu);document.getElementById('panel-expiryindicator').hidden=!(state.activeKey==='expiryindicator'&&state.solutions.expiryindicator);}
function switchTab(key){if(!state.solutions[key]){return;}state.activeKey=key;renderTabs();renderPanels();updateOverview();}
function updateOverview(){var keys=getAvailableSolutionKeys();document.getElementById('activeSolutionCount').textContent=String(keys.length);document.getElementById('activeTabValue').textContent=state.activeKey&&SUPPORTED_SOLUTIONS[state.activeKey]?SUPPORTED_SOLUTIONS[state.activeKey].label:'-';document.getElementById('detectedSolutionsValue').textContent=keys.length?keys.map(function(key){return SUPPORTED_SOLUTIONS[key].label;}).join(', '):'Keine';}
function populateBrandingPanel(solution){document.getElementById('brandingComponentId').textContent=solution.definition.componentId;document.getElementById('brandingLocation').textContent=getActionLocationText(solution.info);document.getElementById('brandingCssPath').value='';document.getElementById('brandingTopJson').value=JSON.stringify(solution.config.placeholdertop.elements,null,2);document.getElementById('brandingBottomJson').value=JSON.stringify(solution.config.placeholderbottom.elements,null,2);renderBrandingCssFiles();updateBrandingPreview();}
function renderBrandingCssFiles(){var host=document.getElementById('brandingCssList');var solution=state.solutions.branding;var cssFiles=solution?solution.config.cssfiles:[];if(!cssFiles||!cssFiles.length){host.innerHTML='<div class="ps-list-empty">Keine CSS-Dateien konfiguriert.</div>';document.getElementById('brandingChip').textContent='0';return;}var html='';for(var i=0;i<cssFiles.length;i++){html+='<div class="ps-item"><div class="ps-path">'+escapeHtml(cssFiles[i].path)+'</div><button type="button" class="ps-btn ps-btn-danger" onclick="removeBrandingCssFile('+i+')">Entfernen</button></div>'; }host.innerHTML=html;document.getElementById('brandingChip').textContent=String(cssFiles.length);}
function addBrandingCssFile(){var solution=state.solutions.branding;if(!solution){showStatus('Custom Branding ist aktuell nicht aktiv konfiguriert.','warning');return;}var input=document.getElementById('brandingCssPath');var path=input.value.trim();if(!path){showStatus('Bitte geben Sie einen CSS-Pfad ein.','warning');return;}var exists=solution.config.cssfiles.some(function(item){return item.path===path;});if(exists){showStatus('Diese CSS-Datei ist bereits vorhanden.','warning');return;}solution.config.cssfiles.push({path:path});input.value='';renderBrandingCssFiles();updateBrandingPreview();showStatus('CSS-Datei hinzugefuegt.','success');}
function removeBrandingCssFile(index){var solution=state.solutions.branding;if(!solution){return;}solution.config.cssfiles.splice(index,1);renderBrandingCssFiles();updateBrandingPreview();showStatus('CSS-Datei entfernt.','success');}
function buildBrandingConfig(){var solution=state.solutions.branding;if(!solution){throw new Error('Custom Branding ist aktuell nicht aktiv konfiguriert.');}return{cssfiles:solution.config.cssfiles.slice(),placeholdertop:{elements:parseJsonArray('brandingTopJson','Placeholder Top')},placeholderbottom:{elements:parseJsonArray('brandingBottomJson','Placeholder Bottom')}};}
function validateBrandingEditors(){try{state.solutions.branding.config=normalizeBrandingConfig(buildBrandingConfig());updateBrandingPreview();showStatus('Custom Branding JSON ist gueltig.','success');}catch(error){showStatus(error.message,'error');}}
function formatBrandingEditors(){try{state.solutions.branding.config=normalizeBrandingConfig(buildBrandingConfig());document.getElementById('brandingTopJson').value=JSON.stringify(state.solutions.branding.config.placeholdertop.elements,null,2);document.getElementById('brandingBottomJson').value=JSON.stringify(state.solutions.branding.config.placeholderbottom.elements,null,2);updateBrandingPreview();showStatus('Custom Branding JSON wurde formatiert.','success');}catch(error){showStatus(error.message,'error');}}
function updateBrandingPreview(){var preview=document.getElementById('brandingPreview');try{var config=buildBrandingConfig();preview.textContent=JSON.stringify(config,null,2);document.getElementById('brandingChip').textContent=String(config.cssfiles.length);}catch(error){preview.textContent='Fehler: '+error.message;}}
function applyBrandingConfig(){try{var config=buildBrandingConfig();showStatus('Aktualisiere Custom Branding...','info',true);updateSolutionAction('branding',config,function(){state.solutions.branding.config=normalizeBrandingConfig(config);populateBrandingPanel(state.solutions.branding);showStatus('Custom Branding wurde erfolgreich aktualisiert.','success');},function(message){showStatus('Custom Branding konnte nicht aktualisiert werden: '+message,'error',true);});}catch(error){showStatus(error.message,'error');}}
function populateMegaMenuPanel(solution){document.getElementById('megaMenuComponentId').textContent=solution.definition.componentId;document.getElementById('megaMenuLocation').textContent=getActionLocationText(solution.info);document.getElementById('megaMenuTermSetName').value=solution.config.termSetName||'';document.getElementById('megaMenuCssUrl').value=solution.config.cssUrl||'';document.getElementById('megaMenuDebug').checked=solution.config.debug===true;updateMegaMenuPreview();}
function buildMegaMenuConfig(){var solution=state.solutions.megamenu;if(!solution){throw new Error('Mega Menu ist aktuell nicht aktiv konfiguriert.');}var termSetName=document.getElementById('megaMenuTermSetName').value.trim();return{termSetName:termSetName,cssUrl:document.getElementById('megaMenuCssUrl').value.trim(),debug:document.getElementById('megaMenuDebug').checked===true};}
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 propertyBagJson(properties,key,fallback){if(!properties){return fallback;}var value;Object.keys(properties).some(function(name){if(name.toLowerCase()===key.toLowerCase()){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 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');}
function saveExpirySiteDefaults(){var config;try{config=parseExpiryIndicatorEditor();}catch(error){showStatus(error.message,'error',true);return;}showStatus('Speichere Site-Collection-Standard...','info',true);setRootPropertyBagValue(EXPIRY_DEFAULTS_PROPERTY,JSON.stringify(config),function(){state.solutions.expiryindicator.info.siteDefaults=config;state.solutions.expiryindicator.config=config;showStatus('Site-Collection-Standard wurde gespeichert. Vererbende Bindungen verwenden ihn beim naechsten Laden.','success');},function(message){showStatus('Site-Collection-Standard konnte nicht gespeichert werden: '+message,'error',true);});}
function setRootPropertyBagValue(key,value,onSuccess,onError){try{var context=new SP.ClientContext(getSiteUrl());var web=context.get_web();var properties=web.get_allProperties();context.load(properties);context.executeQueryAsync(function(){try{properties.set_item(key,value);web.update();context.executeQueryAsync(onSuccess,function(sender,args){onError(args.get_message());});}catch(error){onError(error.message||String(error));}},function(sender,args){onError(args.get_message());});}catch(error){onError(error.message||String(error));}}
function inheritancePayload(fieldName){return{__metadata:{type:'SP.Field'},ClientSideComponentProperties:JSON.stringify({schemaVersion:2,expiryField:fieldName,inheritSiteDefaults:true})};}
function setSelectedExpiryBindingToInheritance(){var target;try{target=getExpiryTarget();}catch(error){showStatus(error.message,'error');return;}showStatus('Aktiviere Vererbung fuer die ausgewaehlte Bindung...','info',true);postJsonMerge(expiryFieldUrl(target),inheritancePayload(target.fieldName),function(){if(state.solutions.expiryindicator.info){state.solutions.expiryindicator.info.selected=null;}loadExpirySiteDefaults();showStatus('Die Bindung erbt jetzt den Site-Collection-Standard.','success');},function(message){showStatus('Vererbung konnte nicht aktiviert werden: '+message,'error',true);});}
function expiryBindingFieldUrl(binding){return String(binding.webUrl||'').replace(/\/$/,'')+"/_api/web/lists(guid'"+normalizeGuid(binding.listId)+"')/fields/getbyinternalnameortitle('"+String(binding.fieldInternalName||'ExpiryDate').replace(/'/g,"''")+"')";}
function setAllExpiryBindingsToInheritance(){var solution=state.solutions.expiryindicator;var bindings=solution&&solution.info?solution.info.bindings:[];if(!bindings.length){showStatus('Es sind keine Bindungen registriert.','warning');return;}if(!window.confirm('Lokale Konfigurationen von '+bindings.length+' Bindung(en) durch Vererbung ersetzen?')){return;}showStatus('Aktiviere Vererbung fuer '+bindings.length+' Bindung(en)...','info',true);var errors=[];function next(index){if(index>=bindings.length){if(errors.length){showStatus('Vererbung wurde mit Hinweisen abgeschlossen: '+errors.join(' | '),'warning',true);}else{showStatus('Alle registrierten Bindungen erben jetzt den Site-Collection-Standard.','success');}return;}var binding=bindings[index];postJsonMerge(expiryBindingFieldUrl(binding),inheritancePayload(binding.fieldInternalName),function(){next(index+1);},function(message){errors.push((binding.listTitle||binding.listId)+': '+message);next(index+1);});}next(0);}
function getExpiryTarget(){var webUrl=document.getElementById('expiryWebUrl').value.trim().replace(/\/$/,'');var listTitle=document.getElementById('expiryListTitle').value.trim();var fieldName=document.getElementById('expiryFieldName').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};}
function expiryFieldUrl(target){return target.webUrl+"/_api/web/lists/getbytitle('"+target.listTitle.replace(/'/g,"''")+"')/fields/getbyinternalnameortitle('"+target.fieldName+"')";}
function loadExpiryIndicatorConfig(){var target;try{target=getExpiryTarget();}catch(error){showStatus(error.message,'error');return;}showStatus('Lade ExpiryIndicator-Konfiguration...','info',true);getJson(expiryFieldUrl(target)+'?$select=Id,InternalName,ClientSideComponentId,ClientSideComponentProperties',function(payload){var field=payload.d||payload;if(normalizeGuid(field.ClientSideComponentId)!==normalizeGuid(EXPIRY_FIELD_COMPONENT_ID)){showStatus('Das Feld ist nicht mit dem ExpiryIndicator Field Customizer verbunden.','error',true);return;}var config=defaultExpiryIndicatorConfig(field.InternalName);var inherited=false;if(field.ClientSideComponentProperties&&field.ClientSideComponentProperties.trim()){try{var stored=JSON.parse(field.ClientSideComponentProperties);if(stored.inheritSiteDefaults===true){inherited=true;config=JSON.parse(JSON.stringify(state.solutions.expiryindicator.info.siteDefaults||config));}else{config=stored;}}catch(error){showStatus('Die gespeicherten Component Properties enthalten ungueltiges JSON: '+error.message,'error',true);return;}}config.expiryField=field.InternalName;state.solutions.expiryindicator.info.selected={target:target,field:field,updateUrl:expiryFieldUrl(target),actionType:field.__metadata&&field.__metadata.type?field.__metadata.type:'SP.Field'};state.solutions.expiryindicator.config=config;document.getElementById('expiryFieldName').value=field.InternalName;document.getElementById('expiryIndicatorJson').value=JSON.stringify(config,null,2);document.getElementById('expiryIndicatorChip').textContent=inherited?'ERBT':'LOKAL';showStatus(inherited?'Die Bindung erbt den Site-Collection-Standard.':'Lokale ExpiryIndicator-Konfiguration wurde geladen.','success');},function(message){showStatus('ExpiryIndicator-Konfiguration konnte nicht geladen werden: '+message,'error',true);});}
function parseExpiryIndicatorEditor(){var raw=document.getElementById('expiryIndicatorJson').value.trim();if(!raw){throw new Error('Die ExpiryIndicator-Konfiguration darf nicht leer sein.');}var config;try{config=JSON.parse(raw);}catch(error){throw new Error('Ungueltiges JSON: '+error.message);}var errors=validateExpiryIndicatorObject(config);if(errors.length){throw new Error(errors.join(' | '));}return config;}
function validateExpiryIndicatorObject(config){var errors=[];function safeName(value){return/^[A-Za-z_][A-Za-z0-9_]*$/.test(value||'');}function validDuration(value,path){if(!value||typeof value.value!=='number'||!isFinite(value.value)||['days','months','years'].indexOf(value.unit)<0){errors.push(path+' muss value und unit days/months/years enthalten.');}}function validColorRules(value,path){if(!Array.isArray(value)){errors.push(path+' muss ein Array sein.');return;}for(var i=0;i<value.length;i++){var item=value[i];if(!item||typeof item.daysUntilExpiry!=='number'||['lessThan','lessOrEqual','equal','greaterOrEqual','greaterThan'].indexOf(item.operator)<0||typeof item.backgroundColor!=='string'||typeof item.textColor!=='string'){errors.push(path+'['+i+'] ist ungueltig.');}}}function validGroup(group,path,depth){if(depth>10){errors.push(path+' ist tiefer als 10 Ebenen.');return;}if(!group||(group.operator!=='and'&&group.operator!=='or')){errors.push(path+'.operator muss and oder or sein.');}if(!group||!Array.isArray(group.conditions)||!group.conditions.length){errors.push(path+'.conditions muss mindestens einen Eintrag enthalten.');return;}for(var i=0;i<group.conditions.length;i++){var condition=group.conditions[i];var child=path+'.conditions['+i+']';if(condition&&typeof condition.operator!=='undefined'){validGroup(condition,child,depth+1);}else if(!condition||!safeName(condition.columnName)||typeof condition.columnValue==='undefined'){errors.push(child+' benoetigt columnName und columnValue.');}}}if(!config||typeof config!=='object'){return['Konfiguration muss ein Objekt sein.'];}if(!safeName(config.baseField)){errors.push('baseField ist ungueltig.');}if(!safeName(config.expiryField)){errors.push('expiryField ist ungueltig.');}if(!config.default){errors.push('default fehlt.');}else{validDuration(config.default.lifeTime,'default.lifeTime');validColorRules(config.default.columnRule,'default.columnRule');}if(!Array.isArray(config.rules)){errors.push('rules muss ein Array sein.');}else{for(var r=0;r<config.rules.length;r++){var rule=config.rules[r];var path='rules['+r+']';if(rule&&rule.condition){validGroup(rule.condition,path+'.condition',0);}else if(!rule||!safeName(rule.columnName)||typeof rule.columnValue==='undefined'){errors.push(path+' benoetigt condition oder columnName/columnValue.');}if(rule){validDuration(rule.lifeTime,path+'.lifeTime');validColorRules(rule.columnRule,path+'.columnRule');}}}return errors;}
function validateExpiryIndicatorConfig(){try{parseExpiryIndicatorEditor();showStatus('ExpiryIndicator V2 JSON ist gueltig.','success');document.getElementById('expiryIndicatorChip').textContent='OK';}catch(error){showStatus(error.message,'error',true);document.getElementById('expiryIndicatorChip').textContent='!';}}
function formatExpiryIndicatorConfig(){try{var config=parseExpiryIndicatorEditor();document.getElementById('expiryIndicatorJson').value=JSON.stringify(config,null,2);showStatus('ExpiryIndicator JSON wurde formatiert.','success');}catch(error){showStatus(error.message,'error',true);}}
function applyExpiryIndicatorConfig(){var solution=state.solutions.expiryindicator;var selected=solution&&solution.info?solution.info.selected:null;if(!selected){showStatus('Bitte zuerst eine lokale ExpiryIndicator-Konfiguration laden.','warning',true);return;}var config;try{config=parseExpiryIndicatorEditor();}catch(error){showStatus(error.message,'error',true);return;}config.expiryField=selected.field.InternalName;var payload={__metadata:{type:selected.actionType},ClientSideComponentProperties:JSON.stringify(config)};showStatus('Aktualisiere lokale ExpiryIndicator-Ausnahme...','info',true);postJsonMerge(selected.updateUrl,payload,function(){solution.config=config;document.getElementById('expiryIndicatorJson').value=JSON.stringify(config,null,2);document.getElementById('expiryIndicatorChip').textContent='LOKAL';showStatus('Lokale ExpiryIndicator-Konfiguration wurde erfolgreich aktualisiert.','success');},function(message){showStatus('ExpiryIndicator konnte nicht aktualisiert werden: '+message,'error',true);});}
function parseSolutionConfig(key,rawValue){var parsed={};if(typeof rawValue==='string'&&rawValue.trim().length>0){try{parsed=JSON.parse(rawValue);}catch(error){parsed={};}}if(key==='branding'){return normalizeBrandingConfig(parsed);}if(key==='megamenu'){return normalizeMegaMenuConfig(parsed);}return parsed||{};}
function normalizeBrandingConfig(config){var normalized={cssfiles:[],placeholdertop:{elements:[]},placeholderbottom:{elements:[]}};if(config&&Array.isArray(config.cssfiles)){normalized.cssfiles=config.cssfiles.filter(function(item){return item&&typeof item.path==='string'&&item.path.trim().length>0;}).map(function(item){return{path:item.path.trim()};});}if(config&&config.placeholdertop&&Array.isArray(config.placeholdertop.elements)){normalized.placeholdertop.elements=config.placeholdertop.elements;}else if(config&&Array.isArray(config.elements)){normalized.placeholdertop.elements=config.elements;}if(config&&config.placeholderbottom&&Array.isArray(config.placeholderbottom.elements)){normalized.placeholderbottom.elements=config.placeholderbottom.elements;}return normalized;}
function normalizeMegaMenuConfig(config){return{termSetName:config&&typeof config.termSetName==='string'?config.termSetName.trim():'',cssUrl:config&&typeof config.cssUrl==='string'?config.cssUrl.trim():'',debug:!!(config&&config.debug===true)};}
function parseJsonArray(textareaId,label){var textarea=document.getElementById(textareaId);var rawValue=textarea.value.trim();if(!rawValue){return [];}var parsedValue;try{parsedValue=JSON.parse(rawValue);}catch(error){throw new Error(label+' enthaelt kein gueltiges JSON: '+error.message);}if(!Array.isArray(parsedValue)){throw new Error(label+' muss ein JSON-Array sein.');}return parsedValue;}
function updateSolutionAction(key,config,onSuccess,onError){var solution=state.solutions[key];if(!solution||!solution.info||!solution.info.action){onError('Die passende UserCustomAction wurde nicht gefunden.');return;}var actionId=solution.info.action.Id;if(!actionId){onError('Die UserCustomAction hat keine gueltige Id.');return;}var updateUrl=solution.info.updateBaseUrl+'(guid\''+actionId+'\')';var payload={__metadata:{type:solution.info.actionType||'SP.UserCustomAction'},ClientSideComponentProperties:JSON.stringify(config)};postJsonMerge(updateUrl,payload,onSuccess,onError);}
function readCustomizerActionInfo(definition,onSuccess,onError){var baseSiteUrl=getSiteUrl().replace(/\/$/,'');var endpoints=[{scopeLabel:'Site Collection',readUrl:baseSiteUrl+'/_api/site/UserCustomActions',updateBaseUrl:baseSiteUrl+'/_api/site/UserCustomActions'},{scopeLabel:'Web',readUrl:baseSiteUrl+'/_api/site/rootweb/UserCustomActions',updateBaseUrl:baseSiteUrl+'/_api/site/rootweb/UserCustomActions'}];readCustomizerActionInfoFromEndpoints(definition,endpoints,0,[],onSuccess,onError);}
function readCustomizerActionInfoFromEndpoints(definition,endpoints,index,errors,onSuccess,onError){if(index>=endpoints.length){if(errors.length){onError(errors.join(' | '));return;}onSuccess(null);return;}var endpoint=endpoints[index];getJson(endpoint.readUrl,function(payload){try{var actions=getResultArray(payload);var action=findActiveCustomizerAction(actions,definition);if(action){onSuccess({action:action,updateBaseUrl:endpoint.updateBaseUrl,actionType:action.__metadata&&action.__metadata.type?action.__metadata.type:'SP.UserCustomAction',scopeLabel:endpoint.scopeLabel});return;}readCustomizerActionInfoFromEndpoints(definition,endpoints,index+1,errors,onSuccess,onError);}catch(error){errors.push(error.message||String(error));readCustomizerActionInfoFromEndpoints(definition,endpoints,index+1,errors,onSuccess,onError);}},function(message){errors.push(message);readCustomizerActionInfoFromEndpoints(definition,endpoints,index+1,errors,onSuccess,onError);});}
function findActiveCustomizerAction(actions,definition){if(!Array.isArray(actions)||!definition){return null;}var expected=normalizeGuid(definition.componentId);var fallback=null;for(var i=0;i<actions.length;i++){var action=actions[i];if(normalizeGuid(action&&action.ClientSideComponentId)!==expected){continue;}if(!matchesSolutionTag(action,definition)){continue;}if(!fallback){fallback=action;}if(typeof action.Location==='string'&&action.Location.indexOf('ClientSideExtension.ApplicationCustomizer')!==-1){return action;}}return fallback;}function matchesSolutionTag(action,definition){var description=action&&typeof action.Description==='string'?action.Description:'';if(description.indexOf(CUSTOM_SOLUTION_TAG)===-1){return false;}if(definition&&typeof definition.descriptionTag==='string'&&definition.descriptionTag){return description.indexOf(definition.descriptionTag)!==-1||description===CUSTOM_SOLUTION_TAG;}return true;}
function getJson(url,onSuccess,onError){try{var request=new XMLHttpRequest();request.open('GET',url,true);request.setRequestHeader('Accept','application/json;odata=verbose');request.onreadystatechange=function(){if(request.readyState!==4){return;}if(request.status>=200&&request.status<300){try{onSuccess(JSON.parse(request.responseText||'{}'));}catch(error){onError('Antwort konnte nicht gelesen werden: '+(error.message||String(error)));}return;}onError(buildRequestErrorMessage(request));};request.onerror=function(){onError('Netzwerkfehler beim Lesen von '+url);};request.send();}catch(error){onError(error.message||String(error));}}
function postJsonMerge(url,payload,onSuccess,onError){try{var digest=getRequestDigest();if(!digest){onError('Kein Request Digest gefunden. Laden Sie die Seite neu und versuchen Sie es erneut.');return;}var request=new XMLHttpRequest();request.open('POST',url,true);request.setRequestHeader('Accept','application/json;odata=verbose');request.setRequestHeader('Content-Type','application/json;odata=verbose');request.setRequestHeader('X-RequestDigest',digest);request.setRequestHeader('IF-MATCH','*');request.setRequestHeader('X-HTTP-Method','MERGE');request.onreadystatechange=function(){if(request.readyState!==4){return;}if(request.status>=200&&request.status<300){onSuccess();return;}onError(buildRequestErrorMessage(request));};request.onerror=function(){onError('Netzwerkfehler beim Aktualisieren von '+url);};request.send(JSON.stringify(payload));}catch(error){onError(error.message||String(error));}}
function getRequestDigest(){var digestField=document.getElementById('__REQUESTDIGEST');return digestField&&digestField.value?digestField.value:'';}
function getResultArray(payload){if(payload&&payload.d&&Array.isArray(payload.d.results)){return payload.d.results;}if(payload&&Array.isArray(payload.value)){return payload.value;}return [];}
function normalizeGuid(value){if(typeof value!=='string'){return '';}return value.replace(/[{}]/g,'').toLowerCase();}
function buildRequestErrorMessage(request){var message=String(request.status||0);if(request.statusText){message+=' '+request.statusText;}if(request.responseText){try{var payload=JSON.parse(request.responseText);if(payload&&payload.error&&payload.error.message){message+=': '+(payload.error.message.value||payload.error.message);}}catch(ignoreError){}}return message;}
function getAvailableSolutionKeys(){return Object.keys(SUPPORTED_SOLUTIONS).filter(function(key){return !!state.solutions[key];});}
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 escapeHtml(text){var div=document.createElement('div');div.textContent=text===null||text===undefined?'':String(text);return div.innerHTML;}
</script></div></div>
</asp:Content>

View File

@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
<Module Name="PortalSettingsPageV11" Url="SitePages" RootWebOnly="TRUE">
<File
Path="PortalSettings.aspx"
Url="PortalSettings.aspx"
Type="GhostableInLibrary"
IgnoreIfAlreadyExists="FALSE"
ReplaceContent="TRUE" />
</Module>
</Elements>

View File

@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
<Module Name="PortalSettingsPageV2" Url="SitePages" RootWebOnly="TRUE">
<File
Path="PortalSettings.aspx"
Url="PortalSettings.aspx"
Type="GhostableInLibrary"
IgnoreIfAlreadyExists="FALSE"
ReplaceContent="TRUE" />
</Module>
</Elements>

View File

@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
<Module Name="PortalSettingsPage" Url="SitePages" RootWebOnly="TRUE">
<File
Path="PortalSettings.aspx"
Url="PortalSettings.aspx"
Type="GhostableInLibrary"
IgnoreIfAlreadyExists="FALSE" />
</Module>
</Elements>

View File

@@ -0,0 +1,3 @@
<ApplyElementManifests>
<ElementManifest Location="57bd9daf-48f9-49e2-aebd-11b15a0b5e93\elements-v1.1.xml" />
</ApplyElementManifests>

View File

@@ -0,0 +1,3 @@
<ApplyElementManifests>
<ElementManifest Location="57bd9daf-48f9-49e2-aebd-11b15a0b5e93\elements-v2.xml" />
</ApplyElementManifests>