SRxExplorer.psm1

#Region '.\Enum\SRxCustomizationFilter.ps1' 0
enum SRxCustomizationFilter { 
    FILTER
    SAVED
    ACTIVESCOPE
    CHANDED 
} 
#EndRegion '.\Enum\SRxCustomizationFilter.ps1' 7
#Region '.\Enum\SRxFilteringResult.ps1' 0
enum SRxFilteringResult { 
    REMOVED
    PROCESSED
    ATTRIBUTES 
} 
#EndRegion '.\Enum\SRxFilteringResult.ps1' 6
#Region '.\Classes\CustomizationsReader.ps1' 0

class CustomizationsReader {

    hidden $_environment = $null

    #--------------------
    # Constructor
    #--------------------
    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
                #Write-Host "xElement = $($targetNode.LocalName)"
                #Write-Host ($filter | FT | Out-String)
                return $object
            }
        } 
        catch {
            Write-Host ($_.Exception.Message) 
            Write-Host ($_.Exception) 
        }
        return $null
    }
    hidden [System.Array] resolveFilters($filters, [int]$priority) {
        $retVal = @()
        try {
            #if( $titles ) { Write-SRx INFO " | $($titles[$priority])" }
            

            ($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)']"
            }
        }
        #Write-Host "xpathChild = $xpathChild"
        return $xpathChild
    }
    hidden [System.XML.XMLNode] selectXMLNode ([System.XML.XMLDocument]$XML, [string]$XPath) {
            #$namespace = @{ pnp = "http://schemas.dev.office.com/PnP/2021/03/ProvisioningSchema" }
            #$nodes = select-xml -Xml $XML -Namespace $namespace -XPath $XPath
    
            # Get namespace
            $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) {
    #Write-Host "addElement"
            try {
                #create XmlElement
                $xmlNodeName = $termHost.Name
                $ts_NodeName = $termHost | Get-SRxCustomPropertyValueByKey_JSON -Key "ts_NodeName"
                if($null -ne $ts_NodeName) {
                    $xmlNodeName = $ts_NodeName               
                }
             
                $xmlNode = $xmlDoc.CreateElement($xmlNodeName)
    
                #create XmlAttributes
                $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 {
                            # Creation of an attribute in the principal node
                            $hashtable[$property.Key] = $property.Value
                            $xmlAtt = $xmlDoc.CreateAttribute($($property.Key))
                            $xmlAtt.Value = $($property.Value)
                            $xmlNode.Attributes.Append($xmlAtt) | Out-Null
                        }
                    }
                }
                if( $ts_KeyAttribute) {
                    # Creation of an attribute in the principal node
                    $xmlAtt = $xmlDoc.CreateAttribute($ts_KeyAttribute)
                    $xmlAtt.Value = $ts_KeyAttributeValue
                    $xmlNode.Attributes.Append($xmlAtt) | Out-Null
                }
    
                # Update target xml node
                $xpathChild = $this.getXPathChild( $xpathParent, $termHost, $xmlNode)
                # Add the node to the document
                $xmlParent.AppendChild($xmlNode) | Out-Null
            } catch {
                Write-Host ($_.Exception.Message) 
                Write-Host ($_.Exception) 
                return $null  
            }

            #---------------- exception fix for root ProvisioningTemplate node
            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 #$xmlNode #success
    }
    hidden [System.Array] addElements($xmlDoc, $xmlParent, [string]$xpathParent, $provisioningActivity, $termHost, [int]$priority, $optype) {
#Write-Host "addElements"
        $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
                        }
                    }
                    #Write-SRx VERBOSE ("Add-Elements run = $run")
                    if( $run ) {
                        
                        $filterNode = $this.addElement($xmlDoc, $xmlParent, $xpathParent, $_, $priority, $optype)
                        $retVal += $filterNode
                        $xmlNode = $filterNode.xmlNode
                        #if(-not $_.IsDeprecated) { #.IsAvailableForTagging) {
                        $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 $null
        }
        return $retVal
    }
    hidden [System.Array] getSiteTemplateCustomizations($termHost, [int]$priority, $optype) { #[boolean]$save,[string]$fileName,
        $filters = $null
#Write-Host "getSiteTemplateCustomizations"
        try {
            
            $tID = $termHost | Get-SRxCustomPropertyValueByKey_JSON -Key "ts_ProvisioningTemplate"
            if( $tID ) {
#Write-Host "tID = $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)

                        # Store to a file
# if($save) {
# $customXmlTemplate = $fileName
# $xmlDoc.Save($customXmlTemplate)
# Write-SRx VERBOSE $(" > Saved custom xml template: $customXmlTemplate")
# }
                    }
                }
                else {  Write-SRx WARNING $(" 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) { #[string]$environment, [string]$siteDesign, [string]$termID) {

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

        $treeNode = $null
        $optype = 1 #update
        $activeScope = -1 #all

        $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"]) { #on site node

                $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 = @()
            #$titles = @()
            
            # profile
            #Write-Host $(" Profile")
            #$titles += "Global | PnP site template customizations:"
            if($profile2) {
                $profile_ = Get-SRxTermByCustomProperty_JSON -Key "ts_KeyProvisioning" -Value "Provisioning"
                $filters += $this.getSiteTemplateCustomizations( $profile_, 0,  $(-1)) # -save $save -fileName $($desfolderPath + "\profile.xml")

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

    # Write-Host ( $filters | FL | Out-String)
                # environment
                #$titles += "$environment | environment customizations:"
                if($environment) {

                    #Write-Host $(" Environment : $environment")
                    $environment_ = Get-SRxTermByCustomProperty_JSON -Key "ts_Environment" -Value $environment           
                    $filters += $this.getSiteTemplateCustomizations( $environment_, 1, $(-1)) # -save $save -fileName $($desfolderPath + "\environment.xml")
                
                    $treeNode = $props["node_ts_Environment"]
                    $optype = 1 #update
                    $activeScope =  1 #environment
                    
                    # design
                    #$titles += "$siteDesign | design customizations:"
                    if($siteDesign) {
                        #Write-Host $(" Design : $siteDesign")
                        #-----------------------------------------------------------------------------------------------------
                        # Design customization block physically resides under Master/[Design Name] environment at termstore
                        # XXXXX However [Design Name] under Environment has reference on that customization block
                        #-----------------------------------------------------------------------------------------------------
                        $master_ = Get-SRxTermByCustomProperty_JSON -Key "ts_Environment" -Value "Master"           
                        $design_ = $master_ | Get-SRxTermByCustomProperty_JSON -Key "ts_Design" -Value $siteDesign
                        #$design_ = $environment_ | Get-SRxTermByCustomProperty_JSON -Key "ts_Design" -Value $siteDesign
                        $filters += $this.getSiteTemplateCustomizations( $design_, 2, $(-1)) #-save $save -fileName $($desfolderPath + "\design.xml")

                        $treeNode = $props["node_ts_Design"]
                        $optype = 1 #update
                        $activeScope =  2 #design
    
                        
                        $filtersNewSite = @()
                        $filtersOldSite = @()
            
                        # sites
                        #$titles += "$siteDesign | new site customizations:"
                        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_) { #may not exists if no sites created yet
                                $sites_ = $design_ | Get-SRxTermByCustomProperty_JSON -Key "ts_Sites" -Value $siteDesign
                                if($sites_) {
                                    $filtersNewSite += $this.getSiteTemplateCustomizations( $sites_, 3, 0) # -save $save -fileName $($desfolderPath + "\sites.xml")

                                    $treeNode = $props["node_ts_Sites"]
                                    $optype = 0 #new
                                    $activeScope =  3 #site
                                            
                                    
                                }
                            }
                        }
                        # site
                        if($site) {
                            if($termID) {
                                $site_ = Get-SRxTermByCustomProperty_JSON -ID -Value $termID   
                                if($site_) {       
                                    #$titles += "$($site_.Name) | site customizations:"
                                    $filtersOldSite += $this.getSiteTemplateCustomizations( $site_, 3, 1) #-save $save -fileName $($desfolderPath + "\site.xml")

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

                                }
                            }
                        }
                        #if( $explorerMode) {
                            $filters += $filtersNewSite
                            $filters += $filtersOldSite
                        #}
                        #else {
                        # if( $isNewSite ) { $filters += $filtersNewSite }
                        # else { $filters += $filtersOldSite }
                        #}
                    }
                }
            }

            $priority = 3
            do {
                #if( $explorerMode ) {
                    $fn = $this.resolveFilters( $filters, $priority)
                    if( $fn ) { $filterNodes += $fn }    
                #}
                #else {
                # Resolve-Filters -xmlSiteTemplateDoc $xmlSiteTemplateDoc -filters $filters -titles $titles -priority $priority -isNewSite $isNewSite | Out-Null
                #}
                $priority = $priority - 1
            } 
            while( $priority -ge 0)


            #if( $explorerMode) {
            # $global:SRxEnv.SetCustomProperty("GetXMLTemplate3", $xmlSiteTemplateDoc)
            # $global:SRxEnv.SetCustomProperty("FilterNodes", $filterNodes)

            # $xmlFilterNodes = $desfolderPath + "\FilterNodes.xml"
            # $filterNodes | Export-Clixml -Path $xmlFilterNodes -Force
            #}
            
            
        } catch {
            Write-Host "Nodes: $($_.Exception.Message)" 
            Write-Host ($_.Exception) 
            return $null  
        }
        $filter = [pscustomobject]@{
            'TreeNode'      = $treeNode
            'Optype'        = $optype
            'ActiveScope'   = $activeScope
            'Nodes'         = $filterNodes
        }    

        return $filter
    } 

}
#EndRegion '.\Classes\CustomizationsReader.ps1' 413
#Region '.\Classes\Explorer.ps1' 0

