public/New-AppVentiXShortcutUserSetting.ps1
|
<#
.SYNOPSIS Creates an AppVentiX UserSetting XML file for a Shortcuts configuration. .DESCRIPTION Builds and saves an AppVentiX UserSetting XML file that defines one or more shortcuts, including executable path, arguments, working directory, desktop placement, and icon data. Supports two input modes: - ShortcutEntries mode: accepts an array of hashtables describing individual shortcut entries. - InputObject mode: accepts a pre-structured PSCustomObject (e.g. from Get-IvantiWCShortcut), which includes shortcut metadata and icon data (base64) ready for direct use. Icon data handling: - If the InputObject or ShortcutEntry already contains base64 icon data, it is used as-is. - If icon data is absent and an IconFile path is provided, the function generates all icon sizes (16, 32, 48, 256 and default 32) from that file. - If neither is available, a warning is issued and icon fields are left empty. .PARAMETER FriendlyName Display name for the AppVentiX UserSetting. .PARAMETER Description Optional description for the UserSetting. .PARAMETER ExecutionOrder Execution order for the UserSetting. Defaults to 0. .PARAMETER ProcessAtLogin Whether the setting is applied at login. Defaults to $true. .PARAMETER ProcessAtRefresh Whether the setting is applied at refresh. Defaults to $true. .PARAMETER ProcessAtReconnectAndUnlock Whether the setting is applied at reconnect and unlock. Defaults to $false. .PARAMETER MachineGroupFriendlyName One or more machine group friendly names to apply the setting to. Defaults to 'All Machine Groups'. .PARAMETER ShortcutEntries Array of hashtables describing shortcut entries. Each entry must contain: ShortcutName, ExecutablePath. Optional keys: Arguments, WorkingDirectory, PlaceOnDesktop, IconData, IconData16, IconData32, IconData48, IconData256, IconFile. .PARAMETER InputObject Pre-structured PSCustomObject containing shortcut metadata and entries. .PARAMETER IconFile Path to a PNG, ICO, or EXE file used as icon source when icon data is absent in the entries. The function will generate all required icon sizes from this file. .PARAMETER ConfigShare Path to the AppVentiX config share. Defaults to the module variable. .NOTES Function : New-AppVentiXShortcutUserSetting Author : John Billekens Copyright : (c) John Billekens Consultancy & AppVentiX Version : 2026.321.1000 #> function New-AppVentiXShortcutUserSetting { [CmdletBinding()] [Alias("New-AppVentiXShortcut")] param ( [Parameter(Mandatory = $true, ParameterSetName = "ShortcutEntries")] [Parameter(Mandatory = $false, ParameterSetName = "InputObject")] [string]$FriendlyName, [Parameter(Mandatory = $false, ParameterSetName = "ShortcutEntries")] [Parameter(Mandatory = $false, ParameterSetName = "InputObject")] [string]$Description, [Parameter(Mandatory = $false, ParameterSetName = "ShortcutEntries")] [Parameter(Mandatory = $false, ParameterSetName = "InputObject")] [int]$ExecutionOrder = 0, [Parameter(Mandatory = $false, ParameterSetName = "ShortcutEntries")] [Parameter(Mandatory = $false, ParameterSetName = "InputObject")] [bool]$ProcessAtLogin = $true, [Parameter(Mandatory = $false, ParameterSetName = "ShortcutEntries")] [Parameter(Mandatory = $false, ParameterSetName = "InputObject")] [bool]$ProcessAtRefresh = $true, [Parameter(Mandatory = $false, ParameterSetName = "ShortcutEntries")] [Parameter(Mandatory = $false, ParameterSetName = "InputObject")] [bool]$ProcessAtReconnectAndUnlock = $false, [Parameter(Mandatory = $false, ParameterSetName = "ShortcutEntries")] [Parameter(Mandatory = $false, ParameterSetName = "InputObject")] [string[]]$MachineGroupFriendlyName = @("All Machine Groups"), [Parameter(Mandatory = $true, ParameterSetName = "ShortcutEntries")] [hashtable[]]$ShortcutEntries = @(), [Parameter(Mandatory = $true, ParameterSetName = "InputObject")] [PSCustomObject]$InputObject, [Parameter(Mandatory = $false, ParameterSetName = "ShortcutEntries")] [Parameter(Mandatory = $false, ParameterSetName = "InputObject")] [ValidateScript({ Test-Path $_ -PathType Leaf })] [string]$IconFile, [Parameter(DontShow)] [ValidateNotNullOrEmpty()] [string]$ConfigShare = $Script:AppVentix.ConfigShare ) #region functions function ConvertFrom-IconFilePath { <# .SYNOPSIS Converts an image file to a base64 PNG string, optionally resized. #> [CmdletBinding()] [OutputType([string])] param( [Parameter(Mandatory = $true)] [string]$Path, [Parameter(Mandatory = $false)] [ValidateRange(16, 256)] [int]$Size = 32 ) try { Add-Type -AssemblyName System.Drawing -ErrorAction Stop $originalImage = [System.Drawing.Image]::FromFile($Path) if ($originalImage.Width -eq $Size -and $originalImage.Height -eq $Size) { $ms = New-Object System.IO.MemoryStream $originalImage.Save($ms, [System.Drawing.Imaging.ImageFormat]::Png) $result = [Convert]::ToBase64String($ms.ToArray()) $ms.Dispose() } else { $resized = New-Object System.Drawing.Bitmap $Size, $Size $g = [System.Drawing.Graphics]::FromImage($resized) $g.InterpolationMode = [System.Drawing.Drawing2D.InterpolationMode]::HighQualityBicubic $g.SmoothingMode = [System.Drawing.Drawing2D.SmoothingMode]::HighQuality $g.PixelOffsetMode = [System.Drawing.Drawing2D.PixelOffsetMode]::HighQuality $g.DrawImage($originalImage, 0, 0, $Size, $Size) $g.Dispose() $ms = New-Object System.IO.MemoryStream $resized.Save($ms, [System.Drawing.Imaging.ImageFormat]::Png) $result = [Convert]::ToBase64String($ms.ToArray()) $ms.Dispose() $resized.Dispose() } $originalImage.Dispose() return $result } catch { Write-Error "Failed to convert icon file '$Path' to Base64: $_" return "" } } function New-ShortcutUserSettingXml { [CmdletBinding()] param () $XmlDoc = New-Object System.Xml.XmlDocument $Root = $XmlDoc.CreateElement("UserSetting") [void]$XmlDoc.AppendChild($Root) function Add-TextElement { param ( [System.Xml.XmlElement]$Parent, [string]$Name, [string]$Text ) $el = $XmlDoc.CreateElement($Name) $el.InnerText = $Text [void]$Parent.AppendChild($el) return $el } [void](Add-TextElement -Parent $Root -Name "Id" -Text $SettingId) [void](Add-TextElement -Parent $Root -Name "FriendlyName" -Text $FriendlyName) [void](Add-TextElement -Parent $Root -Name "Description" -Text $Description) [void](Add-TextElement -Parent $Root -Name "Type" -Text $Type) [void](Add-TextElement -Parent $Root -Name "ExecutionOrder" -Text $ExecutionOrder) [void](Add-TextElement -Parent $Root -Name "CreatedDate" -Text $CreatedDate) [void](Add-TextElement -Parent $Root -Name "LastModifiedDate" -Text $LastModifiedDate) [void](Add-TextElement -Parent $Root -Name "ProcessAtLogin" -Text (ConvertTo-BooleanString -Value $ProcessAtLogin)) [void](Add-TextElement -Parent $Root -Name "ProcessAtRefresh" -Text (ConvertTo-BooleanString -Value $ProcessAtRefresh)) [void](Add-TextElement -Parent $Root -Name "ProcessAtReconnectAndUnlock" -Text (ConvertTo-BooleanString -Value $ProcessAtReconnectAndUnlock)) $MachineGroupsElement = $XmlDoc.CreateElement("MachineGroups") foreach ($Group in $MachineGroups) { [void](Add-TextElement -Parent $MachineGroupsElement -Name "MachineGroup" -Text $Group) } [void]$Root.AppendChild($MachineGroupsElement) $MachineGroupFriendlyNamesElement = $XmlDoc.CreateElement("MachineGroupFriendlyNames") foreach ($GroupFriendlyName in $MachineGroupFriendlyNames) { [void](Add-TextElement -Parent $MachineGroupFriendlyNamesElement -Name "FriendlyName" -Text $GroupFriendlyName) } [void]$Root.AppendChild($MachineGroupFriendlyNamesElement) $ShortcutEntriesElement = $XmlDoc.CreateElement("ShortcutEntries") foreach ($EntryData in $NewShortcutEntries) { $ShortcutEntryElement = $XmlDoc.CreateElement("ShortcutEntry") [void](Add-TextElement -Parent $ShortcutEntryElement -Name "ShortcutName" -Text $EntryData.ShortcutName) [void](Add-TextElement -Parent $ShortcutEntryElement -Name "ExecutablePath" -Text $EntryData.ExecutablePath) [void](Add-TextElement -Parent $ShortcutEntryElement -Name "Arguments" -Text $EntryData.Arguments) [void](Add-TextElement -Parent $ShortcutEntryElement -Name "WorkingDirectory" -Text $EntryData.WorkingDirectory) [void](Add-TextElement -Parent $ShortcutEntryElement -Name "PlaceOnDesktop" -Text (ConvertTo-BooleanString -Value ([bool]$EntryData.PlaceOnDesktop))) [void](Add-TextElement -Parent $ShortcutEntryElement -Name "IconData" -Text $EntryData.IconData) [void](Add-TextElement -Parent $ShortcutEntryElement -Name "IconData16" -Text $EntryData.IconData16) [void](Add-TextElement -Parent $ShortcutEntryElement -Name "IconData32" -Text $EntryData.IconData32) [void](Add-TextElement -Parent $ShortcutEntryElement -Name "IconData48" -Text $EntryData.IconData48) [void](Add-TextElement -Parent $ShortcutEntryElement -Name "IconData256" -Text $EntryData.IconData256) [void]$ShortcutEntriesElement.AppendChild($ShortcutEntryElement) } [void]$Root.AppendChild($ShortcutEntriesElement) return $XmlDoc } #endregion functions if (Test-AppVentiXIsLicensed -ConfigShare $ConfigShare) { $SettingId = [guid]::NewGuid().ToString() $Type = "Shortcuts" $userSettingsPath = Join-Path -Path $ConfigShare -ChildPath $Script:AppVentix.UserSettingsPath # Process InputObject if ($PSBoundParameters.ContainsKey("InputObject")) { Write-Verbose "Processing InputObject parameter..." if (-not [string]::IsNullOrEmpty($InputObject.FriendlyName)) { $FriendlyName = $InputObject.FriendlyName } if (-not [string]::IsNullOrEmpty($InputObject.Description)) { $Description = $InputObject.Description } if ($PSBoundParameters.ContainsKey("ProcessAtLogin") -eq $false -and $null -ne $InputObject.ProcessAtLogin) { $ProcessAtLogin = [bool]$InputObject.ProcessAtLogin } if ($PSBoundParameters.ContainsKey("ProcessAtRefresh") -eq $false -and $null -ne $InputObject.ProcessAtRefresh) { $ProcessAtRefresh = [bool]$InputObject.ProcessAtRefresh } if ($PSBoundParameters.ContainsKey("ProcessAtReconnectAndUnlock") -eq $false -and $null -ne $InputObject.ProcessAtReconnectAndUnlock) { $ProcessAtReconnectAndUnlock = [bool]$InputObject.ProcessAtReconnectAndUnlock } $ShortcutEntries = @( $InputObject.ShortcutEntries | ForEach-Object { @{ ShortcutName = "$($_.ShortcutName)" ExecutablePath = "$($_.ExecutablePath)" Arguments = "$($_.Arguments)" WorkingDirectory = "$($_.WorkingDirectory)" PlaceOnDesktop = $_.PlaceOnDesktop IconData = "$($_.IconData)" IconData16 = "$($_.IconData16)" IconData32 = "$($_.IconData32)" IconData48 = "$($_.IconData48)" IconData256 = "$($_.IconData256)" } } ) } # Resolve MachineGroups if ($MachineGroupFriendlyName.Count -gt 0) { if ($MachineGroupFriendlyName -notcontains "All Machine Groups" -and $MachineGroupFriendlyName -notcontains "All" -and $MachineGroupFriendlyName.Count -gt 0) { $AvailableGroups = Get-AppVentiXMachineGroup $MachineGroups = @() $MachineGroupFriendlyNames = @() foreach ($Group in $MachineGroupFriendlyName) { $selectedGroup = $AvailableGroups | Where-Object { $_.FriendlyName -ieq $Group } if ($selectedGroup) { $MachineGroups += $selectedGroup.GroupName.ToString() $MachineGroupFriendlyNames += $selectedGroup.FriendlyName.ToString() } } } else { $MachineGroups = @("All") $MachineGroupFriendlyNames = @("All Machine Groups") } } else { $MachineGroups = @("All") $MachineGroupFriendlyNames = @("All Machine Groups") } #region --- Build $NewShortcutEntries --- $NewShortcutEntries = @() foreach ($entry in $ShortcutEntries) { if (-not ($entry.ContainsKey("ShortcutName") -and $entry.ContainsKey("ExecutablePath"))) { Write-Error "Each shortcut entry must be a hashtable containing at least the keys: ShortcutName, ExecutablePath" return } $NewEntry = [ordered]@{ ShortcutName = "$($entry.ShortcutName)" ExecutablePath = "$($entry.ExecutablePath)" Arguments = "$($entry.Arguments)" WorkingDirectory = "$($entry.WorkingDirectory)" PlaceOnDesktop = $false IconData = "$($entry.IconData)" IconData16 = "$($entry.IconData16)" IconData32 = "$($entry.IconData32)" IconData48 = "$($entry.IconData48)" IconData256 = "$($entry.IconData256)" } # Normalize PlaceOnDesktop switch ($entry.PlaceOnDesktop) { "True" { $NewEntry.PlaceOnDesktop = $true } "False" { $NewEntry.PlaceOnDesktop = $false } "1" { $NewEntry.PlaceOnDesktop = $true } "0" { $NewEntry.PlaceOnDesktop = $false } $true { $NewEntry.PlaceOnDesktop = $true } $false { $NewEntry.PlaceOnDesktop = $false } default { $NewEntry.PlaceOnDesktop = $false } } # Icon resolution: use base64 from entry if present, otherwise fall back to IconFile $iconSizes = @{ IconData = 32; IconData16 = 16; IconData32 = 32; IconData48 = 48; IconData256 = 256 } $hasAnyIconData = $iconSizes.Keys | Where-Object { -not [string]::IsNullOrEmpty($NewEntry[$_]) } if (-not $hasAnyIconData) { if ($PSBoundParameters.ContainsKey("IconFile")) { Write-Verbose "No icon data in entry '$($NewEntry.ShortcutName)'. Generating from IconFile '$IconFile'." foreach ($iconField in $iconSizes.GetEnumerator()) { $NewEntry[$iconField.Key] = ConvertFrom-IconFilePath -Path $IconFile -Size $iconField.Value } } else { Write-Warning "No icon data found for shortcut '$($NewEntry.ShortcutName)' and no -IconFile specified. Icon fields will be empty." } } elseif ($PSBoundParameters.ContainsKey("IconFile")) { Write-Verbose "Entry '$($NewEntry.ShortcutName)' already contains icon data. -IconFile parameter will be ignored for this entry." } $NewShortcutEntries += $NewEntry } #endregion --- Build $NewShortcutEntries --- $policyFilename = "$($Type)-$($SettingId).xml" $policyFullname = Join-Path -Path $userSettingsPath -ChildPath $policyFilename $CreatedDate = (Get-Date).ToString("yyyy-MM-dd HH:mm:ss") $LastModifiedDate = (Get-Date).ToString("yyyy-MM-dd HH:mm:ss") if ([string]::IsNullOrEmpty($FriendlyName)) { Write-Error "FriendlyName is required. Please provide a value for the FriendlyName parameter or include it in the InputObject." return } try { Write-Verbose "Building UserSetting XML..." $XmlDocument = New-ShortcutUserSettingXml $XmlWriterSettings = New-Object System.Xml.XmlWriterSettings $XmlWriterSettings.Indent = $true $XmlWriterSettings.IndentChars = " " $XmlWriterSettings.Encoding = [System.Text.Encoding]::UTF8 $XmlWriterSettings.OmitXmlDeclaration = $true Write-Verbose "Saving UserSetting XML to $policyFullname" $XmlWriter = [System.Xml.XmlWriter]::Create($policyFullname, $XmlWriterSettings) $XmlDocument.Save($XmlWriter) $XmlWriter.Close() Write-AppVentiXLogEntry -Feature UserSettings -Action $($MyInvocation.MyCommand.Name) -Details "New Shortcut User Setting '$FriendlyName' added with $($NewShortcutEntries.Count) shortcut(s)." Write-Output ( [PSCustomObject]@{ Id = $SettingId FriendlyName = $FriendlyName FileName = $policyFilename } ) } catch { Write-Error "Failed to generate XML: $($_.Exception.Message)" } } else { Write-Warning 'AppVentiX is not licensed!' } } # SIG # Begin signature block # MIImdwYJKoZIhvcNAQcCoIImaDCCJmQCAQExDzANBglghkgBZQMEAgEFADB5Bgor # BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG # KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCDcKsO4LgW2pbvG # sXZfXHAMX9amKJxM4nmUBpHoya/adaCCIAowggYUMIID/KADAgECAhB6I67aU2mW # D5HIPlz0x+M/MA0GCSqGSIb3DQEBDAUAMFcxCzAJBgNVBAYTAkdCMRgwFgYDVQQK # Ew9TZWN0aWdvIExpbWl0ZWQxLjAsBgNVBAMTJVNlY3RpZ28gUHVibGljIFRpbWUg # U3RhbXBpbmcgUm9vdCBSNDYwHhcNMjEwMzIyMDAwMDAwWhcNMzYwMzIxMjM1OTU5 # WjBVMQswCQYDVQQGEwJHQjEYMBYGA1UEChMPU2VjdGlnbyBMaW1pdGVkMSwwKgYD # VQQDEyNTZWN0aWdvIFB1YmxpYyBUaW1lIFN0YW1waW5nIENBIFIzNjCCAaIwDQYJ # KoZIhvcNAQEBBQADggGPADCCAYoCggGBAM2Y2ENBq26CK+z2M34mNOSJjNPvIhKA # VD7vJq+MDoGD46IiM+b83+3ecLvBhStSVjeYXIjfa3ajoW3cS3ElcJzkyZlBnwDE # JuHlzpbN4kMH2qRBVrjrGJgSlzzUqcGQBaCxpectRGhhnOSwcjPMI3G0hedv2eNm # GiUbD12OeORN0ADzdpsQ4dDi6M4YhoGE9cbY11XxM2AVZn0GiOUC9+XE0wI7CQKf # OUfigLDn7i/WeyxZ43XLj5GVo7LDBExSLnh+va8WxTlA+uBvq1KO8RSHUQLgzb1g # bL9Ihgzxmkdp2ZWNuLc+XyEmJNbD2OIIq/fWlwBp6KNL19zpHsODLIsgZ+WZ1AzC # s1HEK6VWrxmnKyJJg2Lv23DlEdZlQSGdF+z+Gyn9/CRezKe7WNyxRf4e4bwUtrYE # 2F5Q+05yDD68clwnweckKtxRaF0VzN/w76kOLIaFVhf5sMM/caEZLtOYqYadtn03 # 4ykSFaZuIBU9uCSrKRKTPJhWvXk4CllgrwIDAQABo4IBXDCCAVgwHwYDVR0jBBgw # FoAU9ndq3T/9ARP/FqFsggIv0Ao9FCUwHQYDVR0OBBYEFF9Y7UwxeqJhQo1SgLqz # YZcZojKbMA4GA1UdDwEB/wQEAwIBhjASBgNVHRMBAf8ECDAGAQH/AgEAMBMGA1Ud # JQQMMAoGCCsGAQUFBwMIMBEGA1UdIAQKMAgwBgYEVR0gADBMBgNVHR8ERTBDMEGg # P6A9hjtodHRwOi8vY3JsLnNlY3RpZ28uY29tL1NlY3RpZ29QdWJsaWNUaW1lU3Rh # bXBpbmdSb290UjQ2LmNybDB8BggrBgEFBQcBAQRwMG4wRwYIKwYBBQUHMAKGO2h0 # dHA6Ly9jcnQuc2VjdGlnby5jb20vU2VjdGlnb1B1YmxpY1RpbWVTdGFtcGluZ1Jv # b3RSNDYucDdjMCMGCCsGAQUFBzABhhdodHRwOi8vb2NzcC5zZWN0aWdvLmNvbTAN # BgkqhkiG9w0BAQwFAAOCAgEAEtd7IK0ONVgMnoEdJVj9TC1ndK/HYiYh9lVUacah # RoZ2W2hfiEOyQExnHk1jkvpIJzAMxmEc6ZvIyHI5UkPCbXKspioYMdbOnBWQUn73 # 3qMooBfIghpR/klUqNxx6/fDXqY0hSU1OSkkSivt51UlmJElUICZYBodzD3M/SFj # eCP59anwxs6hwj1mfvzG+b1coYGnqsSz2wSKr+nDO+Db8qNcTbJZRAiSazr7KyUJ # Go1c+MScGfG5QHV+bps8BX5Oyv9Ct36Y4Il6ajTqV2ifikkVtB3RNBUgwu/mSiSU # ice/Jp/q8BMk/gN8+0rNIE+QqU63JoVMCMPY2752LmESsRVVoypJVt8/N3qQ1c6F # ibbcRabo3azZkcIdWGVSAdoLgAIxEKBeNh9AQO1gQrnh1TA8ldXuJzPSuALOz1Uj # b0PCyNVkWk7hkhVHfcvBfI8NtgWQupiaAeNHe0pWSGH2opXZYKYG4Lbukg7HpNi/ # KqJhue2Keak6qH9A8CeEOB7Eob0Zf+fU+CCQaL0cJqlmnx9HCDxF+3BLbUufrV64 # EbTI40zqegPZdA+sXCmbcZy6okx/SjwsusWRItFA3DE8MORZeFb6BmzBtqKJ7l93 # 9bbKBy2jvxcJI98Va95Q5JnlKor3m0E7xpMeYRriWklUPsetMSf2NvUQa/E5vVye # fQIwggZFMIIELaADAgECAhAIMk+dt9qRb2Pk8qM8Xl1RMA0GCSqGSIb3DQEBCwUA # MFYxCzAJBgNVBAYTAlBMMSEwHwYDVQQKExhBc3NlY28gRGF0YSBTeXN0ZW1zIFMu # QS4xJDAiBgNVBAMTG0NlcnR1bSBDb2RlIFNpZ25pbmcgMjAyMSBDQTAeFw0yNDA0 # MDQxNDA0MjRaFw0yNzA0MDQxNDA0MjNaMGsxCzAJBgNVBAYTAk5MMRIwEAYDVQQH # DAlTY2hpam5kZWwxIzAhBgNVBAoMGkpvaG4gQmlsbGVrZW5zIENvbnN1bHRhbmN5 # MSMwIQYDVQQDDBpKb2huIEJpbGxla2VucyBDb25zdWx0YW5jeTCCAaIwDQYJKoZI # hvcNAQEBBQADggGPADCCAYoCggGBAMslntDbSQwHZXwFhmibivbnd0Qfn6sqe/6f # os3pKzKxEsR907RkDMet2x6RRg3eJkiIr3TFPwqBooyXXgK3zxxpyhGOcuIqyM9J # 28DVf4kUyZHsjGO/8HFjrr3K1hABNUszP0o7H3o6J31eqV1UmCXYhQlNoW9FOmRC # 1amlquBmh7w4EKYEytqdmdOBavAD5Xq4vLPxNP6kyA+B2YTtk/xM27TghtbwFGKn # u9Vwnm7dFcpLxans4ONt2OxDQOMA5NwgcUv/YTpjhq9qoz6ivG55NRJGNvUXsM3w # 2o7dR6Xh4MuEGrTSrOWGg2A5EcLH1XqQtkF5cZnAPM8W/9HUp8ggornWnFVQ9/6M # ga+ermy5wy5XrmQpN+x3u6tit7xlHk1Hc+4XY4a4ie3BPXG2PhJhmZAn4ebNSBwN # Hh8z7WTT9X9OFERepGSytZVeEP7hgyptSLcuhpwWeR4QdBb7dV++4p3PsAUQVHFp # wkSbrRTv4EiJ0Lcz9P1HPGFoHiFAQQIDAQABo4IBeDCCAXQwDAYDVR0TAQH/BAIw # ADA9BgNVHR8ENjA0MDKgMKAuhixodHRwOi8vY2NzY2EyMDIxLmNybC5jZXJ0dW0u # cGwvY2NzY2EyMDIxLmNybDBzBggrBgEFBQcBAQRnMGUwLAYIKwYBBQUHMAGGIGh0 # dHA6Ly9jY3NjYTIwMjEub2NzcC1jZXJ0dW0uY29tMDUGCCsGAQUFBzAChilodHRw # Oi8vcmVwb3NpdG9yeS5jZXJ0dW0ucGwvY2NzY2EyMDIxLmNlcjAfBgNVHSMEGDAW # gBTddF1MANt7n6B0yrFu9zzAMsBwzTAdBgNVHQ4EFgQUO6KtBpOBgmrlANVAnyiQ # C6W6lJwwSwYDVR0gBEQwQjAIBgZngQwBBAEwNgYLKoRoAYb2dwIFAQQwJzAlBggr # BgEFBQcCARYZaHR0cHM6Ly93d3cuY2VydHVtLnBsL0NQUzATBgNVHSUEDDAKBggr # BgEFBQcDAzAOBgNVHQ8BAf8EBAMCB4AwDQYJKoZIhvcNAQELBQADggIBAEQsN8wg # PMdWVkwHPPTN+jKpdns5AKVFjcn00psf2NGVVgWWNQBIQc9lEuTBWb54IK6Ga3hx # QRZfnPNo5HGl73YLmFgdFQrFzZ1lnaMdIcyh8LTWv6+XNWfoyCM9wCp4zMIDPOs8 # LKSMQqA/wRgqiACWnOS4a6fyd5GUIAm4CuaptpFYr90l4Dn/wAdXOdY32UhgzmSu # xpUbhD8gVJUaBNVmQaRqeU8y49MxiVrUKJXde1BCrtR9awXbqembc7Nqvmi60tYK # lD27hlpKtj6eGPjkht0hHEsgzU0Fxw7ZJghYG2wXfpF2ziN893ak9Mi/1dmCNmor # GOnybKYfT6ff6YTCDDNkod4egcMZdOSv+/Qv+HAeIgEvrxE9QsGlzTwbRtbm6gwY # YcVBs/SsVUdBn/TSB35MMxRhHE5iC3aUTkDbceo/XP3uFhVL4g2JZHpFfCSu2TQr # rzRn2sn07jfMvzeHArCOJgBW1gPqR3WrJ4hUxL06Rbg1gs9tU5HGGz9KNQMfQFQ7 # 0Wz7UIhezGcFcRfkIfSkMmQYYpsc7rfzj+z0ThfDVzzJr2dMOFsMlfj1T6l22GBq # 9XQx0A4lcc5Fl9pRxbOuHHWFqIBD/BCEhwniOCySzqENd2N+oz8znKooSISStnkN # aYXt6xblJF2dx9Dn89FK7d1IquNxOwt0tI5dMIIGYjCCBMqgAwIBAgIRAKQpO24e # 3denNAiHrXpOtyQwDQYJKoZIhvcNAQEMBQAwVTELMAkGA1UEBhMCR0IxGDAWBgNV # BAoTD1NlY3RpZ28gTGltaXRlZDEsMCoGA1UEAxMjU2VjdGlnbyBQdWJsaWMgVGlt # ZSBTdGFtcGluZyBDQSBSMzYwHhcNMjUwMzI3MDAwMDAwWhcNMzYwMzIxMjM1OTU5 # WjByMQswCQYDVQQGEwJHQjEXMBUGA1UECBMOV2VzdCBZb3Jrc2hpcmUxGDAWBgNV # BAoTD1NlY3RpZ28gTGltaXRlZDEwMC4GA1UEAxMnU2VjdGlnbyBQdWJsaWMgVGlt # ZSBTdGFtcGluZyBTaWduZXIgUjM2MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIIC # CgKCAgEA04SV9G6kU3jyPRBLeBIHPNyUgVNnYayfsGOyYEXrn3+SkDYTLs1crcw/ # ol2swE1TzB2aR/5JIjKNf75QBha2Ddj+4NEPKDxHEd4dEn7RTWMcTIfm492TW22I # 8LfH+A7Ehz0/safc6BbsNBzjHTt7FngNfhfJoYOrkugSaT8F0IzUh6VUwoHdYDpi # ln9dh0n0m545d5A5tJD92iFAIbKHQWGbCQNYplqpAFasHBn77OqW37P9BhOASdmj # p3IijYiFdcA0WQIe60vzvrk0HG+iVcwVZjz+t5OcXGTcxqOAzk1frDNZ1aw8nFhG # EvG0ktJQknnJZE3D40GofV7O8WzgaAnZmoUn4PCpvH36vD4XaAF2CjiPsJWiY/j2 # xLsJuqx3JtuI4akH0MmGzlBUylhXvdNVXcjAuIEcEQKtOBR9lU4wXQpISrbOT8ux # +96GzBq8TdbhoFcmYaOBZKlwPP7pOp5Mzx/UMhyBA93PQhiCdPfIVOCINsUY4U23 # p4KJ3F1HqP3H6Slw3lHACnLilGETXRg5X/Fp8G8qlG5Y+M49ZEGUp2bneRLZoyHT # yynHvFISpefhBCV0KdRZHPcuSL5OAGWnBjAlRtHvsMBrI3AAA0Tu1oGvPa/4yeei # Ayu+9y3SLC98gDVbySnXnkujjhIh+oaatsk/oyf5R2vcxHahajMCAwEAAaOCAY4w # ggGKMB8GA1UdIwQYMBaAFF9Y7UwxeqJhQo1SgLqzYZcZojKbMB0GA1UdDgQWBBSI # YYyhKjdkgShgoZsx0Iz9LALOTzAOBgNVHQ8BAf8EBAMCBsAwDAYDVR0TAQH/BAIw # ADAWBgNVHSUBAf8EDDAKBggrBgEFBQcDCDBKBgNVHSAEQzBBMDUGDCsGAQQBsjEB # AgEDCDAlMCMGCCsGAQUFBwIBFhdodHRwczovL3NlY3RpZ28uY29tL0NQUzAIBgZn # gQwBBAIwSgYDVR0fBEMwQTA/oD2gO4Y5aHR0cDovL2NybC5zZWN0aWdvLmNvbS9T # ZWN0aWdvUHVibGljVGltZVN0YW1waW5nQ0FSMzYuY3JsMHoGCCsGAQUFBwEBBG4w # bDBFBggrBgEFBQcwAoY5aHR0cDovL2NydC5zZWN0aWdvLmNvbS9TZWN0aWdvUHVi # bGljVGltZVN0YW1waW5nQ0FSMzYuY3J0MCMGCCsGAQUFBzABhhdodHRwOi8vb2Nz # cC5zZWN0aWdvLmNvbTANBgkqhkiG9w0BAQwFAAOCAYEAAoE+pIZyUSH5ZakuPVKK # 4eWbzEsTRJOEjbIu6r7vmzXXLpJx4FyGmcqnFZoa1dzx3JrUCrdG5b//LfAxOGy9 # Ph9JtrYChJaVHrusDh9NgYwiGDOhyyJ2zRy3+kdqhwtUlLCdNjFjakTSE+hkC9F5 # ty1uxOoQ2ZkfI5WM4WXA3ZHcNHB4V42zi7Jk3ktEnkSdViVxM6rduXW0jmmiu71Z # pBFZDh7Kdens+PQXPgMqvzodgQJEkxaION5XRCoBxAwWwiMm2thPDuZTzWp/gUFz # i7izCmEt4pE3Kf0MOt3ccgwn4Kl2FIcQaV55nkjv1gODcHcD9+ZVjYZoyKTVWb4V # qMQy/j8Q3aaYd/jOQ66Fhk3NWbg2tYl5jhQCuIsE55Vg4N0DUbEWvXJxtxQQaVR5 # xzhEI+BjJKzh3TQ026JxHhr2fuJ0mV68AluFr9qshgwS5SpN5FFtaSEnAwqZv3IS # +mlG50rK7W3qXbWwi4hmpylUfygtYLEdLQukNEX1jiOKMIIGgjCCBGqgAwIBAgIQ # NsKwvXwbOuejs902y8l1aDANBgkqhkiG9w0BAQwFADCBiDELMAkGA1UEBhMCVVMx # EzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNleSBDaXR5MR4wHAYD # VQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMTJVVTRVJUcnVzdCBS # U0EgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMjEwMzIyMDAwMDAwWhcNMzgw # MTE4MjM1OTU5WjBXMQswCQYDVQQGEwJHQjEYMBYGA1UEChMPU2VjdGlnbyBMaW1p # dGVkMS4wLAYDVQQDEyVTZWN0aWdvIFB1YmxpYyBUaW1lIFN0YW1waW5nIFJvb3Qg # UjQ2MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAiJ3YuUVnnR3d6Lkm # gZpUVMB8SQWbzFoVD9mUEES0QUCBdxSZqdTkdizICFNeINCSJS+lV1ipnW5ihkQy # C0cRLWXUJzodqpnMRs46npiJPHrfLBOifjfhpdXJ2aHHsPHggGsCi7uE0awqKggE # /LkYw3sqaBia67h/3awoqNvGqiFRJ+OTWYmUCO2GAXsePHi+/JUNAax3kpqstbl3 # vcTdOGhtKShvZIvjwulRH87rbukNyHGWX5tNK/WABKf+Gnoi4cmisS7oSimgHUI0 # Wn/4elNd40BFdSZ1EwpuddZ+Wr7+Dfo0lcHflm/FDDrOJ3rWqauUP8hsokDoI7D/ # yUVI9DAE/WK3Jl3C4LKwIpn1mNzMyptRwsXKrop06m7NUNHdlTDEMovXAIDGAvYy # nPt5lutv8lZeI5w3MOlCybAZDpK3Dy1MKo+6aEtE9vtiTMzz/o2dYfdP0KWZwZIX # bYsTIlg1YIetCpi5s14qiXOpRsKqFKqav9R1R5vj3NgevsAsvxsAnI8Oa5s2oy25 # qhsoBIGo/zi6GpxFj+mOdh35Xn91y72J4RGOJEoqzEIbW3q0b2iPuWLA911cRxgY # 5SJYubvjay3nSMbBPPFsyl6mY4/WYucmyS9lo3l7jk27MAe145GWxK4O3m3gEFEI # kv7kRmefDR7Oe2T1HxAnICQvr9sCAwEAAaOCARYwggESMB8GA1UdIwQYMBaAFFN5 # v1qqK0rPVIDh2JvAnfKyA2bLMB0GA1UdDgQWBBT2d2rdP/0BE/8WoWyCAi/QCj0U # JTAOBgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zATBgNVHSUEDDAKBggr # BgEFBQcDCDARBgNVHSAECjAIMAYGBFUdIAAwUAYDVR0fBEkwRzBFoEOgQYY/aHR0 # cDovL2NybC51c2VydHJ1c3QuY29tL1VTRVJUcnVzdFJTQUNlcnRpZmljYXRpb25B # dXRob3JpdHkuY3JsMDUGCCsGAQUFBwEBBCkwJzAlBggrBgEFBQcwAYYZaHR0cDov # L29jc3AudXNlcnRydXN0LmNvbTANBgkqhkiG9w0BAQwFAAOCAgEADr5lQe1oRLjl # ocXUEYfktzsljOt+2sgXke3Y8UPEooU5y39rAARaAdAxUeiX1ktLJ3+lgxtoLQhn # 5cFb3GF2SSZRX8ptQ6IvuD3wz/LNHKpQ5nX8hjsDLRhsyeIiJsms9yAWnvdYOdEM # q1W61KE9JlBkB20XBee6JaXx4UBErc+YuoSb1SxVf7nkNtUjPfcxuFtrQdRMRi/f # InV/AobE8Gw/8yBMQKKaHt5eia8ybT8Y/Ffa6HAJyz9gvEOcF1VWXG8OMeM7Vy7B # s6mSIkYeYtddU1ux1dQLbEGur18ut97wgGwDiGinCwKPyFO7ApcmVJOtlw9FVJxw # /mL1TbyBns4zOgkaXFnnfzg4qbSvnrwyj1NiurMp4pmAWjR+Pb/SIduPnmFzbSN/ # G8reZCL4fvGlvPFk4Uab/JVCSmj59+/mB2Gn6G/UYOy8k60mKcmaAZsEVkhOFuoj # 4we8CYyaR9vd9PGZKSinaZIkvVjbH/3nlLb0a7SBIkiRzfPfS9T+JesylbHa1LtR # V9U/7m0q7Ma2CQ/t392ioOssXW7oKLdOmMBl14suVFBmbzrt5V5cQPnwtd3UOTpS # 9oCG+ZZheiIvPgkDmA8FzPsnfXW5qHELB43ET7HHFHeRPRYrMBKjkb8/IN7Po0d0 # hQoF4TeMM+zYAJzoKQnVKOLg8pZVPT8wgga5MIIEoaADAgECAhEAmaOACiZVO2Wr # 3G6EprPqOTANBgkqhkiG9w0BAQwFADCBgDELMAkGA1UEBhMCUEwxIjAgBgNVBAoT # GVVuaXpldG8gVGVjaG5vbG9naWVzIFMuQS4xJzAlBgNVBAsTHkNlcnR1bSBDZXJ0 # aWZpY2F0aW9uIEF1dGhvcml0eTEkMCIGA1UEAxMbQ2VydHVtIFRydXN0ZWQgTmV0 # d29yayBDQSAyMB4XDTIxMDUxOTA1MzIxOFoXDTM2MDUxODA1MzIxOFowVjELMAkG # A1UEBhMCUEwxITAfBgNVBAoTGEFzc2VjbyBEYXRhIFN5c3RlbXMgUy5BLjEkMCIG # A1UEAxMbQ2VydHVtIENvZGUgU2lnbmluZyAyMDIxIENBMIICIjANBgkqhkiG9w0B # AQEFAAOCAg8AMIICCgKCAgEAnSPPBDAjO8FGLOczcz5jXXp1ur5cTbq96y34vuTm # flN4mSAfgLKTvggv24/rWiVGzGxT9YEASVMw1Aj8ewTS4IndU8s7VS5+djSoMcbv # IKck6+hI1shsylP4JyLvmxwLHtSworV9wmjhNd627h27a8RdrT1PH9ud0IF+njvM # k2xqbNTIPsnWtw3E7DmDoUmDQiYi/ucJ42fcHqBkbbxYDB7SYOouu9Tj1yHIohzu # C8KNqfcYf7Z4/iZgkBJ+UFNDcc6zokZ2uJIxWgPWXMEmhu1gMXgv8aGUsRdaCtVD # 2bSlbfsq7BiqljjaCun+RJgTgFRCtsuAEw0pG9+FA+yQN9n/kZtMLK+Wo837Q4QO # ZgYqVWQ4x6cM7/G0yswg1ElLlJj6NYKLw9EcBXE7TF3HybZtYvj9lDV2nT8mFSkc # SkAExzd4prHwYjUXTeZIlVXqj+eaYqoMTpMrfh5MCAOIG5knN4Q/JHuurfTI5XDY # O962WZayx7ACFf5ydJpoEowSP07YaBiQ8nXpDkNrUA9g7qf/rCkKbWpQ5boufUnq # 1UiYPIAHlezf4muJqxqIns/kqld6JVX8cixbd6PzkDpwZo4SlADaCi2JSplKShBS # ND36E/ENVv8urPS0yOnpG4tIoBGxVCARPCg1BnyMJ4rBJAcOSnAWd18Jx5n858JS # qPECAwEAAaOCAVUwggFRMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFN10XUwA # 23ufoHTKsW73PMAywHDNMB8GA1UdIwQYMBaAFLahVDkCw6A/joq8+tT4HKbROg79 # MA4GA1UdDwEB/wQEAwIBBjATBgNVHSUEDDAKBggrBgEFBQcDAzAwBgNVHR8EKTAn # MCWgI6Ahhh9odHRwOi8vY3JsLmNlcnR1bS5wbC9jdG5jYTIuY3JsMGwGCCsGAQUF # BwEBBGAwXjAoBggrBgEFBQcwAYYcaHR0cDovL3N1YmNhLm9jc3AtY2VydHVtLmNv # bTAyBggrBgEFBQcwAoYmaHR0cDovL3JlcG9zaXRvcnkuY2VydHVtLnBsL2N0bmNh # Mi5jZXIwOQYDVR0gBDIwMDAuBgRVHSAAMCYwJAYIKwYBBQUHAgEWGGh0dHA6Ly93 # d3cuY2VydHVtLnBsL0NQUzANBgkqhkiG9w0BAQwFAAOCAgEAdYhYD+WPUCiaU58Q # 7EP89DttyZqGYn2XRDhJkL6P+/T0IPZyxfxiXumYlARMgwRzLRUStJl490L94C9L # GF3vjzzH8Jq3iR74BRlkO18J3zIdmCKQa5LyZ48IfICJTZVJeChDUyuQy6rGDxLU # UAsO0eqeLNhLVsgw6/zOfImNlARKn1FP7o0fTbj8ipNGxHBIutiRsWrhWM2f8pXd # d3x2mbJCKKtl2s42g9KUJHEIiLni9ByoqIUul4GblLQigO0ugh7bWRLDm0CdY9rN # LqyA3ahe8WlxVWkxyrQLjH8ItI17RdySaYayX3PhRSC4Am1/7mATwZWwSD+B7eMc # ZNhpn8zJ+6MTyE6YoEBSRVrs0zFFIHUR08Wk0ikSf+lIe5Iv6RY3/bFAEloMU+vU # BfSouCReZwSLo8WdrDlPXtR0gicDnytO7eZ5827NS2x7gCBibESYkOh1/w1tVxTp # V2Na3PR7nxYVlPu1JPoRZCbH86gc96UTvuWiOruWmyOEMLOGGniR+x+zPF/2DaGg # K2W1eEJfo2qyrBNPvF7wuAyQfiFXLwvWHamoYtPZo0LHuH8X3n9C+xN4YaNjt2yw # zOr+tKyEVAotnyU9vyEVOaIYMk3IeBrmFnn0gbKeTTyYeEEUz/Qwt4HOUBCrW602 # NCmvO1nm+/80nLy5r0AZvCQxaQ4xggXDMIIFvwIBATBqMFYxCzAJBgNVBAYTAlBM # MSEwHwYDVQQKExhBc3NlY28gRGF0YSBTeXN0ZW1zIFMuQS4xJDAiBgNVBAMTG0Nl # cnR1bSBDb2RlIFNpZ25pbmcgMjAyMSBDQQIQCDJPnbfakW9j5PKjPF5dUTANBglg # hkgBZQMEAgEFAKCBhDAYBgorBgEEAYI3AgEMMQowCKACgAChAoAAMBkGCSqGSIb3 # DQEJAzEMBgorBgEEAYI3AgEEMBwGCisGAQQBgjcCAQsxDjAMBgorBgEEAYI3AgEV # MC8GCSqGSIb3DQEJBDEiBCAeoRimG5w/NrjNn3mNeEYIdiAqSbWvJVyHcxWXjsC2 # dzANBgkqhkiG9w0BAQEFAASCAYCJZdddAxIDEyq//MHDfu7U8E8ivDzbWdd6hzwg # Xewr3AN6JGZz7suIFwosTbJ2y1danElg0X9bv1/ZH+f94qBQebmh4hDkjzGWgUm1 # UDqPPK0+UFmOP0+uTLTdBWEYEl6/Z0ORnIkHRW/CbTAKYnhNcRIQJjc/VqR/azSq # NZa2OJhdiXPxEZlIP5BIoUrpGyWao0X+BcQAmBS41dWsdeFHIVgy0bBBkMPUjfai # 0XD1hS3ayd7dJHAuzbnB+apejkU9Db9lHmY9+1f1zKbOaWH+e+WbAyyTh3elybeW # GPNkmMfiEMtzGCb5/7n+i1VJpBnDrJNKWU5Qh6UxpB7ZZk3mBnmjEwCRIt2cdXRs # PnSKNUxcX+rlf2Cbd/01aYWHFR+LCQ8d9rd/2n9tw64V+uFlxZdp6LWciz3fiYf+ # FF56mlBK2JbuFi2q/FMGaQtR05yWj4tkDQ/l+dORBkAuq0ca9jH7QPurdHFvqUok # L0Jnwgb5KuQhRshiaNk/cyw+uHyhggMjMIIDHwYJKoZIhvcNAQkGMYIDEDCCAwwC # AQEwajBVMQswCQYDVQQGEwJHQjEYMBYGA1UEChMPU2VjdGlnbyBMaW1pdGVkMSww # KgYDVQQDEyNTZWN0aWdvIFB1YmxpYyBUaW1lIFN0YW1waW5nIENBIFIzNgIRAKQp # O24e3denNAiHrXpOtyQwDQYJYIZIAWUDBAICBQCgeTAYBgkqhkiG9w0BCQMxCwYJ # KoZIhvcNAQcBMBwGCSqGSIb3DQEJBTEPFw0yNjA2MTYxODMzMzdaMD8GCSqGSIb3 # DQEJBDEyBDAylDFyz5bTYKVpgaaiOtd1VpQPHqYl37lmGhb6U+xGCjREDWfCFbOb # pFc2dc3rgNkwDQYJKoZIhvcNAQEBBQAEggIAphawqv79ni0VrLfIZXTHeLAafmDn # G4f+8rdcQoh0yq5crtLeQ1l8vLURJS5WK/KJ4NlHT/NHxzQ7syp6ltD1+9dscRAT # 2LYpUN8XPJpUoo5Q7MVtBRMLZSg3q8BNtVrVgQLk3n4Y6Aifr7hAJJnJlyRN3ZBH # CighvQ/CWd8uXBLZUtMBmwEe2T1PRDfbz+i8RK7AsGfbx7+/16D4kP1luMEwOEMO # cO59YkkbiG8Sq8awLJGFFaADX88P+44bQXtqtxNUZlPM1hvMw6UIoPIBeAHHAc4u # WEjAKtO1qxGd4BjHUol/aTSlxO5OeMOR41u3muj7zxMvJB62XjkDCuj1JhVz1lnC # o4ZXby8DxRkXQZvWG4ZvtMhzNtNNC1PJAHX0F2LjRLNc8UBxORj1AeTh84R/OH48 # HQ/k0uuOr1GdjXSG1AnaQ85GkqcI1yzpSozt2YqMPW10mqVHgtusShNoZ1kVVmvT # ap64DqHHGJkVEN6wFfs7g6pq9mm5gRP0CalR0pXlJs4t643/tkuDVpULqpQ0tSEZ # l7hKzmaTjHxAEhOyKi2vwngiDqLN+mH1uqcCpzskrGtOmKdpH+yt0tZxzdf5iir4 # N6DiEHW+tD8GMXL70X/82GQxch5ofBwQYiUc4hUR/06nvIOHb2XqG/UDNE3IyFs5 # 7bELsznSWRjPSrg= # SIG # End signature block |