TreeControl/SRxSchemaTreeControl.ps1
|
class SRxSchemaTreeControl : SRxTreeControl { hidden [string[]] $_roleKeys = @( 'ts_KeyProvisioning','ts_TermGroup','ts_Site','ts_Sites', 'ts_SubSite','ts_SubSites','ts_Design','ts_Environment' ) [bool] $ShowCustomizations = $false [bool] $Verbose = $false [bool] $StoreOuterXml = $false [string] $LastLoadStats = '' hidden [string[]] $_treeFilter = @('Operations') hidden [hashtable] $_groupKeys = @{} SRxSchemaTreeControl() : base() { $this.SetSideBorder( [SRxBorderSides]::Left, [System.Drawing.SystemColors]::ControlDark, [float]1.0 ) $this.Padding = [System.Windows.Forms.Padding]::new(1, 0, 0, 0) $this.LazyRealize = $true } [void] LoadFromXml($xmlRoot) { try { $this.ClearModel() if ($null -eq $xmlRoot) { $this.SyncToUI(); return } $this.prepareBuild() $this.addXmlChildren($xmlRoot, '') $this.SyncToUI() } catch { Write-Host "SRxSchemaTreeControl.LoadFromXml - $($_.Exception.Message)" } } hidden [void] addXmlChildren($xElement, [string]$parentId) { foreach ($xEl in $xElement.ChildNodes) { if ($xEl.LocalName -eq 'CustomProperties') { continue } $treeFilter = $this._treeFilter $nameAttr = '' try { if ($null -ne $xEl.Attributes['Name']) { $nameAttr = [string]$xEl.Attributes['Name'].Value } } catch { } if ($nameAttr -in $treeFilter) { continue } if ($xEl.LocalName -in @('TermSets','Terms')) { if ($xEl.HasChildNodes) { $this.addXmlChildren($xEl, $parentId) } continue } $id = '' try { if ($null -ne $xEl.Attributes['ID']) { $id = [string]$xEl.Attributes['ID'].Value } } catch { } if ([string]::IsNullOrEmpty($id)) { $pos = 0 for ($k = 0; $k -lt $xElement.ChildNodes.Count; $k++) { if ($xElement.ChildNodes[$k] -eq $xEl) { $pos = $k; break } } $id = ('syn:{0}:{1}' -f $parentId, $pos) } $text = $nameAttr if ([string]::IsNullOrEmpty($text)) { $text = [string]$xEl.LocalName if ($text -eq 'Property') { try { $text = [string]$xEl.Attributes['Key'].Value } catch { } } } $line = @($text -split '--') $title = [string]$line[0] $tooltip = $null if ($line.Count -gt 1) { $tooltip = [string]$line[1] [datetime]$logDate = New-Object DateTime if ([DateTime]::TryParseExact($tooltip, 'o', [System.Globalization.CultureInfo]::InvariantCulture, [System.Globalization.DateTimeStyles]::None, [ref]$logDate)) { $tooltip = $logDate.ToString('yyyy-MM-dd HH:mm:ss') } } $deprecated = $false try { $s = [string]$xEl.Attributes['IsDeprecated'].Value if (-not [string]::IsNullOrEmpty($s)) { $deprecated = ($s -eq 'true') } } catch { } $props = @{} foreach ($cp in $xEl.ChildNodes) { if ($cp.LocalName -ne 'CustomProperties') { continue } foreach ($p in $cp.ChildNodes) { if ($p.LocalName -ne 'Property') { continue } try { $props[[string]$p.Attributes['Key'].Value] = [string]$p.Attributes['Value'].Value } catch { } } } $role = 'Unknown' foreach ($rk in $this._roleKeys) { if ($props.ContainsKey($rk) -and -not [string]::IsNullOrEmpty([string]$props[$rk])) { if ($rk -eq 'ts_KeyProvisioning') { $role = 'ProvisioningSchema' } elseif ($rk -eq 'ts_TermGroup') { $role = 'Root' } elseif ($rk -eq 'ts_Site') { $role = 'Site' } elseif ($rk -eq 'ts_SubSite') { $role = 'SubSite' } elseif ($rk -in @('ts_Sites','ts_SubSites')) { $role = 'SitesCollection' } elseif ($rk -eq 'ts_Design') { $role = 'Design' } elseif ($rk -eq 'ts_Environment') { $role = 'Environment' } break } } $hostId = $parentId if ($props.ContainsKey('ts_Group') -and -not [string]::IsNullOrEmpty([string]$props['ts_Group'])) { $gText = [string]$props['ts_Group'] $gKey = $gText if ($this._groupKeys.ContainsKey($gText)) { $gKey = [string]$this._groupKeys[$gText] } else { try { $gKey = Remove-SRxStringSpecialCharacter -String $gText } catch { } $this._groupKeys[$gText] = $gKey } $gid = ('{0}#{1}' -f $parentId, $gKey) if ($null -eq $this.GetNode($gid)) { $this.AddNode($gid, $parentId, $gText, @{ role = 'Group'; synthetic = $true }) } $hostId = $gid } $tag = @{ termID = $id role = $role CustomProperties = $props Operations = @{} Xml = $xEl deprecated = $deprecated tooltip = $tooltip title = $title synthetic = ($id -like 'syn:*') } if ($this.StoreOuterXml) { $tag['OuterXml'] = [string]$xEl.OuterXml } $this.AddNode($id, $hostId, $text, $tag) if ($xEl.HasChildNodes) { $this.addXmlChildren($xEl, $id) } } } [string] GetRole([string]$id) { $n = $this.GetNode($id) if ($null -eq $n -or $null -eq $n.Tag) { return 'Unknown' } if ($n.Tag -is [hashtable] -and $n.Tag.ContainsKey('role')) { return [string]$n.Tag['role'] } return 'Unknown' } [string] GetOwnProp([string]$id, [string]$key) { $n = $this.GetNode($id) if ($null -eq $n -or $null -eq $n.Tag) { return '' } if ($n.Tag -is [hashtable] -and $n.Tag.ContainsKey('CustomProperties')) { $cp = $n.Tag['CustomProperties'] if ($cp -is [hashtable] -and $cp.ContainsKey($key)) { return [string]$cp[$key] } } return '' } [string] GetEffectiveProp([string]$id, [string]$key) { $own = $this.GetOwnProp($id, $key) if (-not [string]::IsNullOrEmpty($own)) { return $own } foreach ($a in $this.AncestorIds($id)) { $v = $this.GetOwnProp($a, $key) if (-not [string]::IsNullOrEmpty($v)) { return $v } } return '' } [string] GetOwningId([string]$id, [string]$key) { if (-not [string]::IsNullOrEmpty($this.GetOwnProp($id, $key))) { return $id } foreach ($a in $this.AncestorIds($id)) { if (-not [string]::IsNullOrEmpty($this.GetOwnProp($a, $key))) { return $a } } return '' } [string] GetRoleValue([string]$id) { $role = $this.GetRole($id) switch ($role) { 'ProvisioningSchema' { return $this.GetOwnProp($id, 'ts_KeyProvisioning') } 'Root' { return $this.GetOwnProp($id, 'ts_TermGroup') } 'Site' { return $this.GetOwnProp($id, 'ts_Site') } 'SubSite' { return $this.GetOwnProp($id, 'ts_SubSite') } 'Design' { return $this.GetOwnProp($id, 'ts_Design') } 'Environment' { return $this.GetOwnProp($id, 'ts_Environment') } } return '' } [boolean] IsSynthetic([string]$id) { if ([string]::IsNullOrEmpty($id)) { return $true } if ($id.Contains('#')) { return $true } if ($id.StartsWith('syn:')) { return $true } return $false } [void] ApplyDeprecatedOverlay() { try { $styles = @{} foreach ($id in @($this._model.Keys)) { $n = $this._model[$id] if ($null -eq $n -or -not ($n.Tag -is [hashtable])) { continue } if ($n.Tag.ContainsKey('deprecated') -and [bool]$n.Tag['deprecated']) { $styles[[string]$id] = @{ Fore = [System.Drawing.Color]::LightGray } } } $this.SetOverlayStyles('deprecated', $styles) } catch { Write-Host "ApplyDeprecatedOverlay - $($_.Exception.Message)" } } [void] ApplyScopeOverlay([string]$scopeId) { try { $this.PushOverlay('scope') $this.ClearOverlayStyles('scope') if (-not [string]::IsNullOrEmpty($scopeId)) { $this.SetOverlayStyle('scope', $scopeId, @{ Fore = [System.Drawing.Color]::Red FontStyle = [System.Drawing.FontStyle]::Bold }) } $this.applyAppearance() } catch { Write-Host "ApplyScopeOverlay - $($_.Exception.Message)" } } [void] ClearScopeOverlay() { $this.PopOverlay('scope') } [string[]] AllIds() { $out = New-Object System.Collections.ArrayList foreach ($rid in $this.RootIds()) { $this.collectIds($rid, $out) } return @($out) } hidden [void] collectIds([string]$id, $acc) { [void]$acc.Add($id) foreach ($c in $this.ChildIds($id)) { $this.collectIds($c, $acc) } } [void] LoadFromRoot($termGroupElement, [string]$rootKey, [string]$rootText) { try { $swModel = [System.Diagnostics.Stopwatch]::StartNew() $this.ClearModel() $this.prepareBuild() $outer = '' if ($null -ne $termGroupElement -and $this.StoreOuterXml) { $outer = [string]$termGroupElement.OuterXml } $rootTermId = '' if ($null -ne $termGroupElement) { $rootTermId = $rootKey } $this.AddNode($rootKey, '', $rootText, @{ termID = $rootTermId role = 'Root' CustomProperties = @{ 'ts_TermGroup' = $rootText } Operations = @{} OuterXml = $outer Xml = $termGroupElement deprecated = $false tooltip = $null title = $rootText synthetic = $false }) if ($null -ne $termGroupElement) { $this.addXmlChildren($termGroupElement, $rootKey) } $modelMs = $swModel.ElapsedMilliseconds $swView = [System.Diagnostics.Stopwatch]::StartNew() $this.SyncToUI() $this.LastLoadStats = ('model {0} ms ({1} nodes) + view {2} ms ({3} TreeNodes realized)' -f $modelMs, $this.Count(), $swView.ElapsedMilliseconds, $this.RealizedCount()) if (Get-Command TR -ErrorAction Ignore) { TR ('[TREE] LoadFromRoot: ' + $this.LastLoadStats) } if ($this.Verbose) { Write-Host ("LoadFromRoot - model built: {0} nodes" -f $this.Count()) } } catch { Write-Host "LoadFromRoot - FAILED (model is PARTIAL): $($_.Exception.Message)" try { $this.SyncToUI() } catch { } } } [object] FindUiNode([string]$id) { try { if (-not [string]::IsNullOrEmpty($id) -and $this._uiIndex.ContainsKey($id)) { $n = $this._uiIndex[$id] if ($null -ne $n -and $null -ne $n.TreeView) { return $n } } if (-not [string]::IsNullOrEmpty($id) -and $this._model.Contains($id)) { return $null } } catch { } try { $found = $this.InnerTree().Nodes.Find($id, $true) if ($found.Count -gt 0) { return $found[0] } } catch { } return $null } [object] SelectedTag() { $id = $this.SelectedId() if ([string]::IsNullOrEmpty($id)) { return $null } $n = $this.GetNode($id) if ($null -eq $n) { return $null } return $n.Tag } hidden [void] prepareBuild() { $f = @('Operations') if (-not $this.ShowCustomizations) { $f += 'Customizations' } $this._treeFilter = $f } static [string] OuterXmlOf($tag) { if ($null -eq $tag -or -not ($tag -is [hashtable])) { return '' } $o = [string]$tag['OuterXml'] if (-not [string]::IsNullOrEmpty($o)) { return $o } $x = $tag['Xml'] if ($x -is [System.Xml.XmlNode]) { return [string]$x.OuterXml } return '' } [string] GetOuterXml([string]$id) { $n = $this.GetNode($id) if ($null -eq $n) { return '' } return [SRxSchemaTreeControl]::OuterXmlOf($n.Tag) } [void] ShowCustomizationBranches([bool]$show) { try { if ($show) { $this.PopOverlay('customizations-hidden') return } $this.PushOverlay('customizations-hidden') $this.ClearOverlayStyles('customizations-hidden') foreach ($id in $this.AllIds()) { if ($this.GetRole($id) -ne 'Customizations') { continue } $this.SetOverlayStyle('customizations-hidden', $id, @{ Hidden = $true }) } $this.RebuildUI() } catch { Write-Host "ShowCustomizationBranches - $($_.Exception.Message)" } } } |