$( . $("$PSScriptRoot\Explorer\Assemblies.ps1")) 
$( . $("$PSScriptRoot\Explorer\Explorer.ps1")) 


#EndRegion '.\Classes\Explorer.ps1' 6
#Region '.\Classes\Pipes.ps1' 0
$( . $("$PSScriptRoot\Pipes\Assemblies.ps1")) 
$( . $("$PSScriptRoot\Pipes\NamedPipes.ps1")) 
#EndRegion '.\Classes\Pipes.ps1' 3
#Region '.\Classes\Runspaces.ps1' 0
$( . $("$PSScriptRoot\Runspaces\Assemblies.ps1")) 
$( . $("$PSScriptRoot\Runspaces\Runspaces.ps1")) 
#EndRegion '.\Classes\Runspaces.ps1' 3
#Region '.\Private\ExecuteRulesetScriptFile.ps1' 0
function ExecuteRulesetScriptFile() {
    $("$PSScriptRoot\Explorer\ExecuteRulesetJob.ps1")
}
#EndRegion '.\Private\ExecuteRulesetScriptFile.ps1' 4
#Region '.\Private\HideNodeCheckBox2.ps1' 0
function HideNodeCheckBox2([System.Windows.Forms.TreeNode]$node) {
    # P/invoke hack to hide Node CheckBox
    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)
    }
}
#EndRegion '.\Private\HideNodeCheckBox2.ps1' 14
#Region '.\Private\LoadBitmap.ps1' 0
function LoadBitmap($path) {
    $bitmapPath = $PSScriptRoot + $path
    $bitmap = [System.Drawing.Bitmap]::FromFile($bitmapPath)
    return $bitmap
}
#EndRegion '.\Private\LoadBitmap.ps1' 6
#Region '.\Private\ResumeProcess.ps1' 0
function ResumeProcess($procId) {
    return $([Kernel32]::DebugActiveProcessStop($procId))
}
#EndRegion '.\Private\ResumeProcess.ps1' 4
#Region '.\Private\SuspendProcess.ps1' 0
function SuspendProcess($procId) {
    return $([Kernel32]::DebugActiveProcess($procId))
}
#EndRegion '.\Private\SuspendProcess.ps1' 4
#Region '.\Public\CustomizationsReader.ps1' 0
function CustomizationsReader{
    return [CustomizationsReader]::new()
}
#EndRegion '.\Public\CustomizationsReader.ps1' 4
#Region '.\Public\Explorer.ps1' 0
function Explorer{
    Param(
        [string]$schema,
        [string]$source
    )    
    return [TemplateExplorer]::new( $schema, $source)
}
#EndRegion '.\Public\Explorer.ps1' 8
#Region '.\Public\New-Runspaces.ps1' 0
function New-Runspaces{
    Param(
        [string]$ScriptFile
    )
    return [Runspaces]::new($ScriptFile)
}
#EndRegion '.\Public\New-Runspaces.ps1' 7
#Region '.\Public\New-SRxPipeClient.ps1' 0
function New-SRxPipeClient{
    Param(
        [string]$PipeName
    )
    
    return [PipeClient]::new($PipeName)
}
#EndRegion '.\Public\New-SRxPipeClient.ps1' 8
#Region '.\Public\New-SRxPipeServer.ps1' 0
function New-SRxPipeServer{
    Param(
        [string]$PipeName,
        [boolean]$DoEvents
    )
    
    return [PipeServer]::new($PipeName, $DoEvents)
}
#EndRegion '.\Public\New-SRxPipeServer.ps1' 9
#Region '.\Public\Receive-StringFromPipeServer.ps1' 0
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
}
#EndRegion '.\Public\Receive-StringFromPipeServer.ps1' 16
#Region '.\Public\Request-SRxPipeServer.ps1' 0
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
}
#EndRegion '.\Public\Request-SRxPipeServer.ps1' 17
#Region '.\Public\Send-StringToPipeServer.ps1' 0
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) 
    }
}
#EndRegion '.\Public\Send-StringToPipeServer.ps1' 13