First push
ASP.NET AJAX / build_web_app (push) Waiting to run
Angular / build_angular (push) Waiting to run
ASP.NET Core (with Reporting) / build_windows (push) Waiting to run
Blazor (with Reporting) / build_windows (push) Waiting to run
Blazor (with Reporting) / build_linux (push) Waiting to run
Console (.NET) / build_console (arm64, linux) (push) Waiting to run
Console (.NET) / build_console (arm64, win) (push) Waiting to run
Console (.NET) / build_console (x64, linux) (push) Waiting to run
Console (.NET) / build_console (x64, win) (push) Waiting to run
MAUI / Windows Smoketest (push) Waiting to run
MAUI / Android Smoketest (push) Waiting to run
MAUI / iOS Smoketest (push) Waiting to run
MAUI / MacCatalyst Smoketest (push) Waiting to run
WinForms (.NET Framework) / build_desktop (Release, x64) (push) Waiting to run
WinForms (.NET Framework) / build_desktop (Release, x86) (push) Waiting to run
WinUI3 / build-windows (push) Waiting to run
WPF (.NET Framework) / build_desktop (Release, x64) (push) Waiting to run
WPF (.NET Framework) / build_desktop (Release, x86) (push) Waiting to run
ASP.NET Core (with Reporting) - Docker / Microsoft Base - Publish to Docker Hub (push) Waiting to run
ASP.NET Core (with Reporting) - Docker / CentOS Base - Publish to Docker Hub (push) Waiting to run
Blazor (with Reporting) - Docker / Dockerfile Build and Publish (push) Waiting to run

This commit is contained in:
Lance McCarthy
2026-05-21 15:10:03 -04:00
parent b6c6d21876
commit 82e0ef17e9
307 changed files with 58592 additions and 732 deletions
+19
View File
@@ -0,0 +1,19 @@
# Project Overview
This project contains many different applications, whose purpose is to demonstrate how to build Telerik and Kendo applications on CI/CD platforms like GitHub Actions, Azure DevOps, and others. The repo has a variety of project types, from WPF to .NET MAUI that has an accompanying build example in the respective CI YAML file.
## Folder Structure
- `/src`: Contains the project folders, each project folder contains one example of a Telerik product being used in that platform. The folder names are describing which framework/platform that the demo is for. For example, the 'WPF' folder contains a WPF project that uses Telerik UI for WPF... and the 'Blazor' folder contains a project for ASP.NET Blazor that is using Telerik UI for Blazor. That same pattern is followed for all the subfolders of the 'src' folder
## Libraries and Frameworks
- .NET and C# for the WPF, WinForms, ASP.NET, Blazor, MAUI, WinUI and AJAX demos
- Angular and Node.js for the Angular project
- GitHub Actions flavor YAML for the workflows in '/.github/workflows'
- Azure DevOps flavored YAML for the 'azure-pipelines.yaml' file
## CI Examples
- `/.github/workflows`: Contains all the GitHub Actions examples
- `azure-pipelines.yaml`: Contains all the Azure DevOps examples
+16
View File
@@ -0,0 +1,16 @@
# Combines all dependency updates into a single PR
# https://docs.github.com/en/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file
version: 2
updates:
- package-ecosystem: "npm"
directory: "/src/Kendo/angular_demo"
schedule:
interval: "monthly"
ignore:
- dependency-name: "*"
update-types: ["version-update:semver-patch"]
groups:
dependencies:
applies-to: version-updates
patterns:
- "*"
@@ -0,0 +1,112 @@
---
name: configure-telerik-nuget
version: 1.1.0
description: Helps setup, configure and manage Telerik NuGet feeds in your repo's nuget.config file.
required_binaries:
- pwsh
- dotnet
author: Lance McCarthy
homepage: https://github.com/DevOpsExamples/.github
source: https://github.com/DevOpsExamples/.github/tree/main/skills/configure-telerik-nuget
---
Use these helper functions to manage Telerik NuGet source configuration.
Get a new api key at https://www.telerik.com/account/downloads/api-keys
## Function: configure
Sets a global user environment variable for the Telerik API key and updates/creates `nuget.config` so credentials use the environment variable instead of a hardcoded secret.
```powershell
function Configure-TelerikNuGetSource {
param(
[Parameter(Mandatory = $true)]
[string]$ApiKey,
[string]$ApiKeyEnvVarName = "TELERIK_NUGET_API_KEY",
[string]$SourceName = "Telerik_NuGet_Server",
[string]$SourceUrl = "https://nuget.telerik.com/v3/index.json",
[string]$NuGetConfigPath = "./nuget.config"
)
# Store API key as a user-level environment variable so it is available globally
[Environment]::SetEnvironmentVariable($ApiKeyEnvVarName, $ApiKey, "User")
$env:$ApiKeyEnvVarName = $ApiKey
if (-not (Test-Path $NuGetConfigPath)) {
@"
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<packageSources />
<packageSourceCredentials />
</configuration>
"@ | Set-Content -Path $NuGetConfigPath -Encoding UTF8
}
[xml]$nugetConfig = Get-Content -Path $NuGetConfigPath
if (-not $nugetConfig.configuration.packageSources) {
$packageSourcesNode = $nugetConfig.CreateElement("packageSources")
$nugetConfig.configuration.AppendChild($packageSourcesNode) | Out-Null
}
if (-not $nugetConfig.configuration.packageSourceCredentials) {
$credentialsNode = $nugetConfig.CreateElement("packageSourceCredentials")
$nugetConfig.configuration.AppendChild($credentialsNode) | Out-Null
}
$existingSource = $nugetConfig.configuration.packageSources.add | Where-Object {
$_.key -eq $SourceName
}
if ($existingSource) {
$existingSource.value = $SourceUrl
} else {
$sourceNode = $nugetConfig.CreateElement("add")
$sourceNode.SetAttribute("key", $SourceName)
$sourceNode.SetAttribute("value", $SourceUrl)
$nugetConfig.configuration.packageSources.AppendChild($sourceNode) | Out-Null
}
$existingCredentialNode = $nugetConfig.configuration.packageSourceCredentials.$SourceName
if (-not $existingCredentialNode) {
$existingCredentialNode = $nugetConfig.CreateElement($SourceName)
$nugetConfig.configuration.packageSourceCredentials.AppendChild($existingCredentialNode) | Out-Null
}
$existingCredentialNode.RemoveAll()
$usernameNode = $nugetConfig.CreateElement("add")
$usernameNode.SetAttribute("key", "Username")
$usernameNode.SetAttribute("value", "api-key")
$existingCredentialNode.AppendChild($usernameNode) | Out-Null
$passwordNode = $nugetConfig.CreateElement("add")
$passwordNode.SetAttribute("key", "ClearTextPassword")
$passwordNode.SetAttribute("value", "%$ApiKeyEnvVarName%")
$existingCredentialNode.AppendChild($passwordNode) | Out-Null
$nugetConfig.Save((Resolve-Path $NuGetConfigPath))
Write-Host "Configured Telerik feed '$SourceName' in '$NuGetConfigPath'."
Write-Host "Saved API key in user environment variable '$ApiKeyEnvVarName'."
Write-Host "Restart your terminal/IDE so new processes can read the updated environment variable."
}
```
Example usage:
```powershell
Configure-TelerikNuGetSource -ApiKey "<telerik-api-key>"
```
## Changelog
### 1.1.0 - 2026-03-20
- Added skill package metadata: `required_binaries`, `author`, `homepage`, and `source`.
- Updated configure function to save API key as a user-level environment variable.
- Updated `nuget.config` credential handling to use environment variable expansion instead of hardcoded API key values.
- Added `version` field to frontmatter.
@@ -0,0 +1,17 @@
---
name: github-actions-failure-debugging
description: Guide for debugging failing GitHub Actions workflows. Use this when asked to debug failing GitHub Actions workflows.
version: 1.0.0
author: Lance McCarthy
homepage: https://github.com/DevOpsExamples/.github
source: https://github.com/DevOpsExamples/.github/tree/main/skills/github-actions-failure-debugging
---
To debug failing GitHub Actions workflows in a pull request, follow this process, using tools provided from the GitHub MCP Server:
1. Use the `list_workflow_runs` tool to look up recent workflow runs for the pull request and their status
2. Use the `summarize_job_log_failures` tool to get an AI summary of the logs for failed jobs, to understand what went wrong without filling your context windows with thousands of lines of logs
3. If you still need more information, use the `get_job_logs` or `get_workflow_run_logs` tool to get the full, detailed failure logs
4. Try to reproduce the failure yourself in your own environment.
5. Fix the failing build. If you were able to reproduce the failure yourself, make sure it is fixed before committing your changes.ng to use environment variable expansion instead of hardcoded API key values.
- Added `version` field to frontmatter.
+45
View File
@@ -0,0 +1,45 @@
# This example shows you how you can use the named environment variables in the nuget.config file to set the credentials, for more information see https://github.com/LanceMcCarthy/DevOpsExamples#github-actions-using-secrets-to-set-environment-variables
name: ASP.NET AJAX
on:
workflow_dispatch:
push:
branches:
- main
- "ajax/*"
paths:
- 'src/Ajax/**/*'
- '.github/workflows/main_build-ajax.yml'
env:
CSPROJ_PATH: "src/Ajax/MySite.sln"
NUGETCONFIG_PATH: "src/NuGet.Config"
jobs:
build_web_app:
runs-on: windows-latest
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Setup Nuget.exe
uses: nuget/setup-nuget@v2
with:
nuget-version: '5.x'
- name: Setup MSBuild.exe
uses: microsoft/setup-msbuild@v2
# Important: We are using nuget CLI (not dotnet CLI) See https://docs.microsoft.com/en-us/nuget/reference/nuget-exe-cli-reference
- name: NuGet.exe Restore
run: nuget restore ${{env.CSPROJ_PATH}} -ConfigFile ${{env.NUGETCONFIG_PATH}}
env:
TELERIK_USERNAME: "api-key" # Variable name used in the nuget.config file
TELERIK_PASSWORD: ${{secrets.TELERIK_NUGET_KEY}} # Variable name used in the nuget.config file
- name: Build the AJAX application
run: msbuild ${{env.CSPROJ_PATH}} /t:Restore /p:Configuration=Release /p:RuntimeIdentifier=any
env:
TELERIK_LICENSE: ${{secrets.TELERIK_LICENSE_KEY}}
+37
View File
@@ -0,0 +1,37 @@
name: Angular
on:
workflow_dispatch:
push:
branches:
- main
- "angular/*"
paths:
- 'src/Kendo/angular_demo/**/*'
- '.github/workflows/main_build-angular.yml'
jobs:
build_angular:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Build App
working-directory: src/Kendo/angular_demo
run: |
# 1. Clean the angular cache, to avoid using any expired keys
rm -rf .angular/cache
# 2. Install your project dependencies
npm install
# npm install --save @progress/kendo-licensing; # if missing from package.json
#3. Activate
npx kendo-ui-license activate
#4. Build the project
npm run build --prod
env:
TELERIK_LICENSE: ${{secrets.TELERIK_LICENSE_KEY}}
@@ -0,0 +1,56 @@
name: ASP.NET Core (with Reporting)
on:
workflow_dispatch:
push:
branches:
- main
- "aspnetcore/*"
paths:
- 'src/AspNetCore/**/*'
- '.github/workflows/main_build-aspnetcore.yml'
permissions:
id-token: write # JWT for Akeyless and Azure auth
env:
DOTNET_VERSION: "10.0.x"
PROJECT_PATH: src/AspNetCore/MyAspNetCoreApp/MyAspNetCoreApp.csproj
NUGETCONFIG_PATH: src/NuGet.Config
jobs:
build_windows:
runs-on: windows-latest
steps:
- name: Checkout
uses: actions/checkout@v6
# Using AKeyless for secrets in this demo, but you can use github Secrets instead
- name: Fetch secrets from AKeyless
id: akeyless
uses: LanceMcCarthy/akeyless-action@v5
with:
access-id: 'p-4blpeo5zdfeaom'
static-secrets: |
{
"/progress/TELERIK_NUGET_KEY":"TELERIK_NUGET_KEY",
"/progress/TELERIK_LICENSE":"TELERIK_LICENSE_KEY"
}
export-secrets-to-outputs: true
export-secrets-to-environment: false
- name: Setup .NET Core SDK
uses: actions/setup-dotnet@v5
with:
dotnet-version: ${{env.DOTNET_VERSION}}
- name: Restore NuGet Packages
run: dotnet restore ${{env.PROJECT_PATH}} --configfile ${{env.NUGETCONFIG_PATH}}
env:
TELERIK_USERNAME: "api-key"
TELERIK_PASSWORD: ${{steps.akeyless.outputs.TELERIK_NUGET_KEY}}
- name: Build ASP.NET Core Project
run: dotnet build ${{env.PROJECT_PATH}} -c Debug --no-restore
env:
TELERIK_LICENSE: ${{steps.akeyless.outputs.TELERIK_LICENSE_KEY}}
@@ -0,0 +1,65 @@
name: ASP.NET Core (with Reporting) - Azure
on:
workflow_dispatch:
env:
DOTNET_VERSION: "10.0.x"
PROJECT_PATH: src/AspNetCore/MyAspNetCoreApp/MyAspNetCoreApp.csproj
NUGETCONFIG_PATH: src/NuGet.Config
AZURE_WEBAPP_NAME: DevOpsExamplesCore
ARTIFACTS_DIRECTORY: "src/AspNetCore/MyAspNetCoreApp/artifacts"
TELERIK_USERNAME: "api-key"
TELERIK_PASSWORD: ${{secrets.TELERIK_NUGET_KEY}}
TELERIK_LICENSE: ${{secrets.TELERIK_LICENSE_KEY}}
jobs:
build_windows:
runs-on: windows-latest
steps:
- name: Checkout
uses: actions/checkout@v6
# ----------------------------------------------------------------------- #
# BUILD
# ----------------------------------------------------------------------- #
- name: Setup .NET Core SDK
uses: actions/setup-dotnet@v5
with:
dotnet-version: ${{env.DOTNET_VERSION}}
- name: Restore NuGet Packages
run: dotnet restore ${{env.PROJECT_PATH}} --configfile ${{env.NUGETCONFIG_PATH}}
- name: Build ASP.NET Core Project
run: dotnet publish ${{env.PROJECT_PATH}} -o ${{env.ARTIFACTS_DIRECTORY}} --no-restore
# ----------------------------------------------------------------------- #
# DEPLOY to https://devopsexamplescore.azurewebsites.net/
# ----------------------------------------------------------------------- #
# Option 1
# Deploy using the publish profile saved as a Github Actions secret
- name: Deploy to Azure WebApp
uses: azure/webapps-deploy@v3
with:
app-name: ${{env.AZURE_WEBAPP_NAME}}
publish-profile: ${{secrets.DEVOPSEXAMPLESCORE_PUBLISHSETTINGS}}
package: ${{env.ARTIFACTS_DIRECTORY}}
# Option 2
# Use a service principal to log into Azure, no reliance on GitHub secrets
# - name: Azure Login
# uses: azure/login@v2
# with:
# client-id: ${{secrets.AZUREAPPSERVICE_CLIENTID}}
# tenant-id: ${{secrets.AZUREAPPSERVICE_TENANTID}}
# subscription-id: ${{secrets.AZUREAPPSERVICE_SUBSCRIPTIONID}}
# - name: Deploy to Azure WebApp
# uses: azure/webapps-deploy@v3
# with:
# app-name: ${{env.AZURE_WEBAPP_NAME}}
# package: ${{env.ARTIFACTS_DIRECTORY}}
+108
View File
@@ -0,0 +1,108 @@
# This workflow has three examples (one for IIS, two using containers)
# [Option A] Typical IIS build & publish
# [Option B] DOCKER FILE BUILD - publishes the image to ghcr.io (GitHub container registry)
# [Option C] .NET SDK CONTAINER BUILD - publishes the image to Docker Hub
name: Blazor (with Reporting)
on:
workflow_dispatch:
push:
branches:
- main
- "blazor/*"
paths:
- 'src/Blazor/**/*'
- '.github/workflows/main_build-blazor.yml'
permissions:
contents: read
packages: write # to publish to GitHub container registry
id-token: write # # JWT for Akeyless auth
env:
CONFIGURATION: Release
BLAZOR_PROJ_PATH: src/Blazor/MyBlazorApp/MyBlazorApp.csproj
TEST_PROJ_PATH: src/Blazor/MyBlazorApp.Tests/MyBlazorApp.Tests.csproj
NUGET_CONFIG_PATH: src/NuGet.Config
DOTNET_VERSION: "10.0.x"
jobs:
build_windows:
runs-on: windows-latest
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
# Using AKeyless for secrets in this demo
- name: Fetch secrets from AKeyless
id: akeyless
uses: LanceMcCarthy/akeyless-action@v5
with:
access-id: 'p-4blpeo5zdfeaom'
static-secrets: |
{
"/progress/TELERIK_NUGET_KEY":"TELERIK_NUGET_KEY",
"/progress/TELERIK_LICENSE":"TELERIK_LICENSE_KEY"
}
export-secrets-to-outputs: true
export-secrets-to-environment: false
- name: Setup .NET Core SDK
uses: actions/setup-dotnet@v4
with:
dotnet-version: ${{env.DOTNET_VERSION}}
- name: Restore NuGet Packages
run: |
dotnet restore ${{env.BLAZOR_PROJ_PATH}} --configfile ${{env.NUGET_CONFIG_PATH}}
dotnet restore ${{env.TEST_PROJ_PATH}} --configfile ${{env.NUGET_CONFIG_PATH}}
env:
TELERIK_USERNAME: "api-key"
TELERIK_PASSWORD: ${{steps.akeyless.outputs.TELERIK_NUGET_KEY}}
- name: Build Test Project
run: dotnet build ${{env.TEST_PROJ_PATH}} -c ${{env.CONFIGURATION}} --no-restore
env:
TELERIK_LICENSE: ${{steps.akeyless.outputs.TELERIK_LICENSE_KEY}}
build_linux:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
# Using AKeyless for secrets in this demo
- name: Fetch secrets from AKeyless
id: akeyless
uses: LanceMcCarthy/akeyless-action@v5
with:
access-id: 'p-4blpeo5zdfeaom'
static-secrets: |
{
"/progress/TELERIK_NUGET_KEY":"TELERIK_NUGET_KEY",
"/progress/TELERIK_LICENSE":"TELERIK_LICENSE_KEY"
}
export-secrets-to-outputs: true
export-secrets-to-environment: false
- name: Setup .NET Core SDK
uses: actions/setup-dotnet@v4
with:
dotnet-version: ${{env.DOTNET_VERSION}}
- name: Restore NuGet Packages
run: |
dotnet restore ${{env.BLAZOR_PROJ_PATH}} --configfile ${{env.NUGET_CONFIG_PATH}}
dotnet restore ${{env.TEST_PROJ_PATH}} --configfile ${{env.NUGET_CONFIG_PATH}}
env:
TELERIK_USERNAME: "api-key"
TELERIK_PASSWORD: ${{steps.akeyless.outputs.TELERIK_NUGET_KEY}}
- name: Build Test Project
run: dotnet build ${{env.TEST_PROJ_PATH}} -c ${{env.CONFIGURATION}} --no-restore
env:
TELERIK_LICENSE: ${{steps.akeyless.outputs.TELERIK_LICENSE_KEY}}
+50
View File
@@ -0,0 +1,50 @@
# This example shows you how ot use Akeyless to fetch the required secrets instead of GitHub Actions secrets
name: Console (.NET)
on:
workflow_dispatch:
push:
branches:
- main
- "console/*"
paths:
- 'src/Console/**/*'
- '.github/workflows/main_build-console.yml'
defaults:
run:
shell: pwsh
env:
CSPROJ_PATH: src/Console/MyDocProcApp/MyDocProcApp.csproj
NUGETCONFIG_PATH: src/NuGet.Config
BUILD_CONFIGURATION: Release
jobs:
build_console:
runs-on: ubuntu-22.04
strategy:
matrix:
os: [linux, win]
config: [x64, arm64]
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Setup .NET SDK
uses: actions/setup-dotnet@v4
with:
dotnet-version: '10.0.x'
- name: Update NuGet Package Sources
run: dotnet nuget update source 'Telerik_v3_Feed' -s 'https://nuget.telerik.com/v3/index.json' -u "api-key" -p ${{secrets.TELERIK_NUGET_KEY}} --configfile ${{env.NUGETCONFIG_PATH}} --store-password-in-clear-text
- name: Restore NuGet packages
run: dotnet restore ${{env.CSPROJ_PATH}} --configfile ${{env.NUGETCONFIG_PATH}} --runtime ${{matrix.os}}-${{matrix.config}}
- name: Build project
run: dotnet build ${{env.CSPROJ_PATH}} --configuration ${{env.BUILD_CONFIGURATION}} --runtime ${{matrix.os}}-${{matrix.config}} --no-self-contained --no-restore
env:
TELERIK_LICENSE: ${{secrets.TELERIK_LICENSE_KEY}}
@@ -0,0 +1,119 @@
name: Console (.NET) - Trusted Signing
on:
workflow_dispatch:
defaults:
run:
shell: pwsh
permissions:
id-token: write # For OIDC auth
env:
CSPROJ_PATH: src/Console/MyDocProcApp/MyDocProcApp.csproj
NUGETCONFIG_PATH: src/NuGet.Config
BUILD_CONFIGURATION: Release
jobs:
build_console:
runs-on: ubuntu-22.04
strategy:
matrix:
os: [linux, win]
config: [x64, arm64]
env:
OUTPUT_DIR: ${{github.workspace}}/output/${{matrix.os}}-${{matrix.config}}/
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Setup .NET SDK
uses: actions/setup-dotnet@v4
with:
dotnet-version: '10.0.x'
- name: Update NuGet Package Sources
run: dotnet nuget update source 'Telerik_v3_Feed' -s 'https://nuget.telerik.com/v3/index.json' -u "api-key" -p ${{secrets.TELERIK_NUGET_KEY}} --configfile ${{env.NUGETCONFIG_PATH}} --store-password-in-clear-text
- name: Restore NuGet packages
run: dotnet restore ${{env.CSPROJ_PATH}} --configfile ${{env.NUGETCONFIG_PATH}} --runtime ${{matrix.os}}-${{matrix.config}}
- name: Build project
run: dotnet publish ${{env.CSPROJ_PATH}} --configuration ${{env.BUILD_CONFIGURATION}} --runtime ${{matrix.os}}-${{matrix.config}} --no-self-contained --no-restore --output ${{env.OUTPUT_DIR}}
env:
TELERIK_LICENSE: ${{secrets.TELERIK_LICENSE_KEY}}
- name: Attach artifacts
id: upload-artifact
uses: actions/upload-artifact@v4
with:
name: "Console_${{matrix.os}}-${{matrix.config}}"
path: ${{env.OUTPUT_DIR}}
if-no-files-found: error
retention-days: 30
codesign_release:
name: Codesign Release
if: ${{ success() }}
runs-on: windows-latest
needs: [build_console]
steps:
- name: Download artifacts
uses: actions/download-artifact@v4
with:
path: ${{github.workspace}}/artifacts/
# No secrets needed, uses the GitHub OIDC token to authenticate.
- name: Azure login using OIDC via GitHub
uses: azure/login@v2
id: azlogin
with:
client-id: "32daa13b-f4bb-4809-8ef6-58cb39051acd"
tenant-id: "bd47e796-3473-4b8a-9101-1f4c0c7af31a"
subscription-id: "48ab4839-62af-4ab3-afe6-043ea4d7c137"
# Codesign files with Azure Trusted Signing
- name: Sign files with Trusted Signing
uses: azure/trusted-signing-action@v0.4.0
with:
endpoint: https://eus.codesigning.azure.net/
trusted-signing-account-name: PrimaryCodeSign
certificate-profile-name: lancemccarthylivepublic
timestamp-rfc3161: http://timestamp.acs.microsoft.com
timestamp-digest: SHA256
file-digest: SHA256
files-folder: ${{github.workspace}}/artifacts/
files-folder-filter: exe
files-folder-depth: 3
exclude-azure-cli-credential: false
exclude-environment-credential: true
exclude-workload-identity-credential: true
exclude-managed-identity-credential: true
exclude-shared-token-cache-credential: true
exclude-visual-studio-credential: true
exclude-visual-studio-code-credential: true
exclude-azure-powershell-credential: true
exclude-azure-developer-cli-credential: true
exclude-interactive-browser-credential: true
- name: Attach signed artifact
id: upload-artifact
uses: actions/upload-artifact@v4
with:
name: "Console_Codesigned"
path: ${{github.workspace}}/artifacts/
if-no-files-found: error
retention-days: 30
- name: Delete unsigned artifacts from run
uses: geekyeggo/delete-artifact@v5
with:
name: |
Console_linux-x64
Console_linux-arm64
Console_win-x64
Console_win-arm64
failOnError: false
@@ -0,0 +1,50 @@
name: Console (.NET) - Artifactory Only
on:
workflow_dispatch:
defaults:
run:
shell: pwsh
jobs:
build_console:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: actions/setup-dotnet@v4
with:
dotnet-version: '10.0.x'
- name: Build Console App w/only Artifactory Source
run: |
# 1. Remove all existing package sources
$sourceList = dotnet nuget list source
$sourceNames = @()
foreach ($line in $sourceList) {
if ($line -match '^\s*\d+\.\s+(.*)\s+\[Enabled\]') {
$sourceNames += $matches[1].Trim()
}
}
foreach ($name in $sourceNames) {
dotnet nuget remove source "$name"
Write-Host "Removed $name ..."
}
# 2. Add Artifactory source with credentials from GitHub Secrets
dotnet nuget add source "https://bed-artifactory.bedford.progress.com/artifactory/api/nuget/v3/dt-nuget-virtual-tierpoint/index.json" `
--name "artifactory_virtual" `
--username ${{secrets.ARTIFACTORY_NUGET_USERNAME}} `
--password ${{secrets.ARTIFACTORY_NUGET_PASSWORD}} `
--store-password-in-clear-text
# 3. List sources to verify
dotnet nuget list source
# 4. Build the project
dotnet build "src/Console/MyDocProcApp/MyDocProcApp.csproj"
env:
TELERIK_LICENSE: ${{secrets.TELERIK_LICENSE_KEY}}
+137
View File
@@ -0,0 +1,137 @@
name: MAUI
on:
workflow_dispatch:
push:
branches:
- main
paths:
- 'src/MAUI/**/*'
- '.github/workflows/main_build-maui.yml'
env:
TELERIK_LICENSE: ${{secrets.TELERIK_LICENSE_KEY}}
PROJECT_PATH: "src/MAUI/MauiDemo.csproj"
NUGETCONFIG_PATH: "src/NuGet.Config"
DOTNET_VERSION: "10.0.x"
MAC_DOTNET_VERSION: "10.0.203" # Used to avoid Xcode and MAUI workload problems
TFM_VERSION: "net10.0"
XCODE_VERSION: '26.4'
BUILD_CONFIG: "Debug"
jobs:
maui-windows:
name: Windows Smoketest
runs-on: windows-latest
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Setup .NET SDK
uses: actions/setup-dotnet@v5
with:
dotnet-version: ${{env.DOTNET_VERSION}}
# Only needed for WinUI builds
- name: Add msbuild to PATH
uses: microsoft/setup-msbuild@v3
- name: Install MAUI workloads
run: dotnet workload install maui --source https://api.nuget.org/v3/index.json
- name: Set Telerik NuGet Credentials
run: dotnet nuget update source 'Telerik_v3_Feed' -s 'https://nuget.telerik.com/v3/index.json' -u 'api-key' -p "${{secrets.TELERIK_NUGET_KEY}}" --configfile ${{env.NUGETCONFIG_PATH}} --store-password-in-clear-text
- name: Restore NuGet packages
run: dotnet restore ${{env.PROJECT_PATH}} --configfile ${{env.NUGETCONFIG_PATH}}
- name: Build Maui WinUI project
run: dotnet build ${{env.PROJECT_PATH}} -c ${{env.BUILD_CONFIG}} -f "${{env.TFM_VERSION}}-windows10.0.22621.0" --no-restore
maui-android:
name: Android Smoketest
runs-on: windows-latest
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Setup .NET SDK
uses: actions/setup-dotnet@v5
with:
dotnet-version: ${{env.DOTNET_VERSION}}
- uses: actions/setup-java@v4
with:
distribution: 'microsoft'
java-version: '11'
- name: Install MAUI workloads
run: dotnet workload install maui --source https://api.nuget.org/v3/index.json
- name: Set Telerik NuGet Credentials
run: dotnet nuget update source 'Telerik_v3_Feed' -s 'https://nuget.telerik.com/v3/index.json' -u 'api-key' -p "${{secrets.TELERIK_NUGET_KEY}}" --configfile ${{env.NUGETCONFIG_PATH}} --store-password-in-clear-text
- name: Restore NuGet packages
run: dotnet restore ${{env.PROJECT_PATH}} --configfile ${{env.NUGETCONFIG_PATH}}
- name: Build Maui Android project
run: dotnet build ${{env.PROJECT_PATH}} -c ${{env.BUILD_CONFIG}} -f "${{env.TFM_VERSION}}-android" --no-restore
maui-ios:
name: iOS Smoketest
runs-on: macos-26
steps:
- name: Checkout
uses: actions/checkout@v6
- uses: maxim-lobanov/setup-xcode@v1
with:
xcode-version: ${{env.XCODE_VERSION}}
- name: Setup .NET SDK
uses: actions/setup-dotnet@v5
with:
dotnet-version: ${{env.MAC_DOTNET_VERSION}}
- name: Install MAUI workloads
run: dotnet workload install maui --source https://api.nuget.org/v3/index.json
- name: Set Telerik NuGet Credentials
run: dotnet nuget update source 'Telerik_v3_Feed' -s 'https://nuget.telerik.com/v3/index.json' -u 'api-key' -p "${{secrets.TELERIK_NUGET_KEY}}" --configfile ${{env.NUGETCONFIG_PATH}} --store-password-in-clear-text
- name: Restore NuGet packages
run: dotnet restore ${{env.PROJECT_PATH}} --configfile ${{env.NUGETCONFIG_PATH}}
- name: Build MAUI iOS project
run: dotnet build ${{env.PROJECT_PATH}} -c ${{env.BUILD_CONFIG}} -f "${{env.TFM_VERSION}}-ios" --no-restore -p:PublishTrimmed=True -p:MtouchLink=SdkOnly
maui-macos:
name: MacCatalyst Smoketest
runs-on: macos-26
steps:
- name: Checkout
uses: actions/checkout@v6
- uses: maxim-lobanov/setup-xcode@v1
with:
xcode-version: ${{env.XCODE_VERSION}}
- name: Setup .NET SDK
uses: actions/setup-dotnet@v5
with:
dotnet-version: ${{env.MAC_DOTNET_VERSION}}
- name: Install MAUI workloads
run: dotnet workload install maui --source https://api.nuget.org/v3/index.json
- name: Set Telerik NuGet Credentials
run: dotnet nuget update source 'Telerik_v3_Feed' -s 'https://nuget.telerik.com/v3/index.json' -u 'api-key' -p "${{secrets.TELERIK_NUGET_KEY}}" --configfile ${{env.NUGETCONFIG_PATH}} --store-password-in-clear-text
- name: Restore NuGet packages
run: dotnet restore ${{env.PROJECT_PATH}} --configfile ${{env.NUGETCONFIG_PATH}}
- name: Build MAUI MacCatalyst project
run: dotnet build ${{env.PROJECT_PATH}} -c ${{env.BUILD_CONFIG}} -f "${{env.TFM_VERSION}}-maccatalyst" --no-restore -p:PublishTrimmed=True -p:MtouchLink=SdkOnly
@@ -0,0 +1,771 @@
name: MAUI (Distribution)
on:
workflow_dispatch:
defaults:
run:
shell: pwsh
permissions:
actions: write # Needed to delete artifacts after bundle upload
contents: write # Needed to create GitHub Releases
id-token: write # Needed for Azure auth (OIDC)
env:
# ____Global____________________________________________
APP_NAME: "MauiDemo"
PROJECT_PATH: "src/MAUI/MauiDemo.csproj"
PROJECT_DIRECTORY: "src/MAUI"
SDK_VERSION: '10.0.x'
NET_TFM: 'net10.0'
XCODE_VERSION: '26.4'
MAC_DOTNET_VERSION: '10.0.203'
NUGET_CONFIG_PATH: 'src/nuget.config'
TELERIK_NUGET_KEY: ${{secrets.TELERIK_NUGET_KEY}}
TELERIK_LICENSE: ${{secrets.TELERIK_LICENSE_KEY}}
# ____Android___________________________________________
ANDROID_ARTIFACTS_PATH: "artifacts_android"
ANDROID_KEYSTORE_PATH: "${{github.workspace}}/android-upload.keystore"
JAVA_VERSION: '17'
JAVA_DISTRIBUTION: 'microsoft'
# ____MacCatalyst_______________________________________
APPLE_DEV_ID_APP_CERT_NAME: "Developer ID Application: Lancelot Software, LLC (L65255N3F7)"
APPLE_DEV_ID_INSTALLER_CERT_NAME: "Developer ID Installer: Lancelot Software, LLC (L65255N3F7)"
APPLE_NOTARY_TEAM_ID: "L65255N3F7"
MAC_PACKAGE_NAME: "MauiDemo-MacCatalyst"
MAC_APP_BUNDLE_PATH: "src/Maui/bin/Release/net10.0-maccatalyst/MauiDemo.app"
MAC_ARTIFACTS_PATH: "artifacts/maccatalyst"
# ____iOS______________________________________________ (also check matrix vars)
APPLE_APP_ID: "com.lancelotsoftware.MauiDemoApp"
IOS_RID: ios-arm64
# ____Windows__________________________________________ (also check matrix vars)
WINDOWS_ARTIFACTS_PATH: "artifacts_windows"
SERVICE_PRINCIPAL_CLIENT_ID: "32daa13b-f4bb-4809-8ef6-58cb39051acd"
SERVICE_PRINCIPAL_TENANT_ID: "bd47e796-3473-4b8a-9101-1f4c0c7af31a"
SERVICE_PRINCIPAL_SUBSCRIPTION_ID: "48ab4839-62af-4ab3-afe6-043ea4d7c137"
SIGNING_ACCT_NAME: "PrimaryCodeSign"
SIGNING_ACCT_CERT_PROFILE: "lancemccarthylivepublic"
SIGNING_ACCT_ENDPOINT_URL: "https://eus.codesigning.azure.net/"
# ------------------ REQUIRED SECRETS ---------------- #
# ____Global____________________________________________
# TELERIK_NUGET_KEY
# TELERIK_LICENSE_KEY
# ____Android___________________________________________
# ANDROID_SIGNING_KEYSTORE_BASE64
# ANDROID_SIGNING_KEYSTORE_PASS
# ANDROID_SIGNING_KEY_ALIAS
# ANDROID_SIGNING_KEY_PASS
# ____MacCatalyst_______________________________________
# APPLE_DEVELOPER_ID_INSTALLER_CERT_BASE64
# APPLE_DEVELOPER_ID_INSTALLER_CERT_PASSWORD
# APPLE_DEVELOPER_ID_APPLICATION_CERT_BASE64
# APPLE_NOTARY_APPLE_ID
# APPLE_NOTARY_APP_PASSWORD
# ____iOS______________________________________________
# APPLE_DISTRIBUTION_CERT_BASE64
# APPLE_DISTRIBUTION_CERT_PASSWORD
# APPSTORE_API_ISSUER_ID
# APPSTORE_API_KEY_ID
# APPSTORE_API_PRIVATE_KEY
# ____Windows__________________________________________
# No additional secrets needed, uses Azure Trusted Signing.
jobs:
# ********************************************************************************** #
# Shared Resources #
# ********************************************************************************** #
shared-resources:
name: Create Shared Resources
runs-on: windows-latest
outputs:
app_version: ${{steps.version-creator.outputs.app_version}}
steps:
# Generates a version number using year.monthday.run_number (e.g., 2024.824.1)
- name: Generate version number using date and run number
id: version-creator
shell: pwsh
run: |
$buildDay = Get-Date -Format "yyyy.Mdd"
$runNumber = "$env:GITHUB_RUN_NUMBER"
$ver = $buildDay + "." + $runNumber
echo "app_version=$ver" >> $env:GITHUB_OUTPUT
# ********************************************************************************** #
# Android #
# ********************************************************************************** #
android:
name: Build Android (Store Upload)
runs-on: windows-latest
needs: shared-resources
if: ${{ success() && needs.shared-resources.outputs.app_version != '' }}
steps:
- uses: actions/checkout@v6
- name: Decode the Keystore
shell: pwsh
run: |
$file_bytes = [System.Convert]::FromBase64String("${{secrets.ANDROID_SIGNING_KEYSTORE_BASE64}}")
[IO.File]::WriteAllBytes("${{env.ANDROID_KEYSTORE_PATH}}", $file_bytes)
- name: Setup .NET SDK
uses: actions/setup-dotnet@v5
with:
dotnet-version: ${{env.SDK_VERSION}}
- uses: actions/setup-java@v5
with:
distribution: ${{env.JAVA_DISTRIBUTION}}
java-version: ${{env.JAVA_VERSION}}
- name: Install MAUI Workloads
run: dotnet workload install maui --source https://api.nuget.org/v3/index.json
- name: Set Telerik NuGet Credentials
run: dotnet nuget update source 'Telerik_v3_Feed' -s 'https://nuget.telerik.com/v3/index.json' -u 'api-key' -p "${{secrets.TELERIK_NUGET_KEY}}" --configfile '${{env.NUGET_CONFIG_PATH}}' --store-password-in-clear-text
- name: Restore NuGet packages
run: dotnet restore ${{env.PROJECT_PATH}} --configfile ${{env.NUGET_CONFIG_PATH}}
- name: Publish MAUI Android
run: |
dotnet publish ${{env.PROJECT_PATH}} -c Release -f ${{env.NET_TFM}}-android /p:AndroidKeyStore=true /p:AndroidSigningKeyStore=${{env.ANDROID_KEYSTORE_PATH}} /p:AndroidSigningStorePass=${{secrets.ANDROID_SIGNING_KEYSTORE_PASS}} /p:AndroidSigningKeyAlias=${{secrets.ANDROID_SIGNING_KEY_ALIAS}} /p:AndroidSigningKeyPass=${{secrets.ANDROID_SIGNING_KEY_PASS}}
- name: Create artifacts folder
run: |
New-Item -ItemType Directory -Force -Path ${{env.ANDROID_ARTIFACTS_PATH}} | Out-Null
- name: Copy signed APKs & AABs
run: |
$publishRoot = "src/Maui/bin/Release/${{env.NET_TFM}}-android"
$apkFiles = Get-ChildItem -Path $publishRoot -Filter *-Signed.apk -File -Recurse -ErrorAction SilentlyContinue
$aabFiles = Get-ChildItem -Path $publishRoot -Filter *-Signed.aab -File -Recurse -ErrorAction SilentlyContinue
if ($apkFiles.Count -eq 0 -and $aabFiles.Count -eq 0) {
throw "No signed Android APK/AAB files found under $publishRoot"
}
$apkFiles | ForEach-Object { Copy-Item -Path $_.FullName -Destination ${{env.ANDROID_ARTIFACTS_PATH}} -Force }
$aabFiles | ForEach-Object { Copy-Item -Path $_.FullName -Destination ${{env.ANDROID_ARTIFACTS_PATH}} -Force }
- name: Publish Android build artifacts
uses: actions/upload-artifact@v7
with:
name: "${{env.APP_NAME}}_v${{needs.shared-resources.outputs.app_version}}_signed-android"
path: "${{env.ANDROID_ARTIFACTS_PATH}}/*"
if-no-files-found: error
retention-days: 60
# ********************************************************************************** #
# Windows (Sideload - msixbundle) #
# ********************************************************************************** #
windows-sideload-packages:
name: Build Windows (Sideload)
needs: shared-resources
runs-on: windows-latest
if: ${{ needs.shared-resources.outputs.app_version != ''}}
strategy:
fail-fast: false
matrix:
include:
- arch: x64
runtime_id: win-x64
platform: x64
- arch: arm64
runtime_id: win-arm64
platform: ARM64
steps:
- name: Checkout
uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Setup .NET Core SDK
uses: actions/setup-dotnet@v5
with:
dotnet-version: ${{env.SDK_VERSION}}
# Needed only for WinUI builds
- name: Add msbuild to PATH
uses: microsoft/setup-msbuild@v3
- name: Install MAUI workloads
run: dotnet workload install maui --source https://api.nuget.org/v3/index.json
- name: Set Telerik NuGet Credentials
run: dotnet nuget update source 'Telerik_v3_Feed' -s 'https://nuget.telerik.com/v3/index.json' -u 'api-key' -p "${{secrets.TELERIK_NUGET_KEY}}" --configfile '${{env.NUGET_CONFIG_PATH}}' --store-password-in-clear-text
- name: Restore NuGet packages
run: dotnet restore ${{env.PROJECT_PATH}} --configfile ${{env.NUGET_CONFIG_PATH}}
- name: Update manifest for sideload build
run: |
[xml]$manifest = Get-Content '${{env.PROJECT_DIRECTORY}}\Platforms\Windows\Package.appxmanifest'
$manifest.Package.Identity.Version = "${{needs.shared-resources.outputs.app_version}}.0"
$manifest.Save('${{env.PROJECT_DIRECTORY}}\Platforms\Windows\Package.appxmanifest')
- name: Publish ${{matrix.arch}} Windows package
id: publish-package
run: |
$appVersion = "${{needs.shared-resources.outputs.app_version}}.0"
$artifactDir = "${{github.workspace}}\${{env.WINDOWS_ARTIFACTS_PATH}}\${{matrix.arch}}"
New-Item -ItemType Directory -Force -Path $artifactDir | Out-Null
Write-Host "Publishing Windows ${{matrix.arch}}..."
$publishArgs = @(
'publish'
'${{env.PROJECT_PATH}}'
'-c', 'Release'
'-f', '${{env.NET_TFM}}-windows10.0.19041.0'
'-p:Platform=${{matrix.platform}}'
'-p:AppxPackageBuildPlatform=${{matrix.platform}}'
'-p:RuntimeIdentifierOverride=${{matrix.runtime_id}}'
'-p:GenerateAppxPackageOnBuild=true'
'-p:AppxPackageSigningEnabled=false'
'-p:UapAppxPackageBuildMode=SideloadOnly'
'-p:AppxBundle=Never'
'-p:WindowsPackageType=MSIX'
)
& dotnet @publishArgs
if ($LASTEXITCODE -ne 0) { throw "dotnet publish failed for ${{matrix.arch}} with exit code $LASTEXITCODE" }
# MAUI writes MSIX to src\${{env.APP_NAME}}\AppPackages\<name>_<ver>_<arch>_Test\*.msix
$appPackagesRoot = "src\${{env.APP_NAME}}\AppPackages"
$candidates = Get-ChildItem -Path $appPackagesRoot -Filter *.msix -Recurse -ErrorAction SilentlyContinue |
Where-Object { $_.FullName -notmatch '\\Dependencies\\' -and $_.Name -match "_${{matrix.arch}}\.msix$" }
Write-Host "Found $($candidates.Count) candidate MSIX file(s) for ${{matrix.arch}}:"
$candidates | ForEach-Object { Write-Host " $($_.FullName)" }
$msix = $candidates | Sort-Object LastWriteTime -Descending | Select-Object -ExpandProperty FullName -First 1
if (-not $msix) { throw "No .msix found after publish for ${{matrix.arch}}" }
Write-Host "Found ${{matrix.arch}} package: $msix"
Copy-Item -Path $msix -Destination "$artifactDir\${{env.APP_NAME}}.${{matrix.arch}}.msix" -Force
echo "PACKAGE_PATH=$artifactDir\${{env.APP_NAME}}.${{matrix.arch}}.msix" >> $env:GITHUB_OUTPUT
- name: Upload ${{matrix.arch}} artifact
uses: actions/upload-artifact@v7
with:
name: "${{env.APP_NAME}}_v${{needs.shared-resources.outputs.app_version}}_${{matrix.runtime_id}}"
path: ${{steps.publish-package.outputs.PACKAGE_PATH}}
if-no-files-found: error
retention-days: 30
windows-generate-msixbundle:
name: Bundle MSIX packages
needs: [shared-resources, windows-sideload-packages]
runs-on: windows-latest
steps:
- name: Clean bundle workspace
run: |
Remove-Item -Recurse -Force "${{github.workspace}}\bundle-input" -ErrorAction SilentlyContinue
Remove-Item -Recurse -Force "${{github.workspace}}\bundle-output" -ErrorAction SilentlyContinue
- name: Download x64 artifact
uses: actions/download-artifact@v8
with:
name: "${{env.APP_NAME}}_v${{needs.shared-resources.outputs.app_version}}_win-x64"
path: ${{github.workspace}}\bundle-input
- name: Download arm64 artifact
uses: actions/download-artifact@v8
with:
name: "${{env.APP_NAME}}_v${{needs.shared-resources.outputs.app_version}}_win-arm64"
path: ${{github.workspace}}\bundle-input
- name: Create MSIX bundle
id: bundle
run: |
$appVersion = "${{needs.shared-resources.outputs.app_version}}.0"
$inputDir = "${{github.workspace}}\bundle-input"
$outputDir = "${{github.workspace}}\bundle-output"
New-Item -ItemType Directory -Force -Path $outputDir | Out-Null
# Locate makeappx.exe from the latest installed Windows 10 SDK
$sdkRoot = "C:\Program Files (x86)\Windows Kits\10\bin"
$makeAppx = Get-ChildItem -Path $sdkRoot -Recurse -Filter makeappx.exe -ErrorAction SilentlyContinue |
Where-Object { $_.FullName -match '\\x64\\makeappx\.exe$' } |
Sort-Object FullName -Descending | Select-Object -First 1
if (-not $makeAppx) { throw "makeappx.exe not found under $sdkRoot" }
Write-Host "Using makeappx: $($makeAppx.FullName)"
Get-ChildItem $inputDir -Filter *.msix | ForEach-Object { Write-Host "Input: $($_.FullName)" }
$bundlePath = "$outputDir\${{env.APP_NAME}}_v${{needs.shared-resources.outputs.app_version}}.msixbundle"
& $makeAppx.FullName bundle /d $inputDir /p $bundlePath /bv $appVersion /o
if ($LASTEXITCODE -ne 0) { throw "makeappx bundle failed with exit code $LASTEXITCODE" }
echo "BUNDLE_PATH=$bundlePath" >> $env:GITHUB_OUTPUT
# Entra ID App Registration (Akeyless OIDC Provider) > Certificates and Secrets > Federated Credentials
- name: Azure login using OIDC via GitHub
uses: azure/login@v3
id: azlogin
with:
client-id: ${{env.SERVICE_PRINCIPAL_CLIENT_ID}}
tenant-id: ${{env.SERVICE_PRINCIPAL_TENANT_ID}}
subscription-id: ${{env.SERVICE_PRINCIPAL_SUBSCRIPTION_ID}}
- name: Sign MSIX bundle
uses: azure/trusted-signing-action@v1.2.0
with:
endpoint: ${{env.SIGNING_ACCT_ENDPOINT_URL}}
signing-account-name: ${{env.SIGNING_ACCT_NAME}}
certificate-profile-name: ${{env.SIGNING_ACCT_CERT_PROFILE}}
timestamp-rfc3161: http://timestamp.acs.microsoft.com
timestamp-digest: SHA256
file-digest: SHA256
files: ${{steps.bundle.outputs.BUNDLE_PATH}}
exclude-azure-cli-credential: false
exclude-environment-credential: true
exclude-workload-identity-credential: true
exclude-managed-identity-credential: true
exclude-shared-token-cache-credential: true
exclude-visual-studio-credential: true
exclude-visual-studio-code-credential: true
exclude-azure-powershell-credential: true
exclude-azure-developer-cli-credential: true
exclude-interactive-browser-credential: true
- name: Upload MSIX bundle artifact
uses: actions/upload-artifact@v7
with:
name: "${{env.APP_NAME}}_v${{needs.shared-resources.outputs.app_version}}_signed-windows.msixbundle"
path: ${{steps.bundle.outputs.BUNDLE_PATH}}
if-no-files-found: error
retention-days: 30
# ********************************************************************************** #
# Windows (Store - msixupload) #
# ********************************************************************************** #
windows-store:
name: Build Windows (Store Upload)
runs-on: windows-latest
needs: shared-resources
if: ${{ success() && needs.shared-resources.outputs.app_version != '' }}
steps:
- uses: actions/checkout@v6
- name: Setup .NET SDK
uses: actions/setup-dotnet@v5
with:
dotnet-version: ${{env.SDK_VERSION}}
- name: Add msbuild to PATH
uses: microsoft/setup-msbuild@v3
- name: Install MAUI Workloads
run: dotnet workload install maui --source https://api.nuget.org/v3/index.json
- name: Set Telerik NuGet Credentials
run: dotnet nuget update source 'Telerik_v3_Feed' -s 'https://nuget.telerik.com/v3/index.json' -u 'api-key' -p "${{secrets.TELERIK_NUGET_KEY}}" --configfile '${{env.NUGET_CONFIG_PATH}}' --store-password-in-clear-text
- name: Restore NuGet packages
run: dotnet restore ${{env.PROJECT_PATH}} --configfile ${{env.NUGET_CONFIG_PATH}}
- name: Update manifest for store build
run: |
[xml]$manifest = Get-Content '${{env.PROJECT_DIRECTORY}}\Platforms\Windows\Package.appxmanifest'
$manifest.Package.Identity.Version = "${{needs.shared-resources.outputs.app_version}}.0"
$manifest.Save('${{env.PROJECT_DIRECTORY}}\Platforms\Windows\Package.appxmanifest')
- name: Build Maui WinUI project
run: dotnet publish ${{env.PROJECT_PATH}} -c Release -f ${{env.NET_TFM}}-windows10.0.19041.0 -p:AppxPackageSigningEnabled=false -p:AppxSymbolPackageEnabled=false -p:UapAppxPackageBuildMode=StoreUpload -p:AppxBundle=Always "-p:AppxBundlePlatforms=x64|arm64" -p:PlatformTarget=AnyCPU
- name: Publish build artifacts
uses: actions/upload-artifact@v7
with:
name: "${{env.APP_NAME}}_v${{needs.shared-resources.outputs.app_version}}_storeupload-windows"
path: "**/*.msixupload"
if-no-files-found: error
retention-days: 60
# ********************************************************************************** #
# MacCatalyst #
# ********************************************************************************** #
maccatalyst:
name: Build MacCatalyst (Signed & Notiarized)
runs-on: macos-26 # https://github.com/actions/runner-images#available-images
needs: shared-resources
if: ${{ success() && needs.shared-resources.outputs.app_version != '' }}
steps:
- uses: actions/checkout@v6
- uses: maxim-lobanov/setup-xcode@v1
with:
xcode-version: ${{env.XCODE_VERSION}}
- name: Setup .NET SDK
uses: actions/setup-dotnet@v5
with:
dotnet-version: ${{env.SDK_VERSION}}
- name: Install MAUI Workloads
run: dotnet workload install maui --source https://api.nuget.org/v3/index.json
- name: Set Telerik NuGet Credentials
run: dotnet nuget update source 'Telerik_v3_Feed' -s 'https://nuget.telerik.com/v3/index.json' -u 'api-key' -p "${{secrets.TELERIK_NUGET_KEY}}" --configfile '${{env.NUGET_CONFIG_PATH}}' --store-password-in-clear-text
- name: Restore NuGet packages
run: dotnet restore ${{env.PROJECT_PATH}} --configfile ${{env.NUGET_CONFIG_PATH}}
- name: Import Developer ID Installer Certificates
uses: Apple-Actions/import-codesign-certs@v7
with:
p12-file-base64: "${{secrets.APPLE_DEVELOPER_ID_INSTALLER_CERT_BASE64}}"
p12-password: "${{secrets.APPLE_DEVELOPER_ID_INSTALLER_CERT_PASSWORD}}"
keychain: installer_signing_temp
- name: Import Developer ID Application Certificates
uses: Apple-Actions/import-codesign-certs@v7
with:
p12-file-base64: "${{secrets.APPLE_DEVELOPER_ID_APPLICATION_CERT_BASE64}}"
p12-password: "${{secrets.APPLE_DEVELOPER_ID_APPLICATION_CERT_PASSWORD}}"
keychain: application_signing_temp
# Each import-codesign-certs call rewrites the keychain search list. The second import step (Application cert) drops the first (Installer) keychain, so product build cannot find the "Developer ID Installer" identity. This step re-adds both keychains explicitly before any signing occurs.
- name: Ensure both signing keychains are in the search list
shell: bash
run: |
set -euo pipefail
security list-keychains -d user -s "$HOME/Library/Keychains/installer_signing_temp.keychain-db" "$HOME/Library/Keychains/application_signing_temp.keychain-db" "$HOME/Library/Keychains/login.keychain-db"
echo "Keychain search list:"
security list-keychains -d user
# Finally make sure both certs are available before we build
- name: Verify Developer ID identities are available
shell: bash
run: |
set -euo pipefail
APPLE_DEV_ID_APP_CERT_NAME="${{env.APPLE_DEV_ID_APP_CERT_NAME}}"
APPLE_DEV_ID_INSTALLER_CERT_NAME="${{env.APPLE_DEV_ID_INSTALLER_CERT_NAME}}"
security find-identity -v -p codesigning
security find-certificate -a -c "$APPLE_DEV_ID_INSTALLER_CERT_NAME" || true
if ! security find-identity -v -p codesigning | grep -F "$APPLE_DEV_ID_APP_CERT_NAME" >/dev/null; then
echo "Missing Developer ID Application identity: $APPLE_DEV_ID_APP_CERT_NAME"
exit 1
fi
if ! security find-certificate -a -c "$APPLE_DEV_ID_INSTALLER_CERT_NAME" >/dev/null; then
echo "Missing Developer ID Installer certificate: $APPLE_DEV_ID_INSTALLER_CERT_NAME"
exit 1
fi
- name: Build and sign MacCatalyst artifacts
shell: bash
run: |
set -euo pipefail
cd "$GITHUB_WORKSPACE"
if [[ ! -f "$PROJECT_PATH" ]]; then
echo "Project file not found: $PROJECT_PATH"
echo "Current directory: $(pwd)"
exit 1
fi
mkdir -p "$ARTIFACTS_DIR"
echo "Cleaning project..."
dotnet clean "$PROJECT_PATH" -c "$CONFIGURATION" -f "$TFM"
publish_args=(
"$PROJECT_PATH"
-c "$CONFIGURATION"
-f "$TFM"
"-p:CodesignKey=\"$CODESIGN_KEY\""
"-p:ApplicationVersion=$APP_VERSION"
"-p:ApplicationDisplayVersion=$APP_VERSION"
-p:UseHardenedRuntime=true
)
echo "Publishing project..."
dotnet publish "${publish_args[@]}"
if [[ ! -d "$APP_BUNDLE_PATH" ]]; then
echo "Expected app bundle not found at $APP_BUNDLE_PATH"
exit 1
fi
echo "Verifying app signature..."
codesign -dv --verbose=2 "$APP_BUNDLE_PATH" >/dev/null 2>&1
echo "Creating signed app zip and installer pkg..."
rm -f "$SIGNED_ZIP_PATH" "$SIGNED_PKG_PATH"
ditto -c -k --sequesterRsrc --keepParent "$APP_BUNDLE_PATH" "$SIGNED_ZIP_PATH"
productbuild --component "$APP_BUNDLE_PATH" /Applications --sign "$INSTALLER_SIGN_ID" "$SIGNED_PKG_PATH"
echo "Done. Signed artifacts:"
echo "- $SIGNED_PKG_PATH"
echo "- $SIGNED_ZIP_PATH"
env:
PROJECT_PATH: "${{env.PROJECT_PATH}}"
TFM: "${{env.NET_TFM}}-maccatalyst"
CONFIGURATION: "Release"
APP_VERSION: "${{needs.shared-resources.outputs.app_version}}"
ARTIFACTS_DIR: "${{env.MAC_ARTIFACTS_PATH}}"
APP_BUNDLE_PATH: "${{env.MAC_APP_BUNDLE_PATH}}"
CODESIGN_KEY: "${{env.APPLE_DEV_ID_APP_CERT_NAME}}"
INSTALLER_SIGN_ID: "${{ env.APPLE_DEV_ID_INSTALLER_CERT_NAME}}"
SIGNED_ZIP_PATH: "$ARTIFACTS_DIR/${{env.MAC_PACKAGE_NAME}}-Release-signed.zip"
SIGNED_PKG_PATH: "$ARTIFACTS_DIR/${{env.MAC_PACKAGE_NAME}}-Release-signed.pkg"
- name: Notarize and staple MacCatalyst artifacts
shell: bash
run: |
set -euo pipefail
cd "$GITHUB_WORKSPACE"
if [[ -z "$APPLE_NOTARY_APPLE_ID" || -z "$APPLE_NOTARY_APP_PASSWORD" || -z "$APPLE_NOTARY_TEAM_ID" ]]; then
echo "Missing notarization credentials."
echo "Set APPLE_NOTARY_APPLE_ID, APPLE_NOTARY_APP_PASSWORD, and APPLE_NOTARY_TEAM_ID."
exit 1
fi
if [[ ! -d "$APP_BUNDLE_PATH" ]]; then
echo "Expected app bundle not found at $APP_BUNDLE_PATH"
exit 1
fi
if [[ ! -f "$SIGNED_PKG_PATH" || ! -f "$SIGNED_ZIP_PATH" ]]; then
echo "Expected signed artifacts not found in $ARTIFACTS_DIR"
exit 1
fi
echo "Submitting pkg for notarization..."
pkg_submit_json="$(xcrun notarytool submit "$SIGNED_PKG_PATH" --apple-id "$APPLE_NOTARY_APPLE_ID" --team-id "$APPLE_NOTARY_TEAM_ID" --password "$APPLE_NOTARY_APP_PASSWORD" --wait --output-format json)"
echo "$pkg_submit_json"
if ! grep -Eq '"status"[[:space:]]*:[[:space:]]*"Accepted"' <<< "$pkg_submit_json"; then
echo "Notarization failed for pkg."
exit 1
fi
echo "Stapling and validating pkg..."
xcrun stapler staple "$SIGNED_PKG_PATH"
xcrun stapler validate "$SIGNED_PKG_PATH"
echo "Submitting signed app zip for notarization..."
zip_submit_json="$(xcrun notarytool submit "$SIGNED_ZIP_PATH" --apple-id "$APPLE_NOTARY_APPLE_ID" --team-id "$APPLE_NOTARY_TEAM_ID" --password "$APPLE_NOTARY_APP_PASSWORD" --wait --output-format json)"
echo "$zip_submit_json"
if ! grep -Eq '"status"[[:space:]]*:[[:space:]]*"Accepted"' <<< "$zip_submit_json"; then
echo "Notarization failed for app zip."
exit 1
fi
echo "Stapling and validating app..."
xcrun stapler staple "$APP_BUNDLE_PATH"
xcrun stapler validate "$APP_BUNDLE_PATH"
# Recreate the final distributable zip from the stapled app.
ditto -c -k --sequesterRsrc --keepParent "$APP_BUNDLE_PATH" "$NOTARIZED_ZIP_PATH"
echo "Gatekeeper checks..."
spctl -a -t exec -vv "$APP_BUNDLE_PATH"
spctl -a -t install -vv "$SIGNED_PKG_PATH"
echo "Done. Artifacts:"
echo "- $SIGNED_PKG_PATH"
echo "- $NOTARIZED_ZIP_PATH"
env:
ARTIFACTS_DIR: "${{env.MAC_ARTIFACTS_PATH}}"
APP_BUNDLE_PATH: "${{env.MAC_APP_BUNDLE_PATH}}"
APPLE_NOTARY_APPLE_ID: "${{secrets.APPLE_NOTARY_APPLE_ID}}"
APPLE_NOTARY_APP_PASSWORD: "${{secrets.APPLE_NOTARY_APP_PASSWORD}}"
APPLE_NOTARY_TEAM_ID: "${{env.APPLE_NOTARY_TEAM_ID}}"
SIGNED_ZIP_PATH: "${{env.MAC_ARTIFACTS_PATH}}/${{env.MAC_PACKAGE_NAME}}-Release-signed.zip"
NOTARIZED_ZIP_PATH: "${{env.MAC_ARTIFACTS_PATH}}/${{env.MAC_PACKAGE_NAME}}-Release-notarized.zip"
SIGNED_PKG_PATH: "${{env.MAC_ARTIFACTS_PATH}}/${{env.MAC_PACKAGE_NAME}}-Release-signed.pkg"
- name: Publish MacCatalyst build artifacts
uses: actions/upload-artifact@v7
with:
name: "${{env.APP_NAME}}_v${{needs.shared-resources.outputs.app_version}}_signed-maccatalyst"
path: ${{env.MAC_ARTIFACTS_PATH}}/${{env.MAC_PACKAGE_NAME}}-Release-signed.pkg
if-no-files-found: error
retention-days: 30
# ********************************************************************************** #
# iOS #
# ********************************************************************************** #
ios:
name: Build iOS (${{matrix.distribution_name}})
runs-on: macos-26 # https://github.com/actions/runner-images#available-images
needs: shared-resources
if: ${{ success() && needs.shared-resources.outputs.app_version != '' }}
strategy:
fail-fast: false
matrix:
include:
- distribution_name: Store Upload IPA
artifact_suffix: storeupload-ios
provisioning_profile: MauiDemo_AppStore_Distribution
provisioning_profile_type: IOS_APP_STORE
rid: ios-arm64
- distribution_name: Ad Hoc Sideload IPA
artifact_suffix: sideload-ios
provisioning_profile: MauiDemo_AdHoc_Distribution
provisioning_profile_type: IOS_APP_ADHOC
rid: ios-arm64
env:
APPLE_PROV_PROFILE: ${{matrix.provisioning_profile}}
APPLE_PROV_PROFILE_TYPE: ${{matrix.provisioning_profile_type}}
steps:
- uses: actions/checkout@v6
- uses: maxim-lobanov/setup-xcode@v1
with:
xcode-version: ${{env.XCODE_VERSION}}
- name: Setup .NET SDK
uses: actions/setup-dotnet@v5
with:
dotnet-version: ${{env.SDK_VERSION}}
- name: Install MAUI Workloads
run: dotnet workload install maui --source https://api.nuget.org/v3/index.json
- name: Set Telerik NuGet Credentials
run: dotnet nuget update source 'Telerik_v3_Feed' -s 'https://nuget.telerik.com/v3/index.json' -u 'api-key' -p "${{secrets.TELERIK_NUGET_KEY}}" --configfile '${{env.NUGET_CONFIG_PATH}}' --store-password-in-clear-text
- name: Restore NuGet packages
run: dotnet restore ${{env.PROJECT_PATH}} --configfile ${{env.NUGET_CONFIG_PATH}}
# Docs https://github.com/Apple-Actions/import-codesign-certs
- name: Import Code-Signing Certificates
uses: Apple-Actions/import-codesign-certs@v7
with:
p12-file-base64: "${{secrets.APPLE_DISTRIBUTION_CERT_BASE64}}"
p12-password: "${{secrets.APPLE_DISTRIBUTION_CERT_PASSWORD}}"
# Docs https://github.com/Apple-Actions/download-provisioning-profiles
- id: provisioning-profiles
uses: Apple-Actions/download-provisioning-profiles@v6
with:
profile-type: "${{env.APPLE_PROV_PROFILE_TYPE}}"
bundle-id: "${{env.APPLE_APP_ID}}"
issuer-id: "${{secrets.APPSTORE_API_ISSUER_ID}}"
api-key-id: "${{secrets.APPSTORE_API_KEY_ID}}"
api-private-key: "${{secrets.APPSTORE_API_PRIVATE_KEY}}"
- name: Verify provisioning profile
run: |
$profiles = '${{steps.provisioning-profiles.outputs.profiles}}' | ConvertFrom-Json
$profile = $profiles | Where-Object { $_.name -eq $env:APPLE_PROV_PROFILE -and $_.type -eq $env:APPLE_PROV_PROFILE_TYPE } | Select-Object -First 1
if ($null -eq $profile) {
$profiles | Format-Table -AutoSize | Out-String | Write-Host
throw "Provisioning profile '$env:APPLE_PROV_PROFILE' with type '$env:APPLE_PROV_PROFILE_TYPE' was not downloaded."
}
- name: Verify iOS signing identity is available
shell: bash
run: |
set -euo pipefail
security find-identity -v -p codesigning
if ! security find-identity -v -p codesigning | grep -F "Apple Distribution" >/dev/null; then
echo "Missing Apple Distribution identity"
exit 1
fi
# Docs https://learn.microsoft.com/en-us/dotnet/maui/ios/deployment/publish-cli?view=net-maui-9.0
- name: Publish MAUI iOS IPA
run: |
# Query the actual signing identity to avoid comma parsing issues
$identityOutput = security find-identity -v -p codesigning | Select-String "Apple Distribution"
if (-not $identityOutput) { throw "No Apple Distribution identity found in keychain" }
$codesignKey = [regex]::Match($identityOutput.Line, '"(?<name>Apple Distribution:[^"]+)"').Groups['name'].Value
if (-not $codesignKey) { throw "Could not parse Apple Distribution identity from keychain output" }
Write-Host "Using codesign key: $codesignKey"
$quotedCodesignKey = '"' + $codesignKey + '"'
$publishArgs = @(
'publish'
'${{env.PROJECT_PATH}}'
'-f', '${{env.NET_TFM}}-ios'
'-c', 'Release'
'-p:ArchiveOnBuild=true'
'-p:RuntimeIdentifier=${{matrix.rid}}'
'-p:MtouchLink=SdkOnly'
'-p:ApplicationId=${{env.APPLE_APP_ID}}'
'-p:ApplicationVersion=${{needs.shared-resources.outputs.app_version}}'
'-p:CodesignProvision=${{env.APPLE_PROV_PROFILE}}'
"-p:CodesignKey=$quotedCodesignKey"
)
& dotnet @publishArgs
if ($LASTEXITCODE -ne 0) { throw "dotnet publish failed with exit code $LASTEXITCODE" }
- name: Publish iOS build artifacts
uses: actions/upload-artifact@v7
with:
name: "${{env.APP_NAME}}_v${{needs.shared-resources.outputs.app_version}}_${{matrix.artifact_suffix}}"
path: "${{env.PROJECT_DIRECTORY}}/bin/Release/${{env.NET_TFM}}-ios/${{matrix.rid}}/publish/*.ipa"
if-no-files-found: error
retention-days: 60
# ********************************************************************************** #
# GitHub Release #
# ********************************************************************************** #
# create-release:
# name: Create GitHub Release
# runs-on: ubuntu-latest
# needs: [shared-resources, android, windows-sideload-packages, windows-generate-msixbundle, windows-store, maccatalyst, ios]
# steps:
# - name: Download all artifacts
# uses: actions/download-artifact@v8
# with:
# path: release-artifacts
# - name: List downloaded artifacts
# shell: bash
# run: find release-artifacts -type f | sort
# - name: Prepare release files
# shell: bash
# run: |
# set -euo pipefail
# VER="${{needs.shared-resources.outputs.app_version}}"
# PREFIX="${{env.APP_NAME}}_v${VER}"
# mkdir -p release-upload
# copy_one() {
# local artifact_dir="$1"
# local pattern="$2"
# local destination="$3"
# local -a matches=()
# if [[ ! -d "$artifact_dir" ]]; then
# echo "Expected artifact directory not found: $artifact_dir" >&2
# exit 1
# fi
# mapfile -d '' matches < <(find "$artifact_dir" -type f -name "$pattern" -print0 | sort -z)
# if [[ ${#matches[@]} -ne 1 ]]; then
# echo "Expected exactly one match for '$pattern' under '$artifact_dir', found ${#matches[@]}." >&2
# find "$artifact_dir" -type f | sort >&2
# exit 1
# fi
# cp -- "${matches[0]}" "$destination"
# }
# # Android
# copy_one "release-artifacts/${PREFIX}_signed-android" "*-Signed.apk" "release-upload/${PREFIX}_android-signed.apk"
# copy_one "release-artifacts/${PREFIX}_signed-android" "*-Signed.aab" "release-upload/${PREFIX}_android-signed.aab"
# # Windows msixbundle (signed)
# copy_one "release-artifacts/${PREFIX}_signed-windows.msixbundle" "*.msixbundle" "release-upload/${PREFIX}_windows.msixbundle"
# # Windows Store
# copy_one "release-artifacts/${PREFIX}_storeupload-windows" "*.msixupload" "release-upload/${PREFIX}_msstore.msixupload"
# # MacCatalyst
# copy_one "release-artifacts/${PREFIX}_signed-maccatalyst" "*.pkg" "release-upload/${PREFIX}_mac.pkg"
# # iOS
# copy_one "release-artifacts/${PREFIX}_ios-adhoc" "*.ipa" "release-upload/${PREFIX}_ios-adhoc.ipa"
# copy_one "release-artifacts/${PREFIX}_ios-store" "*.ipa" "release-upload/${PREFIX}_ios-store.ipa"
# echo "Files prepared for release:"
# ls -lh release-upload/
# - name: Create GitHub Release
# uses: softprops/action-gh-release@v3.0.0
# with:
# tag_name: "v${{needs.shared-resources.outputs.app_version}}"
# name: "${{env.APP_NAME}} v${{needs.shared-resources.outputs.app_version}}"
# draft: false
# prerelease: false
# generate_release_notes: true
# files: release-upload/*
+49
View File
@@ -0,0 +1,49 @@
# This example shows you how you can use the named environment variables in the nuget.config file to set the credentials, for more information see https://github.com/LanceMcCarthy/DevOpsExamples#github-actions-using-secrets-to-set-environment-variables
name: WinForms (.NET Framework)
on:
workflow_dispatch:
push:
branches:
- main
- "winforms/*"
paths:
- 'src/WinForms/**/*'
- '.github/workflows/main_build-winforms.yml'
env:
TELERIK_USERNAME: "api-key" # Variable name used in the nuget.config file
TELERIK_PASSWORD: ${{secrets.TELERIK_NUGET_KEY}} # Variable name used in the nuget.config file
TELERIK_LICENSE: ${{secrets.TELERIK_LICENSE_KEY}} # Used when compiling the project
CSPROJ_PATH: "src/WinForms/MyWinFormsApp/MyWinFormsApp.csproj"
NUGETCONFIG_PATH: "src/NuGet.Config"
jobs:
# A job that builds a .NET Framework WPF application using Telerik UI for WinForms
build_desktop:
runs-on: windows-latest # WinForms apps must be built on Windows runners
strategy:
matrix:
configuration: [Release]
platform: [x86, x64]
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Setup .NET SDK
uses: actions/setup-dotnet@v4
with:
dotnet-version: '10.0.x'
- name: Setup MSBuild.exe
uses: microsoft/setup-msbuild@v2
# We use the dotnet CLI (instead of nuget CLI) to restore the nuget packages before using msbuild
- name: NuGet Restore
run: dotnet restore ${{env.CSPROJ_PATH}} --configfile ${{env.NUGETCONFIG_PATH}} --runtime win-${{matrix.platform}}
# Use msbuild to compile the .NET Framework WinForms project
- name: Build the WinForms application
run: msbuild ${{env.CSPROJ_PATH}} /t:Restore /p:Configuration=${{matrix.configuration}} /p:RuntimeIdentifier=win-${{matrix.platform}}
+57
View File
@@ -0,0 +1,57 @@
name: WinUI3
on:
workflow_dispatch:
push:
branches:
- main
- "winui/*"
paths:
- 'src/WinUI/**/*'
- '.github/workflows/main_build-winui.yml'
env:
TELERIK_USERNAME: "api-key" # Used by the nuget.config file
TELERIK_PASSWORD: ${{secrets.TELERIK_NUGET_KEY}} # Used by the nuget.config file
SOLUTION_NAME: "src/WinUI/MyDemo.sln"
NUGETCONFIG_PATH: "src/NuGet.Config"
jobs:
build-windows:
runs-on: windows-2022
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: '10.0.x'
- name: Add msbuild to PATH
uses: microsoft/setup-msbuild@v2
- name: Restore NuGet packages
shell: pwsh
run: dotnet restore ${{env.SOLUTION_NAME}} --configfile ${{env.NUGETCONFIG_PATH}}
# Restore the application to populate the obj folder with RuntimeIdentifiers
- name: Restore RIDs
run: msbuild ${{env.SOLUTION_NAME}} /t:Restore /p:Configuration=Release
- name: Build and Create MSIX
run: |
msbuild ${{env.SOLUTION_NAME}} `
/p:Configuration=Release `
/p:Platform=x64 `
/p:AppxBundlePlatforms="x64|arm64" `
/p:UapAppxPackageBuildMode=CI `
/p:AppxBundle=Never `
/p:AppxPackageDir="${{github.workspace}}/AppPackages" `
/p:GenerateAppxPackageOnBuild=true `
/p:AppxPackageSigningEnabled=false `
/p:SelfContained=true
env:
TELERIK_LICENSE: ${{secrets.TELERIK_LICENSE_KEY}}
+48
View File
@@ -0,0 +1,48 @@
# This example shows you how you can use the named environment variables in the nuget.config file to set the credentials
name: WPF (.NET Framework)
on:
workflow_dispatch:
push:
branches:
- main
- "wpf/*"
paths:
- 'src/Wpf/**/*'
- '.github/workflows/main_build-wpf.yml'
env:
TELERIK_USERNAME: "api-key" # Variable name used in the nuget.config file
TELERIK_PASSWORD: ${{secrets.TELERIK_NUGET_KEY}} # Variable name used in the nuget.config file
TELERIK_LICENSE: ${{secrets.TELERIK_LICENSE_KEY}} # Used when compiling the project
CSPROJ_PATH: "src/Wpf/MyWpfApp/MyWpfApp.csproj"
NUGETCONFIG_PATH: "src/NuGet.Config"
jobs:
build_desktop:
runs-on: windows-latest
strategy:
matrix:
configuration: [Release]
platform: [x86, x64]
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Install .NET SDK
uses: actions/setup-dotnet@v4
with:
dotnet-version: '10.0.x'
- name: Setup MSBuild.exe
uses: microsoft/setup-msbuild@v2
# We use the dotnet CLI (instead of nuget.exe) to restore the nuget packages before using msbuild
- name: NuGet Restore
run: dotnet restore ${{env.CSPROJ_PATH}} --configfile ${{env.NUGETCONFIG_PATH}} --runtime win-${{matrix.platform}}
# Use msbuild to compile the .NET Framework WPF project
- name: Build the WPF application
run: msbuild ${{env.CSPROJ_PATH}} /t:Restore /p:Configuration=${{matrix.configuration}} /p:RuntimeIdentifier=${{matrix.platform}}
+173
View File
@@ -0,0 +1,173 @@
name: ASP.NET Core (with Reporting) - Docker
on:
workflow_dispatch:
push:
branches:
- main
paths:
- 'src/AspNetCore/**/*'
env:
WORKING_DIRECTORY: "src/AspNetCore/MyAspNetCoreApp"
TARGET_PLATFORMS: "linux/amd64,linux/arm64"
jobs:
#################### Example 1 ##########################
# Dockerfile build and publish to Docker Hub
# - Uses mcr.microsoft.com/dotnet/aspnet base image
# - Webook to automatically redeploy stack in Portainer
#########################################################
dockerhub_msftbase_build:
name: Microsoft Base - Publish to Docker Hub
runs-on: ubuntu-latest
env:
CONTAINER_REPOSITORY: "lancemccarthy/aspnetcore-reporting-from-msftbase"
DOCKERFILE_PATH: "src/AspNetCore/MyAspNetCoreApp/Dockerfile_MSRuntimeBase"
CONTAINER_REGISTRY: "docker.io"
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Set up QEMU for multi-arch support
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Get package metadata from Docker Hub
id: meta
uses: docker/metadata-action@v5
with:
images: ${{env.CONTAINER_REGISTRY}}/${{env.CONTAINER_REPOSITORY}}
- name: Login to DockerHub
uses: docker/login-action@v3
with:
username: ${{secrets.DOCKER_HUB_USERNAME}}
password: ${{secrets.DOCKER_HUB_PAT}}
- name: Build and push to Docker Hub
uses: docker/build-push-action@v5
with:
file: ${{env.DOCKERFILE_PATH}}
context: ${{env.WORKING_DIRECTORY}}
platforms: ${{env.TARGET_PLATFORMS}}
push: true
secrets: |
telerik-nuget-key=${{secrets.TELERIK_NUGET_KEY}}
telerik-license-key=${{secrets.TELERIK_LICENSE_KEY}}
tags: ${{steps.meta.outputs.tags}}
- name: Trigger Portainer to pull images and redeploy the stack
run: curl -X POST ${{secrets.PORTAINER_WEBHOOK_ASPNETCORE}}
#################### Example 2 ##########################
# Dockerfile build and publish to Docker Hub
# - Uses a CentOS Base Image
# - Webook to automatically redeploy stack in Portainer
#########################################################
dockerhub_centosbase_build:
name: CentOS Base - Publish to Docker Hub
runs-on: ubuntu-latest
env:
CONTAINER_REPOSITORY: "lancemccarthy/aspnetcore-reporting-from-centosbase"
DOCKERFILE_PATH: "src/AspNetCore/MyAspNetCoreApp/Dockerfile_CentOS"
CONTAINER_REGISTRY: "docker.io"
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Set up QEMU for multi-arch support
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Get package metadata from Docker Hub
id: meta
uses: docker/metadata-action@v5
with:
images: ${{env.CONTAINER_REGISTRY}}/${{env.CONTAINER_REPOSITORY}}
- name: Login to DockerHub
uses: docker/login-action@v3
with:
username: ${{secrets.DOCKER_HUB_USERNAME}}
password: ${{secrets.DOCKER_HUB_PAT}}
- name: Build and push to Docker Hub
uses: docker/build-push-action@v5
with:
file: ${{env.DOCKERFILE_PATH}}
context: ${{env.WORKING_DIRECTORY}}
platforms: ${{env.TARGET_PLATFORMS}}
push: true
secrets: |
telerik-nuget-key=${{secrets.TELERIK_NUGET_KEY}}
telerik-license-key=${{secrets.TELERIK_LICENSE_KEY}}
tags: ${{steps.meta.outputs.tags}}
# ############################################################################
# ghcr.io option
# ############################################################################
# Set worklow permissions for publishing to GitHub Container Registry
# permissions:
# contents: read
# packages: write # to publish to GitHub container registry
# ghcr_msftbase_build:
# name: Microsoft Base - Publish to GitHub Container Registry
# runs-on: ubuntu-latest
# env:
# CONTAINER_REGISTRY: "ghcr.io"
# CONTAINER_REPOSITORY: "lancemccarthy/reporting-msft-base"
# DOCKERFILE_PATH: "src/AspNetCore/MyAspNetCoreApp/Dockerfile_MSRuntimeBase"
# steps:
# - name: Checkout
# uses: actions/checkout@v6
# with:
# fetch-depth: 0
# - name: Set up QEMU
# uses: docker/setup-qemu-action@v3
# - name: Set up Docker Buildx
# uses: docker/setup-buildx-action@v3
# - name: Get package metadata from Docker Hub
# id: meta
# uses: docker/metadata-action@v5
# with:
# images: ${{env.CONTAINER_REGISTRY}}/${{env.CONTAINER_REPOSITORY}}
# - name: Login to DockerHub
# uses: docker/login-action@v3
# with:
# registry: ${{env.CONTAINER_REGISTRY}}
# username: ${{secrets.DOCKER_HUB_USERNAME}}
# password: ${{secrets.DOCKER_HUB_PAT}}
# - name: Build and push to Docker Hub
# uses: docker/build-push-action@v5
# with:
# file: ${{env.DOCKERFILE_PATH}}
# context: ${{env.WORKING_DIRECTORY}}
# platforms: ${{env.TARGET_PLATFORMS}}
# push: true
# secrets: |
# telerik-nuget-key=${{secrets.TELERIK_NUGET_KEY}}
# telerik-license-key=${{secrets.TELERIK_LICENSE_KEY}}
# tags: ${{steps.meta.outputs.tags}}
# - name: Delete old Docker images
# uses: actions/delete-package-versions@v5
# with:
# package-name: "myaspnetcoreapp"
# package-type: container
# min-versions-to-keep: 3
# token: ${{secrets.GITHUB_TOKEN}}
+213
View File
@@ -0,0 +1,213 @@
name: Blazor (with Reporting) - Docker
on:
workflow_dispatch:
push:
branches:
- main
paths:
- 'src/Blazor/**/*'
env:
DOTNET_VERSION: "10.0.x"
BLAZOR_PROJ_PATH: src/Blazor/MyBlazorApp/MyBlazorApp.csproj
NUGET_CONFIG_PATH: src/NuGet.Config
CONTAINER_REPOSITORY: "lancemccarthy/myblazorapp"
# For ghcr.io access
permissions:
contents: read
packages: write
jobs:
#################### Example 1 ##########################
# Dockerfile build and publish to Docker Hub
# - Webook to automatically redeploy stack in Portainer
#########################################################
dockerfile_to_dockerhub:
name: "Dockerfile Build and Publish"
runs-on: ubuntu-latest
env:
CONTAINER_REGISTRY: "docker.io"
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Generate tag version
id: tag-creator
run: |
buildDay=`date +%Y.%m.%d`
tags="$buildDay.$GITHUB_RUN_NUMBER"
echo "VERSION_TAG=$tags" >> $GITHUB_OUTPUT
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Get package metadata from Docker Hub
id: meta
uses: docker/metadata-action@v5
with:
images: ${{env.CONTAINER_REGISTRY}}/${{env.CONTAINER_REPOSITORY}}
- name: Login to DockerHub
uses: docker/login-action@v3
with:
username: ${{secrets.DOCKER_HUB_USERNAME}}
password: ${{secrets.DOCKER_HUB_PAT}}
- name: Build and Publish arm64, amd64 Container Images
uses: docker/build-push-action@v5
with:
context: src/Blazor/MyBlazorApp
platforms: linux/arm64,linux/amd64
push: true
secrets: |
telerik-nuget-key=${{secrets.TELERIK_NUGET_KEY}}
telerik-license-key=${{secrets.TELERIK_LICENSE_KEY}}
tags: ${{steps.meta.outputs.tags}}
# Required because actions/delete-package-versions@v5 does not work with Docker Hub
- name: Delete old (untagged) images
run: |
IMAGE_TAGS=$(curl -s "https://hub.docker.com/v2/repositories/${{env.CONTAINER_REPOSITORY}}/tags/?page_size=100" | jq -r '.results|.[]|.name')
for TAGG in $IMAGE_TAGS; do
if [[ "$TAGG" == "null" ]]; then
docker rmi "${{env.CONTAINER_REPOSITORY}}:$TAGG"
curl -s -X DELETE "https://hub.docker.com/v2/repositories/${{env.CONTAINER_REPOSITORY}}/tags/$TAGG/"
fi
done
- name: Trigger Portainer to pull images and redeploy the stack
run: curl -X POST ${{secrets.PORTAINER_WEBHOOK_BLAZOR}}
#################### Example 2 ##########################
# .NET SDK build and publish to ghcr.io
############################################################
# # 2.1 - Build the lancemccarthy/myblazorapp:latest-x64 image
# build_x64:
# runs-on: ubuntu-latest
# name: "[NET SDK] Create x64 image"
# outputs:
# build_tag: ${{steps.build.outputs.build_tag}}
# env:
# target_arch: "x64"
# CONTAINER_REGISTRY: "ghcr.io"
# CONTAINER_BASE_IMAGE: "docker.io/lancemccarthy/skia-aspnet:10.0"
# steps:
# - name: Checkout
# uses: actions/checkout@v6
# with:
# fetch-depth: 0
# - name: Setup .NET Core SDK
# uses: actions/setup-dotnet@v4
# with:
# dotnet-version: ${{env.DOTNET_VERSION}}
# # Needed because dotnet publish -t:PublishContainer uses the CLI (not docker/login-action@v3)
# - name: Login to ghcr.io
# run: docker login ${{env.CONTAINER_REGISTRY}} -u ${{github.actor}} -p ${{secrets.GITHUB_TOKEN}}
# - name: Restore NuGet Packages
# run: dotnet restore ${{env.BLAZOR_PROJ_PATH}} -r "linux-${{env.target_arch}}"
# env:
# TELERIK_USERNAME: "api-key"
# TELERIK_PASSWORD: ${{secrets.TELERIK_NUGET_KEY}}
# - name: build the x64 image
# id: build
# run: |
# TAG="latest-${{env.target_arch}}"
# dotnet publish ${{env.BLAZOR_PROJ_PATH}} \
# -t:PublishContainer \
# -p PublishProfile=DefaultContainer \
# -p ContainerBaseImage=${{env.CONTAINER_BASE_IMAGE}} \
# -p ContainerRegistry="${{env.CONTAINER_REGISTRY}}" \
# -p ContainerRepository="${{env.CONTAINER_REPOSITORY}}" \
# -p ContainerImageTag="$TAG" \
# --arch ${{env.target_arch}} \
# --no-restore
# echo "build_tag=$TAG" >> $GITHUB_OUTPUT
# env:
# TELERIK_LICENSE: ${{secrets.TELERIK_LICENSE_KEY}}
# # Builds the lancemccarthy/myblazorapp:latest-arm64 image
# build_arm64:
# name: "[NET SDK] Create arm64 image"
# runs-on: ubuntu-latest
# outputs:
# build_tag: ${{steps.build.outputs.build_tag}}
# env:
# target_arch: "arm64"
# CONTAINER_REGISTRY: "ghcr.io"
# CONTAINER_BASE_IMAGE: "docker.io/lancemccarthy/skia-aspnet:10.0"
# steps:
# - name: Checkout
# uses: actions/checkout@v4
# with:
# fetch-depth: 0
# - name: Setup .NET Core SDK
# uses: actions/setup-dotnet@v4
# with:
# dotnet-version: ${{env.DOTNET_VERSION}}
# # Needed because dotnet publish -t:PublishContainer uses the CLI (not docker/login-action@v3)
# - name: Login to ghcr.io
# run: docker login ${{env.CONTAINER_REGISTRY}} -u ${{github.actor}} -p ${{secrets.GITHUB_TOKEN}}
# - name: Restore NuGet Packages
# run: dotnet restore ${{env.BLAZOR_PROJ_PATH}} -r "linux-${{env.target_arch}}"
# env:
# TELERIK_USERNAME: "api-key"
# TELERIK_PASSWORD: ${{secrets.TELERIK_NUGET_KEY}}
# - name: build the arm64 image
# id: build
# run: |
# TAG="latest-${{env.target_arch}}"
# dotnet publish ${{env.BLAZOR_PROJ_PATH}} \
# -t:PublishContainer \
# -p PublishProfile=DefaultContainer \
# -p ContainerBaseImage=${{env.CONTAINER_BASE_IMAGE}} \
# -p ContainerRegistry="${{env.CONTAINER_REGISTRY}}" \
# -p ContainerRepository="${{env.CONTAINER_REPOSITORY}}" \
# -p ContainerImageTag="$TAG" \
# --arch ${{env.target_arch}} \
# --no-restore
# echo "build_tag=$TAG" >> $GITHUB_OUTPUT
# env:
# TELERIK_LICENSE: ${{secrets.TELERIK_LICENSE_KEY}}
# # Create a manifest to combine both images into a single "lancemccarthy/myblazorapp:latest" tag
# publish_combined_manifest:
# runs-on: ubuntu-latest
# name: "[NET SDK] Publish multi-arch manifest"
# needs: [build_x64, build_arm64]
# env:
# CONTAINER_REGISTRY: "ghcr.io"
# steps:
# - name: Checkout
# uses: actions/checkout@v4
# - name: Login to ghcr.io
# run: docker login ${{env.CONTAINER_REGISTRY}} -u ${{github.actor}} -p ${{secrets.GITHUB_TOKEN}}
# - name: Create multi-arch manifest
# run: docker manifest create "${{env.CONTAINER_REPOSITORY}}:latest" "${{env.CONTAINER_REPOSITORY}}:${{needs.build_x64.outputs.build_tag}}" "${{env.CONTAINER_REPOSITORY}}:${{needs.build_arm64.outputs.build_tag}}"
# - name: Push multi-arch manifest
# run: docker manifest push "${{env.CONTAINER_REPOSITORY}}:latest"
# - name: Delete old images
# uses: actions/delete-package-versions@v5
# with:
# package-name: "myblazorapp"
# package-type: container
# min-versions-to-keep: 2
# token: ${{secrets.GITHUB_TOKEN}}
+359 -402
View File
@@ -1,402 +1,359 @@
# ---> VisualStudio
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
##
## Get latest from https://github.com/github/gitignore/blob/main/VisualStudio.gitignore
# User-specific files
*.rsuser
*.suo
*.user
*.userosscache
*.sln.docstates
# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs
# Mono auto generated files
mono_crash.*
# Build results
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
x64/
x86/
[Ww][Ii][Nn]32/
[Aa][Rr][Mm]/
[Aa][Rr][Mm]64/
bld/
[Bb]in/
[Oo]bj/
[Ll]og/
[Ll]ogs/
# Visual Studio 2015/2017 cache/options directory
.vs/
# Uncomment if you have tasks that create the project's static files in wwwroot
#wwwroot/
# Visual Studio 2017 auto generated files
Generated\ Files/
# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
# NUnit
*.VisualState.xml
TestResult.xml
nunit-*.xml
# Build Results of an ATL Project
[Dd]ebugPS/
[Rr]eleasePS/
dlldata.c
# Benchmark Results
BenchmarkDotNet.Artifacts/
# .NET Core
project.lock.json
project.fragment.lock.json
artifacts/
# ASP.NET Scaffolding
ScaffoldingReadMe.txt
# StyleCop
StyleCopReport.xml
# Files built by Visual Studio
*_i.c
*_p.c
*_h.h
*.ilk
*.meta
*.obj
*.iobj
*.pch
*.pdb
*.ipdb
*.pgc
*.pgd
*.rsp
# but not Directory.Build.rsp, as it configures directory-level build defaults
!Directory.Build.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*_wpftmp.csproj
*.log
*.tlog
*.vspscc
*.vssscc
.builds
*.pidb
*.svclog
*.scc
# Chutzpah Test files
_Chutzpah*
# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opendb
*.opensdf
*.sdf
*.cachefile
*.VC.db
*.VC.VC.opendb
# Visual Studio profiler
*.psess
*.vsp
*.vspx
*.sap
# Visual Studio Trace Files
*.e2e
# TFS 2012 Local Workspace
$tf/
# Guidance Automation Toolkit
*.gpState
# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
*.DotSettings.user
# TeamCity is a build add-in
_TeamCity*
# DotCover is a Code Coverage Tool
*.dotCover
# AxoCover is a Code Coverage Tool
.axoCover/*
!.axoCover/settings.json
# Coverlet is a free, cross platform Code Coverage Tool
coverage*.json
coverage*.xml
coverage*.info
# Visual Studio code coverage results
*.coverage
*.coveragexml
# NCrunch
_NCrunch_*
.*crunch*.local.xml
nCrunchTemp_*
# MightyMoose
*.mm.*
AutoTest.Net/
# Web workbench (sass)
.sass-cache/
# Installshield output folder
[Ee]xpress/
# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html
# Click-Once directory
publish/
# Publish Web Output
*.[Pp]ublish.xml
*.azurePubxml
# Note: Comment the next line if you want to checkin your web deploy settings,
# but database connection strings (with potential passwords) will be unencrypted
*.pubxml
*.publishproj
# Microsoft Azure Web App publish settings. Comment the next line if you want to
# checkin your Azure Web App publish settings, but sensitive information contained
# in these scripts will be unencrypted
PublishScripts/
# NuGet Packages
*.nupkg
# NuGet Symbol Packages
*.snupkg
# The packages folder can be ignored because of Package Restore
**/[Pp]ackages/*
# except build/, which is used as an MSBuild target.
!**/[Pp]ackages/build/
# Uncomment if necessary however generally it will be regenerated when needed
#!**/[Pp]ackages/repositories.config
# NuGet v3's project.json files produces more ignorable files
*.nuget.props
*.nuget.targets
# Microsoft Azure Build Output
csx/
*.build.csdef
# Microsoft Azure Emulator
ecf/
rcf/
# Windows Store app package directories and files
AppPackages/
BundleArtifacts/
Package.StoreAssociation.xml
_pkginfo.txt
*.appx
*.appxbundle
*.appxupload
# Visual Studio cache files
# files ending in .cache can be ignored
*.[Cc]ache
# but keep track of directories ending in .cache
!?*.[Cc]ache/
# Others
ClientBin/
~$*
*~
*.dbmdl
*.dbproj.schemaview
*.jfm
*.pfx
*.publishsettings
orleans.codegen.cs
# Including strong name files can present a security risk
# (https://github.com/github/gitignore/pull/2483#issue-259490424)
#*.snk
# Since there are multiple workflows, uncomment next line to ignore bower_components
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
#bower_components/
# RIA/Silverlight projects
Generated_Code/
# Backup & report files from converting an old project file
# to a newer Visual Studio version. Backup files are not needed,
# because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm
ServiceFabricBackup/
*.rptproj.bak
# SQL Server files
*.mdf
*.ldf
*.ndf
# Business Intelligence projects
*.rdl.data
*.bim.layout
*.bim_*.settings
*.rptproj.rsuser
*- [Bb]ackup.rdl
*- [Bb]ackup ([0-9]).rdl
*- [Bb]ackup ([0-9][0-9]).rdl
# Microsoft Fakes
FakesAssemblies/
# GhostDoc plugin setting file
*.GhostDoc.xml
# Node.js Tools for Visual Studio
.ntvs_analysis.dat
node_modules/
# Visual Studio 6 build log
*.plg
# Visual Studio 6 workspace options file
*.opt
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
*.vbw
# Visual Studio 6 auto-generated project file (contains which files were open etc.)
*.vbp
# Visual Studio 6 workspace and project file (working project files containing files to include in project)
*.dsw
*.dsp
# Visual Studio 6 technical files
*.ncb
*.aps
# Visual Studio LightSwitch build output
**/*.HTMLClient/GeneratedArtifacts
**/*.DesktopClient/GeneratedArtifacts
**/*.DesktopClient/ModelManifest.xml
**/*.Server/GeneratedArtifacts
**/*.Server/ModelManifest.xml
_Pvt_Extensions
# Paket dependency manager
.paket/paket.exe
paket-files/
# FAKE - F# Make
.fake/
# CodeRush personal settings
.cr/personal
# Python Tools for Visual Studio (PTVS)
__pycache__/
*.pyc
# Cake - Uncomment if you are using it
# tools/**
# !tools/packages.config
# Tabs Studio
*.tss
# Telerik's JustMock configuration file
*.jmconfig
# BizTalk build output
*.btp.cs
*.btm.cs
*.odx.cs
*.xsd.cs
# OpenCover UI analysis results
OpenCover/
# Azure Stream Analytics local run output
ASALocalRun/
# MSBuild Binary and Structured Log
*.binlog
# NVidia Nsight GPU debugger configuration file
*.nvuser
# MFractors (Xamarin productivity tool) working folder
.mfractor/
# Local History for Visual Studio
.localhistory/
# Visual Studio History (VSHistory) files
.vshistory/
# BeatPulse healthcheck temp database
healthchecksdb
# Backup folder for Package Reference Convert tool in Visual Studio 2017
MigrationBackup/
# Ionide (cross platform F# VS Code tools) working folder
.ionide/
# Fody - auto-generated XML schema
FodyWeavers.xsd
# VS Code files for those working on multiple tools
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
*.code-workspace
# Local History for Visual Studio Code
.history/
# Windows Installer files from build outputs
*.cab
*.msi
*.msix
*.msm
*.msp
# JetBrains Rider
*.sln.iml
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
##
## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
# User-specific files
*.rsuser
*.suo
*.user
*.userosscache
*.sln.docstates
# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs
# Angular cache
.angular/
# Mono auto generated files
mono_crash.*
# Build results
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
x64/
x86/
[Aa][Rr][Mm]/
[Aa][Rr][Mm]64/
bld/
[Bb]in/
[Oo]bj/
[Ll]og/
[Ll]ogs/
# Visual Studio 2015/2017 cache/options directory
.vs/
# Uncomment if you have tasks that create the project's static files in wwwroot
#wwwroot/
# Visual Studio 2017 auto generated files
Generated\ Files/
# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
# NUnit
*.VisualState.xml
TestResult.xml
nunit-*.xml
# Build Results of an ATL Project
[Dd]ebugPS/
[Rr]eleasePS/
dlldata.c
# Benchmark Results
BenchmarkDotNet.Artifacts/
# .NET Core
project.lock.json
project.fragment.lock.json
artifacts/
# StyleCop
StyleCopReport.xml
# Files built by Visual Studio
*_i.c
*_p.c
*_h.h
*.ilk
*.meta
*.obj
*.iobj
*.pch
*.pdb
*.ipdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*_wpftmp.csproj
*.log
*.vspscc
*.vssscc
.builds
*.pidb
*.svclog
*.scc
# Chutzpah Test files
_Chutzpah*
# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opendb
*.opensdf
*.sdf
*.cachefile
*.VC.db
*.VC.VC.opendb
# Visual Studio profiler
*.psess
*.vsp
*.vspx
*.sap
# Visual Studio Trace Files
*.e2e
# TFS 2012 Local Workspace
$tf/
# Guidance Automation Toolkit
*.gpState
# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
*.DotSettings.user
# TeamCity is a build add-in
_TeamCity*
# DotCover is a Code Coverage Tool
*.dotCover
# AxoCover is a Code Coverage Tool
.axoCover/*
!.axoCover/settings.json
# Visual Studio code coverage results
*.coverage
*.coveragexml
# NCrunch
_NCrunch_*
.*crunch*.local.xml
nCrunchTemp_*
# MightyMoose
*.mm.*
AutoTest.Net/
# Web workbench (sass)
.sass-cache/
# Installshield output folder
[Ee]xpress/
# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html
# Click-Once directory
publish/
# Publish Web Output
*.[Pp]ublish.xml
*.azurePubxml
# Note: Comment the next line if you want to checkin your web deploy settings,
# but database connection strings (with potential passwords) will be unencrypted
*.pubxml
*.publishproj
# Microsoft Azure Web App publish settings. Comment the next line if you want to
# checkin your Azure Web App publish settings, but sensitive information contained
# in these scripts will be unencrypted
PublishScripts/
# NuGet Packages
*.nupkg
# NuGet Symbol Packages
*.snupkg
# The packages folder can be ignored because of Package Restore
**/[Pp]ackages/*
# except build/, which is used as an MSBuild target.
!**/[Pp]ackages/build/
# Uncomment if necessary however generally it will be regenerated when needed
#!**/[Pp]ackages/repositories.config
# NuGet v3's project.json files produces more ignorable files
*.nuget.props
*.nuget.targets
# Microsoft Azure Build Output
csx/
*.build.csdef
# Microsoft Azure Emulator
ecf/
rcf/
# Windows Store app package directories and files
AppPackages/
BundleArtifacts/
Package.StoreAssociation.xml
_pkginfo.txt
*.appx
*.appxbundle
*.appxupload
# Visual Studio cache files
# files ending in .cache can be ignored
*.[Cc]ache
# but keep track of directories ending in .cache
!?*.[Cc]ache/
# Others
ClientBin/
~$*
*~
*.dbmdl
*.dbproj.schemaview
*.jfm
*.pfx
*.publishsettings
orleans.codegen.cs
# Including strong name files can present a security risk
# (https://github.com/github/gitignore/pull/2483#issue-259490424)
#*.snk
# Since there are multiple workflows, uncomment next line to ignore bower_components
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
#bower_components/
# RIA/Silverlight projects
Generated_Code/
# Backup & report files from converting an old project file
# to a newer Visual Studio version. Backup files are not needed,
# because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm
ServiceFabricBackup/
*.rptproj.bak
# SQL Server files
*.mdf
*.ldf
*.ndf
# Business Intelligence projects
*.rdl.data
*.bim.layout
*.bim_*.settings
*.rptproj.rsuser
*- [Bb]ackup.rdl
*- [Bb]ackup ([0-9]).rdl
*- [Bb]ackup ([0-9][0-9]).rdl
# Microsoft Fakes
FakesAssemblies/
# GhostDoc plugin setting file
*.GhostDoc.xml
# Node.js Tools for Visual Studio
.ntvs_analysis.dat
node_modules/
# Visual Studio 6 build log
*.plg
# Visual Studio 6 workspace options file
*.opt
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
*.vbw
# Visual Studio LightSwitch build output
**/*.HTMLClient/GeneratedArtifacts
**/*.DesktopClient/GeneratedArtifacts
**/*.DesktopClient/ModelManifest.xml
**/*.Server/GeneratedArtifacts
**/*.Server/ModelManifest.xml
_Pvt_Extensions
# Paket dependency manager
.paket/paket.exe
paket-files/
# FAKE - F# Make
.fake/
# CodeRush personal settings
.cr/personal
# Python Tools for Visual Studio (PTVS)
__pycache__/
*.pyc
# Cake - Uncomment if you are using it
# tools/**
# !tools/packages.config
# Tabs Studio
*.tss
# Telerik's JustMock configuration file
*.jmconfig
# BizTalk build output
*.btp.cs
*.btm.cs
*.odx.cs
*.xsd.cs
# OpenCover UI analysis results
OpenCover/
# Azure Stream Analytics local run output
ASALocalRun/
# MSBuild Binary and Structured Log
*.binlog
# NVidia Nsight GPU debugger configuration file
*.nvuser
# Local History for Visual Studio
.localhistory/
# BeatPulse healthcheck temp database
healthchecksdb
# Backup folder for Package Reference Convert tool in Visual Studio 2017
MigrationBackup/
# Ionide (cross platform F# VS Code tools) working folder
.ionide/
# macoOS
.DS_Store
# Any generated license data
src/AspNetCore/MyAspNetCoreApp/wwwroot/js/kendo-ui-license.js
# Jetbrains Rider
.idea
+33
View File
@@ -0,0 +1,33 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": ".NET Core Launch (web)",
"type": "coreclr",
"request": "launch",
"preLaunchTask": "build",
"program": "${workspaceFolder}/src/Blazor/MyBlazorApp/bin/Debug/net7.0/MyBlazorApp.dll",
"args": [],
"cwd": "${workspaceFolder}/src/Blazor/MyBlazorApp",
"stopAtEntry": false,
"serverReadyAction": {
"action": "openExternally",
"pattern": "\\bNow listening on:\\s+(https?://\\S+)"
},
"env": {
"ASPNETCORE_ENVIRONMENT": "Development"
},
"sourceFileMap": {
"/Views": "${workspaceFolder}/Views"
}
},
{
"name": ".NET Core Attach",
"type": "coreclr",
"request": "attach"
}
]
}
+41
View File
@@ -0,0 +1,41 @@
{
"version": "2.0.0",
"tasks": [
{
"label": "build blazor",
"command": "dotnet",
"type": "process",
"args": [
"build",
"${workspaceFolder}/src/Blazor/MyBlazorApp.sln",
"/property:GenerateFullPaths=true",
"/consoleloggerparameters:NoSummary"
],
"problemMatcher": "$msCompile"
},
{
"label": "publish blazor",
"command": "dotnet",
"type": "process",
"args": [
"publish",
"${workspaceFolder}/src/Blazor/MyBlazorApp.sln",
"/property:GenerateFullPaths=true",
"/consoleloggerparameters:NoSummary"
],
"problemMatcher": "$msCompile"
},
{
"label": "watch blazor",
"command": "dotnet",
"type": "process",
"args": [
"watch",
"run",
"--project",
"${workspaceFolder}/src/Blazor/MyBlazorApp.sln"
],
"problemMatcher": "$msCompile"
}
]
}
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2020 Lance McCarthy
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+330 -330
View File
@@ -1,330 +1,330 @@
# DevOps - Pipeline and Workflow Examples
This repository contains a rich set of CI-CD demos where I show you how to:
- Connect to private nuget feeds; Azure, GitHub packages, and custom (eg Telerik).
- Build .NET apps and publish to a container registry; Docker, Azure, GitHub, etc.
Although I use Telerik's NuGet server because I have a license, these demos are good for any private feed type; just use your source URL and credentials instead!
## Table of Contents
- [CI Systems](https://github.com/LanceMcCarthy/DevOpsExamples#ci-systems)
- [Build Badges](https://github.com/LanceMcCarthy/DevOpsExamples#badges)
- [Docker Examples](https://github.com/LanceMcCarthy/DevOpsExamples#docker-examples)
- [Video: Authenticating in Azure DevOps](https://github.com/LanceMcCarthy/DevOpsExamples#videos)
- [Tips and Troubleshooting](https://github.com/LanceMcCarthy/DevOpsExamples#tips-and-troubleshooting)
- [Walkthrough: Use GitHub Secrets](https://github.com/LanceMcCarthy/DevOpsExamples#github-actions-using-secrets-to-set-environment-variables)
- [Example: Update package source dynamically](https://github.com/LanceMcCarthy/DevOpsExamples#powershell-update-package-source-dynamically)
- [Example: Using Telerik NuGet Keys](https://github.com/LanceMcCarthy/DevOpsExamples#using-telerik-nuget-keys)
- [Dockerfile: Using Secrets](https://github.com/LanceMcCarthy/DevOpsExamples#dockerfile-using-secrets)
- [Telerik License Approaches](https://github.com/LanceMcCarthy/DevOpsExamples#telerik-license-approaches)
- Related Blog Posts
- [Blog: DevOps and Telerik NuGet Packages](https://www.telerik.com/blogs/azure-devops-and-telerik-nuget-packages)
- [Blog: Announcing Telerik NuGet Keys](https://www.telerik.com/blogs/announcing-nuget-keys)
## CI Systems
| System | CI/CD file(s) |
|---------------|------------------|
| GitHub Actions | [.github/workflows](/.github/workflows) |
| Azure DevOps (YAML) | [azure-pipelines.yml](https://github.com/LanceMcCarthy/DevOpsExamples/blob/main/azure-pipelines.yml) |
| Azure DevOps (classic) | click build badge |
| GitLab CI/CD | [.gitlab-ci.yml](https://gitlab.com/LanceMcCarthy/DevOpsExamples/-/blob/main/.gitlab-ci.yml) ↗|
## Badges
| Project | GitHub Actions | Azure DevOps | GitLab CI |
|---------|--------------|----------------|-----------|
| **.NET MAUI** | [![MAUI main](https://github.com/LanceMcCarthy/DevOpsExamples/actions/workflows/main_build-maui.yml/badge.svg?branch=main)](https://github.com/LanceMcCarthy/DevOpsExamples/actions/workflows/main_build-maui.yml) | [![Build - CLASSIC](https://dev.azure.com/lance/DevOps%20Examples/_apis/build/status/Build%20MAUI)](https://dev.azure.com/lance/DevOps%20Examples/_build/latest?definitionId=72) | [![Build status](https://gitlab.com/LanceMcCarthy/DevOpsExamples/badges/main/pipeline.svg)](https://gitlab.com/LanceMcCarthy/DevOpsExamples) |
| **ASP.NET Core** | [![Build ASP.NET Core Application](https://github.com/LanceMcCarthy/DevOpsExamples/actions/workflows/main_build-aspnetcore.yml/badge.svg)](https://github.com/LanceMcCarthy/DevOpsExamples/actions/workflows/main_build-aspnetcore.yml) | [![Build Status](https://dev.azure.com/lance/DevOps%20Examples/_apis/build/status%2FLanceMcCarthy.DevOpsExamples?branchName=main&jobName=BuildAspNetCoreApp)](https://dev.azure.com/lance/DevOps%20Examples/_build/latest?definitionId=45&branchName=main) | [![Build status](https://gitlab.com/LanceMcCarthy/DevOpsExamples/badges/main/pipeline.svg)](https://gitlab.com/LanceMcCarthy/DevOpsExamples) |
| **ASP.NET Blazor** | [![Build Blazor Application](https://github.com/LanceMcCarthy/DevOpsExamples/actions/workflows/main_build-blazor.yml/badge.svg)](https://github.com/LanceMcCarthy/DevOpsExamples/actions/workflows/main_build-blazor.yml) | [![Build Status](https://dev.azure.com/lance/DevOps%20Examples/_apis/build/status%2FLanceMcCarthy.DevOpsExamples?branchName=main&jobName=BuildBlazorApp)](https://dev.azure.com/lance/DevOps%20Examples/_build/latest?definitionId=45&branchName=main) | [![Build status](https://gitlab.com/LanceMcCarthy/DevOpsExamples/badges/main/pipeline.svg)](https://gitlab.com/LanceMcCarthy/DevOpsExamples) |
| **WPF** (`net48`) | [![WPF (.NET Framework)](https://github.com/LanceMcCarthy/DevOpsExamples/actions/workflows/main_build-wpf.yml/badge.svg)](https://github.com/LanceMcCarthy/DevOpsExamples/actions/workflows/main_build-wpf.yml) | [![Build - CLASSIC](https://dev.azure.com/lance/DevOps%20Examples/_apis/build/status/Build%20WPF%20and%20WinForms)](https://dev.azure.com/lance/DevOps%20Examples/_build/latest?definitionId=46) | [![Build status](https://gitlab.com/LanceMcCarthy/DevOpsExamples/badges/main/pipeline.svg)](https://gitlab.com/LanceMcCarthy/DevOpsExamples) |
| **WinForms** (`net48`) | [![WinForms (.NET Framework)](https://github.com/LanceMcCarthy/DevOpsExamples/actions/workflows/main_build-winforms.yml/badge.svg)](https://github.com/LanceMcCarthy/DevOpsExamples/actions/workflows/main_build-winforms.yml) | [![Build - CLASSIC](https://dev.azure.com/lance/DevOps%20Examples/_apis/build/status/Build%20WinForms?branchName=main)](https://dev.azure.com/lance/DevOps%20Examples/_build/latest?definitionId=79&branchName=main) | [![Build status](https://gitlab.com/LanceMcCarthy/DevOpsExamples/badges/main/pipeline.svg)](https://gitlab.com/LanceMcCarthy/DevOpsExamples) |
| **Console** | [![Console (.NET)](https://github.com/LanceMcCarthy/DevOpsExamples/actions/workflows/main_build-console.yml/badge.svg)](https://github.com/LanceMcCarthy/DevOpsExamples/actions/workflows/main_build-console.yml) | [![Build Status AKEYLESS](https://dev.azure.com/lance/DevOps%20Examples/_apis/build/status%2FLanceMcCarthy.DevOpsExamples?branchName=main&jobName=BuildConsoleApp_Akeyless)](https://dev.azure.com/lance/DevOps%20Examples/_build/latest?definitionId=45&branchName=main) | [![Build status](https://gitlab.com/LanceMcCarthy/DevOpsExamples/badges/main/pipeline.svg)](https://gitlab.com/LanceMcCarthy/DevOpsExamples) |
| **WinUI 3** | [![Build WinUI3 Project](https://github.com/LanceMcCarthy/DevOpsExamples/actions/workflows/main_build-winui.yml/badge.svg)](https://github.com/LanceMcCarthy/DevOpsExamples/actions/workflows/main_build-winui.yml) | [![Build Status](https://dev.azure.com/lance/DevOps%20Examples/_apis/build/status%2FLanceMcCarthy.DevOpsExamples?branchName=main&jobName=BuildWinUI)](https://dev.azure.com/lance/DevOps%20Examples/_build/latest?definitionId=45&branchName=main) | [![Build status](https://gitlab.com/LanceMcCarthy/DevOpsExamples/badges/main/pipeline.svg)](https://gitlab.com/LanceMcCarthy/DevOpsExamples) |
| **Kendo Angular** | [![Build Angular](https://github.com/LanceMcCarthy/DevOpsExamples/actions/workflows/main_build-angular.yml/badge.svg)](https://github.com/LanceMcCarthy/DevOpsExamples/actions/workflows/main_build-angular.yml) | [![Build Status](https://dev.azure.com/lance/DevOps%20Examples/_apis/build/status%2FLanceMcCarthy.DevOpsExamples?branchName=main&jobName=BuildAngularAppWithVariables)](https://dev.azure.com/lance/DevOps%20Examples/_build/latest?definitionId=45&branchName=main) | [![Build status](https://gitlab.com/LanceMcCarthy/DevOpsExamples/badges/main/pipeline.svg)](https://gitlab.com/LanceMcCarthy/DevOpsExamples) |
| **ASP.NET AJAX** (`net48`) | [![Build AJAX Application](https://github.com/LanceMcCarthy/DevOpsExamples/actions/workflows/main_build-ajax.yml/badge.svg)](https://github.com/LanceMcCarthy/DevOpsExamples/actions/workflows/main_build-ajax.yml) | [![Build Status](https://dev.azure.com/lance/DevOps%20Examples/_apis/build/status%2FLanceMcCarthy.DevOpsExamples?branchName=main&jobName=BuildAjaxApp)](https://dev.azure.com/lance/DevOps%20Examples/_build/latest?definitionId=45&branchName=main) | [![Build status](https://gitlab.com/LanceMcCarthy/DevOpsExamples/badges/main/pipeline.svg)](https://gitlab.com/LanceMcCarthy/DevOpsExamples) |
> All Azure DevOps status badges are for classic pipelines, except the `Console` project, which uses Azure DevOps YAML pipelines.
## Docker Examples
This repo also contains examples on how to build and publish a Telerik powered .NET project as a container image. The image names below are published to the `lancemccarthy` Docker Hub user, but the approach also works for any container registry.
| Image | GitHub Action | Dockerfile |
|--------------|---------------|------------|
| `aspnetcore-reporting-from-centosbase` | [![](https://github.com/LanceMcCarthy/DevOpsExamples/actions/workflows/main_docker-aspnetcore.yml/badge.svg)](https://github.com/LanceMcCarthy/DevOpsExamples/actions/workflows/main_docker-aspnetcore.yml) | [link](https://github.com/LanceMcCarthy/DevOpsExamples/blob/main/src/AspNetCore/MyAspNetCoreApp/Dockerfile_CentOS "MyAspNetCoreApp/Dockerfile_CentOS") |
| `aspnetcore-reporting-from-msftbase` | [![](https://github.com/LanceMcCarthy/DevOpsExamples/actions/workflows/main_docker-aspnetcore.yml/badge.svg)](https://github.com/LanceMcCarthy/DevOpsExamples/actions/workflows/main_docker-aspnetcore.yml) | [link](https://github.com/LanceMcCarthy/DevOpsExamples/blob/main/src/AspNetCore/MyAspNetCoreApp/Dockerfile_MSRuntimeBase "MyAspNetCoreApp/Dockerfile_MSRuntimeBase") |
| `myblazorapp` | [![](https://github.com/LanceMcCarthy/DevOpsExamples/actions/workflows/main_docker-blazor.yml/badge.svg)](https://github.com/LanceMcCarthy/DevOpsExamples/actions/workflows/main_docker-blazor.yml) | [link](https://github.com/LanceMcCarthy/DevOpsExamples/blob/main/src/Blazor/MyBlazorApp/Dockerfile "MyBlazorApp/Dockerfile") |
> [!NOTE]
> When creating a container, forward port 8080 to the host. For example:
> 1. Run `docker run -d -p 9999:8080 lancemccarthy/myblazorapp:latest`
> 2. Visit site on http://localhost:9999
## Videos
### Azure DevOps with Telerik NuGet Server
The following **4 minute** video takes you though all the steps on adding a private NuGet feed as a Service Connection and consuming that service in three different pipeline setups.
[![YouTube tutorial](https://img.youtube.com/vi/rUWU2n6FwgA/0.jpg)](https://www.youtube.com/watch?v=rUWU2n6FwgA)
- [0:09](https://youtu.be/rUWU2n6FwgA?t=9) Add a Service connection to the Telerik server
- [1:14](https://youtu.be/rUWU2n6FwgA?t=74) Classic pipeline for .NET Core
- [1:47](https://youtu.be/rUWU2n6FwgA?t=107) Classic .NET Framework pipeline
- [2:25](https://youtu.be/rUWU2n6FwgA?t=145) YAML pipeline setup for .NET Core
> [!IMPORTANT]
> The recording has some outdated information, take the following updates into consideration when watching:
> - Use the v3 server address `https://nuget.telerik.com/v3/index.json`
> - Use an API key credential if your telerik.com account is SSO (see [Announcing NuGet Keys](https://www.telerik.com/blogs/announcing-nuget-keys)).
## Tips and Troubleshooting
### GitHub Actions: Using Secrets to Set Environment Variables
If you have environment variable placeholders in your nuget.config file, you can easily set them using GitHub Secrets. For example, let's say in your packageSourceCredentials, you have the following the environment variable placeholders `%TELERIK_USERNAME%` and `%TELERIK_PASSWORD%`
```xaml
<?xml version="1.0" encoding="utf-8"?>
<configuration>
...
<packageSourceCredentials>
<Telerik_v3_Feed>
<add key="Username" value="%TELERIK_USERNAME%" />
<add key="ClearTextPassword" value="%TELERIK_PASSWORD%" />
</Telerik_v3_Feed>
</packageSourceCredentials>
...
</configuration>
```
You can directly set those vars on the same step which you invoke the `dotnet restore/build/publish` command. For example, here I use an API key from my GitHub Actions Secrets for credentials
```yaml
- name: Restore NuGet Packages
run: dotnet restore src/MyProject.csproj --configfile src/nuget.config
env:
TELERIK_USERNAME: "api-key"
TELERIK_PASSWORD: ${{secrets.TELERIK_API_KEY}}
```
> [!TIP]
> This is also very useful for Dependabot runs. You can set a Dependabot secret (in the repo settings) and it will be able to restore packages during checks that were triggered by Dependabot.
### Powershell: Adding or Updating Package Source Dynamically
#### Option 1 - Update existing package source
You could also dynamically update the credentials of a Package Source defined in your nuget.config file This is a good option when you do not want to use a `packageSourceCredentials` section that uses environment variables.
```powershell
# Setting credentials for the 'Telerik_v3_Feed' defined in the nuget.config file.
dotnet nuget update source "Telerik_v3_Feed" -s "https://nuget.telerik.com/v3/index.json" -u '${{secrets.MyTelerikEmail}}' -p '${{secrets.MyTelerikPassword}}' --configfile "src/nuget.config" --store-password-in-clear-text
```
That command will look through the nuget.config for a package source with the key `Telerik_v3_Feed` and then add/update the credentials for that source.
#### Option 2 - Add a new package source
The other approach is a bit simpler because you dont need a custom nuget.config file. Just use the dotnet nuget add source command
```powershell
dotnet nuget add source 'https://nuget.telerik.com/v3/index.json' -n "AddedTelerikServer" -u ${{secrets.MyTelerikEmail}} -p ${{secrets.MyTelerikPassword}} --store-password-in-clear-text
```
> The `--store-password-in-clear-text` switch is important. It does *not* mean the password is visible, rather it means that you're using the password text and not a custom encrypted variant. For more information, please visit https://docs.microsoft.com/en-us/nuget/reference/nuget-config-file#packagesourcecredentials
### Using Telerik NuGet Keys
You can use the same approach in the previous section. Everything is exactly the same, except you use `api-key` for the username and the NuGet key for the password.
Please visit the [Announcing NuGet Keys](https://www.telerik.com/blogs/announcing-nuget-keys) blog post for more details how ot create the key and how to use it.
```powershell
dotnet nuget update source "Telerik_v3_Feed" -s "https://nuget.telerik.com/v3/index.json" -u 'api-key' -p '${{secrets.MyNuGetKey}}' --configfile "src/nuget.config" --store-password-in-clear-text
```
> [!CAUTION]
> Protect your key by storing it in a GitHub Secret, then use the secret's ID in the command.
### Dockerfile: Using Secrets
When using a Dockerfile to build a .NET project that uses the Telerik NuGet server, you'll need a safe and secure way to handle your NuGet crednetials and your Telerik License Key. This can be done my mounting a Docker secret.
In your GitHub Actions workflow, you can define and set docker secrets in the docker build step. In the following example, notice how we are setting two docker secrets (`nuget-sec` and `license-sec`) using the values from GitHub secrets.
```yaml
- uses: docker/build-push-action@v3
with:
secrets: |
nuget-sec=${{secrets.MY_NUGET_KEY}}
license-sec=${{secrets.MY_TELERIK_LICENSE_KEY}}
```
Now, inside the Dockerfile, you can mount and use those secrets. See Stage 2 in the following example:
```Dockerfile
### STAGE 1 ###
FROM --platform=$BUILDPLATFORM mcr.microsoft.com/dotnet/aspnet:10.0 AS base
WORKDIR /app
### STAGE 2 ###
FROM mcr.microsoft.com/dotnet/sdk:10.0 AS build
WORKDIR /src
COPY . .
# STEP 1. Mount the 'nuget-sec' secret, then:
# a. add the Telerik package source
# b. restore packages
RUN --mount=type=secret,id=nuget-sec,required \
dotnet nuget add source 'https://nuget.telerik.com/v3/index.json' -n "Telerik_v3_Feed" -u "api-key" -p "$(cat /run/secrets/nuget-sec)" --store-password-in-clear-text \
&& \
dotnet restore "MyBlazorApp.csproj"
# STEP 2. Mount the "license-sec" secret, then:
# a. create the license file
# b. build the project
# c. delete the file so you don't distribute it in your image (important!)
RUN --mount=type=secret,id=license-key,required \
mkdir -p ~/.telerik && echo "$(cat /run/secrets/license-sec)" > ~/.telerik/telerik-license.txt \
&& \
dotnet publish "Researcher.Web/Researcher.Web.csproj" -o /app/publish /p:UseAppHost=false --no-restore --self-contained false \
&& \
rm -rf ~/.telerik
### STAGE 3 ###
# Build final from base, but copy ONLY THE PUBLISH ARTIFACTS from stage 2
FROM base AS final
WORKDIR /app
COPY --from=build /app/publish .
ENTRYPOINT ["dotnet", "MyBlazorApp.dll"]
```
> [!CAUTION]
> Pay attention to whether or not you are including any secrets in your final image. You can run your container to explore the files (and env vars in Exec) to make sure.
### Telerik License Approaches
Depending on how you're building our code, there are several ways to introduce the Telerik License Key at the right time for the build. Let me show you two; variable and file.
- [Approach 1 - Using an Environment Variable](https://github.com/LanceMcCarthy/DevOpsExamples?tab=readme-ov-file#approach-1---using-a-variable)
- [Approach 2 - Using a License File](https://github.com/LanceMcCarthy/DevOpsExamples?tab=readme-ov-file#approach-2---using-a-file)
- [In a YAML Pipeline](https://github.com/LanceMcCarthy/DevOpsExamples?tab=readme-ov-file#yaml-pipeline)
- [In a Classic Pipeline](https://github.com/LanceMcCarthy/DevOpsExamples?tab=readme-ov-file#classic-pipeline)
- [Approach 1 - Using a Variable](https://github.com/LanceMcCarthy/DevOpsExamples#approach-1---using-a-variable)
- [Approach 2 - Using a File](https://github.com/LanceMcCarthy/DevOpsExamples#approach-2---using-a-file)
- [Secure File - YAML Pipeline](https://github.com/LanceMcCarthy/DevOpsExamples#secure-file---yaml-pipeline)
- [Secure File - Classic Pipeline](https://github.com/LanceMcCarthy/DevOpsExamples#secure-file---classic-pipeline)
- [Scenario 1 - Task With Env Vars Inputs](https://github.com/LanceMcCarthy/DevOpsExamples#scenario-1---task-with-env-var-inputs)
- [Scenario 2 - Task Without Env Var Inputs](https://github.com/LanceMcCarthy/DevOpsExamples#scenario-2---task-without-env-var-inputs)
- [Scenario 3 - Move Secure File](https://github.com/LanceMcCarthy/DevOpsExamples#scenario-3---move-secure-file)
#### Approach 1 - Using a Variable
This is by far the easiest and safest way. You can use a secret (GitHub Action secret or AzDO Variable secret) and set the `TELERIK_LICENSE` environment variable before the project is compiled.
In a YAML workflow/pipeline, you can set the environment variable at the beginning of the job or on a step that needs it.
GH Actions
```yaml
- run: dotnet publish MyApp.csproj -o /app/publish /p:UseAppHost=false --no-restore
env:
TELERIK_LICENSE: ${{secrets.TELERIK_LICENSE_KEY}}
```
Azure Pipelines YAML
```yaml
- powershell: dotnet publish MyApp.csproj -o /app/publish /p:UseAppHost=false --no-restore
displayName: 'Build and publish the project'
env:
TELERIK_LICENSE: $(MY_TELERIK_LICENSE_KEY) # AzDO pipeline secret variable
```
If you're using classic pipelines, you can use a pipeline variable:
![Image](https://github.com/user-attachments/assets/bcdcc8c3-8ec7-43af-8452-4bace4e8ee83)
> [!IMPORTANT]
> License key length - If you are using a Library **Variable Group**, there is a character limit for the variable values. The only way to have a long value in the Variable Group is to link it from Azure KeyVault. If you cannot use Azure KeyVault, then use a normal pipeline variable instead (seen above) or use the Secure File approach instead (see below).
#### Approach 2 - Using a File
You have two options for a file-base option. Set the TELERIK_LICENSE_PATH variable or add a file named **telerik-license.txt** to the project directory. The licensing runtime will do a recursive check from the project directory to root, and then finally %appdata%/telerik/.
On Azure DevOps, there is a powerful feature called Secure Files. It lets you upload a file and then use it in a pipeline. Go to your Library tab, then select Secure File. After you've uploaded the Secure File to your Azure DevOps project, you can use it in a pipeline.
Here are several ways to use that Secure File.
> [!CAUTION]
> Never include the **telerik-license.txt** file inside your application/docker image!
##### YAML Pipeline
With a YAML pipeline, you can use the **DownloadSecureFile@1** task, then use `$(name.secureFilePath)` to reference it. For example:
```yaml
- task: DownloadSecureFile@1
name: DownloadTelerikLicenseFile # defining the 'name' is important
displayName: 'Download Telerik License Key File'
inputs:
secureFile: 'telerik-license.txt'
- task: MSBuild@1
displayName: 'Build Project'
inputs:
solution: 'myapp.csproj'
platform: Any CPU
configuration: Release
msbuildArguments: '/p:RestorePackages=false'
env:
# use the name.secureFilePath value to set the special TELERIK_LICENSE_PATH
TELERIK_LICENSE_PATH: $(DownloadTelerikLicenseFile.secureFilePath)
```
##### Classic Pipeline
With a classic pipeline, you can use the same `DownloadSecureFile` Task
![Image](https://github.com/user-attachments/assets/8c9f0aa4-0ef8-48a9-9805-b0686db1109c)
> [!IMPORTANT]
> Make sure you set the **reference name** which gets prefixed to the `.secureFilePath` output variable.
###### Scenario 1 - Task With Env Var Inputs
With the secure file downloaded to the runner, you can now set the **TELERIK_LICENSE_PATH** variable using `$(telerik.secureFilePath)`.
<img width="700" alt="image" src="https://github.com/user-attachments/assets/e6bb5425-a721-4307-9837-0ef2e6f8cefa" />
###### Scenario 2 - Task Without Env Var Inputs
Not all AzDO tasks have the "Environment variables" section (e.g. MsBuild task doesn't have it). To solve this, you can set a pipeline variable before that task. Using the secure file
1. Add a new Powershell or Bash task (_after_ the Download Secure File task)
2. Set the **TELERIK_LICENSE_PATH** using `task.setvariable` command with `issecret=true`and the secure file task's output variable
```powershell
# If using Powershell
Write-Host "##vso[task.setvariable variable=TELERIK_LICENSE_PATH;issecret=true]$(telerik.secureFilePath)"
# If using Bash
echo "##vso[task.setvariable variable=TELERIK_LICENSE_PATH;issecret=true]$(telerik.secureFilePath)"
```
<img width="700" alt="image" src="https://github.com/user-attachments/assets/9f1bcecd-a2d2-45f2-95b6-76bdfbda6516" />
###### Scenario 3 - Move Secure File
If you have nay trouble with the TELERIK_LICENSE_PATH variable, you can just simply move the file to the root build directory.
1. Add a new Powershell or Bash task (_after_ the Download Secure File task)
```powershell
Move-Item -Path "$(telerik.secureFilePath)" -Destination "$(Build.Repository.LocalPath)/telerik-license.txt" -Force
```
2. Build the code
3. Delete the file (so you don't accidentally include it in your distribution)
```powershell
Remove-Item -Path "$(Build.Repository.LocalPath)/telerik-license.txt" -Force
```
As you can see, there are a wide range of options. The one you choose highly depends on your environment and CI requirements.
# DevOps - Pipeline and Workflow Examples
This repository contains a rich set of CI-CD demos where I show you how to:
- Connect to private nuget feeds; Azure, GitHub packages, and custom (eg Telerik).
- Build .NET apps and publish to a container registry; Docker, Azure, GitHub, etc.
Although I use Telerik's NuGet server because I have a license, these demos are good for any private feed type; just use your source URL and credentials instead!
## Table of Contents
- [CI Systems](https://github.com/LanceMcCarthy/DevOpsExamples#ci-systems)
- [Build Badges](https://github.com/LanceMcCarthy/DevOpsExamples#badges)
- [Docker Examples](https://github.com/LanceMcCarthy/DevOpsExamples#docker-examples)
- [Video: Authenticating in Azure DevOps](https://github.com/LanceMcCarthy/DevOpsExamples#videos)
- [Tips and Troubleshooting](https://github.com/LanceMcCarthy/DevOpsExamples#tips-and-troubleshooting)
- [Walkthrough: Use GitHub Secrets](https://github.com/LanceMcCarthy/DevOpsExamples#github-actions-using-secrets-to-set-environment-variables)
- [Example: Update package source dynamically](https://github.com/LanceMcCarthy/DevOpsExamples#powershell-update-package-source-dynamically)
- [Example: Using Telerik NuGet Keys](https://github.com/LanceMcCarthy/DevOpsExamples#using-telerik-nuget-keys)
- [Dockerfile: Using Secrets](https://github.com/LanceMcCarthy/DevOpsExamples#dockerfile-using-secrets)
- [Telerik License Approaches](https://github.com/LanceMcCarthy/DevOpsExamples#telerik-license-approaches)
- Related Blog Posts
- [Blog: DevOps and Telerik NuGet Packages](https://www.telerik.com/blogs/azure-devops-and-telerik-nuget-packages)
- [Blog: Announcing Telerik NuGet Keys](https://www.telerik.com/blogs/announcing-nuget-keys)
## CI Systems
| System | CI/CD file(s) |
|---------------|------------------|
| GitHub Actions | [.github/workflows](/.github/workflows) |
| Azure DevOps (YAML) | [azure-pipelines.yml](https://github.com/LanceMcCarthy/DevOpsExamples/blob/main/azure-pipelines.yml) |
| Azure DevOps (classic) | click build badge |
| GitLab CI/CD | [.gitlab-ci.yml](https://gitlab.com/LanceMcCarthy/DevOpsExamples/-/blob/main/.gitlab-ci.yml) ↗|
## Badges
| Project | GitHub Actions | Azure DevOps | GitLab CI |
|---------|--------------|----------------|-----------|
| **.NET MAUI** | [![MAUI main](https://github.com/LanceMcCarthy/DevOpsExamples/actions/workflows/main_build-maui.yml/badge.svg?branch=main)](https://github.com/LanceMcCarthy/DevOpsExamples/actions/workflows/main_build-maui.yml) | [![Build - CLASSIC](https://dev.azure.com/lance/DevOps%20Examples/_apis/build/status/Build%20MAUI)](https://dev.azure.com/lance/DevOps%20Examples/_build/latest?definitionId=72) | [![Build status](https://gitlab.com/LanceMcCarthy/DevOpsExamples/badges/main/pipeline.svg)](https://gitlab.com/LanceMcCarthy/DevOpsExamples) |
| **ASP.NET Core** | [![Build ASP.NET Core Application](https://github.com/LanceMcCarthy/DevOpsExamples/actions/workflows/main_build-aspnetcore.yml/badge.svg)](https://github.com/LanceMcCarthy/DevOpsExamples/actions/workflows/main_build-aspnetcore.yml) | [![Build Status](https://dev.azure.com/lance/DevOps%20Examples/_apis/build/status%2FLanceMcCarthy.DevOpsExamples?branchName=main&jobName=BuildAspNetCoreApp)](https://dev.azure.com/lance/DevOps%20Examples/_build/latest?definitionId=45&branchName=main) | [![Build status](https://gitlab.com/LanceMcCarthy/DevOpsExamples/badges/main/pipeline.svg)](https://gitlab.com/LanceMcCarthy/DevOpsExamples) |
| **ASP.NET Blazor** | [![Build Blazor Application](https://github.com/LanceMcCarthy/DevOpsExamples/actions/workflows/main_build-blazor.yml/badge.svg)](https://github.com/LanceMcCarthy/DevOpsExamples/actions/workflows/main_build-blazor.yml) | [![Build Status](https://dev.azure.com/lance/DevOps%20Examples/_apis/build/status%2FLanceMcCarthy.DevOpsExamples?branchName=main&jobName=BuildBlazorApp)](https://dev.azure.com/lance/DevOps%20Examples/_build/latest?definitionId=45&branchName=main) | [![Build status](https://gitlab.com/LanceMcCarthy/DevOpsExamples/badges/main/pipeline.svg)](https://gitlab.com/LanceMcCarthy/DevOpsExamples) |
| **WPF** (`net48`) | [![WPF (.NET Framework)](https://github.com/LanceMcCarthy/DevOpsExamples/actions/workflows/main_build-wpf.yml/badge.svg)](https://github.com/LanceMcCarthy/DevOpsExamples/actions/workflows/main_build-wpf.yml) | [![Build - CLASSIC](https://dev.azure.com/lance/DevOps%20Examples/_apis/build/status/Build%20WPF%20and%20WinForms)](https://dev.azure.com/lance/DevOps%20Examples/_build/latest?definitionId=46) | [![Build status](https://gitlab.com/LanceMcCarthy/DevOpsExamples/badges/main/pipeline.svg)](https://gitlab.com/LanceMcCarthy/DevOpsExamples) |
| **WinForms** (`net48`) | [![WinForms (.NET Framework)](https://github.com/LanceMcCarthy/DevOpsExamples/actions/workflows/main_build-winforms.yml/badge.svg)](https://github.com/LanceMcCarthy/DevOpsExamples/actions/workflows/main_build-winforms.yml) | [![Build - CLASSIC](https://dev.azure.com/lance/DevOps%20Examples/_apis/build/status/Build%20WinForms?branchName=main)](https://dev.azure.com/lance/DevOps%20Examples/_build/latest?definitionId=79&branchName=main) | [![Build status](https://gitlab.com/LanceMcCarthy/DevOpsExamples/badges/main/pipeline.svg)](https://gitlab.com/LanceMcCarthy/DevOpsExamples) |
| **Console** | [![Console (.NET)](https://github.com/LanceMcCarthy/DevOpsExamples/actions/workflows/main_build-console.yml/badge.svg)](https://github.com/LanceMcCarthy/DevOpsExamples/actions/workflows/main_build-console.yml) | [![Build Status AKEYLESS](https://dev.azure.com/lance/DevOps%20Examples/_apis/build/status%2FLanceMcCarthy.DevOpsExamples?branchName=main&jobName=BuildConsoleApp_Akeyless)](https://dev.azure.com/lance/DevOps%20Examples/_build/latest?definitionId=45&branchName=main) | [![Build status](https://gitlab.com/LanceMcCarthy/DevOpsExamples/badges/main/pipeline.svg)](https://gitlab.com/LanceMcCarthy/DevOpsExamples) |
| **WinUI 3** | [![Build WinUI3 Project](https://github.com/LanceMcCarthy/DevOpsExamples/actions/workflows/main_build-winui.yml/badge.svg)](https://github.com/LanceMcCarthy/DevOpsExamples/actions/workflows/main_build-winui.yml) | [![Build Status](https://dev.azure.com/lance/DevOps%20Examples/_apis/build/status%2FLanceMcCarthy.DevOpsExamples?branchName=main&jobName=BuildWinUI)](https://dev.azure.com/lance/DevOps%20Examples/_build/latest?definitionId=45&branchName=main) | [![Build status](https://gitlab.com/LanceMcCarthy/DevOpsExamples/badges/main/pipeline.svg)](https://gitlab.com/LanceMcCarthy/DevOpsExamples) |
| **Kendo Angular** | [![Build Angular](https://github.com/LanceMcCarthy/DevOpsExamples/actions/workflows/main_build-angular.yml/badge.svg)](https://github.com/LanceMcCarthy/DevOpsExamples/actions/workflows/main_build-angular.yml) | [![Build Status](https://dev.azure.com/lance/DevOps%20Examples/_apis/build/status%2FLanceMcCarthy.DevOpsExamples?branchName=main&jobName=BuildAngularAppWithVariables)](https://dev.azure.com/lance/DevOps%20Examples/_build/latest?definitionId=45&branchName=main) | [![Build status](https://gitlab.com/LanceMcCarthy/DevOpsExamples/badges/main/pipeline.svg)](https://gitlab.com/LanceMcCarthy/DevOpsExamples) |
| **ASP.NET AJAX** (`net48`) | [![Build AJAX Application](https://github.com/LanceMcCarthy/DevOpsExamples/actions/workflows/main_build-ajax.yml/badge.svg)](https://github.com/LanceMcCarthy/DevOpsExamples/actions/workflows/main_build-ajax.yml) | [![Build Status](https://dev.azure.com/lance/DevOps%20Examples/_apis/build/status%2FLanceMcCarthy.DevOpsExamples?branchName=main&jobName=BuildAjaxApp)](https://dev.azure.com/lance/DevOps%20Examples/_build/latest?definitionId=45&branchName=main) | [![Build status](https://gitlab.com/LanceMcCarthy/DevOpsExamples/badges/main/pipeline.svg)](https://gitlab.com/LanceMcCarthy/DevOpsExamples) |
> All Azure DevOps status badges are for classic pipelines, except the `Console` project, which uses Azure DevOps YAML pipelines.
## Docker Examples
This repo also contains examples on how to build and publish a Telerik powered .NET project as a container image. The image names below are published to the `lancemccarthy` Docker Hub user, but the approach also works for any container registry.
| Image | GitHub Action | Dockerfile |
|--------------|---------------|------------|
| `aspnetcore-reporting-from-centosbase` | [![](https://github.com/LanceMcCarthy/DevOpsExamples/actions/workflows/main_docker-aspnetcore.yml/badge.svg)](https://github.com/LanceMcCarthy/DevOpsExamples/actions/workflows/main_docker-aspnetcore.yml) | [link](https://github.com/LanceMcCarthy/DevOpsExamples/blob/main/src/AspNetCore/MyAspNetCoreApp/Dockerfile_CentOS "MyAspNetCoreApp/Dockerfile_CentOS") |
| `aspnetcore-reporting-from-msftbase` | [![](https://github.com/LanceMcCarthy/DevOpsExamples/actions/workflows/main_docker-aspnetcore.yml/badge.svg)](https://github.com/LanceMcCarthy/DevOpsExamples/actions/workflows/main_docker-aspnetcore.yml) | [link](https://github.com/LanceMcCarthy/DevOpsExamples/blob/main/src/AspNetCore/MyAspNetCoreApp/Dockerfile_MSRuntimeBase "MyAspNetCoreApp/Dockerfile_MSRuntimeBase") |
| `myblazorapp` | [![](https://github.com/LanceMcCarthy/DevOpsExamples/actions/workflows/main_docker-blazor.yml/badge.svg)](https://github.com/LanceMcCarthy/DevOpsExamples/actions/workflows/main_docker-blazor.yml) | [link](https://github.com/LanceMcCarthy/DevOpsExamples/blob/main/src/Blazor/MyBlazorApp/Dockerfile "MyBlazorApp/Dockerfile") |
> [!NOTE]
> When creating a container, forward port 8080 to the host. For example:
> 1. Run `docker run -d -p 9999:8080 lancemccarthy/myblazorapp:latest`
> 2. Visit site on http://localhost:9999
## Videos
### Azure DevOps with Telerik NuGet Server
The following **4 minute** video takes you though all the steps on adding a private NuGet feed as a Service Connection and consuming that service in three different pipeline setups.
[![YouTube tutorial](https://img.youtube.com/vi/rUWU2n6FwgA/0.jpg)](https://www.youtube.com/watch?v=rUWU2n6FwgA)
- [0:09](https://youtu.be/rUWU2n6FwgA?t=9) Add a Service connection to the Telerik server
- [1:14](https://youtu.be/rUWU2n6FwgA?t=74) Classic pipeline for .NET Core
- [1:47](https://youtu.be/rUWU2n6FwgA?t=107) Classic .NET Framework pipeline
- [2:25](https://youtu.be/rUWU2n6FwgA?t=145) YAML pipeline setup for .NET Core
> [!IMPORTANT]
> The recording has some outdated information, take the following updates into consideration when watching:
> - Use the v3 server address `https://nuget.telerik.com/v3/index.json`
> - Use an API key credential if your telerik.com account is SSO (see [Announcing NuGet Keys](https://www.telerik.com/blogs/announcing-nuget-keys)).
## Tips and Troubleshooting
### GitHub Actions: Using Secrets to Set Environment Variables
If you have environment variable placeholders in your nuget.config file, you can easily set them using GitHub Secrets. For example, let's say in your packageSourceCredentials, you have the following the environment variable placeholders `%TELERIK_USERNAME%` and `%TELERIK_PASSWORD%`
```xaml
<?xml version="1.0" encoding="utf-8"?>
<configuration>
...
<packageSourceCredentials>
<Telerik_v3_Feed>
<add key="Username" value="%TELERIK_USERNAME%" />
<add key="ClearTextPassword" value="%TELERIK_PASSWORD%" />
</Telerik_v3_Feed>
</packageSourceCredentials>
...
</configuration>
```
You can directly set those vars on the same step which you invoke the `dotnet restore/build/publish` command. For example, here I use an API key from my GitHub Actions Secrets for credentials
```yaml
- name: Restore NuGet Packages
run: dotnet restore src/MyProject.csproj --configfile src/nuget.config
env:
TELERIK_USERNAME: "api-key"
TELERIK_PASSWORD: ${{secrets.TELERIK_API_KEY}}
```
> [!TIP]
> This is also very useful for Dependabot runs. You can set a Dependabot secret (in the repo settings) and it will be able to restore packages during checks that were triggered by Dependabot.
### Powershell: Adding or Updating Package Source Dynamically
#### Option 1 - Update existing package source
You could also dynamically update the credentials of a Package Source defined in your nuget.config file This is a good option when you do not want to use a `packageSourceCredentials` section that uses environment variables.
```powershell
# Setting credentials for the 'Telerik_v3_Feed' defined in the nuget.config file.
dotnet nuget update source "Telerik_v3_Feed" -s "https://nuget.telerik.com/v3/index.json" -u '${{secrets.MyTelerikEmail}}' -p '${{secrets.MyTelerikPassword}}' --configfile "src/nuget.config" --store-password-in-clear-text
```
That command will look through the nuget.config for a package source with the key `Telerik_v3_Feed` and then add/update the credentials for that source.
#### Option 2 - Add a new package source
The other approach is a bit simpler because you dont need a custom nuget.config file. Just use the dotnet nuget add source command
```powershell
dotnet nuget add source 'https://nuget.telerik.com/v3/index.json' -n "AddedTelerikServer" -u ${{secrets.MyTelerikEmail}} -p ${{secrets.MyTelerikPassword}} --store-password-in-clear-text
```
> The `--store-password-in-clear-text` switch is important. It does *not* mean the password is visible, rather it means that you're using the password text and not a custom encrypted variant. For more information, please visit https://docs.microsoft.com/en-us/nuget/reference/nuget-config-file#packagesourcecredentials
### Using Telerik NuGet Keys
You can use the same approach in the previous section. Everything is exactly the same, except you use `api-key` for the username and the NuGet key for the password.
Please visit the [Announcing NuGet Keys](https://www.telerik.com/blogs/announcing-nuget-keys) blog post for more details how ot create the key and how to use it.
```powershell
dotnet nuget update source "Telerik_v3_Feed" -s "https://nuget.telerik.com/v3/index.json" -u 'api-key' -p '${{secrets.MyNuGetKey}}' --configfile "src/nuget.config" --store-password-in-clear-text
```
> [!CAUTION]
> Protect your key by storing it in a GitHub Secret, then use the secret's ID in the command.
### Dockerfile: Using Secrets
When using a Dockerfile to build a .NET project that uses the Telerik NuGet server, you'll need a safe and secure way to handle your NuGet crednetials and your Telerik License Key. This can be done my mounting a Docker secret.
In your GitHub Actions workflow, you can define and set docker secrets in the docker build step. In the following example, notice how we are setting two docker secrets (`nuget-sec` and `license-sec`) using the values from GitHub secrets.
```yaml
- uses: docker/build-push-action@v3
with:
secrets: |
nuget-sec=${{secrets.MY_NUGET_KEY}}
license-sec=${{secrets.MY_TELERIK_LICENSE_KEY}}
```
Now, inside the Dockerfile, you can mount and use those secrets. See Stage 2 in the following example:
```Dockerfile
### STAGE 1 ###
FROM --platform=$BUILDPLATFORM mcr.microsoft.com/dotnet/aspnet:10.0 AS base
WORKDIR /app
### STAGE 2 ###
FROM mcr.microsoft.com/dotnet/sdk:10.0 AS build
WORKDIR /src
COPY . .
# STEP 1. Mount the 'nuget-sec' secret, then:
# a. add the Telerik package source
# b. restore packages
RUN --mount=type=secret,id=nuget-sec,required \
dotnet nuget add source 'https://nuget.telerik.com/v3/index.json' -n "Telerik_v3_Feed" -u "api-key" -p "$(cat /run/secrets/nuget-sec)" --store-password-in-clear-text \
&& \
dotnet restore "MyBlazorApp.csproj"
# STEP 2. Mount the "license-sec" secret, then:
# a. create the license file
# b. build the project
# c. delete the file so you don't distribute it in your image (important!)
RUN --mount=type=secret,id=license-key,required \
mkdir -p ~/.telerik && echo "$(cat /run/secrets/license-sec)" > ~/.telerik/telerik-license.txt \
&& \
dotnet publish "Researcher.Web/Researcher.Web.csproj" -o /app/publish /p:UseAppHost=false --no-restore --self-contained false \
&& \
rm -rf ~/.telerik
### STAGE 3 ###
# Build final from base, but copy ONLY THE PUBLISH ARTIFACTS from stage 2
FROM base AS final
WORKDIR /app
COPY --from=build /app/publish .
ENTRYPOINT ["dotnet", "MyBlazorApp.dll"]
```
> [!CAUTION]
> Pay attention to whether or not you are including any secrets in your final image. You can run your container to explore the files (and env vars in Exec) to make sure.
### Telerik License Approaches
Depending on how you're building our code, there are several ways to introduce the Telerik License Key at the right time for the build. Let me show you two; variable and file.
- [Approach 1 - Using an Environment Variable](https://github.com/LanceMcCarthy/DevOpsExamples?tab=readme-ov-file#approach-1---using-a-variable)
- [Approach 2 - Using a License File](https://github.com/LanceMcCarthy/DevOpsExamples?tab=readme-ov-file#approach-2---using-a-file)
- [In a YAML Pipeline](https://github.com/LanceMcCarthy/DevOpsExamples?tab=readme-ov-file#yaml-pipeline)
- [In a Classic Pipeline](https://github.com/LanceMcCarthy/DevOpsExamples?tab=readme-ov-file#classic-pipeline)
- [Approach 1 - Using a Variable](https://github.com/LanceMcCarthy/DevOpsExamples#approach-1---using-a-variable)
- [Approach 2 - Using a File](https://github.com/LanceMcCarthy/DevOpsExamples#approach-2---using-a-file)
- [Secure File - YAML Pipeline](https://github.com/LanceMcCarthy/DevOpsExamples#secure-file---yaml-pipeline)
- [Secure File - Classic Pipeline](https://github.com/LanceMcCarthy/DevOpsExamples#secure-file---classic-pipeline)
- [Scenario 1 - Task With Env Vars Inputs](https://github.com/LanceMcCarthy/DevOpsExamples#scenario-1---task-with-env-var-inputs)
- [Scenario 2 - Task Without Env Var Inputs](https://github.com/LanceMcCarthy/DevOpsExamples#scenario-2---task-without-env-var-inputs)
- [Scenario 3 - Move Secure File](https://github.com/LanceMcCarthy/DevOpsExamples#scenario-3---move-secure-file)
#### Approach 1 - Using a Variable
This is by far the easiest and safest way. You can use a secret (GitHub Action secret or AzDO Variable secret) and set the `TELERIK_LICENSE` environment variable before the project is compiled.
In a YAML workflow/pipeline, you can set the environment variable at the beginning of the job or on a step that needs it.
GH Actions
```yaml
- run: dotnet publish MyApp.csproj -o /app/publish /p:UseAppHost=false --no-restore
env:
TELERIK_LICENSE: ${{secrets.TELERIK_LICENSE_KEY}}
```
Azure Pipelines YAML
```yaml
- powershell: dotnet publish MyApp.csproj -o /app/publish /p:UseAppHost=false --no-restore
displayName: 'Build and publish the project'
env:
TELERIK_LICENSE: $(MY_TELERIK_LICENSE_KEY) # AzDO pipeline secret variable
```
If you're using classic pipelines, you can use a pipeline variable:
![Image](https://github.com/user-attachments/assets/bcdcc8c3-8ec7-43af-8452-4bace4e8ee83)
> [!IMPORTANT]
> License key length - If you are using a Library **Variable Group**, there is a character limit for the variable values. The only way to have a long value in the Variable Group is to link it from Azure KeyVault. If you cannot use Azure KeyVault, then use a normal pipeline variable instead (seen above) or use the Secure File approach instead (see below).
#### Approach 2 - Using a File
You have two options for a file-base option. Set the TELERIK_LICENSE_PATH variable or add a file named **telerik-license.txt** to the project directory. The licensing runtime will do a recursive check from the project directory to root, and then finally %appdata%/telerik/.
On Azure DevOps, there is a powerful feature called Secure Files. It lets you upload a file and then use it in a pipeline. Go to your Library tab, then select Secure File. After you've uploaded the Secure File to your Azure DevOps project, you can use it in a pipeline.
Here are several ways to use that Secure File.
> [!CAUTION]
> Never include the **telerik-license.txt** file inside your application/docker image!
##### YAML Pipeline
With a YAML pipeline, you can use the **DownloadSecureFile@1** task, then use `$(name.secureFilePath)` to reference it. For example:
```yaml
- task: DownloadSecureFile@1
name: DownloadTelerikLicenseFile # defining the 'name' is important
displayName: 'Download Telerik License Key File'
inputs:
secureFile: 'telerik-license.txt'
- task: MSBuild@1
displayName: 'Build Project'
inputs:
solution: 'myapp.csproj'
platform: Any CPU
configuration: Release
msbuildArguments: '/p:RestorePackages=false'
env:
# use the name.secureFilePath value to set the special TELERIK_LICENSE_PATH
TELERIK_LICENSE_PATH: $(DownloadTelerikLicenseFile.secureFilePath)
```
##### Classic Pipeline
With a classic pipeline, you can use the same `DownloadSecureFile` Task
![Image](https://github.com/user-attachments/assets/8c9f0aa4-0ef8-48a9-9805-b0686db1109c)
> [!IMPORTANT]
> Make sure you set the **reference name** which gets prefixed to the `.secureFilePath` output variable.
###### Scenario 1 - Task With Env Var Inputs
With the secure file downloaded to the runner, you can now set the **TELERIK_LICENSE_PATH** variable using `$(telerik.secureFilePath)`.
<img width="700" alt="image" src="https://github.com/user-attachments/assets/e6bb5425-a721-4307-9837-0ef2e6f8cefa" />
###### Scenario 2 - Task Without Env Var Inputs
Not all AzDO tasks have the "Environment variables" section (e.g. MsBuild task doesn't have it). To solve this, you can set a pipeline variable before that task. Using the secure file
1. Add a new Powershell or Bash task (_after_ the Download Secure File task)
2. Set the **TELERIK_LICENSE_PATH** using `task.setvariable` command with `issecret=true`and the secure file task's output variable
```powershell
# If using Powershell
Write-Host "##vso[task.setvariable variable=TELERIK_LICENSE_PATH;issecret=true]$(telerik.secureFilePath)"
# If using Bash
echo "##vso[task.setvariable variable=TELERIK_LICENSE_PATH;issecret=true]$(telerik.secureFilePath)"
```
<img width="700" alt="image" src="https://github.com/user-attachments/assets/9f1bcecd-a2d2-45f2-95b6-76bdfbda6516" />
###### Scenario 3 - Move Secure File
If you have nay trouble with the TELERIK_LICENSE_PATH variable, you can just simply move the file to the root build directory.
1. Add a new Powershell or Bash task (_after_ the Download Secure File task)
```powershell
Move-Item -Path "$(telerik.secureFilePath)" -Destination "$(Build.Repository.LocalPath)/telerik-license.txt" -Force
```
2. Build the code
3. Delete the file (so you don't accidentally include it in your distribution)
```powershell
Remove-Item -Path "$(Build.Repository.LocalPath)/telerik-license.txt" -Force
```
As you can see, there are a wide range of options. The one you choose highly depends on your environment and CI requirements.
+406
View File
@@ -0,0 +1,406 @@
trigger:
branches:
include:
- main
paths:
include:
- 'azure-pipelines.yml'
- 'src/Kendo/angular_demo/**/*'
- 'src/Console/**/*'
- 'src/Ajax/**/*'
- 'src/Blazor/**/*'
- 'src/AspNetCore/**/*'
- 'src/WinUI/**/*'
- 'src/MAUI/**/*'
- 'src/Wpf/**/*'
variables:
DOTNET_SDK_VERSION: '10.0.x'
buildConfiguration: 'Release'
nugetConfigPath: 'src/NuGet.Config'
consoleProjectPath: 'src/Console/MyDocProcApp/MyDocProcApp.csproj'
ajaxProjectPath: 'src/Ajax/MySite.sln'
blazorProjectPath: 'src/Blazor/MyBlazorApp.sln'
aspnetProjectPath: 'src/AspNetCore/MyAspNetCoreApp.sln'
winuiProjectPath: 'src/WinUI/MyDemo.sln'
mauiProjectPath: 'src/MAUI/MauiDemo.csproj'
wpfProjectPath: 'src/Wpf/MyWpfApp/MyWpfApp.csproj'
# These pipeline variables (secrets) have been set using the AzDO yaml editor at dev.azure.com.
# AKEYLESS_ACCESS_ID
# MY_TELERIK_NUGET_KEY
# MY_TELERIK_LICENSE_KEY
jobs:
# *************************************************************** #
# * KENDO ANGULAR (env var) * #
# *************************************************************** #
- job: BuildAngularAppWithVariables
pool:
vmImage: 'ubuntu-latest'
steps:
- powershell: |
# 1. Clean the angular cache, to avoid using any expired keys
rm -rf .angular/cache
# 2. Install your project dependencies
npm install
# npm install --save @progress/kendo-licensing; # if missing from package.json
#3. Activate
npx kendo-ui-license activate
#4. Build the project
npm run build
workingDirectory: "src/Kendo/angular_demo"
displayName: "Install Dependencies and Activate Kendo"
env:
# AzDO pipeline secret variable. Note: YAML variable editor supports 14k string values, classic pipelines do not!
TELERIK_LICENSE: $(MY_TELERIK_LICENSE_KEY)
# *************************************************************** #
# * KENDO ANGULAR (secure file) * #
# *************************************************************** #
- job: BuildAngularAppWithSecureFile
pool:
vmImage: 'ubuntu-latest'
steps:
# Download the license key file (this MUST have a 'name' so it can be referenced later).
- task: DownloadSecureFile@1
name: GetLicenseFileTask
displayName: 'Download SecureFile'
inputs:
secureFile: 'telerik-license.txt'
- powershell: |
# 1. Clean the angular cache, to avoid using any expired keys
rm -rf .angular/cache
# 2. Install your project dependencies
npm install
# npm install --save @progress/kendo-licensing; # if missing from package.json
#3. Activate
npx kendo-ui-license activate
#4. Build the project
npm run build
workingDirectory: "src/Kendo/angular_demo"
displayName: "Install Dependencies and Activate Kendo"
env:
TELERIK_LICENSE_PATH: $(GetLicenseFileTask.secureFilePath)
# *************************************************************** #
# * CONSOLE (without nuget.config) * #
# *************************************************************** #
- job: BuildConsoleApp_Akeyless
pool:
vmImage: 'windows-latest'
steps:
# Get an authentication token from Azure so we can authenticate with Akeyless
- task: AzureCLI@2
name: 'AzureCLI'
displayName: 'Get JWT from Azure Service Principal'
inputs:
azureSubscription: 'Azure MSA Account'
scriptType: ps
scriptLocation: inlineScript
inlineScript: |
$JWT=$(az account get-access-token --query accessToken --output tsv)
echo "##vso[task.setvariable variable=azure_jwt;isoutput=true;issecret=true]$JWT"
# Uses LanceMcCarthy/akeyless-extension-azdo to get a secret (uses azureJwt, see https://github.com/LanceMcCarthy/akeyless-extension-azdo/blob/main/docs/getting-started.md)
- task: akeyless-secrets@1
name: 'Akeyless1'
displayName: 'Get Secrets from Akeyless'
inputs:
accessid: 'p-sxfgaph9urt4om' # Auth Method ID (see https://github.com/LanceMcCarthy/akeyless-extension-azdo/blob/main/docs/getting-started.md#akeyless-setup)
azureJwt: '$(AzureCLI.azure_jwt)' # Output variable from the previous step
staticSecrets: '{"/progress/TELERIK_NUGET_KEY":"NUGET_KEY"}'
- task: UseDotNet@2
inputs:
packageType: 'sdk'
version: $(DOTNET_SDK_VERSION)
- powershell: dotnet nuget update source 'Telerik_v3_Feed' --source 'https://nuget.telerik.com/v3/index.json' --username 'api-key' --password '$(Akeyless1.NUGET_KEY)' --configfile $(nugetConfigPath) --store-password-in-clear-text
displayName: 'Update package source credentials'
- powershell: dotnet restore $(consoleProjectPath) --configfile $(nugetConfigPath)
displayName: 'restore packages'
- powershell: dotnet publish $(consoleProjectPath) -o /app/publish /p:UseAppHost=false --no-restore
displayName: 'Publish the project'
env:
# AzDO pipeline secret variable. Note: YAML variable editor supports 14k string values, classic pipelines do not!
TELERIK_LICENSE: $(MY_TELERIK_LICENSE_KEY)
# *************************************************************** #
# * CONSOLE (uses Service connection) * #
# *************************************************************** #
- job: BuildConsoleApp_ServiceConnection
pool:
vmImage: 'ubuntu-latest'
steps:
- task: UseDotNet@2
inputs:
packageType: 'sdk'
version: $(DOTNET_SDK_VERSION)
# Using Service connection for credentials
- task: DotNetCoreCLI@2
displayName: 'NuGet restore MyDocProcApp'
inputs:
command: 'restore'
projects: $(consoleProjectPath)
feedsToUse: 'config'
nugetConfigPath: $(nugetConfigPath)
externalFeedCredentials: 'Telerik_v3'
# Build the project
- task: DotNetCoreCLI@2
displayName: 'Build MyDocProcApp'
inputs:
command: 'build'
projects: $(consoleProjectPath)
configuration: $(buildConfiguration)
env:
# AzDO pipeline secret variable. Note: YAML variable editor supports 14k string values, classic pipelines do not!
TELERIK_LICENSE: $(MY_TELERIK_LICENSE_KEY)
# ************************************************************* #
# * BLAZOR * #
# ************************************************************* #
- job: BuildBlazorApp
pool:
vmImage: 'ubuntu-latest'
steps:
- task: UseDotNet@2
inputs:
packageType: 'sdk'
version: $(DOTNET_SDK_VERSION)
- powershell: dotnet nuget update source 'Telerik_v3_Feed' -s 'https://nuget.telerik.com/v3/index.json' -u 'api-key' -p '$(MY_TELERIK_NUGET_KEY)' --store-password-in-clear-text --configfile $(nugetConfigPath)
displayName: 'Update Package Source Credentials'
- powershell: dotnet restore $(blazorProjectPath) --configfile $(nugetConfigPath)
displayName: 'Restore NuGet Packages'
- powershell: dotnet build $(blazorProjectPath) --no-restore
displayName: 'Build and publish'
env:
# AzDO pipeline secret variable. Note: YAML variable editor supports 14k string values, classic pipelines do not!
TELERIK_LICENSE: $(MY_TELERIK_LICENSE_KEY)
# ************************************************************* #
# * ASPNET Core * #
# ************************************************************* #
- job: BuildAspNetCoreApp
pool:
vmImage: 'ubuntu-latest'
steps:
- task: UseDotNet@2
inputs:
packageType: 'sdk'
version: $(DOTNET_SDK_VERSION)
- powershell: dotnet nuget update source 'Telerik_v3_Feed' -s 'https://nuget.telerik.com/v3/index.json' -u 'api-key' -p '$(MY_TELERIK_NUGET_KEY)' --store-password-in-clear-text --configfile $(nugetConfigPath)
displayName: 'Update Package Source Credentials'
- powershell: dotnet restore $(aspnetProjectPath) --configfile $(nugetConfigPath)
displayName: 'Restore NuGet Packages'
- powershell: dotnet build $(aspnetProjectPath) --no-restore
displayName: 'Build the project'
env:
# AzDO pipeline secret variable. Note: YAML variable editor supports 14k string values, classic pipelines do not!
TELERIK_LICENSE: $(MY_TELERIK_LICENSE_KEY)
# *************************************************************** #
# * ASP.NET AJAX (.NET Framework - SecureFile) * #
# *************************************************************** #
- job: BuildAjaxApp
pool:
vmImage: 'windows-latest'
steps:
- task: DownloadSecureFile@1
name: DownloadTelerikLicenseFile # Note 1: Make sure a name value is set, it's referenced later.
displayName: 'Download Telerik License Key File'
inputs:
secureFile: 'telerik-license.txt'
- task: NuGetToolInstaller@1
displayName: 'Use NuGet.exe'
inputs:
versionSpec: '4.x'
# You could also use nuget.exe's update command to set the credentials of the 'Telerik_v3_Feed' source defined in the nuget.config
# - task: PowerShell@2
# displayName: 'Set Package Source Credentials'
# inputs:
# targetType: 'inline'
# script: nuget sources update -Name 'Telerik_v3_Feed' -Source 'https://nuget.telerik.com/v3/index.json' -Username 'api-key' -Password '$(MY_TELERIK_NUGET_KEY)' -ConfigFile '$(nugetConfigPath)' -StorePasswordInClearText
- task: NuGetCommand@2
displayName: 'NuGet restore'
inputs:
restoreSolution: $(ajaxProjectPath)
feedsToUse: config
nugetConfigPath: $(nugetConfigPath)
externalFeedCredentials: 'Telerik_v3'
- task: MSBuild@1
displayName: 'Build AJAX Project'
inputs:
solution: '$(ajaxProjectPath)'
platform: Any CPU
configuration: Release
msbuildArguments: '/t:Restore /p:Configuration=Release /p:RuntimeIdentifier=any'
env:
TELERIK_LICENSE_PATH: $(DownloadTelerikLicenseFile.secureFilePath) # Note 2: We use the name to reference the secureFilePath value
# ************************************************************* #
# * WinUI 3 * #
# ************************************************************* #
- job: BuildWinUI
pool:
vmImage: 'windows-latest'
steps:
- task: UseDotNet@2
inputs:
packageType: 'sdk'
version: $(DOTNET_SDK_VERSION)
- powershell: dotnet nuget update source 'Telerik_v3_Feed' -s 'https://nuget.telerik.com/v3/index.json' -u 'api-key' -p '$(MY_TELERIK_NUGET_KEY)' --store-password-in-clear-text --configfile $(nugetConfigPath)
displayName: 'Update Package Source Credentials'
- powershell: dotnet restore $(winuiProjectPath) --configfile $(nugetConfigPath)
displayName: 'Restore NuGet Packages'
- task: MSBuild@1
displayName: 'Restore RIDs'
inputs:
solution: '$(winuiProjectPath)'
platform: x64
configuration: Release
msbuildArguments: '/t:Restore /p:Configuration=Release /p:RestorePackages=true /p:RestoreRIDs=true /p:RestoreProjectStyle=PackageReference'
# Note: PFX thumbprint is intentionally set to an empty string, this overrides any previous thumbprint from the project
- task: MSBuild@1
displayName: 'Build and Create MSIX'
inputs:
solution: '$(winuiProjectPath)'
platform: x64
configuration: Release
msbuildArguments: '/p:AppxBundlePlatforms="x64|arm64"
/p:UapAppxPackageBuildMode=CI
/p:AppxBundle=Never
/p:PackageCertificateKeyFile="$(PfxDownloadStep.secureFilePath)"
/p:PackageCertificatePassword="$(PfxPassword)"
/p:PackageCertificateThumbprint=""
/p:AppxPackageDir="$(Build.ArtifactStagingDirectory)/AppPackages"
/p:GenerateAppxPackageOnBuild=true
/p:AppxPackageSigningEnabled=false
/p:SelfContained=true'
env:
TELERIK_LICENSE: $(MY_TELERIK_LICENSE_KEY)
# ************************************************************* #
# * .NET MAUI * #
# ************************************************************* #
# https://learn.microsoft.com/en-us/azure/devops/pipelines/agents/hosted?view=azure-devops&tabs=windows-images%2Cyaml#software
- job: BuildMauiApp
strategy:
matrix:
android:
TFM: 'net10.0-android'
imageName: 'windows-2025' # windows-2025
windows:
TFM: 'net10.0-windows10.0.19041.0'
imageName: 'windows-latest' # windows-2025
ios:
TFM: 'net10.0-ios'
imageName: 'macOS-latest' # macOS-15
maccatalyst:
TFM: 'net10.0-maccatalyst'
imageName: 'macOS-latest' # macOS-15
maxParallel: 4
pool:
vmImage: $(imageName)
steps:
- task: UseDotNet@2
inputs:
packageType: 'sdk'
version: $(DOTNET_SDK_VERSION)
- task: CmdLine@2
displayName: 'Set Xcode to v26.1.0'
condition: eq(variables['Agent.OS'], 'Darwin') # Only run this step on macOS
inputs:
script: |
echo '##vso[task.setvariable variable=MD_APPLE_SDK_ROOT;]'/Applications/Xcode_26.1.app
sudo xcode-select --switch /Applications/Xcode_26.1.app/Contents/Developer
- powershell: dotnet workload install maui --source https://api.nuget.org/v3/index.json
displayName: 'Install maui workloads'
- powershell: dotnet nuget update source 'Telerik_v3_Feed' -s 'https://nuget.telerik.com/v3/index.json' -u 'api-key' -p '$(MY_TELERIK_NUGET_KEY)' --store-password-in-clear-text --configfile $(nugetConfigPath)
displayName: 'Update Package Source Credentials'
- powershell: dotnet restore $(mauiProjectPath) --configfile $(nugetConfigPath)
displayName: 'Restore NuGet Packages'
- powershell: dotnet build $(mauiProjectPath) -f $(TFM) -c Debug --no-restore
displayName: 'Build $(TFM)'
env:
# AzDO pipeline secret variable. Note: YAML variable editor supports 14k string values, classic pipelines do not!
TELERIK_LICENSE: $(MY_TELERIK_LICENSE_KEY)
# ************************************************************* #
# * WPF * #
# ************************************************************* #
- job: BuildWpfApp
pool:
vmImage: windows-2025
steps:
- task: UseDotNet@2
inputs:
packageType: 'sdk'
version: $(DOTNET_SDK_VERSION)
- task: NuGetToolInstaller@1
displayName: 'Use NuGet.exe'
inputs:
versionSpec: '4.x'
- task: NuGetCommand@2
displayName: 'NuGet restore'
inputs:
restoreSolution: $(wpfProjectPath)
feedsToUse: config
nugetConfigPath: $(nugetConfigPath)
externalFeedCredentials: 'Telerik_v3'
- task: MSBuild@1
displayName: 'Build WPF'
inputs:
solution: $(wpfProjectPath)
platform: 'x86'
configuration: 'Release'
msbuildArguments: '/p:RestorePackages=true /p:OutputPath=$(Build.artifactStagingDirectory)'
env:
# AzDO pipeline secret variable. Note: YAML variable editor supports 14k string values, classic pipelines do not!
TELERIK_LICENSE: $(MY_TELERIK_LICENSE_KEY)
# - task: PublishBuildArtifacts@1
# inputs:
# PathtoPublish: '$(Build.ArtifactStagingDirectory)'
# ArtifactName: 'wpf_drop'
# publishLocation: 'Container'
+9
View File
@@ -0,0 +1,9 @@
namespace MySite.Web
{
public class Article
{
public int ID { get; set; }
public string Title { get; set; }
public string Description { get; set; }
}
}
+66
View File
@@ -0,0 +1,66 @@
<%@ Page Title="" Language="C#" MasterPageFile="~/MasterPage.Master" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="MySite.Web.Default" %>
<%@ Register TagPrefix="telerik" Namespace="Telerik.Web.UI" Assembly="Telerik.Web.UI" %>
<asp:Content ID="Content0" ContentPlaceHolderID="head" Runat="Server">
<link href="styles/default.css" rel="stylesheet" />
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="ContentPlaceHolder1" runat="Server">
<telerik:RadPageLayout runat="server" ID="RadPageLayout1">
<Rows>
<telerik:LayoutRow>
<Columns>
<telerik:LayoutColumn CssClass="jumbotron">
<h1>H1 title, font size 36px</h1>
<h2>H2 Title, font size 30 px. Duis nibh dolor, rhoncus in euismod at, feugiat id magna.
<telerik:RadButton runat="server" ID="RadButton0" Text="Button" ButtonType="SkinnedButton"></telerik:RadButton>
</h2>
</telerik:LayoutColumn>
</Columns>
</telerik:LayoutRow>
<telerik:LayoutRow>
<Columns>
<telerik:LayoutColumn HiddenMd="true" HiddenSm="true" HiddenXs="true">
<h3>H3 text, font size 24 px </h3>
Ut aliquam elit eget quam tincidunt, et aliquam libero congue. Phasellus aliquet sed quam vitae dictum. Aliquam erat volutpat. Morbi accumsan a mi quis pretium.
</telerik:LayoutColumn>
</Columns>
</telerik:LayoutRow>
</Rows>
</telerik:RadPageLayout>
</asp:Content>
<asp:Content ID="Content3" ContentPlaceHolderID="ContentPlaceHolder2" runat="Server">
<telerik:RadPageLayout runat="server" ID="Content1">
<Rows>
<telerik:LayoutRow>
<Columns>
<telerik:LayoutColumn Span="4" SpanMd="12" SpanSm="12" HiddenXs="true">
<h4>H4 text font size 18 px.</h4>
<p><strong>Main content text font size 16px</strong>, aliquam turpis sed nisl mattis sagittis. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Ut vitae sapien metus. In hac habitasse platea dictumst. Aenean velit mauris, lobortis eu lacinia sed</p>
<p>Nullam facilisis neque ut aliquet imperdiet. Mauris ut odio augue. Curabitur in mi ac odio vestibulum lobortis. </p>
<telerik:RadButton runat="server" ID="RadButton1" Text="Button" ButtonType="SkinnedButton"></telerik:RadButton>
</telerik:LayoutColumn>
<telerik:LayoutColumn Span="4" SpanMd="12" SpanSm="12" HiddenXs="true">
<h4>H4 text font size 18 px.</h4>
<p><strong>Main content text font size 16px</strong>, aliquam turpis sed nisl mattis sagittis. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Ut vitae sapien metus. In hac habitasse platea dictumst. Aenean velit mauris, lobortis eu lacinia sed</p>
<p>Nullam facilisis neque ut aliquet imperdiet. Mauris ut odio augue. Curabitur in mi ac odio vestibulum lobortis. </p>
<telerik:RadButton runat="server" ID="RadButton2" Text="Button" ButtonType="SkinnedButton"></telerik:RadButton>
</telerik:LayoutColumn>
<telerik:LayoutColumn Span="4" SpanMd="12" SpanSm="12" HiddenXs="true">
<h4>H4 text font size 18 px.</h4>
<p><strong>Main content text font size 16px</strong>, aliquam turpis sed nisl mattis sagittis. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Ut vitae sapien metus. In hac habitasse platea dictumst. Aenean velit mauris, lobortis eu lacinia sed</p>
<p>Nullam facilisis neque ut aliquet imperdiet. Mauris ut odio augue. Curabitur in mi ac odio vestibulum lobortis. </p>
<telerik:RadButton runat="server" ID="RadButton3" Text="Button" ButtonType="SkinnedButton"></telerik:RadButton>
</telerik:LayoutColumn>
</Columns>
</telerik:LayoutRow>
</Rows>
</telerik:RadPageLayout>
</asp:Content>
+17
View File
@@ -0,0 +1,17 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
namespace MySite.Web
{
public partial class Default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
}
}
}
+71
View File
@@ -0,0 +1,71 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace MySite.Web
{
public partial class Default
{
/// <summary>
/// RadPageLayout1 control.
/// </summary>
/// <remarks>
/// Auto-generated field.
/// To modify move field declaration from designer file to code-behind file.
/// </remarks>
protected global::Telerik.Web.UI.RadPageLayout RadPageLayout1;
/// <summary>
/// RadButton0 control.
/// </summary>
/// <remarks>
/// Auto-generated field.
/// To modify move field declaration from designer file to code-behind file.
/// </remarks>
protected global::Telerik.Web.UI.RadButton RadButton0;
/// <summary>
/// Content1 control.
/// </summary>
/// <remarks>
/// Auto-generated field.
/// To modify move field declaration from designer file to code-behind file.
/// </remarks>
protected global::Telerik.Web.UI.RadPageLayout Content1;
/// <summary>
/// RadButton1 control.
/// </summary>
/// <remarks>
/// Auto-generated field.
/// To modify move field declaration from designer file to code-behind file.
/// </remarks>
protected global::Telerik.Web.UI.RadButton RadButton1;
/// <summary>
/// RadButton2 control.
/// </summary>
/// <remarks>
/// Auto-generated field.
/// To modify move field declaration from designer file to code-behind file.
/// </remarks>
protected global::Telerik.Web.UI.RadButton RadButton2;
/// <summary>
/// RadButton3 control.
/// </summary>
/// <remarks>
/// Auto-generated field.
/// To modify move field declaration from designer file to code-behind file.
/// </remarks>
protected global::Telerik.Web.UI.RadButton RadButton3;
}
}
+88
View File
@@ -0,0 +1,88 @@
<%@ Page Title="" Language="C#" MasterPageFile="~/MasterPageSingleMenu.Master" AutoEventWireup="true" CodeBehind="Grid.aspx.cs" Inherits="MySite.Web.Grid" %>
<%@ Register TagPrefix="telerik" Namespace="Telerik.Web.UI" Assembly="Telerik.Web.UI" %>
<asp:Content ID="Content1" ContentPlaceHolderID="head" runat="Server">
<link href="styles/grid.css" rel="stylesheet" />
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="ContentPlaceHolder1" runat="Server">
<telerik:RadPageLayout runat="server" ID="JumbotronLayout" CssClass="jumbotron" GridType="Fluid">
<Rows>
<telerik:LayoutRow>
<Columns>
<telerik:LayoutColumn Span="10" SpanMd="12" SpanSm="12" SpanXs="12">
<h1>H1 title, font size 36px. Duis nibh dolor, rhoncus in euismod at, feugiat id magna.</h1>
<h2>H2 Title, font size 30 px.</h2>
<telerik:RadButton runat="server" ID="RadButton0" Text="Button" ButtonType="SkinnedButton"></telerik:RadButton>
</telerik:LayoutColumn>
<telerik:LayoutColumn Span="2" HiddenMd="true" HiddenSm="true" HiddenXs="true">
<img src="images/Thumbnails/Desert.jpg" />
</telerik:LayoutColumn>
</Columns>
</telerik:LayoutRow>
</Rows>
</telerik:RadPageLayout>
</asp:Content>
<asp:Content ID="Content3" ContentPlaceHolderID="ContentPlaceHolder2" runat="Server">
<telerik:RadAjaxPanel ID="RadAjaxPanel1" ClientEvents-OnRequestStart="onRequestStart" runat="server" CssClass="grid_wrapper">
<telerik:RadGrid ID="RadGrid1" runat="server" PageSize="10" PagerStyle-PageButtonCount="5"
OnNeedDataSource="RadGrid1_NeedDataSource" OnItemCreated="RadGrid1_ItemCreated" OnItemDataBound="RadGrid1_ItemDataBound"
OnUpdateCommand="RadGrid1_UpdateCommand" OnInsertCommand="RadGrid1_InsertCommand" OnDeleteCommand="RadGrid1_DeleteCommand"
AllowPaging="True" AllowSorting="true" ShowGroupPanel="true" RenderMode="Auto">
<GroupingSettings ShowUnGroupButton="true" />
<ExportSettings ExportOnlyData="true" IgnorePaging="true"></ExportSettings>
<MasterTableView AutoGenerateColumns="False"
AllowFilteringByColumn="true" TableLayout="Fixed"
DataKeyNames="ID" CommandItemDisplay="Top"
InsertItemPageIndexAction="ShowItemOnFirstPage">
<CommandItemSettings ShowExportToCsvButton="true" ShowExportToExcelButton="true" ShowExportToPdfButton="true" ShowExportToWordButton="true" />
<Columns>
<telerik:GridBoundColumn DataField="Name" HeaderText="Name" SortExpression="Name"
UniqueName="Name">
<HeaderStyle Width="150px" />
</telerik:GridBoundColumn>
<telerik:GridNumericColumn DataField="Age" HeaderText="Age" SortExpression="Age"
UniqueName="Age">
<HeaderStyle Width="150px" />
</telerik:GridNumericColumn>
<telerik:GridDateTimeColumn DataField="BirthDate" HeaderText="BirthDate" SortExpression="BirthDate"
UniqueName="BirthDate" PickerType="DatePicker" DataFormatString="{0:MM/dd/yyyy}">
<HeaderStyle Width="150px" />
</telerik:GridDateTimeColumn>
<telerik:GridRatingColumn DataField="Rating" HeaderText="Rating" SortExpression="Rating"
UniqueName="Rating" GroupByExpression="Rating Group By Rating">
<HeaderStyle Width="150px" />
</telerik:GridRatingColumn>
<telerik:GridDropDownColumn DataField="City" HeaderText="City" SortExpression="City"
ListDataMember="City" ListTextField="City"
UniqueName="City" DropDownControlType="RadComboBox">
<HeaderStyle Width="150px" />
</telerik:GridDropDownColumn>
<telerik:GridEditCommandColumn UniqueName="EditColumn" HeaderText="Edit Command Column">
<HeaderStyle Width="70px" />
</telerik:GridEditCommandColumn>
<telerik:GridButtonColumn CommandName="Delete" Text="Delete" UniqueName="DeleteColumn" HeaderText="Delete Command Column">
<HeaderStyle Width="70px" />
</telerik:GridButtonColumn>
</Columns>
</MasterTableView>
<ClientSettings AllowColumnsReorder="true" AllowColumnHide="true" AllowDragToGroup="true">
<Selecting AllowRowSelect="true" />
<Scrolling AllowScroll="true" UseStaticHeaders="true" />
</ClientSettings>
</telerik:RadGrid>
</telerik:RadAjaxPanel>
<telerik:RadCodeBlock runat="server">
<script type="text/javascript">
function onRequestStart(sender, args) {
if (args.get_eventTarget().indexOf("Button") >= 0) {
args.set_enableAjax(false);
}
}
</script>
</telerik:RadCodeBlock>
</asp:Content>
+161
View File
@@ -0,0 +1,161 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Web.UI.WebControls;
using Telerik.Web.UI;
namespace MySite.Web
{
public partial class Grid : System.Web.UI.Page
{
public DataTable Sellers
{
get
{
DataTable data = Session["Data"] as DataTable;
if (data == null)
{
data = GetSellers();
Session["Data"] = data;
}
return data;
}
}
public DataTable GetSellers()
{
DataTable data = new DataTable();
data.Columns.Add("ID", typeof(int));
data.Columns.Add("Name");
data.Columns.Add("Age", typeof(int)).DefaultValue = 0;
data.Columns.Add("BirthDate", typeof(DateTime));
data.Columns.Add("Rating", typeof(int)).DefaultValue = 0;
data.Columns.Add("City");
data.PrimaryKey = new DataColumn[] { data.Columns["ID"] };
List<string> firstNames = new List<string>() { "Nancy", "Andrew", "Janet", "Margaret", "Steven", "Michael", "Robert", "Laura", "Anne", "Nige" };
List<string> cities = this.GetCities();
List<DateTime> birthDates = new List<DateTime>() { DateTime.Parse("1948/12/08"), DateTime.Parse("1952/02/19"), DateTime.Parse("1963/08/30"), DateTime.Parse("1937/09/19"), DateTime.Parse("1955/03/04"), DateTime.Parse("1963/07/02"), DateTime.Parse("1960/05/29"), DateTime.Parse("1958/01/09"), DateTime.Parse("1966/01/27"), DateTime.Parse("1966/03/27") };
Random random = new Random();
for (int i = 0; i < 84; i++)
{
DateTime birthDate = birthDates[random.Next(birthDates.Count - 1)];
data.Rows.Add(
random.Next(10000, int.MaxValue),
firstNames[random.Next(firstNames.Count - 1)],
DateTime.Now.Year - birthDate.Year, birthDate, random.Next(1, 5),
cities[random.Next(cities.Count - 1)]);
}
return data;
}
public List<string> GetCities()
{
return new List<string>()
{
"Seattle",
"Tacoma",
"Kirkland",
"Redmond",
"London",
"Philadelphia",
"New York",
"Seattle",
"London",
"Boston"
};
}
protected void RadGrid1_NeedDataSource(object sender, Telerik.Web.UI.GridNeedDataSourceEventArgs e)
{
RadGrid1.DataSource = this.Sellers;
}
protected void RadGrid1_UpdateCommand(object sender, Telerik.Web.UI.GridCommandEventArgs e)
{
Hashtable table = new Hashtable();
(e.Item as GridEditableItem).ExtractValues(table);
DataRow row = this.Sellers.Rows.Find((e.Item as GridEditableItem).GetDataKeyValue("ID"));
foreach (string key in table.Keys)
{
row[key] = table[key] ?? DBNull.Value;
}
DateTime date;
if (DateTime.TryParse((row["BirthDate"].ToString()), out date))
{
row["Age"] = DateTime.Now.Year - date.Year;
}
else
{
row["Age"] = 0;
}
}
protected void RadGrid1_InsertCommand(object sender, GridCommandEventArgs e)
{
Hashtable table = new Hashtable();
(e.Item as GridEditableItem).ExtractValues(table);
DataRow row = this.Sellers.NewRow();
foreach (string key in table.Keys)
{
if (table[key] != null)
{
row[key] = table[key];
}
}
row["ID"] = new Random().Next(int.MaxValue);
DateTime date;
if (DateTime.TryParse((row["BirthDate"].ToString()), out date))
{
row["Age"] = DateTime.Now.Date.Year - date.Year;
}
this.Sellers.Rows.InsertAt(row, 0);
}
protected void RadGrid1_DeleteCommand(object sender, GridCommandEventArgs e)
{
this.Sellers.Rows.Remove(this.Sellers.Rows.Find((e.Item as GridEditableItem).GetDataKeyValue("ID")));
}
protected void RadGrid1_ItemDataBound(object sender, GridItemEventArgs e)
{
RadComboBox comboBox = e.Item.FindControl("RCB_City") as RadComboBox;
if (comboBox != null)
{
if (!(e.Item.DataItem is GridInsertionObject))
{
comboBox.SelectedValue = (e.Item.DataItem as DataRowView)["City"].ToString();
}
comboBox.DataTextField = string.Empty;
comboBox.DataSource = this.GetCities();
comboBox.DataBind();
if (this.RadGrid1.ResolvedRenderMode == RenderMode.Mobile)
{
(e.Item.FindControl("TB_Age") as WebControl).Enabled = false;
}
else
{
((e.Item as GridEditableItem)["Age"].Controls[0] as WebControl).Enabled = false;
}
}
}
protected void RadGrid1_ItemCreated(object sender, GridItemEventArgs e)
{
GridHeaderItem headerItem = e.Item as GridHeaderItem;
if (headerItem != null)
{
headerItem["EditColumn"].Text = string.Empty;
headerItem["DeleteColumn"].Text = string.Empty;
}
}
}
}
+53
View File
@@ -0,0 +1,53 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace MySite.Web
{
public partial class Grid
{
/// <summary>
/// JumbotronLayout control.
/// </summary>
/// <remarks>
/// Auto-generated field.
/// To modify move field declaration from designer file to code-behind file.
/// </remarks>
protected global::Telerik.Web.UI.RadPageLayout JumbotronLayout;
/// <summary>
/// RadButton0 control.
/// </summary>
/// <remarks>
/// Auto-generated field.
/// To modify move field declaration from designer file to code-behind file.
/// </remarks>
protected global::Telerik.Web.UI.RadButton RadButton0;
/// <summary>
/// RadAjaxPanel1 control.
/// </summary>
/// <remarks>
/// Auto-generated field.
/// To modify move field declaration from designer file to code-behind file.
/// </remarks>
protected global::Telerik.Web.UI.RadAjaxPanel RadAjaxPanel1;
/// <summary>
/// RadGrid1 control.
/// </summary>
/// <remarks>
/// Auto-generated field.
/// To modify move field declaration from designer file to code-behind file.
/// </remarks>
protected global::Telerik.Web.UI.RadGrid RadGrid1;
}
}
+11
View File
@@ -0,0 +1,11 @@
namespace MySite.Web
{
public class Image
{
public int ID { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public string ImageUrl { get; set; }
public string ThumbnailUrl { get; set; }
}
}
+67
View File
@@ -0,0 +1,67 @@
<%@ Page Title="" Language="C#" MasterPageFile="~/MasterPageSingleMenu.Master" AutoEventWireup="true" CodeBehind="ListView.aspx.cs" Inherits="MySite.Web.ListView" %>
<%@ Register TagPrefix="telerik" Namespace="Telerik.Web.UI" Assembly="Telerik.Web.UI" %>
<asp:Content ID="Content1" ContentPlaceHolderID="head" runat="Server">
<script src="scripts/scripts.js"></script>
<link href="styles/listView.css" rel="stylesheet" />
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="ContentPlaceHolder1" runat="Server">
<telerik:RadPageLayout runat="server" ID="JumbotronLayout" CssClass="jumbotron" GridType="Fluid">
<Rows>
<telerik:LayoutRow>
<Columns>
<telerik:LayoutColumn Span="10" SpanMd="12" SpanSm="12" SpanXs="12">
<h1>H1 title, font size 36px. Duis nibh dolor, rhoncus in euismod at, feugiat id magna.</h1>
<h2>H2 Title, font size 30 px.</h2>
<telerik:RadButton runat="server" ID="RadButton0" Text="Button" ButtonType="SkinnedButton"></telerik:RadButton>
</telerik:LayoutColumn>
<telerik:LayoutColumn Span="2" HiddenMd="true" HiddenSm="true" HiddenXs="true">
<img src="images/Thumbnails/Desert.jpg" />
</telerik:LayoutColumn>
</Columns>
</telerik:LayoutRow>
</Rows>
</telerik:RadPageLayout>
</asp:Content>
<asp:Content ID="Content3" ContentPlaceHolderID="ContentPlaceHolder2" runat="Server">
<telerik:RadListView runat="server" OnItemDataBound="RadListViewImages_ItemDataBound" OnNeedDataSource="RadListViewImages_NeedDataSource" ID="RadListViewImages" AllowPaging="true" PageSize="3">
<LayoutTemplate>
<div class="listView1">
<asp:Panel ID="itemPlaceholder" runat="server">
</asp:Panel>
</div>
</LayoutTemplate>
<ItemTemplate>
<div class="listViewItem" onclick="example.imageClicked('<%# Eval("ID") %>')">
<asp:Image ImageUrl='<%# Eval("ThumbnailUrl") %>' Width="200px" Height="150px" runat="server" ToolTip="Click to view larger image" />
<p>
<asp:Literal runat="server" ID="LabelShortDescription"></asp:Literal></p>
</div>
</ItemTemplate>
</telerik:RadListView>
<hr />
<telerik:RadListView runat="server" OnNeedDataSource="RadListViewArticles_NeedDataSource" ID="RadListViewArticles" AllowPaging="true" PageSize="4">
<LayoutTemplate>
<div class="listView2">
<asp:Panel ID="itemPlaceholder" runat="server">
</asp:Panel>
</div>
</LayoutTemplate>
<ItemTemplate>
<div class="listViewItem article">
<h4><%# Eval("Title") %></h4>
<%# Eval("Description") %>
</div>
</ItemTemplate>
</telerik:RadListView>
<telerik:RadLightBox DataImageUrlField="ImageUrl" DataDescriptionField="Description" DataTitleField="Name" runat="server" ID="RadLightBoxImageDetails">
<ClientSettings>
<ClientEvents OnLoad="example.radLightBoxLoad" />
</ClientSettings>
</telerik:RadLightBox>
</asp:Content>
+95
View File
@@ -0,0 +1,95 @@
using System;
using System.Collections.Generic;
using System.Web.UI.WebControls;
using Telerik.Web.UI;
namespace MySite.Web
{
public partial class ListView : System.Web.UI.Page
{
public List<Image> Images
{
get
{
List<Image> data = Session["DataImages"] as List<Image>;
if (data == null)
{
data = GetImages();
Session["DataImages"] = data;
}
return data;
}
}
public List<Image> GetImages()
{
return new List<Image>() {
new Image() { ID=1, Name="Chrysanthemum", ImageUrl="~/images/Chrysanthemum.jpg", ThumbnailUrl="~/images/Thumbnails/Chrysanthemum.jpg", Description="Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s"},
new Image() { ID=2, Name="Desert", ImageUrl="~/images/Desert.jpg", ThumbnailUrl="~/images/Thumbnails/Desert.jpg", Description="Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s"},
new Image() { ID=3, Name="Hydrangeas", ImageUrl="~/images/Hydrangeas.jpg", ThumbnailUrl="~/images/Thumbnails/Hydrangeas.jpg", Description="The point of using Lorem Ipsum is that it has a more-or-less normal distribution of letters, as opposed to using 'Content here, content here', making it look like readable English."},
new Image() { ID=4, Name="Jellyfish", ImageUrl="~/images/Jellyfish.jpg", ThumbnailUrl="~/images/Thumbnails/Jellyfish.jpg", Description="Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s"},
new Image() { ID=5, Name="Koala", ImageUrl="~/images/Koala.jpg", ThumbnailUrl="~/images/Thumbnails/Koala.jpg", Description="The point of using Lorem Ipsum is that it has a more-or-less normal distribution of letters, as opposed to using 'Content here, content here', making it look like readable English"},
new Image() { ID=6, Name="Lighthouse", ImageUrl="~/images/Lighthouse.jpg", ThumbnailUrl="~/images/Thumbnails/Lighthouse.jpg", Description="Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s"},
new Image() { ID=7, Name="Penguins", ImageUrl="~/images/Penguins.jpg", ThumbnailUrl="~/images/Thumbnails/Penguins.jpg", Description="The point of using Lorem Ipsum is that it has a more-or-less normal distribution of letters, as opposed to using 'Content here, content here', making it look like readable English"},
new Image() { ID=8, Name="Tulips", ImageUrl="~/images/Tulips.jpg", ThumbnailUrl="~/images/Thumbnails/Tulips.jpg", Description="Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s"}
};
}
public List<Article> Articles
{
get
{
List<Article> data = Session["DataArticles"] as List<Article>;
if (data == null)
{
data = GetArticles();
Session["DataArticles"] = data;
}
return data;
}
}
public List<Article> GetArticles()
{
return new List<Article>() {
new Article(){ ID=1, Title="Phasellus auctor nisi dolor", Description="Donec aliquam turpis sed nisl mattis sagittis. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Ut vitae sapien metus. In hac habitasse platea dictumst. Aenean velit mauris, lobortis eu lacinia sed, imperdiet quis dui. Vestibulum ut metus sagittis dui lacinia mollis ornare eget urna. Mauris vehicula scelerisque sagittis"},
new Article(){ ID=1, Title="In magna mi, dapibus ut tortor", Description="Nullam facilisis neque ut aliquet imperdiet. Mauris ut odio augue. Curabitur in mi ac odio vestibulum lobortis. Donec sed mollis nibh, vitae varius lorem. Fusce ac neque lacinia dui facilisis posuere. Fusce pharetra rhoncus vulputate"},
new Article(){ ID=1, Title="Aenean ut lacus enim", Description="Aenean euismod est tortor, et pharetra mauris ultricies ut. Vivamus fringilla libero nec est tincidunt, sit amet venenatis felis accumsan. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae"},
new Article(){ ID=1, Title="Aenean luctus bibendum", Description="Mauris blandit sit amet diam eget dictum. Ut sit amet tortor sit amet nibh elementum scelerisque. Nullam felis turpis, suscipit ut nunc at, euismod condimentum massa. Ut finibus odio vitae euismod dapibus. Fusce luctus elit leo, at ultrices orci imperdiet quis"},
new Article(){ ID=1, Title="Lorem ipsum dolor sit amet", Description="Etiam nisi felis, ullamcorper et sagittis sed, posuere et lorem. Mauris non rutrum tortor. Suspendisse eu leo nec justo facilisis imperdiet sed vel felis. Nullam eros urna, efficitur in eros at, interdum iaculis massa"}
};
}
protected void RadListViewImages_NeedDataSource(object sender, Telerik.Web.UI.RadListViewNeedDataSourceEventArgs e)
{
RadListViewImages.DataSource = Images;
}
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
RadLightBoxImageDetails.DataSource = Images;
RadLightBoxImageDetails.DataBind();
}
protected void RadListViewImages_ItemDataBound(object sender, Telerik.Web.UI.RadListViewItemEventArgs e)
{
RadListViewDataItem item = e.Item as RadListViewDataItem;
string description = (item.DataItem as Image).Description;
if (description.Length > 100)
{
description = description.Substring(0, 97) + "...";
(item.FindControl("LabelShortDescription") as Literal).Text = description;
}
}
protected void RadListViewArticles_NeedDataSource(object sender, RadListViewNeedDataSourceEventArgs e)
{
RadListViewArticles.DataSource = Articles;
}
}
}
+62
View File
@@ -0,0 +1,62 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace MySite.Web
{
public partial class ListView
{
/// <summary>
/// JumbotronLayout control.
/// </summary>
/// <remarks>
/// Auto-generated field.
/// To modify move field declaration from designer file to code-behind file.
/// </remarks>
protected global::Telerik.Web.UI.RadPageLayout JumbotronLayout;
/// <summary>
/// RadButton0 control.
/// </summary>
/// <remarks>
/// Auto-generated field.
/// To modify move field declaration from designer file to code-behind file.
/// </remarks>
protected global::Telerik.Web.UI.RadButton RadButton0;
/// <summary>
/// RadListViewImages control.
/// </summary>
/// <remarks>
/// Auto-generated field.
/// To modify move field declaration from designer file to code-behind file.
/// </remarks>
protected global::Telerik.Web.UI.RadListView RadListViewImages;
/// <summary>
/// RadListViewArticles control.
/// </summary>
/// <remarks>
/// Auto-generated field.
/// To modify move field declaration from designer file to code-behind file.
/// </remarks>
protected global::Telerik.Web.UI.RadListView RadListViewArticles;
/// <summary>
/// RadLightBoxImageDetails control.
/// </summary>
/// <remarks>
/// Auto-generated field.
/// To modify move field declaration from designer file to code-behind file.
/// </remarks>
protected global::Telerik.Web.UI.RadLightBox RadLightBoxImageDetails;
}
}
+108
View File
@@ -0,0 +1,108 @@
<%@ Master Language="C#" AutoEventWireup="true" CodeBehind="MasterPage.master.cs" Inherits="MySite.Web.MasterPage" %>
<%@ Register TagPrefix="telerik" Namespace="Telerik.Web.UI" Assembly="Telerik.Web.UI" %>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title></title>
<meta name="viewport" content="initial-scale=1.0, minimum-scale=1, maximum-scale=1.0, user-scalable=no" />
<link href="styles/base.css" rel="stylesheet" />
<asp:ContentPlaceHolder ID="head" runat="server">
</asp:ContentPlaceHolder>
</head>
<body>
<form id="form1" runat="server">
<telerik:RadScriptManager runat="server"></telerik:RadScriptManager>
<div id="wrapper">
<telerik:RadPageLayout runat="server" ID="MasterLayout" GridType="Fluid">
<Rows>
<%--Header--%>
<telerik:LayoutRow CssClass="header">
<Columns>
<%--Logo--%>
<telerik:LayoutColumn Span="2" SpanMd="3" SpanSm="12" SpanXs="12">
<a href="#" class="logo">
<img src="images/logo.png" alt="site logo"/>
</a>
</telerik:LayoutColumn>
<%--Main Nav--%>
<telerik:LayoutColumn Span="10" SpanMd="9" SpanSm="12" SpanXs="12">
<telerik:RadMenu ID="RadMenu1" runat="server" RenderMode="Auto">
<Items>
<telerik:RadMenuItem Text="Home" NavigateUrl="Default.aspx" />
<telerik:RadMenuItem IsSeparator="true" />
<telerik:RadMenuItem Text="Grid Page" NavigateUrl="Grid.aspx" />
<telerik:RadMenuItem IsSeparator="true" />
<telerik:RadMenuItem Text="ListView Page" NavigateUrl="ListView.aspx" />
<telerik:RadMenuItem IsSeparator="true" />
<telerik:RadMenuItem Text="About" />
<telerik:RadMenuItem IsSeparator="true" />
<telerik:RadMenuItem Text="Projects" />
<telerik:RadMenuItem IsSeparator="true" />
<telerik:RadMenuItem Text="Dashboard" />
<telerik:RadMenuItem IsSeparator="true" />
<telerik:RadMenuItem Text="..." />
<telerik:RadMenuItem IsSeparator="true" />
<telerik:RadMenuItem Text="..." />
<telerik:RadMenuItem IsSeparator="true" />
<telerik:RadMenuItem Text="Contacts" />
</Items>
</telerik:RadMenu>
</telerik:LayoutColumn>
</Columns>
</telerik:LayoutRow>
<%--Main--%>
<telerik:LayoutRow>
<Columns>
<%--Sidebar--%>
<telerik:LayoutColumn Span="2" HiddenMd="true" HiddenSm="true" HiddenXs="true">
<telerik:RadMenu ID="RadMenu2" CssClass="sidebar" Flow="Vertical" runat="server" >
<Items>
<telerik:RadMenuItem Text="Menu item 1" />
<telerik:RadMenuItem IsSeparator="true" />
<telerik:RadMenuItem Text="Menu item 2" />
<telerik:RadMenuItem IsSeparator="true" />
<telerik:RadMenuItem Text="Menu item 3" />
<telerik:RadMenuItem IsSeparator="true" />
<telerik:RadMenuItem Text="Menu item 4" />
<telerik:RadMenuItem IsSeparator="true" />
<telerik:RadMenuItem Text="Menu item 5" />
<telerik:RadMenuItem IsSeparator="true" />
<telerik:RadMenuItem Text="Menu item 6" />
<telerik:RadMenuItem IsSeparator="true" />
<telerik:RadMenuItem Text="Menu item 7" />
<telerik:RadMenuItem IsSeparator="true" />
<telerik:RadMenuItem Text="Menu item 8" />
</Items>
</telerik:RadMenu>
</telerik:LayoutColumn>
<%--Content--%>
<telerik:CompositeLayoutColumn Span="10" SpanMd="12" SpanSm="12" SpanXs="12">
<Content>
<asp:ContentPlaceHolder ID="ContentPlaceHolder1" runat="server">
</asp:ContentPlaceHolder>
<asp:ContentPlaceHolder ID="ContentPlaceHolder2" runat="server">
</asp:ContentPlaceHolder>
</Content>
</telerik:CompositeLayoutColumn>
</Columns>
</telerik:LayoutRow>
<%--Footer--%>
<telerik:LayoutRow>
<Columns>
<telerik:LayoutColumn CssClass="footer">
<hr />
Footer: © 2002-2015 Company Inc, 201 Jones Rd, Waltham, MA 03451
</telerik:LayoutColumn>
</Columns>
</telerik:LayoutRow>
</Rows>
</telerik:RadPageLayout>
</div>
</form>
</body>
</html>
+12
View File
@@ -0,0 +1,12 @@
using System;
namespace MySite.Web
{
public partial class MasterPage : System.Web.UI.MasterPage
{
protected void Page_Load(object sender, EventArgs e)
{
}
}
}
+80
View File
@@ -0,0 +1,80 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace MySite.Web
{
public partial class MasterPage
{
/// <summary>
/// head control.
/// </summary>
/// <remarks>
/// Auto-generated field.
/// To modify move field declaration from designer file to code-behind file.
/// </remarks>
protected global::System.Web.UI.WebControls.ContentPlaceHolder head;
/// <summary>
/// form1 control.
/// </summary>
/// <remarks>
/// Auto-generated field.
/// To modify move field declaration from designer file to code-behind file.
/// </remarks>
protected global::System.Web.UI.HtmlControls.HtmlForm form1;
/// <summary>
/// MasterLayout control.
/// </summary>
/// <remarks>
/// Auto-generated field.
/// To modify move field declaration from designer file to code-behind file.
/// </remarks>
protected global::Telerik.Web.UI.RadPageLayout MasterLayout;
/// <summary>
/// RadMenu1 control.
/// </summary>
/// <remarks>
/// Auto-generated field.
/// To modify move field declaration from designer file to code-behind file.
/// </remarks>
protected global::Telerik.Web.UI.RadMenu RadMenu1;
/// <summary>
/// RadMenu2 control.
/// </summary>
/// <remarks>
/// Auto-generated field.
/// To modify move field declaration from designer file to code-behind file.
/// </remarks>
protected global::Telerik.Web.UI.RadMenu RadMenu2;
/// <summary>
/// ContentPlaceHolder1 control.
/// </summary>
/// <remarks>
/// Auto-generated field.
/// To modify move field declaration from designer file to code-behind file.
/// </remarks>
protected global::System.Web.UI.WebControls.ContentPlaceHolder ContentPlaceHolder1;
/// <summary>
/// ContentPlaceHolder2 control.
/// </summary>
/// <remarks>
/// Auto-generated field.
/// To modify move field declaration from designer file to code-behind file.
/// </remarks>
protected global::System.Web.UI.WebControls.ContentPlaceHolder ContentPlaceHolder2;
}
}
@@ -0,0 +1,54 @@
<%@ Master Language="C#" AutoEventWireup="true" CodeBehind="MasterPageSingleMenu.master.cs" Inherits="MySite.Web.MasterPageSingleMenu" %>
<%@ Register TagPrefix="telerik" Namespace="Telerik.Web.UI" Assembly="Telerik.Web.UI" %>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title></title>
<meta name="viewport" content="initial-scale=1.0, minimum-scale=1, maximum-scale=1.0, user-scalable=no" />
<link href="styles/base.css" rel="stylesheet" />
<asp:ContentPlaceHolder ID="head" runat="server">
</asp:ContentPlaceHolder>
</head>
<body>
<form id="form1" runat="server">
<telerik:RadScriptManager runat="server"></telerik:RadScriptManager>
<div id="wrapper">
<a href="#" class="logo">
<img src="images/logo.png" alt="site logo" />
</a>
<div class="header">
<telerik:RadMenu ID="RadMenu1" CssClass="mainMenu" runat="server" RenderMode="Auto">
<Items>
<telerik:RadMenuItem Text="Home" NavigateUrl="Default.aspx" />
<telerik:RadMenuItem IsSeparator="true" />
<telerik:RadMenuItem Text="Grid Page" NavigateUrl="Grid.aspx" />
<telerik:RadMenuItem IsSeparator="true" />
<telerik:RadMenuItem Text="ListView Page" NavigateUrl="ListView.aspx" />
<telerik:RadMenuItem IsSeparator="true" />
<telerik:RadMenuItem Text="About" />
<telerik:RadMenuItem IsSeparator="true" />
<telerik:RadMenuItem Text="Projects" />
<telerik:RadMenuItem IsSeparator="true" />
<telerik:RadMenuItem Text="Dashboard" />
<telerik:RadMenuItem IsSeparator="true" />
<telerik:RadMenuItem Text="..." />
<telerik:RadMenuItem IsSeparator="true" />
<telerik:RadMenuItem Text="..." />
<telerik:RadMenuItem IsSeparator="true" />
<telerik:RadMenuItem Text="Contacts" />
</Items>
</telerik:RadMenu>
</div>
<asp:ContentPlaceHolder ID="ContentPlaceHolder1" runat="server">
</asp:ContentPlaceHolder>
<asp:ContentPlaceHolder ID="ContentPlaceHolder2" runat="server">
</asp:ContentPlaceHolder>
<hr style="margin-top: 40px" />
<div class="footer">
Footer: © 2002-2015 Company Inc, 201 Jones Rd, Waltham, MA 03451
</div>
</div>
</form>
</body>
</html>
@@ -0,0 +1,17 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
namespace MySite.Web
{
public partial class MasterPageSingleMenu : System.Web.UI.MasterPage
{
protected void Page_Load(object sender, EventArgs e)
{
}
}
}
@@ -0,0 +1,62 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace MySite.Web
{
public partial class MasterPageSingleMenu
{
/// <summary>
/// head control.
/// </summary>
/// <remarks>
/// Auto-generated field.
/// To modify move field declaration from designer file to code-behind file.
/// </remarks>
protected global::System.Web.UI.WebControls.ContentPlaceHolder head;
/// <summary>
/// form1 control.
/// </summary>
/// <remarks>
/// Auto-generated field.
/// To modify move field declaration from designer file to code-behind file.
/// </remarks>
protected global::System.Web.UI.HtmlControls.HtmlForm form1;
/// <summary>
/// RadMenu1 control.
/// </summary>
/// <remarks>
/// Auto-generated field.
/// To modify move field declaration from designer file to code-behind file.
/// </remarks>
protected global::Telerik.Web.UI.RadMenu RadMenu1;
/// <summary>
/// ContentPlaceHolder1 control.
/// </summary>
/// <remarks>
/// Auto-generated field.
/// To modify move field declaration from designer file to code-behind file.
/// </remarks>
protected global::System.Web.UI.WebControls.ContentPlaceHolder ContentPlaceHolder1;
/// <summary>
/// ContentPlaceHolder2 control.
/// </summary>
/// <remarks>
/// Auto-generated field.
/// To modify move field declaration from designer file to code-behind file.
/// </remarks>
protected global::System.Web.UI.WebControls.ContentPlaceHolder ContentPlaceHolder2;
}
}
+239
View File
@@ -0,0 +1,239 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="Current" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">10.0</VisualStudioVersion>
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
<FileUpgradeFlags>
</FileUpgradeFlags>
<OldToolsVersion>3.5</OldToolsVersion>
<Use64BitIISExpress />
<UseGlobalApplicationHostFile />
<UpgradeBackupLocation />
<NuGetPackageImportStamp>
</NuGetPackageImportStamp>
</PropertyGroup>
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProductVersion>
</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>{ED3D7A4C-2473-41E1-87E7-428DBB517BD8}</ProjectGuid>
<ProjectTypeGuids>{349c5851-65df-11da-9384-00065b846f21};{fae04ec0-301f-11d3-bf4b-00c04f79efbc}</ProjectTypeGuids>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>MySite.Web</RootNamespace>
<AssemblyName>MySite.Web</AssemblyName>
<TargetFrameworkVersion>v4.8</TargetFrameworkVersion>
<UseIISExpress>true</UseIISExpress>
<IISExpressSSLPort />
<IISExpressAnonymousAuthentication />
<IISExpressWindowsAuthentication />
<IISExpressUseClassicPipelineMode />
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="PresentationCore" />
<Reference Include="PresentationFramework" />
<Reference Include="System.IO.Compression, Version=4.3.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089, processorArchitecture=MSIL">
<HintPath>..\packages\System.IO.Compression.4.3.0\lib\net46\System.IO.Compression.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="System.Numerics" />
<Reference Include="System.Web.DynamicData" />
<Reference Include="System.Web.Entity" />
<Reference Include="System.ComponentModel.DataAnnotations" />
<Reference Include="System" />
<Reference Include="System.Data" />
<Reference Include="System.Core" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="System.Web.Extensions" />
<Reference Include="System.Xaml" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Drawing" />
<Reference Include="System.Web" />
<Reference Include="System.Xml" />
<Reference Include="System.Configuration" />
<Reference Include="System.Web.Services" />
<Reference Include="System.EnterpriseServices" />
<Reference Include="Telerik.Licensing.Runtime, Version=1.6.23.0, Culture=neutral, PublicKeyToken=98bb5b04e55c09ef, processorArchitecture=MSIL">
<HintPath>..\..\packages\Telerik.Licensing.1.8.2\lib\net462\Telerik.Licensing.Runtime.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Telerik.Web.Design, Version=2025.3.825.462, Culture=neutral, PublicKeyToken=121fae78165ba3d4, processorArchitecture=MSIL">
<HintPath>..\..\packages\Telerik.UI.for.AspNet.Ajax.Net462.2026.2.519\lib\net462\Telerik.Web.Design.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Telerik.Web.Device.Detection, Version=2025.3.825.462, Culture=neutral, PublicKeyToken=121fae78165ba3d4, processorArchitecture=MSIL">
<HintPath>..\..\packages\Telerik.UI.for.AspNet.Ajax.Net462.2026.2.519\lib\net462\Telerik.Web.Device.Detection.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Telerik.Web.UI, Version=2025.3.825.462, Culture=neutral, PublicKeyToken=121fae78165ba3d4, processorArchitecture=MSIL">
<HintPath>..\..\packages\Telerik.UI.for.AspNet.Ajax.Net462.2026.2.519\lib\net462\Telerik.Web.UI.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Telerik.Web.UI.Skins, Version=2025.3.825.462, Culture=neutral, PublicKeyToken=121fae78165ba3d4, processorArchitecture=MSIL">
<HintPath>..\..\packages\Telerik.UI.for.AspNet.Ajax.Net462.2026.2.519\lib\net462\Telerik.Web.UI.Skins.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Telerik.Windows.Documents.Core, Version=2025.3.806.462, Culture=neutral, PublicKeyToken=5803cfa389c90ce7, processorArchitecture=MSIL">
<HintPath>..\..\packages\Telerik.Windows.Documents.Core.2026.2.519\lib\net462\Telerik.Windows.Documents.Core.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Telerik.Windows.Documents.Flow, Version=2025.3.806.462, Culture=neutral, PublicKeyToken=5803cfa389c90ce7, processorArchitecture=MSIL">
<HintPath>..\..\packages\Telerik.Windows.Documents.Flow.2026.2.519\lib\net462\Telerik.Windows.Documents.Flow.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Telerik.Windows.Documents.Spreadsheet, Version=2025.3.806.462, Culture=neutral, PublicKeyToken=5803cfa389c90ce7, processorArchitecture=MSIL">
<HintPath>..\..\packages\Telerik.Windows.Documents.Spreadsheet.2026.2.519\lib\net462\Telerik.Windows.Documents.Spreadsheet.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Telerik.Windows.Documents.Spreadsheet.FormatProviders.OpenXml, Version=2025.3.806.462, Culture=neutral, PublicKeyToken=5803cfa389c90ce7, processorArchitecture=MSIL">
<HintPath>..\..\packages\Telerik.Windows.Documents.Spreadsheet.FormatProviders.OpenXml.2026.2.519\lib\net462\Telerik.Windows.Documents.Spreadsheet.FormatProviders.OpenXml.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Telerik.Windows.Zip, Version=2025.3.806.462, Culture=neutral, PublicKeyToken=5803cfa389c90ce7, processorArchitecture=MSIL">
<HintPath>..\..\packages\Telerik.Windows.Zip.2026.2.519\lib\net462\Telerik.Windows.Zip.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="WindowsBase" />
</ItemGroup>
<ItemGroup>
<Content Include="MasterPage.Master" />
<Content Include="MasterPageSingleMenu.Master" />
</ItemGroup>
<ItemGroup>
<Content Include="Default.aspx" />
<Content Include="Grid.aspx" />
<Content Include="images\Chrysanthemum.jpg" />
<Content Include="images\Desert.jpg" />
<Content Include="images\Hydrangeas.jpg" />
<Content Include="images\Jellyfish.jpg" />
<Content Include="images\Koala.jpg" />
<Content Include="images\Lighthouse.jpg" />
<Content Include="images\logo.png" />
<Content Include="images\Penguins.jpg" />
<Content Include="images\Thumbnails\Chrysanthemum.jpg" />
<Content Include="images\Thumbnails\Desert.jpg" />
<Content Include="images\Thumbnails\Hydrangeas.jpg" />
<Content Include="images\Thumbnails\Jellyfish.jpg" />
<Content Include="images\Thumbnails\Koala.jpg" />
<Content Include="images\Thumbnails\Lighthouse.jpg" />
<Content Include="images\Thumbnails\Penguins.jpg" />
<Content Include="images\Thumbnails\Tulips.jpg" />
<Content Include="images\Tulips.jpg" />
<Content Include="ListView.aspx" />
<Content Include="scripts\scripts.js" />
<Content Include="styles\base.css" />
<Content Include="styles\default.css" />
<Content Include="styles\grid.css" />
<Content Include="styles\listView.css" />
</ItemGroup>
<ItemGroup>
<Compile Include="Article.cs" />
<Compile Include="Default.aspx.cs">
<DependentUpon>Default.aspx</DependentUpon>
<SubType>ASPXCodeBehind</SubType>
</Compile>
<Compile Include="Default.aspx.designer.cs">
<DependentUpon>Default.aspx</DependentUpon>
</Compile>
<Compile Include="Grid.aspx.cs">
<DependentUpon>Grid.aspx</DependentUpon>
<SubType>ASPXCodeBehind</SubType>
</Compile>
<Compile Include="Grid.aspx.designer.cs">
<DependentUpon>Grid.aspx</DependentUpon>
</Compile>
<Compile Include="Image.cs" />
<Compile Include="ListView.aspx.cs">
<DependentUpon>ListView.aspx</DependentUpon>
<SubType>ASPXCodeBehind</SubType>
</Compile>
<Compile Include="ListView.aspx.designer.cs">
<DependentUpon>ListView.aspx</DependentUpon>
</Compile>
<Compile Include="MasterPage.Master.cs">
<DependentUpon>MasterPage.Master</DependentUpon>
<SubType>ASPXCodeBehind</SubType>
</Compile>
<Compile Include="MasterPage.Master.designer.cs">
<DependentUpon>MasterPage.Master</DependentUpon>
</Compile>
<Compile Include="MasterPageSingleMenu.Master.cs">
<DependentUpon>MasterPageSingleMenu.Master</DependentUpon>
<SubType>ASPXCodeBehind</SubType>
</Compile>
<Compile Include="MasterPageSingleMenu.Master.designer.cs">
<DependentUpon>MasterPageSingleMenu.Master</DependentUpon>
</Compile>
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
<Content Include="web.config" />
<None Include="packages.config" />
<None Include="web.Debug.config">
<DependentUpon>web.config</DependentUpon>
</None>
<None Include="web.Release.config">
<DependentUpon>web.config</DependentUpon>
</None>
</ItemGroup>
<ItemGroup>
<Folder Include="App_Data\" />
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<Import Project="$(VSToolsPath)\WebApplications\Microsoft.WebApplication.targets" Condition="'$(VSToolsPath)' != ''" />
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v10.0\WebApplications\Microsoft.WebApplication.targets" Condition="false" />
<Import Project="$(VSToolsPath)\TypeScript\Microsoft.TypeScript.targets" Condition="Exists('$(VSToolsPath)\TypeScript\Microsoft.TypeScript.targets')" />
<ProjectExtensions>
<VisualStudio>
<FlavorProperties GUID="{349c5851-65df-11da-9384-00065b846f21}">
<WebProjectProperties>
<UseIIS>True</UseIIS>
<AutoAssignPort>True</AutoAssignPort>
<DevelopmentServerPort>49573</DevelopmentServerPort>
<DevelopmentServerVPath>/</DevelopmentServerVPath>
<IISUrl>http://localhost:49573/</IISUrl>
<NTLMAuthentication>False</NTLMAuthentication>
<UseCustomServer>False</UseCustomServer>
<CustomServerUrl>
</CustomServerUrl>
<SaveServerSettingsInUserFile>False</SaveServerSettingsInUserFile>
</WebProjectProperties>
</FlavorProperties>
<UserProperties UseAjaxifiedTemplates="True" UseJQuerySupport="True" />
</VisualStudio>
</ProjectExtensions>
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
<Import Project="..\..\packages\Telerik.Licensing.1.8.2\build\Telerik.Licensing.targets" Condition="Exists('..\..\packages\Telerik.Licensing.1.8.2\build\Telerik.Licensing.targets')" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('..\..\packages\Telerik.Licensing.1.8.2\build\Telerik.Licensing.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\Telerik.Licensing.1.8.2\build\Telerik.Licensing.targets'))" />
</Target>
</Project>
@@ -0,0 +1,35 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("MySite.Web")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("MySite.Web")]
[assembly: AssemblyCopyright("Copyright © 2015")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("d8c45981-3ae0-463b-9b00-1e4df634fc0b")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Revision and Build Numbers
// by using the '*' as shown below:
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]
Binary file not shown.

After

Width:  |  Height:  |  Size: 63 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 73 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

+11
View File
@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="System.IO.Compression" version="4.3.0" targetFramework="net48" />
<package id="Telerik.Licensing" version="1.8.2" targetFramework="net48" />
<package id="Telerik.UI.for.AspNet.Ajax.Net462" version="2026.2.519" targetFramework="net48" />
<package id="Telerik.Windows.Documents.Core" version="2026.2.519" targetFramework="net48" />
<package id="Telerik.Windows.Documents.Flow" version="2026.2.519" targetFramework="net48" />
<package id="Telerik.Windows.Documents.Spreadsheet" version="2026.2.519" targetFramework="net48" />
<package id="Telerik.Windows.Documents.Spreadsheet.FormatProviders.OpenXml" version="2026.2.519" targetFramework="net48" />
<package id="Telerik.Windows.Zip" version="2026.2.519" targetFramework="net48" />
</packages>
+14
View File
@@ -0,0 +1,14 @@
;(function () {
var example = window.example = window.example || {};
var lightBox;
example.imageClicked = function (id) {
lightBox.set_currentItemIndex(parseInt(id, 10) - 1);
lightBox.show();
}
example.radLightBoxLoad = function (sender, eventArgs) {
lightBox = sender;
}
})();
+97
View File
@@ -0,0 +1,97 @@
/*normalize*/
html,
body,
form {
height: 100%;
margin: 0;
padding: 0;
}
body {
font: normal 16px "Segoe UI", Arial, sans-serif;
color: #555555;
}
@media only screen and (min-width: 481px) and (max-width: 720px) {
body {
font-size: 18px;
}
}
@media only screen and (max-width: 480px) {
body {
font-size: 20px;
}
}
a {
background: transparent;
}
img {
border: 0;
}
button,
input {
-webkit-appearance: none;
cursor: pointer;
}
hr {
border-width: 1px 0 0 0;
border-color: #767676;
border-style: solid;
}
/*typography*/
h1, h2, h3, h4, h5, h6 {
line-height: normal;
}
h1, h2, h3 {
font-weight: normal;
margin: .5em 0;
}
h4, h5, h6 {
font-weight: bold;
}
h1 {
font-size: 2.25em;
}
h2 {
font-size: 1.875em;
}
h3 {
font-size: 1.5em;
}
h4 {
font-size: 1.125em
}
h5 {
font-size: 1em;
}
h6 {
font-size: .875em;
}
/*template*/
#wrapper .t-container-fluid {
max-width: 100%;
}
#wrapper .RadButton {
font-size: 1em;
}
#wrapper .RadMenu,
div.RadMenuPopup,
#wrapper .RadMenu_Default, #wrapper .RadMenu_Default a.rmLink {
font-size: 1em;
}
+38
View File
@@ -0,0 +1,38 @@
#wrapper .header {
background-color: #2a2d33;
padding: 10px 0;
margin-bottom: 40px;
}
#wrapper .header .RadMenu {
margin: 10px 0;
float: none;
}
#wrapper .logo {
margin: 10px 0;
width: 100%;
display: inline-block;
}
#wrapper .logo img {
max-width: 100%;
}
#wrapper .jumbotron {
padding: 1.875em;
padding-top: 1em;
background-color: #e9eaea;
}
#wrapper .jumbotron .RadButton {
float: right;
margin-top: .667em;
font-size: .5em;
}
#wrapper .footer {
width:100%;
padding: 30px 15px;
line-height: 30px;
}
+47
View File
@@ -0,0 +1,47 @@
#wrapper .header {
background-color: #2a2d33;
padding: 10px 0;
margin: 0;
text-align: center;
}
#wrapper .header .RadMenu {
float: none;
display: inline-block;
vertical-align: top;
}
#wrapper .logo {
padding: 10px 0;
display: block;
text-align: center;
}
#wrapper .jumbotron {
margin-bottom: 20px;
padding: 0 1.875em 1.875em 1.875em;
border: 1px solid transparent;
background-color: #e9eaea;
overflow: hidden;
}
#wrapper .jumbotron img {
float: right;
margin: 20px 0 0 0;
max-width: 100%;
}
#wrapper .jumbotron h1 {
border-bottom: 1px solid #767676;
display: inline-block;
padding-bottom: 20px;
}
#wrapper .grid_wrapper {
margin: 0 40px;
}
#wrapper .footer {
padding: 20px 15px;
line-height: 30px;
}
+85
View File
@@ -0,0 +1,85 @@
#wrapper .header {
background-color: #2a2d33;
padding: 10px 0;
margin: 0;
text-align: center;
}
@media only screen and (max-width: 720px) {
#wrapper .header {
text-align: left;
padding: 10px 2em;
}
}
#wrapper .header .RadMenu {
float: none;
display: inline-block;
vertical-align: top;
}
#wrapper .logo {
padding: 10px 0;
display: block;
text-align: center;
}
#wrapper .jumbotron {
margin-bottom: 20px;
padding: 0 1.875em 1.875em 1.875em;
border: 1px solid transparent;
background-color: #e9eaea;
overflow: hidden;
}
#wrapper .jumbotron img {
margin: 20px 0 0 0;
max-width: 100%;
}
#wrapper .jumbotron h1 {
border-bottom: 1px solid #767676;
display: inline-block;
padding-bottom: 20px;
}
#wrapper .grid_wrapper {
margin: 0 40px;
}
.listView1,
.listView2 {
padding: 0 20px;
overflow: hidden;
text-align: center;
}
#wrapper .listViewItem {
display:inline-block;
vertical-align: top;
}
#wrapper .listView1 .listViewItem {
max-width: 440px;
width: auto;
}
#wrapper .listView2 .listViewItem {
max-width: 340px;
}
* + html #wrapper .listViewItem {
display: inline;
zoom: 1;
}
#wrapper .footer {
padding: 20px 15px;
line-height: 30px;
}
.listViewItem img {
cursor: pointer;
}
div .RadMenu {
z-index: 700;
}
+30
View File
@@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- For more information on using web.config transformation visit https://go.microsoft.com/fwlink/?LinkId=125889 -->
<configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
<!--
In the example below, the "SetAttributes" transform will change the value of
"connectionString" to use "ReleaseSQLServer" only when the "Match" locator
finds an attribute "name" that has a value of "MyDB".
<connectionStrings>
<add name="MyDB"
connectionString="Data Source=ReleaseSQLServer;Initial Catalog=MyReleaseDB;Integrated Security=True"
xdt:Transform="SetAttributes" xdt:Locator="Match(name)"/>
</connectionStrings>
-->
<system.web>
<!--
In the example below, the "Replace" transform will replace the entire
<customErrors> section of your web.config file.
Note that because there is only one customErrors section under the
<system.web> node, there is no need to use the "xdt:Locator" attribute.
<customErrors defaultRedirect="GenericError.htm"
mode="RemoteOnly" xdt:Transform="Replace">
<error statusCode="500" redirect="InternalError.htm"/>
</customErrors>
-->
</system.web>
</configuration>
+31
View File
@@ -0,0 +1,31 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- For more information on using web.config transformation visit https://go.microsoft.com/fwlink/?LinkId=125889 -->
<configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
<!--
In the example below, the "SetAttributes" transform will change the value of
"connectionString" to use "ReleaseSQLServer" only when the "Match" locator
finds an attribute "name" that has a value of "MyDB".
<connectionStrings>
<add name="MyDB"
connectionString="Data Source=ReleaseSQLServer;Initial Catalog=MyReleaseDB;Integrated Security=True"
xdt:Transform="SetAttributes" xdt:Locator="Match(name)"/>
</connectionStrings>
-->
<system.web>
<compilation xdt:Transform="RemoveAttributes(debug)" />
<!--
In the example below, the "Replace" transform will replace the entire
<customErrors> section of your web.config file.
Note that because there is only one customErrors section under the
<system.web> node, there is no need to use the "xdt:Locator" attribute.
<customErrors defaultRedirect="GenericError.htm"
mode="RemoteOnly" xdt:Transform="Replace">
<error statusCode="500" redirect="InternalError.htm"/>
</customErrors>
-->
</system.web>
</configuration>
+67
View File
@@ -0,0 +1,67 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<appSettings>
<add key="Telerik.Skin" value="Bootstrap" />
<add key="Telerik.ScriptManager.TelerikCdn" value="Disabled" />
<add key="Telerik.StyleSheetManager.TelerikCdn" value="Disabled" />
<add key="Telerik.Web.UI.RenderMode" value="lightweight" />
</appSettings>
<system.web>
<compilation debug="false" targetFramework="4.8" />
<httpRuntime targetFramework="4.8" />
<pages>
<controls>
<add tagPrefix="telerik" namespace="Telerik.Web.UI" assembly="Telerik.Web.UI" />
</controls>
</pages><httpHandlers>
<add path="ChartImage.axd" type="Telerik.Web.UI.ChartHttpHandler" verb="*" validate="false" />
<add path="Telerik.Web.UI.SpellCheckHandler.axd" type="Telerik.Web.UI.SpellCheckHandler" verb="*" validate="false" />
<add path="Telerik.Web.UI.DialogHandler.aspx" type="Telerik.Web.UI.DialogHandler" verb="*" validate="false" />
<add path="Telerik.RadUploadProgressHandler.ashx" type="Telerik.Web.UI.RadUploadProgressHandler" verb="*" validate="false" />
<add path="Telerik.Web.UI.WebResource.axd" type="Telerik.Web.UI.WebResource" verb="*" validate="false" /></httpHandlers><httpModules /></system.web>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="Telerik.Windows.Documents.Flow" publicKeyToken="5803cfa389c90ce7" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-2025.2.701.462" newVersion="2025.2.701.462" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Telerik.Windows.Documents.Core" publicKeyToken="5803cfa389c90ce7" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-2025.2.701.462" newVersion="2025.2.701.462" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Telerik.Windows.Documents.Spreadsheet" publicKeyToken="5803cfa389c90ce7" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-2025.2.701.462" newVersion="2025.2.701.462" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Telerik.Windows.Documents.Spreadsheet.FormatProviders.OpenXml" publicKeyToken="5803cfa389c90ce7" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-2025.2.701.462" newVersion="2025.2.701.462" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Telerik.Licensing.Runtime" publicKeyToken="98bb5b04e55c09ef" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-1.6.40.0" newVersion="1.6.40.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>
<system.webServer>
<validation validateIntegratedModeConfiguration="false" />
<handlers>
<remove name="ChartImage_axd" />
<remove name="Telerik_Web_UI_SpellCheckHandler_axd" />
<remove name="Telerik_Web_UI_DialogHandler_aspx" />
<remove name="Telerik_RadUploadProgressHandler_ashx" />
<remove name="Telerik_Web_UI_WebResource_axd" /><add name="ChartImage_axd" path="ChartImage.axd" type="Telerik.Web.UI.ChartHttpHandler" verb="*" preCondition="integratedMode" />
<add name="Telerik_Web_UI_SpellCheckHandler_axd" path="Telerik.Web.UI.SpellCheckHandler.axd" type="Telerik.Web.UI.SpellCheckHandler" verb="*" preCondition="integratedMode" />
<add name="Telerik_Web_UI_DialogHandler_aspx" path="Telerik.Web.UI.DialogHandler.aspx" type="Telerik.Web.UI.DialogHandler" verb="*" preCondition="integratedMode" />
<add name="Telerik_RadUploadProgressHandler_ashx" path="Telerik.RadUploadProgressHandler.ashx" type="Telerik.Web.UI.RadUploadProgressHandler" verb="*" preCondition="integratedMode" />
<add name="Telerik_Web_UI_WebResource_axd" path="Telerik.Web.UI.WebResource.axd" type="Telerik.Web.UI.WebResource" verb="*" preCondition="integratedMode" />
</handlers>
</system.webServer></configuration>
+25
View File
@@ -0,0 +1,25 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.2.32526.322
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MySite.Web", "MySite.Web\MySite.Web.csproj", "{ED3D7A4C-2473-41E1-87E7-428DBB517BD8}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{ED3D7A4C-2473-41E1-87E7-428DBB517BD8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{ED3D7A4C-2473-41E1-87E7-428DBB517BD8}.Debug|Any CPU.Build.0 = Debug|Any CPU
{ED3D7A4C-2473-41E1-87E7-428DBB517BD8}.Release|Any CPU.ActiveCfg = Release|Any CPU
{ED3D7A4C-2473-41E1-87E7-428DBB517BD8}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {C7458BCE-5F5A-49A9-8C8C-8955E59F8B66}
EndGlobalSection
EndGlobal
+505
View File
@@ -0,0 +1,505 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.2.32526.322
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MySite.Web", "Ajax\MySite.Web\MySite.Web.csproj", "{ED3D7A4C-2473-41E1-87E7-428DBB517BD8}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Web", "Web", "{5D445349-E40E-4936-AE11-7C07276AC4A3}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Desktop", "Desktop", "{C5094142-9DAB-405B-9B46-DBA0AC3B9627}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Maui", "Maui", "{07F120B6-65CD-4EDB-9BC5-F47281F268B3}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MyDocProcApp", "Console\MyDocProcApp\MyDocProcApp.csproj", "{8DA97FE4-645B-4951-92A5-A740D6DD3E5C}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MyWinFormsApp", "WinForms\MyWinFormsApp\MyWinFormsApp.csproj", "{999DDEB3-4CDA-441D-B980-3BC43B654237}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MyWpfApp", "Wpf\MyWpfApp\MyWpfApp.csproj", "{2F4484CA-D992-44F2-AB3B-5C9F1AD83AB0}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MyBlazorApp", "Blazor\MyBlazorApp\MyBlazorApp.csproj", "{8D741D81-6120-472A-BEE0-45F298D74D75}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MyAspNetCoreApp", "AspNetCore\MyAspNetCoreApp\MyAspNetCoreApp.csproj", "{D75E5A16-6863-4A53-84F4-CC0360782292}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MauiDemo", "MAUI\MauiDemo.csproj", "{5FD17957-3AD1-42E5-AAC4-D9B09E0B7E14}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MyDemo", "WinUI\MyDemo\MyDemo.csproj", "{71C1E85B-1A79-C0C4-22BD-49A8DE57B8E7}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Ad-Hoc|Any CPU = Ad-Hoc|Any CPU
Ad-Hoc|ARM = Ad-Hoc|ARM
Ad-Hoc|iPhone = Ad-Hoc|iPhone
Ad-Hoc|iPhoneSimulator = Ad-Hoc|iPhoneSimulator
Ad-Hoc|x64 = Ad-Hoc|x64
Ad-Hoc|x86 = Ad-Hoc|x86
AppStore|Any CPU = AppStore|Any CPU
AppStore|ARM = AppStore|ARM
AppStore|iPhone = AppStore|iPhone
AppStore|iPhoneSimulator = AppStore|iPhoneSimulator
AppStore|x64 = AppStore|x64
AppStore|x86 = AppStore|x86
Debug|Any CPU = Debug|Any CPU
Debug|ARM = Debug|ARM
Debug|iPhone = Debug|iPhone
Debug|iPhoneSimulator = Debug|iPhoneSimulator
Debug|x64 = Debug|x64
Debug|x86 = Debug|x86
Release|Any CPU = Release|Any CPU
Release|ARM = Release|ARM
Release|iPhone = Release|iPhone
Release|iPhoneSimulator = Release|iPhoneSimulator
Release|x64 = Release|x64
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{ED3D7A4C-2473-41E1-87E7-428DBB517BD8}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU
{ED3D7A4C-2473-41E1-87E7-428DBB517BD8}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU
{ED3D7A4C-2473-41E1-87E7-428DBB517BD8}.Ad-Hoc|ARM.ActiveCfg = Release|Any CPU
{ED3D7A4C-2473-41E1-87E7-428DBB517BD8}.Ad-Hoc|ARM.Build.0 = Release|Any CPU
{ED3D7A4C-2473-41E1-87E7-428DBB517BD8}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU
{ED3D7A4C-2473-41E1-87E7-428DBB517BD8}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU
{ED3D7A4C-2473-41E1-87E7-428DBB517BD8}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU
{ED3D7A4C-2473-41E1-87E7-428DBB517BD8}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|Any CPU
{ED3D7A4C-2473-41E1-87E7-428DBB517BD8}.Ad-Hoc|x64.ActiveCfg = Release|Any CPU
{ED3D7A4C-2473-41E1-87E7-428DBB517BD8}.Ad-Hoc|x64.Build.0 = Release|Any CPU
{ED3D7A4C-2473-41E1-87E7-428DBB517BD8}.Ad-Hoc|x86.ActiveCfg = Release|Any CPU
{ED3D7A4C-2473-41E1-87E7-428DBB517BD8}.Ad-Hoc|x86.Build.0 = Release|Any CPU
{ED3D7A4C-2473-41E1-87E7-428DBB517BD8}.AppStore|Any CPU.ActiveCfg = Release|Any CPU
{ED3D7A4C-2473-41E1-87E7-428DBB517BD8}.AppStore|Any CPU.Build.0 = Release|Any CPU
{ED3D7A4C-2473-41E1-87E7-428DBB517BD8}.AppStore|ARM.ActiveCfg = Release|Any CPU
{ED3D7A4C-2473-41E1-87E7-428DBB517BD8}.AppStore|ARM.Build.0 = Release|Any CPU
{ED3D7A4C-2473-41E1-87E7-428DBB517BD8}.AppStore|iPhone.ActiveCfg = Release|Any CPU
{ED3D7A4C-2473-41E1-87E7-428DBB517BD8}.AppStore|iPhone.Build.0 = Release|Any CPU
{ED3D7A4C-2473-41E1-87E7-428DBB517BD8}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU
{ED3D7A4C-2473-41E1-87E7-428DBB517BD8}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU
{ED3D7A4C-2473-41E1-87E7-428DBB517BD8}.AppStore|x64.ActiveCfg = Release|Any CPU
{ED3D7A4C-2473-41E1-87E7-428DBB517BD8}.AppStore|x64.Build.0 = Release|Any CPU
{ED3D7A4C-2473-41E1-87E7-428DBB517BD8}.AppStore|x86.ActiveCfg = Release|Any CPU
{ED3D7A4C-2473-41E1-87E7-428DBB517BD8}.AppStore|x86.Build.0 = Release|Any CPU
{ED3D7A4C-2473-41E1-87E7-428DBB517BD8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{ED3D7A4C-2473-41E1-87E7-428DBB517BD8}.Debug|Any CPU.Build.0 = Debug|Any CPU
{ED3D7A4C-2473-41E1-87E7-428DBB517BD8}.Debug|ARM.ActiveCfg = Debug|Any CPU
{ED3D7A4C-2473-41E1-87E7-428DBB517BD8}.Debug|ARM.Build.0 = Debug|Any CPU
{ED3D7A4C-2473-41E1-87E7-428DBB517BD8}.Debug|iPhone.ActiveCfg = Debug|Any CPU
{ED3D7A4C-2473-41E1-87E7-428DBB517BD8}.Debug|iPhone.Build.0 = Debug|Any CPU
{ED3D7A4C-2473-41E1-87E7-428DBB517BD8}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{ED3D7A4C-2473-41E1-87E7-428DBB517BD8}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
{ED3D7A4C-2473-41E1-87E7-428DBB517BD8}.Debug|x64.ActiveCfg = Debug|Any CPU
{ED3D7A4C-2473-41E1-87E7-428DBB517BD8}.Debug|x64.Build.0 = Debug|Any CPU
{ED3D7A4C-2473-41E1-87E7-428DBB517BD8}.Debug|x86.ActiveCfg = Debug|Any CPU
{ED3D7A4C-2473-41E1-87E7-428DBB517BD8}.Debug|x86.Build.0 = Debug|Any CPU
{ED3D7A4C-2473-41E1-87E7-428DBB517BD8}.Release|Any CPU.ActiveCfg = Release|Any CPU
{ED3D7A4C-2473-41E1-87E7-428DBB517BD8}.Release|Any CPU.Build.0 = Release|Any CPU
{ED3D7A4C-2473-41E1-87E7-428DBB517BD8}.Release|ARM.ActiveCfg = Release|Any CPU
{ED3D7A4C-2473-41E1-87E7-428DBB517BD8}.Release|ARM.Build.0 = Release|Any CPU
{ED3D7A4C-2473-41E1-87E7-428DBB517BD8}.Release|iPhone.ActiveCfg = Release|Any CPU
{ED3D7A4C-2473-41E1-87E7-428DBB517BD8}.Release|iPhone.Build.0 = Release|Any CPU
{ED3D7A4C-2473-41E1-87E7-428DBB517BD8}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
{ED3D7A4C-2473-41E1-87E7-428DBB517BD8}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
{ED3D7A4C-2473-41E1-87E7-428DBB517BD8}.Release|x64.ActiveCfg = Release|Any CPU
{ED3D7A4C-2473-41E1-87E7-428DBB517BD8}.Release|x64.Build.0 = Release|Any CPU
{ED3D7A4C-2473-41E1-87E7-428DBB517BD8}.Release|x86.ActiveCfg = Release|Any CPU
{ED3D7A4C-2473-41E1-87E7-428DBB517BD8}.Release|x86.Build.0 = Release|Any CPU
{8DA97FE4-645B-4951-92A5-A740D6DD3E5C}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU
{8DA97FE4-645B-4951-92A5-A740D6DD3E5C}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU
{8DA97FE4-645B-4951-92A5-A740D6DD3E5C}.Ad-Hoc|ARM.ActiveCfg = Release|Any CPU
{8DA97FE4-645B-4951-92A5-A740D6DD3E5C}.Ad-Hoc|ARM.Build.0 = Release|Any CPU
{8DA97FE4-645B-4951-92A5-A740D6DD3E5C}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU
{8DA97FE4-645B-4951-92A5-A740D6DD3E5C}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU
{8DA97FE4-645B-4951-92A5-A740D6DD3E5C}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU
{8DA97FE4-645B-4951-92A5-A740D6DD3E5C}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|Any CPU
{8DA97FE4-645B-4951-92A5-A740D6DD3E5C}.Ad-Hoc|x64.ActiveCfg = Release|Any CPU
{8DA97FE4-645B-4951-92A5-A740D6DD3E5C}.Ad-Hoc|x64.Build.0 = Release|Any CPU
{8DA97FE4-645B-4951-92A5-A740D6DD3E5C}.Ad-Hoc|x86.ActiveCfg = Release|Any CPU
{8DA97FE4-645B-4951-92A5-A740D6DD3E5C}.Ad-Hoc|x86.Build.0 = Release|Any CPU
{8DA97FE4-645B-4951-92A5-A740D6DD3E5C}.AppStore|Any CPU.ActiveCfg = Release|Any CPU
{8DA97FE4-645B-4951-92A5-A740D6DD3E5C}.AppStore|Any CPU.Build.0 = Release|Any CPU
{8DA97FE4-645B-4951-92A5-A740D6DD3E5C}.AppStore|ARM.ActiveCfg = Release|Any CPU
{8DA97FE4-645B-4951-92A5-A740D6DD3E5C}.AppStore|ARM.Build.0 = Release|Any CPU
{8DA97FE4-645B-4951-92A5-A740D6DD3E5C}.AppStore|iPhone.ActiveCfg = Release|Any CPU
{8DA97FE4-645B-4951-92A5-A740D6DD3E5C}.AppStore|iPhone.Build.0 = Release|Any CPU
{8DA97FE4-645B-4951-92A5-A740D6DD3E5C}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU
{8DA97FE4-645B-4951-92A5-A740D6DD3E5C}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU
{8DA97FE4-645B-4951-92A5-A740D6DD3E5C}.AppStore|x64.ActiveCfg = Release|Any CPU
{8DA97FE4-645B-4951-92A5-A740D6DD3E5C}.AppStore|x64.Build.0 = Release|Any CPU
{8DA97FE4-645B-4951-92A5-A740D6DD3E5C}.AppStore|x86.ActiveCfg = Release|Any CPU
{8DA97FE4-645B-4951-92A5-A740D6DD3E5C}.AppStore|x86.Build.0 = Release|Any CPU
{8DA97FE4-645B-4951-92A5-A740D6DD3E5C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{8DA97FE4-645B-4951-92A5-A740D6DD3E5C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{8DA97FE4-645B-4951-92A5-A740D6DD3E5C}.Debug|ARM.ActiveCfg = Debug|Any CPU
{8DA97FE4-645B-4951-92A5-A740D6DD3E5C}.Debug|ARM.Build.0 = Debug|Any CPU
{8DA97FE4-645B-4951-92A5-A740D6DD3E5C}.Debug|iPhone.ActiveCfg = Debug|Any CPU
{8DA97FE4-645B-4951-92A5-A740D6DD3E5C}.Debug|iPhone.Build.0 = Debug|Any CPU
{8DA97FE4-645B-4951-92A5-A740D6DD3E5C}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{8DA97FE4-645B-4951-92A5-A740D6DD3E5C}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
{8DA97FE4-645B-4951-92A5-A740D6DD3E5C}.Debug|x64.ActiveCfg = Debug|Any CPU
{8DA97FE4-645B-4951-92A5-A740D6DD3E5C}.Debug|x64.Build.0 = Debug|Any CPU
{8DA97FE4-645B-4951-92A5-A740D6DD3E5C}.Debug|x86.ActiveCfg = Debug|Any CPU
{8DA97FE4-645B-4951-92A5-A740D6DD3E5C}.Debug|x86.Build.0 = Debug|Any CPU
{8DA97FE4-645B-4951-92A5-A740D6DD3E5C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{8DA97FE4-645B-4951-92A5-A740D6DD3E5C}.Release|Any CPU.Build.0 = Release|Any CPU
{8DA97FE4-645B-4951-92A5-A740D6DD3E5C}.Release|ARM.ActiveCfg = Release|Any CPU
{8DA97FE4-645B-4951-92A5-A740D6DD3E5C}.Release|ARM.Build.0 = Release|Any CPU
{8DA97FE4-645B-4951-92A5-A740D6DD3E5C}.Release|iPhone.ActiveCfg = Release|Any CPU
{8DA97FE4-645B-4951-92A5-A740D6DD3E5C}.Release|iPhone.Build.0 = Release|Any CPU
{8DA97FE4-645B-4951-92A5-A740D6DD3E5C}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
{8DA97FE4-645B-4951-92A5-A740D6DD3E5C}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
{8DA97FE4-645B-4951-92A5-A740D6DD3E5C}.Release|x64.ActiveCfg = Release|Any CPU
{8DA97FE4-645B-4951-92A5-A740D6DD3E5C}.Release|x64.Build.0 = Release|Any CPU
{8DA97FE4-645B-4951-92A5-A740D6DD3E5C}.Release|x86.ActiveCfg = Release|Any CPU
{8DA97FE4-645B-4951-92A5-A740D6DD3E5C}.Release|x86.Build.0 = Release|Any CPU
{999DDEB3-4CDA-441D-B980-3BC43B654237}.Ad-Hoc|Any CPU.ActiveCfg = Debug|x86
{999DDEB3-4CDA-441D-B980-3BC43B654237}.Ad-Hoc|Any CPU.Build.0 = Debug|x86
{999DDEB3-4CDA-441D-B980-3BC43B654237}.Ad-Hoc|ARM.ActiveCfg = Debug|x86
{999DDEB3-4CDA-441D-B980-3BC43B654237}.Ad-Hoc|ARM.Build.0 = Debug|x86
{999DDEB3-4CDA-441D-B980-3BC43B654237}.Ad-Hoc|iPhone.ActiveCfg = Debug|x86
{999DDEB3-4CDA-441D-B980-3BC43B654237}.Ad-Hoc|iPhone.Build.0 = Debug|x86
{999DDEB3-4CDA-441D-B980-3BC43B654237}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|x86
{999DDEB3-4CDA-441D-B980-3BC43B654237}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|x86
{999DDEB3-4CDA-441D-B980-3BC43B654237}.Ad-Hoc|x64.ActiveCfg = Debug|x86
{999DDEB3-4CDA-441D-B980-3BC43B654237}.Ad-Hoc|x64.Build.0 = Debug|x86
{999DDEB3-4CDA-441D-B980-3BC43B654237}.Ad-Hoc|x86.ActiveCfg = Debug|x86
{999DDEB3-4CDA-441D-B980-3BC43B654237}.Ad-Hoc|x86.Build.0 = Debug|x86
{999DDEB3-4CDA-441D-B980-3BC43B654237}.AppStore|Any CPU.ActiveCfg = Debug|x86
{999DDEB3-4CDA-441D-B980-3BC43B654237}.AppStore|Any CPU.Build.0 = Debug|x86
{999DDEB3-4CDA-441D-B980-3BC43B654237}.AppStore|ARM.ActiveCfg = Debug|x86
{999DDEB3-4CDA-441D-B980-3BC43B654237}.AppStore|ARM.Build.0 = Debug|x86
{999DDEB3-4CDA-441D-B980-3BC43B654237}.AppStore|iPhone.ActiveCfg = Debug|x86
{999DDEB3-4CDA-441D-B980-3BC43B654237}.AppStore|iPhone.Build.0 = Debug|x86
{999DDEB3-4CDA-441D-B980-3BC43B654237}.AppStore|iPhoneSimulator.ActiveCfg = Debug|x86
{999DDEB3-4CDA-441D-B980-3BC43B654237}.AppStore|iPhoneSimulator.Build.0 = Debug|x86
{999DDEB3-4CDA-441D-B980-3BC43B654237}.AppStore|x64.ActiveCfg = Debug|x86
{999DDEB3-4CDA-441D-B980-3BC43B654237}.AppStore|x64.Build.0 = Debug|x86
{999DDEB3-4CDA-441D-B980-3BC43B654237}.AppStore|x86.ActiveCfg = Debug|x86
{999DDEB3-4CDA-441D-B980-3BC43B654237}.AppStore|x86.Build.0 = Debug|x86
{999DDEB3-4CDA-441D-B980-3BC43B654237}.Debug|Any CPU.ActiveCfg = Debug|x86
{999DDEB3-4CDA-441D-B980-3BC43B654237}.Debug|Any CPU.Build.0 = Debug|x86
{999DDEB3-4CDA-441D-B980-3BC43B654237}.Debug|ARM.ActiveCfg = Debug|x86
{999DDEB3-4CDA-441D-B980-3BC43B654237}.Debug|ARM.Build.0 = Debug|x86
{999DDEB3-4CDA-441D-B980-3BC43B654237}.Debug|iPhone.ActiveCfg = Debug|x86
{999DDEB3-4CDA-441D-B980-3BC43B654237}.Debug|iPhone.Build.0 = Debug|x86
{999DDEB3-4CDA-441D-B980-3BC43B654237}.Debug|iPhoneSimulator.ActiveCfg = Debug|x86
{999DDEB3-4CDA-441D-B980-3BC43B654237}.Debug|iPhoneSimulator.Build.0 = Debug|x86
{999DDEB3-4CDA-441D-B980-3BC43B654237}.Debug|x64.ActiveCfg = Debug|x86
{999DDEB3-4CDA-441D-B980-3BC43B654237}.Debug|x64.Build.0 = Debug|x86
{999DDEB3-4CDA-441D-B980-3BC43B654237}.Debug|x86.ActiveCfg = Debug|x86
{999DDEB3-4CDA-441D-B980-3BC43B654237}.Debug|x86.Build.0 = Debug|x86
{999DDEB3-4CDA-441D-B980-3BC43B654237}.Release|Any CPU.ActiveCfg = Release|x86
{999DDEB3-4CDA-441D-B980-3BC43B654237}.Release|Any CPU.Build.0 = Release|x86
{999DDEB3-4CDA-441D-B980-3BC43B654237}.Release|ARM.ActiveCfg = Release|x86
{999DDEB3-4CDA-441D-B980-3BC43B654237}.Release|ARM.Build.0 = Release|x86
{999DDEB3-4CDA-441D-B980-3BC43B654237}.Release|iPhone.ActiveCfg = Release|x86
{999DDEB3-4CDA-441D-B980-3BC43B654237}.Release|iPhone.Build.0 = Release|x86
{999DDEB3-4CDA-441D-B980-3BC43B654237}.Release|iPhoneSimulator.ActiveCfg = Release|x86
{999DDEB3-4CDA-441D-B980-3BC43B654237}.Release|iPhoneSimulator.Build.0 = Release|x86
{999DDEB3-4CDA-441D-B980-3BC43B654237}.Release|x64.ActiveCfg = Release|x86
{999DDEB3-4CDA-441D-B980-3BC43B654237}.Release|x64.Build.0 = Release|x86
{999DDEB3-4CDA-441D-B980-3BC43B654237}.Release|x86.ActiveCfg = Release|x86
{999DDEB3-4CDA-441D-B980-3BC43B654237}.Release|x86.Build.0 = Release|x86
{2F4484CA-D992-44F2-AB3B-5C9F1AD83AB0}.Ad-Hoc|Any CPU.ActiveCfg = Debug|x86
{2F4484CA-D992-44F2-AB3B-5C9F1AD83AB0}.Ad-Hoc|Any CPU.Build.0 = Debug|x86
{2F4484CA-D992-44F2-AB3B-5C9F1AD83AB0}.Ad-Hoc|ARM.ActiveCfg = Debug|x86
{2F4484CA-D992-44F2-AB3B-5C9F1AD83AB0}.Ad-Hoc|ARM.Build.0 = Debug|x86
{2F4484CA-D992-44F2-AB3B-5C9F1AD83AB0}.Ad-Hoc|iPhone.ActiveCfg = Debug|x86
{2F4484CA-D992-44F2-AB3B-5C9F1AD83AB0}.Ad-Hoc|iPhone.Build.0 = Debug|x86
{2F4484CA-D992-44F2-AB3B-5C9F1AD83AB0}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|x86
{2F4484CA-D992-44F2-AB3B-5C9F1AD83AB0}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|x86
{2F4484CA-D992-44F2-AB3B-5C9F1AD83AB0}.Ad-Hoc|x64.ActiveCfg = Debug|x86
{2F4484CA-D992-44F2-AB3B-5C9F1AD83AB0}.Ad-Hoc|x64.Build.0 = Debug|x86
{2F4484CA-D992-44F2-AB3B-5C9F1AD83AB0}.Ad-Hoc|x86.ActiveCfg = Debug|x86
{2F4484CA-D992-44F2-AB3B-5C9F1AD83AB0}.Ad-Hoc|x86.Build.0 = Debug|x86
{2F4484CA-D992-44F2-AB3B-5C9F1AD83AB0}.AppStore|Any CPU.ActiveCfg = Debug|x86
{2F4484CA-D992-44F2-AB3B-5C9F1AD83AB0}.AppStore|Any CPU.Build.0 = Debug|x86
{2F4484CA-D992-44F2-AB3B-5C9F1AD83AB0}.AppStore|ARM.ActiveCfg = Debug|x86
{2F4484CA-D992-44F2-AB3B-5C9F1AD83AB0}.AppStore|ARM.Build.0 = Debug|x86
{2F4484CA-D992-44F2-AB3B-5C9F1AD83AB0}.AppStore|iPhone.ActiveCfg = Debug|x86
{2F4484CA-D992-44F2-AB3B-5C9F1AD83AB0}.AppStore|iPhone.Build.0 = Debug|x86
{2F4484CA-D992-44F2-AB3B-5C9F1AD83AB0}.AppStore|iPhoneSimulator.ActiveCfg = Debug|x86
{2F4484CA-D992-44F2-AB3B-5C9F1AD83AB0}.AppStore|iPhoneSimulator.Build.0 = Debug|x86
{2F4484CA-D992-44F2-AB3B-5C9F1AD83AB0}.AppStore|x64.ActiveCfg = Debug|x86
{2F4484CA-D992-44F2-AB3B-5C9F1AD83AB0}.AppStore|x64.Build.0 = Debug|x86
{2F4484CA-D992-44F2-AB3B-5C9F1AD83AB0}.AppStore|x86.ActiveCfg = Debug|x86
{2F4484CA-D992-44F2-AB3B-5C9F1AD83AB0}.AppStore|x86.Build.0 = Debug|x86
{2F4484CA-D992-44F2-AB3B-5C9F1AD83AB0}.Debug|Any CPU.ActiveCfg = Debug|x86
{2F4484CA-D992-44F2-AB3B-5C9F1AD83AB0}.Debug|Any CPU.Build.0 = Debug|x86
{2F4484CA-D992-44F2-AB3B-5C9F1AD83AB0}.Debug|ARM.ActiveCfg = Debug|x86
{2F4484CA-D992-44F2-AB3B-5C9F1AD83AB0}.Debug|ARM.Build.0 = Debug|x86
{2F4484CA-D992-44F2-AB3B-5C9F1AD83AB0}.Debug|iPhone.ActiveCfg = Debug|x86
{2F4484CA-D992-44F2-AB3B-5C9F1AD83AB0}.Debug|iPhone.Build.0 = Debug|x86
{2F4484CA-D992-44F2-AB3B-5C9F1AD83AB0}.Debug|iPhoneSimulator.ActiveCfg = Debug|x86
{2F4484CA-D992-44F2-AB3B-5C9F1AD83AB0}.Debug|iPhoneSimulator.Build.0 = Debug|x86
{2F4484CA-D992-44F2-AB3B-5C9F1AD83AB0}.Debug|x64.ActiveCfg = Debug|x86
{2F4484CA-D992-44F2-AB3B-5C9F1AD83AB0}.Debug|x64.Build.0 = Debug|x86
{2F4484CA-D992-44F2-AB3B-5C9F1AD83AB0}.Debug|x86.ActiveCfg = Debug|x86
{2F4484CA-D992-44F2-AB3B-5C9F1AD83AB0}.Debug|x86.Build.0 = Debug|x86
{2F4484CA-D992-44F2-AB3B-5C9F1AD83AB0}.Release|Any CPU.ActiveCfg = Release|x86
{2F4484CA-D992-44F2-AB3B-5C9F1AD83AB0}.Release|Any CPU.Build.0 = Release|x86
{2F4484CA-D992-44F2-AB3B-5C9F1AD83AB0}.Release|ARM.ActiveCfg = Release|x86
{2F4484CA-D992-44F2-AB3B-5C9F1AD83AB0}.Release|ARM.Build.0 = Release|x86
{2F4484CA-D992-44F2-AB3B-5C9F1AD83AB0}.Release|iPhone.ActiveCfg = Release|x86
{2F4484CA-D992-44F2-AB3B-5C9F1AD83AB0}.Release|iPhone.Build.0 = Release|x86
{2F4484CA-D992-44F2-AB3B-5C9F1AD83AB0}.Release|iPhoneSimulator.ActiveCfg = Release|x86
{2F4484CA-D992-44F2-AB3B-5C9F1AD83AB0}.Release|iPhoneSimulator.Build.0 = Release|x86
{2F4484CA-D992-44F2-AB3B-5C9F1AD83AB0}.Release|x64.ActiveCfg = Release|x86
{2F4484CA-D992-44F2-AB3B-5C9F1AD83AB0}.Release|x64.Build.0 = Release|x86
{2F4484CA-D992-44F2-AB3B-5C9F1AD83AB0}.Release|x86.ActiveCfg = Release|x86
{2F4484CA-D992-44F2-AB3B-5C9F1AD83AB0}.Release|x86.Build.0 = Release|x86
{8D741D81-6120-472A-BEE0-45F298D74D75}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU
{8D741D81-6120-472A-BEE0-45F298D74D75}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU
{8D741D81-6120-472A-BEE0-45F298D74D75}.Ad-Hoc|ARM.ActiveCfg = Debug|Any CPU
{8D741D81-6120-472A-BEE0-45F298D74D75}.Ad-Hoc|ARM.Build.0 = Debug|Any CPU
{8D741D81-6120-472A-BEE0-45F298D74D75}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU
{8D741D81-6120-472A-BEE0-45F298D74D75}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU
{8D741D81-6120-472A-BEE0-45F298D74D75}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{8D741D81-6120-472A-BEE0-45F298D74D75}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU
{8D741D81-6120-472A-BEE0-45F298D74D75}.Ad-Hoc|x64.ActiveCfg = Debug|Any CPU
{8D741D81-6120-472A-BEE0-45F298D74D75}.Ad-Hoc|x64.Build.0 = Debug|Any CPU
{8D741D81-6120-472A-BEE0-45F298D74D75}.Ad-Hoc|x86.ActiveCfg = Debug|Any CPU
{8D741D81-6120-472A-BEE0-45F298D74D75}.Ad-Hoc|x86.Build.0 = Debug|Any CPU
{8D741D81-6120-472A-BEE0-45F298D74D75}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU
{8D741D81-6120-472A-BEE0-45F298D74D75}.AppStore|Any CPU.Build.0 = Debug|Any CPU
{8D741D81-6120-472A-BEE0-45F298D74D75}.AppStore|ARM.ActiveCfg = Debug|Any CPU
{8D741D81-6120-472A-BEE0-45F298D74D75}.AppStore|ARM.Build.0 = Debug|Any CPU
{8D741D81-6120-472A-BEE0-45F298D74D75}.AppStore|iPhone.ActiveCfg = Debug|Any CPU
{8D741D81-6120-472A-BEE0-45F298D74D75}.AppStore|iPhone.Build.0 = Debug|Any CPU
{8D741D81-6120-472A-BEE0-45F298D74D75}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{8D741D81-6120-472A-BEE0-45F298D74D75}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU
{8D741D81-6120-472A-BEE0-45F298D74D75}.AppStore|x64.ActiveCfg = Debug|Any CPU
{8D741D81-6120-472A-BEE0-45F298D74D75}.AppStore|x64.Build.0 = Debug|Any CPU
{8D741D81-6120-472A-BEE0-45F298D74D75}.AppStore|x86.ActiveCfg = Debug|Any CPU
{8D741D81-6120-472A-BEE0-45F298D74D75}.AppStore|x86.Build.0 = Debug|Any CPU
{8D741D81-6120-472A-BEE0-45F298D74D75}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{8D741D81-6120-472A-BEE0-45F298D74D75}.Debug|Any CPU.Build.0 = Debug|Any CPU
{8D741D81-6120-472A-BEE0-45F298D74D75}.Debug|ARM.ActiveCfg = Debug|Any CPU
{8D741D81-6120-472A-BEE0-45F298D74D75}.Debug|ARM.Build.0 = Debug|Any CPU
{8D741D81-6120-472A-BEE0-45F298D74D75}.Debug|iPhone.ActiveCfg = Debug|Any CPU
{8D741D81-6120-472A-BEE0-45F298D74D75}.Debug|iPhone.Build.0 = Debug|Any CPU
{8D741D81-6120-472A-BEE0-45F298D74D75}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{8D741D81-6120-472A-BEE0-45F298D74D75}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
{8D741D81-6120-472A-BEE0-45F298D74D75}.Debug|x64.ActiveCfg = Debug|Any CPU
{8D741D81-6120-472A-BEE0-45F298D74D75}.Debug|x64.Build.0 = Debug|Any CPU
{8D741D81-6120-472A-BEE0-45F298D74D75}.Debug|x86.ActiveCfg = Debug|Any CPU
{8D741D81-6120-472A-BEE0-45F298D74D75}.Debug|x86.Build.0 = Debug|Any CPU
{8D741D81-6120-472A-BEE0-45F298D74D75}.Release|Any CPU.ActiveCfg = Release|Any CPU
{8D741D81-6120-472A-BEE0-45F298D74D75}.Release|Any CPU.Build.0 = Release|Any CPU
{8D741D81-6120-472A-BEE0-45F298D74D75}.Release|ARM.ActiveCfg = Release|Any CPU
{8D741D81-6120-472A-BEE0-45F298D74D75}.Release|ARM.Build.0 = Release|Any CPU
{8D741D81-6120-472A-BEE0-45F298D74D75}.Release|iPhone.ActiveCfg = Release|Any CPU
{8D741D81-6120-472A-BEE0-45F298D74D75}.Release|iPhone.Build.0 = Release|Any CPU
{8D741D81-6120-472A-BEE0-45F298D74D75}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
{8D741D81-6120-472A-BEE0-45F298D74D75}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
{8D741D81-6120-472A-BEE0-45F298D74D75}.Release|x64.ActiveCfg = Release|Any CPU
{8D741D81-6120-472A-BEE0-45F298D74D75}.Release|x64.Build.0 = Release|Any CPU
{8D741D81-6120-472A-BEE0-45F298D74D75}.Release|x86.ActiveCfg = Release|Any CPU
{8D741D81-6120-472A-BEE0-45F298D74D75}.Release|x86.Build.0 = Release|Any CPU
{D75E5A16-6863-4A53-84F4-CC0360782292}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU
{D75E5A16-6863-4A53-84F4-CC0360782292}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU
{D75E5A16-6863-4A53-84F4-CC0360782292}.Ad-Hoc|ARM.ActiveCfg = Debug|Any CPU
{D75E5A16-6863-4A53-84F4-CC0360782292}.Ad-Hoc|ARM.Build.0 = Debug|Any CPU
{D75E5A16-6863-4A53-84F4-CC0360782292}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU
{D75E5A16-6863-4A53-84F4-CC0360782292}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU
{D75E5A16-6863-4A53-84F4-CC0360782292}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{D75E5A16-6863-4A53-84F4-CC0360782292}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU
{D75E5A16-6863-4A53-84F4-CC0360782292}.Ad-Hoc|x64.ActiveCfg = Debug|Any CPU
{D75E5A16-6863-4A53-84F4-CC0360782292}.Ad-Hoc|x64.Build.0 = Debug|Any CPU
{D75E5A16-6863-4A53-84F4-CC0360782292}.Ad-Hoc|x86.ActiveCfg = Debug|Any CPU
{D75E5A16-6863-4A53-84F4-CC0360782292}.Ad-Hoc|x86.Build.0 = Debug|Any CPU
{D75E5A16-6863-4A53-84F4-CC0360782292}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU
{D75E5A16-6863-4A53-84F4-CC0360782292}.AppStore|Any CPU.Build.0 = Debug|Any CPU
{D75E5A16-6863-4A53-84F4-CC0360782292}.AppStore|ARM.ActiveCfg = Debug|Any CPU
{D75E5A16-6863-4A53-84F4-CC0360782292}.AppStore|ARM.Build.0 = Debug|Any CPU
{D75E5A16-6863-4A53-84F4-CC0360782292}.AppStore|iPhone.ActiveCfg = Debug|Any CPU
{D75E5A16-6863-4A53-84F4-CC0360782292}.AppStore|iPhone.Build.0 = Debug|Any CPU
{D75E5A16-6863-4A53-84F4-CC0360782292}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{D75E5A16-6863-4A53-84F4-CC0360782292}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU
{D75E5A16-6863-4A53-84F4-CC0360782292}.AppStore|x64.ActiveCfg = Debug|Any CPU
{D75E5A16-6863-4A53-84F4-CC0360782292}.AppStore|x64.Build.0 = Debug|Any CPU
{D75E5A16-6863-4A53-84F4-CC0360782292}.AppStore|x86.ActiveCfg = Debug|Any CPU
{D75E5A16-6863-4A53-84F4-CC0360782292}.AppStore|x86.Build.0 = Debug|Any CPU
{D75E5A16-6863-4A53-84F4-CC0360782292}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D75E5A16-6863-4A53-84F4-CC0360782292}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D75E5A16-6863-4A53-84F4-CC0360782292}.Debug|ARM.ActiveCfg = Debug|Any CPU
{D75E5A16-6863-4A53-84F4-CC0360782292}.Debug|ARM.Build.0 = Debug|Any CPU
{D75E5A16-6863-4A53-84F4-CC0360782292}.Debug|iPhone.ActiveCfg = Debug|Any CPU
{D75E5A16-6863-4A53-84F4-CC0360782292}.Debug|iPhone.Build.0 = Debug|Any CPU
{D75E5A16-6863-4A53-84F4-CC0360782292}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{D75E5A16-6863-4A53-84F4-CC0360782292}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
{D75E5A16-6863-4A53-84F4-CC0360782292}.Debug|x64.ActiveCfg = Debug|Any CPU
{D75E5A16-6863-4A53-84F4-CC0360782292}.Debug|x64.Build.0 = Debug|Any CPU
{D75E5A16-6863-4A53-84F4-CC0360782292}.Debug|x86.ActiveCfg = Debug|Any CPU
{D75E5A16-6863-4A53-84F4-CC0360782292}.Debug|x86.Build.0 = Debug|Any CPU
{D75E5A16-6863-4A53-84F4-CC0360782292}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D75E5A16-6863-4A53-84F4-CC0360782292}.Release|Any CPU.Build.0 = Release|Any CPU
{D75E5A16-6863-4A53-84F4-CC0360782292}.Release|ARM.ActiveCfg = Release|Any CPU
{D75E5A16-6863-4A53-84F4-CC0360782292}.Release|ARM.Build.0 = Release|Any CPU
{D75E5A16-6863-4A53-84F4-CC0360782292}.Release|iPhone.ActiveCfg = Release|Any CPU
{D75E5A16-6863-4A53-84F4-CC0360782292}.Release|iPhone.Build.0 = Release|Any CPU
{D75E5A16-6863-4A53-84F4-CC0360782292}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
{D75E5A16-6863-4A53-84F4-CC0360782292}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
{D75E5A16-6863-4A53-84F4-CC0360782292}.Release|x64.ActiveCfg = Release|Any CPU
{D75E5A16-6863-4A53-84F4-CC0360782292}.Release|x64.Build.0 = Release|Any CPU
{D75E5A16-6863-4A53-84F4-CC0360782292}.Release|x86.ActiveCfg = Release|Any CPU
{D75E5A16-6863-4A53-84F4-CC0360782292}.Release|x86.Build.0 = Release|Any CPU
{5FD17957-3AD1-42E5-AAC4-D9B09E0B7E14}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU
{5FD17957-3AD1-42E5-AAC4-D9B09E0B7E14}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU
{5FD17957-3AD1-42E5-AAC4-D9B09E0B7E14}.Ad-Hoc|Any CPU.Deploy.0 = Debug|Any CPU
{5FD17957-3AD1-42E5-AAC4-D9B09E0B7E14}.Ad-Hoc|ARM.ActiveCfg = Debug|Any CPU
{5FD17957-3AD1-42E5-AAC4-D9B09E0B7E14}.Ad-Hoc|ARM.Build.0 = Debug|Any CPU
{5FD17957-3AD1-42E5-AAC4-D9B09E0B7E14}.Ad-Hoc|ARM.Deploy.0 = Debug|Any CPU
{5FD17957-3AD1-42E5-AAC4-D9B09E0B7E14}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU
{5FD17957-3AD1-42E5-AAC4-D9B09E0B7E14}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU
{5FD17957-3AD1-42E5-AAC4-D9B09E0B7E14}.Ad-Hoc|iPhone.Deploy.0 = Debug|Any CPU
{5FD17957-3AD1-42E5-AAC4-D9B09E0B7E14}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{5FD17957-3AD1-42E5-AAC4-D9B09E0B7E14}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU
{5FD17957-3AD1-42E5-AAC4-D9B09E0B7E14}.Ad-Hoc|iPhoneSimulator.Deploy.0 = Debug|Any CPU
{5FD17957-3AD1-42E5-AAC4-D9B09E0B7E14}.Ad-Hoc|x64.ActiveCfg = Debug|Any CPU
{5FD17957-3AD1-42E5-AAC4-D9B09E0B7E14}.Ad-Hoc|x64.Build.0 = Debug|Any CPU
{5FD17957-3AD1-42E5-AAC4-D9B09E0B7E14}.Ad-Hoc|x64.Deploy.0 = Debug|Any CPU
{5FD17957-3AD1-42E5-AAC4-D9B09E0B7E14}.Ad-Hoc|x86.ActiveCfg = Debug|Any CPU
{5FD17957-3AD1-42E5-AAC4-D9B09E0B7E14}.Ad-Hoc|x86.Build.0 = Debug|Any CPU
{5FD17957-3AD1-42E5-AAC4-D9B09E0B7E14}.Ad-Hoc|x86.Deploy.0 = Debug|Any CPU
{5FD17957-3AD1-42E5-AAC4-D9B09E0B7E14}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU
{5FD17957-3AD1-42E5-AAC4-D9B09E0B7E14}.AppStore|Any CPU.Build.0 = Debug|Any CPU
{5FD17957-3AD1-42E5-AAC4-D9B09E0B7E14}.AppStore|Any CPU.Deploy.0 = Debug|Any CPU
{5FD17957-3AD1-42E5-AAC4-D9B09E0B7E14}.AppStore|ARM.ActiveCfg = Debug|Any CPU
{5FD17957-3AD1-42E5-AAC4-D9B09E0B7E14}.AppStore|ARM.Build.0 = Debug|Any CPU
{5FD17957-3AD1-42E5-AAC4-D9B09E0B7E14}.AppStore|ARM.Deploy.0 = Debug|Any CPU
{5FD17957-3AD1-42E5-AAC4-D9B09E0B7E14}.AppStore|iPhone.ActiveCfg = Debug|Any CPU
{5FD17957-3AD1-42E5-AAC4-D9B09E0B7E14}.AppStore|iPhone.Build.0 = Debug|Any CPU
{5FD17957-3AD1-42E5-AAC4-D9B09E0B7E14}.AppStore|iPhone.Deploy.0 = Debug|Any CPU
{5FD17957-3AD1-42E5-AAC4-D9B09E0B7E14}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{5FD17957-3AD1-42E5-AAC4-D9B09E0B7E14}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU
{5FD17957-3AD1-42E5-AAC4-D9B09E0B7E14}.AppStore|iPhoneSimulator.Deploy.0 = Debug|Any CPU
{5FD17957-3AD1-42E5-AAC4-D9B09E0B7E14}.AppStore|x64.ActiveCfg = Debug|Any CPU
{5FD17957-3AD1-42E5-AAC4-D9B09E0B7E14}.AppStore|x64.Build.0 = Debug|Any CPU
{5FD17957-3AD1-42E5-AAC4-D9B09E0B7E14}.AppStore|x64.Deploy.0 = Debug|Any CPU
{5FD17957-3AD1-42E5-AAC4-D9B09E0B7E14}.AppStore|x86.ActiveCfg = Debug|Any CPU
{5FD17957-3AD1-42E5-AAC4-D9B09E0B7E14}.AppStore|x86.Build.0 = Debug|Any CPU
{5FD17957-3AD1-42E5-AAC4-D9B09E0B7E14}.AppStore|x86.Deploy.0 = Debug|Any CPU
{5FD17957-3AD1-42E5-AAC4-D9B09E0B7E14}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{5FD17957-3AD1-42E5-AAC4-D9B09E0B7E14}.Debug|Any CPU.Build.0 = Debug|Any CPU
{5FD17957-3AD1-42E5-AAC4-D9B09E0B7E14}.Debug|Any CPU.Deploy.0 = Debug|Any CPU
{5FD17957-3AD1-42E5-AAC4-D9B09E0B7E14}.Debug|ARM.ActiveCfg = Debug|Any CPU
{5FD17957-3AD1-42E5-AAC4-D9B09E0B7E14}.Debug|ARM.Build.0 = Debug|Any CPU
{5FD17957-3AD1-42E5-AAC4-D9B09E0B7E14}.Debug|ARM.Deploy.0 = Debug|Any CPU
{5FD17957-3AD1-42E5-AAC4-D9B09E0B7E14}.Debug|iPhone.ActiveCfg = Debug|Any CPU
{5FD17957-3AD1-42E5-AAC4-D9B09E0B7E14}.Debug|iPhone.Build.0 = Debug|Any CPU
{5FD17957-3AD1-42E5-AAC4-D9B09E0B7E14}.Debug|iPhone.Deploy.0 = Debug|Any CPU
{5FD17957-3AD1-42E5-AAC4-D9B09E0B7E14}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{5FD17957-3AD1-42E5-AAC4-D9B09E0B7E14}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
{5FD17957-3AD1-42E5-AAC4-D9B09E0B7E14}.Debug|iPhoneSimulator.Deploy.0 = Debug|Any CPU
{5FD17957-3AD1-42E5-AAC4-D9B09E0B7E14}.Debug|x64.ActiveCfg = Debug|Any CPU
{5FD17957-3AD1-42E5-AAC4-D9B09E0B7E14}.Debug|x64.Build.0 = Debug|Any CPU
{5FD17957-3AD1-42E5-AAC4-D9B09E0B7E14}.Debug|x64.Deploy.0 = Debug|Any CPU
{5FD17957-3AD1-42E5-AAC4-D9B09E0B7E14}.Debug|x86.ActiveCfg = Debug|Any CPU
{5FD17957-3AD1-42E5-AAC4-D9B09E0B7E14}.Debug|x86.Build.0 = Debug|Any CPU
{5FD17957-3AD1-42E5-AAC4-D9B09E0B7E14}.Debug|x86.Deploy.0 = Debug|Any CPU
{5FD17957-3AD1-42E5-AAC4-D9B09E0B7E14}.Release|Any CPU.ActiveCfg = Release|Any CPU
{5FD17957-3AD1-42E5-AAC4-D9B09E0B7E14}.Release|Any CPU.Build.0 = Release|Any CPU
{5FD17957-3AD1-42E5-AAC4-D9B09E0B7E14}.Release|Any CPU.Deploy.0 = Release|Any CPU
{5FD17957-3AD1-42E5-AAC4-D9B09E0B7E14}.Release|ARM.ActiveCfg = Release|Any CPU
{5FD17957-3AD1-42E5-AAC4-D9B09E0B7E14}.Release|ARM.Build.0 = Release|Any CPU
{5FD17957-3AD1-42E5-AAC4-D9B09E0B7E14}.Release|ARM.Deploy.0 = Release|Any CPU
{5FD17957-3AD1-42E5-AAC4-D9B09E0B7E14}.Release|iPhone.ActiveCfg = Release|Any CPU
{5FD17957-3AD1-42E5-AAC4-D9B09E0B7E14}.Release|iPhone.Build.0 = Release|Any CPU
{5FD17957-3AD1-42E5-AAC4-D9B09E0B7E14}.Release|iPhone.Deploy.0 = Release|Any CPU
{5FD17957-3AD1-42E5-AAC4-D9B09E0B7E14}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
{5FD17957-3AD1-42E5-AAC4-D9B09E0B7E14}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
{5FD17957-3AD1-42E5-AAC4-D9B09E0B7E14}.Release|iPhoneSimulator.Deploy.0 = Release|Any CPU
{5FD17957-3AD1-42E5-AAC4-D9B09E0B7E14}.Release|x64.ActiveCfg = Release|Any CPU
{5FD17957-3AD1-42E5-AAC4-D9B09E0B7E14}.Release|x64.Build.0 = Release|Any CPU
{5FD17957-3AD1-42E5-AAC4-D9B09E0B7E14}.Release|x64.Deploy.0 = Release|Any CPU
{5FD17957-3AD1-42E5-AAC4-D9B09E0B7E14}.Release|x86.ActiveCfg = Release|Any CPU
{5FD17957-3AD1-42E5-AAC4-D9B09E0B7E14}.Release|x86.Build.0 = Release|Any CPU
{5FD17957-3AD1-42E5-AAC4-D9B09E0B7E14}.Release|x86.Deploy.0 = Release|Any CPU
{71C1E85B-1A79-C0C4-22BD-49A8DE57B8E7}.Ad-Hoc|Any CPU.ActiveCfg = Release|x64
{71C1E85B-1A79-C0C4-22BD-49A8DE57B8E7}.Ad-Hoc|Any CPU.Build.0 = Release|x64
{71C1E85B-1A79-C0C4-22BD-49A8DE57B8E7}.Ad-Hoc|Any CPU.Deploy.0 = Release|x64
{71C1E85B-1A79-C0C4-22BD-49A8DE57B8E7}.Ad-Hoc|ARM.ActiveCfg = Release|x64
{71C1E85B-1A79-C0C4-22BD-49A8DE57B8E7}.Ad-Hoc|ARM.Build.0 = Release|x64
{71C1E85B-1A79-C0C4-22BD-49A8DE57B8E7}.Ad-Hoc|ARM.Deploy.0 = Release|x64
{71C1E85B-1A79-C0C4-22BD-49A8DE57B8E7}.Ad-Hoc|iPhone.ActiveCfg = Release|x64
{71C1E85B-1A79-C0C4-22BD-49A8DE57B8E7}.Ad-Hoc|iPhone.Build.0 = Release|x64
{71C1E85B-1A79-C0C4-22BD-49A8DE57B8E7}.Ad-Hoc|iPhone.Deploy.0 = Release|x64
{71C1E85B-1A79-C0C4-22BD-49A8DE57B8E7}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|x64
{71C1E85B-1A79-C0C4-22BD-49A8DE57B8E7}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|x64
{71C1E85B-1A79-C0C4-22BD-49A8DE57B8E7}.Ad-Hoc|iPhoneSimulator.Deploy.0 = Release|x64
{71C1E85B-1A79-C0C4-22BD-49A8DE57B8E7}.Ad-Hoc|x64.ActiveCfg = Release|x64
{71C1E85B-1A79-C0C4-22BD-49A8DE57B8E7}.Ad-Hoc|x64.Build.0 = Release|x64
{71C1E85B-1A79-C0C4-22BD-49A8DE57B8E7}.Ad-Hoc|x64.Deploy.0 = Release|x64
{71C1E85B-1A79-C0C4-22BD-49A8DE57B8E7}.Ad-Hoc|x86.ActiveCfg = Release|x86
{71C1E85B-1A79-C0C4-22BD-49A8DE57B8E7}.Ad-Hoc|x86.Build.0 = Release|x86
{71C1E85B-1A79-C0C4-22BD-49A8DE57B8E7}.Ad-Hoc|x86.Deploy.0 = Release|x86
{71C1E85B-1A79-C0C4-22BD-49A8DE57B8E7}.AppStore|Any CPU.ActiveCfg = Release|x64
{71C1E85B-1A79-C0C4-22BD-49A8DE57B8E7}.AppStore|Any CPU.Build.0 = Release|x64
{71C1E85B-1A79-C0C4-22BD-49A8DE57B8E7}.AppStore|Any CPU.Deploy.0 = Release|x64
{71C1E85B-1A79-C0C4-22BD-49A8DE57B8E7}.AppStore|ARM.ActiveCfg = Release|x64
{71C1E85B-1A79-C0C4-22BD-49A8DE57B8E7}.AppStore|ARM.Build.0 = Release|x64
{71C1E85B-1A79-C0C4-22BD-49A8DE57B8E7}.AppStore|ARM.Deploy.0 = Release|x64
{71C1E85B-1A79-C0C4-22BD-49A8DE57B8E7}.AppStore|iPhone.ActiveCfg = Release|x64
{71C1E85B-1A79-C0C4-22BD-49A8DE57B8E7}.AppStore|iPhone.Build.0 = Release|x64
{71C1E85B-1A79-C0C4-22BD-49A8DE57B8E7}.AppStore|iPhone.Deploy.0 = Release|x64
{71C1E85B-1A79-C0C4-22BD-49A8DE57B8E7}.AppStore|iPhoneSimulator.ActiveCfg = Release|x64
{71C1E85B-1A79-C0C4-22BD-49A8DE57B8E7}.AppStore|iPhoneSimulator.Build.0 = Release|x64
{71C1E85B-1A79-C0C4-22BD-49A8DE57B8E7}.AppStore|iPhoneSimulator.Deploy.0 = Release|x64
{71C1E85B-1A79-C0C4-22BD-49A8DE57B8E7}.AppStore|x64.ActiveCfg = Release|x64
{71C1E85B-1A79-C0C4-22BD-49A8DE57B8E7}.AppStore|x64.Build.0 = Release|x64
{71C1E85B-1A79-C0C4-22BD-49A8DE57B8E7}.AppStore|x64.Deploy.0 = Release|x64
{71C1E85B-1A79-C0C4-22BD-49A8DE57B8E7}.AppStore|x86.ActiveCfg = Release|x86
{71C1E85B-1A79-C0C4-22BD-49A8DE57B8E7}.AppStore|x86.Build.0 = Release|x86
{71C1E85B-1A79-C0C4-22BD-49A8DE57B8E7}.AppStore|x86.Deploy.0 = Release|x86
{71C1E85B-1A79-C0C4-22BD-49A8DE57B8E7}.Debug|Any CPU.ActiveCfg = Debug|x64
{71C1E85B-1A79-C0C4-22BD-49A8DE57B8E7}.Debug|Any CPU.Build.0 = Debug|x64
{71C1E85B-1A79-C0C4-22BD-49A8DE57B8E7}.Debug|Any CPU.Deploy.0 = Debug|x64
{71C1E85B-1A79-C0C4-22BD-49A8DE57B8E7}.Debug|ARM.ActiveCfg = Debug|x64
{71C1E85B-1A79-C0C4-22BD-49A8DE57B8E7}.Debug|ARM.Build.0 = Debug|x64
{71C1E85B-1A79-C0C4-22BD-49A8DE57B8E7}.Debug|ARM.Deploy.0 = Debug|x64
{71C1E85B-1A79-C0C4-22BD-49A8DE57B8E7}.Debug|iPhone.ActiveCfg = Debug|x64
{71C1E85B-1A79-C0C4-22BD-49A8DE57B8E7}.Debug|iPhone.Build.0 = Debug|x64
{71C1E85B-1A79-C0C4-22BD-49A8DE57B8E7}.Debug|iPhone.Deploy.0 = Debug|x64
{71C1E85B-1A79-C0C4-22BD-49A8DE57B8E7}.Debug|iPhoneSimulator.ActiveCfg = Debug|x64
{71C1E85B-1A79-C0C4-22BD-49A8DE57B8E7}.Debug|iPhoneSimulator.Build.0 = Debug|x64
{71C1E85B-1A79-C0C4-22BD-49A8DE57B8E7}.Debug|iPhoneSimulator.Deploy.0 = Debug|x64
{71C1E85B-1A79-C0C4-22BD-49A8DE57B8E7}.Debug|x64.ActiveCfg = Debug|x64
{71C1E85B-1A79-C0C4-22BD-49A8DE57B8E7}.Debug|x64.Build.0 = Debug|x64
{71C1E85B-1A79-C0C4-22BD-49A8DE57B8E7}.Debug|x64.Deploy.0 = Debug|x64
{71C1E85B-1A79-C0C4-22BD-49A8DE57B8E7}.Debug|x86.ActiveCfg = Debug|x86
{71C1E85B-1A79-C0C4-22BD-49A8DE57B8E7}.Debug|x86.Build.0 = Debug|x86
{71C1E85B-1A79-C0C4-22BD-49A8DE57B8E7}.Debug|x86.Deploy.0 = Debug|x86
{71C1E85B-1A79-C0C4-22BD-49A8DE57B8E7}.Release|Any CPU.ActiveCfg = Release|x64
{71C1E85B-1A79-C0C4-22BD-49A8DE57B8E7}.Release|Any CPU.Build.0 = Release|x64
{71C1E85B-1A79-C0C4-22BD-49A8DE57B8E7}.Release|Any CPU.Deploy.0 = Release|x64
{71C1E85B-1A79-C0C4-22BD-49A8DE57B8E7}.Release|ARM.ActiveCfg = Release|x64
{71C1E85B-1A79-C0C4-22BD-49A8DE57B8E7}.Release|ARM.Build.0 = Release|x64
{71C1E85B-1A79-C0C4-22BD-49A8DE57B8E7}.Release|ARM.Deploy.0 = Release|x64
{71C1E85B-1A79-C0C4-22BD-49A8DE57B8E7}.Release|iPhone.ActiveCfg = Release|x64
{71C1E85B-1A79-C0C4-22BD-49A8DE57B8E7}.Release|iPhone.Build.0 = Release|x64
{71C1E85B-1A79-C0C4-22BD-49A8DE57B8E7}.Release|iPhone.Deploy.0 = Release|x64
{71C1E85B-1A79-C0C4-22BD-49A8DE57B8E7}.Release|iPhoneSimulator.ActiveCfg = Release|x64
{71C1E85B-1A79-C0C4-22BD-49A8DE57B8E7}.Release|iPhoneSimulator.Build.0 = Release|x64
{71C1E85B-1A79-C0C4-22BD-49A8DE57B8E7}.Release|iPhoneSimulator.Deploy.0 = Release|x64
{71C1E85B-1A79-C0C4-22BD-49A8DE57B8E7}.Release|x64.ActiveCfg = Release|x64
{71C1E85B-1A79-C0C4-22BD-49A8DE57B8E7}.Release|x64.Build.0 = Release|x64
{71C1E85B-1A79-C0C4-22BD-49A8DE57B8E7}.Release|x64.Deploy.0 = Release|x64
{71C1E85B-1A79-C0C4-22BD-49A8DE57B8E7}.Release|x86.ActiveCfg = Release|x86
{71C1E85B-1A79-C0C4-22BD-49A8DE57B8E7}.Release|x86.Build.0 = Release|x86
{71C1E85B-1A79-C0C4-22BD-49A8DE57B8E7}.Release|x86.Deploy.0 = Release|x86
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{ED3D7A4C-2473-41E1-87E7-428DBB517BD8} = {5D445349-E40E-4936-AE11-7C07276AC4A3}
{8DA97FE4-645B-4951-92A5-A740D6DD3E5C} = {C5094142-9DAB-405B-9B46-DBA0AC3B9627}
{999DDEB3-4CDA-441D-B980-3BC43B654237} = {C5094142-9DAB-405B-9B46-DBA0AC3B9627}
{2F4484CA-D992-44F2-AB3B-5C9F1AD83AB0} = {C5094142-9DAB-405B-9B46-DBA0AC3B9627}
{8D741D81-6120-472A-BEE0-45F298D74D75} = {5D445349-E40E-4936-AE11-7C07276AC4A3}
{D75E5A16-6863-4A53-84F4-CC0360782292} = {5D445349-E40E-4936-AE11-7C07276AC4A3}
{5FD17957-3AD1-42E5-AAC4-D9B09E0B7E14} = {07F120B6-65CD-4EDB-9BC5-F47281F268B3}
{71C1E85B-1A79-C0C4-22BD-49A8DE57B8E7} = {C5094142-9DAB-405B-9B46-DBA0AC3B9627}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {C7458BCE-5F5A-49A9-8C8C-8955E59F8B66}
EndGlobalSection
EndGlobal
+3
View File
@@ -0,0 +1,3 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:Boolean x:Key="/Default/UserDictionary/Words/=Carthy/@EntryIndexedValue">True</s:Boolean>
</wpf:ResourceDictionary>
+25
View File
@@ -0,0 +1,25 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.2.32630.192
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MyAspNetCoreApp", "MyAspNetCoreApp\MyAspNetCoreApp.csproj", "{F44DA130-BCF4-493B-AC08-D60560DA2A87}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{F44DA130-BCF4-493B-AC08-D60560DA2A87}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F44DA130-BCF4-493B-AC08-D60560DA2A87}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F44DA130-BCF4-493B-AC08-D60560DA2A87}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F44DA130-BCF4-493B-AC08-D60560DA2A87}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {60534E9C-E75A-42A7-ADA8-05C2ADD0D730}
EndGlobalSection
EndGlobal
@@ -0,0 +1,3 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:Boolean x:Key="/Default/UserDictionary/Words/=Carthy/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Podcasts/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
@@ -0,0 +1,129 @@
using Kendo.Mvc.Extensions;
using Kendo.Mvc.UI;
using Microsoft.AspNetCore.Mvc;
using MyAspNetCoreApp.Models;
using Telerik.Documents.Common.Model;
using Telerik.Documents.Media;
using Telerik.Windows.Documents.Spreadsheet.FormatProviders.OpenXml.Xlsx;
using Telerik.Windows.Documents.Spreadsheet.Model;
namespace MyAspNetCoreApp.Controllers;
public class DashboardController : Controller
{
private readonly List<string> firstNames = ["Nancy", "Andrew", "Janet", "Margaret", "Steven", "Michael", "Robert", "Laura", "Anne", "Nige"];
private readonly List<string> lastNames = ["Davolio", "Fuller", "Leverling", "Peacock", "Buchanan", "Suyama", "King", "Callahan", "Dodsworth", "White"];
private readonly List<string> platforms = ["Apple Podcasts", "Spotify", "Other", "Overcast", "Anchor", "Stitcher"];
private readonly List<string> devices = ["iOS", "Android", "Other", "Web"];
private static readonly Random Random = new();
private static readonly List<PodcastViewModel> Podcasts = [];
public ActionResult Podcasts_Read([DataSourceRequest] DataSourceRequest request)
{
var result = GetPodcasts().ToDataSourceResult(request);
ViewData["test"] = result.AggregateResults;
return Json(result);
}
public ActionResult Downloads_Read([DataSourceRequest] DataSourceRequest request)
{
return Json(GetPodcasts());
}
public ActionResult Devices_Read([DataSourceRequest] DataSourceRequest request)
{
var deviceViews = GetPodcasts()
.GroupBy(x => x.Device)
.Select(x => new
{
Device = x.Key,
Views = x.Sum(v => v.Views)
});
return Json(deviceViews);
}
public ActionResult Platforms_Read([DataSourceRequest] DataSourceRequest request)
{
var platformViews = GetPodcasts()
.GroupBy(x => x.PlatformName)
.Select(x => new
{
PlatformName = x.Key,
Views = x.Sum(v => v.Views)
});
return Json(platformViews);
}
public ActionResult Generate_PodcastsExcel()
{
var workbook = new Workbook();
var worksheet = workbook.Worksheets.Add();
worksheet.Name = "Podcasts";
string[] headers = ["Podcast Episode", "Downloads", "Streams", "Views", "Date", "Reach", "Device", "Platform"];
for (var column = 0; column < headers.Length; column++)
{
worksheet.Cells[0, column].SetValue(headers[column]);
}
var headerRow = worksheet.Cells[0, 0, 0, headers.Length - 1];
headerRow.SetFill(PatternFill.CreateSolidFill(Color.FromRgb(0, 51, 102)));
headerRow.SetForeColor(new ThemableColor(Color.FromRgb(255, 255, 255)));
var podcasts = GetPodcasts();
for (var row = 0; row < podcasts.Count; row++)
{
var podcast = podcasts[row];
var worksheetRow = row + 1;
worksheet.Cells[worksheetRow, 0].SetValue(podcast.Name ?? string.Empty);
worksheet.Cells[worksheetRow, 1].SetValue(podcast.Downloads);
worksheet.Cells[worksheetRow, 2].SetValue(podcast.Streams);
worksheet.Cells[worksheetRow, 3].SetValue(podcast.Views);
worksheet.Cells[worksheetRow, 4].SetValue(podcast.Date.ToShortDateString());
worksheet.Cells[worksheetRow, 5].SetValue(podcast.Reach);
worksheet.Cells[worksheetRow, 6].SetValue(podcast.Device ?? string.Empty);
worksheet.Cells[worksheetRow, 7].SetValue(podcast.PlatformName ?? string.Empty);
}
worksheet.Columns[0].SetWidth(new ColumnWidth(290, true));
worksheet.Columns[1].SetWidth(new ColumnWidth(85, true));
worksheet.Columns[4].SetWidth(new ColumnWidth(90, true));
worksheet.Columns[7].SetWidth(new ColumnWidth(120, true));
var formatProvider = new XlsxFormatProvider();
using var stream = new MemoryStream();
formatProvider.Export(workbook, stream, TimeSpan.FromMinutes(2));
return File(stream.ToArray(), "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", "podcasts.xlsx");
}
// HELPER METHODS
private List<PodcastViewModel> GetPodcasts()
{
if (Podcasts.Count == 0)
{
Podcasts.AddRange(Enumerable.Range(1, 50).Select(x => new PodcastViewModel()
{
Name = $"Episode #{Random.Next(0, 200)} with guest {firstNames[Random.Next(0, firstNames.Count)]} {lastNames[Random.Next(0, lastNames.Count)]}",
Streams = Random.Next(0, 18000),
Downloads = Random.Next(0, 15000),
PlatformName = platforms[Random.Next(0, platforms.Count)],
Device = devices[Random.Next(0, devices.Count)],
Date = DateTime.Now.AddDays(-x),
Reach = x * Random.Next(0, 1000)
}));
}
return Podcasts;
}
}
@@ -0,0 +1,35 @@
using Microsoft.AspNetCore.Mvc;
using MyAspNetCoreApp.Models;
using System.Diagnostics;
namespace MyAspNetCoreApp.Controllers;
public class HomeController(ILogger<HomeController> logger, IWebHostEnvironment environment) : Controller
{
public IActionResult Index()
{
return View();
}
public IActionResult ReportViewer()
{
var reportsPath = Path.Combine(environment.ContentRootPath, "Reports");
var reports = Directory.GetFiles(reportsPath, "*.trdp")
.Select(Path.GetFileName)
.OrderBy(n => n)
.ToList();
ViewBag.Reports = reports;
return View();
}
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
public IActionResult Error()
{
logger.LogError($"An error occurred while processing the request. {Activity.Current?.Id ?? HttpContext.TraceIdentifier}");
return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });
}
}
@@ -0,0 +1,25 @@
using Microsoft.AspNetCore.Mvc;
using System.Net;
using System.Net.Mail;
using Telerik.Reporting.Services;
using Telerik.Reporting.Services.AspNetCore;
namespace MyAspNetCoreApp.Controllers;
[Route("api/reports")]
[ApiController]
public class ReportsController(IReportServiceConfiguration reportServiceConfiguration) : ReportsControllerBase(reportServiceConfiguration)
{
protected override HttpStatusCode SendMailMessage(MailMessage mailMessage)
{
throw new NotImplementedException("This method should be implemented in order to send mail messages");
// using (var smtpClient = new SmtpClient("smtp01.mycompany.com", 25))
// {
// smtpClient.DeliveryMethod = SmtpDeliveryMethod.Network;
// smtpClient.EnableSsl = false;
// smtpClient.Send(mailMessage);
// }
// return HttpStatusCode.OK;
}
}
@@ -0,0 +1,65 @@
# Written by Lance McCarthy as an example only. Use at your own risk, carefully review how you are handling your secrets and what is included in your final image.
###############
### STAGE 1 ###
###############
# Install required dependencies into the stripped-down base image
FROM tgagor/centos:latest AS base
WORKDIR /app
EXPOSE 80
EXPOSE 443
# Update the repositories
RUN cd /etc/yum.repos.d/
# For CentOS 7
# RUN sed -i 's/mirrorlist/#mirrorlist/g' /etc/yum.repos.d/CentOS-*
# RUN sed -i 's|#baseurl=http://mirror.centos.org|baseurl=http://vault.centos.org|g' /etc/yum.repos.d/CentOS-*
# For newer CentOS:latest
RUN sed -i 's/mirrorlist/#mirrorlist/g' /etc/yum.repos.d/centos.repo
RUN sed -i 's|#baseurl=http://mirror.centos.org|baseurl=http://vault.centos.org|g' /etc/yum.repos.d/centos.repo
RUN yum update -y
# IMPORTANT - Enable EPEL repostitory (if this doesn't work, use `RUN rpm -ihv --nodeps https://dl.fedoraproject.org/pub/epel/epel-release-latest-8.noarch.rpm` instead)
RUN yum install epel-release -y
# REPORTING DEPENDENCIES - See https://docs.telerik.com/reporting/knowledge-base/how-to-build-and-install-libgdiplus-linux#solution-for-centos-and-amazon-linux
# Install Skia dependencies for new Telerik report rendering with Skia
RUN yum install freetype fontconfig -y
# DOTNET RUNTIME - Install aspnetcore runtime (if your base image does not have it already)
RUN rpm -Uvh https://packages.microsoft.com/config/centos/7/packages-microsoft-prod.rpm
RUN yum install aspnetcore-runtime-10.0 -y
###############
### STAGE 2 ###
###############
# Compile the project and create production-ready artifacts
FROM --platform=$BUILDPLATFORM mcr.microsoft.com/dotnet/sdk:10.0 AS build
WORKDIR /src
COPY . .
# 1. Mount the "telerik-nuget-key" secret and immediately use it to add the Telerik server as a package source
RUN --mount=type=secret,id=telerik-nuget-key \
dotnet nuget add source 'https://nuget.telerik.com/v3/index.json' -n "TelerikNuGetServer" -u "api-key" -p $(cat /run/secrets/telerik-nuget-key) --store-password-in-clear-text
# 2. Restore NuGet packages
RUN dotnet restore "./MyAspNetCoreApp.csproj"
# 3. Mount the "telerik-license-key" secret as an env var and build the project
RUN --mount=type=secret,id=telerik-license-key \
TELERIK_LICENSE="$(cat /run/secrets/telerik-license-key)" \
dotnet publish "./MyAspNetCoreApp.csproj" -o /app/publish /p:UseAppHost=false --no-restore --self-contained false
###############
### STAGE 3 ###
###############
# Build the final image from the base, but copy the pubilsh artifacts from the intermediate stage
FROM base AS final
WORKDIR /app
COPY --from=build /app/publish .
ENTRYPOINT ["dotnet", "MyAspNetCoreApp.dll"]
@@ -0,0 +1,35 @@
# Written by Lance McCarthy as an example only. Use at your own risk, carefully review how you are handling your secrets and what is included in your final image.
### STAGE 1 ###
# Create our base image from the aspnet image, and prep any neccessary settings
FROM mcr.microsoft.com/dotnet/aspnet:10.0 AS base
WORKDIR /app
EXPOSE 80
EXPOSE 443
# Install Telerik Reporting's modern Linux dependencies
RUN apt-get update
RUN apt-get install libfreetype6 libfontconfig1 -y
### STAGE 2 ###
# Compile the project and create production-ready artifacts
FROM --platform=$BUILDPLATFORM mcr.microsoft.com/dotnet/sdk:10.0 AS build
WORKDIR /src
COPY . .
# 1. Mount the "telerik-nuget-key" secret and immediately use it to add the Telerik server as a package source
RUN --mount=type=secret,id=telerik-nuget-key \
dotnet nuget add source 'https://nuget.telerik.com/v3/index.json' -n "TelerikNuGetServer" -u "api-key" -p $(cat /run/secrets/telerik-nuget-key) --store-password-in-clear-text
# 2. Restore NuGet packages
RUN dotnet restore "./MyAspNetCoreApp.csproj"
# 3. Mount the "telerik-license-key" secret as an env var and build the project
RUN --mount=type=secret,id=telerik-license-key \
TELERIK_LICENSE="$(cat /run/secrets/telerik-license-key)" \
dotnet publish "./MyAspNetCoreApp.csproj" -o /app/publish /p:UseAppHost=false --no-restore --self-contained false
### STAGE 3 ###
# Build the final image from the base, but copy the pubilsh artifacts from the intermediate stage
FROM base AS final
WORKDIR /app
COPY --from=build /app/publish .
ENTRYPOINT ["dotnet", "MyAspNetCoreApp.dll"]
@@ -0,0 +1,30 @@
FROM mcr.microsoft.com/dotnet/aspnet:10.0 AS base
WORKDIR /app
EXPOSE 80
EXPOSE 443
# Dependencies for Telerik Reporting on Linux using older GDI+
RUN apt-get update \
&& apt-get install -y --allow-unauthenticated \
libc6-dev \
libgdiplus \
libx11-dev \
&& rm -rf /var/lib/apt/lists/*
FROM --platform=$BUILDPLATFORM mcr.microsoft.com/dotnet/sdk:10.0 AS build
WORKDIR /src
COPY ./NuGet.Config ./NuGet.Config
WORKDIR /src/MyAspNetCoreApp
COPY . .
# Here we use a docker secret to update the 'Telerik' package source in the nuget.config.
RUN --mount=type=secret,id=telerik_key \
echo $(cat /run/secrets/telerik_key) \
&& dotnet nuget update source "Telerik" -s "https://nuget.telerik.com/v3/index.json" -u "api-key" -p $(cat /run/secrets/telerik_key) --configfile "./NuGet.Config" --store-password-in-clear-text \
&& dotnet restore "MyAspNetCoreApp.csproj" --configfile "./NuGet.Config" \
&& dotnet publish "MyAspNetCoreApp.csproj" -c Release -o /app/publish /p:UseAppHost=false --self-contained false
FROM base AS final
WORKDIR /app
COPY --from=build /app/publish .
ENTRYPOINT ["dotnet", "MyAspNetCoreApp.dll"]
@@ -0,0 +1,8 @@
namespace MyAspNetCoreApp.Models;
public class ErrorViewModel
{
public string? RequestId { get; set; }
public bool ShowRequestId => !string.IsNullOrEmpty(RequestId);
}
@@ -0,0 +1,20 @@
namespace MyAspNetCoreApp.Models;
public class PodcastViewModel
{
public string? Name { get; set; }
public int Downloads { get; set; }
public int Streams { get; set; }
public int Views => Downloads + Streams;
public DateTime Date { get; set; }
public int Reach { get; set; }
public string? Device { get; set; }
public string? PlatformName { get; set; }
}
@@ -0,0 +1,74 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<UserSecretsId>6ca2a36f-6cc7-4d98-9cd4-724a3953a16b</UserSecretsId>
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
<ContainerBaseImage>ghcr.io/lancemccarthy/skia-aspnet:10.0</ContainerBaseImage>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.SemanticKernel.Core" Version="1.76.0" />
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.23.0" />
<PackageReference Include="Telerik.Documents.Spreadsheet" Version="2026.2.519" />
<PackageReference Include="Telerik.Documents.Spreadsheet.FormatProviders.OpenXml" Version="2026.2.519" />
<PackageReference Include="Telerik.Drawing.Skia" Version="20.1.26.520" />
<PackageReference Include="Telerik.Licensing" Version="1.8.2" />
<PackageReference Include="Telerik.Reporting.AI.Microsoft.Extensions.AzureOpenAI" Version="20.1.26.520" />
<PackageReference Include="Telerik.Reporting.OpenXmlRendering" Version="20.1.26.520" />
<PackageReference Include="Telerik.Reporting.Services.AspNetCore" Version="20.1.26.520" />
<PackageReference Include="Telerik.UI.for.AspNet.Core" Version="2026.2.520" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="10.0.8" />
<!-- For stripped down container environments -->
<PackageReference Include="SkiaSharp" Version="3.119.2" />
<PackageReference Include="SkiaSharp.NativeAssets.Linux" Version="3.119.2" />
<PackageReference Include="SkiaSharp.NativeAssets.macOS" Version="3.119.2" />
<PackageReference Include="SkiaSharp.NativeAssets.Win32" Version="3.119.2" />
</ItemGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
<DefineConstants>$(DefineConstants);RELEASE</DefineConstants>
</PropertyGroup>
<ItemGroup>
<Compile Remove="Templates\**" />
<Content Remove="Templates\**" />
<EmbeddedResource Remove="Templates\**" />
<None Remove="Templates\**" />
</ItemGroup>
<ItemGroup>
<None Update="Reports\Barcodes Report.trdp">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Reports\Dashboard.trdp">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Reports\Employee Sales Summary.trdp">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Reports\Invoice.trdp">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Reports\Product Line Sales.trdp">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Reports\Product Sales.trdp">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Reports\ReportBook.trbp">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Reports\Sales Order Details.trdp">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
<ProjectExtensions>
<VisualStudio>
<UserProperties UseCdnSupport="" />
</VisualStudio>
</ProjectExtensions>
</Project>
+53
View File
@@ -0,0 +1,53 @@
using Microsoft.Extensions.DependencyInjection.Extensions;
using Telerik.Reporting.Cache.File;
using Telerik.Reporting.Services;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(corsOption => corsOption
// CORS policy for ReportsController
.AddPolicy("ReportingRestPolicy", corsBuilder => corsBuilder.AllowAnyOrigin().AllowAnyMethod().AllowAnyHeader()
));
// Add services to the container (important: Uses System.Text.Json instead of json.net from now on).
builder.Services.AddControllersWithViews()
.AddJsonOptions(options =>
{
options.JsonSerializerOptions.PropertyNamingPolicy = null;
});
// Add Kendo UI services to the services container"
builder.Services.AddKendo();
// Configure dependencies for ReportsController.
builder.Services.TryAddSingleton<IReportServiceConfiguration>(sp =>
new ReportServiceConfiguration
{
ReportingEngineConfiguration = sp.GetService<IConfiguration>(),
HostAppId = "MyAspNetCoreApp",
Storage = new FileStorage(),
ReportSourceResolver = new UriReportSourceResolver(System.IO.Path.Combine(sp.GetService<IWebHostEnvironment>().ContentRootPath, "Reports"))
});
var app = builder.Build();
// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Home/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseCors("ReportingRestPolicy");
app.UseAuthorization();
app.MapControllers();
app.MapControllerRoute(name: "default", pattern: "{controller=Home}/{action=Index}/{id?}");
app.Run();
@@ -0,0 +1,29 @@
{
"profiles": {
"MyAspNetCoreApp": {
"commandName": "Project",
"launchBrowser": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
},
"applicationUrl": "https://localhost:61922;http://localhost:61923"
},
"Docker": {
"commandName": "Docker",
"launchBrowser": true,
"launchUrl": "{Scheme}://{ServiceHost}:{ServicePort}",
"publishAllPorts": true,
"useSSL": true
},
"WSL": {
"commandName": "WSL2",
"launchBrowser": true,
"launchUrl": "https://localhost:61922",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development",
"ASPNETCORE_URLS": "https://localhost:61922;http://localhost:61923"
},
"distributionName": ""
}
}
}
Binary file not shown.
@@ -0,0 +1,176 @@
@inherits Microsoft.VisualStudio.Web.CodeGeneration.Templating.RazorTemplateBase
@using Microsoft.VisualStudio.Web.CodeGeneration.EntityFrameworkCore
@using System.Collections.Generic
@using System.Linq
@@model @Model.ViewDataTypeName
@{
if (Model.IsPartialView)
{
}
else if (Model.IsLayoutPageSelected)
{
@:@@{
@:ViewData["Title"] = "@Model.ViewName";
if (!string.IsNullOrEmpty(Model.LayoutPageFile))
{
@:Layout = "@Model.LayoutPageFile";
}
@:}
@:
@:<h1>@Model.ViewName</h1>
@:
}
else
{
@:@@{
@:Layout = null;
@:}
@:
@:<!DOCTYPE html>
@:
@:<html>
@:<head>
@:<meta name="viewport" content="width=device-width" />
@:<title>@Model.ViewName</title>
@:</head>
@:<body>
@:
// PushIndent(" ");
}
@:<h4>@Model.ViewDataTypeShortName</h4>
@:<hr />
@:<div class="row">
@:<div class="col-md-4">
string formId = Model.ViewDataTypeShortName + "Form";
@:<form asp-action="@Model.ViewName" id="@formId">
@:<div asp-validation-summary="ModelOnly" class="text-danger"></div>
foreach (var property in Model.ModelMetadata.Properties)
{
if (property.Scaffold && !property.IsAutoGenerated && !property.IsReadOnly)
{
if (property.IsForeignKey)
{
@:<div class="form-group">
@:<label asp-for="@property.PropertyName"></label>
@:<select asp-for="@property.PropertyName" asp-items="ViewBag.@property.PropertyName"></select>
@:</div>
continue;
}
bool isCheckbox = property.TypeName.Equals("System.Boolean");
bool isDatetime = property.TypeName.Contains("System.DateTime");
if (isCheckbox)
{
@:<div class="form-group">
@:@@Html.Kendo().CheckBoxFor(m => m.@property.PropertyName)
@:<label asp-for="@property.PropertyName"></label>
@:<span asp-validation-for="@property.PropertyName" class="text-danger k-invalid-msg" data-for="@property.PropertyName"></span>
@:</div>
}
else if (isDatetime)
{
@:<div class="form-group">
@:<label asp-for="@property.PropertyName"></label>
@:<kendo-datetimepicker for="@property.PropertyName" />
@:<span asp-validation-for="@property.PropertyName" class="text-danger k-invalid-msg" data-for="@property.PropertyName"></span>
@:</div>
}
else if (IsNumericType(property))
{
@:<div class="form-group">
@:<label asp-for="@property.PropertyName"></label>
@:<kendo-numerictextbox for="@property.PropertyName" />
@:<span asp-validation-for="@property.PropertyName" class="text-danger k-invalid-msg" data-for="@property.PropertyName"></span>
@:</div>
}
else if (property.IsEnum && !property.IsEnumFlags)
{
@:<div class="form-group">
@:<label asp-for="@property.PropertyName"></label>
@:<select asp-for="@property.PropertyName"></select>
@:<span asp-validation-for="@property.PropertyName" class="text-danger k-invalid-msg" data-for="@property.PropertyName"></span>
@:</div>
}
else if (property.IsMultilineText)
{
@:<div class="form-group">
@:<label asp-for="@property.PropertyName"></label>
@:<textarea asp-for="@property.PropertyName" class="k-textbox"></textarea>
@:<span asp-validation-for="@property.PropertyName" class="text-danger k-invalid-msg" data-for="@property.PropertyName"></span>
@:</div>
}
else
{
@:<div class="form-group">
@:<label asp-for="@property.PropertyName"></label>
@:<input asp-for="@property.PropertyName" class="k-textbox" />
@:<span asp-validation-for="@property.PropertyName" class="text-danger k-invalid-msg" data-for="@property.PropertyName"></span>
@:</div>
}
}
}
}
<div class="form-group">
<input type="submit" value="Create" class="k-button k-button-md k-rounded-md k-button-solid-primary" />
</div>
</form>
</div>
</div>
<div>
<a asp-action="Index" class="k-button k-button-md k-rounded-md k-button-solid">Back to List</a>
</div>
@{
if (Model.ReferenceScriptLibraries)
{
@:<script>
@:$(document).ready(function () {
@:$("#@formId").kendoValidator({
@:// You can define your custom rules here:
@://rules: {}
@:});
@:})
@:</script>
}
// The following code closes the tag used in the case of a view using a layout page and the body and html tags in the case of a regular view page
if (!Model.IsPartialView && !Model.IsLayoutPageSelected)
{
@:</body>
@:</html>
}
}
@functions
{
// Do we need to use this in conjunction with the PrimaryKey check?
bool IsPropertyGuid(IPropertyMetadata property)
{
return string.Equals("System.Guid", property.TypeName, StringComparison.OrdinalIgnoreCase);
}
string GetValueExpression(IPropertyMetadata property)
{
return property.PropertyName;
}
bool IsNumericType(IPropertyMetadata property)
{
var types = new[] {
"System.Byte",
"System.SByte",
"System.UInt16",
"System.UInt32",
"System.UInt64",
"System.Int16",
"System.Int32",
"System.Int64",
"System.Decimal",
"System.Double",
"System.Single"
};
return types.Any(property.TypeName.Contains);
}
}
@@ -0,0 +1,104 @@
@inherits Microsoft.VisualStudio.Web.CodeGeneration.Templating.RazorTemplateBase
@using Microsoft.VisualStudio.Web.CodeGeneration.EntityFrameworkCore
@using System.Collections.Generic
@using System.Linq
@@model @Model.ViewDataTypeName
@{
if (Model.IsPartialView)
{
}
else if (Model.IsLayoutPageSelected)
{
@:@@{
@:ViewData["Title"] = "@Model.ViewName";
if (!string.IsNullOrEmpty(Model.LayoutPageFile))
{
@:Layout = "@Model.LayoutPageFile";
}
@:}
@:
@:<h1>@Model.ViewName</h1>
@:
}
else
{
@:@@{
@:Layout = null;
@:}
@:
@:<!DOCTYPE html>
@:
@:<html>
@:<head>
@:<meta name="viewport" content="width=device-width" />
@:<title>@Model.ViewName</title>
@:</head>
@:<body>
@:
// PushIndent(" ");
}
}
<h3>Are you sure you want to delete this?</h3>
<div>
<h4>@Model.ViewDataTypeShortName</h4>
<hr />
<dl class="row">
@{
Dictionary<string, IPropertyMetadata> propertyLookup = ((IModelMetadata)Model.ModelMetadata).Properties.ToDictionary(x => x.PropertyName, x => x);
Dictionary<string, INavigationMetadata> navigationLookup = ((IModelMetadata)Model.ModelMetadata).Navigations.ToDictionary(x => x.AssociationPropertyName, x => x);
foreach (var item in Model.ModelMetadata.ModelType.GetProperties())
{
if (propertyLookup.TryGetValue(item.Name, out IPropertyMetadata property)
&& property.Scaffold && !property.IsForeignKey && !property.IsPrimaryKey)
{
<dt class = "col-sm-2">
@@Html.DisplayNameFor(model => model.@GetValueExpression(property))
</dt>
<dd class = "col-sm-10">
@@Html.DisplayFor(model => model.@GetValueExpression(property))
</dd>
}
else if (navigationLookup.TryGetValue(item.Name, out INavigationMetadata navigation))
{
<dt class = "col-sm-2">
@@Html.DisplayNameFor(model => model.@GetValueExpression(navigation))
</dt>
<dd class = "col-sm-10">
@@Html.DisplayFor(model => model.@GetValueExpression(navigation).@navigation.DisplayPropertyName)
</dd class>
}
}
@:</dl>
@:
@:<form asp-action="@Model.ViewName">
foreach (var property in Model.ModelMetadata.Properties)
{
if(property.IsPrimaryKey){
@:<input type="hidden" asp-for="@GetValueExpression(property)" />
}
}
@:<input type="submit" value="Delete" class="k-button k-button-md k-rounded-md k-button-solid-primary" /> |
@:<a asp-action="Index" class="k-button k-button-md k-rounded-md k-button-solid">Back to List</a>
@:</form>
@:</div>
if (!Model.IsPartialView && !Model.IsLayoutPageSelected)
{
//ClearIndent();
@:</body>
@:</html>
}
}
@functions
{
string GetValueExpression(IPropertyMetadata property)
{
return property.PropertyName;
}
string GetValueExpression(INavigationMetadata navigation)
{
return navigation.AssociationPropertyName;
}
}
@@ -0,0 +1,114 @@
@inherits Microsoft.VisualStudio.Web.CodeGeneration.Templating.RazorTemplateBase
@using Microsoft.VisualStudio.Web.CodeGeneration.EntityFrameworkCore
@using System.Collections.Generic
@using System.Linq
@@model @Model.ViewDataTypeName
b4
@{
if (Model.IsPartialView)
{
}
else if (Model.IsLayoutPageSelected)
{
@:@@{
@:ViewData["Title"] = "@Model.ViewName";
if (!string.IsNullOrEmpty(Model.LayoutPageFile))
{
@:Layout = "@Model.LayoutPageFile";
}
@:}
@:
@:<h1>@Model.ViewName</h1>
@:
}
else
{
@:@@{
@:Layout = null;
@:}
@:
@:<!DOCTYPE html>
@:
@:<html>
@:<head>
@:<meta name="viewport" content="width=device-width" />
@:<title>@Model.ViewName</title>
@:</head>
@:<body>
@:
// PushIndent(" ");
}
}
<div>
<h4>@Model.ViewDataTypeShortName</h4>
<hr />
<dl class="row">
@{
Dictionary<string, IPropertyMetadata> propertyLookup = ((IModelMetadata)Model.ModelMetadata).Properties.ToDictionary(x => x.PropertyName, x => x);
Dictionary<string, INavigationMetadata> navigationLookup = ((IModelMetadata)Model.ModelMetadata).Navigations.ToDictionary(x => x.AssociationPropertyName, x => x);
foreach (var item in Model.ModelMetadata.ModelType.GetProperties())
{
if (propertyLookup.TryGetValue(item.Name, out IPropertyMetadata property)
&& property.Scaffold && !property.IsForeignKey && !property.IsPrimaryKey)
{
<dt class = "col-sm-2">
@@Html.DisplayNameFor(model => model.@GetValueExpression(property))
</dt>
<dd class = "col-sm-10">
@@Html.DisplayFor(model => model.@GetValueExpression(property))
</dd>
}
else if (navigationLookup.TryGetValue(item.Name, out INavigationMetadata navigation))
{
<dt class = "col-sm-2">
@@Html.DisplayNameFor(model => model.@GetValueExpression(navigation))
</dt>
<dd class = "col-sm-10">
@@Html.DisplayFor(model => model.@GetValueExpression(navigation).@navigation.DisplayPropertyName)
</dd>
}
}
} </dl>
</div>
<div>
@{
string pkName = GetPrimaryKeyName();
if (pkName != null)
{
@:<a asp-action="Edit" asp-route-id="@@Model.@pkName" class="k-button k-button-md k-rounded-md k-button-solid-primary">Edit</a> |
@:<a asp-action="Index" class="k-button k-button-md k-rounded-md k-button-solid">Back to List</a>
}
else
{
@:@@Html.ActionLink("Edit", "Edit", new { /* id = Model.PrimaryKey */ }) |
@:<a asp-action="Index" class="k-button k-button-md k-rounded-md k-button-solid">Back to List</a>
}
}</div>
@{
if (!Model.IsPartialView && !Model.IsLayoutPageSelected)
{
//ClearIndent();
@:</body>
@:</html>
}
}
@functions
{
string GetPrimaryKeyName()
{
return (Model.ModelMetadata.PrimaryKeys != null && Model.ModelMetadata.PrimaryKeys.Length == 1)
? Model.ModelMetadata.PrimaryKeys[0].PropertyName
: null;
}
string GetValueExpression(IPropertyMetadata property)
{
return property.PropertyName;
}
string GetValueExpression(INavigationMetadata navigation)
{
return navigation.AssociationPropertyName;
}
}
@@ -0,0 +1,194 @@
@inherits Microsoft.VisualStudio.Web.CodeGeneration.Templating.RazorTemplateBase
@using Microsoft.VisualStudio.Web.CodeGeneration.EntityFrameworkCore
@using System.Collections.Generic
@using System.Linq
@@model @Model.ViewDataTypeName
@{
if (Model.IsPartialView)
{
}
else if (Model.IsLayoutPageSelected)
{
@:@@{
@:ViewData["Title"] = "@Model.ViewName";
if (!string.IsNullOrEmpty(Model.LayoutPageFile))
{
@:Layout = "@Model.LayoutPageFile";
}
@:}
@:
@:<h1>@Model.ViewName</h1>
@:
}
else
{
@:@@{
@:Layout = null;
@:}
@:
@:<!DOCTYPE html>
@:
@:<html>
@:<head>
@:<meta name="viewport" content="width=device-width" />
@:<title>@Model.ViewName</title>
@:</head>
@:<body>
@:
// PushIndent(" ");
}
@:<h4>@Model.ViewDataTypeShortName</h4>
@:<hr />
@:<div class="row">
@:<div class="col-md-4">
string formId = Model.ViewDataTypeShortName + "Form";
@:<form asp-action="@Model.ViewName" id="@formId">
@:<div asp-validation-summary="ModelOnly" class="text-danger"></div>
foreach (PropertyMetadata property in Model.ModelMetadata.Properties)
{
if (property.IsPrimaryKey)
{
@:<input type="hidden" asp-for="@property.PropertyName" />
continue;
}
if (property.Scaffold)
{
if (property.IsReadOnly)
{
continue;
}
if (property.IsForeignKey)
{
@:<div class="form-group">
@:<label asp-for="@property.PropertyName" class="control-label"></label>
@:<select asp-for="@property.PropertyName" class="form-control" asp-items="ViewBag.@property.PropertyName"></select>
@:<span asp-validation-for="@property.PropertyName" class="text-danger"></span>
@:
@:</div>
continue;
}
bool isCheckbox = property.TypeName.Equals("System.Boolean");
bool isDatetime = property.TypeName.Contains("System.DateTime");
if (isCheckbox)
{
@:<div class="form-group">
@:@@Html.Kendo().CheckBoxFor(m => m.@property.PropertyName)
@:<label asp-for="@property.PropertyName"></label>
@:<span asp-validation-for="@property.PropertyName" class="text-danger k-invalid-msg" data-for="@property.PropertyName"></span>
@:</div>
}
else if (isDatetime)
{
@:<div class="form-group">
@:<label asp-for="@property.PropertyName"></label>
@:<kendo-datetimepicker for="@property.PropertyName" />
@:<span asp-validation-for="@property.PropertyName" class="text-danger k-invalid-msg" data-for="@property.PropertyName"></span>
@:</div>
}
else if (IsNumericType(property))
{
@:<div class="form-group">
@:<label asp-for="@property.PropertyName"></label>
@:<kendo-numerictextbox for="@property.PropertyName" />
@:<span asp-validation-for="@property.PropertyName" class="text-danger k-invalid-msg" data-for="@property.PropertyName"></span>
@:</div>
}
else if (property.IsEnum && !property.IsEnumFlags)
{
@:<div class="form-group">
@:<label asp-for="@property.PropertyName"></label>
@:<select asp-for="@property.PropertyName"></select>
@:<span asp-validation-for="@property.PropertyName" class="text-danger k-invalid-msg" data-for="@property.PropertyName"></span>
@:</div>
}
else if (property.IsMultilineText)
{
@:<div class="form-group">
@:<label asp-for="@property.PropertyName"></label>
@:<textarea asp-for="@property.PropertyName" class="k-textbox"></textarea>
@:<span asp-validation-for="@property.PropertyName" class="text-danger k-invalid-msg" data-for="@property.PropertyName"></span>
@:</div>
}
else
{
@:<div class="form-group">
@:<label asp-for="@property.PropertyName"></label>
@:<input asp-for="@property.PropertyName" class="k-textbox" />
@:<span asp-validation-for="@property.PropertyName" class="text-danger k-invalid-msg" data-for="@property.PropertyName"></span>
@:</div>
}
}
// Ideally we shouldn't be here but if the user marks the foreign key as [ScaffoldColumn(false)], we want to atleast try to make it work.
else if (property.IsForeignKey)
{
@:<input type="hidden" asp-for="@property.PropertyName" />
continue;
}
}
}
<div class="form-group">
<input type="submit" value="Save" class="k-button k-button-md k-rounded-md k-button-solid-primary" />
</div>
</form>
</div>
</div>
<div>
<a asp-action="Index" class="k-button k-button-md k-rounded-md k-button-solid">Back to List</a>
</div>
@{
if (Model.ReferenceScriptLibraries)
{
@:<script>
@:$(document).ready(function () {
@:$("#@formId").kendoValidator({
@:// You can define your custom rules here:
@://rules: {}
@:});
@:})
@:</script>
}
// The following code closes the tag used in the case of a view using a layout page and the body and html tags in the case of a regular view page
if (!Model.IsPartialView && !Model.IsLayoutPageSelected)
{
@:</body>
@:</html>
}
}
@functions
{
string GetAssociationName(IPropertyMetadata property)
{
//Todo: Implement properly.
return property.PropertyName;
}
string GetValueExpression(IPropertyMetadata property)
{
return property.PropertyName;
}
bool IsNumericType(IPropertyMetadata property)
{
var types = new[] {
"System.Byte",
"System.SByte",
"System.UInt16",
"System.UInt32",
"System.UInt64",
"System.Int16",
"System.Int32",
"System.Int64",
"System.Decimal",
"System.Double",
"System.Single"
};
return types.Any(property.TypeName.Contains);
}
}
@@ -0,0 +1,52 @@
@inherits Microsoft.VisualStudio.Web.CodeGeneration.Templating.RazorTemplateBase
@using Microsoft.VisualStudio.Web.CodeGeneration.EntityFrameworkCore
@using System.Collections.Generic
@using System.Linq
@{
if (Model.IsPartialView)
{
@:@@*
@: For more information on enabling MVC for empty projects, visit http://go.microsoft.com/fwlink/?LinkID=397860
@:*@@
}
else if (Model.IsLayoutPageSelected)
{
@:@@{
@:ViewData["Title"] = "@Model.ViewName";
if (!string.IsNullOrEmpty(Model.LayoutPageFile))
{
@:Layout = "@Model.LayoutPageFile";
}
@:}
@:
@:<h1>@Model.ViewName</h1>
@:
}
else
{
@:@@{
@:Layout = null;
@:}
@:
@:<!DOCTYPE html>
@:
@:<html>
@:<head>
@:<meta name="viewport" content="width=device-width" />
@:<title>@Model.ViewName</title>
@:</head>
@:<body>
}
if (Model.ReferenceScriptLibraries)
{
}
// The following code closes the tag used in the case of a view using a layout page and the body and html tags in the case of a regular view page
if (!Model.IsPartialView && !Model.IsLayoutPageSelected)
{
@:</body>
@:</html>
}
}
@@ -0,0 +1,139 @@
@inherits Microsoft.VisualStudio.Web.CodeGeneration.Templating.RazorTemplateBase
@using Microsoft.VisualStudio.Web.CodeGeneration.EntityFrameworkCore
@using System.Collections.Generic
@using System.Linq
@@model @GetEnumerableTypeExpression(Model.ViewDataTypeName)
@{
if (Model.IsPartialView)
{
}
else if (Model.IsLayoutPageSelected)
{
@:@@{
@:ViewData["Title"] = "@Model.ViewName";
if (!string.IsNullOrEmpty(Model.LayoutPageFile))
{
@:Layout = "@Model.LayoutPageFile";
}
@:}
@:
@:<h1>@Model.ViewName</h1>
@:
}
else
{
@:@@{
@:Layout = null;
@:}
@:
@:<!DOCTYPE html>
@:
@:<html>
@:<head>
@:<meta name="viewport" content="width=device-width" />
@:<title>@Model.ViewName</title>
@:</head>
@:<body>
// PushIndent(" ");
}
@:<p>
@:<a asp-action="Create" class="k-button k-button-md k-rounded-md k-button-solid">Create New</a>
@:</p>
@:<table class="table">
@:<thead>
@:<tr>
Dictionary<string, IPropertyMetadata> propertyLookup = ((IModelMetadata)Model.ModelMetadata).Properties.ToDictionary(x => x.PropertyName, x => x);
Dictionary<string, INavigationMetadata> navigationLookup = ((IModelMetadata)Model.ModelMetadata).Navigations.ToDictionary(x => x.AssociationPropertyName, x => x);
foreach (var item in Model.ModelMetadata.ModelType.GetProperties())
{
if (propertyLookup.TryGetValue(item.Name, out IPropertyMetadata property)
&& property.Scaffold && !property.IsForeignKey && !property.IsPrimaryKey)
{
<th>
@@Html.DisplayNameFor(model => model.@GetValueExpression(property))
</th>
}
else if (navigationLookup.TryGetValue(item.Name, out INavigationMetadata navigation))
{
<th>
@@Html.DisplayNameFor(model => model.@GetValueExpression(navigation))
</th>
}
}
@:<th></th>
@:</tr>
@:</thead>
@:<tbody>
@:@@foreach (var item in Model) {
@:<tr>
foreach (var item in Model.ModelMetadata.ModelType.GetProperties())
{
if (propertyLookup.TryGetValue(item.Name, out IPropertyMetadata property)
&& property.Scaffold && !property.IsForeignKey && !property.IsPrimaryKey)
{
<td>
@@Html.DisplayFor(modelItem => item.@GetValueExpression(property))
</td>
}
else if (navigationLookup.TryGetValue(item.Name, out INavigationMetadata navigation))
{
<td>
@@Html.DisplayFor(modelItem => item.@GetValueExpression(navigation).@navigation.DisplayPropertyName)
</td>
}
}
string pkName = GetPrimaryKeyName();
if (pkName != null)
{
@:<td>
@:<a asp-action="Edit" asp-route-id="@@item.@pkName" class="k-button k-button-md k-rounded-md k-button-solid-primary">Edit</a> |
@:<a asp-action="Details" asp-route-id="@@item.@pkName" class="k-button k-button-md k-rounded-md k-button-solid">Details</a> |
@:<a asp-action="Delete" asp-route-id="@@item.@pkName" class="k-button k-button-md k-rounded-md k-button-solid">Delete</a>
@:</td>
}
else
{
<td>
@@Html.ActionLink("Edit", "Edit", new { /* id=item.PrimaryKey */ }) |
@@Html.ActionLink("Details", "Details", new { /* id=item.PrimaryKey */ }) |
@@Html.ActionLink("Delete", "Delete", new { /* id=item.PrimaryKey */ })
</td>
}
@:</tr>
@:}
@:</tbody>
@:</table>
if(!Model.IsPartialView && !Model.IsLayoutPageSelected)
{
//ClearIndent();
@:</body>
@:</html>
}
}
@functions
{
string GetPrimaryKeyName()
{
return (Model.ModelMetadata.PrimaryKeys != null && Model.ModelMetadata.PrimaryKeys.Length == 1)
? Model.ModelMetadata.PrimaryKeys[0].PropertyName
: null;
}
string GetValueExpression(IPropertyMetadata property)
{
return property.PropertyName;
}
string GetValueExpression(INavigationMetadata navigation)
{
return navigation.AssociationPropertyName;
}
string GetEnumerableTypeExpression(string typeName)
{
return "IEnumerable<" + typeName + ">";
}
}

Some files were not shown because too many files have changed in this diff Show More