SRxExplorer.psm1

[Flags()]
enum SRxBorderSides
{
    None   = 0
    Left   = 1
    Top    = 2
    Right  = 4
    Bottom = 8
    All    = 15
}
enum SRxCustomizationFilter { 
    FILTER
    SAVED
    ACTIVESCOPE
    CHANDED 
} 
enum SRxFilteringResult { 
    REMOVED
    PROCESSED
    ATTRIBUTES 
} 
enum SRxNodeState { None; AncestorOfCustomized; Excluded; Modified; Included }

class CustomizationsReader {

    hidden $_environment = $null

    CustomizationsReader () {}
    

    hidden [pscustomobject] resolveFilterNodeExplorer($filterNode, [int]$priority ) {
        try {
            $xpathChild = $filterNode.xpathChild
            $termHost = $filterNode.termHost
            $hashtable = $filterNode.hashtable
            $optype = $filterNode.optype

            $filter = @{}
            if($termHost.IsDeprecated) { $filter["ts_Deprecated"] = $true }
            foreach ($attr in $hashtable.GetEnumerator() ) { 
                if( -not $($attr.Name).StartsWith("ts_") )  {
                    $filter.add($attr.Name, $attr.Value) 
                }
            }
            if($hashtable.ContainsKey("ts_CustomizationTarget")) { $filter["ts_CustomizationTarget"] = $true }
            if(($filter.Count -gt 0)) {
                $details = @{
                    xPath   = $xpathChild
                    Scope   = $priority
                    Filter  = $filter
                    optype  = $optype
                }
                $object = New-Object PSObject -Property $details
                return $object
            }
        } 
        catch {
            Write-Host ($_.Exception.Message) 
            Write-Host ($_.Exception) 
        }
        return $null
    }
    hidden [System.Array] resolveFilters($filters, [int]$priority) {
        $retVal = @()
        try {
            

            ($filters | Where-Object { $_.priority -eq $priority }) | ForEach-Object { 
                $filterNode = $_ 
                $xpathChild = $filterNode.xpathChild
                $processed = $filters | Where-Object { $(($xpathChild -eq $_.xpathChild) -and ($_.priority -gt $priority ) -and ($_.processed -eq $true)) }
                if( $processed ) {
                    $processed | ForEach-Object { 
                        $filterNode.attributes += $_.attributes
                        if( $filterNode.status -ne [SRxFilteringResult]::ATTRIBUTES) { $filterNode.status = $_.status }
                    }
                }
                $fn = $this.resolveFilterNodeExplorer( $filterNode, $priority)
                if( $fn) { $retVal += $fn }
                $_.processed = $true
            }
            

        } catch {
            Write-Host ($_.Exception.Message) 
            Write-Host ($_.Exception) 
        }
        return $retVal
    }
    hidden [string] getXPathChild ([string] $xpathParent, $term, $xmlNode) {

        $xpathChild = $xpathParent + "/" + $xmlNode.Name
    
        $ts_NodeName = $term | Get-SRxCustomPropertyValueByKey_JSON -Key "ts_NodeName"
        if( $null -ne $ts_NodeName )  { 
            $xpathChild = $xpathParent + "/" + $ts_NodeName 
        }
    
        $ts_KeyAttribute = $term | Get-SRxCustomPropertyValueByKey_JSON -Key "ts_KeyAttribute"
        if( $null -ne $ts_KeyAttribute) {
            $ts_KeyAttributeValue = $term | Get-SRxCustomPropertyValueByKey_JSON -Key "ts_KeyAttributeValue"
            if( $null -ne $ts_KeyAttributeValue) {
                $xpathChild = $xpathChild + "[@$ts_KeyAttribute='$ts_KeyAttributeValue']"
            }
            else {
                $xpathChild = $xpathChild + "[@$ts_KeyAttribute='$($term.Name)']"
            }
        }
        return $xpathChild
    }
    hidden [System.XML.XMLNode] selectXMLNode ([System.XML.XMLDocument]$XML, [string]$XPath) {
    
            $NamespaceManager = New-Object -TypeName "Xml.XmlNamespaceManager" -ArgumentList $XML.NameTable
            [string]$namespace = $XML.DocumentElement.NamespaceURI
            $NamespaceManager.AddNamespace("pnp", $Namespace)
            $nodes = $XML.SelectNodes($XPath, $NamespaceManager)
            if($null -eq $nodes)  { return $null }
            if($nodes.Count -eq 0) { return $null }
            return $nodes[0]
    }    
    hidden [pscustomobject] addElement($xmlDoc, $xmlParent, [string]$xpathParent, $termHost, [int]$priority, $optype) {
            try {
                $xmlNodeName = $termHost.Name
                $ts_NodeName = $termHost | Get-SRxCustomPropertyValueByKey_JSON -Key "ts_NodeName"
                if($null -ne $ts_NodeName) {
                    $xmlNodeName = $ts_NodeName               
                }
             
                $xmlNode = $xmlDoc.CreateElement($xmlNodeName)
    
                $hashtable = @{}
                $ts_KeyAttribute = $null
                $ts_KeyAttributeValue = $null
                $termHost.CustomProperties | Resolve-SRxPropertiesArray_JSON | ForEach-Object {
                    $property = $_
    
                    if( $property.Key -ne 'ts_NodeName' ) {
                        if( $property.Key -eq 'ts_KeyAttribute') {
                            $ts_KeyAttribute = $property.Value
                        }
                        elseif( $property.Key -eq 'ts_KeyAttributeValue') {
                            $ts_KeyAttributeValue = $property.Value
                        }
                        else {
                            $hashtable[$property.Key] = $property.Value
                            $xmlAtt = $xmlDoc.CreateAttribute($($property.Key))
                            $xmlAtt.Value = $($property.Value)
                            $xmlNode.Attributes.Append($xmlAtt) | Out-Null
                        }
                    }
                }
                if( $ts_KeyAttribute) {
                    $xmlAtt = $xmlDoc.CreateAttribute($ts_KeyAttribute)
                    $xmlAtt.Value = $ts_KeyAttributeValue
                    $xmlNode.Attributes.Append($xmlAtt) | Out-Null
                }
    
                $xpathChild = $this.getXPathChild( $xpathParent, $termHost, $xmlNode)
                $xmlParent.AppendChild($xmlNode) | Out-Null
            } catch {
                Write-Host ($_.Exception.Message) 
                Write-Host ($_.Exception) 
                return $null  
            }

            if($xpathChild -eq "/pnp:Provisioning/pnp:Templates/pnp:ProvisioningTemplate/pnp:ProvisioningTemplate") { 
                $xpathChild = "/pnp:Provisioning/pnp:Templates/pnp:ProvisioningTemplate" 
            }
    
            $filterNode = [pscustomobject]@{
                'xpathParent'   = $xpathParent
                'xpathChild'    = $xpathChild
                'termHost'      = $termHost
                'hashtable'     = $hashtable
                'xmlNode'       = $xmlNode
                'priority'      = $priority
                'processed'     = $false
                'status'        = $null
                'attributes'    = @()
                'optype'        = $optype
            }    
            
            return $filterNode
    }
    hidden [System.Array] addElements($xmlDoc, $xmlParent, [string]$xpathParent, $provisioningActivity, $termHost, [int]$priority, $optype) {
        $retVal = @()
        try {
                
            if( $termHost.Terms) {            
                $termHost.Terms | Resolve-SRxTermsArray_JSON | ForEach-Object { 
                    $run = $false
                    if ( -not $provisioningActivity) { $run = $true }

                    $ts_ProvisioningActivity = $_ | Get-SRxCustomPropertyValueByKey_JSON -Key "ts_ProvisioningActivity"

                    if( $null -ne $ts_ProvisioningActivity) {
                        if(($ts_ProvisioningActivity -eq $provisioningActivity)) {
                            $run = $true
                        }
                    }
                    if( $run ) {
                        
                        $filterNode = $this.addElement($xmlDoc, $xmlParent, $xpathParent, $_, $priority, $optype)
                        $retVal += $filterNode
                        $xmlNode = $filterNode.xmlNode
                        $xpathChild = $this.getXPathChild( $xpathParent, $_, $xmlNode)
                        $filterNodes = $this.addElements( $xmlDoc, $xmlNode, $xpathChild, $null, $_, $priority, $optype)
                        $retVal += $filterNodes
                    }
                }
            }
        } catch {
            Write-Host "addElements: $($_.Exception.Message)"  
            Write-Host ($_.Exception) 
        }
        return $retVal
    }
    hidden [System.Array] getSiteTemplateCustomizations($termHost, [int]$priority, $optype) {
        $filters = $null
        try {
            
            $tID = $termHost | Get-SRxCustomPropertyValueByKey_JSON -Key "ts_ProvisioningTemplate"
            if( $tID ) {
                $provisioningTemplateTerm_ = Get-SRxTermByCustomProperty_JSON -ID -Value $tID
                if($provisioningTemplateTerm_) {
                    if( -not $provisioningTemplateTerm_.IsDeprecated) {
                        [xml]$xmlDoc = New-Object System.Xml.XmlDocument
                        $xmlDoc.LoadXml("<?xml version=`"1.0`" encoding=`"utf-8`"?><pnp:Provisioning xmlns:pnp='http://schemas.dev.office.com/PnP/2021/03/ProvisioningSchema'><pnp:Templates ID='1'><pnp:ProvisioningTemplate ID='2' /></pnp:Templates></pnp:Provisioning>")
                        $xmlProvisioningTemplate = $this.selectXMLNode( $xmlDoc, "//pnp:Provisioning/pnp:Templates/pnp:ProvisioningTemplate")
                        $xpathParent = "/pnp:Provisioning/pnp:Templates/pnp:ProvisioningTemplate" 
                        $filters = $this.addElements( $xmlDoc, $xmlProvisioningTemplate, $xpathParent, "Optimize-SiteTemplate", $provisioningTemplateTerm_, $priority, $optype)

                    }
                }
                else {  Write-Host $(" x Bad pointer on custom Provisioning Template")  }
            }
        } catch {
            Write-Host "getSiteTemplateCustomizations: $($_.Exception.Message)"  
            Write-Host ($_.Exception) 
        }
        return $filters
    }
    [System.Array] Nodes([System.Collections.Hashtable] $props) {

        if( -not $global:SRxEnv.ProvisioningDatabase) { Connect-SRxProvisioningDatabase_JSON  }

        $treeNode = $null
        $optype = 1
        $activeScope = -1

        $profile2 = $props["ts_KeyProvisioning"]
        $environment = $props["ts_Environment"]
        $siteDesign = $props["ts_Design"]
        $sites = $props["ts_Sites"]
        $site = $props["ts_Site"]

        $siteUrl = $props["ts_URL"]

        $termID = $null
        $filterNodes = @()
        try {

            if( $siteDesign -eq $props["ts_Site"]) {

                $environment_ = Get-SRxTermByCustomProperty_JSON -Key "ts_Environment" -Value $environment           
                $design_ = $environment_ | Get-SRxTermByCustomProperty_JSON -Key "ts_Design" -Value $siteDesign
                $sites_ = $design_ | Get-SRxTermByCustomProperty_JSON -Key "ts_Sites" -Value $siteDesign
                $site_ = Get-SRxTermByCustomProperty_JSON -Key "ts_URL" -Value $siteUrl   
                $termID = $site_.Id   
            }    

            $filters = @()
            
            if($profile2) {
                $profile_ = Get-SRxTermByCustomProperty_JSON -Key "ts_KeyProvisioning" -Value "Provisioning"
                $filters += $this.getSiteTemplateCustomizations( $profile_, 0,  $(-1))

                $treeNode = $props["node_ts_KeyProvisioning"]
                $optype = 1
                $activeScope =  0

                if($environment) {

                    $environment_ = Get-SRxTermByCustomProperty_JSON -Key "ts_Environment" -Value $environment           
                    $filters += $this.getSiteTemplateCustomizations( $environment_, 1, $(-1))
                
                    $treeNode = $props["node_ts_Environment"]
                    $optype = 1
                    $activeScope =  1
                    
                    if($siteDesign) {
                        $master_ = Get-SRxTermByCustomProperty_JSON -Key "ts_Environment" -Value "Master"           
                        $design_ = $master_ | Get-SRxTermByCustomProperty_JSON -Key "ts_Design" -Value $siteDesign
                        $filters += $this.getSiteTemplateCustomizations( $design_, 2, $(-1))

                        $treeNode = $props["node_ts_Design"]
                        $optype = 1
                        $activeScope =  2
    
                        
                        $filtersNewSite = @()
                        $filtersOldSite = @()
            
                        if($sites -and -not $site) {
                            $environment_ = Get-SRxTermByCustomProperty_JSON -Key "ts_Environment" -Value $environment           
                            $design_ = $environment_ | Get-SRxTermByCustomProperty_JSON -Key "ts_Design" -Value $siteDesign
                            if($design_) {
                                $sites_ = $design_ | Get-SRxTermByCustomProperty_JSON -Key "ts_Sites" -Value $siteDesign
                                if($sites_) {
                                    $filtersNewSite += $this.getSiteTemplateCustomizations( $sites_, 3, 0)

                                    $treeNode = $props["node_ts_Sites"]
                                    $optype = 0
                                    $activeScope =  3
                                            
                                    
                                }
                            }
                        }
                        if($site) {
                            if($termID) {
                                $site_ = Get-SRxTermByCustomProperty_JSON -ID -Value $termID   
                                if($site_) {       
                                    $filtersOldSite += $this.getSiteTemplateCustomizations( $site_, 3, 1)

                                    $treeNode = $props["node_ts_Site"]
                                    $optype = 1
                                    $activeScope =  3

                                }
                            }
                        }
                            $filters += $filtersNewSite
                            $filters += $filtersOldSite
                    }
                }
            }

            $priority = 3
            do {
                    $fn = $this.resolveFilters( $filters, $priority)
                    if( $fn ) { $filterNodes += $fn }    
                $priority = $priority - 1
            } 
            while( $priority -ge 0)


        } catch {
            Write-Host "Nodes: $($_.Exception.Message)" 
            Write-Host ($_.Exception) 
            return $null  
        }
        $filter = [pscustomobject]@{
            'TreeNode'      = $treeNode
            'Optype'        = $optype
            'ActiveScope'   = $activeScope
            'Nodes'         = $filterNodes
        }    

        return $filter
    } 

}
$( . $("$PSScriptRoot\Explorer\Assemblies.ps1")) 
$( . $("$PSScriptRoot\Explorer\Explorer.ps1")) 
$( . $("$PSScriptRoot\Explorer\TemplateExplorer.ps1")) 
$( . $("$PSScriptRoot\Pipes\Assemblies.ps1")) 
$( . $("$PSScriptRoot\Pipes\NamedPipes.ps1")) 
$( . $("$PSScriptRoot\Runspaces\Assemblies.ps1")) 
$( . $("$PSScriptRoot\Runspaces\Runspaces.ps1")) 
$( . $("$PSScriptRoot\TreeControl\Assemblies.ps1"))
$( . $("$PSScriptRoot\TreeControl\SRxBorderedUserControl.ps1"))
$( . $("$PSScriptRoot\TreeControl\SRxTreeControl.ps1"))
$( . $("$PSScriptRoot\TreeControl\SRxCheckCommandToolStripMenuItem.ps1")) 
$( . $("$PSScriptRoot\TreeControl\SRxSchemaTreeControl.ps1")) 
$( . $("$PSScriptRoot\TreeControl\SRxXmlTreeControl.ps1"))
$( . $("$PSScriptRoot\TreeControl\SRxCustomizationEngine.ps1"))
$( . $("$PSScriptRoot\TreeControl\SRxDeferredAction.ps1"))
function ExecuteRulesetScriptFile() {
   
    $t = $("$PSScriptRoot\Explorer\ExecuteRulesetJob.ps1")
    Write-Host "ExecuteRulesetScriptFile = $t"
    return $t
}
function HideNodeCheckBox2([System.Windows.Forms.TreeNode]$node) {
    if ($node.TreeView.CheckBoxes) {
        $tvi = [Win32Functions.Win32TreeView+TVITEM]::new()
        $tvi.hItem = $node.Handle
        $tvi.mask = [Win32Functions.Win32TreeView]::TVIF_STATE
        $tvi.stateMask = [Win32Functions.Win32TreeView]::TVIS_STATEIMAGEMASK
        $tvi.state = 0
        [System.IntPtr]$lparam = [System.Runtime.InteropServices.Marshal]::AllocHGlobal([System.Runtime.InteropServices.Marshal]::SizeOf($tvi))
        [System.Runtime.InteropServices.Marshal]::StructureToPtr($tvi, $lparam, $false)
        [Win32Functions.Win32TreeView]::SendMessage($node.TreeView.Handle, [Win32Functions.Win32TreeView]::TVM_SETITEM, [System.IntPtr]::Zero, $lparam)
    }
}
function LoadBitmap($path) {
    $bitmapPath = $PSScriptRoot + $path
    $bitmap = [System.Drawing.Bitmap]::FromFile($bitmapPath)
    return $bitmap
}
function ResumeProcess($procId) {
    return $([Kernel32]::DebugActiveProcessStop($procId))
}
function SuspendProcess($procId) {
    return $([Kernel32]::DebugActiveProcess($procId))
}
function CustomizationsReader{
    return [CustomizationsReader]::new()
}
function Explorer{
    Param(
        [string]$schema,
        [string]$source
    )    
    return [TemplateExplorer]::new( $schema, $source)
}
function New-Runspaces{
    Param(
        [string]$ScriptFile
    )
    return [Runspaces]::new($ScriptFile)
}
function New-SRxCheckCommandToolStripMenuItem {
    [OutputType([object])]
    param()
    return [SRxCheckCommandToolStripMenuItem]::new()
}
function New-SRxCustomizationEngine {
    [OutputType([object])]
    param()
    return [SRxCustomizationEngine]::new()
}

function New-SRxDeferredAction {
    [OutputType([object])]
    param()
    return [SRxDeferredAction]::new()
}
function New-SRxPipeClient{
    Param(
        [string]$PipeName
    )
    
    return [PipeClient]::new($PipeName)
}
function New-SRxPipeServer{
    Param(
        [string]$PipeName,
        [boolean]$DoEvents
    )
    
    return [PipeServer]::new($PipeName, $DoEvents)
}
function New-SRxSchemaTreeControl {
    [OutputType([object])]
    param()
    return [SRxSchemaTreeControl]::new()
}

function New-SRxTreeControl {
    [OutputType([object])]
    param()
    return [SRxTreeControl]::new()
}

function New-SRxXmlTreeControl {
    [OutputType([object])]
    param()
    return [SRxXmlTreeControl]::new()
}
function Receive-StringFromPipeServer( [string]$PipeName, [string]$Message ) {
    $data = $null
    try {
        $client = New-SRxPipeClient $PipeName
        $client.Connect()
        $client.Write($Message)
        $data = $client.Read()
        $client.Close()
    }
    catch {
        Write-Host ($_.Exception.Message) 
        Write-Host ($_.Exception) 
    }
    return $data
}
function Request-SRxPipeServer( [string]$PipeName, $Message ) {
    $data = $null
    try {
        $client = New-SRxPipeClient $PipeName
        $client.Connect()
        $client.Write($Message)
        $data = $client.ReadObject()
        $client.Close()
    }
    catch {
        Write-Host "Request-SRxPipeServer: $($_.Exception.Message)" 
        Write-Host ($_.Exception) 
        $data = $null
    }
    return $data
}
function Send-StringToPipeServer( [string]$PipeName, [string]$Message ) {
    try {
        $client = New-SRxPipeClient $PipeName
        $client.Connect()
        $client.Write($Message)
        $client.Close()
    }
    catch {
        Write-Host ($_.Exception.Message) 
        Write-Host ($_.Exception) 
    }
}