NuGet/New-BcNuGetPackage.ps1
<#
.Synopsis PROOF OF CONCEPT PREVIEW: Create a new Business Central NuGet Package .Description Create a new NuGet package containing a Business Central apps .Parameter appfile App file to include in the NuGet package .Parameter countrySpecificAppFiles Hashtable with country specific app files (runtime packages) to include in the NuGet package .Parameter packageId Template to generate the id, replacing {id}, {name} and {publisher} with the values from the app.json file The default is '{publisher}.{name}.{id}' .Parameter packageVersion Version of the NuGet package The default is the version number from the app.json file .Parameter prereleaseTag If this is a prerelease, then you can specify a prerelease tag, which will be appended to the version number of the NuGet package (together with a dash) .Parameter packageTitle Title of the NuGet package The default is the name from the app.json file .Parameter packageDescription Description of the NuGet package The default is the description from the app.json file If no description exists in the app.json file, the default is the package title .Parameter packageAuthors Authors of the NuGet package The default is the publisher from the app.json file .Parameter githubRepository URL to the GitHub repository for the NuGet package .Parameter dependencyIdTemplate Template to calculate the id of the dependencies The template can contain {id}, {name} and {publisher} which will be replaced with the values from the corresponding dependency from app.json The default is '{publisher}.{name}.{id}' .Parameter dependencyVersionTemplate Template to calculate the version field of the dependencies, default is {version} The template can contain {version} which will be replaced with the verson from the corresponding dependency from app.json The template can also contain {major},{minor},{build} and {revision} which will be replaced with the fields from the version The template can also contain {major+1},{minor+1},{build+1} and {revision+1} which will be replaced with the fields from the version incremented by 1 .Parameter applicationDependencyId Id of the application dependency The default is 'Microsoft.Application' .Parameter applicationDependency Version/Template of the application dependency, default is the Application version from the app.json file The template can contain {version} which will be replaced with the verson from the corresponding dependency from app.json The template can also contain {major},{minor},{build} and {revision} which will be replaced with the fields from the version The template can also contain {major+1},{minor+1},{build+1} and {revision+1} which will be replaced with the fields from the version incremented by 1 .Parameter platformDependencyId Id of the platform dependency The default is 'Microsoft.Platform' .Parameter platformDependency Version/Template of the platform dependency, default is the Platform version from the app.json file The template can contain {version} which will be replaced with the verson from the corresponding dependency from app.json The template can also contain {major},{minor},{build} and {revision} which will be replaced with the fields from the version The template can also contain {major+1},{minor+1},{build+1} and {revision+1} which will be replaced with the fields from the version incremented by 1 .Parameter destinationFolder Folder to create the NuGet package in. Defeault it to create a temporary folder and delete it after the NuGet package has been created .Example $package = New-BcNuGetPackage -appfile "C:\Users\freddyk\Downloads\MyBingMaps-main-Apps-1.0.3.0\Freddy Kristiansen_BingMaps.PTE_4.4.3.0.app" .Example $package = New-BcNuGetPackage -appfile $appfile -packageId "AL-Go-{id}" -dependencyIdTemplate "AL-Go-{id}" #> Function New-BcNuGetPackage { Param( [Parameter(Mandatory=$true)] [alias('appFiles')] [string] $appfile, [Parameter(Mandatory=$false)] [hashtable] $countrySpecificAppFiles = @{}, [Parameter(Mandatory=$false)] [string] $packageId = "{publisher}.{name}.{id}", [Parameter(Mandatory=$false)] [System.Version] $packageVersion = $null, [Parameter(Mandatory=$false)] [string] $prereleaseTag = '', [Parameter(Mandatory=$false)] [string] $packageTitle = "", [Parameter(Mandatory=$false)] [string] $packageDescription = "", [Parameter(Mandatory=$false)] [string] $packageAuthors = "", [Parameter(Mandatory=$false)] [string] $githubRepository = "", [Parameter(Mandatory=$false)] [string] $dependencyIdTemplate = '{publisher}.{name}.{id}', [Parameter(Mandatory=$false)] [string] $dependencyVersionTemplate = '{version}', [Parameter(Mandatory=$false)] [string] $applicationDependencyId = 'Microsoft.Application', [Parameter(Mandatory=$false)] [string] $applicationDependency = '', [Parameter(Mandatory=$false)] [string] $platformDependencyId = 'Microsoft.Platform', [Parameter(Mandatory=$false)] [string] $platformDependency = '', [Parameter(Mandatory=$false)] [string] $runtimeDependencyId = '{publisher}.{name}.runtime-{version}', [switch] $isIndirectPackage, [Parameter(Mandatory=$false)] [string] $destinationFolder = '', [obsolete('NuGet Dependencies are always included.')] [switch] $includeNuGetDependencies ) function CopyFileToStream([string] $filename, [System.IO.Stream] $stream) { $bytes = [System.IO.File]::ReadAllBytes($filename) $stream.Write($bytes,0,$bytes.Length) } function GetDependencyVersionStr([string] $template, [System.Version] $version) { return $template.Replace('{version}',"$version").Replace('{major}',$version.Major).Replace('{minor}',$version.Minor).Replace('{build}',$version.Build).Replace('{revision}',$version.Revision).Replace('{major+1}',($version.Major+1)).Replace('{minor+1}',($version.Minor+1)).Replace('{build+1}',($version.Build+1)).Replace('{revision+1}',($version.Revision+1)) } Write-Host "Create NuGet package" Write-Host "AppFile:" Write-Host $appFile if (!(Test-Path $appFile)) { throw "Unable to locate file: $_" } $appFile = (Get-Item $appfile).FullName if ($destinationFolder) { $rootFolder = $destinationFolder } else { $rootFolder = Join-Path ([System.IO.Path]::GetTempPath()) ([GUID]::NewGuid().ToString()) } if (Test-Path $rootFolder) { if (Get-ChildItem -Path $rootFolder) { throw "Destination folder is not empty" } } else { New-Item -Path $rootFolder -ItemType Directory | Out-Null } try { if (!$isIndirectPackage.IsPresent) { Copy-Item -Path $appFile -Destination $rootFolder -Force if ($countrySpecificAppFiles) { foreach($country in $countrySpecificAppFiles.Keys) { $countrySpecificAppFile = $countrySpecificAppFiles[$country] if (!(Test-Path $countrySpecificAppFile)) { throw "Unable to locate file: $_" } $countryFolder = Join-Path $rootFolder $country New-Item -Path $countryFolder -ItemType Directory | Out-Null Copy-Item -Path $countrySpecificAppFile -Destination $countryFolder -Force } } } $appJson = Get-AppJsonFromAppFile -appFile $appFile $packageId = Get-BcNuGetPackageId -packageIdTemplate $packageId -publisher $appJson.publisher -name $appJson.name -id $appJson.id -version $appJson.version.replace('.','-') if ($null -eq $packageVersion) { $packageVersion = [System.Version]$appJson.version } if (-not $packageTitle) { $packageTitle = $appJson.name } if (-not $packageDescription) { $packageDescription = $appJson.description if (-not $packageDescription) { $packageDescription = $packageTitle } } if (-not $packageAuthors) { $packageAuthors = $appJson.publisher } if ($appJson.PSObject.Properties.Name -eq 'Application' -and $appJson.Application) { if (-not $applicationDependency) { $applicationDependency = $appJson.Application } else { $applicationDependency = GetDependencyVersionStr -template $applicationDependency -version ([System.Version]::Parse($appJson.Application)) } } elseif ($applicationDependency.Contains('{')) { $applicationDependency = '' } if ($appJson.PSObject.Properties.Name -eq 'Platform' -and $appJson.Platform) { if (-not $platformDependency) { $platformDependency = $appJson.Platform } else { $platformDependency = GetDependencyVersionStr -template $platformDependency -version ([System.Version]::Parse($appJson.Platform)) } } elseif ($platformDependency.Contains('{')) { $platformDependency = '' } if ($prereleaseTag) { $packageVersionStr = "$($packageVersion)-$prereleaseTag" } else { $packageVersionStr = "$packageVersion" } $nuPkgFileName = "$($packageId)-$($packageVersionStr).nupkg" $nupkgFile = Join-Path ([System.IO.Path]::GetTempPath()) $nuPkgFileName if (Test-Path $nuPkgFile -PathType Leaf) { Remove-Item $nupkgFile -Force } $nuspecFileName = Join-Path $rootFolder "manifest.nuspec" $xmlObjectsettings = New-Object System.Xml.XmlWriterSettings $xmlObjectsettings.Indent = $true $xmlObjectsettings.IndentChars = " " $xmlObjectsettings.Encoding = [System.Text.Encoding]::UTF8 $XmlObjectWriter = [System.XML.XmlWriter]::Create($nuspecFileName, $xmlObjectsettings) $XmlObjectWriter.WriteStartDocument() $XmlObjectWriter.WriteStartElement("package", "http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd") $XmlObjectWriter.WriteStartElement("metadata") $XmlObjectWriter.WriteElementString("id", $packageId) $XmlObjectWriter.WriteElementString("version", $packageVersionStr) $XmlObjectWriter.WriteElementString("title", $packageTitle) $XmlObjectWriter.WriteElementString("description", $packageDescription) $XmlObjectWriter.WriteElementString("authors", $packageAuthors) if ($githubRepository) { $XmlObjectWriter.WriteStartElement("repository") $XmlObjectWriter.WriteAttributeString("type", "git"); $XmlObjectWriter.WriteAttributeString("url", $githubRepository) $XmlObjectWriter.WriteEndElement() } $XmlObjectWriter.WriteStartElement("dependencies") if ($appJson.PSObject.Properties.Name -eq 'dependencies') { $appJson.dependencies | ForEach-Object { if ($_.PSObject.Properties.name -eq 'id') { $dependencyId = $_.id } else { $dependencyId = $_.appId } $id = Get-BcNuGetPackageId -packageIdTemplate $dependencyIdTemplate -publisher $_.publisher -name $_.name -id $dependencyId -version $_.version.replace('.','-') $XmlObjectWriter.WriteStartElement("dependency") $XmlObjectWriter.WriteAttributeString("id", $id) $XmlObjectWriter.WriteAttributeString("version", (GetDependencyVersionStr -template $dependencyVersionTemplate -version ([System.Version]::Parse($_.version)))) $XmlObjectWriter.WriteEndElement() } } if ($applicationDependency) { $XmlObjectWriter.WriteStartElement("dependency") $XmlObjectWriter.WriteAttributeString("id", $applicationDependencyId) $XmlObjectWriter.WriteAttributeString("version", $applicationDependency) $XmlObjectWriter.WriteEndElement() } if ($platformDependency) { $XmlObjectWriter.WriteStartElement("dependency") $XmlObjectWriter.WriteAttributeString("id", $platformDependencyId) $XmlObjectWriter.WriteAttributeString("version", $platformDependency) $XmlObjectWriter.WriteEndElement() } if ($isIndirectPackage.IsPresent) { $XmlObjectWriter.WriteStartElement("dependency") $id = Get-BcNuGetPackageId -packageIdTemplate $runtimeDependencyId -publisher $appJson.publisher -name $appJson.name -id $appJson.id -version $appJson.version.replace('.','-') $XmlObjectWriter.WriteAttributeString("id", $id) $XmlObjectWriter.WriteAttributeString("version", '1.0.0.0') $XmlObjectWriter.WriteEndElement() } $XmlObjectWriter.WriteEndElement() $XmlObjectWriter.WriteEndElement() if (!$isIndirectPackage.IsPresent) { $XmlObjectWriter.WriteStartElement("files") $XmlObjectWriter.WriteStartElement("file") $appFileName = [System.IO.Path]::GetFileName($appfile) $XmlObjectWriter.WriteAttributeString("src", $appFileName ); $XmlObjectWriter.WriteAttributeString("target", $appFileName); $XmlObjectWriter.WriteEndElement() if ($countrySpecificAppFiles) { foreach($country in $countrySpecificAppFiles.Keys) { $countrySpecificAppFile = $countrySpecificAppFiles[$country] $XmlObjectWriter.WriteStartElement("file") $appFileName = Join-Path $country ([System.IO.Path]::GetFileName($countrySpecificAppFiles[$country])) $XmlObjectWriter.WriteAttributeString("src", $appFileName ); $XmlObjectWriter.WriteAttributeString("target", $appFileName); $XmlObjectWriter.WriteEndElement() } } $XmlObjectWriter.WriteEndElement() } $XmlObjectWriter.WriteEndElement() $XmlObjectWriter.WriteEndDocument() $XmlObjectWriter.Flush() $XmlObjectWriter.Close() Write-Host "NUSPEC file:" Get-Content -path $nuspecFileName -Encoding UTF8 | Out-Host $nuPkgFileName = "$($packageId)-$($packageVersion).nupkg" $nupkgFile = Join-Path ([System.IO.Path]::GetTempPath()) $nuPkgFileName if (Test-Path $nuPkgFile -PathType Leaf) { Remove-Item $nupkgFile -Force } Compress-Archive -Path "$rootFolder\*" -DestinationPath "$nupkgFile.zip" -Force Rename-Item -Path "$nupkgFile.zip" -NewName $nuPkgFileName $size = (Get-Item $nupkgFile).Length if ($size -gt 1MB) { $sizeStr = "$([int]($size/1MB))Mb" } elseif ($size -gt 1KB) { $sizeStr = "$([int]($size/1KB))Kb" } else { $sizeStr = "$size bytes" } Write-Host -ForegroundColor Green "Successfully created NuGet package (Size: $sizeStr)" $nupkgFile } finally { if ($destinationFolder -ne $rootFolder) { Remove-Item -Path $rootFolder -Recurse -Force -ErrorAction SilentlyContinue } } } Export-ModuleMember -Function New-BcNuGetPackage # SIG # Begin signature block # MIImbAYJKoZIhvcNAQcCoIImXTCCJlkCAQExDzANBglghkgBZQMEAgEFADB5Bgor # BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG # KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCBmrDbGbyHIoFpK # YJUnUoQ+CQSLPQxnTpyB7Z/fLeH82KCCH4QwggWNMIIEdaADAgECAhAOmxiO+dAt # 5+/bUOIIQBhaMA0GCSqGSIb3DQEBDAUAMGUxCzAJBgNVBAYTAlVTMRUwEwYDVQQK # EwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xJDAiBgNV # BAMTG0RpZ2lDZXJ0IEFzc3VyZWQgSUQgUm9vdCBDQTAeFw0yMjA4MDEwMDAwMDBa # Fw0zMTExMDkyMzU5NTlaMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2Vy # dCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lD # ZXJ0IFRydXN0ZWQgUm9vdCBHNDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoC # ggIBAL/mkHNo3rvkXUo8MCIwaTPswqclLskhPfKK2FnC4SmnPVirdprNrnsbhA3E # MB/zG6Q4FutWxpdtHauyefLKEdLkX9YFPFIPUh/GnhWlfr6fqVcWWVVyr2iTcMKy # unWZanMylNEQRBAu34LzB4TmdDttceItDBvuINXJIB1jKS3O7F5OyJP4IWGbNOsF # xl7sWxq868nPzaw0QF+xembud8hIqGZXV59UWI4MK7dPpzDZVu7Ke13jrclPXuU1 # 5zHL2pNe3I6PgNq2kZhAkHnDeMe2scS1ahg4AxCN2NQ3pC4FfYj1gj4QkXCrVYJB # MtfbBHMqbpEBfCFM1LyuGwN1XXhm2ToxRJozQL8I11pJpMLmqaBn3aQnvKFPObUR # WBf3JFxGj2T3wWmIdph2PVldQnaHiZdpekjw4KISG2aadMreSx7nDmOu5tTvkpI6 # nj3cAORFJYm2mkQZK37AlLTSYW3rM9nF30sEAMx9HJXDj/chsrIRt7t/8tWMcCxB # YKqxYxhElRp2Yn72gLD76GSmM9GJB+G9t+ZDpBi4pncB4Q+UDCEdslQpJYls5Q5S # UUd0viastkF13nqsX40/ybzTQRESW+UQUOsxxcpyFiIJ33xMdT9j7CFfxCBRa2+x # q4aLT8LWRV+dIPyhHsXAj6KxfgommfXkaS+YHS312amyHeUbAgMBAAGjggE6MIIB # NjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTs1+OC0nFdZEzfLmc/57qYrhwP # TzAfBgNVHSMEGDAWgBRF66Kv9JLLgjEtUYunpyGd823IDzAOBgNVHQ8BAf8EBAMC # AYYweQYIKwYBBQUHAQEEbTBrMCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdp # Y2VydC5jb20wQwYIKwYBBQUHMAKGN2h0dHA6Ly9jYWNlcnRzLmRpZ2ljZXJ0LmNv # bS9EaWdpQ2VydEFzc3VyZWRJRFJvb3RDQS5jcnQwRQYDVR0fBD4wPDA6oDigNoY0 # aHR0cDovL2NybDMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0QXNzdXJlZElEUm9vdENB # LmNybDARBgNVHSAECjAIMAYGBFUdIAAwDQYJKoZIhvcNAQEMBQADggEBAHCgv0Nc # Vec4X6CjdBs9thbX979XB72arKGHLOyFXqkauyL4hxppVCLtpIh3bb0aFPQTSnov # Lbc47/T/gLn4offyct4kvFIDyE7QKt76LVbP+fT3rDB6mouyXtTP0UNEm0Mh65Zy # oUi0mcudT6cGAxN3J0TU53/oWajwvy8LpunyNDzs9wPHh6jSTEAZNUZqaVSwuKFW # juyk1T3osdz9HNj0d1pcVIxv76FQPfx2CWiEn2/K2yCNNWAcAgPLILCsWKAOQGPF # mCLBsln1VWvPJ6tsds5vIy30fnFqI2si/xK4VC0nftg62fC2h5b9W9FcrBjDTZ9z # twGpn1eqXijiuZQwggYaMIIEAqADAgECAhBiHW0MUgGeO5B5FSCJIRwKMA0GCSqG # SIb3DQEBDAUAMFYxCzAJBgNVBAYTAkdCMRgwFgYDVQQKEw9TZWN0aWdvIExpbWl0 # ZWQxLTArBgNVBAMTJFNlY3RpZ28gUHVibGljIENvZGUgU2lnbmluZyBSb290IFI0 # NjAeFw0yMTAzMjIwMDAwMDBaFw0zNjAzMjEyMzU5NTlaMFQxCzAJBgNVBAYTAkdC # MRgwFgYDVQQKEw9TZWN0aWdvIExpbWl0ZWQxKzApBgNVBAMTIlNlY3RpZ28gUHVi # bGljIENvZGUgU2lnbmluZyBDQSBSMzYwggGiMA0GCSqGSIb3DQEBAQUAA4IBjwAw # ggGKAoIBgQCbK51T+jU/jmAGQ2rAz/V/9shTUxjIztNsfvxYB5UXeWUzCxEeAEZG # bEN4QMgCsJLZUKhWThj/yPqy0iSZhXkZ6Pg2A2NVDgFigOMYzB2OKhdqfWGVoYW3 # haT29PSTahYkwmMv0b/83nbeECbiMXhSOtbam+/36F09fy1tsB8je/RV0mIk8XL/ # tfCK6cPuYHE215wzrK0h1SWHTxPbPuYkRdkP05ZwmRmTnAO5/arnY83jeNzhP06S # hdnRqtZlV59+8yv+KIhE5ILMqgOZYAENHNX9SJDm+qxp4VqpB3MV/h53yl41aHU5 # pledi9lCBbH9JeIkNFICiVHNkRmq4TpxtwfvjsUedyz8rNyfQJy/aOs5b4s+ac7I # H60B+Ja7TVM+EKv1WuTGwcLmoU3FpOFMbmPj8pz44MPZ1f9+YEQIQty/NQd/2yGg # W+ufflcZ/ZE9o1M7a5Jnqf2i2/uMSWymR8r2oQBMdlyh2n5HirY4jKnFH/9gRvd+ # QOfdRrJZb1sCAwEAAaOCAWQwggFgMB8GA1UdIwQYMBaAFDLrkpr/NZZILyhAQnAg # NpFcF4XmMB0GA1UdDgQWBBQPKssghyi47G9IritUpimqF6TNDDAOBgNVHQ8BAf8E # BAMCAYYwEgYDVR0TAQH/BAgwBgEB/wIBADATBgNVHSUEDDAKBggrBgEFBQcDAzAb # BgNVHSAEFDASMAYGBFUdIAAwCAYGZ4EMAQQBMEsGA1UdHwREMEIwQKA+oDyGOmh0 # dHA6Ly9jcmwuc2VjdGlnby5jb20vU2VjdGlnb1B1YmxpY0NvZGVTaWduaW5nUm9v # dFI0Ni5jcmwwewYIKwYBBQUHAQEEbzBtMEYGCCsGAQUFBzAChjpodHRwOi8vY3J0 # LnNlY3RpZ28uY29tL1NlY3RpZ29QdWJsaWNDb2RlU2lnbmluZ1Jvb3RSNDYucDdj # MCMGCCsGAQUFBzABhhdodHRwOi8vb2NzcC5zZWN0aWdvLmNvbTANBgkqhkiG9w0B # AQwFAAOCAgEABv+C4XdjNm57oRUgmxP/BP6YdURhw1aVcdGRP4Wh60BAscjW4HL9 # hcpkOTz5jUug2oeunbYAowbFC2AKK+cMcXIBD0ZdOaWTsyNyBBsMLHqafvIhrCym # laS98+QpoBCyKppP0OcxYEdU0hpsaqBBIZOtBajjcw5+w/KeFvPYfLF/ldYpmlG+ # vd0xqlqd099iChnyIMvY5HexjO2AmtsbpVn0OhNcWbWDRF/3sBp6fWXhz7DcML4i # TAWS+MVXeNLj1lJziVKEoroGs9Mlizg0bUMbOalOhOfCipnx8CaLZeVme5yELg09 # Jlo8BMe80jO37PU8ejfkP9/uPak7VLwELKxAMcJszkyeiaerlphwoKx1uHRzNyE6 # bxuSKcutisqmKL5OTunAvtONEoteSiabkPVSZ2z76mKnzAfZxCl/3dq3dUNw4rg3 # sTCggkHSRqTqlLMS7gjrhTqBmzu1L90Y1KWN/Y5JKdGvspbOrTfOXyXvmPL6E52z # 1NZJ6ctuMFBQZH3pwWvqURR8AgQdULUvrxjUYbHHj95Ejza63zdrEcxWLDX6xWls # /GDnVNueKjWUH3fTv1Y8Wdho698YADR7TNx8X8z2Bev6SivBBOHY+uqiirZtg0y9 # ShQoPzmCcn63Syatatvx157YK9hlcPmVoa1oDE5/L9Uo2bC5a4CH2RwwggZZMIIE # waADAgECAhANIM3qwHRbWKHw+Zq6JhzlMA0GCSqGSIb3DQEBDAUAMFQxCzAJBgNV # BAYTAkdCMRgwFgYDVQQKEw9TZWN0aWdvIExpbWl0ZWQxKzApBgNVBAMTIlNlY3Rp # Z28gUHVibGljIENvZGUgU2lnbmluZyBDQSBSMzYwHhcNMjExMDIyMDAwMDAwWhcN # MjQxMDIxMjM1OTU5WjBdMQswCQYDVQQGEwJESzEUMBIGA1UECAwLSG92ZWRzdGFk # ZW4xGzAZBgNVBAoMEkZyZWRkeSBLcmlzdGlhbnNlbjEbMBkGA1UEAwwSRnJlZGR5 # IEtyaXN0aWFuc2VuMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAgYC5 # tlg+VRktRRkahxxaV8+DAd6vHoDpcO6w7yT24lnSoMuA6nR7kgy90Y/sHIwKE9Ww # t/px/GAY8eBePWjJrFpG8fBtJbXadRTVd/470Hs/q9t+kh6A/0ELj7wYsKSNOyuF # Poy4rtClOv9ZmrRpoDVnh8Epwg2DpklX2BNzykzBQxIbkpp+xVo2mhPNWDIesntc # 4/BnSebLGw1Vkxmu2acKkIjYrne/7lsuyL9ue0vk8TGk9JBPNPbGKJvHu9szP9oG # oH36fU1sEZ+AacXrp+onsyPf/hkkpAMHAhzQHl+5Ikvcus/cDm06twm7VywmZcas # 2rFAV5MyE6WMEaYAolwAHiPz9WAs2GDhFtZZg1tzbRjJIIgPpR+doTIcpcDBcHnN # dSdgWKrTkr2f339oT5bnJfo7oVzc/2HGWvb8Fom6LQAqSC11vWmznHYsCm72g+fo # TKqW8lLDfLF0+aFvToLosrtW9l6Z+l+RQ8MtJ9EHOm2Ny8cFLzZCDZYw32BydwcL # V5rKdy4Ica9on5xZvyMOLiFwuL4v2V4pjEgKJaGSS/IVSMEGjrM9DHT6YS4/oq9q # 20rQUmMZZQmGmEyyKQ8t11si8VHtScN5m0Li8peoWfCU9mRFxSESwTWow8d462+o # 9/SzmDxCACdFwzvfKx4JqDMm55cL+beunIvc0NsCAwEAAaOCAZwwggGYMB8GA1Ud # IwQYMBaAFA8qyyCHKLjsb0iuK1SmKaoXpM0MMB0GA1UdDgQWBBTZD6uy9ZWIIqQh # 3srYu1FlUhdM0TAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADATBgNVHSUE # DDAKBggrBgEFBQcDAzARBglghkgBhvhCAQEEBAMCBBAwSgYDVR0gBEMwQTA1Bgwr # BgEEAbIxAQIBAwIwJTAjBggrBgEFBQcCARYXaHR0cHM6Ly9zZWN0aWdvLmNvbS9D # UFMwCAYGZ4EMAQQBMEkGA1UdHwRCMEAwPqA8oDqGOGh0dHA6Ly9jcmwuc2VjdGln # by5jb20vU2VjdGlnb1B1YmxpY0NvZGVTaWduaW5nQ0FSMzYuY3JsMHkGCCsGAQUF # BwEBBG0wazBEBggrBgEFBQcwAoY4aHR0cDovL2NydC5zZWN0aWdvLmNvbS9TZWN0 # aWdvUHVibGljQ29kZVNpZ25pbmdDQVIzNi5jcnQwIwYIKwYBBQUHMAGGF2h0dHA6 # Ly9vY3NwLnNlY3RpZ28uY29tMA0GCSqGSIb3DQEBDAUAA4IBgQASEbZACurQeQN8 # WDTR+YyNpoQ29YAbbdBRhhzHkT/1ao7LE0QIOgGR4GwKRzufCAwu8pCBiMOUTDHT # ezkh0rQrG6khxBX2nSTBL5i4LwKMR08HgZBsbECciABy15yexYWoB/D0H8WuGe63 # PhGWueR4IFPbIz+jEVxfW0Nyyr7bXTecpKd1iprm+TOmzc2E6ab95dkcXdJVx6Zy # s++QrrOfQ+a57qEXkS/wnjjbN9hukL0zg+g8L4DHLKTodzfiQOampvV8QzbnB7Y8 # YjNcxR9s/nptnlQH3jorNFhktiBXvD62jc8pAIg6wyH6NxSMjtTsn7QhkIp2kusw # IQwD8hN/fZ/m6gkXZhRJWFr2WRZOz+edZ62Jf25C/NYWscwfBwn2hzRZf1HgyxkX # Al88dvvUA3kw1T6uo8aAB9IcL6Owiy7q4T+RLRF7oqx0vcw0193Yhq/gPOaUFlqz # ExP6TQ5TR9XWVPQk+a1B1ATKMLi1JShO6KWTmNkFkgkgpkW69BEwggauMIIElqAD # AgECAhAHNje3JFR82Ees/ShmKl5bMA0GCSqGSIb3DQEBCwUAMGIxCzAJBgNVBAYT # AlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2Vy # dC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0IFRydXN0ZWQgUm9vdCBHNDAeFw0yMjAz # MjMwMDAwMDBaFw0zNzAzMjIyMzU5NTlaMGMxCzAJBgNVBAYTAlVTMRcwFQYDVQQK # Ew5EaWdpQ2VydCwgSW5jLjE7MDkGA1UEAxMyRGlnaUNlcnQgVHJ1c3RlZCBHNCBS # U0E0MDk2IFNIQTI1NiBUaW1lU3RhbXBpbmcgQ0EwggIiMA0GCSqGSIb3DQEBAQUA # A4ICDwAwggIKAoICAQDGhjUGSbPBPXJJUVXHJQPE8pE3qZdRodbSg9GeTKJtoLDM # g/la9hGhRBVCX6SI82j6ffOciQt/nR+eDzMfUBMLJnOWbfhXqAJ9/UO0hNoR8XOx # s+4rgISKIhjf69o9xBd/qxkrPkLcZ47qUT3w1lbU5ygt69OxtXXnHwZljZQp09ns # ad/ZkIdGAHvbREGJ3HxqV3rwN3mfXazL6IRktFLydkf3YYMZ3V+0VAshaG43IbtA # rF+y3kp9zvU5EmfvDqVjbOSmxR3NNg1c1eYbqMFkdECnwHLFuk4fsbVYTXn+149z # k6wsOeKlSNbwsDETqVcplicu9Yemj052FVUmcJgmf6AaRyBD40NjgHt1biclkJg6 # OBGz9vae5jtb7IHeIhTZgirHkr+g3uM+onP65x9abJTyUpURK1h0QCirc0PO30qh # HGs4xSnzyqqWc0Jon7ZGs506o9UD4L/wojzKQtwYSH8UNM/STKvvmz3+DrhkKvp1 # KCRB7UK/BZxmSVJQ9FHzNklNiyDSLFc1eSuo80VgvCONWPfcYd6T/jnA+bIwpUzX # 6ZhKWD7TA4j+s4/TXkt2ElGTyYwMO1uKIqjBJgj5FBASA31fI7tk42PgpuE+9sJ0 # sj8eCXbsq11GdeJgo1gJASgADoRU7s7pXcheMBK9Rp6103a50g5rmQzSM7TNsQID # AQABo4IBXTCCAVkwEgYDVR0TAQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQUuhbZbU2F # L3MpdpovdYxqII+eyG8wHwYDVR0jBBgwFoAU7NfjgtJxXWRM3y5nP+e6mK4cD08w # DgYDVR0PAQH/BAQDAgGGMBMGA1UdJQQMMAoGCCsGAQUFBwMIMHcGCCsGAQUFBwEB # BGswaTAkBggrBgEFBQcwAYYYaHR0cDovL29jc3AuZGlnaWNlcnQuY29tMEEGCCsG # AQUFBzAChjVodHRwOi8vY2FjZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVz # dGVkUm9vdEc0LmNydDBDBgNVHR8EPDA6MDigNqA0hjJodHRwOi8vY3JsMy5kaWdp # Y2VydC5jb20vRGlnaUNlcnRUcnVzdGVkUm9vdEc0LmNybDAgBgNVHSAEGTAXMAgG # BmeBDAEEAjALBglghkgBhv1sBwEwDQYJKoZIhvcNAQELBQADggIBAH1ZjsCTtm+Y # qUQiAX5m1tghQuGwGC4QTRPPMFPOvxj7x1Bd4ksp+3CKDaopafxpwc8dB+k+YMjY # C+VcW9dth/qEICU0MWfNthKWb8RQTGIdDAiCqBa9qVbPFXONASIlzpVpP0d3+3J0 # FNf/q0+KLHqrhc1DX+1gtqpPkWaeLJ7giqzl/Yy8ZCaHbJK9nXzQcAp876i8dU+6 # WvepELJd6f8oVInw1YpxdmXazPByoyP6wCeCRK6ZJxurJB4mwbfeKuv2nrF5mYGj # VoarCkXJ38SNoOeY+/umnXKvxMfBwWpx2cYTgAnEtp/Nh4cku0+jSbl3ZpHxcpzp # SwJSpzd+k1OsOx0ISQ+UzTl63f8lY5knLD0/a6fxZsNBzU+2QJshIUDQtxMkzdwd # eDrknq3lNHGS1yZr5Dhzq6YBT70/O3itTK37xJV77QpfMzmHQXh6OOmc4d0j/R0o # 08f56PGYX/sr2H7yRp11LB4nLCbbbxV7HhmLNriT1ObyF5lZynDwN7+YAN8gFk8n # +2BnFqFmut1VwDophrCYoCvtlUG3OtUVmDG0YgkPCr2B2RP+v6TR81fZvAT6gt4y # 3wSJ8ADNXcL50CN/AAvkdgIm2fBldkKmKYcJRyvmfxqkhQ/8mJb2VVQrH4D6wPIO # K+XW+6kvRBVK5xMOHds3OBqhK/bt1nz8MIIGwjCCBKqgAwIBAgIQBUSv85SdCDmm # v9s/X+VhFjANBgkqhkiG9w0BAQsFADBjMQswCQYDVQQGEwJVUzEXMBUGA1UEChMO # RGlnaUNlcnQsIEluYy4xOzA5BgNVBAMTMkRpZ2lDZXJ0IFRydXN0ZWQgRzQgUlNB # NDA5NiBTSEEyNTYgVGltZVN0YW1waW5nIENBMB4XDTIzMDcxNDAwMDAwMFoXDTM0 # MTAxMzIzNTk1OVowSDELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lDZXJ0LCBJ # bmMuMSAwHgYDVQQDExdEaWdpQ2VydCBUaW1lc3RhbXAgMjAyMzCCAiIwDQYJKoZI # hvcNAQEBBQADggIPADCCAgoCggIBAKNTRYcdg45brD5UsyPgz5/X5dLnXaEOCdwv # SKOXejsqnGfcYhVYwamTEafNqrJq3RApih5iY2nTWJw1cb86l+uUUI8cIOrHmjsv # lmbjaedp/lvD1isgHMGXlLSlUIHyz8sHpjBoyoNC2vx/CSSUpIIa2mq62DvKXd4Z # GIX7ReoNYWyd/nFexAaaPPDFLnkPG2ZS48jWPl/aQ9OE9dDH9kgtXkV1lnX+3RCh # G4PBuOZSlbVH13gpOWvgeFmX40QrStWVzu8IF+qCZE3/I+PKhu60pCFkcOvV5aDa # Y7Mu6QXuqvYk9R28mxyyt1/f8O52fTGZZUdVnUokL6wrl76f5P17cz4y7lI0+9S7 # 69SgLDSb495uZBkHNwGRDxy1Uc2qTGaDiGhiu7xBG3gZbeTZD+BYQfvYsSzhUa+0 # rRUGFOpiCBPTaR58ZE2dD9/O0V6MqqtQFcmzyrzXxDtoRKOlO0L9c33u3Qr/eTQQ # fqZcClhMAD6FaXXHg2TWdc2PEnZWpST618RrIbroHzSYLzrqawGw9/sqhux7Ujip # mAmhcbJsca8+uG+W1eEQE/5hRwqM/vC2x9XH3mwk8L9CgsqgcT2ckpMEtGlwJw1P # t7U20clfCKRwo+wK8REuZODLIivK8SgTIUlRfgZm0zu++uuRONhRB8qUt+JQofM6 # 04qDy0B7AgMBAAGjggGLMIIBhzAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIw # ADAWBgNVHSUBAf8EDDAKBggrBgEFBQcDCDAgBgNVHSAEGTAXMAgGBmeBDAEEAjAL # BglghkgBhv1sBwEwHwYDVR0jBBgwFoAUuhbZbU2FL3MpdpovdYxqII+eyG8wHQYD # VR0OBBYEFKW27xPn783QZKHVVqllMaPe1eNJMFoGA1UdHwRTMFEwT6BNoEuGSWh0 # dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRHNFJTQTQwOTZT # SEEyNTZUaW1lU3RhbXBpbmdDQS5jcmwwgZAGCCsGAQUFBwEBBIGDMIGAMCQGCCsG # AQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2VydC5jb20wWAYIKwYBBQUHMAKGTGh0 # dHA6Ly9jYWNlcnRzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRHNFJTQTQw # OTZTSEEyNTZUaW1lU3RhbXBpbmdDQS5jcnQwDQYJKoZIhvcNAQELBQADggIBAIEa # 1t6gqbWYF7xwjU+KPGic2CX/yyzkzepdIpLsjCICqbjPgKjZ5+PF7SaCinEvGN1O # tt5s1+FgnCvt7T1IjrhrunxdvcJhN2hJd6PrkKoS1yeF844ektrCQDifXcigLiV4 # JZ0qBXqEKZi2V3mP2yZWK7Dzp703DNiYdk9WuVLCtp04qYHnbUFcjGnRuSvExnvP # nPp44pMadqJpddNQ5EQSviANnqlE0PjlSXcIWiHFtM+YlRpUurm8wWkZus8W8oM3 # NG6wQSbd3lqXTzON1I13fXVFoaVYJmoDRd7ZULVQjK9WvUzF4UbFKNOt50MAcN7M # mJ4ZiQPq1JE3701S88lgIcRWR+3aEUuMMsOI5ljitts++V+wQtaP4xeR0arAVeOG # v6wnLEHQmjNKqDbUuXKWfpd5OEhfysLcPTLfddY2Z1qJ+Panx+VPNTwAvb6cKmx5 # AdzaROY63jg7B145WPR8czFVoIARyxQMfq68/qTreWWqaNYiyjvrmoI1VygWy2ny # Mpqy0tg6uLFGhmu6F/3Ed2wVbK6rr3M66ElGt9V/zLY4wNjsHPW2obhDLN9OTH0e # aHDAdwrUAuBcYLso/zjlUlrWrBciI0707NMX+1Br/wd3H3GXREHJuEbTbDJ8WC9n # R2XlG3O2mflrLAZG70Ee8PBf4NvZrZCARK+AEEGKMYIGPjCCBjoCAQEwaDBUMQsw # CQYDVQQGEwJHQjEYMBYGA1UEChMPU2VjdGlnbyBMaW1pdGVkMSswKQYDVQQDEyJT # ZWN0aWdvIFB1YmxpYyBDb2RlIFNpZ25pbmcgQ0EgUjM2AhANIM3qwHRbWKHw+Zq6 # JhzlMA0GCWCGSAFlAwQCAQUAoIGEMBgGCisGAQQBgjcCAQwxCjAIoAKAAKECgAAw # GQYJKoZIhvcNAQkDMQwGCisGAQQBgjcCAQQwHAYKKwYBBAGCNwIBCzEOMAwGCisG # AQQBgjcCARUwLwYJKoZIhvcNAQkEMSIEIJFiinYuXnQxUUptUi8F9pbm9FhcN56h # CArvoORLTlSbMA0GCSqGSIb3DQEBAQUABIICABdGswJJ+Swz7AobBgnDIbkdfMJm # Hg0fmMqF+RIGA0EButILAacdcRLsJsPMaRIz9q2MHRIr9M9Ay7HOY7SyMW/Nguuh # NOuZa6O6zP+8uzbjT559uuCDGP/5RiRt3kGejlmmjW0NPb2ltg94pEOWk8bbvjbc # /VIZC2o22S/mDNqBoQ01uPluHrsc/+ARKaLB9beARe/NJYPX/1yAzE61Z7tTpasV # 4ZL6LGx6e+PzWO+I3oZW0nU64ZdyHSFSQrAQ7e4Fo+HuNyNsop7ThhQsIv/rdO3X # k3qcvPf3zHDKPuXl2LoHWp2Jt8RtvhIAkKHJoltQ7t3eGiFDAIqTX3nKaMO50+Th # ZOnruekdB7NFppWC0YdRNG+T75eiZcRK3SD/K/fVivAxmrUlosbWTBwC6aP2bNqa # thRAuh1WC4uYZKu1fZmJKdLTpBlBdcV772SarSe9mPf6E9YrXBvUIwY2NiAiyY/s # ajBCzvm4ZsHDdV5A+I80J+8V0CkN+E3fsLY5kf7c3pwTIQcbQ/z4Jcie4HT4jMfc # d9wzaV7Pbx1bzYMcL03fQUFWSLlWPo2eNf0F8igzziJeLhgOQBrpRSY2vwL2blCL # BTzJgssO29aqSfFxu5cTG3JVzMv/cH2c9/rtNid7TmiUc+pJkHGOK4WYiuVeQ+AM # hTX5JtUbMZ4lWkZSoYIDIDCCAxwGCSqGSIb3DQEJBjGCAw0wggMJAgEBMHcwYzEL # MAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lDZXJ0LCBJbmMuMTswOQYDVQQDEzJE # aWdpQ2VydCBUcnVzdGVkIEc0IFJTQTQwOTYgU0hBMjU2IFRpbWVTdGFtcGluZyBD # QQIQBUSv85SdCDmmv9s/X+VhFjANBglghkgBZQMEAgEFAKBpMBgGCSqGSIb3DQEJ # AzELBgkqhkiG9w0BBwEwHAYJKoZIhvcNAQkFMQ8XDTI0MDkwNzA1MTMxMVowLwYJ # KoZIhvcNAQkEMSIEIF3UTeZmglpWm8eMEYE8mJGfkqRyUVufjRWDPpmDPIbJMA0G # CSqGSIb3DQEBAQUABIICAJZBKrncemUpLXwRF+ZmXhyKEUEY3MuW5rQGq6AMmXiV # AznAR+V3hHiwCSQGl6NGkwIWoiyFWMIfyG1oLzjN7uLHnPLm2vsd8M3VlkGXQmsY # yNODrPjhry6nYkNjGVIxCAOzlRSeUb5wb/j4usi8Jg0lf5vB60SoqGvnxNl83fYt # IZLkLO9PFvV/Pn4RqFOn3kymE2TeZMe6P5vgUs+DV4m6y8NsWEp37unKtxdNMpkM # JLduXGC+drJ7kpYsLCRQ8bT0af6ETi7eeqFeR46PSz02VJ/AS7VZMWUVjkmA1a6/ # HTxgMkgvBJFF2z/qOi1H+SLthdzjrsB/yjk87LNtcLDFYB9tjnMWmLRkjLAmiGy5 # yA4PyGJet7KvDEE6Tq5+qYHYstS6d8XlPxTQopLQN6eEkX2FIPdZFrWmqKkRqBlO # ywJjgVFC3vifTsn8BPs3MtRh+WJURusl24Dx6xZOsuhE/e9sfEm5CHGBPCrdxwZ5 # o3S/gdc78uGdCiJBnM9o2PK9zKtJ417baN58xV4gkssw2QGkmAepdAC1wEddhE90 # 9AxVGCg/GIBD8iKPb/9Unh6EQw6RNjba9TgX/UOamQlhEP9pTzD3taLduZ7BOafM # hb7Y1xXoK17k43ScpahEl0xyfc81aWEI5Mwkisbi9OTwHRUGPZ4GRlRvRtz95REy # SIG # End signature block |