public/Initialize-UcmReport.ps1

Function Initialize-UcmReport
{
  <#
      .SYNOPSIS
      Checks for clears and creates a new object to store status for reporting later.
 
      .DESCRIPTION
      Checks for clears and creates a new object to store status for reporting later.
 
      .EXAMPLE
      Initialize-UcmReport
 
      .INPUTS
      This function accepts no inputs
 
      .OUTPUTS
      This function does not create pipelined output
      This Cmdet returns a PSCustomObject with multiple keys to indicate status
      $Return.Status
      $Return.Message
 
      Return.Status can return one of four values
      "OK" : Imported All Modules successfully
      "Warning" : Modules are already loaded
      "Error" : Something happend when attempting to import the modules, check $return.message for more information
      "Unknown" : Cmdlet reached the end of the function without returning anything, this shouldnt happen, if it does please log an issue on Github
 
      Return.Message returns descriptive text for error messages.
 
      .LINK
      https://www.UcMadScientist.com
      https://github.com/Atreidae/UcmPSTools
 
      .NOTES
      Version: 1.2
      Date: 19/06/2022
 
      .VERSION HISTORY
 
      1.2: Bug fixes for date reporting
      Added per line item numbers
      Added per line timestamps
      Updated Date format to respect system locale
      Added Subtitle support to HTML report
 
      1.1: Reordered functions into logical order
 
      1.0: Initial Public Release
 
      .ACKNOWLEDGEMENTS
  #>

  [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseProcessBlockForPipelineCommand', '', Scope='Function')]
  [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidGlobalVars', '', Scope='Function')] #Required due to how this report works. Report must persist outside of its own scope
  [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUSeDeclaredVarsMoreThanAssignments', '', Scope='Function')] #Required due to how this report works. Variables must persist outside of their own scope

  Param
  (
    [Parameter(ValueFromPipelineByPropertyName=$true, Position=1)] [String]$Title="HTML Report",
    [Parameter(ValueFromPipelineByPropertyName=$true, Position=2)] [String]$SubTitle="The results were as follows",
    [Parameter(ValueFromPipelineByPropertyName=$true, Position=3)] [string]$StartDate=(Get-Date -format dd.MM.yy.hh.mm),
    [Parameter(ValueFromPipelineByPropertyName=$true, Position=4)] [string]$NiceDate=(Get-Date -displayhint datetime)
  )


  #region FunctionSetup, Set Default Variables for HTML Reporting and Write Log
  $function = 'Initialize-UcmReport'
  [hashtable]$Return = @{}
  $return.Function = $function
  $return.Status = "Unknown"
  $return.Message = "Function did not return a status message"

  # Log why we were called
  Write-UcmLog -Message "$($MyInvocation.InvocationName) called with $($MyInvocation.Line)" -Severity 1 -Component $function
  Write-UcmLog -Message "Parameters" -Severity 1 -Component $function -LogOnly
  Write-UcmLog -Message "$($PsBoundParameters.Keys)" -Severity 1 -Component $function -LogOnly
  Write-UcmLog -Message "Parameters Values" -Severity 1 -Component $function -LogOnly
  Write-UcmLog -Message "$($PsBoundParameters.Values)" -Severity 1 -Component $function -LogOnly
  Write-UcmLog -Message "Optional Arguments" -Severity 1 -Component $function -LogOnly
  Write-UcmLog -Message "$Args" -Severity 1 -Component $function -LogOnly

  #endregion FunctionSetup

  #region FunctionWork

  #Declare our reports
  $Global:ProgressReport = @()
  $Global:ThisReport = @()

  #Declare our filenames
  $Global:HTMLReportFilename=".\$Title - $StartDate.html"
  $Global:CSVReportFilename=".\$Title - $StartDate.csv"

  #Import the attributes into the report object
  $Global:ProgressReport | add-member -MemberType NoteProperty -Name "Title"-Value "$title" -Force
  $Global:ProgressReportTitle = $Title
  $Global:ProgressReportSubtitle = $Subtitle
  $Global:ProgressReportStartTime = $NiceDate
  $Global:ProgressReportItemCount = 0

  New-UCMReportStep -StepName "Item" -StepResult "$($Global:ProgressReportItemCount)"
}

Function New-UCMReportItem
{
  <#
      .SYNOPSIS
      Adds a new Line Object to the Report
 
      .DESCRIPTION
      Adds a new Line Object to the Report
 
      .EXAMPLE
      New-UCMReportItem -LineTitle "Username" -LineMessage "bob@contoso.com"
 
      .INPUTS
      This function accepts no inputs
 
      .LINK
      http://www.UcMadScientist.com
      https://github.com/Atreidae/UcmPSTools
 
      .ACKNOWLEDGEMENTS
 
      .NOTES
      Version: 1.1
      Date: 19/06/2022
 
      .VERSION HISTORY
 
      1.1: Added per item numbering and timestamping
 
      1.0: Initial Public Release
  #>

  [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseProcessBlockForPipelineCommand', '', Scope='Function')]
  [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingfunctions', '', Scope='Function')] #process does not change state, ShouldProcess is not required.
  [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidGlobalVars', '', Scope='Function')] #Required due to how this report works. Report must persist outside of its own scope
  [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUSeDeclaredVarsMoreThanAssignments', '', Scope='Function')] #Required due to how this report works. Variables must persist outside of their own scope

  Param
  (
    [Parameter(ValueFromPipelineByPropertyName=$true, Mandatory, Position=1)] $LineTitle,
    [Parameter(ValueFromPipelineByPropertyName=$true, Mandatory, Position=2)] $LineMessage
  )

  #region FunctionSetup, Set Default Variables for HTML Reporting and Write Log
  $function = 'New-UcmReportLine'
  [hashtable]$Return = @{}
  $return.Function = $function
  $return.Status = "Unknown"
  $return.Message = "Function did not return a status message"

  # Log why we were called
  Write-UcmLog -Message "$($MyInvocation.InvocationName) called with $($MyInvocation.Line)" -Severity 1 -Component $function
  Write-UcmLog -Message "Parameters" -Severity 1 -Component $function -LogOnly
  Write-UcmLog -Message "$($PsBoundParameters.Keys)" -Severity 1 -Component $function -LogOnly
  Write-UcmLog -Message "Parameters Values" -Severity 1 -Component $function -LogOnly
  Write-UcmLog -Message "$($PsBoundParameters.Values)" -Severity 1 -Component $function -LogOnly
  Write-UcmLog -Message "Optional Arguments" -Severity 1 -Component $function -LogOnly
  Write-UcmLog -Message "$Args" -Severity 1 -Component $function -LogOnly

  #endregion FunctionSetup

  #region FunctionWork

  #Add the current time to the end of the old line
  New-UCMReportStep -Stepname "Time" -StepResult (Get-Date -displayhint time)

  #Merge the current line item into the report
  $Global:ProgressReport+= $Global:ThisReport

  #Init a new line item
  $Global:ThisReport = @()
  $Global:ThisReport =  New-Object -TypeName PSobject
  $Global:ThisReport | add-member -MemberType NoteProperty -Name "$LineTitle" -Value $LineMessage

  #Increment the line counter and add to the new line
  $Global:ProgressReportItemCount ++
  New-UCMReportStep -Stepname "ItemNumber" -StepResult "$Global:ProgressReportItemCount"
}

Function New-UcmReportStep
{
  <#
      .SYNOPSIS
      Adds a new Step to the Report
 
      .DESCRIPTION
      Creates a new Step for the current line item (for example, creating a user)
 
      .EXAMPLE
      New-UcmReportStep -Stepname "Enable User" -StepResult "OK: Created User"
 
      .INPUTS
      This function accepts no inputs
 
      .REQUIRED FUNCTIONS
      Write-UcmLog: https://github.com/Atreidae/PowerShell-Functions/blob/main/New-Office365User.ps1
 
      .LINK
      http://www.UcMadScientist.com
      https://github.com/Atreidae/UcmPSTools
 
      .ACKNOWLEDGEMENTS
 
      .NOTES
      Version: 1.0
      Date: 18/11/2021
 
      .VERSION HISTORY
 
 
      1.0: Initial Public Release
 
  #>

  [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseProcessBlockForPipelineCommand', '', Scope='Function')]
  [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingfunctions', '', Scope='Function')] #process does not change state, ShouldProcess is not required.
  [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidGlobalVars', '', Scope='Function')] #Required due to how this report works. Report must persist outside of its own scope
  [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUSeDeclaredVarsMoreThanAssignments', '', Scope='Function')] #Required due to how this report works. Variables must persist outside of their own scope

  Param
  (
    [Parameter(ValueFromPipelineByPropertyName=$true, Mandatory, Position=1)] $StepName,
    [Parameter(ValueFromPipelineByPropertyName=$true, Mandatory, Position=2)] $StepResult
  )

  #region FunctionSetup, Set Default Variables for HTML Reporting and Write Log
  $function = 'New-UcmReportStep'
  [hashtable]$Return = @{}
  $return.Function = $function
  $return.Status = "Unknown"
  $return.Message = "Function did not return a status message"

  # Log why we were called
  Write-UcmLog -Message "$($MyInvocation.InvocationName) called with $($MyInvocation.Line)" -Severity 1 -Component $function
  Write-UcmLog -Message "Parameters" -Severity 1 -Component $function -LogOnly
  Write-UcmLog -Message "$($PsBoundParameters.Keys)" -Severity 1 -Component $function -LogOnly
  Write-UcmLog -Message "Parameters Values" -Severity 1 -Component $function -LogOnly
  Write-UcmLog -Message "$($PsBoundParameters.Values)" -Severity 1 -Component $function -LogOnly
  Write-UcmLog -Message "Optional Arguments" -Severity 1 -Component $function -LogOnly
  Write-UcmLog -Message "$Args" -Severity 1 -Component $function -LogOnly

  #endregion FunctionSetup

  #region FunctionWork

  $Global:ThisReport | add-member -MemberType NoteProperty -Name "$StepName"-Value "$StepResult" -Force

  # endregion FunctionWork

}

Function Complete-UcmReport
{
  <#
      .SYNOPSIS
      Adds the last Line Object to the Report
 
      .DESCRIPTION
      Adds the last Line Object to the Report
 
      .EXAMPLE
      Complete-UcmReport
 
      .INPUTS
      This function accepts no inputs
 
      .LINK
      http://www.UcMadScientist.com
      https://github.com/Atreidae/UcmPSTools
 
      .ACKNOWLEDGEMENTS
 
      .NOTES
      Version: 1.1
      Date: 18/11/2021
 
      .VERSION HISTORY
      1.0: Initial Public Release
  #>

  [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseProcessBlockForPipelineCommand', '', Scope='Function')]
  [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidGlobalVars', '', Scope='Function')] #Required due to how this report works. Report must persist outside of its own scope
  [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUSeDeclaredVarsMoreThanAssignments', '', Scope='Function')] #Required due to how this report works. Variables must persist outside of their own scope

  Param
  (
    #none
  )

  #region FunctionSetup, Set Default Variables for HTML Reporting and Write Log
  $function = 'Complete-UcmReport'
  [hashtable]$Return = @{}
  $return.Function = $function
  $return.Status = "Unknown"
  $return.Message = "Function did not return a status message"

  # Log why we were called
  Write-UcmLog -Message "$($MyInvocation.InvocationName) called with $($MyInvocation.Line)" -Severity 1 -Component $function
  Write-UcmLog -Message "Parameters" -Severity 1 -Component $function -LogOnly
  Write-UcmLog -Message "$($PsBoundParameters.Keys)" -Severity 1 -Component $function -LogOnly
  Write-UcmLog -Message "Parameters Values" -Severity 1 -Component $function -LogOnly
  Write-UcmLog -Message "$($PsBoundParameters.Values)" -Severity 1 -Component $function -LogOnly
  Write-UcmLog -Message "Optional Arguments" -Severity 1 -Component $function -LogOnly
  Write-UcmLog -Message "$Args" -Severity 1 -Component $function -LogOnly

  #endregion FunctionSetup

  #region FunctionWork

  #Merge the current item and cleanup
  $Global:ProgressReport+= $Global:ThisReport
  Remove-variable -Name ProgressReport -scope global

  $Global:ThisReport = @()
  $Global:ThisReport = New-Object -TypeName PSobject
  $Global:ThisReport | add-member -MemberType NoteProperty -Name "End of Report" -Value "End of report"

}

Function Export-UcmHTMLReport
{
  <#
      .SYNOPSIS
      Grabs the data stored in the report object and converts it to HTML
 
      .DESCRIPTION
      Grabs the data stored in the report object and converts it to HTML
      By default, exports the current open report as a HTML in the current folder with the filename "$Title - $StartDate.html"
      You can change the path by editing $Global:HTMLReportFilename just before calling this function
 
      .EXAMPLE
      Export-UcmHTMLReport
 
      .INPUTS
      This function accepts no inputs
 
      .LINK
      http://www.UcMadScientist.com
      https://github.com/Atreidae/UcmPSTools
 
      .ACKNOWLEDGEMENTS
 
      .NOTES
      Version: 1.0
      Date: 18/11/2021
 
      .VERSION HISTORY
      1.1: Added per item numbering and timestamping
           Fixed formatting issues
           Fixed repetitive EndDate Bug
           Removed erronus "EndDate" field from each line item
 
      1.0: Initial Public Release
 
  #>

  [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseProcessBlockForPipelineCommand', '', Scope='Function')]
  [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidGlobalVars', '', Scope='Function')] #Required due to how this report works. Report must persist outside of its own scope
  [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUSeDeclaredVarsMoreThanAssignments', '', Scope='Function')] #Required due to how this report works. Variables must persist outside of their own scope

  Param
  (
    [Parameter(ValueFromPipelineByPropertyName=$true, Position=1)] [string]$EndDate=(Get-Date -DisplayHint datetime)
  )

  #region FunctionSetup, Set Default Variables for HTML Reporting and Write Log
  $function = 'Export-UcmHTMLReport'
  [hashtable]$Return = @{}
  $return.Function = $function
  $return.Status = "Unknown"
  $return.Message = "Function did not return a status message"

  # Log why we were called
  Write-UcmLog -Message "$($MyInvocation.InvocationName) called with $($MyInvocation.Line)" -Severity 1 -Component $function
  Write-UcmLog -Message "Parameters" -Severity 1 -Component $function -LogOnly
  Write-UcmLog -Message "$($PsBoundParameters.Keys)" -Severity 1 -Component $function -LogOnly
  Write-UcmLog -Message "Parameters Values" -Severity 1 -Component $function -LogOnly
  Write-UcmLog -Message "$($PsBoundParameters.Values)" -Severity 1 -Component $function -LogOnly
  Write-UcmLog -Message "Optional Arguments" -Severity 1 -Component $function -LogOnly
  Write-UcmLog -Message "$Args" -Severity 1 -Component $function -LogOnly

  #endregion FunctionSetup

  #region FunctionWork

  #Import the end time
  $Global:ProgressReportEndTime = $EndDate

  #$Report = ($PSCommandPath -replace '.ps1',"$ReportDate.html")

  #Define the HTML Style
  $Style = @"
<style>
BODY{background-color::#b0c4de;font-family:Tahoma;font-size:12pt;}
TABLE{border-width: 1px;border-style: solid;border-color: black;border-collapse: collapse;}
TH{border-width: 1px;padding: 3px;border-style: solid;border-color: black;color:white;background-color:#000099}
TD{border-width: 1px;padding: 3px;border-style: solid;border-color: black;text-align:center;}
</style>
"@


  Try #Export the report
  {
    $Global:ProgressReport | ConvertTo-Html -head $Style -body "<h1> $($Global:ProgressReportTitle) </h1> <h2> $Global:ProgressReportSubtitle </h2> The following report was started at $Global:ProgressReportStartTime and finishes at $Global:ProgressReportEndTime <br><br>" | ForEach-Object {
      #Add formatting for the different states
      if($_ -like "*<td>OK*")
      {$_ -replace "<td>OK", "<td bgcolor=#33FF66>OK"}
      Else {$_}
    } | ForEach-Object {
      if($_ -like "*<td>Warning*")
      {$_ -replace "<td>Warning", "<td bgcolor=#F9E79F>Warning"}
    Else {$_} } | ForEach-Object {
      If($_ -like "*<td>Error*")
      {$_ -replace "<td>Error", "<td bgcolor=#CD6155>Error"}
      Else {$_}
      #Write this out
    } | Out-File $Global:HTMLReportFilename

    #Open Browser
    invoke-Item $Global:HTMLReportFilename

    $Return.Status = "OK"
    $Return.Message = "HTML Report "
    Return $Return
  }
  Catch
  {

    Write-UcmLog -Message "Unexpected error when generating HTML report" -Severity 3 -Component $function
    Write-UcmLog -Message "$error[0]" -Severity 2 -Component $function
  }
}

Function Export-UcmCSVReport
{
  <#
      .SYNOPSIS
      Grabs the data stored in the report object and converts it to a CSV
 
      .DESCRIPTION
      Grabs the data stored in the report object and converts it to a CSV
      Exports the current open report as a HTML in the current folder with the filename "$Title - $StartDate.csv"
      You can change the path by editing $Global:CSVReportFilename just before calling this function
 
      .EXAMPLE
      Export-UcmCsvReport
 
      .INPUTS
      This function accepts no inputs
 
      .LINK
      http://www.UcMadScientist.com
      https://github.com/Atreidae/UcmPSTools
 
      .ACKNOWLEDGEMENTS
 
      .NOTES
      Version: 1.0
      Date: 18/11/2021
 
      .VERSION HISTORY
      1.0: Initial Public Release
 
  #>

  [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseProcessBlockForPipelineCommand', '', Scope='Function')]
  [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidGlobalVars', '', Scope='Function')] #Required due to how this report works. Report must persist outside of its own scope
  [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUSeDeclaredVarsMoreThanAssignments', '', Scope='Function')] #Required due to how this report works. Variables must persist outside of their own scope

  Param
  (
    [Parameter(ValueFromPipelineByPropertyName=$true, Position=1)] [string]$EndDate=(Get-Date -DisplayHint datetime)
  )

  #region FunctionSetup, Set Default Variables for HTML Reporting and Write Log
  $function = 'Export-UcmCSVReport'
  [hashtable]$Return = @{}
  $return.Function = $function
  $return.Status = "Unknown"
  $return.Message = "Function did not return a status message"

  # Log why we were called
  Write-UcmLog -Message "$($MyInvocation.InvocationName) called with $($MyInvocation.Line)" -Severity 1 -Component $function
  Write-UcmLog -Message "Parameters" -Severity 1 -Component $function -LogOnly
  Write-UcmLog -Message "$($PsBoundParameters.Keys)" -Severity 1 -Component $function -LogOnly
  Write-UcmLog -Message "Parameters Values" -Severity 1 -Component $function -LogOnly
  Write-UcmLog -Message "$($PsBoundParameters.Values)" -Severity 1 -Component $function -LogOnly
  Write-UcmLog -Message "Optional Arguments" -Severity 1 -Component $function -LogOnly
  Write-UcmLog -Message "$Args" -Severity 1 -Component $function -LogOnly

  #endregion FunctionSetup

  #region FunctionWork

  #Import the end time
  Try
  {
    $Global:ProgressReport | Export-CSV $Global:CSVReportFilename
    $Return.Status = "OK"
    $Return.Message = "CSV Report"
    Return $Return
  }
  Catch
  {

    Write-UcmLog -Message "Unexpected error when generating CSV report" -Severity 3 -Component $function
    Write-UcmLog -Message "$error[0]" -Severity 2 -Component $function
  }
}
# SIG # Begin signature block
# MIIRwgYJKoZIhvcNAQcCoIIRszCCEa8CAQExCzAJBgUrDgMCGgUAMGkGCisGAQQB
# gjcCAQSgWzBZMDQGCisGAQQBgjcCAR4wJgIDAQAABBAfzDtgWUsITrck0sYpfvNR
# AgEAAgEAAgEAAgEAAgEAMCEwCQYFKw4DAhoFAAQUGs/2uQ9aFk3/+Co735xQ3mp2
# 956ggg4OMIIGsDCCBJigAwIBAgIQCK1AsmDSnEyfXs2pvZOu2TANBgkqhkiG9w0B
# AQwFADBiMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYD
# VQQLExB3d3cuZGlnaWNlcnQuY29tMSEwHwYDVQQDExhEaWdpQ2VydCBUcnVzdGVk
# IFJvb3QgRzQwHhcNMjEwNDI5MDAwMDAwWhcNMzYwNDI4MjM1OTU5WjBpMQswCQYD
# VQQGEwJVUzEXMBUGA1UEChMORGlnaUNlcnQsIEluYy4xQTA/BgNVBAMTOERpZ2lD
# ZXJ0IFRydXN0ZWQgRzQgQ29kZSBTaWduaW5nIFJTQTQwOTYgU0hBMzg0IDIwMjEg
# Q0ExMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA1bQvQtAorXi3XdU5
# WRuxiEL1M4zrPYGXcMW7xIUmMJ+kjmjYXPXrNCQH4UtP03hD9BfXHtr50tVnGlJP
# DqFX/IiZwZHMgQM+TXAkZLON4gh9NH1MgFcSa0OamfLFOx/y78tHWhOmTLMBICXz
# ENOLsvsI8IrgnQnAZaf6mIBJNYc9URnokCF4RS6hnyzhGMIazMXuk0lwQjKP+8bq
# HPNlaJGiTUyCEUhSaN4QvRRXXegYE2XFf7JPhSxIpFaENdb5LpyqABXRN/4aBpTC
# fMjqGzLmysL0p6MDDnSlrzm2q2AS4+jWufcx4dyt5Big2MEjR0ezoQ9uo6ttmAaD
# G7dqZy3SvUQakhCBj7A7CdfHmzJawv9qYFSLScGT7eG0XOBv6yb5jNWy+TgQ5urO
# kfW+0/tvk2E0XLyTRSiDNipmKF+wc86LJiUGsoPUXPYVGUztYuBeM/Lo6OwKp7AD
# K5GyNnm+960IHnWmZcy740hQ83eRGv7bUKJGyGFYmPV8AhY8gyitOYbs1LcNU9D4
# R+Z1MI3sMJN2FKZbS110YU0/EpF23r9Yy3IQKUHw1cVtJnZoEUETWJrcJisB9IlN
# Wdt4z4FKPkBHX8mBUHOFECMhWWCKZFTBzCEa6DgZfGYczXg4RTCZT/9jT0y7qg0I
# U0F8WD1Hs/q27IwyCQLMbDwMVhECAwEAAaOCAVkwggFVMBIGA1UdEwEB/wQIMAYB
# Af8CAQAwHQYDVR0OBBYEFGg34Ou2O/hfEYb7/mF7CIhl9E5CMB8GA1UdIwQYMBaA
# FOzX44LScV1kTN8uZz/nupiuHA9PMA4GA1UdDwEB/wQEAwIBhjATBgNVHSUEDDAK
# BggrBgEFBQcDAzB3BggrBgEFBQcBAQRrMGkwJAYIKwYBBQUHMAGGGGh0dHA6Ly9v
# Y3NwLmRpZ2ljZXJ0LmNvbTBBBggrBgEFBQcwAoY1aHR0cDovL2NhY2VydHMuZGln
# aWNlcnQuY29tL0RpZ2lDZXJ0VHJ1c3RlZFJvb3RHNC5jcnQwQwYDVR0fBDwwOjA4
# oDagNIYyaHR0cDovL2NybDMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VHJ1c3RlZFJv
# b3RHNC5jcmwwHAYDVR0gBBUwEzAHBgVngQwBAzAIBgZngQwBBAEwDQYJKoZIhvcN
# AQEMBQADggIBADojRD2NCHbuj7w6mdNW4AIapfhINPMstuZ0ZveUcrEAyq9sMCcT
# Ep6QRJ9L/Z6jfCbVN7w6XUhtldU/SfQnuxaBRVD9nL22heB2fjdxyyL3WqqQz/WT
# auPrINHVUHmImoqKwba9oUgYftzYgBoRGRjNYZmBVvbJ43bnxOQbX0P4PpT/djk9
# ntSZz0rdKOtfJqGVWEjVGv7XJz/9kNF2ht0csGBc8w2o7uCJob054ThO2m67Np37
# 5SFTWsPK6Wrxoj7bQ7gzyE84FJKZ9d3OVG3ZXQIUH0AzfAPilbLCIXVzUstG2MQ0
# HKKlS43Nb3Y3LIU/Gs4m6Ri+kAewQ3+ViCCCcPDMyu/9KTVcH4k4Vfc3iosJocsL
# 6TEa/y4ZXDlx4b6cpwoG1iZnt5LmTl/eeqxJzy6kdJKt2zyknIYf48FWGysj/4+1
# 6oh7cGvmoLr9Oj9FpsToFpFSi0HASIRLlk2rREDjjfAVKM7t8RhWByovEMQMCGQ8
# M4+uKIw8y4+ICw2/O/TOHnuO77Xry7fwdxPm5yg/rBKupS8ibEH5glwVZsxsDsrF
# hsP2JjMMB0ug0wcCampAMEhLNKhRILutG4UI4lkNbcoFUCvqShyepf2gpx8GdOfy
# 1lKQ/a+FSCH5Vzu0nAPthkX0tGFuv2jiJmCG6sivqf6UHedjGzqGVnhOMIIHVjCC
# BT6gAwIBAgIQDyLHeeRvkUFg5QtSFTT8FjANBgkqhkiG9w0BAQsFADBpMQswCQYD
# VQQGEwJVUzEXMBUGA1UEChMORGlnaUNlcnQsIEluYy4xQTA/BgNVBAMTOERpZ2lD
# ZXJ0IFRydXN0ZWQgRzQgQ29kZSBTaWduaW5nIFJTQTQwOTYgU0hBMzg0IDIwMjEg
# Q0ExMB4XDTIzMDIyMzAwMDAwMFoXDTI2MDIyMzIzNTk1OVowXjELMAkGA1UEBhMC
# QVUxETAPBgNVBAgTCFZpY3RvcmlhMRAwDgYDVQQHEwdCZXJ3aWNrMRQwEgYDVQQK
# EwtKYW1lcyBBcmJlcjEUMBIGA1UEAxMLSmFtZXMgQXJiZXIwggIiMA0GCSqGSIb3
# DQEBAQUAA4ICDwAwggIKAoICAQC47oExh25TrxvApIYdMRYvjOdZCb8WwgeTemm3
# ZY7BElIWu6+gzRGqQe8RFsN7oIgin5pvjTYIToxt1CCag2A5o8L0NtULmxJEegc+
# VaF24DZQqI4qGQGH/Qnglqys6+yPkwLnfeSxpeWe4u49HUUGDFIxHCh42MlCLp/f
# fHT49QhhpO+LyeLnDoUs6DmahyIb6NeE2cW5AYRXEesW7GRNfXzygBSlVWJOgvcy
# V5Y4IvAZVx2hKKMTjYFIz4/RYMg7fwYZEJ2LRJ/GnVazobKAvh6ZBet5KwVNI9EI
# 29DtWQyK/RoPOguTRcB5VuiZVlv0xjBYM7iJuH2Soa3StQYVxL/5gjZCC9WOs4NR
# EIGU3XmHoogFDvoT1vf1izMPFQzdZfgPvy/XXsbgTVo5ncesJ6WtZwqwCXG1K0XW
# IPZqTHolc1MyU6K1bEHO+7YWLpKgM9THl644G7PEhcKpNDsHlfvLVQdYhI55UJtc
# iyMrTw11CNECvk3GK1mrluvKsrxdaH6G3Sp9VVHRtef6OZ5SlzkM5ID4egB2bXRb
# R/69bEuZr5hhm+v2lBSWIbZj/Mva6i/a/TAvy4vvPLo3DRcASkYZDC4T8gDMzmpG
# Xs4jAc9sfTL9z+o5u1PLJHFGRjJ+Wa2CgSftCdbKLjn+AY9m8ipc8jmOBKNY9yGI
# pQWapQIDAQABo4ICAzCCAf8wHwYDVR0jBBgwFoAUaDfg67Y7+F8Rhvv+YXsIiGX0
# TkIwHQYDVR0OBBYEFOBsg1xudlbXVSql8pWbiHoTyZS/MA4GA1UdDwEB/wQEAwIH
# gDATBgNVHSUEDDAKBggrBgEFBQcDAzCBtQYDVR0fBIGtMIGqMFOgUaBPhk1odHRw
# Oi8vY3JsMy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkRzRDb2RlU2lnbmlu
# Z1JTQTQwOTZTSEEzODQyMDIxQ0ExLmNybDBToFGgT4ZNaHR0cDovL2NybDQuZGln
# aWNlcnQuY29tL0RpZ2lDZXJ0VHJ1c3RlZEc0Q29kZVNpZ25pbmdSU0E0MDk2U0hB
# Mzg0MjAyMUNBMS5jcmwwPgYDVR0gBDcwNTAzBgZngQwBBAEwKTAnBggrBgEFBQcC
# ARYbaHR0cDovL3d3dy5kaWdpY2VydC5jb20vQ1BTMIGUBggrBgEFBQcBAQSBhzCB
# hDAkBggrBgEFBQcwAYYYaHR0cDovL29jc3AuZGlnaWNlcnQuY29tMFwGCCsGAQUF
# BzAChlBodHRwOi8vY2FjZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVk
# RzRDb2RlU2lnbmluZ1JTQTQwOTZTSEEzODQyMDIxQ0ExLmNydDAJBgNVHRMEAjAA
# MA0GCSqGSIb3DQEBCwUAA4ICAQBOh5vRXqTCQzv0T1F2EgDkq5VnljlHsa7Ov/d/
# lpOvr8NebBiatxbhfhPCnToY7BD2f7YVsUuQ+VDdcIYsskcU5spBHcFYidg2jGu4
# 59FGMaS765XStDwGGTN/360gEsNYSnKWYL4+8jYWHlzRO0jHloyWz+gF5dYWzdDJ
# u1dudLIJ0RgrEVJeLSgIBWygLL5EyIzOPlrxztsILMSbdPTQLeBIm7ipOk4EACx1
# hhBVUsUoCAlASH+yCKDU4v2HFd7SzrkRUrf7XJ2Na2YsiHjiTGqHIE86KyvxGDhT
# 3n2/jX23Nh/bkWHurHwTfaTCOQ44ZlAbnZQjBlmrFn5hPMXRpciiQFmrKTPD/nuo
# 9MVnCciHEpHJ63/JZNF/eno1122/wVkL7MuRlCVHN7L/wuNQxQk3ARdIju6OD/Gi
# Mwg0Qih6HVWJtkHK3ExoUKKKUZCOvIeHxzp+K6FWUupPZKUgWzn4AHMxm6zr+Sde
# laIAACqAkxYsDYKbM7WlNi3uIH2HeXqU9uSDt5tgPpImrog/ab4HrhpDfITRgT1c
# cxaWQezpJEPC+kqVD41T3wlEie1Qm4vYWg+oBVEMBxVLh6CYbeppCRTEXRGnAiCH
# /Ma1uwyWnNCWxrhd1uSi6sj4ISzgnFyGCvsI0gavKpS5AQhapJgk6/fULTFeS+Ee
# kRH9FDGCAx4wggMaAgEBMH0waTELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lD
# ZXJ0LCBJbmMuMUEwPwYDVQQDEzhEaWdpQ2VydCBUcnVzdGVkIEc0IENvZGUgU2ln
# bmluZyBSU0E0MDk2IFNIQTM4NCAyMDIxIENBMQIQDyLHeeRvkUFg5QtSFTT8FjAJ
# BgUrDgMCGgUAoHgwGAYKKwYBBAGCNwIBDDEKMAigAoAAoQKAADAZBgkqhkiG9w0B
# CQMxDAYKKwYBBAGCNwIBBDAcBgorBgEEAYI3AgELMQ4wDAYKKwYBBAGCNwIBFTAj
# BgkqhkiG9w0BCQQxFgQU3I/G0pKide5xkAHjlTcyaiZtBEAwDQYJKoZIhvcNAQEB
# BQAEggIARV9Wu9/OG8TuRvEHFW+SiddzvfJsC/+j7sHd0qFgI4qMopKyfEVvQN9n
# p2PniiMwqvhTvdoFoM5aUrfmg9iyjxeBscu85ArmSxThuG3MHZUg0pfl7NhT58BB
# ej24SourXkHIDaEhxBZJaihISdI7anokzGaymMWUyBmtQLNj5BOtwFjMnUErVj8x
# uUiE1YbkSKuJmoF6xFkez9b0zvLPMz0/vYIk1BnH90oWmhF/rQyxdjaExjIGnGow
# LkEiRtRn3iiznvOR6mv/zmXxVoEj0ZrvLSO11v/cXl8/1BJoc6v+kwLYCIhFo7od
# pNKuZowHnkNJeyOZNuk07Fuxyc4zJjCsSfKnvMviEFSyB8+0gPmCy0bdZAaZlbZO
# Y13s+FON3P+QK/Knf6ftPMp3TIYWPi75Ooaq8mQNZbcyVkOHAAIJDVJrPg4LgyKQ
# msV1TEnZxUHowJVmFBXc1/CmGmXfpFki9/50MD+i7oYL/f6qBL1muUCD8USWRqkA
# 72uA3+hklMg0+1tk2tb5a9uyAPQrZrHy2a3kr6h0Aggg+ZhH+DRA4pdWujTtVJBu
# a2qjrNcMlln/l3X5SQ5ojeJs+CVe6dH9TXwHq7U33CP7C16907IGQ7VOO5ZllMXV
# 1DsDnClHMr3fwnLNSAEaiHtzoEBLDEVWKIOWhC79HOrMFG4UP6Y=
# SIG # End signature block