diff --git a/src/Templates/Microsoft.FluentUI.AspNetCore.Templates.csproj b/src/Templates/Microsoft.FluentUI.AspNetCore.Templates.csproj index 15c6807493..341548d3fe 100644 --- a/src/Templates/Microsoft.FluentUI.AspNetCore.Templates.csproj +++ b/src/Templates/Microsoft.FluentUI.AspNetCore.Templates.csproj @@ -24,10 +24,11 @@ - 9.3.1 - 9.6.0 - 8.0.17 - 9.0.6 + 9.3.2 + 9.7.0 + 8.0.18 + 9.0.7 + 10.0.0-preview.6.25358.103 1.9.0 1.12.0 1.12.0 @@ -77,7 +78,7 @@ <_TemplatesForPackage Include="$(IntermediateOutputPath)\content\templates\**\*" /> - + @@ -97,6 +98,7 @@ .Replace('!!REPLACE_WITH_LATEST_ASPIRE_VERSION!!', '$(AspirePackageVersion)') .Replace('!!REPLACE_WITH_ASPNETCORE_8_VERSION!!', '$(MicrosoftAspNetCorePackageVersionForNet8)') .Replace('!!REPLACE_WITH_ASPNETCORE_9_VERSION!!', '$(MicrosoftAspNetCorePackageVersionForNet9)') + .Replace('!!REPLACE_WITH_ASPNETCORE_10_VERSION!!', '$(MicrosoftAspNetCorePackageVersionForNet10)') .Replace('!!REPLACE_WITH_DOTNET_EXTENSIONS_VERSION!!', '$(MicrosoftExtensionsHttpResiliencePackageVersion)') .Replace('!!REPLACE_WITH_OTEL_LTS_VERSION!!', '$(OpenTelemetryLTSVersion)') .Replace('!!REPLACE_WITH_OTEL_EXPORTER_VERSION!!', '$(OpenTelemetryExporterOpenTelemetryProtocolVersion)') @@ -104,7 +106,6 @@ .Replace('!!REPLACE_WITH_OTEL_ASPNETCORE_VERSION!!', '$(OpenTelemetryInstrumentationAspNetCoreVersion)') .Replace('!!REPLACE_WITH_OTEL_HTTP_VERSION!!', '$(OpenTelemetryInstrumentationHttpVersion)') .Replace('!!REPLACE_WITH_OTEL_RUNTIME_VERSION!!', '$(OpenTelemetryInstrumentationRuntimeVersion)') )" - Overwrite="true" /> diff --git a/src/Templates/Properties/launchSettings.json b/src/Templates/Properties/launchSettings.json deleted file mode 100644 index 5468b30512..0000000000 --- a/src/Templates/Properties/launchSettings.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "profiles": { - "Microsoft.FluentUI.AspNetCore.Templates": { - "commandName": "Project", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - }, - "applicationUrl": "https://localhost:58017;http://localhost:58018" - } - } -} \ No newline at end of file diff --git a/src/Templates/templates/blazorweb-csharp/.template.config/dotnetcli.host.json b/src/Templates/templates/blazorweb-csharp-10/.template.config/dotnetcli.host.json similarity index 100% rename from src/Templates/templates/blazorweb-csharp/.template.config/dotnetcli.host.json rename to src/Templates/templates/blazorweb-csharp-10/.template.config/dotnetcli.host.json diff --git a/src/Templates/templates/blazorweb-csharp/.template.config/icon.png b/src/Templates/templates/blazorweb-csharp-10/.template.config/icon.png similarity index 100% rename from src/Templates/templates/blazorweb-csharp/.template.config/icon.png rename to src/Templates/templates/blazorweb-csharp-10/.template.config/icon.png diff --git a/src/Templates/templates/blazorweb-csharp-10/.template.config/ide.host.json b/src/Templates/templates/blazorweb-csharp-10/.template.config/ide.host.json new file mode 100644 index 0000000000..71b187aeeb --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-10/.template.config/ide.host.json @@ -0,0 +1,30 @@ +{ + "$schema": "https://json.schemastore.org/ide.host", + "order": 610, + "icon": "icon.png", + "disableHttpsSymbol": "NoHttps", + "symbolInfo": [ + { + "id": "InteractivityPlatform", + "isVisible": true, + "persistenceScope": "templateGroup" + }, + { + "id": "InteractivityLocation", + "isVisible": true, + "persistenceScope": "templateGroup" + }, + { + "id": "IncludeSampleContent", + "isVisible": true, + "persistenceScope": "shared", + "persistenceScopeName": "Microsoft" + }, + { + "id": "UseProgramMain", + "isVisible": true, + "persistenceScope": "shared", + "persistenceScopeName": "Microsoft" + } + ] +} diff --git a/src/Templates/templates/blazorweb-csharp-10/.template.config/localize/templatestrings.cs.json b/src/Templates/templates/blazorweb-csharp-10/.template.config/localize/templatestrings.cs.json new file mode 100644 index 0000000000..135fad38a2 --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-10/.template.config/localize/templatestrings.cs.json @@ -0,0 +1,44 @@ +{ + "author": "Microsoft", + "name": "Webová aplikace Fluent Blazor", + "description": "Šablona projektu pro vytvoření webové aplikace Blazor, která podporuje vykreslování na straně serveru i interaktivitu klienta. Tato šablona se dá použít pro webové aplikace s bohatými dynamickými uživatelskými rozhraními (UI).", + "symbols/Framework/description": "Cílová architektura pro projekt", + "symbols/Framework/choices/net10.0/description": "Cíl net10.0", + "symbols/UserSecretsId/description": "ID, které se má použít pro tajné kódy (používá se s individuálním ověřováním).", + "symbols/skipRestore/description": "Pokud se tato možnost zadá, přeskočí automatické obnovení projektu při vytvoření.", + "symbols/ExcludeLaunchSettings/description": "Určuje, jestli se má z vygenerované šablony vyloučit soubor launchSettings.json.", + "symbols/kestrelHttpPort/description": "Číslo portu, který se má použít pro koncový bod HTTP v souboru launchSettings.json.", + "symbols/kestrelHttpsPort/description": "Číslo portu, který se má použít pro koncový bod HTTPS v souboru launchSettings.json. Tato možnost se dá použít jenom v případě, že se nepoužije parametr no-https (no-https se bude ignorovat, pokud se použije individuální ověřování).", + "symbols/iisHttpPort/description": "Číslo portu, který se má použít pro koncový bod IIS Express HTTP v souboru launchSettings.json.", + "symbols/iisHttpsPort/description": "Číslo portu, který se má použít pro koncový bod IIS Express HTTPS v souboru launchSettings.json. Tato možnost se dá použít jenom v případě, že se nepoužije parametr no-https (no-https se bude ignorovat, pokud se použije individuální ověřování).", + "symbols/InteractivityPlatform/displayName": "_Interaktivní režim vykreslování", + "symbols/InteractivityPlatform/description": "Umožňuje zvolit, který interaktivní režim vykreslování se má použít pro interaktivní komponenty.", + "symbols/InteractivityPlatform/choices/None/displayName": "Žádné", + "symbols/InteractivityPlatform/choices/None/description": "Žádná interaktivita (pouze vykreslování statického serveru)", + "symbols/InteractivityPlatform/choices/Server/displayName": "Server", + "symbols/InteractivityPlatform/choices/Server/description": "Spouští se na serveru.", + "symbols/InteractivityPlatform/choices/WebAssembly/displayName": "WebAssembly", + "symbols/InteractivityPlatform/choices/WebAssembly/description": "Spustí se v prohlížeči pomocí WebAssembly.", + "symbols/InteractivityPlatform/choices/Auto/displayName": "Automaticky (Server a WebAssembly)", + "symbols/InteractivityPlatform/choices/Auto/description": "Při stahování prostředků WebAssembly používá Server a pak WebAssembly.", + "symbols/InteractivityLocation/displayName": "U_místění interaktivity", + "symbols/InteractivityLocation/description": "Zvolí, které komponenty budou mít povolené interaktivní vykreslování.", + "symbols/InteractivityLocation/choices/InteractivePerPage/displayName": "Na stránku nebo komponentu", + "symbols/InteractivityLocation/choices/InteractivePerPage/description": "Interaktivita se používá pro jednotlivé stránky nebo jednotlivé komponenty.", + "symbols/InteractivityLocation/choices/InteractiveGlobal/displayName": "Globální", + "symbols/InteractivityLocation/choices/InteractiveGlobal/description": "Interaktivita se používá na kořenové úrovni.", + "symbols/IncludeSampleContent/displayName": "Zahrnout _ukázkové stránky", + "symbols/IncludeSampleContent/description": "Nastavuje, jestli se mají přidávat ukázkové stránky a styly pro demonstraci základních vzorů použití.", + "symbols/Empty/description": "Nastavuje, jestli se mají vynechat ukázkové stránky a styly, které demonstrují základní vzory použití.", + "symbols/auth/choices/None/description": "Bez ověřování", + "symbols/auth/choices/Individual/description": "Ověřování Individual", + "symbols/auth/description": "Typ ověřování, který se má použít", + "symbols/UseLocalDB/description": "Určuje, jestli se má použít LocalDB namísto SQLite. Tato možnost platí jenom v případě, že je zadaná možnost --auth Individual.", + "symbols/AllInteractive/displayName": "_Povolit interaktivní vykreslování globálně na celém webu", + "symbols/AllInteractive/description": "Konfiguruje, jestli se má každá stránka nastavit jako interaktivní, a to použitím interaktivního režimu vykreslování na nejvyšší úrovni. Pokud se nastaví na false, stránky budou ve výchozím nastavení používat vykreslování statického serveru a dají se označit jako interaktivní pro jednotlivé stránky nebo komponenty.", + "symbols/NoHttps/description": "Určuje, jestli se má vypnout protokol HTTPS. Tato možnost platí jenom v případě, že se pro --auth nepoužívá jednotlivec.", + "symbols/UseProgramMain/displayName": "Nepoužívat _příkazy nejvyšší úrovně", + "symbols/UseProgramMain/description": "Určuje, jestli se má místo příkazů nejvyšší úrovně generovat explicitní třída Program a metoda Main.", + "postActions/restore/description": "Obnoví balíčky NuGet vyžadované tímto projektem.", + "postActions/restore/manualInstructions/default/text": "Spustit dotnet restore" +} diff --git a/src/Templates/templates/blazorweb-csharp-10/.template.config/localize/templatestrings.de.json b/src/Templates/templates/blazorweb-csharp-10/.template.config/localize/templatestrings.de.json new file mode 100644 index 0000000000..f51a3d07e4 --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-10/.template.config/localize/templatestrings.de.json @@ -0,0 +1,44 @@ +{ + "author": "Microsoft", + "name": "Fluent Blazor Web App", + "description": "Eine Projektvorlage zum Erstellen einer Blazor-Web-App, die sowohl serverseitiges Rendering als auch Clientinteraktivität unterstützt. Diese Vorlage kann für Web-Apps mit umfangreichen dynamischen Benutzeroberflächen (UIs) verwendet werden.", + "symbols/Framework/description": "Das Zielframework für das Projekt.", + "symbols/Framework/choices/net10.0/description": "Ziel.-NET10.0", + "symbols/UserSecretsId/description": "Die für Geheimnisse zu verwendende ID (Verwendung mit individueller Authentifizierung).", + "symbols/skipRestore/description": "Wenn angegeben, wird die automatische Wiederherstellung des Projekts beim Erstellen übersprungen.", + "symbols/ExcludeLaunchSettings/description": "Ob launchSettings.json aus der generierten Vorlage ausgeschlossen werden soll.", + "symbols/kestrelHttpPort/description": "Portnummer, die für den HTTP Endpunkt in launchSettings.json verwendet werden soll.", + "symbols/kestrelHttpsPort/description": "Portnummer, die für den HTTPS Endpunkt in launchSettings.json verwendet werden soll. Diese Option ist nur anwendbar, wenn der Parameter no-https nicht verwendet wird (no-https wird ignoriert, wenn die individuelle Authentifizierung verwendet wird).", + "symbols/iisHttpPort/description": "Portnummer, die für den IIS Express HTTP Endpunkt in launchSettings.json verwendet werden soll.", + "symbols/iisHttpsPort/description": "Portnummer, die für den IIS Express-HTTPS-Endpunkt in launchSettings.json verwendet werden soll. Diese Option ist nur anwendbar, wenn der Parameter no-https nicht verwendet wird (no-https wird ignoriert, wenn die individuelle Authentifizierung verwendet wird).", + "symbols/InteractivityPlatform/displayName": "_Interaktiver Rendermodus", + "symbols/InteractivityPlatform/description": "Wählt aus, welcher interaktive Rendermodus für interaktive Komponenten verwendet werden soll", + "symbols/InteractivityPlatform/choices/None/displayName": "Keine", + "symbols/InteractivityPlatform/choices/None/description": "Keine Interaktivität (nur statisches Serverrendering)", + "symbols/InteractivityPlatform/choices/Server/displayName": "Server", + "symbols/InteractivityPlatform/choices/Server/description": "Wird auf dem Server ausgeführt", + "symbols/InteractivityPlatform/choices/WebAssembly/displayName": "WebAssembly", + "symbols/InteractivityPlatform/choices/WebAssembly/description": "Wird im Browser mithilfe von WebAssembly ausgeführt", + "symbols/InteractivityPlatform/choices/Auto/displayName": "Automatisch (Server und WebAssembly)", + "symbols/InteractivityPlatform/choices/Auto/description": "Verwendet Server beim Herunterladen von WebAssembly-Ressourcen und dann WebAssembly", + "symbols/InteractivityLocation/displayName": "Interaktivitäts_location", + "symbols/InteractivityLocation/description": "Wählt aus, für welche Komponenten das interaktive Rendering aktiviert werden soll", + "symbols/InteractivityLocation/choices/InteractivePerPage/displayName": "Pro Seite/ Komponente", + "symbols/InteractivityLocation/choices/InteractivePerPage/description": "Interaktivität wird auf Seiten- oder Komponentenbasis angewendet", + "symbols/InteractivityLocation/choices/InteractiveGlobal/displayName": "Global", + "symbols/InteractivityLocation/choices/InteractiveGlobal/description": "Interaktivität wird auf Stammebene angewendet", + "symbols/IncludeSampleContent/displayName": "_Beispielseiten einschließen", + "symbols/IncludeSampleContent/description": "Konfiguriert, ob Beispielseiten und Stile hinzugefügt werden, um grundlegende Verwendungsmuster zu veranschaulichen.", + "symbols/Empty/description": "Konfiguriert, ob Beispielseiten und Formatierungen weggelassen werden sollen, die grundlegende Verwendungsmuster veranschaulichen.", + "symbols/auth/choices/None/description": "Keine Authentifizierung", + "symbols/auth/choices/Individual/description": "Individuelle Authentifizierung", + "symbols/auth/description": "Der zu verwendende Authentifizierungstyp", + "symbols/UseLocalDB/description": "Ob LocalDB anstelle von SQLite verwendet werden soll. Diese Option gilt nur, wenn \"--auth Individual\" angegeben ist.", + "symbols/AllInteractive/displayName": "_Aktivieren des interaktiven Renderings global auf der gesamten Website", + "symbols/AllInteractive/description": "Konfiguriert, ob jede Seite interaktiv werden soll, indem ein interaktiver Rendermodus auf der obersten Ebene angewendet wird. False gibt an, dass Seiten standardmäßig statisches Serverrendering verwenden und auf Seiten- oder Komponentenbasis als interaktiv markiert werden können.", + "symbols/NoHttps/description": "Ob HTTPS deaktiviert werden soll. Diese Option gilt nur, wenn \"Individual\" nicht für \"--auth\" verwendet wird.", + "symbols/UseProgramMain/displayName": "Keine Anweisungen_der obersten Ebene verwenden", + "symbols/UseProgramMain/description": "Gibt an, ob anstelle von Anweisungen der obersten Ebene eine explizite Programmklasse und eine Main-Methode generiert werden soll.", + "postActions/restore/description": "„NuGet-Pakete“ wiederherstellen, die für dieses Projekt erforderlich sind.", + "postActions/restore/manualInstructions/default/text": "„dotnet restore“ ausführen" +} diff --git a/src/Templates/templates/blazorweb-csharp-10/.template.config/localize/templatestrings.en.json b/src/Templates/templates/blazorweb-csharp-10/.template.config/localize/templatestrings.en.json new file mode 100644 index 0000000000..19ddf56694 --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-10/.template.config/localize/templatestrings.en.json @@ -0,0 +1,44 @@ +{ + "author": "Microsoft", + "name": "Fluent Blazor Web App", + "description": "A project template for creating a Blazor web app that supports both server-side rendering and client interactivity and uses the Fluent components library. This template can be used for web apps with rich dynamic user interfaces (UIs).", + "symbols/Framework/description": "The target framework for the project.", + "symbols/Framework/choices/net10.0/description": "Target net10.0", + "symbols/UserSecretsId/description": "The ID to use for secrets (use with Individual auth).", + "symbols/skipRestore/description": "If specified, skips the automatic restore of the project on create.", + "symbols/ExcludeLaunchSettings/description": "Whether to exclude launchSettings.json from the generated template.", + "symbols/kestrelHttpPort/description": "Port number to use for the HTTP endpoint in launchSettings.json.", + "symbols/kestrelHttpsPort/description": "Port number to use for the HTTPS endpoint in launchSettings.json. This option is only applicable when the parameter no-https is not used (no-https will be ignored if Individual auth is used).", + "symbols/iisHttpPort/description": "Port number to use for the IIS Express HTTP endpoint in launchSettings.json.", + "symbols/iisHttpsPort/description": "Port number to use for the IIS Express HTTPS endpoint in launchSettings.json. This option is only applicable when the parameter no-https is not used (no-https will be ignored if Individual auth is used).", + "symbols/InteractivityPlatform/displayName": "_Interactive render mode", + "symbols/InteractivityPlatform/description": "Chooses which interactive render mode to use for interactive components", + "symbols/InteractivityPlatform/choices/None/displayName": "None", + "symbols/InteractivityPlatform/choices/None/description": "No interactivity (static server rendering only)", + "symbols/InteractivityPlatform/choices/Server/displayName": "Server", + "symbols/InteractivityPlatform/choices/Server/description": "Runs on the server", + "symbols/InteractivityPlatform/choices/WebAssembly/displayName": "WebAssembly", + "symbols/InteractivityPlatform/choices/WebAssembly/description": "Runs in the browser using WebAssembly", + "symbols/InteractivityPlatform/choices/Auto/displayName": "Auto (Server and WebAssembly)", + "symbols/InteractivityPlatform/choices/Auto/description": "Uses Server while downloading WebAssembly assets, then uses WebAssembly", + "symbols/InteractivityLocation/displayName": "Interactivity _location", + "symbols/InteractivityLocation/description": "Chooses which components will have interactive rendering enabled", + "symbols/InteractivityLocation/choices/InteractivePerPage/displayName": "Per page/component", + "symbols/InteractivityLocation/choices/InteractivePerPage/description": "Interactivity is applied on a per-page or per-component basis", + "symbols/InteractivityLocation/choices/InteractiveGlobal/displayName": "Global", + "symbols/InteractivityLocation/choices/InteractiveGlobal/description": "Interactivity is applied at the root level", + "symbols/IncludeSampleContent/displayName": "Include _sample pages", + "symbols/IncludeSampleContent/description": "Configures whether to add sample pages and styling to demonstrate basic usage patterns.", + "symbols/Empty/description": "Configures whether to omit sample pages and styling that demonstrate basic usage patterns.", + "symbols/auth/choices/None/description": "No authentication", + "symbols/auth/choices/Individual/description": "Individual authentication", + "symbols/auth/description": "The type of authentication to use", + "symbols/UseLocalDB/description": "Whether to use LocalDB instead of SQLite. This option only applies if --auth Individual is specified.", + "symbols/AllInteractive/displayName": "_Enable interactive rendering globally throughout the site", + "symbols/AllInteractive/description": "Configures whether to make every page interactive by applying an interactive render mode at the top level. If false, pages will use static server rendering by default, and can be marked interactive on a per-page or per-component basis.", + "symbols/NoHttps/description": "Whether to turn off HTTPS. This option only applies if Individual isn't used for --auth.", + "symbols/UseProgramMain/displayName": "Do not use _top-level statements", + "symbols/UseProgramMain/description": "Whether to generate an explicit Program class and Main method instead of top-level statements.", + "postActions/restore/description": "Restore NuGet packages required by this project.", + "postActions/restore/manualInstructions/default/text": "Run 'dotnet restore'" +} diff --git a/src/Templates/templates/blazorweb-csharp-10/.template.config/localize/templatestrings.es.json b/src/Templates/templates/blazorweb-csharp-10/.template.config/localize/templatestrings.es.json new file mode 100644 index 0000000000..3ed4b63366 --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-10/.template.config/localize/templatestrings.es.json @@ -0,0 +1,44 @@ +{ + "author": "Microsoft", + "name": "Aplicación web Fluent Blazor", + "description": "Plantilla de proyecto para crear una aplicación web de Blazor que admita tanto la representación del lado del servidor como la interactividad del cliente. Esta plantilla se puede usar para las aplicaciones web con interfaces de usuario dinámicas enriquecidas.", + "symbols/Framework/description": "Marco de destino del proyecto.", + "symbols/Framework/choices/net10.0/description": "Destino net10.0", + "symbols/UserSecretsId/description": "Identificador que se va a usar para los secretos (se usa con la autenticación individual).", + "symbols/skipRestore/description": "Si se especifica, se omite la restauración automática del proyecto durante la creación.", + "symbols/ExcludeLaunchSettings/description": "Indica si se va a excluir launchSettings.json de la plantilla generada.", + "symbols/kestrelHttpPort/description": "Número de puerto que se va a usar para el punto de conexión HTTP en launchSettings.json.", + "symbols/kestrelHttpsPort/description": "Número de puerto que se va a usar para el punto de conexión HTTPS en launchSettings.json. Esta opción solo es aplicable cuando no se usa el parámetro no-https (no-https se omitirá si se usa la autenticación individual).", + "symbols/iisHttpPort/description": "Número de puerto que se va a usar para el punto de conexión HTTP de IIS Express en launchSettings.json.", + "symbols/iisHttpsPort/description": "Número de puerto que se va a usar para el punto de conexión HTTPS de IIS Express en launchSettings.json. Esta opción solo es aplicable cuando no se usa el parámetro no-https (no-https se omitirá si se usa la autenticación individual).", + "symbols/InteractivityPlatform/displayName": "_Interactive modo de representación", + "symbols/InteractivityPlatform/description": "Elige qué modo de representación interactiva usar para los componentes interactivos", + "symbols/InteractivityPlatform/choices/None/displayName": "Ninguno", + "symbols/InteractivityPlatform/choices/None/description": "Sin interactividad (solo representación de servidor estático)", + "symbols/InteractivityPlatform/choices/Server/displayName": "Servidor", + "symbols/InteractivityPlatform/choices/Server/description": "Se ejecuta en el servidor", + "symbols/InteractivityPlatform/choices/WebAssembly/displayName": "WebAssembly", + "symbols/InteractivityPlatform/choices/WebAssembly/description": "Se ejecuta en el explorador mediante WebAssembly", + "symbols/InteractivityPlatform/choices/Auto/displayName": "Automático (Servidor y WebAssembly)", + "symbols/InteractivityPlatform/choices/Auto/description": "Usa el servidor al descargar los activos de WebAssembly y, a continuación, usa WebAssembly", + "symbols/InteractivityLocation/displayName": "Interactividad _ubicación", + "symbols/InteractivityLocation/description": "Elige qué componentes tendrán habilitada la representación interactiva", + "symbols/InteractivityLocation/choices/InteractivePerPage/displayName": "Por página o componente", + "symbols/InteractivityLocation/choices/InteractivePerPage/description": "La interactividad se aplica por página o por componente", + "symbols/InteractivityLocation/choices/InteractiveGlobal/displayName": "Global", + "symbols/InteractivityLocation/choices/InteractiveGlobal/description": "La interactividad se aplica en el nivel raíz", + "symbols/IncludeSampleContent/displayName": "Incluir páginas de _sample", + "symbols/IncludeSampleContent/description": "Configura si se van a agregar páginas de ejemplo y estilos para mostrar patrones de uso básicos.", + "symbols/Empty/description": "Configura si se omiten las páginas de ejemplo y los estilos que muestran patrones de uso básicos.", + "symbols/auth/choices/None/description": "Sin autenticación", + "symbols/auth/choices/Individual/description": "Autenticación individual", + "symbols/auth/description": "El tipo de autenticación que se va a usar", + "symbols/UseLocalDB/description": "Si se va a usar LocalDB en lugar de SQLite. Esta opción solo se aplica si se especifica --auth Individual.", + "symbols/AllInteractive/displayName": "_Enable representación interactiva globalmente en todo el sitio", + "symbols/AllInteractive/description": "Configura si todas las páginas son interactivas aplicando un modo de representación interactivo en el nivel superior. Si es false, las páginas usarán la representación de servidor estático de forma predeterminada y se pueden marcar como interactivas por página o por componente.", + "symbols/NoHttps/description": "Si se va a desactivar HTTPS. Esta opción solo se aplica si individual no se usa para --auth.", + "symbols/UseProgramMain/displayName": "No usar instrucciones de _nivel superior", + "symbols/UseProgramMain/description": "Indica si se debe generar una clase Program explícita y un método Main en lugar de instrucciones de nivel superior.", + "postActions/restore/description": "Restaure los paquetes NuGet necesarios para este proyecto.", + "postActions/restore/manualInstructions/default/text": "Ejecutar \"dotnet restore\"" +} diff --git a/src/Templates/templates/blazorweb-csharp-10/.template.config/localize/templatestrings.fr.json b/src/Templates/templates/blazorweb-csharp-10/.template.config/localize/templatestrings.fr.json new file mode 100644 index 0000000000..806966875c --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-10/.template.config/localize/templatestrings.fr.json @@ -0,0 +1,44 @@ +{ + "author": "Microsoft", + "name": "Application web Fluent Blazor", + "description": "Modèle de projet pour la création d’une application web Blazor qui prend en charge le rendu côté serveur et l’interactivité du client. Ce modèle peut être utilisé pour les applications web avec des interfaces utilisateur dynamiques enrichies.", + "symbols/Framework/description": "Framework cible du projet.", + "symbols/Framework/choices/net10.0/description": "Net10.0 cible", + "symbols/UserSecretsId/description": "ID à utiliser pour les secrets (à utiliser avec l’authentification individuelle).", + "symbols/skipRestore/description": "S’il est spécifié, ignore la restauration automatique du projet lors de la création.", + "symbols/ExcludeLaunchSettings/description": "Indique s’il faut exclure launchSettings.json du modèle généré.", + "symbols/kestrelHttpPort/description": "Numéro de port à utiliser pour le point de terminaison HTTP dans launchSettings.json.", + "symbols/kestrelHttpsPort/description": "Numéro de port à utiliser pour le point de terminaison HTTPS dans launchSettings.json. Cette option s’applique uniquement lorsque le paramètre no-https n’est pas utilisé (no-https sera ignoré si l’authentification individuelle est utilisée).", + "symbols/iisHttpPort/description": "Numéro de port à utiliser pour le point de terminaison HTTP IIS Express dans launchSettings.json.", + "symbols/iisHttpsPort/description": "Numéro de port à utiliser pour le point de terminaison HTTPS IIS Express dans launchSettings.json. Cette option s’applique uniquement lorsque le paramètre no-https n’est pas utilisé (no-https sera ignoré si l’authentification individuelle est utilisée).", + "symbols/InteractivityPlatform/displayName": "_Mode de rendu interactif", + "symbols/InteractivityPlatform/description": "Choisit le mode de rendu interactif à utiliser pour les composants interactifs", + "symbols/InteractivityPlatform/choices/None/displayName": "Aucun", + "symbols/InteractivityPlatform/choices/None/description": "Aucune interactivité (rendu de serveur statique uniquement)", + "symbols/InteractivityPlatform/choices/Server/displayName": "Serveur", + "symbols/InteractivityPlatform/choices/Server/description": "S’exécute sur le serveur", + "symbols/InteractivityPlatform/choices/WebAssembly/displayName": "WebAssembly", + "symbols/InteractivityPlatform/choices/WebAssembly/description": "S’exécute dans le navigateur à l’aide de WebAssembly", + "symbols/InteractivityPlatform/choices/Auto/displayName": "Automatique (Serveur et WebAssembly)", + "symbols/InteractivityPlatform/choices/Auto/description": "Utilise le serveur lors du téléchargement des ressources WebAssembly, puis utilise WebAssembly", + "symbols/InteractivityLocation/displayName": "Interactivité _location", + "symbols/InteractivityLocation/description": "Choisit les composants pour lesquels le rendu interactif est activé", + "symbols/InteractivityLocation/choices/InteractivePerPage/displayName": "Par page/composant", + "symbols/InteractivityLocation/choices/InteractivePerPage/description": "L’interactivité est appliquée sur une base par page ou par composant", + "symbols/InteractivityLocation/choices/InteractiveGlobal/displayName": "Mondial", + "symbols/InteractivityLocation/choices/InteractiveGlobal/description": "L’interactivité est appliquée au niveau racine", + "symbols/IncludeSampleContent/displayName": "Inclure les pages _sample", + "symbols/IncludeSampleContent/description": "Configure s'il faut ajouter des exemples de pages et de style pour illustrer les modèles d'utilisation de base.", + "symbols/Empty/description": "Configure s'il faut omettre les exemples de pages et le style qui illustrent les modèles d'utilisation de base.", + "symbols/auth/choices/None/description": "Aucune authentification", + "symbols/auth/choices/Individual/description": "Authentification individuelle", + "symbols/auth/description": "Type d’authentification à utiliser", + "symbols/UseLocalDB/description": "Indique s’il faut utiliser LocalDB au lieu de SQLite. Cette option s’applique uniquement si --auth Individual est spécifié.", + "symbols/AllInteractive/displayName": "_Enable rendu interactif globalement sur l’ensemble du site", + "symbols/AllInteractive/description": "Configure si chaque page doit être interactive en appliquant un mode de rendu interactif au niveau supérieur. Si la valeur est False, les pages utilisent le rendu de serveur statique par défaut et peuvent être marquées comme interactives par page ou par composant.", + "symbols/NoHttps/description": "Indique s’il faut désactiver HTTPS. Cette option s’applique uniquement si Individual n’est pas utilisé pour --auth.", + "symbols/UseProgramMain/displayName": "N’utilisez pas _d’instructions de niveau supérieur.", + "symbols/UseProgramMain/description": "Indique s’il faut générer une classe Programme explicite et une méthode Main au lieu d’instructions de niveau supérieur.", + "postActions/restore/description": "Restaurez les packages NuGet requis par ce projet.", + "postActions/restore/manualInstructions/default/text": "Exécuter « dotnet restore »" +} diff --git a/src/Templates/templates/blazorweb-csharp-10/.template.config/localize/templatestrings.it.json b/src/Templates/templates/blazorweb-csharp-10/.template.config/localize/templatestrings.it.json new file mode 100644 index 0000000000..c38d4eb87f --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-10/.template.config/localize/templatestrings.it.json @@ -0,0 +1,44 @@ +{ + "author": "Microsoft", + "name": "App Web Fluent Blazor", + "description": "Modello di progetto per la creazione di un'app Web Blazor che supporta sia il rendering lato server sia l'interattività client. Questo modello può essere usato per app Web con interfacce utente dinamiche avanzate.", + "symbols/Framework/description": "Il framework di destinazione per il progetto.", + "symbols/Framework/choices/net10.0/description": "Destinazione net10.0", + "symbols/UserSecretsId/description": "ID da usare per i segreti (usare con l'autenticazione Individual).", + "symbols/skipRestore/description": "Se specificato, ignora il ripristino automatico del progetto durante la creazione.", + "symbols/ExcludeLaunchSettings/description": "Indica se escludere launchSettings.json dal modello generato.", + "symbols/kestrelHttpPort/description": "Numero di porta da usare per l'endpoint HTTP in launchSettings.json.", + "symbols/kestrelHttpsPort/description": "Numero di porta da usare per l'endpoint HTTP in launchSettings.json. Questa opzione è applicabile solo quando il parametro no-https non viene usato (no-https verrà ignorato se si usa l'autenticazione Individual).", + "symbols/iisHttpPort/description": "Numero di porta da usare per l'endpoint HTTP in launchSettings.json.", + "symbols/iisHttpsPort/description": "Numero di porta da usare per l'endpoint HTTPS IIS Express in launchSettings.json. Questa opzione è applicabile solo quando il parametro no-https non viene usato (no-https verrà ignorato se si usa l'autenticazione Individual).", + "symbols/InteractivityPlatform/displayName": "_Modalità di rendering interattivo", + "symbols/InteractivityPlatform/description": "Consente di scegliere la modalità di rendering interattivo da usare per i componenti interattivi", + "symbols/InteractivityPlatform/choices/None/displayName": "Nessuno", + "symbols/InteractivityPlatform/choices/None/description": "Nessuna interattività (solo rendering server statico)", + "symbols/InteractivityPlatform/choices/Server/displayName": "Server", + "symbols/InteractivityPlatform/choices/Server/description": "Viene eseguito nel server", + "symbols/InteractivityPlatform/choices/WebAssembly/displayName": "WebAssembly", + "symbols/InteractivityPlatform/choices/WebAssembly/description": "Viene eseguito nel browser tramite WebAssembly", + "symbols/InteractivityPlatform/choices/Auto/displayName": "Automatico (Server e WebAssembly)", + "symbols/InteractivityPlatform/choices/Auto/description": "Usa Server durante il download degli asset WebAssembly, quindi usa WebAssembly", + "symbols/InteractivityLocation/displayName": "Posizione _interattività", + "symbols/InteractivityLocation/description": "Sceglie quali componenti avranno il rendering interattivo abilitato", + "symbols/InteractivityLocation/choices/InteractivePerPage/displayName": "Per pagina/componente", + "symbols/InteractivityLocation/choices/InteractivePerPage/description": "L'interattività viene applicata per pagina o per componente", + "symbols/InteractivityLocation/choices/InteractiveGlobal/displayName": "Globale", + "symbols/InteractivityLocation/choices/InteractiveGlobal/description": "L'interattività viene applicata a livello radice", + "symbols/IncludeSampleContent/displayName": "Includi _pagine di esempio", + "symbols/IncludeSampleContent/description": "Consente di configurare se aggiungere pagine di esempio e stile per mostrare modelli di utilizzo di base.", + "symbols/Empty/description": "Consente di configurare se omettere pagine di esempio e stile che mostrano modelli di utilizzo di base.", + "symbols/auth/choices/None/description": "Nessuna autenticazione", + "symbols/auth/choices/Individual/description": "Autenticazione Individual", + "symbols/auth/description": "Tipo di autenticazione da usare.", + "symbols/UseLocalDB/description": "Indica se usare LocalDB invece di SQLite. Questa opzione si applica solo se è specificato --auth Individual.", + "symbols/AllInteractive/displayName": "_Enable rendering interattivo a livello globale in tutto il sito", + "symbols/AllInteractive/description": "Configura se rendere interattiva ogni pagina applicando una modalità di rendering interattiva al livello superiore. Se false, le pagine useranno il rendering statico del server per impostazione predefinita e possono essere contrassegnate come interattive per pagina o per componente.", + "symbols/NoHttps/description": "Indica se disattivare HTTPS. Questa opzione si applica solo se Individual non viene usata per --auth.", + "symbols/UseProgramMain/displayName": "Non usare_istruzioni di primo livello", + "symbols/UseProgramMain/description": "Indica se generare una classe Program esplicita e un metodo Main anziché istruzioni di primo livello.", + "postActions/restore/description": "Ripristina i pacchetti NuGet richiesti da questo progetto.", + "postActions/restore/manualInstructions/default/text": "Esegui 'dotnet restore'" +} diff --git a/src/Templates/templates/blazorweb-csharp-10/.template.config/localize/templatestrings.ja.json b/src/Templates/templates/blazorweb-csharp-10/.template.config/localize/templatestrings.ja.json new file mode 100644 index 0000000000..05613d90b1 --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-10/.template.config/localize/templatestrings.ja.json @@ -0,0 +1,44 @@ +{ + "author": "Microsoft", + "name": "Fluent Blazor Web アプリ", + "description": "サーバー側のレンダリングとクライアントの対話機能の両方をサポートする Blazor Web アプリを作成するためのプロジェクト テンプレートです。このテンプレートは、リッチな動的ユーザー インターフェイス (UI) を持つ Web アプリに使用できます。", + "symbols/Framework/description": "プロジェクトのターゲット フレームワークです。", + "symbols/Framework/choices/net10.0/description": "ターゲット net10.0", + "symbols/UserSecretsId/description": "シークレットで使用する ID (IndividualAuth で使用)。", + "symbols/skipRestore/description": "指定した場合、作成時にプロジェクトの自動復元がスキップされます。", + "symbols/ExcludeLaunchSettings/description": "生成されたテンプレートから launchSettings.json を除外するかどうか。", + "symbols/kestrelHttpPort/description": "launchSettings.json の HTTP エンドポイントに使用するポート番号。", + "symbols/kestrelHttpsPort/description": "launchSettings.json で HTTPS エンドポイントに使用するポート番号。このオプションは、no-https パラメーターが使用されていない場合にのみ適用されます (IndividualAuth が使用されている場合は、no-https は無視されます)。", + "symbols/iisHttpPort/description": "launchSettings.json の IIS Express HTTP エンドポイントに使用するポート番号。", + "symbols/iisHttpsPort/description": "launchSettings.json で IIS Express HTTPS エンドポイントに使用するポート番号。このオプションは、no-https パラメーターが使用されていない場合にのみ適用されます (IndividualAuth が使用されている場合は、no-https は無視されます)。", + "symbols/InteractivityPlatform/displayName": "_対話型レンダリング モード", + "symbols/InteractivityPlatform/description": "対話型コンポーネントに使用する対話型レンダリング モードを選択します", + "symbols/InteractivityPlatform/choices/None/displayName": "なし", + "symbols/InteractivityPlatform/choices/None/description": "インタラクティビティなし (静的サーバー レンダリングのみ)", + "symbols/InteractivityPlatform/choices/Server/displayName": "サーバー", + "symbols/InteractivityPlatform/choices/Server/description": "サーバーで実行", + "symbols/InteractivityPlatform/choices/WebAssembly/displayName": "WebAssembly", + "symbols/InteractivityPlatform/choices/WebAssembly/description": "WebAssembly を使用してブラウザーで実行します", + "symbols/InteractivityPlatform/choices/Auto/displayName": "自動 (サーバーと WebAssembly)", + "symbols/InteractivityPlatform/choices/Auto/description": "WebAssembly 資産のダウンロード中にサーバーを使用してから、WebAssembly を使用します", + "symbols/InteractivityLocation/displayName": "インタラクティビティの場所(_L)", + "symbols/InteractivityLocation/description": "対話型レンダリングを有効にするコンポーネントを選択します", + "symbols/InteractivityLocation/choices/InteractivePerPage/displayName": "ページ/コンポーネントごと", + "symbols/InteractivityLocation/choices/InteractivePerPage/description": "インタラクティビティは、ページ単位またはコンポーネント単位で適用されます", + "symbols/InteractivityLocation/choices/InteractiveGlobal/displayName": "グローバル", + "symbols/InteractivityLocation/choices/InteractiveGlobal/description": "インタラクティビティはルート レベルで適用されます", + "symbols/IncludeSampleContent/displayName": "サンプル ページを含める(_S)", + "symbols/IncludeSampleContent/description": "基本的な使用パターンを示すサンプル ページとスタイルを追加するかどうかを構成します。", + "symbols/Empty/description": "基本的な使用パターンを示すサンプル ページとスタイルを省略するかどうかを構成します。", + "symbols/auth/choices/None/description": "認証なし", + "symbols/auth/choices/Individual/description": "個別の認証", + "symbols/auth/description": "使用する認証の種類", + "symbols/UseLocalDB/description": "SQLite の代わりに LocalDB を使用するかどうか。このオプションは、--auth Individual が指定されている場合にのみ適用されます。", + "symbols/AllInteractive/displayName": "インタラクティビティ レンダリングをサイト全体でグローバルに有効化(_E)", + "symbols/AllInteractive/description": "最上位レベルで対話型レンダリング モードを適用して、すべてのページを対話型にするかどうかを構成します。false の場合、ページは既定で静的サーバー レンダリングを使用し、ページ単位またはコンポーネント単位で対話型としてマークできます。", + "symbols/NoHttps/description": "HTTPS をオフにするかどうか。このオプションは、Individual が --auth に使用されていない場合にのみ適用されます。", + "symbols/UseProgramMain/displayName": "最上位レベルのステートメントを使用しない(_T)", + "symbols/UseProgramMain/description": "最上位レベルのステートメントではなく、明示的な Program クラスと Main メソッドを生成するかどうか。", + "postActions/restore/description": "このプロジェクトに必要な NuGet パッケージを復元します。", + "postActions/restore/manualInstructions/default/text": "'dotnet restore' を実行する" +} diff --git a/src/Templates/templates/blazorweb-csharp-10/.template.config/localize/templatestrings.ko.json b/src/Templates/templates/blazorweb-csharp-10/.template.config/localize/templatestrings.ko.json new file mode 100644 index 0000000000..61e337070e --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-10/.template.config/localize/templatestrings.ko.json @@ -0,0 +1,44 @@ +{ + "author": "Microsoft", + "name": "Fluent Blazor 웹앱", + "description": "서버 측 렌더링 및 클라이언트 대화형 작업을 모두 지원하는 Blazor 웹앱을 만들기 위한 프로젝트 템플릿입니다. 이 템플릿은 풍부한 동적 UI(사용자 인터페이스)가 있는 웹앱에 사용할 수 있습니다.", + "symbols/Framework/description": "프로젝트에 대한 대상 프레임워크입니다.", + "symbols/Framework/choices/net10.0/description": "대상 net10.0", + "symbols/UserSecretsId/description": "비밀에 사용할 ID입니다(개별 인증과 함께 사용).", + "symbols/skipRestore/description": "지정된 경우, 프로젝트 생성 시 자동 복원을 건너뜁니다.", + "symbols/ExcludeLaunchSettings/description": "생성된 템플릿에서 launchSettings.json을 제외할지 여부입니다.", + "symbols/kestrelHttpPort/description": "launchSettings.json의 HTTP 엔드포인트에 사용할 포트 번호입니다.", + "symbols/kestrelHttpsPort/description": "launchSettings.json의 HTTPS 엔드포인트에 사용할 포트 번호입니다. 이 옵션은 매개 변수 no-https가 사용되지 않은 경우에만 적용됩니다(개별 인증을 사용하는 경우 no-https가 무시됨).", + "symbols/iisHttpPort/description": "launchSettings.json의 IIS Express HTTP 엔드포인트에 사용할 포트 번호입니다.", + "symbols/iisHttpsPort/description": "launchSettings.json의 IIS Express HTTPS 엔드포인트에 사용할 포트 번호입니다. 이 옵션은 매개 변수 no-https가 사용되지 않은 경우에만 적용됩니다(개별 인증을 사용하는 경우 no-https가 무시됨).", + "symbols/InteractivityPlatform/displayName": "_대화형 렌더링 모드", + "symbols/InteractivityPlatform/description": "대화형 구성 요소에 사용할 대화형 렌더링 모드를 선택합니다.", + "symbols/InteractivityPlatform/choices/None/displayName": "없음", + "symbols/InteractivityPlatform/choices/None/description": "대화형 작업 없음(정적 서버 렌더링만 해당)", + "symbols/InteractivityPlatform/choices/Server/displayName": "서버", + "symbols/InteractivityPlatform/choices/Server/description": "서버에서 실행", + "symbols/InteractivityPlatform/choices/WebAssembly/displayName": "WebAssembly", + "symbols/InteractivityPlatform/choices/WebAssembly/description": "WebAssembly를 사용하여 브라우저에서 실행", + "symbols/InteractivityPlatform/choices/Auto/displayName": "자동(Server 및 WebAssembly)", + "symbols/InteractivityPlatform/choices/Auto/description": "WebAssembly 자산을 다운로드하는 동안 서버를 사용한 다음, WebAssembly를 사용합니다.", + "symbols/InteractivityLocation/displayName": "대화형 위치(_L)", + "symbols/InteractivityLocation/description": "대화형 렌더링을 사용하도록 설정할 구성 요소를 선택합니다.", + "symbols/InteractivityLocation/choices/InteractivePerPage/displayName": "페이지/구성 요소당", + "symbols/InteractivityLocation/choices/InteractivePerPage/description": "상호 작용은 페이지별 또는 구성 요소별로 적용됩니다.", + "symbols/InteractivityLocation/choices/InteractiveGlobal/displayName": "글로벌", + "symbols/InteractivityLocation/choices/InteractiveGlobal/description": "대화형 작업은 루트 수준에서 적용됩니다.", + "symbols/IncludeSampleContent/displayName": "샘플 페이지 포함(_S)", + "symbols/IncludeSampleContent/description": "기본 사용 패턴을 보여주기 위해 샘플 페이지 및 스타일을 추가할지 여부를 구성합니다.", + "symbols/Empty/description": "기본 사용 패턴을 보여주는 샘플 페이지 및 스타일을 생략할지 여부를 구성합니다.", + "symbols/auth/choices/None/description": "인증 없음", + "symbols/auth/choices/Individual/description": "개별 인증", + "symbols/auth/description": "사용할 인증 유형", + "symbols/UseLocalDB/description": "SQLite 대신 LocalDB를 사용할지 여부입니다. 이 옵션은 --auth Individual이 지정된 경우에만 적용됩니다.", + "symbols/AllInteractive/displayName": "_사이트 전체에서 전역적으로 인터랙티브 렌더링 활성화", + "symbols/AllInteractive/description": "최상위 수준에서 대화형 렌더링 모드를 적용하여 모든 페이지를 대화형으로 설정할지 여부를 구성합니다. 만약 false이면 페이지는 기본적으로 정적 서버 렌더링을 사용하며 페이지별 또는 구성 요소별로 대화형으로 표시될 수 있습니다.", + "symbols/NoHttps/description": "HTTPS를 끌지 여부입니다. 이 옵션은 개별 항목이 --auth에 사용되지 않는 경우에만 적용됩니다.", + "symbols/UseProgramMain/displayName": "최상위 문 사용 안 함(_T)", + "symbols/UseProgramMain/description": "최상위 문 대신 명시적 Program 클래스 및 Main 메서드를 생성할지 여부입니다.", + "postActions/restore/description": "이 프로젝트에 필요한 NuGet 패키지를 복원합니다.", + "postActions/restore/manualInstructions/default/text": "'dotnet restore' 실행" +} diff --git a/src/Templates/templates/blazorweb-csharp-10/.template.config/localize/templatestrings.pl.json b/src/Templates/templates/blazorweb-csharp-10/.template.config/localize/templatestrings.pl.json new file mode 100644 index 0000000000..a1b708e62a --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-10/.template.config/localize/templatestrings.pl.json @@ -0,0 +1,44 @@ +{ + "author": "Microsoft", + "name": "Aplikacja internetowa Fluent Blazor", + "description": "Szablon projektu służący do tworzenia aplikacji internetowej platformy Blazor, która obsługuje renderowanie po stronie serwera i interakcyjność klienta. Ten szablon może być używany dla aplikacji internetowych z zaawansowanymi dynamicznymi interfejsami użytkownika.", + "symbols/Framework/description": "Platforma docelowa dla tego projektu.", + "symbols/Framework/choices/net10.0/description": "Docelowa platforma net10.0", + "symbols/UserSecretsId/description": "Identyfikator do użycia dla wpisów tajnych (używany z indywidualnym uwierzytelnianym).", + "symbols/skipRestore/description": "Jeśli ta opcja jest określona, pomija automatyczne przywracanie projektu podczas tworzenia.", + "symbols/ExcludeLaunchSettings/description": "Określa, czy wykluczyć plik launchSettings.json z wygenerowanego szablonu.", + "symbols/kestrelHttpPort/description": "Numer portu do użycia dla punktu końcowego HTTP w pliku launchSettings.json.", + "symbols/kestrelHttpsPort/description": "Numer portu do użycia dla punktu końcowego HTTPS w pliku launchSettings.json. Ta opcja ma zastosowanie tylko wtedy, gdy parametr no-https nie jest używany (no-https będzie ignorowany, jeśli używany jest Individual auth — indywidualny uwierzytelniony dostęp).", + "symbols/iisHttpPort/description": "Numer portu do użycia dla punktu końcowego HTTP usług IIS Express w pliku launchSettings.json.", + "symbols/iisHttpsPort/description": "Numer portu do użycia dla punktu końcowego HTTPS programu IIS Express w pliku launchSettings.json. Ta opcja ma zastosowanie tylko wtedy, gdy parametr no-https nie jest używany (no-https będzie ignorowany, jeśli używany jest Individual auth — indywidualny uwierzytelniony dostęp).", + "symbols/InteractivityPlatform/displayName": "_Interakcyjny tryb renderowania", + "symbols/InteractivityPlatform/description": "Wybiera tryb renderowania interakcyjnego do użycia dla składników interakcyjnych", + "symbols/InteractivityPlatform/choices/None/displayName": "Brak", + "symbols/InteractivityPlatform/choices/None/description": "Brak interakcyjności (tylko renderowanie serwera statycznego)", + "symbols/InteractivityPlatform/choices/Server/displayName": "Serwer", + "symbols/InteractivityPlatform/choices/Server/description": "Działa na serwerze", + "symbols/InteractivityPlatform/choices/WebAssembly/displayName": "WebAssembly", + "symbols/InteractivityPlatform/choices/WebAssembly/description": "Działa w przeglądarce przy użyciu elementu WebAssembly", + "symbols/InteractivityPlatform/choices/Auto/displayName": "Automatycznie (Server i WebAssembly)", + "symbols/InteractivityPlatform/choices/Auto/description": "Używa serwera podczas pobierania zasobów WebAssembly, a następnie używa elementu WebAssembly", + "symbols/InteractivityLocation/displayName": "_Lokalizacja interaktywności", + "symbols/InteractivityLocation/description": "Wybiera, które składniki będą miały włączone renderowanie interakcyjne", + "symbols/InteractivityLocation/choices/InteractivePerPage/displayName": "Na stronę/składnik", + "symbols/InteractivityLocation/choices/InteractivePerPage/description": "Interakcyjność jest stosowana dla poszczególnych stron lub składników", + "symbols/InteractivityLocation/choices/InteractiveGlobal/displayName": "Globalne", + "symbols/InteractivityLocation/choices/InteractiveGlobal/description": "Interakcyjność jest stosowana na poziomie głównym", + "symbols/IncludeSampleContent/displayName": "Dołącz _przykładowe strony", + "symbols/IncludeSampleContent/description": "Konfiguruje, czy dodać przykładowe strony i style w celu zademonstrowania podstawowych wzorców użycia.", + "symbols/Empty/description": "Konfiguruje, czy pomijać przykładowe strony i style demonstrujące podstawowe wzorce użycia.", + "symbols/auth/choices/None/description": "Bez uwierzytelniania", + "symbols/auth/choices/Individual/description": "Uwierzytelnianie indywidualne", + "symbols/auth/description": "Typ uwierzytelniania, który ma zostać użyty.", + "symbols/UseLocalDB/description": "Określa, czy używać bazy danych LocalDB zamiast oprogramowania SQLite. Ta opcja ma zastosowanie tylko wtedy, gdy określono parametr --auth Individual.", + "symbols/AllInteractive/displayName": "_Włącz globalnie renderowanie interakcyjne w całej witrynie", + "symbols/AllInteractive/description": "Konfiguruje, czy każda strona ma być interakcyjna, stosując interakcyjny tryb renderowania na najwyższym poziomie. W przypadku wartości false strony będą domyślnie używać statycznego renderowania serwera i mogą być oznaczone jako interakcyjne dla poszczególnych stron lub składników.", + "symbols/NoHttps/description": "Określa, czy wyłączyć protokół HTTPS. Ta opcja ma zastosowanie tylko wtedy, gdy opcja Individual nie została użyta dla opcji --auth.", + "symbols/UseProgramMain/displayName": "Nie używaj ins_trukcji najwyższego poziomu", + "symbols/UseProgramMain/description": "Określa, czy wygenerować jawną klasę Program i metodę Main zamiast instrukcji najwyższego poziomu.", + "postActions/restore/description": "Przywróć pakiety NuGet wymagane przez ten projekt.", + "postActions/restore/manualInstructions/default/text": "Uruchom polecenie \"dotnet restore\"" +} diff --git a/src/Templates/templates/blazorweb-csharp-10/.template.config/localize/templatestrings.pt-BR.json b/src/Templates/templates/blazorweb-csharp-10/.template.config/localize/templatestrings.pt-BR.json new file mode 100644 index 0000000000..65736325db --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-10/.template.config/localize/templatestrings.pt-BR.json @@ -0,0 +1,44 @@ +{ + "author": "Microsoft", + "name": "Aplicativo Web Fluent Blazor", + "description": "Um modelo de projeto para criar um aplicativo Web Blazor que dá suporte à renderização do lado do servidor e à interatividade do cliente. Este modelo pode ser usado para aplicativos da Web com interfaces de usuário (UIs) dinâmicas avançadas.", + "symbols/Framework/description": "A estrutura de destino do projeto.", + "symbols/Framework/choices/net10.0/description": "Net10.0 de destino", + "symbols/UserSecretsId/description": "O ID a ser usado para segredos (usado com autenticação individual).", + "symbols/skipRestore/description": "Se especificado, ignora a restauração automática do projeto sendo criado.", + "symbols/ExcludeLaunchSettings/description": "Se deve excluir launchSettings.json do modelo gerado.", + "symbols/kestrelHttpPort/description": "Número da porta a ser usada para o ponto de extremidade HTTP em launchSettings.json.", + "symbols/kestrelHttpsPort/description": "Número da porta a ser usada para o ponto de extremidade HTTPS em launchSettings.json. Esta opção só é aplicável quando o parâmetro no-https não é utilizado (no-https será ignorado se for utilizada autenticação individual).", + "symbols/iisHttpPort/description": "Número da porta a ser usada para o ponto de extremidade HTTP do IIS Express em launchSettings.json.", + "symbols/iisHttpsPort/description": "Número da porta a ser usada para o ponto de extremidade HTTPS do IIS Express em launchSettings.json. Esta opção só é aplicável quando o parâmetro no-https não é utilizado (no-https será ignorado se for utilizada autenticação individual).", + "symbols/InteractivityPlatform/displayName": "_Modo de renderização Interativo", + "symbols/InteractivityPlatform/description": "Escolha qual modo de renderização interativo que será usado para componentes interativos", + "symbols/InteractivityPlatform/choices/None/displayName": "Nenhum", + "symbols/InteractivityPlatform/choices/None/description": "Sem interatividade (somente renderização do servidor estático)", + "symbols/InteractivityPlatform/choices/Server/displayName": "Servidor", + "symbols/InteractivityPlatform/choices/Server/description": "É executado no servidor", + "symbols/InteractivityPlatform/choices/WebAssembly/displayName": "WebAssembly", + "symbols/InteractivityPlatform/choices/WebAssembly/description": "É executado no navegador usando o WebAssembly", + "symbols/InteractivityPlatform/choices/Auto/displayName": "Automático (Servidor e WebAssembly)", + "symbols/InteractivityPlatform/choices/Auto/description": "Usa Servidor ao baixar os ativos WebAssembly e usa o WebAssembly", + "symbols/InteractivityLocation/displayName": "Localização da _Interatividade", + "symbols/InteractivityLocation/description": "Escolhe quais componentes terão a renderização interativa habilitada", + "symbols/InteractivityLocation/choices/InteractivePerPage/displayName": "Por página/componente", + "symbols/InteractivityLocation/choices/InteractivePerPage/description": "A interatividade é aplicada por página ou por componente", + "symbols/InteractivityLocation/choices/InteractiveGlobal/displayName": "Global", + "symbols/InteractivityLocation/choices/InteractiveGlobal/description": "A interatividade é aplicada no nível raiz", + "symbols/IncludeSampleContent/displayName": "Incluir páginas de _amostra", + "symbols/IncludeSampleContent/description": "Configura se deseja adicionar páginas de amostra e estilo para demonstrar padrões de uso básicos.", + "symbols/Empty/description": "Configura a omissão de páginas de amostra e estilo que demonstram padrões básicos de uso.", + "symbols/auth/choices/None/description": "Sem autenticação", + "symbols/auth/choices/Individual/description": "Autenticação individual", + "symbols/auth/description": "O tipo de autenticação a ser usado", + "symbols/UseLocalDB/description": "Se deve usar LocalDB em vez de SQLite. Esta opção só se aplica se --auth Individual for especificado.", + "symbols/AllInteractive/displayName": "_Habilitar renderização interativa globalmente no site", + "symbols/AllInteractive/description": "Configura se todas as páginas devem ser interativas aplicando um modo de renderização interativo no nível superior. Se for falso, as páginas usarão a renderização do servidor estático por padrão e poderão ser marcadas como interativas por página ou por componente.", + "symbols/NoHttps/description": "Se deve desligar o HTTPS. Essa opção só se aplica se Individual não for usado para --auth.", + "symbols/UseProgramMain/displayName": "Não use ins_truções de nível superior", + "symbols/UseProgramMain/description": "Se deve gerar uma classe de Programa explícita e um método principal em vez de instruções de nível superior.", + "postActions/restore/description": "Restaure os pacotes NuGet exigidos por este projeto.", + "postActions/restore/manualInstructions/default/text": "Executa 'dotnet restore'" +} diff --git a/src/Templates/templates/blazorweb-csharp-10/.template.config/localize/templatestrings.ru.json b/src/Templates/templates/blazorweb-csharp-10/.template.config/localize/templatestrings.ru.json new file mode 100644 index 0000000000..486d15e130 --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-10/.template.config/localize/templatestrings.ru.json @@ -0,0 +1,44 @@ +{ + "author": "Майкрософт", + "name": "Веб-приложение Fluent Blazor", + "description": "Шаблон проекта для создания приложения Blazor, поддерживающего как отрисовку на стороне сервера, так и интерактивные возможности клиента. Этот шаблон можно использовать для веб-приложений с многофункциональными динамическими пользовательскими интерфейсами (UI).", + "symbols/Framework/description": "Целевая платформа для проекта.", + "symbols/Framework/choices/net10.0/description": "Целевая платформа .NET 10.0", + "symbols/UserSecretsId/description": "Идентификатор, используемый для секретов (применяется с индивидуальной проверкой подлинности).", + "symbols/skipRestore/description": "Если установлено, автоматическое восстановление проекта при создании пропускается.", + "symbols/ExcludeLaunchSettings/description": "Следует ли исключить launchSettings.json из созданного шаблона.", + "symbols/kestrelHttpPort/description": "Номер порта, используемый для конечной точки HTTP в launchSettings.json.", + "symbols/kestrelHttpsPort/description": "Номер порта, используемый для конечной точки HTTPS в launchSettings.json. Этот параметр применим только в том случае, если no-https не используется (no-https игнорируется при использовании индивидуальной проверки подлинности).", + "symbols/iisHttpPort/description": "Номер порта, используемый для конечной точки HTTP IIS Express в launchSettings.json.", + "symbols/iisHttpsPort/description": "Номер порта, используемый для конечной точки HTTPS IIS Express в launchSettings.json. Этот параметр применим только в том случае, если no-https не используется (no-https игнорируется при использовании индивидуальной проверки подлинности).", + "symbols/InteractivityPlatform/displayName": "_Интерактивный режим отрисовки", + "symbols/InteractivityPlatform/description": "Выбор режима интерактивной отрисовки для интерактивных компонентов", + "symbols/InteractivityPlatform/choices/None/displayName": "Нет", + "symbols/InteractivityPlatform/choices/None/description": "Без взаимодействия (только отрисовка статического сервера)", + "symbols/InteractivityPlatform/choices/Server/displayName": "Сервер", + "symbols/InteractivityPlatform/choices/Server/description": "Выполняется на сервере", + "symbols/InteractivityPlatform/choices/WebAssembly/displayName": "WebAssembly", + "symbols/InteractivityPlatform/choices/WebAssembly/description": "Запуск в браузере с помощью WebAssembly", + "symbols/InteractivityPlatform/choices/Auto/displayName": "Авто (Сервер и WebAssembly)", + "symbols/InteractivityPlatform/choices/Auto/description": "Использует сервер при загрузке ресурсов WebAssembly, затем использует WebAssembly.", + "symbols/InteractivityLocation/displayName": "_Расположение интерактивности", + "symbols/InteractivityLocation/description": "Выбор компонентов, для которых будет включена интерактивная отрисовка", + "symbols/InteractivityLocation/choices/InteractivePerPage/displayName": "На страницу или компонент", + "symbols/InteractivityLocation/choices/InteractivePerPage/description": "Интерактивность применяется для каждой страницы или для каждого компонента", + "symbols/InteractivityLocation/choices/InteractiveGlobal/displayName": "Глобальный", + "symbols/InteractivityLocation/choices/InteractiveGlobal/description": "Интерактивность применяется на корневом уровне", + "symbols/IncludeSampleContent/displayName": "Включить _примеры страниц", + "symbols/IncludeSampleContent/description": "Настраивает, следует ли добавлять примеры страниц и стили для демонстрации базовых шаблонов использования.", + "symbols/Empty/description": "Настраивает, следует ли пропускать примеры страниц и стили, демонстрирующие базовые шаблоны использования.", + "symbols/auth/choices/None/description": "Без проверки подлинности", + "symbols/auth/choices/Individual/description": "Индивидуальная проверка подлинности", + "symbols/auth/description": "Тип используемой проверки подлинности", + "symbols/UseLocalDB/description": "Следует ли использовать LocalDB вместо SQLite. Этот параметр применяется, только если указывается --auth Individual.", + "symbols/AllInteractive/displayName": "_Включить интерактивную отрисовку по всему сайту", + "symbols/AllInteractive/description": "Определяет, делать ли каждую страницу интерактивной, применяя интерактивный режим отрисовки на верхнем уровне. Если установлено значение ЛОЖЬ, страницы по умолчанию будут использовать статическую серверную отрисовку и могут быть помечены как интерактивные для каждой страницы или для каждого компонента.", + "symbols/NoHttps/description": "Следует ли отключить HTTPS. Этот параметр применяется только в том случае, если для аргумента --auth не используется значение Individual.", + "symbols/UseProgramMain/displayName": "Не использовать _операторы верхнего уровня", + "symbols/UseProgramMain/description": "Следует ли создавать явный класс Program и метод Main вместо операторов верхнего уровня.", + "postActions/restore/description": "Восстановление пакетов NuGet, необходимых для этого проекта.", + "postActions/restore/manualInstructions/default/text": "Выполнить команду \"dotnet restore\"" +} diff --git a/src/Templates/templates/blazorweb-csharp-10/.template.config/localize/templatestrings.tr.json b/src/Templates/templates/blazorweb-csharp-10/.template.config/localize/templatestrings.tr.json new file mode 100644 index 0000000000..12f6b1e974 --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-10/.template.config/localize/templatestrings.tr.json @@ -0,0 +1,44 @@ +{ + "author": "Microsoft", + "name": "Fluent Blazor Web Uygulaması", + "description": "Hem sunucu tarafı işlemeyi hem de istemci etkileşimini destekleyen bir Blazor web uygulaması oluşturmaya yönelik proje şablonu. Bu şablon, zengin dinamik kullanıcı arabirimlerine (UI) sahip web uygulamaları için kullanılabilir.", + "symbols/Framework/description": "Projenin hedef çerçevesi.", + "symbols/Framework/choices/net10.0/description": "Hedef net10.0", + "symbols/UserSecretsId/description": "Gizli diziler için kullanılacak kimlik (Bireysel kimlik doğrulamayla kullanın).", + "symbols/skipRestore/description": "Belirtilirse, oluşturma sırasında projenin otomatik geri yüklenmesini atlar.", + "symbols/ExcludeLaunchSettings/description": "launchSettings.json öğesinin oluşturulan şablondan dışlanıp dışlanmayacağı.", + "symbols/kestrelHttpPort/description": "launchSettings.json içinde HTTP uç noktası için kullanılacak bağlantı noktası numarası.", + "symbols/kestrelHttpsPort/description": "launchSettings.json içinde HTTPS uç noktası için kullanılacak bağlantı noktası numarası. Bu seçenek yalnızca no-https parametresi kullanılmadığında geçerlidir (Bireysel kimlik doğrulama kullanılırsa no-https göz ardı edilecektir).", + "symbols/iisHttpPort/description": "launchSettings.json içinde IIS Express HTTP uç noktası için kullanılacak bağlantı noktası numarası.", + "symbols/iisHttpsPort/description": "launchSettings.json içinde IIS Express HTTPS uç noktası için kullanılacak bağlantı noktası numarası. Bu seçenek yalnızca no-https parametresi kullanılmadığında geçerlidir (Bireysel kimlik doğrulama kullanılırsa no-https göz ardı edilecektir).", + "symbols/InteractivityPlatform/displayName": "_Etkileşimli işleme modu", + "symbols/InteractivityPlatform/description": "Etkileşimli bileşenler için hangi etkileşimli işleme modunun kullanılacağını seçer", + "symbols/InteractivityPlatform/choices/None/displayName": "Yok", + "symbols/InteractivityPlatform/choices/None/description": "Etkileşim yok (yalnızca statik sunucu işleme)", + "symbols/InteractivityPlatform/choices/Server/displayName": "Server", + "symbols/InteractivityPlatform/choices/Server/description": "Sunucuda çalıştırılır", + "symbols/InteractivityPlatform/choices/WebAssembly/displayName": "WebAssembly", + "symbols/InteractivityPlatform/choices/WebAssembly/description": "WebAssembly kullanarak tarayıcıda çalışır", + "symbols/InteractivityPlatform/choices/Auto/displayName": "Otomatik (Server ve WebAssembly)", + "symbols/InteractivityPlatform/choices/Auto/description": "WebAssembly varlıkları indirilirken Server kullanır, ardından WebAssembly'i kullanır", + "symbols/InteractivityLocation/displayName": "Etkileşim _konumu", + "symbols/InteractivityLocation/description": "Etkileşimli işlemenin etkinleştirileceği bileşenleri belirler", + "symbols/InteractivityLocation/choices/InteractivePerPage/displayName": "Sayfa/bileşen başına", + "symbols/InteractivityLocation/choices/InteractivePerPage/description": "Etkileşim sayfa başına veya bileşen başına temelinde uygulanır", + "symbols/InteractivityLocation/choices/InteractiveGlobal/displayName": "Genel", + "symbols/InteractivityLocation/choices/InteractiveGlobal/description": "Etkileşim kök düzeyinde uygulanır", + "symbols/IncludeSampleContent/displayName": "Örnek sayfalar _ekle", + "symbols/IncludeSampleContent/description": "Temel kullanım düzenlerini göstermek için örnek sayfaların ve stil oluşturma özelliklerinin eklenip eklenmeyeceğini yapılandırır.", + "symbols/Empty/description": "Temel kullanım düzenlerini gösteren örnek sayfaların ve stil oluşturma özelliklerinin atlanıp atlanmayacağını yapılandırır.", + "symbols/auth/choices/None/description": "Kimlik doğrulaması yok", + "symbols/auth/choices/Individual/description": "Bireysel kimlik doğrulaması", + "symbols/auth/description": "Kullanılacak kimlik doğrulaması türü", + "symbols/UseLocalDB/description": "SQLite yerine LocalDB'nin kullanılıp kullanılmayacağı. Bu seçenek yalnızca --auth Bireysel belirtildiğinde geçerlidir.", + "symbols/AllInteractive/displayName": "_Site genelinde etkileşimli işlemeyi genel olarak etkinleştirin", + "symbols/AllInteractive/description": "En üst düzeyde etkileşimli bir işleme modu uygulayarak her sayfanın etkileşimli olup olmayacağını yapılandırır. False ise sayfalar varsayılan olarak statik sunucu işleme kullanır ve sayfa başına veya bileşen başına temelinde etkileşimli olarak işaretlenebilir.", + "symbols/NoHttps/description": "HTTPS'nin kapatılıp kapatılmayacağı. Bu seçenek yalnızca Bireysel --auth için kullanılmadığında geçerlidir.", + "symbols/UseProgramMain/displayName": "_Üst düzey deyimler kullanmayın", + "symbols/UseProgramMain/description": "Üst düzey deyimler yerine açık bir Program sınıfı ve Ana yöntem oluşturup oluşturulmayacağını belirtir.", + "postActions/restore/description": "Bu projenin gerektirdiği NuGet paketlerini geri yükleyin.", + "postActions/restore/manualInstructions/default/text": "'dotnet restore' çalıştır" +} diff --git a/src/Templates/templates/blazorweb-csharp-10/.template.config/localize/templatestrings.zh-Hans.json b/src/Templates/templates/blazorweb-csharp-10/.template.config/localize/templatestrings.zh-Hans.json new file mode 100644 index 0000000000..81139761ad --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-10/.template.config/localize/templatestrings.zh-Hans.json @@ -0,0 +1,44 @@ +{ + "author": "Microsoft", + "name": "Fluent Blazor Web 应用", + "description": "用于创建支持服务器端呈现和客户端交互的 Blazor Web 应用的项目模板。此模板可用于具有丰富动态用户界面 (UI) 的 Web 应用。", + "symbols/Framework/description": "项目的目标框架。", + "symbols/Framework/choices/net10.0/description": "目标 net10.0", + "symbols/UserSecretsId/description": "用于机密的 ID (与个人身份验证一起使用)。", + "symbols/skipRestore/description": "如果指定,则在创建时跳过项目的自动还原。", + "symbols/ExcludeLaunchSettings/description": "是否从生成的模板中排除 launchSettings.json。", + "symbols/kestrelHttpPort/description": "要用于 launchSettings.json 中 HTTP 终结点的端口号。", + "symbols/kestrelHttpsPort/description": "要用于 launchSettings.json 中 HTTPS 终结点的端口号。仅当不使用参数 no-https 时,此选项才适用(如果使用 Induvidual 身份验证,则将忽略 no-https)。", + "symbols/iisHttpPort/description": "要用于 launchSettings.json 中 IIS Express HTTP 终结点的端口号。", + "symbols/iisHttpsPort/description": "要用于 launchSettings.json 中 IIS Express HTTPS 终结点的端口号。仅当不使用参数 no-https 时,此选项才适用(如果使用 Induvidual 身份验证,则将忽略 no-https)。", + "symbols/InteractivityPlatform/displayName": "交互式(_I)呈现模式", + "symbols/InteractivityPlatform/description": "选择要用于交互式组件的交互式呈现模式", + "symbols/InteractivityPlatform/choices/None/displayName": "无", + "symbols/InteractivityPlatform/choices/None/description": "没有交互性 (仅限静态服务器呈现)", + "symbols/InteractivityPlatform/choices/Server/displayName": "服务器", + "symbols/InteractivityPlatform/choices/Server/description": "在服务器上运行", + "symbols/InteractivityPlatform/choices/WebAssembly/displayName": "WebAssembly", + "symbols/InteractivityPlatform/choices/WebAssembly/description": "使用 WebAssembly 在浏览器中运行", + "symbols/InteractivityPlatform/choices/Auto/displayName": "自动 (服务器和 WebAssembly)", + "symbols/InteractivityPlatform/choices/Auto/description": "下载 WebAssembly 资产时使用服务器,然后使用 WebAssembly", + "symbols/InteractivityLocation/displayName": "交互性位置(_L)", + "symbols/InteractivityLocation/description": "选择将启用交互式呈现的组件", + "symbols/InteractivityLocation/choices/InteractivePerPage/displayName": "每页/组件", + "symbols/InteractivityLocation/choices/InteractivePerPage/description": "基于每页或每组件应用交互", + "symbols/InteractivityLocation/choices/InteractiveGlobal/displayName": "全局", + "symbols/InteractivityLocation/choices/InteractiveGlobal/description": "在根级别应用交互性", + "symbols/IncludeSampleContent/displayName": "包含示例页(_S)", + "symbols/IncludeSampleContent/description": "配置是否添加示例页和样式以演示基本使用模式。", + "symbols/Empty/description": "配置是否忽略演示基本使用模式的示例页和样式。", + "symbols/auth/choices/None/description": "无身份验证", + "symbols/auth/choices/Individual/description": "个人身份验证", + "symbols/auth/description": "要使用的身份验证类型", + "symbols/UseLocalDB/description": "是否使用 LocalDB 而不是 SQLite。仅当指定了 --auth Individual 时,此选项才适用。", + "symbols/AllInteractive/displayName": "_在整个网站全局启用交互式呈现", + "symbols/AllInteractive/description": "配置是否通过在顶层应用交互式呈现模式来使每个页面都交互。如果为 false,则默认情况下,页面将使用静态服务器呈现,并且可以按每页或每组件标记为交互。", + "symbols/NoHttps/description": "是否关闭 HTTPS。仅当 Individual 不用于 --auth 时,此选项才适用。", + "symbols/UseProgramMain/displayName": "不使用顶级语句(_T)", + "symbols/UseProgramMain/description": "是否生成显式程序类和主方法,而不是顶级语句。", + "postActions/restore/description": "还原此项目所需的 NuGet 包。", + "postActions/restore/manualInstructions/default/text": "运行 \"dotnet restore\"" +} diff --git a/src/Templates/templates/blazorweb-csharp-10/.template.config/localize/templatestrings.zh-Hant.json b/src/Templates/templates/blazorweb-csharp-10/.template.config/localize/templatestrings.zh-Hant.json new file mode 100644 index 0000000000..5fd2fa4a1a --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-10/.template.config/localize/templatestrings.zh-Hant.json @@ -0,0 +1,44 @@ +{ + "author": "Microsoft", + "name": "Fluent Blazor Web 應用程式", + "description": "用於建立同時支援伺服器端轉譯和用戶端互動的 Blazor Web 應用程式的專案範本。此範本可用於具有豐富動態使用者介面 (UI) 的 Web 應用程式。", + "symbols/Framework/description": "專案的目標 Framework。", + "symbols/Framework/choices/net10.0/description": "目標 net10.0", + "symbols/UserSecretsId/description": "用於祕密的識別碼 (搭配個別驗證使用)。", + "symbols/skipRestore/description": "若指定,會在建立時跳過專案的自動還原。", + "symbols/ExcludeLaunchSettings/description": "是否要從產生的範本排除 launchSettings.json。", + "symbols/kestrelHttpPort/description": "launchSettings.json 中 HTTP 端點要使用的連接埠號碼。", + "symbols/kestrelHttpsPort/description": "launchSettings.json 中 IIS Express HTTPS 端點要使用的連接埠號碼。只有在未使用參數 no-https 時,才適用用此選項 (如果使用個別驗證,則會忽略 no-https)。", + "symbols/iisHttpPort/description": "launchSettings.json 中 IIS Express HTTP 端點要使用的連接埠號碼。", + "symbols/iisHttpsPort/description": "launchSettings.json 中 IIS Express HTTPS 端點要使用的連接埠號碼。只有在未使用參數 no-https 時,才適用用此選項 (如果使用個別驗證,則會忽略 no-https)。", + "symbols/InteractivityPlatform/displayName": "互動式轉譯模式(_I)", + "symbols/InteractivityPlatform/description": "選擇要用於互動式元件的互動式轉譯模式", + "symbols/InteractivityPlatform/choices/None/displayName": "無", + "symbols/InteractivityPlatform/choices/None/description": "沒有互動功能 (只有靜態伺服器轉譯)", + "symbols/InteractivityPlatform/choices/Server/displayName": "伺服器", + "symbols/InteractivityPlatform/choices/Server/description": "在伺服器上執行", + "symbols/InteractivityPlatform/choices/WebAssembly/displayName": "WebAssembly", + "symbols/InteractivityPlatform/choices/WebAssembly/description": "使用 WebAssembly 在瀏覽器中執行", + "symbols/InteractivityPlatform/choices/Auto/displayName": "自動 (伺服器和 WebAssembly)", + "symbols/InteractivityPlatform/choices/Auto/description": "下載 WebAssembly 資產時使用伺服器,然後使用 WebAssembly", + "symbols/InteractivityLocation/displayName": "互動功能位置(_l)", + "symbols/InteractivityLocation/description": "選擇要啟用互動式轉譯的元件", + "symbols/InteractivityLocation/choices/InteractivePerPage/displayName": "每頁/元件", + "symbols/InteractivityLocation/choices/InteractivePerPage/description": "互動性會以每頁或每一元件為基礎套用", + "symbols/InteractivityLocation/choices/InteractiveGlobal/displayName": "全球", + "symbols/InteractivityLocation/choices/InteractiveGlobal/description": "互動功能已套用至根層級", + "symbols/IncludeSampleContent/displayName": "包含範例頁面(_s)", + "symbols/IncludeSampleContent/description": "設定是否要新增範例頁面和樣式,以示範基本使用模式。", + "symbols/Empty/description": "設定是否要省略範例頁面和樣式,其示範基本使用模式。", + "symbols/auth/choices/None/description": "沒有驗證", + "symbols/auth/choices/Individual/description": "個別驗證", + "symbols/auth/description": "要使用的驗證類型。", + "symbols/UseLocalDB/description": "是否使用 LocalDB 而非 SQLite。只有在已指定 --auth Individual 時,才適用此選項。", + "symbols/AllInteractive/displayName": "_啟用整個網站的全域互動式轉譯", + "symbols/AllInteractive/description": "設定是否要在最上層套用互動式轉譯模式,讓每個頁面都成為互動式。如果為 false,則頁面預設會使用靜態伺服器轉譯,而且可以以每頁或每一元件為基礎標示為互動式。", + "symbols/NoHttps/description": "是否要關閉 HTTPS。此選項僅適用於個人未用於 --auth 時。", + "symbols/UseProgramMain/displayName": "不要使用最上層陳述式(_T)", + "symbols/UseProgramMain/description": "是否要產生明確的 Program 類別和 Main 方法,而非最上層語句。", + "postActions/restore/description": "還原此專案所需的 NuGet 套件。", + "postActions/restore/manualInstructions/default/text": "執行 'dotnet restore'" +} diff --git a/src/Templates/templates/blazorweb-csharp-10/.template.config/template.json b/src/Templates/templates/blazorweb-csharp-10/.template.config/template.json new file mode 100644 index 0000000000..149c9c4f71 --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-10/.template.config/template.json @@ -0,0 +1,498 @@ +{ + "$schema": "https://json.schemastore.org/template", + "author": "Microsoft", + "classifications": [ + "Web", + "Fluent", + "Blazor", + "WebAssembly" + ], + "name": "Fluent Blazor Web App", + "defaultName": "BlazorApp", + "description": "A project template for creating a Blazor web app that supports both server-side rendering and client interactivity. This template can be used for web apps with rich dynamic user interfaces (UIs).", + "groupIdentity": "Microsoft.Web.Fluent.Blazor", + "precedence": "10002", + "guids": [ + "4C26868E-5E7C-458D-82E3-040509D0C71F", + "5990939C-7E7B-4CFA-86FF-44CA5756498A", + "650B3CE7-2E93-4CC4-9F46-466686815EAA", + "53bc9b9d-9d6a-45d4-8429-2a2761773502" + ], + "identity": "Microsoft.Web.Fluent.Blazor.CSharp.10.0", + "thirdPartyNotices": "https://aka.ms/aspnetcore/10.0-third-party-notices", + "preferNameDirectory": true, + "primaryOutputs": [ + { + "condition": "(UseWebAssembly && (HostIdentifier == \"dotnetcli\" || HostIdentifier == \"dotnetcli-preview\"))", + "path": "BlazorWeb-CSharp.sln" + }, + { + "condition": "(UseWebAssembly && HostIdentifier != \"dotnetcli\" && HostIdentifier != \"dotnetcli-preview\")", + "path": "BlazorWeb-CSharp/BlazorWeb-CSharp.csproj" + }, + { + "condition": "(!UseWebAssembly)", + "path": "BlazorWeb-CSharp.csproj" + }, + { + "condition": "(UseWebAssembly && HostIdentifier != \"dotnetcli\" && HostIdentifier != \"dotnetcli-preview\")", + "path": "BlazorWeb-CSharp.Client/BlazorWeb-CSharp.Client.csproj" + } + ], + "shortName": "fluentblazor", + "sourceName": "BlazorWeb-CSharp", + "sources": [ + { + "source": "./", + "target": "./", + "exclude": [ + ".template.config/**" + ], + "copyOnly": [ + "**/wwwroot/css/**" + ], + "modifiers": [ + { + "condition": "(!UseWebAssembly)", + "exclude": [ + "BlazorWeb-CSharp.Client/**", + "*.sln" + ], + "rename": { + "BlazorWeb-CSharp/": "./" + } + }, + { + "condition": "(UseWebAssembly && InteractiveAtRoot)", + "rename": { + "BlazorWeb-CSharp/Components/Layout/MainLayout.razor": "./BlazorWeb-CSharp.Client/Layout/MainLayout.razor", + "BlazorWeb-CSharp/Components/Pages/Home.razor": "./BlazorWeb-CSharp.Client/Pages/Home.razor", + "BlazorWeb-CSharp/Components/Pages/NotFound.razor": "./BlazorWeb-CSharp.Client/Pages/NotFound.razor", + "BlazorWeb-CSharp/Components/Pages/Weather.razor": "./BlazorWeb-CSharp.Client/Pages/Weather.razor", + "BlazorWeb-CSharp/Components/Routes.razor": "./BlazorWeb-CSharp.Client/Routes.razor" + } + }, + { + "condition": "(UseWebAssembly)", + "exclude": [ + "BlazorWeb-CSharp/Components/Layout/NavMenu.razor", + "BlazorWeb-CSharp/Components/Layout/NavMenu.razor.css" + ] + }, + { + "condition": "(!UseProgramMain)", + "exclude": [ + "BlazorWeb-CSharp/Program.Main.cs", + "BlazorWeb-CSharp.Client/Program.Main.cs" + ] + }, + { + "condition": "(UseProgramMain)", + "exclude": [ + "BlazorWeb-CSharp/Program.cs", + "BlazorWeb-CSharp.Client/Program.cs" + ], + "rename": { + "Program.Main.cs": "Program.cs" + } + }, + { + "condition": "(!(UseServer && !UseWebAssembly))", + "exclude": [ + "BlazorWeb-CSharp/Components/Pages/Counter.razor" + ] + }, + { + "condition": "(!UseServer)", + "exclude": [ + "BlazorWeb-CSharp/Components/Layout/ReconnectModal.razor", + "BlazorWeb-CSharp/Components/Layout/ReconnectModal.razor.css", + "BlazorWeb-CSharp/Components/Layout/ReconnectModal.razor.js", + "BlazorWeb-CSharp.Client/Layout/ReconnectModal.razor", + "BlazorWeb-CSharp.Client/Layout/ReconnectModal.razor.css", + "BlazorWeb-CSharp.Client/Layout/ReconnectModal.razor.js" + ] + }, + { + "condition": "(ExcludeLaunchSettings)", + "exclude": [ + "BlazorWeb-CSharp/Properties/launchSettings.json" + ] + }, + { + "condition": "(!SampleContent)", + "exclude": [ + "BlazorWeb-CSharp/Components/Pages/Auth.*", + "BlazorWeb-CSharp/Components/Pages/Counter.*", + "BlazorWeb-CSharp/Components/Pages/Weather.*", + "BlazorWeb-CSharp/Components/Layout/NavMenu.*", + "BlazorWeb-CSharp/wwwroot/favicon.*", + "BlazorWeb-CSharp.Client/Layout/NavMenu.*", + "BlazorWeb-CSharp.Client/Pages/**", + "BlazorWeb-CSharp.Client/wwwroot/**" + ] + }, + { + "condition": "(UseWebAssembly && HostIdentifier != \"dotnetcli\" && HostIdentifier != \"dotnetcli-preview\")", + "exclude": [ + "*.sln" + ] + }, + { + "condition": "(!IndividualLocalAuth)", + "exclude": [ + "BlazorWeb-CSharp/Components/Account/**", + "BlazorWeb-CSharp/Data/**", + "BlazorWeb-CSharp.Client/UserInfo.cs", + "BlazorWeb-CSharp.Client/Pages/Auth.razor" + ] + }, + { + "condition": "(!(IndividualLocalAuth && !UseLocalDB))", + "exclude": [ + "BlazorWeb-CSharp/Data/app.db" + ] + }, + { + "condition": "(!(IndividualLocalAuth && !UseWebAssembly))", + "exclude": [ + "BlazorWeb-CSharp/Components/Pages/Auth.razor" + ] + }, + { + "condition": "(!(IndividualLocalAuth && UseServer))", + "exclude": [ + "BlazorWeb-CSharp/Components/Account/IdentityRevalidatingAuthenticationStateProvider.cs" + ] + }, + { + "condition": "(IndividualLocalAuth && UseLocalDB && UseWebAssembly)", + "rename": { + "BlazorWeb-CSharp/Data/SqlServer/": "BlazorWeb-CSharp/Data/Migrations/" + }, + "exclude": [ + "BlazorWeb-CSharp/Data/SqlLite/**" + ] + }, + { + "condition": "(IndividualLocalAuth && UseLocalDB && !UseWebAssembly)", + "rename": { + "BlazorWeb-CSharp/Data/SqlServer/": "Data/Migrations/" + }, + "exclude": [ + "BlazorWeb-CSharp/Data/SqlLite/**" + ] + }, + { + "condition": "(IndividualLocalAuth && !UseLocalDB && UseWebAssembly)", + "rename": { + "BlazorWeb-CSharp/Data/SqlLite/": "BlazorWeb-CSharp/Data/Migrations/" + }, + "exclude": [ + "BlazorWeb-CSharp/Data/SqlServer/**" + ] + }, + { + "condition": "(IndividualLocalAuth && !UseLocalDB && !UseWebAssembly)", + "rename": { + "BlazorWeb-CSharp/Data/SqlLite/": "Data/Migrations/" + }, + "exclude": [ + "BlazorWeb-CSharp/Data/SqlServer/**" + ] + }, + { + "condition": "(IndividualLocalAuth && UseWebAssembly)", + "rename": { + "BlazorWeb-CSharp/Components/Account/Shared/RedirectToLogin.razor": "BlazorWeb-CSharp.Client/RedirectToLogin.razor" + } + } + ] + } + ], + "symbols": { + "Framework": { + "type": "parameter", + "description": "The target framework for the project.", + "datatype": "choice", + "choices": [ + { + "choice": "net10.0", + "description": "Target net10.0" + } + ], + "replaces": "net10.0", + "defaultValue": "net10.0" + }, + "HostIdentifier": { + "type": "bind", + "binding": "HostIdentifier" + }, + "UserSecretsId": { + "type": "parameter", + "datatype": "string", + "replaces": "aspnet-BlazorWeb-CSharp-53bc9b9d-9d6a-45d4-8429-2a2761773502", + "defaultValue": "aspnet-BlazorWeb-CSharp-53bc9b9d-9d6a-45d4-8429-2a2761773502", + "description": "The ID to use for secrets (use with Individual auth)." + }, + "skipRestore": { + "type": "parameter", + "datatype": "bool", + "description": "If specified, skips the automatic restore of the project on create.", + "defaultValue": "false" + }, + "ExcludeLaunchSettings": { + "type": "parameter", + "datatype": "bool", + "defaultValue": "false", + "description": "Whether to exclude launchSettings.json from the generated template." + }, + "kestrelHttpPort": { + "type": "parameter", + "datatype": "integer", + "description": "Port number to use for the HTTP endpoint in launchSettings.json." + }, + "kestrelHttpPortGenerated": { + "type": "generated", + "generator": "port", + "parameters": { + "low": 5000, + "high": 5300 + } + }, + "kestrelHttpPortReplacer": { + "type": "generated", + "generator": "coalesce", + "parameters": { + "sourceVariableName": "kestrelHttpPort", + "fallbackVariableName": "kestrelHttpPortGenerated" + }, + "replaces": "5500" + }, + "kestrelHttpsPort": { + "type": "parameter", + "datatype": "integer", + "description": "Port number to use for the HTTPS endpoint in launchSettings.json. This option is only applicable when the parameter no-https is not used (no-https will be ignored if Individual auth is used)." + }, + "kestrelHttpsPortGenerated": { + "type": "generated", + "generator": "port", + "parameters": { + "low": 7000, + "high": 7300 + } + }, + "kestrelHttpsPortReplacer": { + "type": "generated", + "generator": "coalesce", + "parameters": { + "sourceVariableName": "kestrelHttpsPort", + "fallbackVariableName": "kestrelHttpsPortGenerated" + }, + "replaces": "5501" + }, + "iisHttpPort": { + "type": "parameter", + "datatype": "integer", + "description": "Port number to use for the IIS Express HTTP endpoint in launchSettings.json." + }, + "iisHttpPortGenerated": { + "type": "generated", + "generator": "port" + }, + "iisHttpPortReplacer": { + "type": "generated", + "generator": "coalesce", + "parameters": { + "sourceVariableName": "iisHttpPort", + "fallbackVariableName": "iisHttpPortGenerated" + }, + "replaces": "8080" + }, + "iisHttpsPort": { + "type": "parameter", + "datatype": "integer", + "description": "Port number to use for the IIS Express HTTPS endpoint in launchSettings.json. This option is only applicable when the parameter no-https is not used (no-https will be ignored if Individual auth is used)." + }, + "iisHttpsPortGenerated": { + "type": "generated", + "generator": "port", + "parameters": { + "low": 44300, + "high": 44399 + } + }, + "iisHttpsPortReplacer": { + "type": "generated", + "generator": "coalesce", + "parameters": { + "sourceVariableName": "iisHttpsPort", + "fallbackVariableName": "iisHttpsPortGenerated" + }, + "replaces": "44300" + }, + "InteractivityPlatform": { + "type": "parameter", + "datatype": "choice", + "defaultValue": "Server", + "displayName": "_Interactive render mode", + "description": "Chooses which interactive render mode to use for interactive components", + "choices": [ + { + "choice": "None", + "displayName": "None", + "description": "No interactivity (static server rendering only)" + }, + { + "choice": "Server", + "displayName": "Server", + "description": "Runs on the server" + }, + { + "choice": "WebAssembly", + "displayName": "WebAssembly", + "description": "Runs in the browser using WebAssembly" + }, + { + "choice": "Auto", + "displayName": "Auto (Server and WebAssembly)", + "description": "Uses Server while downloading WebAssembly assets, then uses WebAssembly" + } + ] + }, + "InteractivityLocation": { + "type": "parameter", + "datatype": "choice", + "defaultValue": "InteractivePerPage", + "displayName": "Interactivity _location", + "description": "Chooses which components will have interactive rendering enabled", + "isEnabled": "(InteractivityPlatform != \"None\")", + "choices": [ + { + "choice": "InteractivePerPage", + "displayName": "Per page/component", + "description": "Interactivity is applied on a per-page or per-component basis" + }, + { + "choice": "InteractiveGlobal", + "displayName": "Global", + "description": "Interactivity is applied at the root level" + } + ] + }, + "UseWebAssembly": { + "type": "computed", + "value": "(InteractivityPlatform == \"WebAssembly\" || InteractivityPlatform == \"Auto\")" + }, + "UseServer": { + "type": "computed", + "value": "(InteractivityPlatform == \"Server\" || InteractivityPlatform == \"Auto\")" + }, + "IncludeSampleContent": { + "type": "parameter", + "datatype": "bool", + "defaultValue": "true", + "displayName": "Include _sample pages", + "description": "Configures whether to add sample pages and styling to demonstrate basic usage patterns." + }, + "Empty": { + "type": "parameter", + "datatype": "bool", + "defaultValue": "false", + "description": "Configures whether to omit sample pages and styling that demonstrate basic usage patterns." + }, + "auth": { + "type": "parameter", + "datatype": "choice", + "choices": [ + { + "choice": "None", + "description": "No authentication" + }, + { + "choice": "Individual", + "description": "Individual authentication" + } + ], + "defaultValue": "None", + "description": "The type of authentication to use" + }, + "UseLocalDB": { + "type": "parameter", + "datatype": "bool", + "defaultValue": "false", + "description": "Whether to use LocalDB instead of SQLite. This option only applies if --auth Individual is specified." + }, + "SampleContent": { + "type": "computed", + "value": "(((IncludeSampleContent && (HostIdentifier != \"dotnetcli\" && HostIdentifier != \"dotnetcli-preview\"))) || ((!Empty && (HostIdentifier == \"dotnetcli\" || HostIdentifier == \"dotnetcli-preview\"))))" + }, + "AllInteractive": { + "type": "parameter", + "datatype": "bool", + "isEnabled": "(InteractivityPlatform != \"None\")", + "defaultValue": "false", + "displayName": "_Enable interactive rendering globally throughout the site", + "description": "Configures whether to make every page interactive by applying an interactive render mode at the top level. If false, pages will use static server rendering by default, and can be marked interactive on a per-page or per-component basis." + }, + "InteractiveAtRoot": { + "type": "computed", + "value": "(InteractivityLocation == \"InteractiveGlobal\" || AllInteractive)" + }, + "IndividualLocalAuth": { + "type": "computed", + "value": "(auth == \"Individual\")" + }, + "RequiresHttps": { + "type": "computed", + "value": "(OrganizationalAuth || IndividualLocalAuth)" + }, + "HasHttpProfile": { + "type": "computed", + "value": "(!RequiresHttps)" + }, + "HasHttpsProfile": { + "type": "computed", + "value": "(RequiresHttps || !NoHttps)" + }, + "NoHttps": { + "type": "parameter", + "datatype": "bool", + "defaultValue": "false", + "description": "Whether to turn off HTTPS. This option only applies if Individual isn't used for --auth." + }, + "copyrightYear": { + "type": "generated", + "generator": "now", + "replaces": "copyrightYear", + "parameters": { + "format": "yyyy" + } + }, + "UseProgramMain": { + "type": "parameter", + "datatype": "bool", + "defaultValue": "false", + "displayName": "Do not use _top-level statements", + "description": "Whether to generate an explicit Program class and Main method instead of top-level statements." + } + }, + "tags": { + "language": "C#", + "type": "project" + }, + "postActions": [ + { + "id": "restore", + "condition": "(!skipRestore)", + "description": "Restore NuGet packages required by this project.", + "manualInstructions": [ + { + "text": "Run 'dotnet restore'" + } + ], + "actionId": "210D431B-A78B-4D2F-B762-4ED3E3EA9025", + "continueOnError": true + } + ] +} diff --git a/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp.Client/BlazorWeb-CSharp.Client.csproj b/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp.Client/BlazorWeb-CSharp.Client.csproj new file mode 100644 index 0000000000..ecd9a77914 --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp.Client/BlazorWeb-CSharp.Client.csproj @@ -0,0 +1,21 @@ + + + + net10.0 + enable + enable + true + Default + BlazorWeb-CSharp.Client + $(AssemblyName.Replace(' ', '_')) + true + + + + + + + + + + diff --git a/src/Templates/templates/blazorweb-csharp/BlazorWeb-CSharp.Client/Layout/NavMenu.razor b/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp.Client/Layout/NavMenu.razor similarity index 100% rename from src/Templates/templates/blazorweb-csharp/BlazorWeb-CSharp.Client/Layout/NavMenu.razor rename to src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp.Client/Layout/NavMenu.razor diff --git a/src/Templates/templates/blazorweb-csharp/BlazorWeb-CSharp.Client/Pages/Auth.razor b/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp.Client/Pages/Auth.razor similarity index 100% rename from src/Templates/templates/blazorweb-csharp/BlazorWeb-CSharp.Client/Pages/Auth.razor rename to src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp.Client/Pages/Auth.razor diff --git a/src/Templates/templates/blazorweb-csharp/BlazorWeb-CSharp.Client/Pages/Counter.razor b/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp.Client/Pages/Counter.razor similarity index 100% rename from src/Templates/templates/blazorweb-csharp/BlazorWeb-CSharp.Client/Pages/Counter.razor rename to src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp.Client/Pages/Counter.razor diff --git a/src/Templates/templates/blazorweb-csharp/BlazorWeb-CSharp.Client/Program.Main.cs b/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp.Client/Program.Main.cs similarity index 87% rename from src/Templates/templates/blazorweb-csharp/BlazorWeb-CSharp.Client/Program.Main.cs rename to src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp.Client/Program.Main.cs index 571989923e..20605af0f7 100644 --- a/src/Templates/templates/blazorweb-csharp/BlazorWeb-CSharp.Client/Program.Main.cs +++ b/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp.Client/Program.Main.cs @@ -1,3 +1,5 @@ +using Microsoft.AspNetCore.Components.WebAssembly.Hosting; + namespace BlazorWeb_CSharp.Client; class Program @@ -5,12 +7,12 @@ class Program static async Task Main(string[] args) { var builder = WebAssemblyHostBuilder.CreateDefault(args); - builder.Services.AddFluentUIComponents(); #if (IndividualLocalAuth) builder.Services.AddAuthorizationCore(); builder.Services.AddCascadingAuthenticationState(); builder.Services.AddAuthenticationStateDeserialization(); + #endif await builder.Build().RunAsync(); } diff --git a/src/Templates/templates/blazorweb-csharp/BlazorWeb-CSharp.Client/Program.cs b/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp.Client/Program.cs similarity index 78% rename from src/Templates/templates/blazorweb-csharp/BlazorWeb-CSharp.Client/Program.cs rename to src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp.Client/Program.cs index a55e36857d..19becf2331 100644 --- a/src/Templates/templates/blazorweb-csharp/BlazorWeb-CSharp.Client/Program.cs +++ b/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp.Client/Program.cs @@ -1,8 +1,6 @@ using Microsoft.AspNetCore.Components.WebAssembly.Hosting; -using Microsoft.FluentUI.AspNetCore.Components; var builder = WebAssemblyHostBuilder.CreateDefault(args); -builder.Services.AddFluentUIComponents(); #if (IndividualLocalAuth) builder.Services.AddAuthorizationCore(); diff --git a/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp.Client/_Imports.razor b/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp.Client/_Imports.razor new file mode 100644 index 0000000000..1f2dde0191 --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp.Client/_Imports.razor @@ -0,0 +1,17 @@ +@using System.Net.Http +@using System.Net.Http.Json +@*#if (IndividualLocalAuth) +@using Microsoft.AspNetCore.Components.Authorization +##endif*@ +@using Microsoft.AspNetCore.Components.Forms +@using Microsoft.AspNetCore.Components.Routing +@using Microsoft.AspNetCore.Components.Web +@using static Microsoft.AspNetCore.Components.Web.RenderMode +@using Microsoft.AspNetCore.Components.Web.Virtualization +@using Microsoft.FluentUI.AspNetCore.Components +@using Icons = Microsoft.FluentUI.AspNetCore.Components.Icons +@using Microsoft.JSInterop +@using BlazorWeb_CSharp.Client +@*#if (UseWebAssembly && InteractiveAtRoot) --> +@using BlazorWeb_CSharp.Client.Layout +##endif*@ \ No newline at end of file diff --git a/src/Templates/templates/blazorweb-csharp/BlazorWeb-CSharp.Client/wwwroot/appsettings.Development.json b/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp.Client/wwwroot/appsettings.Development.json similarity index 100% rename from src/Templates/templates/blazorweb-csharp/BlazorWeb-CSharp.Client/wwwroot/appsettings.Development.json rename to src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp.Client/wwwroot/appsettings.Development.json diff --git a/src/Templates/templates/blazorweb-csharp/BlazorWeb-CSharp.Client/wwwroot/appsettings.json b/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp.Client/wwwroot/appsettings.json similarity index 100% rename from src/Templates/templates/blazorweb-csharp/BlazorWeb-CSharp.Client/wwwroot/appsettings.json rename to src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp.Client/wwwroot/appsettings.json diff --git a/src/Templates/templates/blazorweb-csharp/BlazorWeb-CSharp.sln b/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp.sln similarity index 100% rename from src/Templates/templates/blazorweb-csharp/BlazorWeb-CSharp.sln rename to src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp.sln diff --git a/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/BlazorWeb-CSharp.csproj b/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/BlazorWeb-CSharp.csproj new file mode 100644 index 0000000000..4ba1dfc6db --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/BlazorWeb-CSharp.csproj @@ -0,0 +1,36 @@ + + + + net10.0 + enable + enable + aspnet-BlazorWeb-CSharp-53bc9b9d-9d6a-45d4-8429-2a2761773502 + True + BlazorWeb-CSharp + $(AssemblyName.Replace(' ', '_')) + true + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Components/Account/IdentityComponentsEndpointRouteBuilderExtensions.cs b/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Components/Account/IdentityComponentsEndpointRouteBuilderExtensions.cs new file mode 100644 index 0000000000..09dde2a871 --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Components/Account/IdentityComponentsEndpointRouteBuilderExtensions.cs @@ -0,0 +1,163 @@ +using System.Security.Claims; +using System.Text.Json; +using Microsoft.AspNetCore.Authentication; +using Microsoft.AspNetCore.Components.Authorization; +using Microsoft.AspNetCore.Http.Extensions; +using Microsoft.AspNetCore.Identity; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Primitives; +using BlazorWeb_CSharp.Components.Account.Pages; +using BlazorWeb_CSharp.Components.Account.Pages.Manage; +using BlazorWeb_CSharp.Data; + +namespace Microsoft.AspNetCore.Routing; + +internal static class IdentityComponentsEndpointRouteBuilderExtensions +{ + // These endpoints are required by the Identity Razor components defined in the /Components/Account/Pages directory of this project. + public static IEndpointConventionBuilder MapAdditionalIdentityEndpoints(this IEndpointRouteBuilder endpoints) + { + ArgumentNullException.ThrowIfNull(endpoints); + + var accountGroup = endpoints.MapGroup("/Account"); + + accountGroup.MapPost("/PerformExternalLogin", ( + HttpContext context, + [FromServices] SignInManager signInManager, + [FromForm] string provider, + [FromForm] string returnUrl) => + { + IEnumerable> query = [ + new("ReturnUrl", returnUrl), + new("Action", ExternalLogin.LoginCallbackAction)]; + + var redirectUrl = UriHelper.BuildRelative( + context.Request.PathBase, + "/Account/ExternalLogin", + QueryString.Create(query)); + + provider = TemporaryFluentButtonFix(provider); + + var properties = signInManager.ConfigureExternalAuthenticationProperties(provider, redirectUrl); + return TypedResults.Challenge(properties, [provider]); + }); + + accountGroup.MapPost("/Logout", async ( + ClaimsPrincipal user, + [FromServices] SignInManager signInManager, + [FromForm] string returnUrl) => + { + await signInManager.SignOutAsync(); + return TypedResults.LocalRedirect($"~/{returnUrl}"); + }); + + accountGroup.MapPost("/PasskeyCreationOptions", async ( + HttpContext context, + [FromServices] UserManager userManager, + [FromServices] SignInManager signInManager) => + { + var user = await userManager.GetUserAsync(context.User); + if (user is null) + { + return Results.NotFound($"Unable to load user with ID '{userManager.GetUserId(context.User)}'."); + } + + var userId = await userManager.GetUserIdAsync(user); + var userName = await userManager.GetUserNameAsync(user) ?? "User"; + var userEntity = new PasskeyUserEntity(userId, userName, displayName: userName); + var passkeyCreationArgs = new PasskeyCreationArgs(userEntity); + var options = await signInManager.ConfigurePasskeyCreationOptionsAsync(passkeyCreationArgs); + return TypedResults.Content(options.AsJson(), contentType: "application/json"); + }); + + accountGroup.MapPost("/PasskeyRequestOptions", async ( + [FromServices] UserManager userManager, + [FromServices] SignInManager signInManager, + [FromQuery] string? username) => + { + var user = string.IsNullOrEmpty(username) ? null : await userManager.FindByNameAsync(username); + var passkeyRequestArgs = new PasskeyRequestArgs + { + User = user, + }; + var options = await signInManager.ConfigurePasskeyRequestOptionsAsync(passkeyRequestArgs); + return TypedResults.Content(options.AsJson(), contentType: "application/json"); + }); + + var manageGroup = accountGroup.MapGroup("/Manage").RequireAuthorization(); + + manageGroup.MapPost("/LinkExternalLogin", async ( + HttpContext context, + [FromServices] SignInManager signInManager, + [FromForm] string provider) => + { + // Clear the existing external cookie to ensure a clean login process + await context.SignOutAsync(IdentityConstants.ExternalScheme); + + var redirectUrl = UriHelper.BuildRelative( + context.Request.PathBase, + "/Account/Manage/ExternalLogins", + QueryString.Create("Action", ExternalLogins.LinkLoginCallbackAction)); + + provider = TemporaryFluentButtonFix(provider); + + var properties = signInManager.ConfigureExternalAuthenticationProperties(provider, redirectUrl, signInManager.UserManager.GetUserId(context.User)); + return TypedResults.Challenge(properties, [provider]); + }); + + var loggerFactory = endpoints.ServiceProvider.GetRequiredService(); + var downloadLogger = loggerFactory.CreateLogger("DownloadPersonalData"); + + manageGroup.MapPost("/DownloadPersonalData", async ( + HttpContext context, + [FromServices] UserManager userManager, + [FromServices] AuthenticationStateProvider authenticationStateProvider) => + { + var user = await userManager.GetUserAsync(context.User); + if (user is null) + { + return Results.NotFound($"Unable to load user with ID '{userManager.GetUserId(context.User)}'."); + } + + var userId = await userManager.GetUserIdAsync(user); + downloadLogger.LogInformation("User with ID '{UserId}' asked for their personal data.", userId); + + // Only include personal data for download + var personalData = new Dictionary(); + var personalDataProps = typeof(ApplicationUser).GetProperties().Where( + prop => Attribute.IsDefined(prop, typeof(PersonalDataAttribute))); + foreach (var p in personalDataProps) + { + personalData.Add(p.Name, p.GetValue(user)?.ToString() ?? "null"); + } + + var logins = await userManager.GetLoginsAsync(user); + foreach (var l in logins) + { + personalData.Add($"{l.LoginProvider} external login provider key", l.ProviderKey); + } + + personalData.Add("Authenticator Key", (await userManager.GetAuthenticatorKeyAsync(user))!); + var fileBytes = JsonSerializer.SerializeToUtf8Bytes(personalData); + + context.Response.Headers.TryAdd("Content-Disposition", "attachment; filename=PersonalData.json"); + return TypedResults.File(fileBytes, contentType: "application/json", fileDownloadName: "PersonalData.json"); + }); + + return accountGroup; + } + + private static string TemporaryFluentButtonFix(string provider) + { + // Temporary workaround for FluentButton returning a provider value twice + // Split the comma-separated list of strings + var providers = provider.Split(','); + + // Find the value that appears twice in the list + provider = providers.GroupBy(p => p) + .Where(g => g.Count() == 2) + .Select(g => g.Key) + .First(); + return provider; + } +} diff --git a/src/Templates/templates/blazorweb-csharp/BlazorWeb-CSharp/Components/Account/IdentityNoOpEmailSender.cs b/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Components/Account/IdentityNoOpEmailSender.cs similarity index 100% rename from src/Templates/templates/blazorweb-csharp/BlazorWeb-CSharp/Components/Account/IdentityNoOpEmailSender.cs rename to src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Components/Account/IdentityNoOpEmailSender.cs diff --git a/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Components/Account/IdentityRedirectManager.cs b/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Components/Account/IdentityRedirectManager.cs new file mode 100644 index 0000000000..00734c65c7 --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Components/Account/IdentityRedirectManager.cs @@ -0,0 +1,54 @@ +using Microsoft.AspNetCore.Components; +using Microsoft.AspNetCore.Identity; +using BlazorWeb_CSharp.Data; + +namespace BlazorWeb_CSharp.Components.Account; + +internal sealed class IdentityRedirectManager(NavigationManager navigationManager) +{ + public const string StatusCookieName = "Identity.StatusMessage"; + + private static readonly CookieBuilder StatusCookieBuilder = new() + { + SameSite = SameSiteMode.Strict, + HttpOnly = true, + IsEssential = true, + MaxAge = TimeSpan.FromSeconds(5), + }; + + public void RedirectTo(string? uri) + { + uri ??= ""; + + // Prevent open redirects. + if (!Uri.IsWellFormedUriString(uri, UriKind.Relative)) + { + uri = navigationManager.ToBaseRelativePath(uri); + } + + navigationManager.NavigateTo(uri); + } + + public void RedirectTo(string uri, Dictionary queryParameters) + { + var uriWithoutQuery = navigationManager.ToAbsoluteUri(uri).GetLeftPart(UriPartial.Path); + var newUri = navigationManager.GetUriWithQueryParameters(uriWithoutQuery, queryParameters); + RedirectTo(newUri); + } + + public void RedirectToWithStatus(string uri, string message, HttpContext context) + { + context.Response.Cookies.Append(StatusCookieName, message, StatusCookieBuilder.Build(context)); + RedirectTo(uri); + } + + private string CurrentPath => navigationManager.ToAbsoluteUri(navigationManager.Uri).GetLeftPart(UriPartial.Path); + + public void RedirectToCurrentPage() => RedirectTo(CurrentPath); + + public void RedirectToCurrentPageWithStatus(string message, HttpContext context) + => RedirectToWithStatus(CurrentPath, message, context); + + public void RedirectToInvalidUser(UserManager userManager, HttpContext context) + => RedirectToWithStatus("Account/InvalidUser", $"Error: Unable to load user with ID '{userManager.GetUserId(context.User)}'.", context); +} diff --git a/src/Templates/templates/blazorweb-csharp/BlazorWeb-CSharp/Components/Account/IdentityRevalidatingAuthenticationStateProvider.cs b/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Components/Account/IdentityRevalidatingAuthenticationStateProvider.cs similarity index 100% rename from src/Templates/templates/blazorweb-csharp/BlazorWeb-CSharp/Components/Account/IdentityRevalidatingAuthenticationStateProvider.cs rename to src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Components/Account/IdentityRevalidatingAuthenticationStateProvider.cs diff --git a/src/Templates/templates/blazorweb-csharp/BlazorWeb-CSharp/Components/Account/Pages/AccessDenied.razor b/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Components/Account/Pages/AccessDenied.razor similarity index 100% rename from src/Templates/templates/blazorweb-csharp/BlazorWeb-CSharp/Components/Account/Pages/AccessDenied.razor rename to src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Components/Account/Pages/AccessDenied.razor diff --git a/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Components/Account/Pages/ConfirmEmail.razor b/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Components/Account/Pages/ConfirmEmail.razor new file mode 100644 index 0000000000..4254c0e65b --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Components/Account/Pages/ConfirmEmail.razor @@ -0,0 +1,49 @@ +@page "/Account/ConfirmEmail" + +@using System.Text +@using Microsoft.AspNetCore.Identity +@using Microsoft.AspNetCore.WebUtilities +@using BlazorWeb_CSharp.Data + +@inject UserManager UserManager +@inject IdentityRedirectManager RedirectManager + +Confirm email + +

Confirm email

+ + +@code { + private string? statusMessage; + + [CascadingParameter] + private HttpContext HttpContext { get; set; } = default!; + + [SupplyParameterFromQuery] + private string? UserId { get; set; } + + [SupplyParameterFromQuery] + private string? Code { get; set; } + + protected override async Task OnInitializedAsync() + { + if (UserId is null || Code is null) + { + RedirectManager.RedirectTo(""); + return; + } + + var user = await UserManager.FindByIdAsync(UserId); + if (user is null) + { + HttpContext.Response.StatusCode = StatusCodes.Status404NotFound; + statusMessage = $"Error loading user with ID {UserId}"; + } + else + { + var code = Encoding.UTF8.GetString(WebEncoders.Base64UrlDecode(Code)); + var result = await UserManager.ConfirmEmailAsync(user, code); + statusMessage = result.Succeeded ? "Thank you for confirming your email." : "Error confirming your email."; + } + } +} diff --git a/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Components/Account/Pages/ConfirmEmailChange.razor b/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Components/Account/Pages/ConfirmEmailChange.razor new file mode 100644 index 0000000000..a1547b6fa4 --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Components/Account/Pages/ConfirmEmailChange.razor @@ -0,0 +1,69 @@ +@page "/Account/ConfirmEmailChange" + +@using System.Text +@using Microsoft.AspNetCore.Identity +@using Microsoft.AspNetCore.WebUtilities +@using BlazorWeb_CSharp.Data + +@inject UserManager UserManager +@inject SignInManager SignInManager +@inject IdentityRedirectManager RedirectManager + +Confirm email change + +

Confirm email change

+ + + +@code { + private string? message; + + [CascadingParameter] + private HttpContext HttpContext { get; set; } = default!; + + [SupplyParameterFromQuery] + private string? UserId { get; set; } + + [SupplyParameterFromQuery] + private string? Email { get; set; } + + [SupplyParameterFromQuery] + private string? Code { get; set; } + + protected override async Task OnInitializedAsync() + { + if (UserId is null || Email is null || Code is null) + { + RedirectManager.RedirectToWithStatus( + "Account/Login", "Error: Invalid email change confirmation link.", HttpContext); + return; + } + + var user = await UserManager.FindByIdAsync(UserId); + if (user is null) + { + message = "Unable to find user with Id '{userId}'"; + return; + } + + var code = Encoding.UTF8.GetString(WebEncoders.Base64UrlDecode(Code)); + var result = await UserManager.ChangeEmailAsync(user, Email, code); + if (!result.Succeeded) + { + message = "Error changing email."; + return; + } + + // In our UI email and user name are one and the same, so when we update the email + // we need to update the user name. + var setUserNameResult = await UserManager.SetUserNameAsync(user, Email); + if (!setUserNameResult.Succeeded) + { + message = "Error changing user name."; + return; + } + + await SignInManager.RefreshSignInAsync(user); + message = "Thank you for confirming your email change."; + } +} diff --git a/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Components/Account/Pages/ExternalLogin.razor b/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Components/Account/Pages/ExternalLogin.razor new file mode 100644 index 0000000000..70a966c618 --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Components/Account/Pages/ExternalLogin.razor @@ -0,0 +1,214 @@ +@page "/Account/ExternalLogin" + +@using System.ComponentModel.DataAnnotations +@using System.Security.Claims +@using System.Text +@using System.Text.Encodings.Web +@using Microsoft.AspNetCore.Identity +@using Microsoft.AspNetCore.WebUtilities +@using BlazorWeb_CSharp.Data + +@inject SignInManager SignInManager +@inject UserManager UserManager +@inject IUserStore UserStore +@inject IEmailSender EmailSender +@inject NavigationManager NavigationManager +@inject IdentityRedirectManager RedirectManager +@inject ILogger Logger + +Register + + +

Register

+

Associate your @ProviderDisplayName account.

+
+ +
+ You've successfully authenticated with @ProviderDisplayName. + Please enter an email address for this site below and click the Register button to finish + logging in. +
+ + + + + + + + + Register + + + + +@code { + public const string LoginCallbackAction = "LoginCallback"; + + private string? message; + private ExternalLoginInfo? externalLoginInfo; + + [CascadingParameter] + private HttpContext HttpContext { get; set; } = default!; + + [SupplyParameterFromForm] + private InputModel Input { get; set; } = default!; + + [SupplyParameterFromQuery] + private string? RemoteError { get; set; } + + [SupplyParameterFromQuery] + private string? ReturnUrl { get; set; } + + [SupplyParameterFromQuery] + private string? Action { get; set; } + + private string? ProviderDisplayName => externalLoginInfo?.ProviderDisplayName; + + protected override async Task OnInitializedAsync() + { + Input ??= new(); + + if (RemoteError is not null) + { + RedirectManager.RedirectToWithStatus("Account/Login", $"Error from external provider: {RemoteError}", HttpContext); + return; + } + + var info = await SignInManager.GetExternalLoginInfoAsync(); + if (info is null) + { + RedirectManager.RedirectToWithStatus("Account/Login", "Error loading external login information.", HttpContext); + return; + } + + externalLoginInfo = info; + + if (HttpMethods.IsGet(HttpContext.Request.Method)) + { + if (Action == LoginCallbackAction) + { + await OnLoginCallbackAsync(); + return; + } + + // We should only reach this page via the login callback, so redirect back to + // the login page if we get here some other way. + RedirectManager.RedirectTo("Account/Login"); + } + } + + private async Task OnLoginCallbackAsync() + { + if (externalLoginInfo is null) + { + RedirectManager.RedirectToWithStatus("Account/Login", "Error loading external login information.", HttpContext); + return; + } + + // Sign in the user with this external login provider if the user already has a login. + var result = await SignInManager.ExternalLoginSignInAsync( + externalLoginInfo.LoginProvider, + externalLoginInfo.ProviderKey, + isPersistent: false, + bypassTwoFactor: true); + + if (result.Succeeded) + { + Logger.LogInformation( + "{Name} logged in with {LoginProvider} provider.", + externalLoginInfo.Principal.Identity?.Name, + externalLoginInfo.LoginProvider); + RedirectManager.RedirectTo(ReturnUrl); + return; + } + else if (result.IsLockedOut) + { + RedirectManager.RedirectTo("Account/Lockout"); + return; + } + + // If the user does not have an account, then ask the user to create an account. + if (externalLoginInfo.Principal.HasClaim(c => c.Type == ClaimTypes.Email)) + { + Input.Email = externalLoginInfo.Principal.FindFirstValue(ClaimTypes.Email) ?? ""; + } + } + + private async Task OnValidSubmitAsync() + { + if (externalLoginInfo is null) + { + RedirectManager.RedirectToWithStatus("Account/Login", "Error loading external login information during confirmation.", HttpContext); + return; + } + + var emailStore = GetEmailStore(); + var user = CreateUser(); + + await UserStore.SetUserNameAsync(user, Input.Email, CancellationToken.None); + await emailStore.SetEmailAsync(user, Input.Email, CancellationToken.None); + + var result = await UserManager.CreateAsync(user); + if (result.Succeeded) + { + result = await UserManager.AddLoginAsync(user, externalLoginInfo); + if (result.Succeeded) + { + Logger.LogInformation("User created an account using {Name} provider.", externalLoginInfo.LoginProvider); + + var userId = await UserManager.GetUserIdAsync(user); + var code = await UserManager.GenerateEmailConfirmationTokenAsync(user); + code = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(code)); + + var callbackUrl = NavigationManager.GetUriWithQueryParameters( + NavigationManager.ToAbsoluteUri("Account/ConfirmEmail").AbsoluteUri, + new Dictionary { ["userId"] = userId, ["code"] = code }); + await EmailSender.SendConfirmationLinkAsync(user, Input.Email, HtmlEncoder.Default.Encode(callbackUrl)); + + // If account confirmation is required, we need to show the link if we don't have a real email sender + if (UserManager.Options.SignIn.RequireConfirmedAccount) + { + RedirectManager.RedirectTo("Account/RegisterConfirmation", new() { ["email"] = Input.Email }); + } + else + { + await SignInManager.SignInAsync(user, isPersistent: false, externalLoginInfo.LoginProvider); + RedirectManager.RedirectTo(ReturnUrl); + } + } + } + else + { + message = $"Error: {string.Join(",", result.Errors.Select(error => error.Description))}"; + } + } + + private ApplicationUser CreateUser() + { + try + { + return Activator.CreateInstance(); + } + catch + { + throw new InvalidOperationException($"Can't create an instance of '{nameof(ApplicationUser)}'. " + + $"Ensure that '{nameof(ApplicationUser)}' is not an abstract class and has a parameterless constructor"); + } + } + + private IUserEmailStore GetEmailStore() + { + if (!UserManager.SupportsUserEmail) + { + throw new NotSupportedException("The default UI requires a user store with email support."); + } + return (IUserEmailStore)UserStore; + } + + private sealed class InputModel + { + [Required] + [EmailAddress] + public string Email { get; set; } = ""; + } +} diff --git a/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Components/Account/Pages/ForgotPassword.razor b/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Components/Account/Pages/ForgotPassword.razor new file mode 100644 index 0000000000..2bd6fd6326 --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Components/Account/Pages/ForgotPassword.razor @@ -0,0 +1,70 @@ +@page "/Account/ForgotPassword" + +@using System.ComponentModel.DataAnnotations +@using System.Text +@using System.Text.Encodings.Web +@using Microsoft.AspNetCore.Identity +@using Microsoft.AspNetCore.WebUtilities +@using BlazorWeb_CSharp.Data + +@inject UserManager UserManager +@inject IEmailSender EmailSender +@inject NavigationManager NavigationManager +@inject IdentityRedirectManager RedirectManager + +Forgot your password? + +

Forgot your password?

+

Enter your email.

+
+ + + + + + + + Reset password + + + + +@code { + [SupplyParameterFromForm] + private InputModel Input { get; set; } = default!; + + protected override void OnInitialized() + { + Input ??= new(); + } + + private async Task OnValidSubmitAsync() + { + var user = await UserManager.FindByEmailAsync(Input.Email); + if (user is null || !(await UserManager.IsEmailConfirmedAsync(user))) + { + // Don't reveal that the user does not exist or is not confirmed + RedirectManager.RedirectTo("Account/ForgotPasswordConfirmation"); + return; + } + + // For more information on how to enable account confirmation and password reset please + // visit https://go.microsoft.com/fwlink/?LinkID=532713 + var code = await UserManager.GeneratePasswordResetTokenAsync(user); + code = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(code)); + var callbackUrl = NavigationManager.GetUriWithQueryParameters( + NavigationManager.ToAbsoluteUri("Account/ResetPassword").AbsoluteUri, + new Dictionary { ["code"] = code }); + + await EmailSender.SendPasswordResetLinkAsync(user, Input.Email, HtmlEncoder.Default.Encode(callbackUrl)); + + RedirectManager.RedirectTo("Account/ForgotPasswordConfirmation"); + } + + private sealed class InputModel + { + [Required] + [EmailAddress] + public string Email { get; set; } = ""; + } +} diff --git a/src/Templates/templates/blazorweb-csharp/BlazorWeb-CSharp/Components/Account/Pages/ForgotPasswordConfirmation.razor b/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Components/Account/Pages/ForgotPasswordConfirmation.razor similarity index 100% rename from src/Templates/templates/blazorweb-csharp/BlazorWeb-CSharp/Components/Account/Pages/ForgotPasswordConfirmation.razor rename to src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Components/Account/Pages/ForgotPasswordConfirmation.razor diff --git a/src/Templates/templates/blazorweb-csharp/BlazorWeb-CSharp/Components/Account/Pages/InvalidPasswordReset.razor b/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Components/Account/Pages/InvalidPasswordReset.razor similarity index 100% rename from src/Templates/templates/blazorweb-csharp/BlazorWeb-CSharp/Components/Account/Pages/InvalidPasswordReset.razor rename to src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Components/Account/Pages/InvalidPasswordReset.razor diff --git a/src/Templates/templates/blazorweb-csharp/BlazorWeb-CSharp/Components/Account/Pages/InvalidUser.razor b/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Components/Account/Pages/InvalidUser.razor similarity index 100% rename from src/Templates/templates/blazorweb-csharp/BlazorWeb-CSharp/Components/Account/Pages/InvalidUser.razor rename to src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Components/Account/Pages/InvalidUser.razor diff --git a/src/Templates/templates/blazorweb-csharp/BlazorWeb-CSharp/Components/Account/Pages/Lockout.razor b/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Components/Account/Pages/Lockout.razor similarity index 100% rename from src/Templates/templates/blazorweb-csharp/BlazorWeb-CSharp/Components/Account/Pages/Lockout.razor rename to src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Components/Account/Pages/Lockout.razor diff --git a/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Components/Account/Pages/Login.razor b/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Components/Account/Pages/Login.razor new file mode 100644 index 0000000000..f17a272b46 --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Components/Account/Pages/Login.razor @@ -0,0 +1,150 @@ +@page "/Account/Login" + +@using System.ComponentModel.DataAnnotations +@using Microsoft.AspNetCore.Authentication +@using Microsoft.AspNetCore.Identity +@using BlazorWeb_CSharp.Data + +@inject UserManager UserManager +@inject SignInManager SignInManager +@inject ILogger Logger +@inject NavigationManager NavigationManager +@inject IdentityRedirectManager RedirectManager + +Log in + +

Log in

+ + + + + +

Use a local account to log in.

+
+ + + + + + + + Log in +
+

+ Forgot your password? +

+

+ { ["ReturnUrl"] = ReturnUrl }))">Register as a new user +

+

+ Resend email confirmation +

+
+
+
+
+ +

Use another service to log in.

+
+ +
+
+ +@code { + private string? errorMessage; + private EditContext editContext = default!; + + [CascadingParameter] + private HttpContext HttpContext { get; set; } = default!; + + [SupplyParameterFromForm] + private InputModel Input { get; set; } = default!; + + [SupplyParameterFromQuery] + private string? ReturnUrl { get; set; } + + protected override async Task OnInitializedAsync() + { + Input ??= new(); + + editContext = new EditContext(Input); + + if (HttpMethods.IsGet(HttpContext.Request.Method)) + { + // Clear the existing external cookie to ensure a clean login process + await HttpContext.SignOutAsync(IdentityConstants.ExternalScheme); + } + } + + public async Task LoginUser() + { + if (!string.IsNullOrEmpty(Input.Passkey?.Error)) + { + errorMessage = $"Error: {Input.Passkey.Error}"; + return; + } + + SignInResult result; + if (!string.IsNullOrEmpty(Input.Passkey?.CredentialJson)) + { + // When performing passkey sign-in, don't perform form validation. + var options = await SignInManager.RetrievePasskeyRequestOptionsAsync(); + if (options is null) + { + errorMessage = "Error: Could not complete passkey login. Please try again."; + return; + } + + result = await SignInManager.PasskeySignInAsync(Input.Passkey.CredentialJson, options); + } + else + { + // If doing a password sign-in, validate the form. + if (!editContext.Validate()) + { + return; + } + + // This doesn't count login failures towards account lockout + // To enable password failures to trigger account lockout, set lockoutOnFailure: true + result = await SignInManager.PasswordSignInAsync(Input.Email, Input.Password, Input.RememberMe, lockoutOnFailure: false); + } + + if (result.Succeeded) + { + Logger.LogInformation("User logged in."); + RedirectManager.RedirectTo(ReturnUrl); + } + else if (result.RequiresTwoFactor) + { + RedirectManager.RedirectTo( + "Account/LoginWith2fa", + new() { ["returnUrl"] = ReturnUrl, ["rememberMe"] = Input.RememberMe }); + } + else if (result.IsLockedOut) + { + Logger.LogWarning("User account locked out."); + RedirectManager.RedirectTo("Account/Lockout"); + } + else + { + errorMessage = "Error: Invalid login attempt."; + } + } + + private sealed class InputModel + { + [Required] + [EmailAddress] + public string Email { get; set; } = ""; + + [Required] + [DataType(DataType.Password)] + public string Password { get; set; } = ""; + + [Display(Name = "Remember me?")] + public bool RememberMe { get; set; } + + public PasskeyInputModel? Passkey { get; set; } + } +} diff --git a/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Components/Account/Pages/LoginWith2fa.razor b/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Components/Account/Pages/LoginWith2fa.razor new file mode 100644 index 0000000000..0ecaf957d6 --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Components/Account/Pages/LoginWith2fa.razor @@ -0,0 +1,93 @@ +@page "/Account/LoginWith2fa" + +@using System.ComponentModel.DataAnnotations +@using Microsoft.AspNetCore.Identity +@using BlazorWeb_CSharp.Data + +@inject SignInManager SignInManager +@inject UserManager UserManager +@inject IdentityRedirectManager RedirectManager +@inject ILogger Logger + +Two-factor authentication + +

Two-factor authentication

+
+ +

Your login is protected with an authenticator app. Enter your authenticator code below.

+ + + + + + + + + + + Log in + + + +

+ Don't have access to your authenticator device? You can + log in with a recovery code. +

+ +@code { + private string? message; + private ApplicationUser user = default!; + + [SupplyParameterFromForm] + private InputModel Input { get; set; } = default!; + + [SupplyParameterFromQuery] + private string? ReturnUrl { get; set; } + + [SupplyParameterFromQuery] + private bool RememberMe { get; set; } + + protected override async Task OnInitializedAsync() + { + Input ??= new(); + + // Ensure the user has gone through the username & password screen first + user = await SignInManager.GetTwoFactorAuthenticationUserAsync() ?? + throw new InvalidOperationException("Unable to load two-factor authentication user."); + } + + private async Task OnValidSubmitAsync() + { + var authenticatorCode = Input.TwoFactorCode!.Replace(" ", string.Empty).Replace("-", string.Empty); + var result = await SignInManager.TwoFactorAuthenticatorSignInAsync(authenticatorCode, RememberMe, Input.RememberMachine); + var userId = await UserManager.GetUserIdAsync(user); + + if (result.Succeeded) + { + Logger.LogInformation("User with ID '{UserId}' logged in with 2fa.", userId); + RedirectManager.RedirectTo(ReturnUrl); + } + else if (result.IsLockedOut) + { + Logger.LogWarning("User with ID '{UserId}' account locked out.", userId); + RedirectManager.RedirectTo("Account/Lockout"); + } + else + { + Logger.LogWarning("Invalid authenticator code entered for user with ID '{UserId}'.", userId); + message = "Error: Invalid authenticator code."; + } + } + + private sealed class InputModel + { + [Required] + [StringLength(7, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 6)] + [DataType(DataType.Text)] + [Display(Name = "Authenticator code")] + public string? TwoFactorCode { get; set; } + + [Display(Name = "Remember this machine")] + public bool RememberMachine { get; set; } + } +} diff --git a/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Components/Account/Pages/LoginWithRecoveryCode.razor b/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Components/Account/Pages/LoginWithRecoveryCode.razor new file mode 100644 index 0000000000..9294de243e --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Components/Account/Pages/LoginWithRecoveryCode.razor @@ -0,0 +1,83 @@ +@page "/Account/LoginWithRecoveryCode" + +@using System.ComponentModel.DataAnnotations +@using Microsoft.AspNetCore.Identity +@using BlazorWeb_CSharp.Data + +@inject SignInManager SignInManager +@inject UserManager UserManager +@inject IdentityRedirectManager RedirectManager +@inject ILogger Logger + +Recovery code verification + +

Recovery code verification

+
+ +

+ You have requested to log in with a recovery code. This login will not be remembered until you provide + an authenticator app code at log in or disable 2FA and log in again. +

+ + + + + + + + Log in + + + +@code { + private string? message; + private ApplicationUser user = default!; + + [SupplyParameterFromForm] + private InputModel Input { get; set; } = default!; + + [SupplyParameterFromQuery] + private string? ReturnUrl { get; set; } + + protected override async Task OnInitializedAsync() + { + Input ??= new(); + + // Ensure the user has gone through the username & password screen first + user = await SignInManager.GetTwoFactorAuthenticationUserAsync() ?? + throw new InvalidOperationException("Unable to load two-factor authentication user."); + } + + private async Task OnValidSubmitAsync() + { + var recoveryCode = Input.RecoveryCode.Replace(" ", string.Empty); + + var result = await SignInManager.TwoFactorRecoveryCodeSignInAsync(recoveryCode); + + var userId = await UserManager.GetUserIdAsync(user); + + if (result.Succeeded) + { + Logger.LogInformation("User with ID '{UserId}' logged in with a recovery code.", userId); + RedirectManager.RedirectTo(ReturnUrl); + } + else if (result.IsLockedOut) + { + Logger.LogWarning("User account locked out."); + RedirectManager.RedirectTo("Account/Lockout"); + } + else + { + Logger.LogWarning("Invalid recovery code entered for user with ID '{UserId}' ", userId); + message = "Error: Invalid recovery code entered."; + } + } + + private sealed class InputModel + { + [Required] + [DataType(DataType.Text)] + [Display(Name = "Recovery Code")] + public string RecoveryCode { get; set; } = ""; + } +} diff --git a/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Components/Account/Pages/Manage/ChangePassword.razor b/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Components/Account/Pages/Manage/ChangePassword.razor new file mode 100644 index 0000000000..66dcd1e002 --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Components/Account/Pages/Manage/ChangePassword.razor @@ -0,0 +1,103 @@ +@page "/Account/Manage/ChangePassword" + +@using System.ComponentModel.DataAnnotations +@using Microsoft.AspNetCore.Identity +@using TemplateWithPasskey.Data + +@inject UserManager UserManager +@inject SignInManager SignInManager +@inject RedirectManager RedirectManager +@inject IdentityRedirectManager RedirectManager +@inject ILogger Logger + +Change password + +

Change password

+ + + + + + + + + + + + +

+ Update password +

+
+
+
+ +@code { + private string? message; + private ApplicationUser? user; + private bool hasPassword; + + [CascadingParameter] + private HttpContext HttpContext { get; set; } = default!; + + [SupplyParameterFromForm] + private InputModel Input { get; set; } = default!; + + protected override async Task OnInitializedAsync() + { + Input ??= new(); + + user = await UserManager.GetUserAsync(HttpContext.User); + if (user is null) + { + RedirectManager.RedirectToInvalidUser(UserManager, HttpContext); + return; + } + + hasPassword = await UserManager.HasPasswordAsync(user); + if (!hasPassword) + { + RedirectManager.RedirectTo("Account/Manage/SetPassword"); + } + } + + private async Task OnValidSubmitAsync() + { + if (user is null) + { + RedirectManager.RedirectToInvalidUser(UserManager, HttpContext); + return; + } + + var changePasswordResult = await UserManager.ChangePasswordAsync(user, Input.OldPassword, Input.NewPassword); + if (!changePasswordResult.Succeeded) + { + message = $"Error: {string.Join(",", changePasswordResult.Errors.Select(error => error.Description))}"; + return; + } + + await SignInManager.RefreshSignInAsync(user); + Logger.LogInformation("User changed their password successfully."); + + RedirectManager.RedirectToCurrentPageWithStatus("Your password has been changed", HttpContext); + } + + private sealed class InputModel + { + [Required] + [DataType(DataType.Password)] + [Display(Name = "Current password")] + public string OldPassword { get; set; } = ""; + + [Required] + [StringLength(100, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 6)] + [DataType(DataType.Password)] + [Display(Name = "New password")] + public string NewPassword { get; set; } = ""; + + [DataType(DataType.Password)] + [Display(Name = "Confirm new password")] + [Compare("NewPassword", ErrorMessage = "The new password and confirmation password do not match.")] + public string ConfirmPassword { get; set; } = ""; + } +} diff --git a/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Components/Account/Pages/Manage/DeletePersonalData.razor b/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Components/Account/Pages/Manage/DeletePersonalData.razor new file mode 100644 index 0000000000..e00d5721f6 --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Components/Account/Pages/Manage/DeletePersonalData.razor @@ -0,0 +1,96 @@ +@page "/Account/Manage/DeletePersonalData" + +@using System.ComponentModel.DataAnnotations +@using Microsoft.AspNetCore.Identity +@using TemplateWithPasskey.Data + +@inject UserManager UserManager +@inject SignInManager SignInManager +@inject IdentityRedirectManager RedirectManager +@inject ILogger Logger + +Delete Personal Data + + + +

Delete Personal Data

+ + + + + + + + @if (requirePassword) + { + + + } +

+ Delete data and close my account +

+
+
+
+@code { + private string? message; + private ApplicationUser? user; + private bool requirePassword; + + [CascadingParameter] + private HttpContext HttpContext { get; set; } = default!; + + [SupplyParameterFromForm] + private InputModel Input { get; set; } = default!; + + protected override async Task OnInitializedAsync() + { + Input ??= new(); + + user = await UserManager.GetUserAsync(HttpContext.User); + if (user is null) + { + RedirectManager.RedirectToInvalidUser(UserManager, HttpContext); + return; + } + requirePassword = await UserManager.HasPasswordAsync(user); + } + + private async Task OnValidSubmitAsync() + { + if (user is null) + { + RedirectManager.RedirectToInvalidUser(UserManager, HttpContext); + return; + } + + if (requirePassword && !await UserManager.CheckPasswordAsync(user, Input.Password)) + { + message = "Error: Incorrect password."; + return; + } + + var result = await UserManager.DeleteAsync(user); + if (!result.Succeeded) + { + throw new InvalidOperationException("Unexpected error occurred deleting user."); + } + + await SignInManager.SignOutAsync(); + + var userId = await UserManager.GetUserIdAsync(user); + Logger.LogInformation("User with ID '{UserId}' deleted themselves.", userId); + + RedirectManager.RedirectToCurrentPage(); + } + + private sealed class InputModel + { + [DataType(DataType.Password)] + public string Password { get; set; } = ""; + } +} diff --git a/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Components/Account/Pages/Manage/Disable2fa.razor b/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Components/Account/Pages/Manage/Disable2fa.razor new file mode 100644 index 0000000000..69ecc5d336 --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Components/Account/Pages/Manage/Disable2fa.razor @@ -0,0 +1,76 @@ +@page "/Account/Manage/Disable2fa" + +@using Microsoft.AspNetCore.Identity +@using BlazorWeb_CSharp.Data + +@inject UserManager UserManager +@inject IdentityRedirectManager RedirectManager +@inject ILogger Logger + +Disable two-factor authentication (2FA) + + +

Disable two-factor authentication (2FA)

+ + + +
+
+ +

+ Disable 2FA +

+ +
+ +@code { + private ApplicationUser? user; + + [CascadingParameter] + private HttpContext HttpContext { get; set; } = default!; + + protected override async Task OnInitializedAsync() + { + user = await UserManager.GetUserAsync(HttpContext.User); + if (user is null) + { + RedirectManager.RedirectToInvalidUser(UserManager, HttpContext); + return; + } + + if (HttpMethods.IsGet(HttpContext.Request.Method) && !await UserManager.GetTwoFactorEnabledAsync(user)) + { + throw new InvalidOperationException("Cannot disable 2FA for user as it's not currently enabled."); + } + } + + private async Task OnSubmitAsync() + { + if (user is null) + { + RedirectManager.RedirectToInvalidUser(UserManager, HttpContext); + return; + } + + var disable2faResult = await UserManager.SetTwoFactorEnabledAsync(user, false); + if (!disable2faResult.Succeeded) + { + throw new InvalidOperationException("Unexpected error occurred disabling 2FA."); + } + + var userId = await UserManager.GetUserIdAsync(user); + Logger.LogInformation("User with ID '{UserId}' has disabled 2fa.", userId); + RedirectManager.RedirectToWithStatus( + "Account/Manage/TwoFactorAuthentication", + "2fa has been disabled. You can reenable 2fa when you setup an authenticator app", + HttpContext); + } +} diff --git a/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Components/Account/Pages/Manage/Email.razor b/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Components/Account/Pages/Manage/Email.razor new file mode 100644 index 0000000000..89960b5d6c --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Components/Account/Pages/Manage/Email.razor @@ -0,0 +1,135 @@ +@page "/Account/Manage/Email" + +@using System.ComponentModel.DataAnnotations +@using System.Text +@using System.Text.Encodings.Web +@using Microsoft.AspNetCore.Identity +@using Microsoft.AspNetCore.WebUtilities +@using TemplateWithPasskey.Data + +@inject UserManager UserManager +@inject IEmailSender EmailSender +@inject NavigationManager NavigationManager +@inject IdentityRedirectManager RedirectManager + +Manage email + +

Manage email

+ + + + +
+ + + + + + @if (isEmailConfirmed) + { + + + + } + else + { + + Send verification email + } + + +

+ Change email +

+
+
+
+ +@code { + private string? message; + private ApplicationUser? user; + private string? email; + private bool isEmailConfirmed; + + [CascadingParameter] + private HttpContext HttpContext { get; set; } = default!; + + [SupplyParameterFromForm(FormName = "change-email")] + private InputModel Input { get; set; } = default!; + + protected override async Task OnInitializedAsync() + { + Input ??= new(); + + user = await UserManager.GetUserAsync(HttpContext.User); + if (user is null) + { + RedirectManager.RedirectToInvalidUser(UserManager, HttpContext); + return; + } + + email = await UserManager.GetEmailAsync(user); + isEmailConfirmed = await UserManager.IsEmailConfirmedAsync(user); + + Input.NewEmail ??= email; + } + + private async Task OnValidSubmitAsync() + { + if (Input.NewEmail is null || Input.NewEmail == email) + { + message = "Your email is unchanged."; + return; + } + + if (user is null) + { + RedirectManager.RedirectToInvalidUser(UserManager, HttpContext); + return; + } + + var userId = await UserManager.GetUserIdAsync(user); + var code = await UserManager.GenerateChangeEmailTokenAsync(user, Input.NewEmail); + code = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(code)); + var callbackUrl = NavigationManager.GetUriWithQueryParameters( + NavigationManager.ToAbsoluteUri("Account/ConfirmEmailChange").AbsoluteUri, + new Dictionary { ["userId"] = userId, ["email"] = Input.NewEmail, ["code"] = code }); + + await EmailSender.SendConfirmationLinkAsync(user, Input.NewEmail, HtmlEncoder.Default.Encode(callbackUrl)); + + message = "Confirmation link to change email sent. Please check your email."; + } + + private async Task OnSendEmailVerificationAsync() + { + if (email is null) + { + return; + } + + if (user is null) + { + RedirectManager.RedirectToInvalidUser(UserManager, HttpContext); + return; + } + + var userId = await UserManager.GetUserIdAsync(user); + var code = await UserManager.GenerateEmailConfirmationTokenAsync(user); + code = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(code)); + var callbackUrl = NavigationManager.GetUriWithQueryParameters( + NavigationManager.ToAbsoluteUri("Account/ConfirmEmail").AbsoluteUri, + new Dictionary { ["userId"] = userId, ["code"] = code }); + + await EmailSender.SendConfirmationLinkAsync(user, email, HtmlEncoder.Default.Encode(callbackUrl)); + + message = "Verification email sent. Please check your email."; + } + + private sealed class InputModel + { + [Required] + [EmailAddress] + [Display(Name = "New email")] + public string? NewEmail { get; set; } + } +} diff --git a/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Components/Account/Pages/Manage/EnableAuthenticator.razor b/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Components/Account/Pages/Manage/EnableAuthenticator.razor new file mode 100644 index 0000000000..26c8f5ddd7 --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Components/Account/Pages/Manage/EnableAuthenticator.razor @@ -0,0 +1,182 @@ +@page "/Account/Manage/EnableAuthenticator" + +@using System.ComponentModel.DataAnnotations +@using System.Globalization +@using System.Text +@using System.Text.Encodings.Web +@using Microsoft.AspNetCore.Identity +@using BlazorWeb_CSharp.Data + +@inject UserManager UserManager +@inject UrlEncoder UrlEncoder +@inject IdentityRedirectManager RedirectManager +@inject ILogger Logger + +Configure authenticator app + +@if (recoveryCodes is not null) +{ + +} +else +{ + +

Configure authenticator app

+ + +

To use an authenticator app go through the following steps:

+
    +
  1. +

    + Download a two-factor authenticator app like Microsoft Authenticator for + Android and + iOS or + Google Authenticator for + Android and + iOS. +

    +
  2. +
  3. +

    Scan the QR Code or enter this key @sharedKey into your two factor authenticator app. Spaces and casing do not matter.

    + +
    +
    +
  4. +
  5. +

    + Once you have scanned the QR code or input the key above, your two factor authentication app will provide you + with a unique code. Enter the code in the confirmation box below. +

    + + + + + +

    + Verify +

    + +
    +
  6. +
+
+
+} + +@code { + private const string AuthenticatorUriFormat = "otpauth://totp/{0}:{1}?secret={2}&issuer={0}&digits=6"; + + private string? message; + private ApplicationUser? user; + private string? sharedKey; + private string? authenticatorUri; + private IEnumerable? recoveryCodes; + + [CascadingParameter] + private HttpContext HttpContext { get; set; } = default!; + + [SupplyParameterFromForm] + private InputModel Input { get; set; } = default!; + + protected override async Task OnInitializedAsync() + { + Input ??= new(); + + user = await UserManager.GetUserAsync(HttpContext.User); + if (user is null) + { + RedirectManager.RedirectToInvalidUser(UserManager, HttpContext); + return; + } + + await LoadSharedKeyAndQrCodeUriAsync(user); + } + + private async Task OnValidSubmitAsync() + { + if (user is null) + { + RedirectManager.RedirectToInvalidUser(UserManager, HttpContext); + return; + } + + // Strip spaces and hyphens + var verificationCode = Input.Code.Replace(" ", string.Empty).Replace("-", string.Empty); + + var is2faTokenValid = await UserManager.VerifyTwoFactorTokenAsync( + user, UserManager.Options.Tokens.AuthenticatorTokenProvider, verificationCode); + + if (!is2faTokenValid) + { + message = "Error: Verification code is invalid."; + return; + } + + await UserManager.SetTwoFactorEnabledAsync(user, true); + var userId = await UserManager.GetUserIdAsync(user); + Logger.LogInformation("User with ID '{UserId}' has enabled 2FA with an authenticator app.", userId); + + message = "Your authenticator app has been verified."; + + if (await UserManager.CountRecoveryCodesAsync(user) == 0) + { + recoveryCodes = await UserManager.GenerateNewTwoFactorRecoveryCodesAsync(user, 10); + } + else + { + RedirectManager.RedirectToWithStatus("Account/Manage/TwoFactorAuthentication", message, HttpContext); + } + } + + private async ValueTask LoadSharedKeyAndQrCodeUriAsync(ApplicationUser user) + { + // Load the authenticator key & QR code URI to display on the form + var unformattedKey = await UserManager.GetAuthenticatorKeyAsync(user); + if (string.IsNullOrEmpty(unformattedKey)) + { + await UserManager.ResetAuthenticatorKeyAsync(user); + unformattedKey = await UserManager.GetAuthenticatorKeyAsync(user); + } + + sharedKey = FormatKey(unformattedKey!); + + var email = await UserManager.GetEmailAsync(user); + authenticatorUri = GenerateQrCodeUri(email!, unformattedKey!); + } + + private string FormatKey(string unformattedKey) + { + var result = new StringBuilder(); + int currentPosition = 0; + while (currentPosition + 4 < unformattedKey.Length) + { + result.Append(unformattedKey.AsSpan(currentPosition, 4)).Append(' '); + currentPosition += 4; + } + if (currentPosition < unformattedKey.Length) + { + result.Append(unformattedKey.AsSpan(currentPosition)); + } + + return result.ToString().ToLowerInvariant(); + } + + private string GenerateQrCodeUri(string email, string unformattedKey) + { + return string.Format( + CultureInfo.InvariantCulture, + AuthenticatorUriFormat, + UrlEncoder.Encode("Microsoft.AspNetCore.Identity.UI"), + UrlEncoder.Encode(email), + unformattedKey); + } + + private sealed class InputModel + { + [Required] + [StringLength(7, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 6)] + [DataType(DataType.Text)] + [Display(Name = "Verification Code")] + public string Code { get; set; } = ""; + } +} diff --git a/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Components/Account/Pages/Manage/ExternalLogins.razor b/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Components/Account/Pages/Manage/ExternalLogins.razor new file mode 100644 index 0000000000..8ec5d4c6e2 --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Components/Account/Pages/Manage/ExternalLogins.razor @@ -0,0 +1,162 @@ +@page "/Account/Manage/ExternalLogins" + +@using Microsoft.AspNetCore.Authentication +@using Microsoft.AspNetCore.Identity +@using BlazorWeb_CSharp.Data + +@inject UserManager UserManager +@inject SignInManager SignInManager +@inject IUserStore UserStore +@inject IdentityRedirectManager RedirectManager + +Manage your external logins + + +@if (currentLogins?.Count > 0) +{ +

Registered Logins

+ + + @foreach (var login in currentLogins) + { + + + + + } + +
@login.ProviderDisplayName + @if (showRemoveButton) + { +
+ +
+ + + Remove +
+ + } + else + { + @:   + } +
+} +@if (otherLogins?.Count > 0) +{ +

Add another service to log in.

+
+
+ +
+

+ @foreach (var provider in otherLogins) + { + + @provider.DisplayName + + } +

+
+ +} + +@code { + public const string LinkLoginCallbackAction = "LinkLoginCallback"; + + private ApplicationUser? user; + private IList? currentLogins; + private IList? otherLogins; + private bool showRemoveButton; + + [CascadingParameter] + private HttpContext HttpContext { get; set; } = default!; + + [SupplyParameterFromForm] + private string? LoginProvider { get; set; } + + [SupplyParameterFromForm] + private string? ProviderKey { get; set; } + + [SupplyParameterFromQuery] + private string? Action { get; set; } + + protected override async Task OnInitializedAsync() + { + user = await UserManager.GetUserAsync(HttpContext.User); + if (user is null) + { + RedirectManager.RedirectToInvalidUser(UserManager, HttpContext); + return; + } + + currentLogins = await UserManager.GetLoginsAsync(user); + otherLogins = (await SignInManager.GetExternalAuthenticationSchemesAsync()) + .Where(auth => currentLogins.All(ul => auth.Name != ul.LoginProvider)) + .ToList(); + + string? passwordHash = null; + if (UserStore is IUserPasswordStore userPasswordStore) + { + passwordHash = await userPasswordStore.GetPasswordHashAsync(user, HttpContext.RequestAborted); + } + + showRemoveButton = passwordHash is not null || currentLogins.Count > 1; + + if (HttpMethods.IsGet(HttpContext.Request.Method) && Action == LinkLoginCallbackAction) + { + await OnGetLinkLoginCallbackAsync(); + } + } + + private async Task OnSubmitAsync() + { + if (user is null) + { + RedirectManager.RedirectToInvalidUser(UserManager, HttpContext); + return; + } + + var result = await UserManager.RemoveLoginAsync(user, LoginProvider!, ProviderKey!); + if (!result.Succeeded) + { + RedirectManager.RedirectToCurrentPageWithStatus("Error: The external login was not removed.", HttpContext); + } + else + { + await SignInManager.RefreshSignInAsync(user); + RedirectManager.RedirectToCurrentPageWithStatus("The external login was removed.", HttpContext); + } + } + + private async Task OnGetLinkLoginCallbackAsync() + { + if (user is null) + { + RedirectManager.RedirectToInvalidUser(UserManager, HttpContext); + return; + } + + var userId = await UserManager.GetUserIdAsync(user); + var info = await SignInManager.GetExternalLoginInfoAsync(userId); + if (info is null) + { + RedirectManager.RedirectToCurrentPageWithStatus("Error: Could not load external login info.", HttpContext); + return; + } + + var result = await UserManager.AddLoginAsync(user, info); + if (result.Succeeded) + { + // Clear the existing external cookie to ensure a clean login process + await HttpContext.SignOutAsync(IdentityConstants.ExternalScheme); + + RedirectManager.RedirectToCurrentPageWithStatus("The external login was added.", HttpContext); + } + else + { + RedirectManager.RedirectToCurrentPageWithStatus("Error: The external login was not added. External logins can only be associated with one account.", HttpContext); + } + } +} diff --git a/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Components/Account/Pages/Manage/GenerateRecoveryCodes.razor b/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Components/Account/Pages/Manage/GenerateRecoveryCodes.razor new file mode 100644 index 0000000000..904f049cdc --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Components/Account/Pages/Manage/GenerateRecoveryCodes.razor @@ -0,0 +1,80 @@ +@page "/Account/Manage/GenerateRecoveryCodes" + +@using Microsoft.AspNetCore.Identity +@using BlazorWeb_CSharp.Data + +@inject UserManager UserManager +@inject IdentityRedirectManager RedirectManager +@inject ILogger Logger + +Generate two-factor authentication (2FA) recovery codes + +@if (recoveryCodes is not null) +{ + +} +else +{ +

Generate two-factor authentication (2FA) recovery codes

+ +
+
+ +

+ Generate Recovery Codes +

+ +
+} + +@code { + private string? message; + private ApplicationUser? user; + private IEnumerable? recoveryCodes; + + [CascadingParameter] + private HttpContext HttpContext { get; set; } = default!; + + protected override async Task OnInitializedAsync() + { + user = await UserManager.GetUserAsync(HttpContext.User); + if (user is null) + { + RedirectManager.RedirectToInvalidUser(UserManager, HttpContext); + return; + } + + var isTwoFactorEnabled = await UserManager.GetTwoFactorEnabledAsync(user); + if (!isTwoFactorEnabled) + { + throw new InvalidOperationException("Cannot generate recovery codes for user because they do not have 2FA enabled."); + } + } + + private async Task OnSubmitAsync() + { + if (user is null) + { + RedirectManager.RedirectToInvalidUser(UserManager, HttpContext); + return; + } + + var userId = await UserManager.GetUserIdAsync(user); + recoveryCodes = await UserManager.GenerateNewTwoFactorRecoveryCodesAsync(user, 10); + message = "You have generated new recovery codes."; + + Logger.LogInformation("User with ID '{UserId}' has generated new 2FA recovery codes.", userId); + } +} diff --git a/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Components/Account/Pages/Manage/Index.razor b/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Components/Account/Pages/Manage/Index.razor new file mode 100644 index 0000000000..3b2afa492a --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Components/Account/Pages/Manage/Index.razor @@ -0,0 +1,87 @@ +@page "/Account/Manage" + +@using System.ComponentModel.DataAnnotations +@using Microsoft.AspNetCore.Identity +@using TemplateWithPasskey.Data + +@inject UserManager UserManager +@inject SignInManager SignInManager +@inject IdentityRedirectManager RedirectManager + +Profile + +

Profile

+ + + + + + + + + + +

+ Save +

+
+
+
+ +@code { + private ApplicationUser? user; + private string? username; + private string? phoneNumber; + + [CascadingParameter] + private HttpContext HttpContext { get; set; } = default!; + + [SupplyParameterFromForm] + private InputModel Input { get; set; } = default!; + + protected override async Task OnInitializedAsync() + { + Input ??= new(); + + user = await UserManager.GetUserAsync(HttpContext.User); + if (user is null) + { + RedirectManager.RedirectToInvalidUser(UserManager, HttpContext); + return; + } + + username = await UserManager.GetUserNameAsync(user); + phoneNumber = await UserManager.GetPhoneNumberAsync(user); + + Input.PhoneNumber ??= phoneNumber; + } + + private async Task OnValidSubmitAsync() + { + if (user is null) + { + RedirectManager.RedirectToInvalidUser(UserManager, HttpContext); + return; + } + + if (Input.PhoneNumber != phoneNumber) + { + var setPhoneResult = await UserManager.SetPhoneNumberAsync(user, Input.PhoneNumber); + if (!setPhoneResult.Succeeded) + { + RedirectManager.RedirectToCurrentPageWithStatus("Error: Failed to set phone number.", HttpContext); + return; + } + } + + await SignInManager.RefreshSignInAsync(user); + RedirectManager.RedirectToCurrentPageWithStatus("Your profile has been updated", HttpContext); + } + + private sealed class InputModel + { + [Phone] + [Display(Name = "Phone number")] + public string? PhoneNumber { get; set; } + } +} diff --git a/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Components/Account/Pages/Manage/Passkeys.razor b/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Components/Account/Pages/Manage/Passkeys.razor new file mode 100644 index 0000000000..cd3893cd7f --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Components/Account/Pages/Manage/Passkeys.razor @@ -0,0 +1,191 @@ +@page "/Account/Manage/Passkeys" + +@using TemplateWithPasskey.Data +@using Microsoft.AspNetCore.Identity +@using System.ComponentModel.DataAnnotations +@using System.Buffers.Text + +@inject UserManager UserManager +@inject SignInManager SignInManager +@inject IdentityRedirectManager RedirectManager + +Manage your passkeys + +

Manage your passkeys

+ + + + + + + @if (currentPasskeys is { Count: > 0 }) + { + + + + + + + + + @foreach (var passkey in currentPasskeys) + { + + + + + } + +
Name
@(passkey.Name ?? "Unnamed passkey") + @{ + var credentialId = Base64Url.EncodeToString(passkey.CredentialId); + } +
+ +
+ + Rename +
+ +
+ +
+ + Delete +
+ +
+ } + else + { +

No passkeys are registered.

+ } +
+ +

+ Add a new passkey +

+ +
+
+ +@code { + private ApplicationUser? user; + private IList? currentPasskeys; + + [CascadingParameter] + private HttpContext HttpContext { get; set; } = default!; + + [SupplyParameterFromForm] + private string? Action { get; set; } + + [SupplyParameterFromForm] + private string? CredentialId { get; set; } + + [SupplyParameterFromForm(FormName = "add-passkey")] + private PasskeyInputModel Input { get; set; } = default!; + + protected override async Task OnInitializedAsync() + { + Input ??= new(); + + user = await UserManager.GetUserAsync(HttpContext.User); + if (user is null) + { + RedirectManager.RedirectToInvalidUser(UserManager, HttpContext); + return; + } + currentPasskeys = await UserManager.GetPasskeysAsync(user); + } + + private async Task AddPasskey() + { + if (user is null) + { + RedirectManager.RedirectToInvalidUser(UserManager, HttpContext); + return; + } + + if (!string.IsNullOrEmpty(Input.Error)) + { + RedirectManager.RedirectToCurrentPageWithStatus($"Error: {Input.Error}", HttpContext); + return; + } + + if (string.IsNullOrEmpty(Input.CredentialJson)) + { + RedirectManager.RedirectToCurrentPageWithStatus("Error: The browser did not provide a passkey.", HttpContext); + return; + } + + var options = await SignInManager.RetrievePasskeyCreationOptionsAsync(); + if (options is null) + { + RedirectManager.RedirectToCurrentPageWithStatus("Error: Could not retrieve passkey creation options.", HttpContext); + return; + } + + var attestationResult = await SignInManager.PerformPasskeyAttestationAsync(Input.CredentialJson, options); + if (!attestationResult.Succeeded) + { + RedirectManager.RedirectToCurrentPageWithStatus($"Error: Could not add the passkey: {attestationResult.Failure.Message}", HttpContext); + return; + } + + var setPasskeyResult = await UserManager.SetPasskeyAsync(user, attestationResult.Passkey); + if (!setPasskeyResult.Succeeded) + { + RedirectManager.RedirectToCurrentPageWithStatus("Error: The passkey could not be added to your account.", HttpContext); + return; + } + + // Immediately prompt the user to enter a name for the credential + var credentialIdBase64Url = Base64Url.EncodeToString(attestationResult.Passkey.CredentialId); + RedirectManager.RedirectTo($"Account/Manage/RenamePasskey/{credentialIdBase64Url}"); + } + + private async Task UpdatePasskey() + { + switch (Action) + { + case "rename": + RedirectManager.RedirectTo($"Account/Manage/RenamePasskey/{CredentialId}"); + break; + case "delete": + await DeletePasskey(); + break; + default: + RedirectManager.RedirectToCurrentPageWithStatus($"Error: Unknown action '{Action}'.", HttpContext); + break; + } + } + + private async Task DeletePasskey() + { + if (user is null) + { + RedirectManager.RedirectToInvalidUser(UserManager, HttpContext); + return; + } + + byte[] credentialId; + try + { + credentialId = Base64Url.DecodeFromChars(CredentialId); + } + catch (FormatException) + { + RedirectManager.RedirectToCurrentPageWithStatus("Error: The specified passkey ID had an invalid format.", HttpContext); + return; + } + + var result = await UserManager.RemovePasskeyAsync(user, credentialId); + if (!result.Succeeded) + { + RedirectManager.RedirectToCurrentPageWithStatus("Error: The passkey could not be deleted.", HttpContext); + return; + } + + RedirectManager.RedirectToCurrentPageWithStatus("Passkey deleted successfully.", HttpContext); + } +} diff --git a/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Components/Account/Pages/Manage/PersonalData.razor b/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Components/Account/Pages/Manage/PersonalData.razor new file mode 100644 index 0000000000..a77cc5e69e --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Components/Account/Pages/Manage/PersonalData.razor @@ -0,0 +1,45 @@ +@page "/Account/Manage/PersonalData" + +@using Microsoft.AspNetCore.Identity +@using TemplateWithPasskey.Data + +@inject UserManager UserManager +@inject IdentityRedirectManager RedirectManager + +Personal Data + + +

Personal Data

+ + + +

Your account contains personal data that you have given us. This page allows you to download or delete that data.

+

+ Deleting this data will permanently remove your account, and this cannot be recovered. +

+
+ + Download + + +
+ +

+ Delete your personal data +

+
+
+ +@code { + [CascadingParameter] + private HttpContext HttpContext { get; set; } = default!; + + protected override async Task OnInitializedAsync() + { + var user = await UserManager.GetUserAsync(HttpContext.User); + if (user is null) + { + RedirectManager.RedirectToInvalidUser(UserManager, HttpContext); + } + } +} diff --git a/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Components/Account/Pages/Manage/RenamePasskey.razor b/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Components/Account/Pages/Manage/RenamePasskey.razor new file mode 100644 index 0000000000..fef70ae37c --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Components/Account/Pages/Manage/RenamePasskey.razor @@ -0,0 +1,97 @@ +@page "/Account/Manage/RenamePasskey/{Id}" + +@using TemplateWithPasskey.Data +@using System.ComponentModel.DataAnnotations +@using Microsoft.AspNetCore.Identity +@using System.Buffers.Text + +@inject UserManager UserManager +@inject IdentityRedirectManager RedirectManager + + + + + + @if (passkey?.Name is { } name) + { +

Enter a new name for your "@name" passkey

+ } + else + { +

Enter a name for your passkey

+ } +
+ + + Passkey name + + +

+ Continue +

+
+
+
+ +@code { + private ApplicationUser? user; + private UserPasskeyInfo? passkey; + + [CascadingParameter] + private HttpContext HttpContext { get; set; } = default!; + + [Parameter] + public string? Id { get; set; } + + [SupplyParameterFromForm] + private InputModel Input { get; set; } = default!; + + protected override async Task OnInitializedAsync() + { + Input ??= new(); + + user = (await UserManager.GetUserAsync(HttpContext.User))!; + if (user is null) + { + RedirectManager.RedirectToInvalidUser(UserManager, HttpContext); + return; + } + + byte[] credentialId; + try + { + credentialId = Base64Url.DecodeFromChars(Id); + } + catch (FormatException) + { + RedirectManager.RedirectToWithStatus("Account/Manage/Passkeys", "Error: The specified passkey ID had an invalid format.", HttpContext); + return; + } + + passkey = await UserManager.GetPasskeyAsync(user, credentialId); + if (passkey is null) + { + RedirectManager.RedirectToWithStatus("Account/Manage/Passkeys", "Error: The specified passkey could not be found.", HttpContext); + return; + } + } + + private async Task Rename() + { + passkey!.Name = Input.Name; + var result = await UserManager.SetPasskeyAsync(user!, passkey); + if (!result.Succeeded) + { + RedirectManager.RedirectToWithStatus("Account/Manage/Passkeys", "Error: The passkey could not be updated.", HttpContext); + return; + } + + RedirectManager.RedirectToWithStatus("Account/Manage/Passkeys", "Passkey updated successfully.", HttpContext); + } + + private sealed class InputModel + { + [Required] + public string Name { get; set; } = ""; + } +} diff --git a/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Components/Account/Pages/Manage/ResetAuthenticator.razor b/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Components/Account/Pages/Manage/ResetAuthenticator.razor new file mode 100644 index 0000000000..0c306a7d65 --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Components/Account/Pages/Manage/ResetAuthenticator.razor @@ -0,0 +1,59 @@ +@page "/Account/Manage/ResetAuthenticator" + +@using Microsoft.AspNetCore.Identity +@using BlazorWeb_CSharp.Data + +@inject UserManager UserManager +@inject SignInManager SignInManager +@inject IdentityRedirectManager RedirectManager +@inject ILogger Logger + +Reset authenticator key + + +

Reset authenticator key

+ +
+
+ +

+ Reset authenticator key +

+ +
+ +@code { + [CascadingParameter] + private HttpContext HttpContext { get; set; } = default!; + + private async Task OnSubmitAsync() + { + var user = await UserManager.GetUserAsync(HttpContext.User); + if (user is null) + { + RedirectManager.RedirectToInvalidUser(UserManager, HttpContext); + return; + } + + await UserManager.SetTwoFactorEnabledAsync(user, false); + await UserManager.ResetAuthenticatorKeyAsync(user); + var userId = await UserManager.GetUserIdAsync(user); + Logger.LogInformation("User with ID '{UserId}' has reset their authentication app key.", userId); + + await SignInManager.RefreshSignInAsync(user); + + RedirectManager.RedirectToWithStatus( + "Account/Manage/EnableAuthenticator", + "Your authenticator app key has been reset, you will need to configure your authenticator app using the new key.", + HttpContext); + } +} diff --git a/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Components/Account/Pages/Manage/SetPassword.razor b/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Components/Account/Pages/Manage/SetPassword.razor new file mode 100644 index 0000000000..db0c616c80 --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Components/Account/Pages/Manage/SetPassword.razor @@ -0,0 +1,93 @@ +@page "/Account/Manage/SetPassword" + +@using System.ComponentModel.DataAnnotations +@using Microsoft.AspNetCore.Identity +@using TemplateWithPasskey.Data + +@inject UserManager UserManager +@inject SignInManager SignInManager +@inject IdentityRedirectManager RedirectManager + +Set password + +

Set your password

+ +

+ You do not have a local username/password for this site. Add a local + account so you can log in without an external login. +

+ + + + + + + + + + Set password + + + + +@code { + private string? message; + private ApplicationUser? user; + + [CascadingParameter] + private HttpContext HttpContext { get; set; } = default!; + + [SupplyParameterFromForm] + private InputModel Input { get; set; } = default!; + + protected override async Task OnInitializedAsync() + { + Input ??= new(); + + user = await UserManager.GetUserAsync(HttpContext.User); + if (user is null) + { + RedirectManager.RedirectToInvalidUser(UserManager, HttpContext); + return; + } + + var hasPassword = await UserManager.HasPasswordAsync(user); + if (hasPassword) + { + RedirectManager.RedirectTo("Account/Manage/ChangePassword"); + } + } + + private async Task OnValidSubmitAsync() + { + if (user is null) + { + RedirectManager.RedirectToInvalidUser(UserManager, HttpContext); + return; + } + + var addPasswordResult = await UserManager.AddPasswordAsync(user, Input.NewPassword!); + if (!addPasswordResult.Succeeded) + { + message = $"Error: {string.Join(",", addPasswordResult.Errors.Select(error => error.Description))}"; + return; + } + + await SignInManager.RefreshSignInAsync(user); + RedirectManager.RedirectToCurrentPageWithStatus("Your password has been set.", HttpContext); + } + + private sealed class InputModel + { + [Required] + [StringLength(100, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 6)] + [DataType(DataType.Password)] + [Display(Name = "New password")] + public string? NewPassword { get; set; } + + [DataType(DataType.Password)] + [Display(Name = "Confirm new password")] + [Compare("NewPassword", ErrorMessage = "The new password and confirmation password do not match.")] + public string? ConfirmPassword { get; set; } + } +} diff --git a/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Components/Account/Pages/Manage/TwoFactorAuthentication.razor b/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Components/Account/Pages/Manage/TwoFactorAuthentication.razor new file mode 100644 index 0000000000..964879329a --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Components/Account/Pages/Manage/TwoFactorAuthentication.razor @@ -0,0 +1,106 @@ +@page "/Account/Manage/TwoFactorAuthentication" + +@using Microsoft.AspNetCore.Http.Features +@using Microsoft.AspNetCore.Identity +@using TemplateWithPasskey.Data + +@inject UserManager UserManager +@inject SignInManager SignInManager +@inject IdentityRedirectManager RedirectManager + +Two-factor authentication (2FA) + + +

Two-factor authentication (2FA)

+@if (canTrack) +{ + if (is2faEnabled) + { + if (recoveryCodesLeft == 0) + { +
+ You have no recovery codes left. +

You must generate a new set of recovery codes before you can log in with a recovery code.

+
+ } + else if (recoveryCodesLeft == 1) + { +
+ You have 1 recovery code left. +

You can generate a new set of recovery codes.

+
+ } + else if (recoveryCodesLeft <= 3) + { +
+ You have @recoveryCodesLeft recovery codes left. +

You should generate a new set of recovery codes.

+
+ } + + if (isMachineRemembered) + { +
+ + Forget this browser + + } + + Disable 2FA + Reset recovery codes + } + +

Authenticator app

+ @if (!hasAuthenticator) + { + Add authenticator app + } + else + { + Set up authenticator app + Reset authenticator app + } +} +else +{ +
+ Privacy and cookie policy have not been accepted. +

You must accept the policy before you can enable two factor authentication.

+
+} + +@code { + private bool canTrack; + private bool hasAuthenticator; + private int recoveryCodesLeft; + private bool is2faEnabled; + private bool isMachineRemembered; + + [CascadingParameter] + private HttpContext HttpContext { get; set; } = default!; + + protected override async Task OnInitializedAsync() + { + var user = await UserManager.GetUserAsync(HttpContext.User); + if (user is null) + { + RedirectManager.RedirectToInvalidUser(UserManager, HttpContext); + return; + } + + canTrack = HttpContext.Features.Get()?.CanTrack ?? true; + hasAuthenticator = await UserManager.GetAuthenticatorKeyAsync(user) is not null; + is2faEnabled = await UserManager.GetTwoFactorEnabledAsync(user); + isMachineRemembered = await SignInManager.IsTwoFactorClientRememberedAsync(user); + recoveryCodesLeft = await UserManager.CountRecoveryCodesAsync(user); + } + + private async Task OnSubmitForgetBrowserAsync() + { + await SignInManager.ForgetTwoFactorClientAsync(); + + RedirectManager.RedirectToCurrentPageWithStatus( + "The current browser has been forgotten. When you login again from this browser you will be prompted for your 2fa code.", + HttpContext); + } +} diff --git a/src/Templates/templates/blazorweb-csharp/BlazorWeb-CSharp/Components/Account/Pages/Manage/_Imports.razor b/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Components/Account/Pages/Manage/_Imports.razor similarity index 100% rename from src/Templates/templates/blazorweb-csharp/BlazorWeb-CSharp/Components/Account/Pages/Manage/_Imports.razor rename to src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Components/Account/Pages/Manage/_Imports.razor diff --git a/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Components/Account/Pages/Register.razor b/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Components/Account/Pages/Register.razor new file mode 100644 index 0000000000..d444887146 --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Components/Account/Pages/Register.razor @@ -0,0 +1,143 @@ +@page "/Account/Register" + +@using System.ComponentModel.DataAnnotations +@using System.Text +@using System.Text.Encodings.Web +@using Microsoft.AspNetCore.Identity +@using Microsoft.AspNetCore.WebUtilities +@using BlazorWeb_CSharp.Data + +@inject UserManager UserManager +@inject IUserStore UserStore +@inject SignInManager SignInManager +@inject IEmailSender EmailSender +@inject ILogger Logger +@inject NavigationManager NavigationManager +@inject IdentityRedirectManager RedirectManager + +Register + +

Register

+ + + + + + +

Create a new account.

+
+ + + + + + + + + Log in + +
+
+ +

Use another service to log in.

+
+ +
+
+ +@code { + private IEnumerable? identityErrors; + + [SupplyParameterFromForm] + private InputModel Input { get; set; } = default!; + + [SupplyParameterFromQuery] + private string? ReturnUrl { get; set; } + + private string? Message => identityErrors is null ? null : $"Error: {string.Join(", ", identityErrors.Select(error => error.Description))}"; + + protected override void OnInitialized() + { + Input ??= new(); + } + + public async Task RegisterUser(EditContext editContext) + { + var user = CreateUser(); + + await UserStore.SetUserNameAsync(user, Input.Email, CancellationToken.None); + var emailStore = GetEmailStore(); + await emailStore.SetEmailAsync(user, Input.Email, CancellationToken.None); + var result = await UserManager.CreateAsync(user, Input.Password); + + if (!result.Succeeded) + { + identityErrors = result.Errors; + return; + } + + Logger.LogInformation("User created a new account with password."); + + var userId = await UserManager.GetUserIdAsync(user); + var code = await UserManager.GenerateEmailConfirmationTokenAsync(user); + code = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(code)); + var callbackUrl = NavigationManager.GetUriWithQueryParameters( + NavigationManager.ToAbsoluteUri("Account/ConfirmEmail").AbsoluteUri, + new Dictionary { ["userId"] = userId, ["code"] = code, ["returnUrl"] = ReturnUrl }); + + await EmailSender.SendConfirmationLinkAsync(user, Input.Email, HtmlEncoder.Default.Encode(callbackUrl)); + + if (UserManager.Options.SignIn.RequireConfirmedAccount) + { + RedirectManager.RedirectTo( + "Account/RegisterConfirmation", + new() { ["email"] = Input.Email, ["returnUrl"] = ReturnUrl }); + } + else + { + await SignInManager.SignInAsync(user, isPersistent: false); + RedirectManager.RedirectTo(ReturnUrl); + } + } + + private ApplicationUser CreateUser() + { + try + { + return Activator.CreateInstance(); + } + catch + { + throw new InvalidOperationException($"Can't create an instance of '{nameof(ApplicationUser)}'. " + + $"Ensure that '{nameof(ApplicationUser)}' is not an abstract class and has a parameterless constructor."); + } + } + + private IUserEmailStore GetEmailStore() + { + if (!UserManager.SupportsUserEmail) + { + throw new NotSupportedException("The default UI requires a user store with email support."); + } + return (IUserEmailStore)UserStore; + } + + private sealed class InputModel + { + [Required] + [EmailAddress] + [Display(Name = "Email")] + public string Email { get; set; } = ""; + + [Required] + [StringLength(100, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 6)] + [DataType(DataType.Password)] + [Display(Name = "Password")] + public string Password { get; set; } = ""; + + [DataType(DataType.Password)] + [Display(Name = "Confirm password")] + [Compare("Password", ErrorMessage = "The password and confirmation password do not match.")] + public string ConfirmPassword { get; set; } = ""; + } +} diff --git a/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Components/Account/Pages/RegisterConfirmation.razor b/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Components/Account/Pages/RegisterConfirmation.razor new file mode 100644 index 0000000000..52ee959492 --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Components/Account/Pages/RegisterConfirmation.razor @@ -0,0 +1,69 @@ +@page "/Account/RegisterConfirmation" + +@using System.Text +@using Microsoft.AspNetCore.Identity +@using Microsoft.AspNetCore.WebUtilities +@using BlazorWeb_CSharp.Data + +@inject UserManager UserManager +@inject IEmailSender EmailSender +@inject NavigationManager NavigationManager +@inject IdentityRedirectManager RedirectManager + +Register confirmation + +

Register confirmation

+ + + +@if (emailConfirmationLink is not null) +{ +

+ This app does not currently have a real email sender registered, see these docs for how to configure a real email sender. + Normally this would be emailed: Click here to confirm your account +

+} +else +{ +

Please check your email to confirm your account.

+} + +@code { + private string? emailConfirmationLink; + private string? statusMessage; + + [CascadingParameter] + private HttpContext HttpContext { get; set; } = default!; + + [SupplyParameterFromQuery] + private string? Email { get; set; } + + [SupplyParameterFromQuery] + private string? ReturnUrl { get; set; } + + protected override async Task OnInitializedAsync() + { + if (Email is null) + { + RedirectManager.RedirectTo(""); + return; + } + + var user = await UserManager.FindByEmailAsync(Email); + if (user is null) + { + HttpContext.Response.StatusCode = StatusCodes.Status404NotFound; + statusMessage = "Error finding user for unspecified email"; + } + else if (EmailSender is IdentityNoOpEmailSender) + { + // Once you add a real email sender, you should remove this code that lets you confirm the account + var userId = await UserManager.GetUserIdAsync(user); + var code = await UserManager.GenerateEmailConfirmationTokenAsync(user); + code = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(code)); + emailConfirmationLink = NavigationManager.GetUriWithQueryParameters( + NavigationManager.ToAbsoluteUri("Account/ConfirmEmail").AbsoluteUri, + new Dictionary { ["userId"] = userId, ["code"] = code, ["returnUrl"] = ReturnUrl }); + } + } +} diff --git a/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Components/Account/Pages/ResendEmailConfirmation.razor b/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Components/Account/Pages/ResendEmailConfirmation.razor new file mode 100644 index 0000000000..9d4f343e2a --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Components/Account/Pages/ResendEmailConfirmation.razor @@ -0,0 +1,70 @@ +@page "/Account/ResendEmailConfirmation" + +@using System.ComponentModel.DataAnnotations +@using System.Text +@using System.Text.Encodings.Web +@using Microsoft.AspNetCore.Identity +@using Microsoft.AspNetCore.WebUtilities +@using BlazorWeb_CSharp.Data + +@inject UserManager UserManager +@inject IEmailSender EmailSender +@inject NavigationManager NavigationManager +@inject IdentityRedirectManager RedirectManager + +Resend email confirmation + +

Resend email confirmation

+

Enter your email.

+
+ + + + + + + + + Resend + + + + +@code { + private string? message; + + [SupplyParameterFromForm] + private InputModel Input { get; set; } = default!; + + protected override void OnInitialized() + { + Input ??= new(); + } + + private async Task OnValidSubmitAsync() + { + var user = await UserManager.FindByEmailAsync(Input.Email!); + if (user is null) + { + message = "Verification email sent. Please check your email."; + return; + } + + var userId = await UserManager.GetUserIdAsync(user); + var code = await UserManager.GenerateEmailConfirmationTokenAsync(user); + code = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(code)); + var callbackUrl = NavigationManager.GetUriWithQueryParameters( + NavigationManager.ToAbsoluteUri("Account/ConfirmEmail").AbsoluteUri, + new Dictionary { ["userId"] = userId, ["code"] = code }); + await EmailSender.SendConfirmationLinkAsync(user, Input.Email, HtmlEncoder.Default.Encode(callbackUrl)); + + message = "Verification email sent. Please check your email."; + } + + private sealed class InputModel + { + [Required] + [EmailAddress] + public string Email { get; set; } = ""; + } +} diff --git a/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Components/Account/Pages/ResetPassword.razor b/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Components/Account/Pages/ResetPassword.razor new file mode 100644 index 0000000000..7d153ddfdb --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Components/Account/Pages/ResetPassword.razor @@ -0,0 +1,99 @@ +@page "/Account/ResetPassword" + +@using System.ComponentModel.DataAnnotations +@using System.Text +@using Microsoft.AspNetCore.Identity +@using Microsoft.AspNetCore.WebUtilities +@using BlazorWeb_CSharp.Data + +@inject IdentityRedirectManager RedirectManager +@inject UserManager UserManager + +Reset password + +

Reset password

+

Reset your password.

+
+ + + + + + + + + + + + + + + Resend + + + + +@code { + private IEnumerable? identityErrors; + + [SupplyParameterFromForm] + private InputModel Input { get; set; } = default!; + + [SupplyParameterFromQuery] + private string? Code { get; set; } + + private string? Message => identityErrors is null ? null : $"Error: {string.Join(", ", identityErrors.Select(error => error.Description))}"; + + protected override void OnInitialized() + { + Input ??= new(); + + if (Code is null) + { + RedirectManager.RedirectTo("Account/InvalidPasswordReset"); + return; + } + + Input.Code = Encoding.UTF8.GetString(WebEncoders.Base64UrlDecode(Code)); + } + + private async Task OnValidSubmitAsync() + { + var user = await UserManager.FindByEmailAsync(Input.Email); + if (user is null) + { + // Don't reveal that the user does not exist + RedirectManager.RedirectTo("Account/ResetPasswordConfirmation"); + return; + } + + var result = await UserManager.ResetPasswordAsync(user, Input.Code, Input.Password); + if (result.Succeeded) + { + RedirectManager.RedirectTo("Account/ResetPasswordConfirmation"); + return; + } + + identityErrors = result.Errors; + } + + private sealed class InputModel + { + [Required] + [EmailAddress] + public string Email { get; set; } = ""; + + [Required] + [StringLength(100, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 6)] + [DataType(DataType.Password)] + public string Password { get; set; } = ""; + + [DataType(DataType.Password)] + [Display(Name = "Confirm password")] + [Compare("Password", ErrorMessage = "The password and confirmation password do not match.")] + public string ConfirmPassword { get; set; } = ""; + + [Required] + public string Code { get; set; } = ""; + } +} diff --git a/src/Templates/templates/blazorweb-csharp/BlazorWeb-CSharp/Components/Account/Pages/ResetPasswordConfirmation.razor b/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Components/Account/Pages/ResetPasswordConfirmation.razor similarity index 100% rename from src/Templates/templates/blazorweb-csharp/BlazorWeb-CSharp/Components/Account/Pages/ResetPasswordConfirmation.razor rename to src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Components/Account/Pages/ResetPasswordConfirmation.razor diff --git a/src/Templates/templates/blazorweb-csharp/BlazorWeb-CSharp/Components/Account/Pages/_Imports.razor b/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Components/Account/Pages/_Imports.razor similarity index 100% rename from src/Templates/templates/blazorweb-csharp/BlazorWeb-CSharp/Components/Account/Pages/_Imports.razor rename to src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Components/Account/Pages/_Imports.razor diff --git a/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Components/Account/PasskeyInputModel.cs b/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Components/Account/PasskeyInputModel.cs new file mode 100644 index 0000000000..6a5e0eb993 --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Components/Account/PasskeyInputModel.cs @@ -0,0 +1,7 @@ +namespace BlazorWeb_CSharp.Components.Account; + +public class PasskeyInputModel +{ + public string? CredentialJson { get; set; } + public string? Error { get; set; } +} diff --git a/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Components/Account/PasskeyOperation.cs b/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Components/Account/PasskeyOperation.cs new file mode 100644 index 0000000000..3b4b291aac --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Components/Account/PasskeyOperation.cs @@ -0,0 +1,7 @@ +namespace BlazorWeb_CSharp.Components.Account; + +public enum PasskeyOperation +{ + Create = 0, + Request = 1, +} diff --git a/src/Templates/templates/blazorweb-csharp/BlazorWeb-CSharp/Components/Account/Shared/ExternalLoginPicker.razor b/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Components/Account/Shared/ExternalLoginPicker.razor similarity index 100% rename from src/Templates/templates/blazorweb-csharp/BlazorWeb-CSharp/Components/Account/Shared/ExternalLoginPicker.razor rename to src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Components/Account/Shared/ExternalLoginPicker.razor diff --git a/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Components/Account/Shared/ManageLayout.razor b/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Components/Account/Shared/ManageLayout.razor new file mode 100644 index 0000000000..4a58c71961 --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Components/Account/Shared/ManageLayout.razor @@ -0,0 +1,22 @@ +@inherits LayoutComponentBase +@*#if (UseWebAssembly && InteractiveAtRoot) +@layout BlazorWeb_CSharp.Client.Layout.MainLayout +##else +@layout BlazorWeb_CSharp.Components.Layout.MainLayout +##endif*@ + +

Manage your account

+ +
+

Change your account settings

+
+ + + + + + + @Body + + +
diff --git a/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Components/Account/Shared/ManageNavMenu.razor b/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Components/Account/Shared/ManageNavMenu.razor new file mode 100644 index 0000000000..a267f7816c --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Components/Account/Shared/ManageNavMenu.razor @@ -0,0 +1,28 @@ +@using Microsoft.AspNetCore.Identity +@using BlazorWeb_CSharp.Data + +@inject SignInManager SignInManager + + + Profile + Email + Password + @if (hasExternalLogins) + { + External logins + } + Two-factor authentication + Passkeys + Personal data + + + + +@code { + private bool hasExternalLogins; + + protected override async Task OnInitializedAsync() + { + hasExternalLogins = (await SignInManager.GetExternalAuthenticationSchemesAsync()).Any(); + } +} diff --git a/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Components/Account/Shared/PasskeySubmit.razor b/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Components/Account/Shared/PasskeySubmit.razor new file mode 100644 index 0000000000..e20206d9c7 --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Components/Account/Shared/PasskeySubmit.razor @@ -0,0 +1,21 @@ +@ChildContent + + +@code { + [Parameter] + [EditorRequired] + public PasskeyOperation Operation { get; set; } + + [Parameter] + [EditorRequired] + public string Name { get; set; } = default!; + + [Parameter] + public string? EmailName { get; set; } + + [Parameter] + public RenderFragment? ChildContent { get; set; } + + [Parameter(CaptureUnmatchedValues = true)] + public IDictionary? AdditionalAttributes { get; set; } +} diff --git a/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Components/Account/Shared/PasskeySubmit.razor.js b/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Components/Account/Shared/PasskeySubmit.razor.js new file mode 100644 index 0000000000..42d5d150aa --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Components/Account/Shared/PasskeySubmit.razor.js @@ -0,0 +1,112 @@ +const browserSupportsPasskeys = + typeof navigator.credentials !== 'undefined' && + typeof window.PublicKeyCredential !== 'undefined' && + typeof window.PublicKeyCredential.parseCreationOptionsFromJSON === 'function' && + typeof window.PublicKeyCredential.parseRequestOptionsFromJSON === 'function'; + +async function fetchWithErrorHandling(url, options = {}) { + const response = await fetch(url, { + credentials: 'include', + ...options + }); + if (!response.ok) { + const text = await response.text(); + console.error(text); + throw new Error(`The server responded with status ${response.status}.`); + } + return response; +} + +async function createCredential(signal) { + const optionsResponse = await fetchWithErrorHandling('/Account/PasskeyCreationOptions', { + method: 'POST', + signal, + }); + const optionsJson = await optionsResponse.json(); + const options = PublicKeyCredential.parseCreationOptionsFromJSON(optionsJson); + return await navigator.credentials.create({ publicKey: options, signal }); +} + +async function requestCredential(email, mediation, signal) { + const optionsResponse = await fetchWithErrorHandling(`/Account/PasskeyRequestOptions?username=${email}`, { + method: 'POST', + signal, + }); + const optionsJson = await optionsResponse.json(); + const options = PublicKeyCredential.parseRequestOptionsFromJSON(optionsJson); + return await navigator.credentials.get({ publicKey: options, mediation, signal }); +} + +customElements.define('passkey-submit', class extends HTMLElement { + static formAssociated = true; + + connectedCallback() { + this.internals = this.attachInternals(); + this.attrs = { + operation: this.getAttribute('operation'), + name: this.getAttribute('name'), + emailName: this.getAttribute('email-name'), + }; + + this.internals.form.addEventListener('submit', (event) => { + if (event.submitter?.name === '__passkeySubmit') { + event.preventDefault(); + this.obtainAndSubmitCredential(); + } + }); + + this.tryAutofillPasskey(); + } + + disconnectedCallback() { + this.abortController?.abort(); + } + + async obtainCredential(useConditionalMediation, signal) { + if (!browserSupportsPasskeys) { + throw new Error('Some passkey features are missing. Please update your browser.'); + } + + if (this.attrs.operation === 'Create') { + return await createCredential(signal); + } else if (this.attrs.operation === 'Request') { + const email = new FormData(this.internals.form).get(this.attrs.emailName); + const mediation = useConditionalMediation ? 'conditional' : undefined; + return await requestCredential(email, mediation, signal); + } else { + throw new Error(`Unknown passkey operation '${this.attrs.operation}'.`); + } + } + + async obtainAndSubmitCredential(useConditionalMediation = false) { + this.abortController?.abort(); + this.abortController = new AbortController(); + const signal = this.abortController.signal; + const formData = new FormData(); + try { + const credential = await this.obtainCredential(useConditionalMediation, signal); + const credentialJson = JSON.stringify(credential); + formData.append(`${this.attrs.name}.CredentialJson`, credentialJson); + } catch (error) { + console.error(error); + if (useConditionalMediation || error.name === 'AbortError') { + // We do not relay the error to the user if: + // 1. We are attempting conditional mediation, meaning the user did not initiate the operation. + // 2. The user explicitly canceled the operation. + return; + } + const errorMessage = error.name === 'NotAllowedError' + ? 'No passkey was provided by the authenticator.' + : error.message; + formData.append(`${this.attrs.name}.Error`, errorMessage); + } + this.internals.setFormValue(formData); + this.internals.form.submit(); + } + + async tryAutofillPasskey() { + if (browserSupportsPasskeys && this.attrs.operation === 'Request' && await PublicKeyCredential.isConditionalMediationAvailable?.()) { + await this.obtainAndSubmitCredential(/* useConditionalMediation */ true); + } + } +}); diff --git a/src/Templates/templates/blazorweb-csharp/BlazorWeb-CSharp/Components/Account/Shared/RedirectToLogin.razor b/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Components/Account/Shared/RedirectToLogin.razor similarity index 100% rename from src/Templates/templates/blazorweb-csharp/BlazorWeb-CSharp/Components/Account/Shared/RedirectToLogin.razor rename to src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Components/Account/Shared/RedirectToLogin.razor diff --git a/src/Templates/templates/blazorweb-csharp/BlazorWeb-CSharp/Components/Account/Shared/ShowRecoveryCodes.razor b/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Components/Account/Shared/ShowRecoveryCodes.razor similarity index 100% rename from src/Templates/templates/blazorweb-csharp/BlazorWeb-CSharp/Components/Account/Shared/ShowRecoveryCodes.razor rename to src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Components/Account/Shared/ShowRecoveryCodes.razor diff --git a/src/Templates/templates/blazorweb-csharp/BlazorWeb-CSharp/Components/Account/Shared/StatusMessage.razor b/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Components/Account/Shared/StatusMessage.razor similarity index 100% rename from src/Templates/templates/blazorweb-csharp/BlazorWeb-CSharp/Components/Account/Shared/StatusMessage.razor rename to src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Components/Account/Shared/StatusMessage.razor diff --git a/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Components/App.razor b/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Components/App.razor new file mode 100644 index 0000000000..c8b5d622b7 --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Components/App.razor @@ -0,0 +1,79 @@ + + + + + + + + + + + + @*#if (SampleContent) + + ##endif*@ + @*#if (!InteractiveAtRoot) + + ##elseif (IndividualLocalAuth) + + ##elseif (UseServer && UseWebAssembly) + + ##elseif (UseServer) + + ##else + + ##endif*@ + + + + @*#if (!InteractiveAtRoot) + + ##elseif (IndividualLocalAuth) + + ##elseif (UseServer && UseWebAssembly) + + ##elseif (UseServer) + + ##else + + ##endif*@ + @*#if (UseServer) --> + + ##endif*@ + + @*#if (IndividualLocalAuth) + + ##endif*@ + + + +@*#if (!InteractiveAtRoot || !IndividualLocalAuth) +#elseif (UseServer && UseWebAssembly) + +@code { + [CascadingParameter] + private HttpContext HttpContext { get; set; } = default!; + + private IComponentRenderMode? PageRenderMode => + HttpContext.AcceptsInteractiveRouting() ? InteractiveAuto : null; +} +#elseif (UseServer) + +@code { + [CascadingParameter] + private HttpContext HttpContext { get; set; } = default!; + + private IComponentRenderMode? PageRenderMode => + HttpContext.AcceptsInteractiveRouting() ? InteractiveServer : null; +} +#else + +@code { + [CascadingParameter] + private HttpContext HttpContext { get; set; } = default!; + + private IComponentRenderMode? PageRenderMode => + HttpContext.AcceptsInteractiveRouting() ? InteractiveWebAssembly : null; +} +#endif*@ + diff --git a/src/Templates/templates/blazorweb-csharp/BlazorWeb-CSharp/Components/Layout/MainLayout.razor b/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Components/Layout/MainLayout.razor similarity index 100% rename from src/Templates/templates/blazorweb-csharp/BlazorWeb-CSharp/Components/Layout/MainLayout.razor rename to src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Components/Layout/MainLayout.razor diff --git a/src/Templates/templates/blazorweb-csharp/BlazorWeb-CSharp/Components/Layout/NavMenu.razor b/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Components/Layout/NavMenu.razor similarity index 100% rename from src/Templates/templates/blazorweb-csharp/BlazorWeb-CSharp/Components/Layout/NavMenu.razor rename to src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Components/Layout/NavMenu.razor diff --git a/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Components/Layout/ReconnectModal.razor b/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Components/Layout/ReconnectModal.razor new file mode 100644 index 0000000000..a85217a113 --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Components/Layout/ReconnectModal.razor @@ -0,0 +1,35 @@ +@*#if (UseWebAssembly && InteractiveAtRoot) --> + +##else + +##endif*@ + + +
+ +

+ Rejoining the server... +

+

+ Rejoin failed... trying again in seconds. +

+

+ Failed to rejoin.
Please retry or reload the page. +

+ +

+ The session has been paused by the server. +

+ +

+ Failed to resume the session.
Please reload the page. +

+
+
diff --git a/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Components/Layout/ReconnectModal.razor.css b/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Components/Layout/ReconnectModal.razor.css new file mode 100644 index 0000000000..3ad3773f39 --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Components/Layout/ReconnectModal.razor.css @@ -0,0 +1,157 @@ +.components-reconnect-first-attempt-visible, +.components-reconnect-repeated-attempt-visible, +.components-reconnect-failed-visible, +.components-pause-visible, +.components-resume-failed-visible, +.components-rejoining-animation { + display: none; +} + +#components-reconnect-modal.components-reconnect-show .components-reconnect-first-attempt-visible, +#components-reconnect-modal.components-reconnect-show .components-rejoining-animation, +#components-reconnect-modal.components-reconnect-paused .components-pause-visible, +#components-reconnect-modal.components-reconnect-resume-failed .components-resume-failed-visible, +#components-reconnect-modal.components-reconnect-retrying, +#components-reconnect-modal.components-reconnect-retrying .components-reconnect-repeated-attempt-visible, +#components-reconnect-modal.components-reconnect-retrying .components-rejoining-animation, +#components-reconnect-modal.components-reconnect-failed, +#components-reconnect-modal.components-reconnect-failed .components-reconnect-failed-visible { + display: block; +} + + +#components-reconnect-modal { + background-color: white; + width: 20rem; + margin: 20vh auto; + padding: 2rem; + border: 0; + border-radius: 0.5rem; + box-shadow: 0 3px 6px 2px rgba(0, 0, 0, 0.3); + opacity: 0; + transition: display 0.5s allow-discrete, overlay 0.5s allow-discrete; + animation: components-reconnect-modal-fadeOutOpacity 0.5s both; + &[open] + +{ + animation: components-reconnect-modal-slideUp 1.5s cubic-bezier(.05, .89, .25, 1.02) 0.3s, components-reconnect-modal-fadeInOpacity 0.5s ease-in-out 0.3s; + animation-fill-mode: both; +} + +} + +#components-reconnect-modal::backdrop { + background-color: rgba(0, 0, 0, 0.4); + animation: components-reconnect-modal-fadeInOpacity 0.5s ease-in-out; + opacity: 1; +} + +@keyframes components-reconnect-modal-slideUp { + 0% { + transform: translateY(30px) scale(0.95); + } + + 100% { + transform: translateY(0); + } +} + +@keyframes components-reconnect-modal-fadeInOpacity { + 0% { + opacity: 0; + } + + 100% { + opacity: 1; + } +} + +@keyframes components-reconnect-modal-fadeOutOpacity { + 0% { + opacity: 1; + } + + 100% { + opacity: 0; + } +} + +.components-reconnect-container { + display: flex; + flex-direction: column; + align-items: center; + gap: 1rem; +} + +#components-reconnect-modal p { + margin: 0; + text-align: center; +} + +#components-reconnect-modal button { + border: 0; + background-color: #6b9ed2; + color: white; + padding: 4px 24px; + border-radius: 4px; +} + + #components-reconnect-modal button:hover { + background-color: #3b6ea2; + } + + #components-reconnect-modal button:active { + background-color: #6b9ed2; + } + +.components-rejoining-animation { + position: relative; + width: 80px; + height: 80px; +} + + .components-rejoining-animation div { + position: absolute; + border: 3px solid #0087ff; + opacity: 1; + border-radius: 50%; + animation: components-rejoining-animation 1.5s cubic-bezier(0, 0.2, 0.8, 1) infinite; + } + + .components-rejoining-animation div:nth-child(2) { + animation-delay: -0.5s; + } + +@keyframes components-rejoining-animation { + 0% { + top: 40px; + left: 40px; + width: 0; + height: 0; + opacity: 0; + } + + 4.9% { + top: 40px; + left: 40px; + width: 0; + height: 0; + opacity: 0; + } + + 5% { + top: 40px; + left: 40px; + width: 0; + height: 0; + opacity: 1; + } + + 100% { + top: 0px; + left: 0px; + width: 80px; + height: 80px; + opacity: 0; + } +} diff --git a/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Components/Layout/ReconnectModal.razor.js b/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Components/Layout/ReconnectModal.razor.js new file mode 100644 index 0000000000..f58438240a --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Components/Layout/ReconnectModal.razor.js @@ -0,0 +1,63 @@ +// Set up event handlers +const reconnectModal = document.getElementById("components-reconnect-modal"); +reconnectModal.addEventListener("components-reconnect-state-changed", handleReconnectStateChanged); + +const retryButton = document.getElementById("components-reconnect-button"); +retryButton.addEventListener("click", retry); + +const resumeButton = document.getElementById("components-resume-button"); +resumeButton.addEventListener("click", resume); + +function handleReconnectStateChanged(event) { + if (event.detail.state === "show") { + reconnectModal.showModal(); + } else if (event.detail.state === "hide") { + reconnectModal.close(); + } else if (event.detail.state === "failed") { + document.addEventListener("visibilitychange", retryWhenDocumentBecomesVisible); + } else if (event.detail.state === "rejected") { + location.reload(); + } +} + +async function retry() { + document.removeEventListener("visibilitychange", retryWhenDocumentBecomesVisible); + + try { + // Reconnect will asynchronously return: + // - true to mean success + // - false to mean we reached the server, but it rejected the connection (e.g., unknown circuit ID) + // - exception to mean we didn't reach the server (this can be sync or async) + const successful = await Blazor.reconnect(); + if (!successful) { + // We have been able to reach the server, but the circuit is no longer available. + // We'll reload the page so the user can continue using the app as quickly as possible. + const resumeSuccessful = await Blazor.resume(); + if (!resumeSuccessful) { + location.reload(); + } else { + reconnectModal.close(); + } + } + } catch (err) { + // We got an exception, server is currently unavailable + document.addEventListener("visibilitychange", retryWhenDocumentBecomesVisible); + } +} + +async function resume() { + try { + const successful = await Blazor.resume(); + if (!successful) { + location.reload(); + } + } catch { + location.reload(); + } +} + +async function retryWhenDocumentBecomesVisible() { + if (document.visibilityState === "visible") { + await retry(); + } +} diff --git a/src/Templates/templates/blazorweb-csharp/BlazorWeb-CSharp/Components/Pages/Auth.razor b/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Components/Pages/Auth.razor similarity index 100% rename from src/Templates/templates/blazorweb-csharp/BlazorWeb-CSharp/Components/Pages/Auth.razor rename to src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Components/Pages/Auth.razor diff --git a/src/Templates/templates/blazorweb-csharp/BlazorWeb-CSharp/Components/Pages/Counter.razor b/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Components/Pages/Counter.razor similarity index 100% rename from src/Templates/templates/blazorweb-csharp/BlazorWeb-CSharp/Components/Pages/Counter.razor rename to src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Components/Pages/Counter.razor diff --git a/src/Templates/templates/blazorweb-csharp/BlazorWeb-CSharp/Components/Pages/Error.razor b/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Components/Pages/Error.razor similarity index 100% rename from src/Templates/templates/blazorweb-csharp/BlazorWeb-CSharp/Components/Pages/Error.razor rename to src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Components/Pages/Error.razor diff --git a/src/Templates/templates/blazorweb-csharp/BlazorWeb-CSharp/Components/Pages/Home.razor b/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Components/Pages/Home.razor similarity index 100% rename from src/Templates/templates/blazorweb-csharp/BlazorWeb-CSharp/Components/Pages/Home.razor rename to src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Components/Pages/Home.razor diff --git a/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Components/Pages/NotFound.razor b/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Components/Pages/NotFound.razor new file mode 100644 index 0000000000..917ada1d23 --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Components/Pages/NotFound.razor @@ -0,0 +1,5 @@ +@page "/not-found" +@layout MainLayout + +

Not Found

+

Sorry, the content you are looking for does not exist.

\ No newline at end of file diff --git a/src/Templates/templates/blazorweb-csharp/BlazorWeb-CSharp/Components/Pages/Weather.razor b/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Components/Pages/Weather.razor similarity index 100% rename from src/Templates/templates/blazorweb-csharp/BlazorWeb-CSharp/Components/Pages/Weather.razor rename to src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Components/Pages/Weather.razor diff --git a/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Components/Routes.razor b/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Components/Routes.razor new file mode 100644 index 0000000000..094f187377 --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Components/Routes.razor @@ -0,0 +1,20 @@ +@*#if (IndividualLocalAuth && !UseWebAssembly) +@using BlazorWeb_CSharp.Components.Account.Shared +##endif*@ +@*#if (UseWebAssembly && !InteractiveAtRoot) + +##else + +##endif*@ + + @*#if (IndividualLocalAuth) + + + + + + ##else + + ##endif*@ + + diff --git a/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Components/_Imports.razor b/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Components/_Imports.razor new file mode 100644 index 0000000000..0913cf3345 --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Components/_Imports.razor @@ -0,0 +1,24 @@ +@using System.Net.Http +@using System.Net.Http.Json +@*#if (IndividualLocalAuth) +@using Microsoft.AspNetCore.Components.Authorization +##endif*@ +@using Microsoft.AspNetCore.Components.Forms +@using Microsoft.AspNetCore.Components.Routing +@using Microsoft.AspNetCore.Components.Web +@using static Microsoft.AspNetCore.Components.Web.RenderMode +@using Microsoft.AspNetCore.Components.Web.Virtualization +@using Microsoft.FluentUI.AspNetCore.Components +@using Icons = Microsoft.FluentUI.AspNetCore.Components.Icons +@using Microsoft.JSInterop +@using BlazorWeb_CSharp +@*#if (UseWebAssembly) --> +@using BlazorWeb_CSharp.Client +##endif*@ +@*#if (UseServer && UseWebAssembly && InteractiveAtRoot) --> +@using BlazorWeb_CSharp.Client.Layout +##endif*@ +@using BlazorWeb_CSharp.Components +@*#if ((UseServer && !UseWebAssembly) || !InteractiveAtRoot) --> +@using BlazorWeb_CSharp.Components.Layout +##endif*@ diff --git a/src/Templates/templates/blazorweb-csharp/BlazorWeb-CSharp/Data/ApplicationDbContext.cs b/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Data/ApplicationDbContext.cs similarity index 100% rename from src/Templates/templates/blazorweb-csharp/BlazorWeb-CSharp/Data/ApplicationDbContext.cs rename to src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Data/ApplicationDbContext.cs diff --git a/src/Templates/templates/blazorweb-csharp/BlazorWeb-CSharp/Data/ApplicationUser.cs b/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Data/ApplicationUser.cs similarity index 100% rename from src/Templates/templates/blazorweb-csharp/BlazorWeb-CSharp/Data/ApplicationUser.cs rename to src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Data/ApplicationUser.cs diff --git a/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Data/SqlLite/00000000000000_CreateIdentitySchema.Designer.cs b/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Data/SqlLite/00000000000000_CreateIdentitySchema.Designer.cs new file mode 100644 index 0000000000..ae1764e8d3 --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Data/SqlLite/00000000000000_CreateIdentitySchema.Designer.cs @@ -0,0 +1,333 @@ +// +using System; +using BlazorWeb_CSharp.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace BlazorWeb_CSharp.Migrations +{ + [DbContext(typeof(ApplicationDbContext))] + [Migration("00000000000000_CreateIdentitySchema")] + partial class CreateIdentitySchema + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder.HasAnnotation("ProductVersion", "10.0.0"); + + modelBuilder.Entity("BlazorWeb_CSharp.Data.ApplicationUser", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("AccessFailedCount") + .HasColumnType("INTEGER"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("TEXT"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("EmailConfirmed") + .HasColumnType("INTEGER"); + + b.Property("LockoutEnabled") + .HasColumnType("INTEGER"); + + b.Property("LockoutEnd") + .HasColumnType("TEXT"); + + b.Property("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("PasswordHash") + .HasColumnType("TEXT"); + + b.Property("PhoneNumber") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("INTEGER"); + + b.Property("SecurityStamp") + .HasColumnType("TEXT"); + + b.Property("TwoFactorEnabled") + .HasColumnType("INTEGER"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex"); + + b.ToTable("AspNetUsers", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("TEXT"); + + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("NormalizedName") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasDatabaseName("RoleNameIndex"); + + b.ToTable("AspNetRoles", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ClaimType") + .HasColumnType("TEXT"); + + b.Property("ClaimValue") + .HasColumnType("TEXT"); + + b.Property("RoleId") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetRoleClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ClaimType") + .HasColumnType("TEXT"); + + b.Property("ClaimValue") + .HasColumnType("TEXT"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasMaxLength(128) + .HasColumnType("TEXT"); + + b.Property("ProviderKey") + .HasMaxLength(128) + .HasColumnType("TEXT"); + + b.Property("ProviderDisplayName") + .HasColumnType("TEXT"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserLogins", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserPasskey", b => + { + b.Property("CredentialId") + .HasMaxLength(1024) + .HasColumnType("BLOB"); + + b.Property("AttestationObject") + .IsRequired() + .HasColumnType("BLOB"); + + b.Property("ClientDataJson") + .IsRequired() + .HasColumnType("BLOB"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("IsBackedUp") + .HasColumnType("INTEGER"); + + b.Property("IsBackupEligible") + .HasColumnType("INTEGER"); + + b.Property("IsUserVerified") + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("PublicKey") + .IsRequired() + .HasMaxLength(1024) + .HasColumnType("BLOB"); + + b.Property("SignCount") + .HasColumnType("INTEGER"); + + b.PrimitiveCollection("Transports") + .HasColumnType("TEXT"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("CredentialId"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserPasskeys", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("TEXT"); + + b.Property("RoleId") + .HasColumnType("TEXT"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetUserRoles", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("TEXT"); + + b.Property("LoginProvider") + .HasMaxLength(128) + .HasColumnType("TEXT"); + + b.Property("Name") + .HasMaxLength(128) + .HasColumnType("TEXT"); + + b.Property("Value") + .HasColumnType("TEXT"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("AspNetUserTokens", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("BlazorWeb_CSharp.Data.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("BlazorWeb_CSharp.Data.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserPasskey", b => + { + b.HasOne("BlazorWeb_CSharp.Data.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("BlazorWeb_CSharp.Data.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("BlazorWeb_CSharp.Data.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Data/SqlLite/00000000000000_CreateIdentitySchema.cs b/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Data/SqlLite/00000000000000_CreateIdentitySchema.cs new file mode 100644 index 0000000000..92e0a0ea2c --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Data/SqlLite/00000000000000_CreateIdentitySchema.cs @@ -0,0 +1,258 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace BlazorWeb_CSharp.Migrations +{ + /// + public partial class CreateIdentitySchema : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "AspNetRoles", + columns: table => new + { + Id = table.Column(type: "TEXT", nullable: false), + Name = table.Column(type: "TEXT", maxLength: 256, nullable: true), + NormalizedName = table.Column(type: "TEXT", maxLength: 256, nullable: true), + ConcurrencyStamp = table.Column(type: "TEXT", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_AspNetRoles", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "AspNetUsers", + columns: table => new + { + Id = table.Column(type: "TEXT", nullable: false), + UserName = table.Column(type: "TEXT", maxLength: 256, nullable: true), + NormalizedUserName = table.Column(type: "TEXT", maxLength: 256, nullable: true), + Email = table.Column(type: "TEXT", maxLength: 256, nullable: true), + NormalizedEmail = table.Column(type: "TEXT", maxLength: 256, nullable: true), + EmailConfirmed = table.Column(type: "INTEGER", nullable: false), + PasswordHash = table.Column(type: "TEXT", nullable: true), + SecurityStamp = table.Column(type: "TEXT", nullable: true), + ConcurrencyStamp = table.Column(type: "TEXT", nullable: true), + PhoneNumber = table.Column(type: "TEXT", maxLength: 256, nullable: true), + PhoneNumberConfirmed = table.Column(type: "INTEGER", nullable: false), + TwoFactorEnabled = table.Column(type: "INTEGER", nullable: false), + LockoutEnd = table.Column(type: "TEXT", nullable: true), + LockoutEnabled = table.Column(type: "INTEGER", nullable: false), + AccessFailedCount = table.Column(type: "INTEGER", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_AspNetUsers", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "AspNetRoleClaims", + columns: table => new + { + Id = table.Column(type: "INTEGER", nullable: false) + .Annotation("Sqlite:Autoincrement", true), + RoleId = table.Column(type: "TEXT", nullable: false), + ClaimType = table.Column(type: "TEXT", nullable: true), + ClaimValue = table.Column(type: "TEXT", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_AspNetRoleClaims", x => x.Id); + table.ForeignKey( + name: "FK_AspNetRoleClaims_AspNetRoles_RoleId", + column: x => x.RoleId, + principalTable: "AspNetRoles", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "AspNetUserClaims", + columns: table => new + { + Id = table.Column(type: "INTEGER", nullable: false) + .Annotation("Sqlite:Autoincrement", true), + UserId = table.Column(type: "TEXT", nullable: false), + ClaimType = table.Column(type: "TEXT", nullable: true), + ClaimValue = table.Column(type: "TEXT", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_AspNetUserClaims", x => x.Id); + table.ForeignKey( + name: "FK_AspNetUserClaims_AspNetUsers_UserId", + column: x => x.UserId, + principalTable: "AspNetUsers", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "AspNetUserLogins", + columns: table => new + { + LoginProvider = table.Column(type: "TEXT", maxLength: 128, nullable: false), + ProviderKey = table.Column(type: "TEXT", maxLength: 128, nullable: false), + ProviderDisplayName = table.Column(type: "TEXT", nullable: true), + UserId = table.Column(type: "TEXT", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_AspNetUserLogins", x => new { x.LoginProvider, x.ProviderKey }); + table.ForeignKey( + name: "FK_AspNetUserLogins_AspNetUsers_UserId", + column: x => x.UserId, + principalTable: "AspNetUsers", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "AspNetUserPasskeys", + columns: table => new + { + CredentialId = table.Column(type: "BLOB", maxLength: 1024, nullable: false), + UserId = table.Column(type: "TEXT", nullable: false), + PublicKey = table.Column(type: "BLOB", maxLength: 1024, nullable: false), + Name = table.Column(type: "TEXT", nullable: true), + CreatedAt = table.Column(type: "TEXT", nullable: false), + SignCount = table.Column(type: "INTEGER", nullable: false), + Transports = table.Column(type: "TEXT", nullable: true), + IsUserVerified = table.Column(type: "INTEGER", nullable: false), + IsBackupEligible = table.Column(type: "INTEGER", nullable: false), + IsBackedUp = table.Column(type: "INTEGER", nullable: false), + AttestationObject = table.Column(type: "BLOB", nullable: false), + ClientDataJson = table.Column(type: "BLOB", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_AspNetUserPasskeys", x => x.CredentialId); + table.ForeignKey( + name: "FK_AspNetUserPasskeys_AspNetUsers_UserId", + column: x => x.UserId, + principalTable: "AspNetUsers", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "AspNetUserRoles", + columns: table => new + { + UserId = table.Column(type: "TEXT", nullable: false), + RoleId = table.Column(type: "TEXT", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_AspNetUserRoles", x => new { x.UserId, x.RoleId }); + table.ForeignKey( + name: "FK_AspNetUserRoles_AspNetRoles_RoleId", + column: x => x.RoleId, + principalTable: "AspNetRoles", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_AspNetUserRoles_AspNetUsers_UserId", + column: x => x.UserId, + principalTable: "AspNetUsers", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "AspNetUserTokens", + columns: table => new + { + UserId = table.Column(type: "TEXT", nullable: false), + LoginProvider = table.Column(type: "TEXT", maxLength: 128, nullable: false), + Name = table.Column(type: "TEXT", maxLength: 128, nullable: false), + Value = table.Column(type: "TEXT", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_AspNetUserTokens", x => new { x.UserId, x.LoginProvider, x.Name }); + table.ForeignKey( + name: "FK_AspNetUserTokens_AspNetUsers_UserId", + column: x => x.UserId, + principalTable: "AspNetUsers", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateIndex( + name: "IX_AspNetRoleClaims_RoleId", + table: "AspNetRoleClaims", + column: "RoleId"); + + migrationBuilder.CreateIndex( + name: "RoleNameIndex", + table: "AspNetRoles", + column: "NormalizedName", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_AspNetUserClaims_UserId", + table: "AspNetUserClaims", + column: "UserId"); + + migrationBuilder.CreateIndex( + name: "IX_AspNetUserLogins_UserId", + table: "AspNetUserLogins", + column: "UserId"); + + migrationBuilder.CreateIndex( + name: "IX_AspNetUserPasskeys_UserId", + table: "AspNetUserPasskeys", + column: "UserId"); + + migrationBuilder.CreateIndex( + name: "IX_AspNetUserRoles_RoleId", + table: "AspNetUserRoles", + column: "RoleId"); + + migrationBuilder.CreateIndex( + name: "EmailIndex", + table: "AspNetUsers", + column: "NormalizedEmail"); + + migrationBuilder.CreateIndex( + name: "UserNameIndex", + table: "AspNetUsers", + column: "NormalizedUserName", + unique: true); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "AspNetRoleClaims"); + + migrationBuilder.DropTable( + name: "AspNetUserClaims"); + + migrationBuilder.DropTable( + name: "AspNetUserLogins"); + + migrationBuilder.DropTable( + name: "AspNetUserPasskeys"); + + migrationBuilder.DropTable( + name: "AspNetUserRoles"); + + migrationBuilder.DropTable( + name: "AspNetUserTokens"); + + migrationBuilder.DropTable( + name: "AspNetRoles"); + + migrationBuilder.DropTable( + name: "AspNetUsers"); + } + } +} diff --git a/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Data/SqlLite/ApplicationDbContextModelSnapshot.cs b/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Data/SqlLite/ApplicationDbContextModelSnapshot.cs new file mode 100644 index 0000000000..f6bdeaf144 --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Data/SqlLite/ApplicationDbContextModelSnapshot.cs @@ -0,0 +1,330 @@ +// +using System; +using BlazorWeb_CSharp.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace BlazorWeb_CSharp.Migrations +{ + [DbContext(typeof(ApplicationDbContext))] + partial class ApplicationDbContextModelSnapshot : ModelSnapshot + { + protected override void BuildModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder.HasAnnotation("ProductVersion", "10.0.0"); + + modelBuilder.Entity("BlazorWeb_CSharp.Data.ApplicationUser", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("AccessFailedCount") + .HasColumnType("INTEGER"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("TEXT"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("EmailConfirmed") + .HasColumnType("INTEGER"); + + b.Property("LockoutEnabled") + .HasColumnType("INTEGER"); + + b.Property("LockoutEnd") + .HasColumnType("TEXT"); + + b.Property("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("PasswordHash") + .HasColumnType("TEXT"); + + b.Property("PhoneNumber") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("INTEGER"); + + b.Property("SecurityStamp") + .HasColumnType("TEXT"); + + b.Property("TwoFactorEnabled") + .HasColumnType("INTEGER"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex"); + + b.ToTable("AspNetUsers", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("TEXT"); + + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("NormalizedName") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasDatabaseName("RoleNameIndex"); + + b.ToTable("AspNetRoles", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ClaimType") + .HasColumnType("TEXT"); + + b.Property("ClaimValue") + .HasColumnType("TEXT"); + + b.Property("RoleId") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetRoleClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ClaimType") + .HasColumnType("TEXT"); + + b.Property("ClaimValue") + .HasColumnType("TEXT"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasMaxLength(128) + .HasColumnType("TEXT"); + + b.Property("ProviderKey") + .HasMaxLength(128) + .HasColumnType("TEXT"); + + b.Property("ProviderDisplayName") + .HasColumnType("TEXT"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserLogins", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserPasskey", b => + { + b.Property("CredentialId") + .HasMaxLength(1024) + .HasColumnType("BLOB"); + + b.Property("AttestationObject") + .IsRequired() + .HasColumnType("BLOB"); + + b.Property("ClientDataJson") + .IsRequired() + .HasColumnType("BLOB"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("IsBackedUp") + .HasColumnType("INTEGER"); + + b.Property("IsBackupEligible") + .HasColumnType("INTEGER"); + + b.Property("IsUserVerified") + .HasColumnType("INTEGER"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("PublicKey") + .IsRequired() + .HasMaxLength(1024) + .HasColumnType("BLOB"); + + b.Property("SignCount") + .HasColumnType("INTEGER"); + + b.PrimitiveCollection("Transports") + .HasColumnType("TEXT"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("CredentialId"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserPasskeys", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("TEXT"); + + b.Property("RoleId") + .HasColumnType("TEXT"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetUserRoles", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("TEXT"); + + b.Property("LoginProvider") + .HasMaxLength(128) + .HasColumnType("TEXT"); + + b.Property("Name") + .HasMaxLength(128) + .HasColumnType("TEXT"); + + b.Property("Value") + .HasColumnType("TEXT"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("AspNetUserTokens", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("BlazorWeb_CSharp.Data.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("BlazorWeb_CSharp.Data.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserPasskey", b => + { + b.HasOne("BlazorWeb_CSharp.Data.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("BlazorWeb_CSharp.Data.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("BlazorWeb_CSharp.Data.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Data/SqlServer/00000000000000_CreateIdentitySchema.Designer.cs b/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Data/SqlServer/00000000000000_CreateIdentitySchema.Designer.cs new file mode 100644 index 0000000000..c92d84f26d --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Data/SqlServer/00000000000000_CreateIdentitySchema.Designer.cs @@ -0,0 +1,344 @@ +// +using System; +using BlazorWeb_CSharp.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace BlazorWeb_CSharp.Migrations +{ + [DbContext(typeof(ApplicationDbContext))] + [Migration("00000000000000_CreateIdentitySchema")] + partial class CreateIdentitySchema + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "10.0.0") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); + + modelBuilder.Entity("BlazorWeb_CSharp.Data.ApplicationUser", b => + { + b.Property("Id") + .HasColumnType("nvarchar(450)"); + + b.Property("AccessFailedCount") + .HasColumnType("int"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("nvarchar(max)"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("EmailConfirmed") + .HasColumnType("bit"); + + b.Property("LockoutEnabled") + .HasColumnType("bit"); + + b.Property("LockoutEnd") + .HasColumnType("datetimeoffset"); + + b.Property("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("PasswordHash") + .HasColumnType("nvarchar(max)"); + + b.Property("PhoneNumber") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("bit"); + + b.Property("SecurityStamp") + .HasColumnType("nvarchar(max)"); + + b.Property("TwoFactorEnabled") + .HasColumnType("bit"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex") + .HasFilter("[NormalizedUserName] IS NOT NULL"); + + b.ToTable("AspNetUsers", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .HasColumnType("nvarchar(450)"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("NormalizedName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasDatabaseName("RoleNameIndex") + .HasFilter("[NormalizedName] IS NOT NULL"); + + b.ToTable("AspNetRoles", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("nvarchar(max)"); + + b.Property("ClaimValue") + .HasColumnType("nvarchar(max)"); + + b.Property("RoleId") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetRoleClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("nvarchar(max)"); + + b.Property("ClaimValue") + .HasColumnType("nvarchar(max)"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("ProviderKey") + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("ProviderDisplayName") + .HasColumnType("nvarchar(max)"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserLogins", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserPasskey", b => + { + b.Property("CredentialId") + .HasMaxLength(1024) + .HasColumnType("varbinary(1024)"); + + b.Property("AttestationObject") + .IsRequired() + .HasColumnType("varbinary(max)"); + + b.Property("ClientDataJson") + .IsRequired() + .HasColumnType("varbinary(max)"); + + b.Property("CreatedAt") + .HasColumnType("datetimeoffset"); + + b.Property("IsBackedUp") + .HasColumnType("bit"); + + b.Property("IsBackupEligible") + .HasColumnType("bit"); + + b.Property("IsUserVerified") + .HasColumnType("bit"); + + b.Property("Name") + .HasColumnType("nvarchar(max)"); + + b.Property("PublicKey") + .IsRequired() + .HasMaxLength(1024) + .HasColumnType("varbinary(1024)"); + + b.Property("SignCount") + .HasColumnType("bigint"); + + b.PrimitiveCollection("Transports") + .HasColumnType("nvarchar(max)"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.HasKey("CredentialId"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserPasskeys", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("nvarchar(450)"); + + b.Property("RoleId") + .HasColumnType("nvarchar(450)"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetUserRoles", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("nvarchar(450)"); + + b.Property("LoginProvider") + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("Name") + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("Value") + .HasColumnType("nvarchar(max)"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("AspNetUserTokens", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("BlazorWeb_CSharp.Data.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("BlazorWeb_CSharp.Data.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserPasskey", b => + { + b.HasOne("BlazorWeb_CSharp.Data.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("BlazorWeb_CSharp.Data.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("BlazorWeb_CSharp.Data.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Data/SqlServer/00000000000000_CreateIdentitySchema.cs b/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Data/SqlServer/00000000000000_CreateIdentitySchema.cs new file mode 100644 index 0000000000..8d26035044 --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Data/SqlServer/00000000000000_CreateIdentitySchema.cs @@ -0,0 +1,257 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace BlazorWeb_CSharp.Migrations +{ + /// + public partial class CreateIdentitySchema : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "AspNetRoles", + columns: table => new + { + Id = table.Column(type: "nvarchar(450)", nullable: false), + Name = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), + NormalizedName = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), + ConcurrencyStamp = table.Column(type: "nvarchar(max)", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_AspNetRoles", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "AspNetUsers", + columns: table => new + { + Id = table.Column(type: "nvarchar(450)", nullable: false), + UserName = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), + NormalizedUserName = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), + Email = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), + NormalizedEmail = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), + EmailConfirmed = table.Column(type: "bit", nullable: false), + PasswordHash = table.Column(type: "nvarchar(max)", nullable: true), + SecurityStamp = table.Column(type: "nvarchar(max)", nullable: true), + ConcurrencyStamp = table.Column(type: "nvarchar(max)", nullable: true), + PhoneNumber = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), + PhoneNumberConfirmed = table.Column(type: "bit", nullable: false), + TwoFactorEnabled = table.Column(type: "bit", nullable: false), + LockoutEnd = table.Column(type: "datetimeoffset", nullable: true), + LockoutEnabled = table.Column(type: "bit", nullable: false), + AccessFailedCount = table.Column(type: "int", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_AspNetUsers", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "AspNetRoleClaims", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("SqlServer:Identity", "1, 1"), + RoleId = table.Column(type: "nvarchar(450)", nullable: false), + ClaimType = table.Column(type: "nvarchar(max)", nullable: true), + ClaimValue = table.Column(type: "nvarchar(max)", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_AspNetRoleClaims", x => x.Id); + table.ForeignKey( + name: "FK_AspNetRoleClaims_AspNetRoles_RoleId", + column: x => x.RoleId, + principalTable: "AspNetRoles", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "AspNetUserClaims", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("SqlServer:Identity", "1, 1"), + UserId = table.Column(type: "nvarchar(450)", nullable: false), + ClaimType = table.Column(type: "nvarchar(max)", nullable: true), + ClaimValue = table.Column(type: "nvarchar(max)", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_AspNetUserClaims", x => x.Id); + table.ForeignKey( + name: "FK_AspNetUserClaims_AspNetUsers_UserId", + column: x => x.UserId, + principalTable: "AspNetUsers", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "AspNetUserLogins", + columns: table => new + { + LoginProvider = table.Column(type: "nvarchar(128)", maxLength: 128, nullable: false), + ProviderKey = table.Column(type: "nvarchar(128)", maxLength: 128, nullable: false), + ProviderDisplayName = table.Column(type: "nvarchar(max)", nullable: true), + UserId = table.Column(type: "nvarchar(450)", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_AspNetUserLogins", x => new { x.LoginProvider, x.ProviderKey }); + table.ForeignKey( + name: "FK_AspNetUserLogins_AspNetUsers_UserId", + column: x => x.UserId, + principalTable: "AspNetUsers", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "AspNetUserPasskeys", + columns: table => new + { + CredentialId = table.Column(type: "varbinary(1024)", maxLength: 1024, nullable: false), + UserId = table.Column(type: "nvarchar(450)", nullable: false), + PublicKey = table.Column(type: "varbinary(1024)", maxLength: 1024, nullable: false), + Name = table.Column(type: "nvarchar(max)", nullable: true), + CreatedAt = table.Column(type: "datetimeoffset", nullable: false), + SignCount = table.Column(type: "bigint", nullable: false), + Transports = table.Column(type: "nvarchar(max)", nullable: true), + IsUserVerified = table.Column(type: "bit", nullable: false), + IsBackupEligible = table.Column(type: "bit", nullable: false), + IsBackedUp = table.Column(type: "bit", nullable: false), + AttestationObject = table.Column(type: "varbinary(max)", nullable: false), + ClientDataJson = table.Column(type: "varbinary(max)", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_AspNetUserPasskeys", x => x.CredentialId); + table.ForeignKey( + name: "FK_AspNetUserPasskeys_AspNetUsers_UserId", + column: x => x.UserId, + principalTable: "AspNetUsers", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "AspNetUserRoles", + columns: table => new + { + UserId = table.Column(type: "nvarchar(450)", nullable: false), + RoleId = table.Column(type: "nvarchar(450)", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_AspNetUserRoles", x => new { x.UserId, x.RoleId }); + table.ForeignKey( + name: "FK_AspNetUserRoles_AspNetRoles_RoleId", + column: x => x.RoleId, + principalTable: "AspNetRoles", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_AspNetUserRoles_AspNetUsers_UserId", + column: x => x.UserId, + principalTable: "AspNetUsers", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "AspNetUserTokens", + columns: table => new + { + UserId = table.Column(type: "nvarchar(450)", nullable: false), + LoginProvider = table.Column(type: "nvarchar(128)", maxLength: 128, nullable: false), + Name = table.Column(type: "nvarchar(128)", maxLength: 128, nullable: false), + Value = table.Column(type: "nvarchar(max)", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_AspNetUserTokens", x => new { x.UserId, x.LoginProvider, x.Name }); + table.ForeignKey( + name: "FK_AspNetUserTokens_AspNetUsers_UserId", + column: x => x.UserId, + principalTable: "AspNetUsers", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateIndex( + name: "IX_AspNetRoleClaims_RoleId", + table: "AspNetRoleClaims", + column: "RoleId"); + + migrationBuilder.CreateIndex( + name: "RoleNameIndex", + table: "AspNetRoles", + column: "NormalizedName", + unique: true, + filter: "[NormalizedName] IS NOT NULL"); + + migrationBuilder.CreateIndex( + name: "IX_AspNetUserClaims_UserId", + table: "AspNetUserClaims", + column: "UserId"); + + migrationBuilder.CreateIndex( + name: "IX_AspNetUserLogins_UserId", + table: "AspNetUserLogins", + column: "UserId"); + + migrationBuilder.CreateIndex( + name: "IX_AspNetUserPasskeys_UserId", + table: "AspNetUserPasskeys", + column: "UserId"); + + migrationBuilder.CreateIndex( + name: "IX_AspNetUserRoles_RoleId", + table: "AspNetUserRoles", + column: "RoleId"); + + migrationBuilder.CreateIndex( + name: "EmailIndex", + table: "AspNetUsers", + column: "NormalizedEmail"); + + migrationBuilder.CreateIndex( + name: "UserNameIndex", + table: "AspNetUsers", + column: "NormalizedUserName", + unique: true, + filter: "[NormalizedUserName] IS NOT NULL"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "AspNetRoleClaims"); + + migrationBuilder.DropTable( + name: "AspNetUserClaims"); + + migrationBuilder.DropTable( + name: "AspNetUserLogins"); + + migrationBuilder.DropTable( + name: "AspNetUserRoles"); + + migrationBuilder.DropTable( + name: "AspNetUserTokens"); + + migrationBuilder.DropTable( + name: "AspNetRoles"); + + migrationBuilder.DropTable( + name: "AspNetUsers"); + } + } +} diff --git a/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Data/SqlServer/ApplicationDbContextModelSnapshot.cs b/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Data/SqlServer/ApplicationDbContextModelSnapshot.cs new file mode 100644 index 0000000000..3f5e8fe8be --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Data/SqlServer/ApplicationDbContextModelSnapshot.cs @@ -0,0 +1,341 @@ +// +using System; +using BlazorWeb_CSharp.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace BlazorWeb_CSharp.Migrations +{ + [DbContext(typeof(ApplicationDbContext))] + partial class ApplicationDbContextModelSnapshot : ModelSnapshot + { + protected override void BuildModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "10.0.0") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); + + modelBuilder.Entity("BlazorWeb_CSharp.Data.ApplicationUser", b => + { + b.Property("Id") + .HasColumnType("nvarchar(450)"); + + b.Property("AccessFailedCount") + .HasColumnType("int"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("nvarchar(max)"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("EmailConfirmed") + .HasColumnType("bit"); + + b.Property("LockoutEnabled") + .HasColumnType("bit"); + + b.Property("LockoutEnd") + .HasColumnType("datetimeoffset"); + + b.Property("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("PasswordHash") + .HasColumnType("nvarchar(max)"); + + b.Property("PhoneNumber") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("bit"); + + b.Property("SecurityStamp") + .HasColumnType("nvarchar(max)"); + + b.Property("TwoFactorEnabled") + .HasColumnType("bit"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex") + .HasFilter("[NormalizedUserName] IS NOT NULL"); + + b.ToTable("AspNetUsers", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .HasColumnType("nvarchar(450)"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("NormalizedName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasDatabaseName("RoleNameIndex") + .HasFilter("[NormalizedName] IS NOT NULL"); + + b.ToTable("AspNetRoles", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("nvarchar(max)"); + + b.Property("ClaimValue") + .HasColumnType("nvarchar(max)"); + + b.Property("RoleId") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetRoleClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("nvarchar(max)"); + + b.Property("ClaimValue") + .HasColumnType("nvarchar(max)"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("ProviderKey") + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("ProviderDisplayName") + .HasColumnType("nvarchar(max)"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserLogins", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserPasskey", b => + { + b.Property("CredentialId") + .HasMaxLength(1024) + .HasColumnType("varbinary(1024)"); + + b.Property("AttestationObject") + .IsRequired() + .HasColumnType("varbinary(max)"); + + b.Property("ClientDataJson") + .IsRequired() + .HasColumnType("varbinary(max)"); + + b.Property("CreatedAt") + .HasColumnType("datetimeoffset"); + + b.Property("IsBackedUp") + .HasColumnType("bit"); + + b.Property("IsBackupEligible") + .HasColumnType("bit"); + + b.Property("IsUserVerified") + .HasColumnType("bit"); + + b.Property("Name") + .HasColumnType("nvarchar(max)"); + + b.Property("PublicKey") + .IsRequired() + .HasMaxLength(1024) + .HasColumnType("varbinary(1024)"); + + b.Property("SignCount") + .HasColumnType("bigint"); + + b.PrimitiveCollection("Transports") + .HasColumnType("nvarchar(max)"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.HasKey("CredentialId"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserPasskeys", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("nvarchar(450)"); + + b.Property("RoleId") + .HasColumnType("nvarchar(450)"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetUserRoles", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("nvarchar(450)"); + + b.Property("LoginProvider") + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("Name") + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.Property("Value") + .HasColumnType("nvarchar(max)"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("AspNetUserTokens", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("BlazorWeb_CSharp.Data.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("BlazorWeb_CSharp.Data.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserPasskey", b => + { + b.HasOne("BlazorWeb_CSharp.Data.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("BlazorWeb_CSharp.Data.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("BlazorWeb_CSharp.Data.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Data/app.db b/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Data/app.db new file mode 100644 index 0000000000..74d0af2189 Binary files /dev/null and b/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Data/app.db differ diff --git a/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Program.Main.cs b/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Program.Main.cs new file mode 100644 index 0000000000..597fdbc05e --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Program.Main.cs @@ -0,0 +1,145 @@ +#if (IndividualLocalAuth) +#if (UseServer) +using Microsoft.AspNetCore.Components.Authorization; +#endif +using Microsoft.AspNetCore.Identity; +using Microsoft.EntityFrameworkCore; +#endif +#if (UseWebAssembly && SampleContent) +using BlazorWeb_CSharp.Client.Pages; +#endif +using Microsoft.FluentUI.AspNetCore.Components; +using BlazorWeb_CSharp.Components; +#if (IndividualLocalAuth) +using BlazorWeb_CSharp.Components.Account; +using BlazorWeb_CSharp.Data; +#endif + +namespace BlazorWeb_CSharp; + +public class Program +{ + public static void Main(string[] args) + { + var builder = WebApplication.CreateBuilder(args); + + // Add services to the container. + #if (!UseServer && !UseWebAssembly) + builder.Services.AddRazorComponents(); + #else + builder.Services.AddRazorComponents() + #if (UseServer && UseWebAssembly && IndividualLocalAuth) + .AddInteractiveServerComponents() + .AddInteractiveWebAssemblyComponents() + .AddAuthenticationStateSerialization(); + #elif (UseServer && UseWebAssembly) + .AddInteractiveServerComponents() + .AddInteractiveWebAssemblyComponents(); + #elif (UseServer) + .AddInteractiveServerComponents(); + #elif (UseWebAssembly && IndividualLocalAuth) + .AddInteractiveWebAssemblyComponents() + .AddAuthenticationStateSerialization(); + #elif (UseWebAssembly) + .AddInteractiveWebAssemblyComponents(); + #endif + #endif + builder.Services.AddFluentUIComponents(); + + #if (IndividualLocalAuth) + builder.Services.AddCascadingAuthenticationState(); + builder.Services.AddScoped(); + #if (UseServer) + builder.Services.AddScoped(); + #endif + + builder.Services.AddAuthentication(options => + { + options.DefaultScheme = IdentityConstants.ApplicationScheme; + options.DefaultSignInScheme = IdentityConstants.ExternalScheme; + }) + .AddIdentityCookies(); + #if (!UseServer) + builder.Services.AddAuthorization(); + #endif + + var connectionString = builder.Configuration.GetConnectionString("DefaultConnection") ?? throw new InvalidOperationException("Connection string 'DefaultConnection' not found."); + builder.Services.AddDbContext(options => + #if (UseLocalDB) + options.UseSqlServer(connectionString)); + #else + options.UseSqlite(connectionString)); + #endif + builder.Services.AddDatabaseDeveloperPageExceptionFilter(); + + builder.Services.AddIdentityCore(options => + { + options.SignIn.RequireConfirmedAccount = true; + options.Stores.SchemaVersion = IdentitySchemaVersions.Version3; + }) + .AddEntityFrameworkStores() + .AddSignInManager() + .AddDefaultTokenProviders(); + + builder.Services.AddSingleton, IdentityNoOpEmailSender>(); + + #endif + var app = builder.Build(); + + // Configure the HTTP request pipeline. +#if (UseWebAssembly || IndividualLocalAuth) + if (app.Environment.IsDevelopment()) + { +#if (UseWebAssembly) + app.UseWebAssemblyDebugging(); +#endif +#if (IndividualLocalAuth) + app.UseMigrationsEndPoint(); +#endif + } + else +#else + if (!app.Environment.IsDevelopment()) +#endif + { + app.UseExceptionHandler("/Error"); + #if (HasHttpsProfile) + // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts. + app.UseHsts(); + #endif + } + + app.UseStatusCodePagesWithReExecute("/not-found", createScopeForErrors: true); + + #if (HasHttpsProfile) + app.UseHttpsRedirection(); + +#endif + app.UseAntiforgery(); + + app.MapStaticAssets(); + #if (UseServer && UseWebAssembly) + app.MapRazorComponents() + .AddInteractiveServerRenderMode() + .AddInteractiveWebAssemblyRenderMode() + #elif (UseServer) + app.MapRazorComponents() + .AddInteractiveServerRenderMode(); + #elif (UseWebAssembly) + app.MapRazorComponents() + .AddInteractiveWebAssemblyRenderMode() + #else + app.MapRazorComponents(); + #endif + #if (UseWebAssembly) + .AddAdditionalAssemblies(typeof(Client._Imports).Assembly); + #endif + + #if (IndividualLocalAuth) + // Add additional endpoints required by the Identity /Account Razor components. + app.MapAdditionalIdentityEndpoints(); + + #endif + app.Run(); + } +} diff --git a/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Program.cs b/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Program.cs new file mode 100644 index 0000000000..41451382dc --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Program.cs @@ -0,0 +1,136 @@ +#if (IndividualLocalAuth) +#if (UseServer) +using Microsoft.AspNetCore.Components.Authorization; +#endif +using Microsoft.AspNetCore.Identity; +using Microsoft.EntityFrameworkCore; +#endif +#if (UseWebAssembly && SampleContent) +using BlazorWeb_CSharp.Client.Pages; +#endif +using BlazorWeb_CSharp.Components; +#if (IndividualLocalAuth) +using BlazorWeb_CSharp.Components.Account; +using BlazorWeb_CSharp.Data; +#endif +using Microsoft.FluentUI.AspNetCore.Components; + +var builder = WebApplication.CreateBuilder(args); + +// Add services to the container. +#if (!UseServer && !UseWebAssembly) +builder.Services.AddRazorComponents(); +#else +builder.Services.AddRazorComponents() +#if (UseServer && UseWebAssembly && IndividualLocalAuth) + .AddInteractiveServerComponents() + .AddInteractiveWebAssemblyComponents() + .AddAuthenticationStateSerialization(); +#elif (UseServer && UseWebAssembly) + .AddInteractiveServerComponents() + .AddInteractiveWebAssemblyComponents(); +#elif (UseServer) + .AddInteractiveServerComponents(); +#elif (UseWebAssembly && IndividualLocalAuth) + .AddInteractiveWebAssemblyComponents() + .AddAuthenticationStateSerialization(); +#elif (UseWebAssembly) + .AddInteractiveWebAssemblyComponents(); +#endif +#endif +builder.Services.AddFluentUIComponents(); + +#if (IndividualLocalAuth) +builder.Services.AddCascadingAuthenticationState(); +builder.Services.AddScoped(); +#if (UseServer) +builder.Services.AddScoped(); +#endif + +builder.Services.AddAuthentication(options => + { + options.DefaultScheme = IdentityConstants.ApplicationScheme; + options.DefaultSignInScheme = IdentityConstants.ExternalScheme; + }) + .AddIdentityCookies(); +#if (!UseServer) +builder.Services.AddAuthorization(); +#endif + +var connectionString = builder.Configuration.GetConnectionString("DefaultConnection") ?? throw new InvalidOperationException("Connection string 'DefaultConnection' not found."); +builder.Services.AddDbContext(options => +#if (UseLocalDB) + options.UseSqlServer(connectionString)); +#else + options.UseSqlite(connectionString)); +#endif +builder.Services.AddDatabaseDeveloperPageExceptionFilter(); + +builder.Services.AddIdentityCore(options => + { + options.SignIn.RequireConfirmedAccount = true; + options.Stores.SchemaVersion = IdentitySchemaVersions.Version3; + }) + .AddEntityFrameworkStores() + .AddSignInManager() + .AddDefaultTokenProviders(); + +builder.Services.AddSingleton, IdentityNoOpEmailSender>(); + +#endif +var app = builder.Build(); + +// Configure the HTTP request pipeline. +#if (UseWebAssembly || IndividualLocalAuth) +if (app.Environment.IsDevelopment()) +{ +#if (UseWebAssembly) + app.UseWebAssemblyDebugging(); +#endif +#if (IndividualLocalAuth) + app.UseMigrationsEndPoint(); +#endif +} +else +#else +if (!app.Environment.IsDevelopment()) +#endif +{ + app.UseExceptionHandler("/Error", createScopeForErrors: true); +#if (HasHttpsProfile) + // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts. + app.UseHsts(); +#endif +} +app.UseStatusCodePagesWithReExecute("/not-found", createScopeForErrors: true); + +#if (HasHttpsProfile) +app.UseHttpsRedirection(); + +#endif +app.UseAntiforgery(); + +app.MapStaticAssets(); +#if (UseServer && UseWebAssembly) +app.MapRazorComponents() + .AddInteractiveServerRenderMode() + .AddInteractiveWebAssemblyRenderMode() +#elif (UseServer) +app.MapRazorComponents() + .AddInteractiveServerRenderMode(); +#elif (UseWebAssembly) +app.MapRazorComponents() + .AddInteractiveWebAssemblyRenderMode() +#else +app.MapRazorComponents(); +#endif +#if (UseWebAssembly) + .AddAdditionalAssemblies(typeof(BlazorWeb_CSharp.Client._Imports).Assembly); +#endif + +#if (IndividualLocalAuth) +// Add additional endpoints required by the Identity /Account Razor components. +app.MapAdditionalIdentityEndpoints(); + +#endif +app.Run(); diff --git a/src/Templates/templates/blazorweb-csharp/BlazorWeb-CSharp/Properties/launchSettings.json b/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Properties/launchSettings.json similarity index 100% rename from src/Templates/templates/blazorweb-csharp/BlazorWeb-CSharp/Properties/launchSettings.json rename to src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/Properties/launchSettings.json diff --git a/src/Templates/templates/blazorweb-csharp/BlazorWeb-CSharp/appsettings.Development.json b/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/appsettings.Development.json similarity index 100% rename from src/Templates/templates/blazorweb-csharp/BlazorWeb-CSharp/appsettings.Development.json rename to src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/appsettings.Development.json diff --git a/src/Templates/templates/blazorweb-csharp/BlazorWeb-CSharp/appsettings.json b/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/appsettings.json similarity index 100% rename from src/Templates/templates/blazorweb-csharp/BlazorWeb-CSharp/appsettings.json rename to src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/appsettings.json diff --git a/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/wwwroot/app.css b/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/wwwroot/app.css new file mode 100644 index 0000000000..ae88d5809c --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/wwwroot/app.css @@ -0,0 +1,219 @@ +@import '_content/Microsoft.FluentUI.AspNetCore.Components/css/reboot.css'; + +body { + --body-font: "Segoe UI Variable", "Segoe UI", sans-serif; + font-family: var(--body-font); + font-size: var(--type-ramp-base-font-size); + line-height: var(--type-ramp-base-line-height); + margin: 0; +} + +.navmenu-icon { + display: none; +} + +.main { + min-height: calc(100dvh - 86px); + color: var(--neutral-foreground-rest); + align-items: stretch !important; +} + +.body-content { + align-self: stretch; + height: calc(100dvh - 86px) !important; + display: flex; +} + +.content { + padding: 0.5rem 1.5rem; + align-self: stretch !important; + width: 100%; +} + +.manage { + width: 100dvw; +} + +footer { + background: var(--neutral-layer-4); + color: var(--neutral-foreground-rest); + align-items: center; + padding: 10px 10px; +} + + footer a { + color: var(--neutral-foreground-rest); + text-decoration: none; + } + + footer a:focus { + outline: 1px dashed; + outline-offset: 3px; + } + + footer a:hover { + text-decoration: underline; + } + +.alert { + border: 1px dashed var(--accent-fill-rest); + padding: 5px; + margin: 10px 0; +} + +.alert-danger { + border: 1px dashed var(--error); +} + +.alert-success { + border: 1px dashed var(--success); +} + +.alert-warning { + border: 1px dashed var(--warning); +} + +#blazor-error-ui { + background: lightyellow; + bottom: 0; + box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.2); + display: none; + left: 0; + padding: 0.6rem 1.25rem 0.7rem 1.25rem; + position: fixed; + width: 100%; + z-index: 1000; + margin: 20px 0; +} + + #blazor-error-ui .dismiss { + cursor: pointer; + position: absolute; + right: 0.75rem; + top: 0.5rem; + } + +.blazor-error-boundary { + background: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTYiIGhlaWdodD0iNDkiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIG92ZXJmbG93PSJoaWRkZW4iPjxkZWZzPjxjbGlwUGF0aCBpZD0iY2xpcDAiPjxyZWN0IHg9IjIzNSIgeT0iNTEiIHdpZHRoPSI1NiIgaGVpZ2h0PSI0OSIvPjwvY2xpcFBhdGg+PC9kZWZzPjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMCkiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yMzUgLTUxKSI+PHBhdGggZD0iTTI2My41MDYgNTFDMjY0LjcxNyA1MSAyNjUuODEzIDUxLjQ4MzcgMjY2LjYwNiA1Mi4yNjU4TDI2Ny4wNTIgNTIuNzk4NyAyNjcuNTM5IDUzLjYyODMgMjkwLjE4NSA5Mi4xODMxIDI5MC41NDUgOTIuNzk1IDI5MC42NTYgOTIuOTk2QzI5MC44NzcgOTMuNTEzIDI5MSA5NC4wODE1IDI5MSA5NC42NzgyIDI5MSA5Ny4wNjUxIDI4OS4wMzggOTkgMjg2LjYxNyA5OUwyNDAuMzgzIDk5QzIzNy45NjMgOTkgMjM2IDk3LjA2NTEgMjM2IDk0LjY3ODIgMjM2IDk0LjM3OTkgMjM2LjAzMSA5NC4wODg2IDIzNi4wODkgOTMuODA3MkwyMzYuMzM4IDkzLjAxNjIgMjM2Ljg1OCA5Mi4xMzE0IDI1OS40NzMgNTMuNjI5NCAyNTkuOTYxIDUyLjc5ODUgMjYwLjQwNyA1Mi4yNjU4QzI2MS4yIDUxLjQ4MzcgMjYyLjI5NiA1MSAyNjMuNTA2IDUxWk0yNjMuNTg2IDY2LjAxODNDMjYwLjczNyA2Ni4wMTgzIDI1OS4zMTMgNjcuMTI0NSAyNTkuMzEzIDY5LjMzNyAyNTkuMzEzIDY5LjYxMDIgMjU5LjMzMiA2OS44NjA4IDI1OS4zNzEgNzAuMDg4N0wyNjEuNzk1IDg0LjAxNjEgMjY1LjM4IDg0LjAxNjEgMjY3LjgyMSA2OS43NDc1QzI2Ny44NiA2OS43MzA5IDI2Ny44NzkgNjkuNTg3NyAyNjcuODc5IDY5LjMxNzkgMjY3Ljg3OSA2Ny4xMTgyIDI2Ni40NDggNjYuMDE4MyAyNjMuNTg2IDY2LjAxODNaTTI2My41NzYgODYuMDU0N0MyNjEuMDQ5IDg2LjA1NDcgMjU5Ljc4NiA4Ny4zMDA1IDI1OS43ODYgODkuNzkyMSAyNTkuNzg2IDkyLjI4MzcgMjYxLjA0OSA5My41Mjk1IDI2My41NzYgOTMuNTI5NSAyNjYuMTE2IDkzLjUyOTUgMjY3LjM4NyA5Mi4yODM3IDI2Ny4zODcgODkuNzkyMSAyNjcuMzg3IDg3LjMwMDUgMjY2LjExNiA4Ni4wNTQ3IDI2My41NzYgODYuMDU0N1oiIGZpbGw9IiNGRkU1MDAiIGZpbGwtcnVsZT0iZXZlbm9kZCIvPjwvZz48L3N2Zz4=) no-repeat 1rem/1.8rem, #b32121; + padding: 1rem 1rem 1rem 3.7rem; + color: white; +} + + .blazor-error-boundary::before { + content: "An error has occurred. " + } + +.loading-progress { + position: relative; + display: block; + width: 8rem; + height: 8rem; + margin: 20vh auto 1rem auto; +} + + .loading-progress circle { + fill: none; + stroke: #e0e0e0; + stroke-width: 0.6rem; + transform-origin: 50% 50%; + transform: rotate(-90deg); + } + + .loading-progress circle:last-child { + stroke: #1b6ec2; + stroke-dasharray: calc(3.141 * var(--blazor-load-percentage, 0%) * 0.8), 500%; + transition: stroke-dasharray 0.05s ease-in-out; + } + +.loading-progress-text { + position: absolute; + text-align: center; + font-weight: bold; + inset: calc(20vh + 3.25rem) 0 auto 0.2rem; +} + + .loading-progress-text:after { + content: var(--blazor-load-percentage-text, "Loading"); + } + +code { + color: #c02d76; +} + +@media (max-width: 600px) { + .header-gutters { + margin: 0.5rem 3rem 0.5rem 1.5rem !important; + } + + [dir="rtl"] .header-gutters { + margin: 0.5rem 1.5rem 0.5rem 3rem !important; + } + + .main { + flex-direction: column !important; + row-gap: 0 !important; + } + + nav.sitenav { + width: 100%; + height: 100%; + } + + #main-menu { + width: 100% !important; + } + + #main-menu > div:first-child:is(.expander) { + display: none; + } + + .navmenu { + width: 100%; + } + + #navmenu-toggle { + appearance: none; + } + + #navmenu-toggle ~ nav { + display: none; + } + + #navmenu-toggle:checked ~ nav { + display: block; + } + + .navmenu-icon { + cursor: pointer; + z-index: 10; + display: block; + position: absolute; + top: 15px; + left: unset; + right: 20px; + width: 20px; + height: 20px; + border: none; + } + + [dir="rtl"] .navmenu-icon { + left: 20px; + right: unset; + } +} + +/* Table */ + +table { + width: 100%; + border-collapse: collapse; + border-spacing: 0; +} + + table td, + table th { + padding: var(--spacingVerticalS) var(--spacingHorizontalM); + text-align: left; + vertical-align: middle; + border-bottom: calc(var(--stroke-width) * 1px) solid var(--neutral-stroke-divider-rest); + } diff --git a/src/Templates/templates/blazorweb-csharp/BlazorWeb-CSharp/wwwroot/favicon.ico b/src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/wwwroot/favicon.ico similarity index 100% rename from src/Templates/templates/blazorweb-csharp/BlazorWeb-CSharp/wwwroot/favicon.ico rename to src/Templates/templates/blazorweb-csharp-10/BlazorWeb-CSharp/wwwroot/favicon.ico diff --git a/src/Templates/templates/blazorweb-csharp-8/.template.config/dotnetcli.host.json b/src/Templates/templates/blazorweb-csharp-8/.template.config/dotnetcli.host.json new file mode 100644 index 0000000000..9c853513c3 --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-8/.template.config/dotnetcli.host.json @@ -0,0 +1,61 @@ +{ + "$schema": "https://json.schemastore.org/dotnetcli.host", + "symbolInfo": { + "skipRestore": { + "longName": "no-restore", + "shortName": "" + }, + "InteractivityPlatform": { + "longName": "interactivity", + "shortName": "int" + }, + "AllInteractive": { + "longName": "all-interactive" + }, + "InteractivityLocation": { + "isHidden": true + }, + "Empty": { + "longName": "empty" + }, + "IncludeSampleContent": { + "isHidden": true + }, + "UseLocalDB": { + "longName": "use-local-db" + }, + "Framework": { + "longName": "framework" + }, + "kestrelHttpPort": { + "isHidden": true + }, + "kestrelHttpsPort": { + "isHidden": true + }, + "iisHttpPort": { + "isHidden": true + }, + "iisHttpsPort": { + "isHidden": true + }, + "ExcludeLaunchSettings": { + "longName": "exclude-launch-settings", + "shortName": "" + }, + "UserSecretsId": { + "isHidden": true + }, + "NoHttps": { + "longName": "no-https", + "shortName": "" + }, + "UseProgramMain": { + "longName": "use-program-main", + "shortName": "" + } + }, + "usageExamples": [ + "-int auto --auth individual --use-local-db" + ] +} diff --git a/src/Templates/templates/componentswebassembly-csharp/.template.config/icon.png b/src/Templates/templates/blazorweb-csharp-8/.template.config/icon.png similarity index 100% rename from src/Templates/templates/componentswebassembly-csharp/.template.config/icon.png rename to src/Templates/templates/blazorweb-csharp-8/.template.config/icon.png diff --git a/src/Templates/templates/blazorweb-csharp/.template.config/ide.host.json b/src/Templates/templates/blazorweb-csharp-8/.template.config/ide.host.json similarity index 100% rename from src/Templates/templates/blazorweb-csharp/.template.config/ide.host.json rename to src/Templates/templates/blazorweb-csharp-8/.template.config/ide.host.json diff --git a/src/Templates/templates/blazorweb-csharp-8/.template.config/localize/templatestrings.cs.json b/src/Templates/templates/blazorweb-csharp-8/.template.config/localize/templatestrings.cs.json new file mode 100644 index 0000000000..a4fd02ec54 --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-8/.template.config/localize/templatestrings.cs.json @@ -0,0 +1,44 @@ +{ + "author": "Microsoft", + "name": "Webová aplikace Fluent Blazor", + "description": "Šablona projektu pro vytvoření webové aplikace Blazor, která podporuje vykreslování na straně serveru i interaktivitu klienta. Tato šablona se dá použít pro webové aplikace s bohatými dynamickými uživatelskými rozhraními (UI).", + "symbols/Framework/description": "Cílová architektura pro projekt", + "symbols/Framework/choices/net8.0/description": "Cílový net8.0", + "symbols/UserSecretsId/description": "ID, které se má použít pro tajné kódy (používá se s individuálním ověřováním).", + "symbols/skipRestore/description": "Pokud se tato možnost zadá, přeskočí automatické obnovení projektu při vytvoření.", + "symbols/ExcludeLaunchSettings/description": "Určuje, jestli se má z vygenerované šablony vyloučit soubor launchSettings.json.", + "symbols/kestrelHttpPort/description": "Číslo portu, který se má použít pro koncový bod HTTP v souboru launchSettings.json.", + "symbols/kestrelHttpsPort/description": "Číslo portu, který se má použít pro koncový bod HTTPS v souboru launchSettings.json. Tato možnost se dá použít jenom v případě, že se nepoužije parametr no-https (no-https se bude ignorovat, pokud se použije individuální ověřování).", + "symbols/iisHttpPort/description": "Číslo portu, který se má použít pro koncový bod IIS Express HTTP v souboru launchSettings.json.", + "symbols/iisHttpsPort/description": "Číslo portu, který se má použít pro koncový bod IIS Express HTTPS v souboru launchSettings.json. Tato možnost se dá použít jenom v případě, že se nepoužije parametr no-https (no-https se bude ignorovat, pokud se použije individuální ověřování).", + "symbols/InteractivityPlatform/displayName": "Typ _Interactivity", + "symbols/InteractivityPlatform/description": "Určuje, která hostitelská platforma se má použít pro interaktivní komponenty.", + "symbols/InteractivityPlatform/choices/None/displayName": "Žádné", + "symbols/InteractivityPlatform/choices/None/description": "Žádná interaktivita (pouze vykreslování statického serveru)", + "symbols/InteractivityPlatform/choices/Server/displayName": "Server", + "symbols/InteractivityPlatform/choices/Server/description": "Spouští se na serveru.", + "symbols/InteractivityPlatform/choices/WebAssembly/displayName": "WebAssembly", + "symbols/InteractivityPlatform/choices/WebAssembly/description": "Spustí se v prohlížeči pomocí WebAssembly.", + "symbols/InteractivityPlatform/choices/Auto/displayName": "Automaticky (Server a WebAssembly)", + "symbols/InteractivityPlatform/choices/Auto/description": "Při stahování prostředků WebAssembly používá Server a pak WebAssembly.", + "symbols/InteractivityLocation/displayName": "_Interactivity umístění", + "symbols/InteractivityLocation/description": "Zvolí, které komponenty budou mít povolené interaktivní vykreslování.", + "symbols/InteractivityLocation/choices/InteractivePerPage/displayName": "Na stránku nebo komponentu", + "symbols/InteractivityLocation/choices/InteractivePerPage/description": "Interaktivita se používá pro jednotlivé stránky nebo jednotlivé komponenty.", + "symbols/InteractivityLocation/choices/InteractiveGlobal/displayName": "Globální", + "symbols/InteractivityLocation/choices/InteractiveGlobal/description": "Interaktivita se používá na kořenové úrovni.", + "symbols/IncludeSampleContent/displayName": "_Zahrnout ukázkové stránky", + "symbols/IncludeSampleContent/description": "Nastavuje, jestli se mají přidávat ukázkové stránky a styly pro demonstraci základních vzorů použití.", + "symbols/Empty/description": "Nastavuje, jestli se mají vynechat ukázkové stránky a styly, které demonstrují základní vzory použití.", + "symbols/auth/choices/None/description": "Bez ověřování", + "symbols/auth/choices/Individual/description": "Ověřování Individual", + "symbols/auth/description": "Typ ověřování, který se má použít", + "symbols/UseLocalDB/description": "Určuje, jestli se má použít LocalDB namísto SQLite. Tato možnost platí jenom v případě, že je zadaná možnost --auth Individual.", + "symbols/AllInteractive/displayName": "_Povolit interaktivní vykreslování globálně na celém webu", + "symbols/AllInteractive/description": "Konfiguruje, jestli se má každá stránka nastavit jako interaktivní, a to použitím interaktivního režimu vykreslování na nejvyšší úrovni. Pokud se nastaví na false, stránky budou ve výchozím nastavení používat vykreslování statického serveru a dají se označit jako interaktivní pro jednotlivé stránky nebo komponenty.", + "symbols/NoHttps/description": "Určuje, jestli se má vypnout protokol HTTPS. Tato možnost platí jenom v případě, že se pro --auth nepoužívá jednotlivec.", + "symbols/UseProgramMain/displayName": "Nepoužívat _příkazy nejvyšší úrovně", + "symbols/UseProgramMain/description": "Určuje, jestli se má místo příkazů nejvyšší úrovně generovat explicitní třída Program a metoda Main.", + "postActions/restore/description": "Obnoví balíčky NuGet vyžadované tímto projektem.", + "postActions/restore/manualInstructions/default/text": "Spustit dotnet restore" +} diff --git a/src/Templates/templates/blazorweb-csharp-8/.template.config/localize/templatestrings.de.json b/src/Templates/templates/blazorweb-csharp-8/.template.config/localize/templatestrings.de.json new file mode 100644 index 0000000000..15ab40bb36 --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-8/.template.config/localize/templatestrings.de.json @@ -0,0 +1,44 @@ +{ + "author": "Microsoft", + "name": "Fluent Blazor Web App", + "description": "Eine Projektvorlage zum Erstellen einer Blazor-Web-App, die sowohl serverseitiges Rendering als auch Clientinteraktivität unterstützt. Diese Vorlage kann für Web-Apps mit umfangreichen dynamischen Benutzeroberflächen (UIs) verwendet werden.", + "symbols/Framework/description": "Das Zielframework für das Projekt.", + "symbols/Framework/choices/net8.0/description": "Ziel net8.0", + "symbols/UserSecretsId/description": "Die für Geheimnisse zu verwendende ID (Verwendung mit individueller Authentifizierung).", + "symbols/skipRestore/description": "Wenn angegeben, wird die automatische Wiederherstellung des Projekts beim Erstellen übersprungen.", + "symbols/ExcludeLaunchSettings/description": "Ob launchSettings.json aus der generierten Vorlage ausgeschlossen werden soll.", + "symbols/kestrelHttpPort/description": "Portnummer, die für den HTTP Endpunkt in launchSettings.json verwendet werden soll.", + "symbols/kestrelHttpsPort/description": "Portnummer, die für den HTTPS Endpunkt in launchSettings.json verwendet werden soll. Diese Option ist nur anwendbar, wenn der Parameter no-https nicht verwendet wird (no-https wird ignoriert, wenn die individuelle Authentifizierung verwendet wird).", + "symbols/iisHttpPort/description": "Portnummer, die für den IIS Express HTTP Endpunkt in launchSettings.json verwendet werden soll.", + "symbols/iisHttpsPort/description": "Portnummer, die für den IIS Express-HTTPS-Endpunkt in launchSettings.json verwendet werden soll. Diese Option ist nur anwendbar, wenn der Parameter no-https nicht verwendet wird (no-https wird ignoriert, wenn die individuelle Authentifizierung verwendet wird).", + "symbols/InteractivityPlatform/displayName": "_Interaktivitätstyp", + "symbols/InteractivityPlatform/description": "Wählt aus, welche Hostingplattform für interaktive Komponenten verwendet werden soll.", + "symbols/InteractivityPlatform/choices/None/displayName": "Keine", + "symbols/InteractivityPlatform/choices/None/description": "Keine Interaktivität (nur statisches Serverrendering)", + "symbols/InteractivityPlatform/choices/Server/displayName": "Server", + "symbols/InteractivityPlatform/choices/Server/description": "Wird auf dem Server ausgeführt", + "symbols/InteractivityPlatform/choices/WebAssembly/displayName": "WebAssembly", + "symbols/InteractivityPlatform/choices/WebAssembly/description": "Wird im Browser mithilfe von WebAssembly ausgeführt", + "symbols/InteractivityPlatform/choices/Auto/displayName": "Automatisch (Server und WebAssembly)", + "symbols/InteractivityPlatform/choices/Auto/description": "Verwendet Server beim Herunterladen von WebAssembly-Ressourcen und dann WebAssembly", + "symbols/InteractivityLocation/displayName": "_Interaktivitätsstandort", + "symbols/InteractivityLocation/description": "Wählt aus, für welche Komponenten das interaktive Rendering aktiviert werden soll", + "symbols/InteractivityLocation/choices/InteractivePerPage/displayName": "Pro Seite/ Komponente", + "symbols/InteractivityLocation/choices/InteractivePerPage/description": "Interaktivität wird auf Seiten- oder Komponentenbasis angewendet", + "symbols/InteractivityLocation/choices/InteractiveGlobal/displayName": "Global", + "symbols/InteractivityLocation/choices/InteractiveGlobal/description": "Interaktivität wird auf Stammebene angewendet", + "symbols/IncludeSampleContent/displayName": "_Include Beispielseiten", + "symbols/IncludeSampleContent/description": "Konfiguriert, ob Beispielseiten und Stile hinzugefügt werden, um grundlegende Verwendungsmuster zu veranschaulichen.", + "symbols/Empty/description": "Konfiguriert, ob Beispielseiten und Formatierungen weggelassen werden sollen, die grundlegende Verwendungsmuster veranschaulichen.", + "symbols/auth/choices/None/description": "Keine Authentifizierung", + "symbols/auth/choices/Individual/description": "Individuelle Authentifizierung", + "symbols/auth/description": "Der zu verwendende Authentifizierungstyp", + "symbols/UseLocalDB/description": "Ob LocalDB anstelle von SQLite verwendet werden soll. Diese Option gilt nur, wenn \"--auth Individual\" angegeben ist.", + "symbols/AllInteractive/displayName": "_Aktivieren des interaktiven Renderings global auf der gesamten Website", + "symbols/AllInteractive/description": "Konfiguriert, ob jede Seite interaktiv werden soll, indem ein interaktiver Rendermodus auf der obersten Ebene angewendet wird. False gibt an, dass Seiten standardmäßig statisches Serverrendering verwenden und auf Seiten- oder Komponentenbasis als interaktiv markiert werden können.", + "symbols/NoHttps/description": "Ob HTTPS deaktiviert werden soll. Diese Option gilt nur, wenn \"Individual\" nicht für \"--auth\" verwendet wird.", + "symbols/UseProgramMain/displayName": "Keine Anweisungen_der obersten Ebene verwenden", + "symbols/UseProgramMain/description": "Gibt an, ob anstelle von Anweisungen der obersten Ebene eine explizite Programmklasse und eine Main-Methode generiert werden soll.", + "postActions/restore/description": "„NuGet-Pakete“ wiederherstellen, die für dieses Projekt erforderlich sind.", + "postActions/restore/manualInstructions/default/text": "„dotnet restore“ ausführen" +} diff --git a/src/Templates/templates/blazorweb-csharp-8/.template.config/localize/templatestrings.en.json b/src/Templates/templates/blazorweb-csharp-8/.template.config/localize/templatestrings.en.json new file mode 100644 index 0000000000..eb2290131a --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-8/.template.config/localize/templatestrings.en.json @@ -0,0 +1,44 @@ +{ + "author": "Microsoft", + "name": "Fluent Blazor Web App", + "description": "A project template for creating a Blazor web app that supports both server-side rendering and client interactivity and uses the Fluent components library. This template can be used for web apps with rich dynamic user interfaces (UIs).", + "symbols/Framework/description": "The target framework for the project.", + "symbols/Framework/choices/net8.0/description": "Target net8.0", + "symbols/UserSecretsId/description": "The ID to use for secrets (use with Individual auth).", + "symbols/skipRestore/description": "If specified, skips the automatic restore of the project on create.", + "symbols/ExcludeLaunchSettings/description": "Whether to exclude launchSettings.json from the generated template.", + "symbols/kestrelHttpPort/description": "Port number to use for the HTTP endpoint in launchSettings.json.", + "symbols/kestrelHttpsPort/description": "Port number to use for the HTTPS endpoint in launchSettings.json. This option is only applicable when the parameter no-https is not used (no-https will be ignored if Individual auth is used).", + "symbols/iisHttpPort/description": "Port number to use for the IIS Express HTTP endpoint in launchSettings.json.", + "symbols/iisHttpsPort/description": "Port number to use for the IIS Express HTTPS endpoint in launchSettings.json. This option is only applicable when the parameter no-https is not used (no-https will be ignored if Individual auth is used).", + "symbols/InteractivityPlatform/displayName": "_Interactive render mode", + "symbols/InteractivityPlatform/description": "Chooses which interactive render mode to use for interactive components", + "symbols/InteractivityPlatform/choices/None/displayName": "None", + "symbols/InteractivityPlatform/choices/None/description": "No interactivity (static server rendering only)", + "symbols/InteractivityPlatform/choices/Server/displayName": "Server", + "symbols/InteractivityPlatform/choices/Server/description": "Runs on the server", + "symbols/InteractivityPlatform/choices/WebAssembly/displayName": "WebAssembly", + "symbols/InteractivityPlatform/choices/WebAssembly/description": "Runs in the browser using WebAssembly", + "symbols/InteractivityPlatform/choices/Auto/displayName": "Auto (Server and WebAssembly)", + "symbols/InteractivityPlatform/choices/Auto/description": "Uses Server while downloading WebAssembly assets, then uses WebAssembly", + "symbols/InteractivityLocation/displayName": "_Interactivity location", + "symbols/InteractivityLocation/description": "Chooses which components will have interactive rendering enabled", + "symbols/InteractivityLocation/choices/InteractivePerPage/displayName": "Per page/component", + "symbols/InteractivityLocation/choices/InteractivePerPage/description": "Interactivity is applied on a per-page or per-component basis", + "symbols/InteractivityLocation/choices/InteractiveGlobal/displayName": "Global", + "symbols/InteractivityLocation/choices/InteractiveGlobal/description": "Interactivity is applied at the root level", + "symbols/IncludeSampleContent/displayName": "_Include sample pages", + "symbols/IncludeSampleContent/description": "Configures whether to add sample pages and styling to demonstrate basic usage patterns.", + "symbols/Empty/description": "Configures whether to omit sample pages and styling that demonstrate basic usage patterns.", + "symbols/auth/choices/None/description": "No authentication", + "symbols/auth/choices/Individual/description": "Individual authentication", + "symbols/auth/description": "The type of authentication to use", + "symbols/UseLocalDB/description": "Whether to use LocalDB instead of SQLite. This option only applies if --auth Individual is specified.", + "symbols/AllInteractive/displayName": "_Enable interactive rendering globally throughout the site", + "symbols/AllInteractive/description": "Configures whether to make every page interactive by applying an interactive render mode at the top level. If false, pages will use static server rendering by default, and can be marked interactive on a per-page or per-component basis.", + "symbols/NoHttps/description": "Whether to turn off HTTPS. This option only applies if Individual isn't used for --auth.", + "symbols/UseProgramMain/displayName": "Do not use _top-level statements", + "symbols/UseProgramMain/description": "Whether to generate an explicit Program class and Main method instead of top-level statements.", + "postActions/restore/description": "Restore NuGet packages required by this project.", + "postActions/restore/manualInstructions/default/text": "Run 'dotnet restore'" +} diff --git a/src/Templates/templates/blazorweb-csharp-8/.template.config/localize/templatestrings.es.json b/src/Templates/templates/blazorweb-csharp-8/.template.config/localize/templatestrings.es.json new file mode 100644 index 0000000000..15be2421e3 --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-8/.template.config/localize/templatestrings.es.json @@ -0,0 +1,44 @@ +{ + "author": "Microsoft", + "name": "Aplicación web Fluent Blazor", + "description": "Plantilla de proyecto para crear una aplicación web de Blazor que admita tanto la representación del lado del servidor como la interactividad del cliente. Esta plantilla se puede usar para las aplicaciones web con interfaces de usuario dinámicas enriquecidas.", + "symbols/Framework/description": "Marco de destino del proyecto.", + "symbols/Framework/choices/net8.0/description": "net8.0 de destino", + "symbols/UserSecretsId/description": "Identificador que se va a usar para los secretos (se usa con la autenticación individual).", + "symbols/skipRestore/description": "Si se especifica, se omite la restauración automática del proyecto durante la creación.", + "symbols/ExcludeLaunchSettings/description": "Indica si se va a excluir launchSettings.json de la plantilla generada.", + "symbols/kestrelHttpPort/description": "Número de puerto que se va a usar para el punto de conexión HTTP en launchSettings.json.", + "symbols/kestrelHttpsPort/description": "Número de puerto que se va a usar para el punto de conexión HTTPS en launchSettings.json. Esta opción solo es aplicable cuando no se usa el parámetro no-https (no-https se omitirá si se usa la autenticación individual).", + "symbols/iisHttpPort/description": "Número de puerto que se va a usar para el punto de conexión HTTP de IIS Express en launchSettings.json.", + "symbols/iisHttpsPort/description": "Número de puerto que se va a usar para el punto de conexión HTTPS de IIS Express en launchSettings.json. Esta opción solo es aplicable cuando no se usa el parámetro no-https (no-https se omitirá si se usa la autenticación individual).", + "symbols/InteractivityPlatform/displayName": "_Interactivity tipo", + "symbols/InteractivityPlatform/description": "Elige la plataforma de hospedaje que se va a usar para los componentes interactivos", + "symbols/InteractivityPlatform/choices/None/displayName": "Ninguno", + "symbols/InteractivityPlatform/choices/None/description": "Sin interactividad (solo representación de servidor estático)", + "symbols/InteractivityPlatform/choices/Server/displayName": "Servidor", + "symbols/InteractivityPlatform/choices/Server/description": "Se ejecuta en el servidor", + "symbols/InteractivityPlatform/choices/WebAssembly/displayName": "WebAssembly", + "symbols/InteractivityPlatform/choices/WebAssembly/description": "Se ejecuta en el explorador mediante WebAssembly", + "symbols/InteractivityPlatform/choices/Auto/displayName": "Automático (Servidor y WebAssembly)", + "symbols/InteractivityPlatform/choices/Auto/description": "Usa el servidor al descargar los activos de WebAssembly y, a continuación, usa WebAssembly", + "symbols/InteractivityLocation/displayName": "_Interactivity ubicación", + "symbols/InteractivityLocation/description": "Elige qué componentes tendrán habilitada la representación interactiva", + "symbols/InteractivityLocation/choices/InteractivePerPage/displayName": "Por página o componente", + "symbols/InteractivityLocation/choices/InteractivePerPage/description": "La interactividad se aplica por página o por componente", + "symbols/InteractivityLocation/choices/InteractiveGlobal/displayName": "Global", + "symbols/InteractivityLocation/choices/InteractiveGlobal/description": "La interactividad se aplica en el nivel raíz", + "symbols/IncludeSampleContent/displayName": "_Incluir páginas de ejemplo", + "symbols/IncludeSampleContent/description": "Configura si se van a agregar páginas de ejemplo y estilos para mostrar patrones de uso básicos.", + "symbols/Empty/description": "Configura si se omiten las páginas de ejemplo y los estilos que muestran patrones de uso básicos.", + "symbols/auth/choices/None/description": "Sin autenticación", + "symbols/auth/choices/Individual/description": "Autenticación individual", + "symbols/auth/description": "El tipo de autenticación que se va a usar", + "symbols/UseLocalDB/description": "Si se va a usar LocalDB en lugar de SQLite. Esta opción solo se aplica si se especifica --auth Individual.", + "symbols/AllInteractive/displayName": "_Enable representación interactiva globalmente en todo el sitio", + "symbols/AllInteractive/description": "Configura si todas las páginas son interactivas aplicando un modo de representación interactivo en el nivel superior. Si es false, las páginas usarán la representación de servidor estático de forma predeterminada y se pueden marcar como interactivas por página o por componente.", + "symbols/NoHttps/description": "Si se va a desactivar HTTPS. Esta opción solo se aplica si individual no se usa para --auth.", + "symbols/UseProgramMain/displayName": "No usar instrucciones de _nivel superior", + "symbols/UseProgramMain/description": "Indica si se debe generar una clase Program explícita y un método Main en lugar de instrucciones de nivel superior.", + "postActions/restore/description": "Restaure los paquetes NuGet necesarios para este proyecto.", + "postActions/restore/manualInstructions/default/text": "Ejecutar \"dotnet restore\"" +} diff --git a/src/Templates/templates/blazorweb-csharp-8/.template.config/localize/templatestrings.fr.json b/src/Templates/templates/blazorweb-csharp-8/.template.config/localize/templatestrings.fr.json new file mode 100644 index 0000000000..5381d58611 --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-8/.template.config/localize/templatestrings.fr.json @@ -0,0 +1,44 @@ +{ + "author": "Microsoft", + "name": "Application web Fluent Blazor", + "description": "Modèle de projet pour la création d’une application web Blazor qui prend en charge le rendu côté serveur et l’interactivité du client. Ce modèle peut être utilisé pour les applications web avec des interfaces utilisateur dynamiques enrichies.", + "symbols/Framework/description": "Framework cible du projet.", + "symbols/Framework/choices/net8.0/description": "Cible net8.0", + "symbols/UserSecretsId/description": "ID à utiliser pour les secrets (à utiliser avec l’authentification individuelle).", + "symbols/skipRestore/description": "S’il est spécifié, ignore la restauration automatique du projet lors de la création.", + "symbols/ExcludeLaunchSettings/description": "Indique s’il faut exclure launchSettings.json du modèle généré.", + "symbols/kestrelHttpPort/description": "Numéro de port à utiliser pour le point de terminaison HTTP dans launchSettings.json.", + "symbols/kestrelHttpsPort/description": "Numéro de port à utiliser pour le point de terminaison HTTPS dans launchSettings.json. Cette option s’applique uniquement lorsque le paramètre no-https n’est pas utilisé (no-https sera ignoré si l’authentification individuelle est utilisée).", + "symbols/iisHttpPort/description": "Numéro de port à utiliser pour le point de terminaison HTTP IIS Express dans launchSettings.json.", + "symbols/iisHttpsPort/description": "Numéro de port à utiliser pour le point de terminaison HTTPS IIS Express dans launchSettings.json. Cette option s’applique uniquement lorsque le paramètre no-https n’est pas utilisé (no-https sera ignoré si l’authentification individuelle est utilisée).", + "symbols/InteractivityPlatform/displayName": "type _Interactivity", + "symbols/InteractivityPlatform/description": "Choisit la plateforme d’hébergement à utiliser pour les composants interactifs", + "symbols/InteractivityPlatform/choices/None/displayName": "Aucun", + "symbols/InteractivityPlatform/choices/None/description": "Aucune interactivité (rendu de serveur statique uniquement)", + "symbols/InteractivityPlatform/choices/Server/displayName": "Serveur", + "symbols/InteractivityPlatform/choices/Server/description": "S’exécute sur le serveur", + "symbols/InteractivityPlatform/choices/WebAssembly/displayName": "WebAssembly", + "symbols/InteractivityPlatform/choices/WebAssembly/description": "S’exécute dans le navigateur à l’aide de WebAssembly", + "symbols/InteractivityPlatform/choices/Auto/displayName": "Automatique (Serveur et WebAssembly)", + "symbols/InteractivityPlatform/choices/Auto/description": "Utilise le serveur lors du téléchargement des ressources WebAssembly, puis utilise WebAssembly", + "symbols/InteractivityLocation/displayName": "emplacement _Interactivity", + "symbols/InteractivityLocation/description": "Choisit les composants pour lesquels le rendu interactif est activé", + "symbols/InteractivityLocation/choices/InteractivePerPage/displayName": "Par page/composant", + "symbols/InteractivityLocation/choices/InteractivePerPage/description": "L’interactivité est appliquée sur une base par page ou par composant", + "symbols/InteractivityLocation/choices/InteractiveGlobal/displayName": "Mondial", + "symbols/InteractivityLocation/choices/InteractiveGlobal/description": "L’interactivité est appliquée au niveau racine", + "symbols/IncludeSampleContent/displayName": "_Inclure des exemples de pages", + "symbols/IncludeSampleContent/description": "Configure s'il faut ajouter des exemples de pages et de style pour illustrer les modèles d'utilisation de base.", + "symbols/Empty/description": "Configure s'il faut omettre les exemples de pages et le style qui illustrent les modèles d'utilisation de base.", + "symbols/auth/choices/None/description": "Aucune authentification", + "symbols/auth/choices/Individual/description": "Authentification individuelle", + "symbols/auth/description": "Type d’authentification à utiliser", + "symbols/UseLocalDB/description": "Indique s’il faut utiliser LocalDB au lieu de SQLite. Cette option s’applique uniquement si --auth Individual est spécifié.", + "symbols/AllInteractive/displayName": "_Enable rendu interactif globalement sur l’ensemble du site", + "symbols/AllInteractive/description": "Configure si chaque page doit être interactive en appliquant un mode de rendu interactif au niveau supérieur. Si la valeur est False, les pages utilisent le rendu de serveur statique par défaut et peuvent être marquées comme interactives par page ou par composant.", + "symbols/NoHttps/description": "Indique s’il faut désactiver HTTPS. Cette option s’applique uniquement si Individual n’est pas utilisé pour --auth.", + "symbols/UseProgramMain/displayName": "N’utilisez pas _d’instructions de niveau supérieur.", + "symbols/UseProgramMain/description": "Indique s’il faut générer une classe Programme explicite et une méthode Main au lieu d’instructions de niveau supérieur.", + "postActions/restore/description": "Restaurez les packages NuGet requis par ce projet.", + "postActions/restore/manualInstructions/default/text": "Exécuter « dotnet restore »" +} diff --git a/src/Templates/templates/blazorweb-csharp-8/.template.config/localize/templatestrings.it.json b/src/Templates/templates/blazorweb-csharp-8/.template.config/localize/templatestrings.it.json new file mode 100644 index 0000000000..43c07a1456 --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-8/.template.config/localize/templatestrings.it.json @@ -0,0 +1,44 @@ +{ + "author": "Microsoft", + "name": "App Web Fluent Blazor", + "description": "Modello di progetto per la creazione di un'app Web Blazor che supporta sia il rendering lato server sia l'interattività client. Questo modello può essere usato per app Web con interfacce utente dinamiche avanzate.", + "symbols/Framework/description": "Il framework di destinazione per il progetto.", + "symbols/Framework/choices/net8.0/description": "Destinazione net8.0", + "symbols/UserSecretsId/description": "ID da usare per i segreti (usare con l'autenticazione Individual).", + "symbols/skipRestore/description": "Se specificato, ignora il ripristino automatico del progetto durante la creazione.", + "symbols/ExcludeLaunchSettings/description": "Indica se escludere launchSettings.json dal modello generato.", + "symbols/kestrelHttpPort/description": "Numero di porta da usare per l'endpoint HTTP in launchSettings.json.", + "symbols/kestrelHttpsPort/description": "Numero di porta da usare per l'endpoint HTTP in launchSettings.json. Questa opzione è applicabile solo quando il parametro no-https non viene usato (no-https verrà ignorato se si usa l'autenticazione Individual).", + "symbols/iisHttpPort/description": "Numero di porta da usare per l'endpoint HTTP in launchSettings.json.", + "symbols/iisHttpsPort/description": "Numero di porta da usare per l'endpoint HTTPS IIS Express in launchSettings.json. Questa opzione è applicabile solo quando il parametro no-https non viene usato (no-https verrà ignorato se si usa l'autenticazione Individual).", + "symbols/InteractivityPlatform/displayName": "tipo di _Interactivity", + "symbols/InteractivityPlatform/description": "Sceglie la piattaforma di hosting da usare per i componenti interattivi", + "symbols/InteractivityPlatform/choices/None/displayName": "Nessuno", + "symbols/InteractivityPlatform/choices/None/description": "Nessuna interattività (solo rendering server statico)", + "symbols/InteractivityPlatform/choices/Server/displayName": "Server", + "symbols/InteractivityPlatform/choices/Server/description": "Viene eseguito nel server", + "symbols/InteractivityPlatform/choices/WebAssembly/displayName": "WebAssembly", + "symbols/InteractivityPlatform/choices/WebAssembly/description": "Viene eseguito nel browser tramite WebAssembly", + "symbols/InteractivityPlatform/choices/Auto/displayName": "Automatico (Server e WebAssembly)", + "symbols/InteractivityPlatform/choices/Auto/description": "Usa Server durante il download degli asset WebAssembly, quindi usa WebAssembly", + "symbols/InteractivityLocation/displayName": "_Interactivity posizione", + "symbols/InteractivityLocation/description": "Sceglie quali componenti avranno il rendering interattivo abilitato", + "symbols/InteractivityLocation/choices/InteractivePerPage/displayName": "Per pagina/componente", + "symbols/InteractivityLocation/choices/InteractivePerPage/description": "L'interattività viene applicata per pagina o per componente", + "symbols/InteractivityLocation/choices/InteractiveGlobal/displayName": "Globale", + "symbols/InteractivityLocation/choices/InteractiveGlobal/description": "L'interattività viene applicata a livello radice", + "symbols/IncludeSampleContent/displayName": "_Include pagine di esempio", + "symbols/IncludeSampleContent/description": "Consente di configurare se aggiungere pagine di esempio e stile per mostrare modelli di utilizzo di base.", + "symbols/Empty/description": "Consente di configurare se omettere pagine di esempio e stile che mostrano modelli di utilizzo di base.", + "symbols/auth/choices/None/description": "Nessuna autenticazione", + "symbols/auth/choices/Individual/description": "Autenticazione Individual", + "symbols/auth/description": "Tipo di autenticazione da usare.", + "symbols/UseLocalDB/description": "Indica se usare LocalDB invece di SQLite. Questa opzione si applica solo se è specificato --auth Individual.", + "symbols/AllInteractive/displayName": "_Enable rendering interattivo a livello globale in tutto il sito", + "symbols/AllInteractive/description": "Configura se rendere interattiva ogni pagina applicando una modalità di rendering interattiva al livello superiore. Se false, le pagine useranno il rendering statico del server per impostazione predefinita e possono essere contrassegnate come interattive per pagina o per componente.", + "symbols/NoHttps/description": "Indica se disattivare HTTPS. Questa opzione si applica solo se Individual non viene usata per --auth.", + "symbols/UseProgramMain/displayName": "Non usare_istruzioni di primo livello", + "symbols/UseProgramMain/description": "Indica se generare una classe Program esplicita e un metodo Main anziché istruzioni di primo livello.", + "postActions/restore/description": "Ripristina i pacchetti NuGet richiesti da questo progetto.", + "postActions/restore/manualInstructions/default/text": "Esegui 'dotnet restore'" +} diff --git a/src/Templates/templates/blazorweb-csharp-8/.template.config/localize/templatestrings.ja.json b/src/Templates/templates/blazorweb-csharp-8/.template.config/localize/templatestrings.ja.json new file mode 100644 index 0000000000..8709631297 --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-8/.template.config/localize/templatestrings.ja.json @@ -0,0 +1,44 @@ +{ + "author": "Microsoft", + "name": "Fluent Blazor Web アプリ", + "description": "サーバー側のレンダリングとクライアントの対話機能の両方をサポートする Blazor Web アプリを作成するためのプロジェクト テンプレートです。このテンプレートは、リッチな動的ユーザー インターフェイス (UI) を持つ Web アプリに使用できます。", + "symbols/Framework/description": "プロジェクトのターゲット フレームワークです。", + "symbols/Framework/choices/net8.0/description": "ターゲット net8.0", + "symbols/UserSecretsId/description": "シークレットで使用する ID (IndividualAuth で使用)。", + "symbols/skipRestore/description": "指定した場合、作成時にプロジェクトの自動復元がスキップされます。", + "symbols/ExcludeLaunchSettings/description": "生成されたテンプレートから launchSettings.json を除外するかどうか。", + "symbols/kestrelHttpPort/description": "launchSettings.json の HTTP エンドポイントに使用するポート番号。", + "symbols/kestrelHttpsPort/description": "launchSettings.json で HTTPS エンドポイントに使用するポート番号。このオプションは、no-https パラメーターが使用されていない場合にのみ適用されます (IndividualAuth が使用されている場合は、no-https は無視されます)。", + "symbols/iisHttpPort/description": "launchSettings.json の IIS Express HTTP エンドポイントに使用するポート番号。", + "symbols/iisHttpsPort/description": "launchSettings.json で IIS Express HTTPS エンドポイントに使用するポート番号。このオプションは、no-https パラメーターが使用されていない場合にのみ適用されます (IndividualAuth が使用されている場合は、no-https は無視されます)。", + "symbols/InteractivityPlatform/displayName": "インタラクティビティ型(_I)", + "symbols/InteractivityPlatform/description": "対話型コンポーネントに使用するホスティング プラットフォームを選択します", + "symbols/InteractivityPlatform/choices/None/displayName": "なし", + "symbols/InteractivityPlatform/choices/None/description": "インタラクティビティなし (静的サーバー レンダリングのみ)", + "symbols/InteractivityPlatform/choices/Server/displayName": "サーバー", + "symbols/InteractivityPlatform/choices/Server/description": "サーバーで実行", + "symbols/InteractivityPlatform/choices/WebAssembly/displayName": "WebAssembly", + "symbols/InteractivityPlatform/choices/WebAssembly/description": "WebAssembly を使用してブラウザーで実行します", + "symbols/InteractivityPlatform/choices/Auto/displayName": "自動 (サーバーと WebAssembly)", + "symbols/InteractivityPlatform/choices/Auto/description": "WebAssembly 資産のダウンロード中にサーバーを使用してから、WebAssembly を使用します", + "symbols/InteractivityLocation/displayName": "インタラクティビティ場所(_I)", + "symbols/InteractivityLocation/description": "対話型レンダリングを有効にするコンポーネントを選択します", + "symbols/InteractivityLocation/choices/InteractivePerPage/displayName": "ページ/コンポーネントごと", + "symbols/InteractivityLocation/choices/InteractivePerPage/description": "インタラクティビティは、ページ単位またはコンポーネント単位で適用されます", + "symbols/InteractivityLocation/choices/InteractiveGlobal/displayName": "グローバル", + "symbols/InteractivityLocation/choices/InteractiveGlobal/description": "インタラクティビティはルート レベルで適用されます", + "symbols/IncludeSampleContent/displayName": "サンプル ページを含める(_I)", + "symbols/IncludeSampleContent/description": "基本的な使用パターンを示すサンプル ページとスタイルを追加するかどうかを構成します。", + "symbols/Empty/description": "基本的な使用パターンを示すサンプル ページとスタイルを省略するかどうかを構成します。", + "symbols/auth/choices/None/description": "認証なし", + "symbols/auth/choices/Individual/description": "個別の認証", + "symbols/auth/description": "使用する認証の種類", + "symbols/UseLocalDB/description": "SQLite の代わりに LocalDB を使用するかどうか。このオプションは、--auth Individual が指定されている場合にのみ適用されます。", + "symbols/AllInteractive/displayName": "インタラクティビティ レンダリングをサイト全体でグローバルに有効化(_E)", + "symbols/AllInteractive/description": "最上位レベルで対話型レンダリング モードを適用して、すべてのページを対話型にするかどうかを構成します。false の場合、ページは既定で静的サーバー レンダリングを使用し、ページ単位またはコンポーネント単位で対話型としてマークできます。", + "symbols/NoHttps/description": "HTTPS をオフにするかどうか。このオプションは、Individual が --auth に使用されていない場合にのみ適用されます。", + "symbols/UseProgramMain/displayName": "最上位レベルのステートメントを使用しない(_T)", + "symbols/UseProgramMain/description": "最上位レベルのステートメントではなく、明示的な Program クラスと Main メソッドを生成するかどうか。", + "postActions/restore/description": "このプロジェクトに必要な NuGet パッケージを復元します。", + "postActions/restore/manualInstructions/default/text": "'dotnet restore' を実行する" +} diff --git a/src/Templates/templates/blazorweb-csharp-8/.template.config/localize/templatestrings.ko.json b/src/Templates/templates/blazorweb-csharp-8/.template.config/localize/templatestrings.ko.json new file mode 100644 index 0000000000..85b840abff --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-8/.template.config/localize/templatestrings.ko.json @@ -0,0 +1,44 @@ +{ + "author": "Microsoft", + "name": "Fluent Blazor 웹앱", + "description": "서버 측 렌더링 및 클라이언트 대화형 작업을 모두 지원하는 Blazor 웹앱을 만들기 위한 프로젝트 템플릿입니다. 이 템플릿은 풍부한 동적 UI(사용자 인터페이스)가 있는 웹앱에 사용할 수 있습니다.", + "symbols/Framework/description": "프로젝트에 대한 대상 프레임워크입니다.", + "symbols/Framework/choices/net8.0/description": "대상 net8.0", + "symbols/UserSecretsId/description": "비밀에 사용할 ID입니다(개별 인증과 함께 사용).", + "symbols/skipRestore/description": "지정된 경우, 프로젝트 생성 시 자동 복원을 건너뜁니다.", + "symbols/ExcludeLaunchSettings/description": "생성된 템플릿에서 launchSettings.json을 제외할지 여부입니다.", + "symbols/kestrelHttpPort/description": "launchSettings.json의 HTTP 엔드포인트에 사용할 포트 번호입니다.", + "symbols/kestrelHttpsPort/description": "launchSettings.json의 HTTPS 엔드포인트에 사용할 포트 번호입니다. 이 옵션은 매개 변수 no-https가 사용되지 않은 경우에만 적용됩니다(개별 인증을 사용하는 경우 no-https가 무시됨).", + "symbols/iisHttpPort/description": "launchSettings.json의 IIS Express HTTP 엔드포인트에 사용할 포트 번호입니다.", + "symbols/iisHttpsPort/description": "launchSettings.json의 IIS Express HTTPS 엔드포인트에 사용할 포트 번호입니다. 이 옵션은 매개 변수 no-https가 사용되지 않은 경우에만 적용됩니다(개별 인증을 사용하는 경우 no-https가 무시됨).", + "symbols/InteractivityPlatform/displayName": "_Interactivity 유형", + "symbols/InteractivityPlatform/description": "대화형 구성 요소에 사용할 호스팅 플랫폼을 선택합니다.", + "symbols/InteractivityPlatform/choices/None/displayName": "없음", + "symbols/InteractivityPlatform/choices/None/description": "대화형 작업 없음(정적 서버 렌더링만 해당)", + "symbols/InteractivityPlatform/choices/Server/displayName": "서버", + "symbols/InteractivityPlatform/choices/Server/description": "서버에서 실행", + "symbols/InteractivityPlatform/choices/WebAssembly/displayName": "WebAssembly", + "symbols/InteractivityPlatform/choices/WebAssembly/description": "WebAssembly를 사용하여 브라우저에서 실행", + "symbols/InteractivityPlatform/choices/Auto/displayName": "자동(Server 및 WebAssembly)", + "symbols/InteractivityPlatform/choices/Auto/description": "WebAssembly 자산을 다운로드하는 동안 서버를 사용한 다음, WebAssembly를 사용합니다.", + "symbols/InteractivityLocation/displayName": "_Interactivity 위치", + "symbols/InteractivityLocation/description": "대화형 렌더링을 사용하도록 설정할 구성 요소를 선택합니다.", + "symbols/InteractivityLocation/choices/InteractivePerPage/displayName": "페이지/구성 요소당", + "symbols/InteractivityLocation/choices/InteractivePerPage/description": "상호 작용은 페이지별 또는 구성 요소별로 적용됩니다.", + "symbols/InteractivityLocation/choices/InteractiveGlobal/displayName": "글로벌", + "symbols/InteractivityLocation/choices/InteractiveGlobal/description": "대화형 작업은 루트 수준에서 적용됩니다.", + "symbols/IncludeSampleContent/displayName": "샘플 페이지 포함(_I)", + "symbols/IncludeSampleContent/description": "기본 사용 패턴을 보여주기 위해 샘플 페이지 및 스타일을 추가할지 여부를 구성합니다.", + "symbols/Empty/description": "기본 사용 패턴을 보여주는 샘플 페이지 및 스타일을 생략할지 여부를 구성합니다.", + "symbols/auth/choices/None/description": "인증 없음", + "symbols/auth/choices/Individual/description": "개별 인증", + "symbols/auth/description": "사용할 인증 유형", + "symbols/UseLocalDB/description": "SQLite 대신 LocalDB를 사용할지 여부입니다. 이 옵션은 --auth Individual이 지정된 경우에만 적용됩니다.", + "symbols/AllInteractive/displayName": "_사이트 전체에서 전역적으로 인터랙티브 렌더링 활성화", + "symbols/AllInteractive/description": "최상위 수준에서 대화형 렌더링 모드를 적용하여 모든 페이지를 대화형으로 설정할지 여부를 구성합니다. 만약 false이면 페이지는 기본적으로 정적 서버 렌더링을 사용하며 페이지별 또는 구성 요소별로 대화형으로 표시될 수 있습니다.", + "symbols/NoHttps/description": "HTTPS를 끌지 여부입니다. 이 옵션은 개별 항목이 --auth에 사용되지 않는 경우에만 적용됩니다.", + "symbols/UseProgramMain/displayName": "최상위 문 사용 안 함(_T)", + "symbols/UseProgramMain/description": "최상위 문 대신 명시적 Program 클래스 및 Main 메서드를 생성할지 여부입니다.", + "postActions/restore/description": "이 프로젝트에 필요한 NuGet 패키지를 복원합니다.", + "postActions/restore/manualInstructions/default/text": "'dotnet restore' 실행" +} diff --git a/src/Templates/templates/blazorweb-csharp-8/.template.config/localize/templatestrings.pl.json b/src/Templates/templates/blazorweb-csharp-8/.template.config/localize/templatestrings.pl.json new file mode 100644 index 0000000000..fbcc46dcf7 --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-8/.template.config/localize/templatestrings.pl.json @@ -0,0 +1,44 @@ +{ + "author": "Microsoft", + "name": "Aplikacja internetowa Fluent Blazor", + "description": "Szablon projektu służący do tworzenia aplikacji internetowej platformy Blazor, która obsługuje renderowanie po stronie serwera i interakcyjność klienta. Ten szablon może być używany dla aplikacji internetowych z zaawansowanymi dynamicznymi interfejsami użytkownika.", + "symbols/Framework/description": "Platforma docelowa dla tego projektu.", + "symbols/Framework/choices/net8.0/description": "Docelowa platforma net8.0", + "symbols/UserSecretsId/description": "Identyfikator do użycia dla wpisów tajnych (używany z indywidualnym uwierzytelnianym).", + "symbols/skipRestore/description": "Jeśli ta opcja jest określona, pomija automatyczne przywracanie projektu podczas tworzenia.", + "symbols/ExcludeLaunchSettings/description": "Określa, czy wykluczyć plik launchSettings.json z wygenerowanego szablonu.", + "symbols/kestrelHttpPort/description": "Numer portu do użycia dla punktu końcowego HTTP w pliku launchSettings.json.", + "symbols/kestrelHttpsPort/description": "Numer portu do użycia dla punktu końcowego HTTPS w pliku launchSettings.json. Ta opcja ma zastosowanie tylko wtedy, gdy parametr no-https nie jest używany (no-https będzie ignorowany, jeśli używany jest Individual auth — indywidualny uwierzytelniony dostęp).", + "symbols/iisHttpPort/description": "Numer portu do użycia dla punktu końcowego HTTP usług IIS Express w pliku launchSettings.json.", + "symbols/iisHttpsPort/description": "Numer portu do użycia dla punktu końcowego HTTPS programu IIS Express w pliku launchSettings.json. Ta opcja ma zastosowanie tylko wtedy, gdy parametr no-https nie jest używany (no-https będzie ignorowany, jeśli używany jest Individual auth — indywidualny uwierzytelniony dostęp).", + "symbols/InteractivityPlatform/displayName": "_Typy interakcyjności", + "symbols/InteractivityPlatform/description": "Wybiera platformę hostingu, która ma być używana na potrzeby składników interakcyjnych", + "symbols/InteractivityPlatform/choices/None/displayName": "Brak", + "symbols/InteractivityPlatform/choices/None/description": "Brak interakcyjności (tylko renderowanie serwera statycznego)", + "symbols/InteractivityPlatform/choices/Server/displayName": "Serwer", + "symbols/InteractivityPlatform/choices/Server/description": "Działa na serwerze", + "symbols/InteractivityPlatform/choices/WebAssembly/displayName": "WebAssembly", + "symbols/InteractivityPlatform/choices/WebAssembly/description": "Działa w przeglądarce przy użyciu elementu WebAssembly", + "symbols/InteractivityPlatform/choices/Auto/displayName": "Automatycznie (Server i WebAssembly)", + "symbols/InteractivityPlatform/choices/Auto/description": "Używa serwera podczas pobierania zasobów WebAssembly, a następnie używa elementu WebAssembly", + "symbols/InteractivityLocation/displayName": "_Lokalizacja interaktywności", + "symbols/InteractivityLocation/description": "Wybiera, które składniki będą miały włączone renderowanie interakcyjne", + "symbols/InteractivityLocation/choices/InteractivePerPage/displayName": "Na stronę/składnik", + "symbols/InteractivityLocation/choices/InteractivePerPage/description": "Interakcyjność jest stosowana dla poszczególnych stron lub składników", + "symbols/InteractivityLocation/choices/InteractiveGlobal/displayName": "Globalne", + "symbols/InteractivityLocation/choices/InteractiveGlobal/description": "Interakcyjność jest stosowana na poziomie głównym", + "symbols/IncludeSampleContent/displayName": "_Dołącz przykładowe strony", + "symbols/IncludeSampleContent/description": "Konfiguruje, czy dodać przykładowe strony i style w celu zademonstrowania podstawowych wzorców użycia.", + "symbols/Empty/description": "Konfiguruje, czy pomijać przykładowe strony i style demonstrujące podstawowe wzorce użycia.", + "symbols/auth/choices/None/description": "Bez uwierzytelniania", + "symbols/auth/choices/Individual/description": "Uwierzytelnianie indywidualne", + "symbols/auth/description": "Typ uwierzytelniania, który ma zostać użyty.", + "symbols/UseLocalDB/description": "Określa, czy używać bazy danych LocalDB zamiast oprogramowania SQLite. Ta opcja ma zastosowanie tylko wtedy, gdy określono parametr --auth Individual.", + "symbols/AllInteractive/displayName": "_Włącz globalnie renderowanie interakcyjne w całej witrynie", + "symbols/AllInteractive/description": "Konfiguruje, czy każda strona ma być interakcyjna, stosując interakcyjny tryb renderowania na najwyższym poziomie. W przypadku wartości false strony będą domyślnie używać statycznego renderowania serwera i mogą być oznaczone jako interakcyjne dla poszczególnych stron lub składników.", + "symbols/NoHttps/description": "Określa, czy wyłączyć protokół HTTPS. Ta opcja ma zastosowanie tylko wtedy, gdy opcja Individual nie została użyta dla opcji --auth.", + "symbols/UseProgramMain/displayName": "Nie używaj ins_trukcji najwyższego poziomu", + "symbols/UseProgramMain/description": "Określa, czy wygenerować jawną klasę Program i metodę Main zamiast instrukcji najwyższego poziomu.", + "postActions/restore/description": "Przywróć pakiety NuGet wymagane przez ten projekt.", + "postActions/restore/manualInstructions/default/text": "Uruchom polecenie \"dotnet restore\"" +} diff --git a/src/Templates/templates/blazorweb-csharp-8/.template.config/localize/templatestrings.pt-BR.json b/src/Templates/templates/blazorweb-csharp-8/.template.config/localize/templatestrings.pt-BR.json new file mode 100644 index 0000000000..a66bdb1e8f --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-8/.template.config/localize/templatestrings.pt-BR.json @@ -0,0 +1,44 @@ +{ + "author": "Microsoft", + "name": "Aplicativo Web Fluent Blazor", + "description": "Um modelo de projeto para criar um aplicativo Web Blazor que dá suporte à renderização do lado do servidor e à interatividade do cliente. Este modelo pode ser usado para aplicativos da Web com interfaces de usuário (UIs) dinâmicas avançadas.", + "symbols/Framework/description": "A estrutura de destino do projeto.", + "symbols/Framework/choices/net8.0/description": "net8.0 de destino", + "symbols/UserSecretsId/description": "O ID a ser usado para segredos (usado com autenticação individual).", + "symbols/skipRestore/description": "Se especificado, ignora a restauração automática do projeto sendo criado.", + "symbols/ExcludeLaunchSettings/description": "Se deve excluir launchSettings.json do modelo gerado.", + "symbols/kestrelHttpPort/description": "Número da porta a ser usada para o ponto de extremidade HTTP em launchSettings.json.", + "symbols/kestrelHttpsPort/description": "Número da porta a ser usada para o ponto de extremidade HTTPS em launchSettings.json. Esta opção só é aplicável quando o parâmetro no-https não é utilizado (no-https será ignorado se for utilizada autenticação individual).", + "symbols/iisHttpPort/description": "Número da porta a ser usada para o ponto de extremidade HTTP do IIS Express em launchSettings.json.", + "symbols/iisHttpsPort/description": "Número da porta a ser usada para o ponto de extremidade HTTPS do IIS Express em launchSettings.json. Esta opção só é aplicável quando o parâmetro no-https não é utilizado (no-https será ignorado se for utilizada autenticação individual).", + "symbols/InteractivityPlatform/displayName": "_Tipo de interatividade", + "symbols/InteractivityPlatform/description": "Escolhe qual plataforma de hospedagem usar com os componentes interativos", + "symbols/InteractivityPlatform/choices/None/displayName": "Nenhum", + "symbols/InteractivityPlatform/choices/None/description": "Sem interatividade (somente renderização do servidor estático)", + "symbols/InteractivityPlatform/choices/Server/displayName": "Servidor", + "symbols/InteractivityPlatform/choices/Server/description": "É executado no servidor", + "symbols/InteractivityPlatform/choices/WebAssembly/displayName": "WebAssembly", + "symbols/InteractivityPlatform/choices/WebAssembly/description": "É executado no navegador usando o WebAssembly", + "symbols/InteractivityPlatform/choices/Auto/displayName": "Automático (Servidor e WebAssembly)", + "symbols/InteractivityPlatform/choices/Auto/description": "Usa Servidor ao baixar os ativos WebAssembly e usa o WebAssembly", + "symbols/InteractivityLocation/displayName": "_Interatividade local", + "symbols/InteractivityLocation/description": "Escolhe quais componentes terão a renderização interativa habilitada", + "symbols/InteractivityLocation/choices/InteractivePerPage/displayName": "Por página/componente", + "symbols/InteractivityLocation/choices/InteractivePerPage/description": "A interatividade é aplicada por página ou por componente", + "symbols/InteractivityLocation/choices/InteractiveGlobal/displayName": "Global", + "symbols/InteractivityLocation/choices/InteractiveGlobal/description": "A interatividade é aplicada no nível raiz", + "symbols/IncludeSampleContent/displayName": "_Incluir páginas de amostra", + "symbols/IncludeSampleContent/description": "Configura se deseja adicionar páginas de amostra e estilo para demonstrar padrões de uso básicos.", + "symbols/Empty/description": "Configura a omissão de páginas de amostra e estilo que demonstram padrões básicos de uso.", + "symbols/auth/choices/None/description": "Sem autenticação", + "symbols/auth/choices/Individual/description": "Autenticação individual", + "symbols/auth/description": "O tipo de autenticação a ser usado", + "symbols/UseLocalDB/description": "Se deve usar LocalDB em vez de SQLite. Esta opção só se aplica se --auth Individual for especificado.", + "symbols/AllInteractive/displayName": "_Habilitar renderização interativa globalmente no site", + "symbols/AllInteractive/description": "Configura se todas as páginas devem ser interativas aplicando um modo de renderização interativo no nível superior. Se for falso, as páginas usarão a renderização do servidor estático por padrão e poderão ser marcadas como interativas por página ou por componente.", + "symbols/NoHttps/description": "Se deve desligar o HTTPS. Essa opção só se aplica se Individual não for usado para --auth.", + "symbols/UseProgramMain/displayName": "Não use ins_truções de nível superior", + "symbols/UseProgramMain/description": "Se deve gerar uma classe de Programa explícita e um método principal em vez de instruções de nível superior.", + "postActions/restore/description": "Restaure os pacotes NuGet exigidos por este projeto.", + "postActions/restore/manualInstructions/default/text": "Executa 'dotnet restore'" +} diff --git a/src/Templates/templates/blazorweb-csharp-8/.template.config/localize/templatestrings.ru.json b/src/Templates/templates/blazorweb-csharp-8/.template.config/localize/templatestrings.ru.json new file mode 100644 index 0000000000..dd831b5d4b --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-8/.template.config/localize/templatestrings.ru.json @@ -0,0 +1,44 @@ +{ + "author": "Майкрософт", + "name": "Веб-приложение Fluent Blazor", + "description": "Шаблон проекта для создания приложения Blazor, поддерживающего как отрисовку на стороне сервера, так и интерактивные возможности клиента. Этот шаблон можно использовать для веб-приложений с многофункциональными динамическими пользовательскими интерфейсами (UI).", + "symbols/Framework/description": "Целевая платформа для проекта.", + "symbols/Framework/choices/net8.0/description": "Целевая net8.0", + "symbols/UserSecretsId/description": "Идентификатор, используемый для секретов (применяется с индивидуальной проверкой подлинности).", + "symbols/skipRestore/description": "Если установлено, автоматическое восстановление проекта при создании пропускается.", + "symbols/ExcludeLaunchSettings/description": "Следует ли исключить launchSettings.json из созданного шаблона.", + "symbols/kestrelHttpPort/description": "Номер порта, используемый для конечной точки HTTP в launchSettings.json.", + "symbols/kestrelHttpsPort/description": "Номер порта, используемый для конечной точки HTTPS в launchSettings.json. Этот параметр применим только в том случае, если no-https не используется (no-https игнорируется при использовании индивидуальной проверки подлинности).", + "symbols/iisHttpPort/description": "Номер порта, используемый для конечной точки HTTP IIS Express в launchSettings.json.", + "symbols/iisHttpsPort/description": "Номер порта, используемый для конечной точки HTTPS IIS Express в launchSettings.json. Этот параметр применим только в том случае, если no-https не используется (no-https игнорируется при использовании индивидуальной проверки подлинности).", + "symbols/InteractivityPlatform/displayName": "Тип _интерактивности", + "symbols/InteractivityPlatform/description": "Выбор платформы размещения для интерактивных компонентов", + "symbols/InteractivityPlatform/choices/None/displayName": "Нет", + "symbols/InteractivityPlatform/choices/None/description": "Без взаимодействия (только отрисовка статического сервера)", + "symbols/InteractivityPlatform/choices/Server/displayName": "Сервер", + "symbols/InteractivityPlatform/choices/Server/description": "Выполняется на сервере", + "symbols/InteractivityPlatform/choices/WebAssembly/displayName": "WebAssembly", + "symbols/InteractivityPlatform/choices/WebAssembly/description": "Запуск в браузере с помощью WebAssembly", + "symbols/InteractivityPlatform/choices/Auto/displayName": "Авто (Сервер и WebAssembly)", + "symbols/InteractivityPlatform/choices/Auto/description": "Использует сервер при загрузке ресурсов WebAssembly, затем использует WebAssembly.", + "symbols/InteractivityLocation/displayName": "расположение _интерактивности", + "symbols/InteractivityLocation/description": "Выбор компонентов, для которых будет включена интерактивная отрисовка", + "symbols/InteractivityLocation/choices/InteractivePerPage/displayName": "На страницу или компонент", + "symbols/InteractivityLocation/choices/InteractivePerPage/description": "Интерактивность применяется для каждой страницы или для каждого компонента", + "symbols/InteractivityLocation/choices/InteractiveGlobal/displayName": "Глобальный", + "symbols/InteractivityLocation/choices/InteractiveGlobal/description": "Интерактивность применяется на корневом уровне", + "symbols/IncludeSampleContent/displayName": "_Включить примеры страниц", + "symbols/IncludeSampleContent/description": "Настраивает, следует ли добавлять примеры страниц и стили для демонстрации базовых шаблонов использования.", + "symbols/Empty/description": "Настраивает, следует ли пропускать примеры страниц и стили, демонстрирующие базовые шаблоны использования.", + "symbols/auth/choices/None/description": "Без проверки подлинности", + "symbols/auth/choices/Individual/description": "Индивидуальная проверка подлинности", + "symbols/auth/description": "Тип используемой проверки подлинности", + "symbols/UseLocalDB/description": "Следует ли использовать LocalDB вместо SQLite. Этот параметр применяется, только если указывается --auth Individual.", + "symbols/AllInteractive/displayName": "_Включить интерактивную отрисовку по всему сайту", + "symbols/AllInteractive/description": "Определяет, делать ли каждую страницу интерактивной, применяя интерактивный режим отрисовки на верхнем уровне. Если установлено значение ЛОЖЬ, страницы по умолчанию будут использовать статическую серверную отрисовку и могут быть помечены как интерактивные для каждой страницы или для каждого компонента.", + "symbols/NoHttps/description": "Следует ли отключить HTTPS. Этот параметр применяется только в том случае, если для аргумента --auth не используется значение Individual.", + "symbols/UseProgramMain/displayName": "Не использовать _операторы верхнего уровня", + "symbols/UseProgramMain/description": "Следует ли создавать явный класс Program и метод Main вместо операторов верхнего уровня.", + "postActions/restore/description": "Восстановление пакетов NuGet, необходимых для этого проекта.", + "postActions/restore/manualInstructions/default/text": "Выполнить команду \"dotnet restore\"" +} diff --git a/src/Templates/templates/blazorweb-csharp-8/.template.config/localize/templatestrings.tr.json b/src/Templates/templates/blazorweb-csharp-8/.template.config/localize/templatestrings.tr.json new file mode 100644 index 0000000000..2646cf047a --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-8/.template.config/localize/templatestrings.tr.json @@ -0,0 +1,44 @@ +{ + "author": "Microsoft", + "name": "Fluent Blazor Web Uygulaması", + "description": "Hem sunucu tarafı işlemeyi hem de istemci etkileşimini destekleyen bir Blazor web uygulaması oluşturmaya yönelik proje şablonu. Bu şablon, zengin dinamik kullanıcı arabirimlerine (UI) sahip web uygulamaları için kullanılabilir.", + "symbols/Framework/description": "Projenin hedef çerçevesi.", + "symbols/Framework/choices/net8.0/description": "Hedef net8.0", + "symbols/UserSecretsId/description": "Gizli diziler için kullanılacak kimlik (Bireysel kimlik doğrulamayla kullanın).", + "symbols/skipRestore/description": "Belirtilirse, oluşturma sırasında projenin otomatik geri yüklenmesini atlar.", + "symbols/ExcludeLaunchSettings/description": "launchSettings.json öğesinin oluşturulan şablondan dışlanıp dışlanmayacağı.", + "symbols/kestrelHttpPort/description": "launchSettings.json içinde HTTP uç noktası için kullanılacak bağlantı noktası numarası.", + "symbols/kestrelHttpsPort/description": "launchSettings.json içinde HTTPS uç noktası için kullanılacak bağlantı noktası numarası. Bu seçenek yalnızca no-https parametresi kullanılmadığında geçerlidir (Bireysel kimlik doğrulama kullanılırsa no-https göz ardı edilecektir).", + "symbols/iisHttpPort/description": "launchSettings.json içinde IIS Express HTTP uç noktası için kullanılacak bağlantı noktası numarası.", + "symbols/iisHttpsPort/description": "launchSettings.json içinde IIS Express HTTPS uç noktası için kullanılacak bağlantı noktası numarası. Bu seçenek yalnızca no-https parametresi kullanılmadığında geçerlidir (Bireysel kimlik doğrulama kullanılırsa no-https göz ardı edilecektir).", + "symbols/InteractivityPlatform/displayName": "_Etkileşim türü", + "symbols/InteractivityPlatform/description": "Etkileşimli bileşenler için kullanılacak barındırma platformunu seçer", + "symbols/InteractivityPlatform/choices/None/displayName": "Yok", + "symbols/InteractivityPlatform/choices/None/description": "Etkileşim yok (yalnızca statik sunucu işleme)", + "symbols/InteractivityPlatform/choices/Server/displayName": "Server", + "symbols/InteractivityPlatform/choices/Server/description": "Sunucuda çalıştırılır", + "symbols/InteractivityPlatform/choices/WebAssembly/displayName": "WebAssembly", + "symbols/InteractivityPlatform/choices/WebAssembly/description": "WebAssembly kullanarak tarayıcıda çalışır", + "symbols/InteractivityPlatform/choices/Auto/displayName": "Otomatik (Server ve WebAssembly)", + "symbols/InteractivityPlatform/choices/Auto/description": "WebAssembly varlıkları indirilirken Server kullanır, ardından WebAssembly'i kullanır", + "symbols/InteractivityLocation/displayName": "_Etkileşimin konumu", + "symbols/InteractivityLocation/description": "Etkileşimli işlemenin etkinleştirileceği bileşenleri belirler", + "symbols/InteractivityLocation/choices/InteractivePerPage/displayName": "Sayfa/bileşen başına", + "symbols/InteractivityLocation/choices/InteractivePerPage/description": "Etkileşim sayfa başına veya bileşen başına temelinde uygulanır", + "symbols/InteractivityLocation/choices/InteractiveGlobal/displayName": "Genel", + "symbols/InteractivityLocation/choices/InteractiveGlobal/description": "Etkileşim kök düzeyinde uygulanır", + "symbols/IncludeSampleContent/displayName": "Örnek _sayfalar ekle", + "symbols/IncludeSampleContent/description": "Temel kullanım düzenlerini göstermek için örnek sayfaların ve stil oluşturma özelliklerinin eklenip eklenmeyeceğini yapılandırır.", + "symbols/Empty/description": "Temel kullanım düzenlerini gösteren örnek sayfaların ve stil oluşturma özelliklerinin atlanıp atlanmayacağını yapılandırır.", + "symbols/auth/choices/None/description": "Kimlik doğrulaması yok", + "symbols/auth/choices/Individual/description": "Bireysel kimlik doğrulaması", + "symbols/auth/description": "Kullanılacak kimlik doğrulaması türü", + "symbols/UseLocalDB/description": "SQLite yerine LocalDB'nin kullanılıp kullanılmayacağı. Bu seçenek yalnızca --auth Bireysel belirtildiğinde geçerlidir.", + "symbols/AllInteractive/displayName": "_Site genelinde etkileşimli işlemeyi genel olarak etkinleştirin", + "symbols/AllInteractive/description": "En üst düzeyde etkileşimli bir işleme modu uygulayarak her sayfanın etkileşimli olup olmayacağını yapılandırır. False ise sayfalar varsayılan olarak statik sunucu işleme kullanır ve sayfa başına veya bileşen başına temelinde etkileşimli olarak işaretlenebilir.", + "symbols/NoHttps/description": "HTTPS'nin kapatılıp kapatılmayacağı. Bu seçenek yalnızca Bireysel --auth için kullanılmadığında geçerlidir.", + "symbols/UseProgramMain/displayName": "_Üst düzey deyimler kullanmayın", + "symbols/UseProgramMain/description": "Üst düzey deyimler yerine açık bir Program sınıfı ve Ana yöntem oluşturup oluşturulmayacağını belirtir.", + "postActions/restore/description": "Bu projenin gerektirdiği NuGet paketlerini geri yükleyin.", + "postActions/restore/manualInstructions/default/text": "'dotnet restore' çalıştır" +} diff --git a/src/Templates/templates/blazorweb-csharp-8/.template.config/localize/templatestrings.zh-Hans.json b/src/Templates/templates/blazorweb-csharp-8/.template.config/localize/templatestrings.zh-Hans.json new file mode 100644 index 0000000000..7d118564ba --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-8/.template.config/localize/templatestrings.zh-Hans.json @@ -0,0 +1,44 @@ +{ + "author": "Microsoft", + "name": "Fluent Blazor Web 应用", + "description": "用于创建支持服务器端呈现和客户端交互的 Blazor Web 应用的项目模板。此模板可用于具有丰富动态用户界面 (UI) 的 Web 应用。", + "symbols/Framework/description": "项目的目标框架。", + "symbols/Framework/choices/net8.0/description": "目标 net8.0", + "symbols/UserSecretsId/description": "用于机密的 ID (与个人身份验证一起使用)。", + "symbols/skipRestore/description": "如果指定,则在创建时跳过项目的自动还原。", + "symbols/ExcludeLaunchSettings/description": "是否从生成的模板中排除 launchSettings.json。", + "symbols/kestrelHttpPort/description": "要用于 launchSettings.json 中 HTTP 终结点的端口号。", + "symbols/kestrelHttpsPort/description": "要用于 launchSettings.json 中 HTTPS 终结点的端口号。仅当不使用参数 no-https 时,此选项才适用(如果使用 Induvidual 身份验证,则将忽略 no-https)。", + "symbols/iisHttpPort/description": "要用于 launchSettings.json 中 IIS Express HTTP 终结点的端口号。", + "symbols/iisHttpsPort/description": "要用于 launchSettings.json 中 IIS Express HTTPS 终结点的端口号。仅当不使用参数 no-https 时,此选项才适用(如果使用 Induvidual 身份验证,则将忽略 no-https)。", + "symbols/InteractivityPlatform/displayName": "_交互性类型", + "symbols/InteractivityPlatform/description": "选择交互式组件要使用的托管平台", + "symbols/InteractivityPlatform/choices/None/displayName": "无", + "symbols/InteractivityPlatform/choices/None/description": "没有交互性 (仅限静态服务器呈现)", + "symbols/InteractivityPlatform/choices/Server/displayName": "服务器", + "symbols/InteractivityPlatform/choices/Server/description": "在服务器上运行", + "symbols/InteractivityPlatform/choices/WebAssembly/displayName": "WebAssembly", + "symbols/InteractivityPlatform/choices/WebAssembly/description": "使用 WebAssembly 在浏览器中运行", + "symbols/InteractivityPlatform/choices/Auto/displayName": "自动 (服务器和 WebAssembly)", + "symbols/InteractivityPlatform/choices/Auto/description": "下载 WebAssembly 资产时使用服务器,然后使用 WebAssembly", + "symbols/InteractivityLocation/displayName": "_交互性位置", + "symbols/InteractivityLocation/description": "选择将启用交互式呈现的组件", + "symbols/InteractivityLocation/choices/InteractivePerPage/displayName": "每页/组件", + "symbols/InteractivityLocation/choices/InteractivePerPage/description": "基于每页或每组件应用交互", + "symbols/InteractivityLocation/choices/InteractiveGlobal/displayName": "全局", + "symbols/InteractivityLocation/choices/InteractiveGlobal/description": "在根级别应用交互性", + "symbols/IncludeSampleContent/displayName": "包含示例页(_I)", + "symbols/IncludeSampleContent/description": "配置是否添加示例页和样式以演示基本使用模式。", + "symbols/Empty/description": "配置是否忽略演示基本使用模式的示例页和样式。", + "symbols/auth/choices/None/description": "无身份验证", + "symbols/auth/choices/Individual/description": "个人身份验证", + "symbols/auth/description": "要使用的身份验证类型", + "symbols/UseLocalDB/description": "是否使用 LocalDB 而不是 SQLite。仅当指定了 --auth Individual 时,此选项才适用。", + "symbols/AllInteractive/displayName": "_在整个网站全局启用交互式呈现", + "symbols/AllInteractive/description": "配置是否通过在顶层应用交互式呈现模式来使每个页面都交互。如果为 false,则默认情况下,页面将使用静态服务器呈现,并且可以按每页或每组件标记为交互。", + "symbols/NoHttps/description": "是否关闭 HTTPS。仅当 Individual 不用于 --auth 时,此选项才适用。", + "symbols/UseProgramMain/displayName": "不使用顶级语句(_T)", + "symbols/UseProgramMain/description": "是否生成显式程序类和主方法,而不是顶级语句。", + "postActions/restore/description": "还原此项目所需的 NuGet 包。", + "postActions/restore/manualInstructions/default/text": "运行 \"dotnet restore\"" +} diff --git a/src/Templates/templates/blazorweb-csharp-8/.template.config/localize/templatestrings.zh-Hant.json b/src/Templates/templates/blazorweb-csharp-8/.template.config/localize/templatestrings.zh-Hant.json new file mode 100644 index 0000000000..aacb565d85 --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-8/.template.config/localize/templatestrings.zh-Hant.json @@ -0,0 +1,44 @@ +{ + "author": "Microsoft", + "name": "Fluent Blazor Web 應用程式", + "description": "用於建立同時支援伺服器端轉譯和用戶端互動的 Blazor Web 應用程式的專案範本。此範本可用於具有豐富動態使用者介面 (UI) 的 Web 應用程式。", + "symbols/Framework/description": "專案的目標 Framework。", + "symbols/Framework/choices/net8.0/description": "目標 net8.0", + "symbols/UserSecretsId/description": "用於祕密的識別碼 (搭配個別驗證使用)。", + "symbols/skipRestore/description": "若指定,會在建立時跳過專案的自動還原。", + "symbols/ExcludeLaunchSettings/description": "是否要從產生的範本排除 launchSettings.json。", + "symbols/kestrelHttpPort/description": "launchSettings.json 中 HTTP 端點要使用的連接埠號碼。", + "symbols/kestrelHttpsPort/description": "launchSettings.json 中 IIS Express HTTPS 端點要使用的連接埠號碼。只有在未使用參數 no-https 時,才適用用此選項 (如果使用個別驗證,則會忽略 no-https)。", + "symbols/iisHttpPort/description": "launchSettings.json 中 IIS Express HTTP 端點要使用的連接埠號碼。", + "symbols/iisHttpsPort/description": "launchSettings.json 中 IIS Express HTTPS 端點要使用的連接埠號碼。只有在未使用參數 no-https 時,才適用用此選項 (如果使用個別驗證,則會忽略 no-https)。", + "symbols/InteractivityPlatform/displayName": "_互動功能類型", + "symbols/InteractivityPlatform/description": "選擇要用於互動式元件的裝載平台", + "symbols/InteractivityPlatform/choices/None/displayName": "無", + "symbols/InteractivityPlatform/choices/None/description": "沒有互動功能 (只有靜態伺服器轉譯)", + "symbols/InteractivityPlatform/choices/Server/displayName": "伺服器", + "symbols/InteractivityPlatform/choices/Server/description": "在伺服器上執行", + "symbols/InteractivityPlatform/choices/WebAssembly/displayName": "WebAssembly", + "symbols/InteractivityPlatform/choices/WebAssembly/description": "使用 WebAssembly 在瀏覽器中執行", + "symbols/InteractivityPlatform/choices/Auto/displayName": "自動 (伺服器和 WebAssembly)", + "symbols/InteractivityPlatform/choices/Auto/description": "下載 WebAssembly 資產時使用伺服器,然後使用 WebAssembly", + "symbols/InteractivityLocation/displayName": "_互動功能位置", + "symbols/InteractivityLocation/description": "選擇要啟用互動式轉譯的元件", + "symbols/InteractivityLocation/choices/InteractivePerPage/displayName": "每頁/元件", + "symbols/InteractivityLocation/choices/InteractivePerPage/description": "互動性會以每頁或每一元件為基礎套用", + "symbols/InteractivityLocation/choices/InteractiveGlobal/displayName": "全球", + "symbols/InteractivityLocation/choices/InteractiveGlobal/description": "互動功能已套用至根層級", + "symbols/IncludeSampleContent/displayName": "包含範例頁面(_I)", + "symbols/IncludeSampleContent/description": "設定是否要新增範例頁面和樣式,以示範基本使用模式。", + "symbols/Empty/description": "設定是否要省略範例頁面和樣式,其示範基本使用模式。", + "symbols/auth/choices/None/description": "沒有驗證", + "symbols/auth/choices/Individual/description": "個別驗證", + "symbols/auth/description": "要使用的驗證類型。", + "symbols/UseLocalDB/description": "是否使用 LocalDB 而非 SQLite。只有在已指定 --auth Individual 時,才適用此選項。", + "symbols/AllInteractive/displayName": "_啟用整個網站的全域互動式轉譯", + "symbols/AllInteractive/description": "設定是否要在最上層套用互動式轉譯模式,讓每個頁面都成為互動式。如果為 false,則頁面預設會使用靜態伺服器轉譯,而且可以以每頁或每一元件為基礎標示為互動式。", + "symbols/NoHttps/description": "是否要關閉 HTTPS。此選項僅適用於個人未用於 --auth 時。", + "symbols/UseProgramMain/displayName": "不要使用最上層陳述式(_T)", + "symbols/UseProgramMain/description": "是否要產生明確的 Program 類別和 Main 方法,而非最上層語句。", + "postActions/restore/description": "還原此專案所需的 NuGet 套件。", + "postActions/restore/manualInstructions/default/text": "執行 'dotnet restore'" +} diff --git a/src/Templates/templates/blazorweb-csharp-8/.template.config/template.json b/src/Templates/templates/blazorweb-csharp-8/.template.config/template.json new file mode 100644 index 0000000000..89f9317b09 --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-8/.template.config/template.json @@ -0,0 +1,486 @@ +{ + "$schema": "https://json.schemastore.org/template", + "author": "Microsoft", + "classifications": [ + "Web", + "Fluent", + "Blazor", + "WebAssembly" + ], + "name": "Fluent Blazor Web App", + "defaultName": "BlazorApp", + "description": "A project template for creating a Blazor web app that supports both server-side rendering and client interactivity. This template can be used for web apps with rich dynamic user interfaces (UIs).", + "groupIdentity": "Microsoft.Web.Fluent.Blazor", + "precedence": "8002", + "guids": [ + "4C26868E-5E7C-458D-82E3-040509D0C71F", + "5990939C-7E7B-4CFA-86FF-44CA5756498A", + "650B3CE7-2E93-4CC4-9F46-466686815EAA", + "53bc9b9d-9d6a-45d4-8429-2a2761773502" + ], + "identity": "Microsoft.Web.Fluent.Blazor.CSharp.8.0", + "thirdPartyNotices": "https://aka.ms/aspnetcore/8.0-third-party-notices", + "preferNameDirectory": true, + "primaryOutputs": [ + { + "condition": "(UseWebAssembly && (HostIdentifier == \"dotnetcli\" || HostIdentifier == \"dotnetcli-preview\"))", + "path": "BlazorWeb-CSharp.sln" + }, + { + "condition": "(UseWebAssembly && HostIdentifier != \"dotnetcli\" && HostIdentifier != \"dotnetcli-preview\")", + "path": "BlazorWeb-CSharp/BlazorWeb-CSharp.csproj" + }, + { + "condition": "(!UseWebAssembly)", + "path": "BlazorWeb-CSharp.csproj" + }, + { + "condition": "(UseWebAssembly && HostIdentifier != \"dotnetcli\" && HostIdentifier != \"dotnetcli-preview\")", + "path": "BlazorWeb-CSharp.Client/BlazorWeb-CSharp.Client.csproj" + } + ], + "shortName": "fluentblazor", + "sourceName": "BlazorWeb-CSharp", + "sources": [ + { + "source": "./", + "target": "./", + "exclude": [ + ".template.config/**" + ], + "copyOnly": [ + "**/wwwroot/css/**" + ], + "modifiers": [ + { + "condition": "(!UseWebAssembly)", + "exclude": [ + "BlazorWeb-CSharp.Client/**", + "*.sln" + ], + "rename": { + "BlazorWeb-CSharp/": "./" + } + }, + { + "condition": "(UseWebAssembly && InteractiveAtRoot)", + "rename": { + "BlazorWeb-CSharp/Components/Layout/MainLayout.razor": "./BlazorWeb-CSharp.Client/Layout/MainLayout.razor", + "BlazorWeb-CSharp/Components/Pages/Home.razor": "./BlazorWeb-CSharp.Client/Pages/Home.razor", + "BlazorWeb-CSharp/Components/Pages/Weather.razor": "./BlazorWeb-CSharp.Client/Pages/Weather.razor", + "BlazorWeb-CSharp/Components/Routes.razor": "./BlazorWeb-CSharp.Client/Routes.razor" + } + }, + { + "condition": "(UseWebAssembly)", + "exclude": [ + "BlazorWeb-CSharp/Components/Layout/NavMenu.razor", + "BlazorWeb-CSharp/Components/Layout/NavMenu.razor.css" + ] + }, + { + "condition": "(!UseProgramMain)", + "exclude": [ + "BlazorWeb-CSharp/Program.Main.cs", + "BlazorWeb-CSharp.Client/Program.Main.cs" + ] + }, + { + "condition": "(UseProgramMain)", + "exclude": [ + "BlazorWeb-CSharp/Program.cs", + "BlazorWeb-CSharp.Client/Program.cs" + ], + "rename": { + "Program.Main.cs": "Program.cs" + } + }, + { + "condition": "(!(UseServer && !UseWebAssembly))", + "exclude": [ + "BlazorWeb-CSharp/Components/Pages/Counter.razor" + ] + }, + { + "condition": "(ExcludeLaunchSettings)", + "exclude": [ + "BlazorWeb-CSharp/Properties/launchSettings.json" + ] + }, + { + "condition": "(!SampleContent)", + "exclude": [ + "BlazorWeb-CSharp/Components/Pages/Auth.*", + "BlazorWeb-CSharp/Components/Pages/Counter.*", + "BlazorWeb-CSharp/Components/Pages/Weather.*", + "BlazorWeb-CSharp/Components/Layout/NavMenu.*", + "BlazorWeb-CSharp/wwwroot/favicon.*", + "BlazorWeb-CSharp.Client/Layout/NavMenu.*", + "BlazorWeb-CSharp.Client/Pages/**", + "BlazorWeb-CSharp.Client/wwwroot/**" + ] + }, + { + "condition": "(UseWebAssembly && HostIdentifier != \"dotnetcli\" && HostIdentifier != \"dotnetcli-preview\")", + "exclude": [ + "*.sln" + ] + }, + { + "condition": "(!IndividualLocalAuth)", + "exclude": [ + "BlazorWeb-CSharp/Components/Account/**", + "BlazorWeb-CSharp/Data/**", + "BlazorWeb-CSharp.Client/UserInfo.cs", + "BlazorWeb-CSharp.Client/Pages/Auth.razor" + ] + }, + { + "condition": "(!(IndividualLocalAuth && !UseLocalDB))", + "exclude": [ + "BlazorWeb-CSharp/Data/app.db" + ] + }, + { + "condition": "(!(IndividualLocalAuth && !UseWebAssembly))", + "exclude": [ + "BlazorWeb-CSharp/Components/Pages/Auth.razor" + ] + }, + { + "condition": "(!(IndividualLocalAuth && UseServer))", + "exclude": [ + "BlazorWeb-CSharp/Components/Account/IdentityRevalidatingAuthenticationStateProvider.cs" + ] + }, + { + "condition": "(IndividualLocalAuth && UseLocalDB && UseWebAssembly)", + "rename": { + "BlazorWeb-CSharp/Data/SqlServer/": "BlazorWeb-CSharp/Data/Migrations/" + }, + "exclude": [ + "BlazorWeb-CSharp/Data/SqlLite/**" + ] + }, + { + "condition": "(IndividualLocalAuth && UseLocalDB && !UseWebAssembly)", + "rename": { + "BlazorWeb-CSharp/Data/SqlServer/": "Data/Migrations/" + }, + "exclude": [ + "BlazorWeb-CSharp/Data/SqlLite/**" + ] + }, + { + "condition": "(IndividualLocalAuth && !UseLocalDB && UseWebAssembly)", + "rename": { + "BlazorWeb-CSharp/Data/SqlLite/": "BlazorWeb-CSharp/Data/Migrations/" + }, + "exclude": [ + "BlazorWeb-CSharp/Data/SqlServer/**" + ] + }, + { + "condition": "(IndividualLocalAuth && !UseLocalDB && !UseWebAssembly)", + "rename": { + "BlazorWeb-CSharp/Data/SqlLite/": "Data/Migrations/" + }, + "exclude": [ + "BlazorWeb-CSharp/Data/SqlServer/**" + ] + }, + { + "condition": "(IndividualLocalAuth && UseWebAssembly)", + "rename": { + "BlazorWeb-CSharp/Components/Account/Shared/RedirectToLogin.razor": "BlazorWeb-CSharp.Client/RedirectToLogin.razor" + } + } + ] + } + ], + "symbols": { + "Framework": { + "type": "parameter", + "description": "The target framework for the project.", + "datatype": "choice", + "choices": [ + { + "choice": "net8.0", + "description": "Target net8.0" + } + ], + "replaces": "net8.0", + "defaultValue": "net8.0" + }, + "HostIdentifier": { + "type": "bind", + "binding": "HostIdentifier" + }, + "UserSecretsId": { + "type": "parameter", + "datatype": "string", + "replaces": "aspnet-BlazorWeb-CSharp-53bc9b9d-9d6a-45d4-8429-2a2761773502", + "defaultValue": "aspnet-BlazorWeb-CSharp-53bc9b9d-9d6a-45d4-8429-2a2761773502", + "description": "The ID to use for secrets (use with Individual auth)." + }, + "skipRestore": { + "type": "parameter", + "datatype": "bool", + "description": "If specified, skips the automatic restore of the project on create.", + "defaultValue": "false" + }, + "ExcludeLaunchSettings": { + "type": "parameter", + "datatype": "bool", + "defaultValue": "false", + "description": "Whether to exclude launchSettings.json from the generated template." + }, + "kestrelHttpPort": { + "type": "parameter", + "datatype": "integer", + "description": "Port number to use for the HTTP endpoint in launchSettings.json." + }, + "kestrelHttpPortGenerated": { + "type": "generated", + "generator": "port", + "parameters": { + "low": 5000, + "high": 5300 + } + }, + "kestrelHttpPortReplacer": { + "type": "generated", + "generator": "coalesce", + "parameters": { + "sourceVariableName": "kestrelHttpPort", + "fallbackVariableName": "kestrelHttpPortGenerated" + }, + "replaces": "5500" + }, + "kestrelHttpsPort": { + "type": "parameter", + "datatype": "integer", + "description": "Port number to use for the HTTPS endpoint in launchSettings.json. This option is only applicable when the parameter no-https is not used (no-https will be ignored if Individual auth is used)." + }, + "kestrelHttpsPortGenerated": { + "type": "generated", + "generator": "port", + "parameters": { + "low": 7000, + "high": 7300 + } + }, + "kestrelHttpsPortReplacer": { + "type": "generated", + "generator": "coalesce", + "parameters": { + "sourceVariableName": "kestrelHttpsPort", + "fallbackVariableName": "kestrelHttpsPortGenerated" + }, + "replaces": "5501" + }, + "iisHttpPort": { + "type": "parameter", + "datatype": "integer", + "description": "Port number to use for the IIS Express HTTP endpoint in launchSettings.json." + }, + "iisHttpPortGenerated": { + "type": "generated", + "generator": "port" + }, + "iisHttpPortReplacer": { + "type": "generated", + "generator": "coalesce", + "parameters": { + "sourceVariableName": "iisHttpPort", + "fallbackVariableName": "iisHttpPortGenerated" + }, + "replaces": "8080" + }, + "iisHttpsPort": { + "type": "parameter", + "datatype": "integer", + "description": "Port number to use for the IIS Express HTTPS endpoint in launchSettings.json. This option is only applicable when the parameter no-https is not used (no-https will be ignored if Individual auth is used)." + }, + "iisHttpsPortGenerated": { + "type": "generated", + "generator": "port", + "parameters": { + "low": 44300, + "high": 44399 + } + }, + "iisHttpsPortReplacer": { + "type": "generated", + "generator": "coalesce", + "parameters": { + "sourceVariableName": "iisHttpsPort", + "fallbackVariableName": "iisHttpsPortGenerated" + }, + "replaces": "44300" + }, + "InteractivityPlatform": { + "type": "parameter", + "datatype": "choice", + "defaultValue": "Server", + "displayName": "_Interactive render mode", + "description": "Chooses which interactive render mode to use for interactive components", + "choices": [ + { + "choice": "None", + "displayName": "None", + "description": "No interactivity (static server rendering only)" + }, + { + "choice": "Server", + "displayName": "Server", + "description": "Runs on the server" + }, + { + "choice": "WebAssembly", + "displayName": "WebAssembly", + "description": "Runs in the browser using WebAssembly" + }, + { + "choice": "Auto", + "displayName": "Auto (Server and WebAssembly)", + "description": "Uses Server while downloading WebAssembly assets, then uses WebAssembly" + } + ] + }, + "InteractivityLocation": { + "type": "parameter", + "datatype": "choice", + "defaultValue": "InteractivePerPage", + "displayName": "_Interactivity location", + "description": "Chooses which components will have interactive rendering enabled", + "isEnabled": "(InteractivityPlatform != \"None\")", + "choices": [ + { + "choice": "InteractivePerPage", + "displayName": "Per page/component", + "description": "Interactivity is applied on a per-page or per-component basis" + }, + { + "choice": "InteractiveGlobal", + "displayName": "Global", + "description": "Interactivity is applied at the root level" + } + ] + }, + "UseWebAssembly": { + "type": "computed", + "value": "(InteractivityPlatform == \"WebAssembly\" || InteractivityPlatform == \"Auto\")" + }, + "UseServer": { + "type": "computed", + "value": "(InteractivityPlatform == \"Server\" || InteractivityPlatform == \"Auto\")" + }, + "IncludeSampleContent": { + "type": "parameter", + "datatype": "bool", + "defaultValue": "true", + "displayName": "_Include sample pages", + "description": "Configures whether to add sample pages and styling to demonstrate basic usage patterns." + }, + "Empty": { + "type": "parameter", + "datatype": "bool", + "defaultValue": "false", + "description": "Configures whether to omit sample pages and styling that demonstrate basic usage patterns." + }, + "auth": { + "type": "parameter", + "datatype": "choice", + "choices": [ + { + "choice": "None", + "description": "No authentication" + }, + { + "choice": "Individual", + "description": "Individual authentication" + } + ], + "defaultValue": "None", + "description": "The type of authentication to use" + }, + "UseLocalDB": { + "type": "parameter", + "datatype": "bool", + "defaultValue": "false", + "description": "Whether to use LocalDB instead of SQLite. This option only applies if --auth Individual is specified." + }, + "SampleContent": { + "type": "computed", + "value": "(((IncludeSampleContent && (HostIdentifier != \"dotnetcli\" && HostIdentifier != \"dotnetcli-preview\"))) || ((!Empty && (HostIdentifier == \"dotnetcli\" || HostIdentifier == \"dotnetcli-preview\"))))" + }, + "AllInteractive": { + "type": "parameter", + "datatype": "bool", + "isEnabled": "(InteractivityPlatform != \"None\")", + "defaultValue": "false", + "displayName": "_Enable interactive rendering globally throughout the site", + "description": "Configures whether to make every page interactive by applying an interactive render mode at the top level. If false, pages will use static server rendering by default, and can be marked interactive on a per-page or per-component basis." + }, + "InteractiveAtRoot": { + "type": "computed", + "value": "(InteractivityLocation == \"InteractiveGlobal\" || AllInteractive)" + }, + "IndividualLocalAuth": { + "type": "computed", + "value": "(auth == \"Individual\")" + }, + "RequiresHttps": { + "type": "computed", + "value": "(OrganizationalAuth || IndividualLocalAuth)" + }, + "HasHttpProfile": { + "type": "computed", + "value": "(!RequiresHttps)" + }, + "HasHttpsProfile": { + "type": "computed", + "value": "(RequiresHttps || !NoHttps)" + }, + "NoHttps": { + "type": "parameter", + "datatype": "bool", + "defaultValue": "false", + "description": "Whether to turn off HTTPS. This option only applies if Individual isn't used for --auth." + }, + "copyrightYear": { + "type": "generated", + "generator": "now", + "replaces": "copyrightYear", + "parameters": { + "format": "yyyy" + } + }, + "UseProgramMain": { + "type": "parameter", + "datatype": "bool", + "defaultValue": "false", + "displayName": "Do not use _top-level statements", + "description": "Whether to generate an explicit Program class and Main method instead of top-level statements." + } + }, + "tags": { + "language": "C#", + "type": "project" + }, + "postActions": [ + { + "id": "restore", + "condition": "(!skipRestore)", + "description": "Restore NuGet packages required by this project.", + "manualInstructions": [ + { + "text": "Run 'dotnet restore'" + } + ], + "actionId": "210D431B-A78B-4D2F-B762-4ED3E3EA9025", + "continueOnError": true + } + ] +} diff --git a/src/Templates/templates/blazorweb-csharp/BlazorWeb-CSharp.Client/BlazorWeb-CSharp.Client.csproj b/src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp.Client/BlazorWeb-CSharp.Client.csproj similarity index 64% rename from src/Templates/templates/blazorweb-csharp/BlazorWeb-CSharp.Client/BlazorWeb-CSharp.Client.csproj rename to src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp.Client/BlazorWeb-CSharp.Client.csproj index 72ebafd709..9cd7b95590 100644 --- a/src/Templates/templates/blazorweb-csharp/BlazorWeb-CSharp.Client/BlazorWeb-CSharp.Client.csproj +++ b/src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp.Client/BlazorWeb-CSharp.Client.csproj @@ -11,10 +11,8 @@
- - - - + + diff --git a/src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp.Client/Layout/NavMenu.razor b/src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp.Client/Layout/NavMenu.razor new file mode 100644 index 0000000000..80a5626cb6 --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp.Client/Layout/NavMenu.razor @@ -0,0 +1,65 @@ +@*#if (UseServer && !InteractiveAtRoot) +@rendermode InteractiveAuto +##elseif (!InteractiveAtRoot) +@rendermode InteractiveWebAssembly +##endif*@ + +@*#if (IndividualLocalAuth) +@implements IDisposable + +@inject NavigationManager NavigationManager + +##endif*@ + + +@code { + private bool expanded = true; + + @*#if (IndividualLocalAuth) + private string? currentUrl; + + protected override void OnInitialized() + { + currentUrl = NavigationManager.ToBaseRelativePath(NavigationManager.Uri); + NavigationManager.LocationChanged += OnLocationChanged; + } + + private void OnLocationChanged(object? sender, LocationChangedEventArgs e) + { + currentUrl = NavigationManager.ToBaseRelativePath(e.Location); + StateHasChanged(); + } + + public void Dispose() + { + NavigationManager.LocationChanged -= OnLocationChanged; + } +##endif*@ +} diff --git a/src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp.Client/Pages/Auth.razor b/src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp.Client/Pages/Auth.razor new file mode 100644 index 0000000000..6b805cfa02 --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp.Client/Pages/Auth.razor @@ -0,0 +1,18 @@ +@page "/auth" + +@using Microsoft.AspNetCore.Authorization + +@attribute [Authorize] +@*#if (UseServer && !InteractiveAtRoot) +@rendermode InteractiveAuto +##elseif (!InteractiveAtRoot) +@rendermode InteractiveWebAssembly +##endif*@ + +Auth + +

You are authenticated

+ + + Hello @context.User.Identity?.Name! + diff --git a/src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp.Client/Pages/Counter.razor b/src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp.Client/Pages/Counter.razor new file mode 100644 index 0000000000..7f672c7ab7 --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp.Client/Pages/Counter.razor @@ -0,0 +1,25 @@ +@page "/counter" +@*#if (UseServer && !InteractiveAtRoot) +@rendermode InteractiveAuto +##elseif (!InteractiveAtRoot) +@rendermode InteractiveWebAssembly +##endif*@ + +Counter + +

Counter

+ +
+ Current count: @currentCount +
+ +Click me + +@code { + private int currentCount = 0; + + private void IncrementCount() + { + currentCount++; + } +} diff --git a/src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp.Client/PersistentAuthenticationStateProvider.cs b/src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp.Client/PersistentAuthenticationStateProvider.cs new file mode 100644 index 0000000000..d97e09a6fc --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp.Client/PersistentAuthenticationStateProvider.cs @@ -0,0 +1,40 @@ +using System.Security.Claims; +using Microsoft.AspNetCore.Components; +using Microsoft.AspNetCore.Components.Authorization; + +namespace BlazorWeb_CSharp.Client; + +// This is a client-side AuthenticationStateProvider that determines the user's authentication state by +// looking for data persisted in the page when it was rendered on the server. This authentication state will +// be fixed for the lifetime of the WebAssembly application. So, if the user needs to log in or out, a full +// page reload is required. +// +// This only provides a user name and email for display purposes. It does not actually include any tokens +// that authenticate to the server when making subsequent requests. That works separately using a +// cookie that will be included on HttpClient requests to the server. +internal class PersistentAuthenticationStateProvider : AuthenticationStateProvider +{ + private static readonly Task defaultUnauthenticatedTask = + Task.FromResult(new AuthenticationState(new ClaimsPrincipal(new ClaimsIdentity()))); + + private readonly Task authenticationStateTask = defaultUnauthenticatedTask; + + public PersistentAuthenticationStateProvider(PersistentComponentState state) + { + if (!state.TryTakeFromJson(nameof(UserInfo), out var userInfo) || userInfo is null) + { + return; + } + + Claim[] claims = [ + new Claim(ClaimTypes.NameIdentifier, userInfo.UserId), + new Claim(ClaimTypes.Name, userInfo.Email), + new Claim(ClaimTypes.Email, userInfo.Email) ]; + + authenticationStateTask = Task.FromResult( + new AuthenticationState(new ClaimsPrincipal(new ClaimsIdentity(claims, + authenticationType: nameof(PersistentAuthenticationStateProvider))))); + } + + public override Task GetAuthenticationStateAsync() => authenticationStateTask; +} diff --git a/src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp.Client/Program.Main.cs b/src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp.Client/Program.Main.cs new file mode 100644 index 0000000000..bc35c43451 --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp.Client/Program.Main.cs @@ -0,0 +1,23 @@ +#if (IndividualLocalAuth) +using BlazorWeb_CSharp.Client; +using Microsoft.AspNetCore.Components.Authorization; +#endif +using Microsoft.AspNetCore.Components.WebAssembly.Hosting; + +namespace BlazorWeb_CSharp.Client; + +class Program +{ + static async Task Main(string[] args) + { + var builder = WebAssemblyHostBuilder.CreateDefault(args); + +#if (IndividualLocalAuth) + builder.Services.AddAuthorizationCore(); + builder.Services.AddCascadingAuthenticationState(); + builder.Services.AddSingleton(); + +#endif + await builder.Build().RunAsync(); + } +} diff --git a/src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp.Client/Program.cs b/src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp.Client/Program.cs new file mode 100644 index 0000000000..600e37d365 --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp.Client/Program.cs @@ -0,0 +1,15 @@ +#if (IndividualLocalAuth) +using BlazorWeb_CSharp.Client; +using Microsoft.AspNetCore.Components.Authorization; +#endif +using Microsoft.AspNetCore.Components.WebAssembly.Hosting; + +var builder = WebAssemblyHostBuilder.CreateDefault(args); + +#if (IndividualLocalAuth) +builder.Services.AddAuthorizationCore(); +builder.Services.AddCascadingAuthenticationState(); +builder.Services.AddSingleton(); + +#endif +await builder.Build().RunAsync(); diff --git a/src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp.Client/UserInfo.cs b/src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp.Client/UserInfo.cs new file mode 100644 index 0000000000..b62cdbbe0c --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp.Client/UserInfo.cs @@ -0,0 +1,9 @@ +namespace BlazorWeb_CSharp.Client; + +// Add properties to this class and update the server and client AuthenticationStateProviders +// to expose more information about the authenticated user to the client. +public class UserInfo +{ + public required string UserId { get; set; } + public required string Email { get; set; } +} diff --git a/src/Templates/templates/blazorweb-csharp/BlazorWeb-CSharp.Client/_Imports.razor b/src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp.Client/_Imports.razor similarity index 100% rename from src/Templates/templates/blazorweb-csharp/BlazorWeb-CSharp.Client/_Imports.razor rename to src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp.Client/_Imports.razor diff --git a/src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp.Client/wwwroot/appsettings.Development.json b/src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp.Client/wwwroot/appsettings.Development.json new file mode 100644 index 0000000000..0c208ae918 --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp.Client/wwwroot/appsettings.Development.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + } +} diff --git a/src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp.Client/wwwroot/appsettings.json b/src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp.Client/wwwroot/appsettings.json new file mode 100644 index 0000000000..0c208ae918 --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp.Client/wwwroot/appsettings.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + } +} diff --git a/src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp.sln b/src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp.sln new file mode 100644 index 0000000000..88a77b704b --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp.sln @@ -0,0 +1,50 @@ +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.0.0 +MinimumVisualStudioVersion = 16.0.0.0 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BlazorWeb-CSharp", "BlazorWeb-CSharp\BlazorWeb-CSharp.csproj", "{650B3CE7-2E93-4CC4-9F46-466686815EAA}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BlazorWeb-CSharp.Client", "BlazorWeb-CSharp.Client\BlazorWeb-CSharp.Client.csproj", "{5990939C-7E7B-4CFA-86FF-44CA5756498A}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {5990939C-7E7B-4CFA-86FF-44CA5756498A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5990939C-7E7B-4CFA-86FF-44CA5756498A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5990939C-7E7B-4CFA-86FF-44CA5756498A}.Debug|x64.ActiveCfg = Debug|Any CPU + {5990939C-7E7B-4CFA-86FF-44CA5756498A}.Debug|x64.Build.0 = Debug|Any CPU + {5990939C-7E7B-4CFA-86FF-44CA5756498A}.Debug|x86.ActiveCfg = Debug|Any CPU + {5990939C-7E7B-4CFA-86FF-44CA5756498A}.Debug|x86.Build.0 = Debug|Any CPU + {5990939C-7E7B-4CFA-86FF-44CA5756498A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5990939C-7E7B-4CFA-86FF-44CA5756498A}.Release|Any CPU.Build.0 = Release|Any CPU + {5990939C-7E7B-4CFA-86FF-44CA5756498A}.Release|x64.ActiveCfg = Release|Any CPU + {5990939C-7E7B-4CFA-86FF-44CA5756498A}.Release|x64.Build.0 = Release|Any CPU + {5990939C-7E7B-4CFA-86FF-44CA5756498A}.Release|x86.ActiveCfg = Release|Any CPU + {5990939C-7E7B-4CFA-86FF-44CA5756498A}.Release|x86.Build.0 = Release|Any CPU + {650B3CE7-2E93-4CC4-9F46-466686815EAA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {650B3CE7-2E93-4CC4-9F46-466686815EAA}.Debug|Any CPU.Build.0 = Debug|Any CPU + {650B3CE7-2E93-4CC4-9F46-466686815EAA}.Debug|x64.ActiveCfg = Debug|Any CPU + {650B3CE7-2E93-4CC4-9F46-466686815EAA}.Debug|x64.Build.0 = Debug|Any CPU + {650B3CE7-2E93-4CC4-9F46-466686815EAA}.Debug|x86.ActiveCfg = Debug|Any CPU + {650B3CE7-2E93-4CC4-9F46-466686815EAA}.Debug|x86.Build.0 = Debug|Any CPU + {650B3CE7-2E93-4CC4-9F46-466686815EAA}.Release|Any CPU.ActiveCfg = Release|Any CPU + {650B3CE7-2E93-4CC4-9F46-466686815EAA}.Release|Any CPU.Build.0 = Release|Any CPU + {650B3CE7-2E93-4CC4-9F46-466686815EAA}.Release|x64.ActiveCfg = Release|Any CPU + {650B3CE7-2E93-4CC4-9F46-466686815EAA}.Release|x64.Build.0 = Release|Any CPU + {650B3CE7-2E93-4CC4-9F46-466686815EAA}.Release|x86.ActiveCfg = Release|Any CPU + {650B3CE7-2E93-4CC4-9F46-466686815EAA}.Release|x86.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {4C26868E-5E7C-458D-82E3-040509D0C71F} + EndGlobalSection +EndGlobal diff --git a/src/Templates/templates/blazorweb-csharp/BlazorWeb-CSharp/BlazorWeb-CSharp.csproj b/src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp/BlazorWeb-CSharp.csproj similarity index 58% rename from src/Templates/templates/blazorweb-csharp/BlazorWeb-CSharp/BlazorWeb-CSharp.csproj rename to src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp/BlazorWeb-CSharp.csproj index cc6a048cc3..4f5344d35e 100644 --- a/src/Templates/templates/blazorweb-csharp/BlazorWeb-CSharp/BlazorWeb-CSharp.csproj +++ b/src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp/BlazorWeb-CSharp.csproj @@ -19,18 +19,12 @@ - - - - - - - - - - - - + + + + + + diff --git a/src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp/Components/Account/IdentityComponentsEndpointRouteBuilderExtensions.cs b/src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp/Components/Account/IdentityComponentsEndpointRouteBuilderExtensions.cs new file mode 100644 index 0000000000..0ff46f811b --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp/Components/Account/IdentityComponentsEndpointRouteBuilderExtensions.cs @@ -0,0 +1,130 @@ +using System.Security.Claims; +using System.Text.Json; +using Microsoft.AspNetCore.Authentication; +using Microsoft.AspNetCore.Components.Authorization; +using Microsoft.AspNetCore.Http.Extensions; +using Microsoft.AspNetCore.Identity; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Primitives; +using BlazorWeb_CSharp.Components.Account.Pages; +using BlazorWeb_CSharp.Components.Account.Pages.Manage; +using BlazorWeb_CSharp.Data; + +namespace Microsoft.AspNetCore.Routing; + +internal static class IdentityComponentsEndpointRouteBuilderExtensions +{ + // These endpoints are required by the Identity Razor components defined in the /Components/Account/Pages directory of this project. + public static IEndpointConventionBuilder MapAdditionalIdentityEndpoints(this IEndpointRouteBuilder endpoints) + { + ArgumentNullException.ThrowIfNull(endpoints); + + var accountGroup = endpoints.MapGroup("/Account"); + + accountGroup.MapPost("/PerformExternalLogin", ( + HttpContext context, + SignInManager signInManager, + [FromForm] string provider, + [FromForm] string returnUrl) => + { + IEnumerable> query = [ + new("ReturnUrl", returnUrl), + new("Action", ExternalLogin.LoginCallbackAction)]; + + var redirectUrl = UriHelper.BuildRelative( + context.Request.PathBase, + "/Account/ExternalLogin", + QueryString.Create(query)); + + provider = TemporaryFluentButtonFix(provider); + + var properties = signInManager.ConfigureExternalAuthenticationProperties(provider, redirectUrl); + return TypedResults.Challenge(properties, [provider]); + }); + + accountGroup.MapPost("/Logout", async ( + ClaimsPrincipal user, + [FromServices] SignInManager signInManager, + [FromForm] string returnUrl) => + { + await signInManager.SignOutAsync(); + return TypedResults.LocalRedirect($"~/{returnUrl}"); + }); + + var manageGroup = accountGroup.MapGroup("/Manage").RequireAuthorization(); + + manageGroup.MapPost("/LinkExternalLogin", async ( + HttpContext context, + [FromServices] SignInManager signInManager, + [FromForm] string provider) => + { + // Clear the existing external cookie to ensure a clean login process + await context.SignOutAsync(IdentityConstants.ExternalScheme); + + var redirectUrl = UriHelper.BuildRelative( + context.Request.PathBase, + "/Account/Manage/ExternalLogins", + QueryString.Create("Action", ExternalLogins.LinkLoginCallbackAction)); + + provider = TemporaryFluentButtonFix(provider); + + var properties = signInManager.ConfigureExternalAuthenticationProperties(provider, redirectUrl, signInManager.UserManager.GetUserId(context.User)); + return TypedResults.Challenge(properties, [provider]); + }); + + var loggerFactory = endpoints.ServiceProvider.GetRequiredService(); + var downloadLogger = loggerFactory.CreateLogger("DownloadPersonalData"); + + manageGroup.MapPost("/DownloadPersonalData", async ( + HttpContext context, + [FromServices] UserManager userManager, + [FromServices] AuthenticationStateProvider authenticationStateProvider) => + { + var user = await userManager.GetUserAsync(context.User); + if (user is null) + { + return Results.NotFound($"Unable to load user with ID '{userManager.GetUserId(context.User)}'."); + } + + var userId = await userManager.GetUserIdAsync(user); + downloadLogger.LogInformation("User with ID '{UserId}' asked for their personal data.", userId); + + // Only include personal data for download + var personalData = new Dictionary(); + var personalDataProps = typeof(ApplicationUser).GetProperties().Where( + prop => Attribute.IsDefined(prop, typeof(PersonalDataAttribute))); + foreach (var p in personalDataProps) + { + personalData.Add(p.Name, p.GetValue(user)?.ToString() ?? "null"); + } + + var logins = await userManager.GetLoginsAsync(user); + foreach (var l in logins) + { + personalData.Add($"{l.LoginProvider} external login provider key", l.ProviderKey); + } + + personalData.Add("Authenticator Key", (await userManager.GetAuthenticatorKeyAsync(user))!); + var fileBytes = JsonSerializer.SerializeToUtf8Bytes(personalData); + + context.Response.Headers.TryAdd("Content-Disposition", "attachment; filename=PersonalData.json"); + return TypedResults.File(fileBytes, contentType: "application/json", fileDownloadName: "PersonalData.json"); + }); + + return accountGroup; + } + + private static string TemporaryFluentButtonFix(string provider) + { + // Temporary workaround for FluentButton returning a provider value twice + // Split the comma-separated list of strings + var providers = provider.Split(','); + + // Find the value that appears twice in the list + provider = providers.GroupBy(p => p) + .Where(g => g.Count() == 2) + .Select(g => g.Key) + .First(); + return provider; + } +} diff --git a/src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp/Components/Account/IdentityNoOpEmailSender.cs b/src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp/Components/Account/IdentityNoOpEmailSender.cs new file mode 100644 index 0000000000..1256a02a78 --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp/Components/Account/IdentityNoOpEmailSender.cs @@ -0,0 +1,20 @@ +using Microsoft.AspNetCore.Identity; +using Microsoft.AspNetCore.Identity.UI.Services; +using BlazorWeb_CSharp.Data; + +namespace BlazorWeb_CSharp.Components.Account; + +// Remove the "else if (EmailSender is IdentityNoOpEmailSender)" block from RegisterConfirmation.razor after updating with a real implementation. +internal sealed class IdentityNoOpEmailSender : IEmailSender +{ + private readonly IEmailSender emailSender = new NoOpEmailSender(); + + public Task SendConfirmationLinkAsync(ApplicationUser user, string email, string confirmationLink) => + emailSender.SendEmailAsync(email, "Confirm your email", $"Please confirm your account by clicking here."); + + public Task SendPasswordResetLinkAsync(ApplicationUser user, string email, string resetLink) => + emailSender.SendEmailAsync(email, "Reset your password", $"Please reset your password by clicking here."); + + public Task SendPasswordResetCodeAsync(ApplicationUser user, string email, string resetCode) => + emailSender.SendEmailAsync(email, "Reset your password", $"Please reset your password using the following code: {resetCode}"); +} diff --git a/src/Templates/templates/blazorweb-csharp/BlazorWeb-CSharp/Components/Account/IdentityRedirectManager.cs b/src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp/Components/Account/IdentityRedirectManager.cs similarity index 100% rename from src/Templates/templates/blazorweb-csharp/BlazorWeb-CSharp/Components/Account/IdentityRedirectManager.cs rename to src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp/Components/Account/IdentityRedirectManager.cs diff --git a/src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp/Components/Account/IdentityRevalidatingAuthenticationStateProvider.cs b/src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp/Components/Account/IdentityRevalidatingAuthenticationStateProvider.cs new file mode 100644 index 0000000000..cf8bf6d893 --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp/Components/Account/IdentityRevalidatingAuthenticationStateProvider.cs @@ -0,0 +1,47 @@ +using System.Security.Claims; +using Microsoft.AspNetCore.Components.Authorization; +using Microsoft.AspNetCore.Components.Server; +using Microsoft.AspNetCore.Identity; +using Microsoft.Extensions.Options; +using BlazorWeb_CSharp.Data; + +namespace BlazorWeb_CSharp.Components.Account; + +// This is a server-side AuthenticationStateProvider that revalidates the security stamp for the connected user +// every 30 minutes an interactive circuit is connected. +internal sealed class IdentityRevalidatingAuthenticationStateProvider( + ILoggerFactory loggerFactory, + IServiceScopeFactory scopeFactory, + IOptions options) + : RevalidatingServerAuthenticationStateProvider(loggerFactory) +{ + protected override TimeSpan RevalidationInterval => TimeSpan.FromMinutes(30); + + protected override async Task ValidateAuthenticationStateAsync( + AuthenticationState authenticationState, CancellationToken cancellationToken) + { + // Get the user manager from a new scope to ensure it fetches fresh data + await using var scope = scopeFactory.CreateAsyncScope(); + var userManager = scope.ServiceProvider.GetRequiredService>(); + return await ValidateSecurityStampAsync(userManager, authenticationState.User); + } + + private async Task ValidateSecurityStampAsync(UserManager userManager, ClaimsPrincipal principal) + { + var user = await userManager.GetUserAsync(principal); + if (user is null) + { + return false; + } + else if (!userManager.SupportsUserSecurityStamp) + { + return true; + } + else + { + var principalStamp = principal.FindFirstValue(options.Value.ClaimsIdentity.SecurityStampClaimType); + var userStamp = await userManager.GetSecurityStampAsync(user); + return principalStamp == userStamp; + } + } +} diff --git a/src/Templates/templates/blazorweb-csharp/BlazorWeb-CSharp/Components/Account/IdentityUserAccessor.cs b/src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp/Components/Account/IdentityUserAccessor.cs similarity index 100% rename from src/Templates/templates/blazorweb-csharp/BlazorWeb-CSharp/Components/Account/IdentityUserAccessor.cs rename to src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp/Components/Account/IdentityUserAccessor.cs diff --git a/src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp/Components/Account/Pages/AccessDenied.razor b/src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp/Components/Account/Pages/AccessDenied.razor new file mode 100644 index 0000000000..905dec3487 --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp/Components/Account/Pages/AccessDenied.razor @@ -0,0 +1,8 @@ +@page "/Account/AccessDenied" + +Access denied + +
+

Access denied

+

You do not have access to this resource.

+
diff --git a/src/Templates/templates/blazorweb-csharp/BlazorWeb-CSharp/Components/Account/Pages/ConfirmEmail.razor b/src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp/Components/Account/Pages/ConfirmEmail.razor similarity index 100% rename from src/Templates/templates/blazorweb-csharp/BlazorWeb-CSharp/Components/Account/Pages/ConfirmEmail.razor rename to src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp/Components/Account/Pages/ConfirmEmail.razor diff --git a/src/Templates/templates/blazorweb-csharp/BlazorWeb-CSharp/Components/Account/Pages/ConfirmEmailChange.razor b/src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp/Components/Account/Pages/ConfirmEmailChange.razor similarity index 100% rename from src/Templates/templates/blazorweb-csharp/BlazorWeb-CSharp/Components/Account/Pages/ConfirmEmailChange.razor rename to src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp/Components/Account/Pages/ConfirmEmailChange.razor diff --git a/src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp/Components/Account/Pages/ExternalLogin.razor b/src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp/Components/Account/Pages/ExternalLogin.razor new file mode 100644 index 0000000000..fb37f95af6 --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp/Components/Account/Pages/ExternalLogin.razor @@ -0,0 +1,192 @@ +@page "/Account/ExternalLogin" + +@using System.ComponentModel.DataAnnotations +@using System.Security.Claims +@using System.Text +@using System.Text.Encodings.Web +@using Microsoft.AspNetCore.Identity +@using Microsoft.AspNetCore.WebUtilities +@using BlazorWeb_CSharp.Data + +@inject SignInManager SignInManager +@inject UserManager UserManager +@inject IUserStore UserStore +@inject IEmailSender EmailSender +@inject NavigationManager NavigationManager +@inject IdentityRedirectManager RedirectManager +@inject ILogger Logger + +Register + + +

Register

+

Associate your @ProviderDisplayName account.

+
+ +
+ You've successfully authenticated with @ProviderDisplayName. + Please enter an email address for this site below and click the Register button to finish + logging in. +
+ + + + + + + + + Register + + + + +@code { + public const string LoginCallbackAction = "LoginCallback"; + + private string? message; + private ExternalLoginInfo? externalLoginInfo; + + [CascadingParameter] + private HttpContext HttpContext { get; set; } = default!; + + [SupplyParameterFromForm] + private InputModel Input { get; set; } = new(); + + [SupplyParameterFromQuery] + private string? RemoteError { get; set; } + + [SupplyParameterFromQuery] + private string? ReturnUrl { get; set; } + + [SupplyParameterFromQuery] + private string? Action { get; set; } + + private string? ProviderDisplayName => externalLoginInfo?.ProviderDisplayName; + + protected override async Task OnInitializedAsync() + { + if (RemoteError is not null) + { + RedirectManager.RedirectToWithStatus("Account/Login", $"Error from external provider: {RemoteError}", HttpContext); + } + + var info = await SignInManager.GetExternalLoginInfoAsync(); + if (info is null) + { + RedirectManager.RedirectToWithStatus("Account/Login", "Error loading external login information.", HttpContext); + } + + externalLoginInfo = info; + + if (HttpMethods.IsGet(HttpContext.Request.Method)) + { + if (Action == LoginCallbackAction) + { + await OnLoginCallbackAsync(); + return; + } + + // We should only reach this page via the login callback, so redirect back to + // the login page if we get here some other way. + RedirectManager.RedirectTo("Account/Login"); + } + } + + private async Task OnLoginCallbackAsync() + { + // Sign in the user with this external login provider if the user already has a login. + var result = await SignInManager.ExternalLoginSignInAsync( + externalLoginInfo.LoginProvider, + externalLoginInfo.ProviderKey, + isPersistent: false, + bypassTwoFactor: true); + + if (result.Succeeded) + { + Logger.LogInformation( + "{Name} logged in with {LoginProvider} provider.", + externalLoginInfo.Principal.Identity?.Name, + externalLoginInfo.LoginProvider); + RedirectManager.RedirectTo(ReturnUrl); + } + else if (result.IsLockedOut) + { + RedirectManager.RedirectTo("Account/Lockout"); + } + + // If the user does not have an account, then ask the user to create an account. + if (externalLoginInfo.Principal.HasClaim(c => c.Type == ClaimTypes.Email)) + { + Input.Email = externalLoginInfo.Principal.FindFirstValue(ClaimTypes.Email) ?? ""; + } + } + + private async Task OnValidSubmitAsync() + { + var emailStore = GetEmailStore(); + var user = CreateUser(); + + await UserStore.SetUserNameAsync(user, Input.Email, CancellationToken.None); + await emailStore.SetEmailAsync(user, Input.Email, CancellationToken.None); + + var result = await UserManager.CreateAsync(user); + if (result.Succeeded) + { + result = await UserManager.AddLoginAsync(user, externalLoginInfo); + if (result.Succeeded) + { + Logger.LogInformation("User created an account using {Name} provider.", externalLoginInfo.LoginProvider); + + var userId = await UserManager.GetUserIdAsync(user); + var code = await UserManager.GenerateEmailConfirmationTokenAsync(user); + code = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(code)); + + var callbackUrl = NavigationManager.GetUriWithQueryParameters( + NavigationManager.ToAbsoluteUri("Account/ConfirmEmail").AbsoluteUri, + new Dictionary { ["userId"] = userId, ["code"] = code }); + await EmailSender.SendConfirmationLinkAsync(user, Input.Email, HtmlEncoder.Default.Encode(callbackUrl)); + + // If account confirmation is required, we need to show the link if we don't have a real email sender + if (UserManager.Options.SignIn.RequireConfirmedAccount) + { + RedirectManager.RedirectTo("Account/RegisterConfirmation", new() { ["email"] = Input.Email }); + } + + await SignInManager.SignInAsync(user, isPersistent: false, externalLoginInfo.LoginProvider); + RedirectManager.RedirectTo(ReturnUrl); + } + } + + message = $"Error: {string.Join(",", result.Errors.Select(error => error.Description))}"; + } + + private ApplicationUser CreateUser() + { + try + { + return Activator.CreateInstance(); + } + catch + { + throw new InvalidOperationException($"Can't create an instance of '{nameof(ApplicationUser)}'. " + + $"Ensure that '{nameof(ApplicationUser)}' is not an abstract class and has a parameterless constructor"); + } + } + + private IUserEmailStore GetEmailStore() + { + if (!UserManager.SupportsUserEmail) + { + throw new NotSupportedException("The default UI requires a user store with email support."); + } + return (IUserEmailStore)UserStore; + } + + private sealed class InputModel + { + [Required] + [EmailAddress] + public string Email { get; set; } = ""; + } +} diff --git a/src/Templates/templates/blazorweb-csharp/BlazorWeb-CSharp/Components/Account/Pages/ForgotPassword.razor b/src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp/Components/Account/Pages/ForgotPassword.razor similarity index 100% rename from src/Templates/templates/blazorweb-csharp/BlazorWeb-CSharp/Components/Account/Pages/ForgotPassword.razor rename to src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp/Components/Account/Pages/ForgotPassword.razor diff --git a/src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp/Components/Account/Pages/ForgotPasswordConfirmation.razor b/src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp/Components/Account/Pages/ForgotPasswordConfirmation.razor new file mode 100644 index 0000000000..a771a3a1d4 --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp/Components/Account/Pages/ForgotPasswordConfirmation.razor @@ -0,0 +1,8 @@ +@page "/Account/ForgotPasswordConfirmation" + +Forgot password confirmation + +

Forgot password confirmation

+

+ Please check your email to reset your password. +

diff --git a/src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp/Components/Account/Pages/InvalidPasswordReset.razor b/src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp/Components/Account/Pages/InvalidPasswordReset.razor new file mode 100644 index 0000000000..561b651a4e --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp/Components/Account/Pages/InvalidPasswordReset.razor @@ -0,0 +1,8 @@ +@page "/Account/InvalidPasswordReset" + +Invalid password reset + +

Invalid password reset

+

+ The password reset link is invalid. +

diff --git a/src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp/Components/Account/Pages/InvalidUser.razor b/src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp/Components/Account/Pages/InvalidUser.razor new file mode 100644 index 0000000000..e61fe5def5 --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp/Components/Account/Pages/InvalidUser.razor @@ -0,0 +1,7 @@ +@page "/Account/InvalidUser" + +Invalid user + +

Invalid user

+ + diff --git a/src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp/Components/Account/Pages/Lockout.razor b/src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp/Components/Account/Pages/Lockout.razor new file mode 100644 index 0000000000..017e31d38b --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp/Components/Account/Pages/Lockout.razor @@ -0,0 +1,8 @@ +@page "/Account/Lockout" + +Locked out + +
+

Locked out

+ +
diff --git a/src/Templates/templates/blazorweb-csharp/BlazorWeb-CSharp/Components/Account/Pages/Login.razor b/src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp/Components/Account/Pages/Login.razor similarity index 100% rename from src/Templates/templates/blazorweb-csharp/BlazorWeb-CSharp/Components/Account/Pages/Login.razor rename to src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp/Components/Account/Pages/Login.razor diff --git a/src/Templates/templates/blazorweb-csharp/BlazorWeb-CSharp/Components/Account/Pages/LoginWith2fa.razor b/src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp/Components/Account/Pages/LoginWith2fa.razor similarity index 100% rename from src/Templates/templates/blazorweb-csharp/BlazorWeb-CSharp/Components/Account/Pages/LoginWith2fa.razor rename to src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp/Components/Account/Pages/LoginWith2fa.razor diff --git a/src/Templates/templates/blazorweb-csharp/BlazorWeb-CSharp/Components/Account/Pages/LoginWithRecoveryCode.razor b/src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp/Components/Account/Pages/LoginWithRecoveryCode.razor similarity index 100% rename from src/Templates/templates/blazorweb-csharp/BlazorWeb-CSharp/Components/Account/Pages/LoginWithRecoveryCode.razor rename to src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp/Components/Account/Pages/LoginWithRecoveryCode.razor diff --git a/src/Templates/templates/blazorweb-csharp/BlazorWeb-CSharp/Components/Account/Pages/Manage/ChangePassword.razor b/src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp/Components/Account/Pages/Manage/ChangePassword.razor similarity index 100% rename from src/Templates/templates/blazorweb-csharp/BlazorWeb-CSharp/Components/Account/Pages/Manage/ChangePassword.razor rename to src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp/Components/Account/Pages/Manage/ChangePassword.razor diff --git a/src/Templates/templates/blazorweb-csharp/BlazorWeb-CSharp/Components/Account/Pages/Manage/DeletePersonalData.razor b/src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp/Components/Account/Pages/Manage/DeletePersonalData.razor similarity index 100% rename from src/Templates/templates/blazorweb-csharp/BlazorWeb-CSharp/Components/Account/Pages/Manage/DeletePersonalData.razor rename to src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp/Components/Account/Pages/Manage/DeletePersonalData.razor diff --git a/src/Templates/templates/blazorweb-csharp/BlazorWeb-CSharp/Components/Account/Pages/Manage/Disable2fa.razor b/src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp/Components/Account/Pages/Manage/Disable2fa.razor similarity index 100% rename from src/Templates/templates/blazorweb-csharp/BlazorWeb-CSharp/Components/Account/Pages/Manage/Disable2fa.razor rename to src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp/Components/Account/Pages/Manage/Disable2fa.razor diff --git a/src/Templates/templates/blazorweb-csharp/BlazorWeb-CSharp/Components/Account/Pages/Manage/Email.razor b/src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp/Components/Account/Pages/Manage/Email.razor similarity index 100% rename from src/Templates/templates/blazorweb-csharp/BlazorWeb-CSharp/Components/Account/Pages/Manage/Email.razor rename to src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp/Components/Account/Pages/Manage/Email.razor diff --git a/src/Templates/templates/blazorweb-csharp/BlazorWeb-CSharp/Components/Account/Pages/Manage/EnableAuthenticator.razor b/src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp/Components/Account/Pages/Manage/EnableAuthenticator.razor similarity index 100% rename from src/Templates/templates/blazorweb-csharp/BlazorWeb-CSharp/Components/Account/Pages/Manage/EnableAuthenticator.razor rename to src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp/Components/Account/Pages/Manage/EnableAuthenticator.razor diff --git a/src/Templates/templates/blazorweb-csharp/BlazorWeb-CSharp/Components/Account/Pages/Manage/ExternalLogins.razor b/src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp/Components/Account/Pages/Manage/ExternalLogins.razor similarity index 100% rename from src/Templates/templates/blazorweb-csharp/BlazorWeb-CSharp/Components/Account/Pages/Manage/ExternalLogins.razor rename to src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp/Components/Account/Pages/Manage/ExternalLogins.razor diff --git a/src/Templates/templates/blazorweb-csharp/BlazorWeb-CSharp/Components/Account/Pages/Manage/GenerateRecoveryCodes.razor b/src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp/Components/Account/Pages/Manage/GenerateRecoveryCodes.razor similarity index 100% rename from src/Templates/templates/blazorweb-csharp/BlazorWeb-CSharp/Components/Account/Pages/Manage/GenerateRecoveryCodes.razor rename to src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp/Components/Account/Pages/Manage/GenerateRecoveryCodes.razor diff --git a/src/Templates/templates/blazorweb-csharp/BlazorWeb-CSharp/Components/Account/Pages/Manage/Index.razor b/src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp/Components/Account/Pages/Manage/Index.razor similarity index 100% rename from src/Templates/templates/blazorweb-csharp/BlazorWeb-CSharp/Components/Account/Pages/Manage/Index.razor rename to src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp/Components/Account/Pages/Manage/Index.razor diff --git a/src/Templates/templates/blazorweb-csharp/BlazorWeb-CSharp/Components/Account/Pages/Manage/PersonalData.razor b/src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp/Components/Account/Pages/Manage/PersonalData.razor similarity index 100% rename from src/Templates/templates/blazorweb-csharp/BlazorWeb-CSharp/Components/Account/Pages/Manage/PersonalData.razor rename to src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp/Components/Account/Pages/Manage/PersonalData.razor diff --git a/src/Templates/templates/blazorweb-csharp/BlazorWeb-CSharp/Components/Account/Pages/Manage/ResetAuthenticator.razor b/src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp/Components/Account/Pages/Manage/ResetAuthenticator.razor similarity index 100% rename from src/Templates/templates/blazorweb-csharp/BlazorWeb-CSharp/Components/Account/Pages/Manage/ResetAuthenticator.razor rename to src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp/Components/Account/Pages/Manage/ResetAuthenticator.razor diff --git a/src/Templates/templates/blazorweb-csharp/BlazorWeb-CSharp/Components/Account/Pages/Manage/SetPassword.razor b/src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp/Components/Account/Pages/Manage/SetPassword.razor similarity index 100% rename from src/Templates/templates/blazorweb-csharp/BlazorWeb-CSharp/Components/Account/Pages/Manage/SetPassword.razor rename to src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp/Components/Account/Pages/Manage/SetPassword.razor diff --git a/src/Templates/templates/blazorweb-csharp/BlazorWeb-CSharp/Components/Account/Pages/Manage/TwoFactorAuthentication.razor b/src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp/Components/Account/Pages/Manage/TwoFactorAuthentication.razor similarity index 100% rename from src/Templates/templates/blazorweb-csharp/BlazorWeb-CSharp/Components/Account/Pages/Manage/TwoFactorAuthentication.razor rename to src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp/Components/Account/Pages/Manage/TwoFactorAuthentication.razor diff --git a/src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp/Components/Account/Pages/Manage/_Imports.razor b/src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp/Components/Account/Pages/Manage/_Imports.razor new file mode 100644 index 0000000000..ada5bb010a --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp/Components/Account/Pages/Manage/_Imports.razor @@ -0,0 +1,2 @@ +@layout ManageLayout +@attribute [Microsoft.AspNetCore.Authorization.Authorize] diff --git a/src/Templates/templates/blazorweb-csharp/BlazorWeb-CSharp/Components/Account/Pages/Register.razor b/src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp/Components/Account/Pages/Register.razor similarity index 100% rename from src/Templates/templates/blazorweb-csharp/BlazorWeb-CSharp/Components/Account/Pages/Register.razor rename to src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp/Components/Account/Pages/Register.razor diff --git a/src/Templates/templates/blazorweb-csharp/BlazorWeb-CSharp/Components/Account/Pages/RegisterConfirmation.razor b/src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp/Components/Account/Pages/RegisterConfirmation.razor similarity index 100% rename from src/Templates/templates/blazorweb-csharp/BlazorWeb-CSharp/Components/Account/Pages/RegisterConfirmation.razor rename to src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp/Components/Account/Pages/RegisterConfirmation.razor diff --git a/src/Templates/templates/blazorweb-csharp/BlazorWeb-CSharp/Components/Account/Pages/ResendEmailConfirmation.razor b/src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp/Components/Account/Pages/ResendEmailConfirmation.razor similarity index 100% rename from src/Templates/templates/blazorweb-csharp/BlazorWeb-CSharp/Components/Account/Pages/ResendEmailConfirmation.razor rename to src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp/Components/Account/Pages/ResendEmailConfirmation.razor diff --git a/src/Templates/templates/blazorweb-csharp/BlazorWeb-CSharp/Components/Account/Pages/ResetPassword.razor b/src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp/Components/Account/Pages/ResetPassword.razor similarity index 100% rename from src/Templates/templates/blazorweb-csharp/BlazorWeb-CSharp/Components/Account/Pages/ResetPassword.razor rename to src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp/Components/Account/Pages/ResetPassword.razor diff --git a/src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp/Components/Account/Pages/ResetPasswordConfirmation.razor b/src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp/Components/Account/Pages/ResetPasswordConfirmation.razor new file mode 100644 index 0000000000..247e96eca3 --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp/Components/Account/Pages/ResetPasswordConfirmation.razor @@ -0,0 +1,7 @@ +@page "/Account/ResetPasswordConfirmation" +Reset password confirmation + +

Reset password confirmation

+

+ Your password has been reset. Please click here to log in. +

diff --git a/src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp/Components/Account/Pages/_Imports.razor b/src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp/Components/Account/Pages/_Imports.razor new file mode 100644 index 0000000000..0edf330c06 --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp/Components/Account/Pages/_Imports.razor @@ -0,0 +1,3 @@ +@using BlazorWeb_CSharp.Components.Account.Shared +@layout AccountLayout + diff --git a/src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp/Components/Account/PersistingRevalidatingAuthenticationStateProvider.cs b/src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp/Components/Account/PersistingRevalidatingAuthenticationStateProvider.cs new file mode 100644 index 0000000000..32439c3c53 --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp/Components/Account/PersistingRevalidatingAuthenticationStateProvider.cs @@ -0,0 +1,109 @@ +using System.Diagnostics; +using System.Security.Claims; +using Microsoft.AspNetCore.Components; +using Microsoft.AspNetCore.Components.Authorization; +using Microsoft.AspNetCore.Components.Server; +using Microsoft.AspNetCore.Components.Web; +using Microsoft.AspNetCore.Identity; +using Microsoft.Extensions.Options; +using BlazorWeb_CSharp.Client; +using BlazorWeb_CSharp.Data; + +namespace BlazorWeb_CSharp.Components.Account; + +// This is a server-side AuthenticationStateProvider that revalidates the security stamp for the connected user +// every 30 minutes an interactive circuit is connected. It also uses PersistentComponentState to flow the +// authentication state to the client which is then fixed for the lifetime of the WebAssembly application. +internal sealed class PersistingRevalidatingAuthenticationStateProvider : RevalidatingServerAuthenticationStateProvider +{ + private readonly IServiceScopeFactory scopeFactory; + private readonly PersistentComponentState state; + private readonly IdentityOptions options; + + private readonly PersistingComponentStateSubscription subscription; + + private Task? authenticationStateTask; + + public PersistingRevalidatingAuthenticationStateProvider( + ILoggerFactory loggerFactory, + IServiceScopeFactory serviceScopeFactory, + PersistentComponentState persistentComponentState, + IOptions optionsAccessor) + : base(loggerFactory) + { + scopeFactory = serviceScopeFactory; + state = persistentComponentState; + options = optionsAccessor.Value; + + AuthenticationStateChanged += OnAuthenticationStateChanged; + subscription = state.RegisterOnPersisting(OnPersistingAsync, RenderMode.InteractiveWebAssembly); + } + + protected override TimeSpan RevalidationInterval => TimeSpan.FromMinutes(30); + + protected override async Task ValidateAuthenticationStateAsync( + AuthenticationState authenticationState, CancellationToken cancellationToken) + { + // Get the user manager from a new scope to ensure it fetches fresh data + await using var scope = scopeFactory.CreateAsyncScope(); + var userManager = scope.ServiceProvider.GetRequiredService>(); + return await ValidateSecurityStampAsync(userManager, authenticationState.User); + } + + private async Task ValidateSecurityStampAsync(UserManager userManager, ClaimsPrincipal principal) + { + var user = await userManager.GetUserAsync(principal); + if (user is null) + { + return false; + } + else if (!userManager.SupportsUserSecurityStamp) + { + return true; + } + else + { + var principalStamp = principal.FindFirstValue(options.ClaimsIdentity.SecurityStampClaimType); + var userStamp = await userManager.GetSecurityStampAsync(user); + return principalStamp == userStamp; + } + } + + private void OnAuthenticationStateChanged(Task task) + { + authenticationStateTask = task; + } + + private async Task OnPersistingAsync() + { + if (authenticationStateTask is null) + { + throw new UnreachableException($"Authentication state not set in {nameof(OnPersistingAsync)}()."); + } + + var authenticationState = await authenticationStateTask; + var principal = authenticationState.User; + + if (principal.Identity?.IsAuthenticated == true) + { + var userId = principal.FindFirst(options.ClaimsIdentity.UserIdClaimType)?.Value; + var email = principal.FindFirst(options.ClaimsIdentity.EmailClaimType)?.Value; + + if (userId != null && email != null) + { + state.PersistAsJson(nameof(UserInfo), new UserInfo + { + UserId = userId, + Email = email, + }); + } + } + } + + protected override void Dispose(bool disposing) + { + subscription.Dispose(); + AuthenticationStateChanged -= OnAuthenticationStateChanged; + base.Dispose(disposing); + } +} diff --git a/src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp/Components/Account/PersistingServerAuthenticationStateProvider.cs b/src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp/Components/Account/PersistingServerAuthenticationStateProvider.cs new file mode 100644 index 0000000000..c7e967e77a --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp/Components/Account/PersistingServerAuthenticationStateProvider.cs @@ -0,0 +1,70 @@ +using System.Diagnostics; +using Microsoft.AspNetCore.Components; +using Microsoft.AspNetCore.Components.Authorization; +using Microsoft.AspNetCore.Components.Server; +using Microsoft.AspNetCore.Components.Web; +using Microsoft.AspNetCore.Identity; +using Microsoft.Extensions.Options; +using BlazorWeb_CSharp.Client; + +namespace BlazorWeb_CSharp.Components.Account; + +// This is a server-side AuthenticationStateProvider that uses PersistentComponentState to flow the +// authentication state to the client which is then fixed for the lifetime of the WebAssembly application. +internal sealed class PersistingServerAuthenticationStateProvider : ServerAuthenticationStateProvider, IDisposable +{ + private readonly PersistentComponentState state; + private readonly IdentityOptions options; + + private readonly PersistingComponentStateSubscription subscription; + + private Task? authenticationStateTask; + + public PersistingServerAuthenticationStateProvider( + PersistentComponentState persistentComponentState, + IOptions optionsAccessor) + { + state = persistentComponentState; + options = optionsAccessor.Value; + + AuthenticationStateChanged += OnAuthenticationStateChanged; + subscription = state.RegisterOnPersisting(OnPersistingAsync, RenderMode.InteractiveWebAssembly); + } + + private void OnAuthenticationStateChanged(Task task) + { + authenticationStateTask = task; + } + + private async Task OnPersistingAsync() + { + if (authenticationStateTask is null) + { + throw new UnreachableException($"Authentication state not set in {nameof(OnPersistingAsync)}()."); + } + + var authenticationState = await authenticationStateTask; + var principal = authenticationState.User; + + if (principal.Identity?.IsAuthenticated == true) + { + var userId = principal.FindFirst(options.ClaimsIdentity.UserIdClaimType)?.Value; + var email = principal.FindFirst(options.ClaimsIdentity.EmailClaimType)?.Value; + + if (userId != null && email != null) + { + state.PersistAsJson(nameof(UserInfo), new UserInfo + { + UserId = userId, + Email = email, + }); + } + } + } + + public void Dispose() + { + subscription.Dispose(); + AuthenticationStateChanged -= OnAuthenticationStateChanged; + } +} diff --git a/src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp/Components/Account/Shared/AccountLayout.razor b/src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp/Components/Account/Shared/AccountLayout.razor new file mode 100644 index 0000000000..13680eae9d --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp/Components/Account/Shared/AccountLayout.razor @@ -0,0 +1,32 @@ +@inherits LayoutComponentBase +@*#if (UseWebAssembly && InteractiveAtRoot) +@layout BlazorWeb_CSharp.Client.Layout.MainLayout +##else +@layout BlazorWeb_CSharp.Components.Layout.MainLayout +##endif*@ +@inject NavigationManager NavigationManager + +@if (HttpContext is null) +{ +

Loading...

+} +else +{ + @Body +} + +@code { + [CascadingParameter] + private HttpContext? HttpContext { get; set; } + + protected override void OnParametersSet() + { + if (HttpContext is null) + { + // If this code runs, we're currently rendering in interactive mode, so there is no HttpContext. + // The identity pages need to set cookies, so they require an HttpContext. To achieve this we + // must transition back from interactive mode to a server-rendered page. + NavigationManager.Refresh(forceReload: true); + } + } +} diff --git a/src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp/Components/Account/Shared/ExternalLoginPicker.razor b/src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp/Components/Account/Shared/ExternalLoginPicker.razor new file mode 100644 index 0000000000..9894a7efa1 --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp/Components/Account/Shared/ExternalLoginPicker.razor @@ -0,0 +1,44 @@ +@using Microsoft.AspNetCore.Authentication +@using Microsoft.AspNetCore.Identity +@using BlazorWeb_CSharp.Data + +@inject SignInManager SignInManager +@inject IdentityRedirectManager RedirectManager + +@if (externalLogins.Length == 0) +{ +
+

+ There are no external authentication services configured. See this article + about setting up this ASP.NET application to support logging in via external services. +

+
+} +else +{ +
+
+ + +

+ @foreach (var provider in externalLogins) + { + @provider.DisplayName +   + } +

+
+
+} + +@code { + private AuthenticationScheme[] externalLogins = []; + + [SupplyParameterFromQuery] + private string? ReturnUrl { get; set; } + + protected override async Task OnInitializedAsync() + { + externalLogins = (await SignInManager.GetExternalAuthenticationSchemesAsync()).ToArray(); + } +} diff --git a/src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp/Components/Account/Shared/ManageLayout.razor b/src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp/Components/Account/Shared/ManageLayout.razor new file mode 100644 index 0000000000..b985ba8bc3 --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp/Components/Account/Shared/ManageLayout.razor @@ -0,0 +1,18 @@ +@inherits LayoutComponentBase +@layout AccountLayout + +

Manage your account

+ +
+

Change your account settings

+
+ + + + + + + @Body + + +
diff --git a/src/Templates/templates/blazorweb-csharp/BlazorWeb-CSharp/Components/Account/Shared/ManageNavMenu.razor b/src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp/Components/Account/Shared/ManageNavMenu.razor similarity index 100% rename from src/Templates/templates/blazorweb-csharp/BlazorWeb-CSharp/Components/Account/Shared/ManageNavMenu.razor rename to src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp/Components/Account/Shared/ManageNavMenu.razor diff --git a/src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp/Components/Account/Shared/RedirectToLogin.razor b/src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp/Components/Account/Shared/RedirectToLogin.razor new file mode 100644 index 0000000000..c8b8eff4af --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp/Components/Account/Shared/RedirectToLogin.razor @@ -0,0 +1,8 @@ +@inject NavigationManager NavigationManager + +@code { + protected override void OnInitialized() + { + NavigationManager.NavigateTo($"Account/Login?returnUrl={Uri.EscapeDataString(NavigationManager.Uri)}", forceLoad: true); + } +} diff --git a/src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp/Components/Account/Shared/ShowRecoveryCodes.razor b/src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp/Components/Account/Shared/ShowRecoveryCodes.razor new file mode 100644 index 0000000000..038afba67a --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp/Components/Account/Shared/ShowRecoveryCodes.razor @@ -0,0 +1,31 @@ + +

Recovery codes

+ + + + @foreach (var recoveryCode in RecoveryCodes) + { +
+ @recoveryCode +
+ } + +
+ +
+ + +@code { + [Parameter] + public string[] RecoveryCodes { get; set; } = []; + + [Parameter] + public string? StatusMessage { get; set; } +} diff --git a/src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp/Components/Account/Shared/StatusMessage.razor b/src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp/Components/Account/Shared/StatusMessage.razor new file mode 100644 index 0000000000..12cd544cfe --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp/Components/Account/Shared/StatusMessage.razor @@ -0,0 +1,29 @@ +@if (!string.IsNullOrEmpty(DisplayMessage)) +{ + var statusMessageClass = DisplayMessage.StartsWith("Error") ? "danger" : "success"; + +} + +@code { + private string? messageFromCookie; + + [Parameter] + public string? Message { get; set; } + + [CascadingParameter] + private HttpContext HttpContext { get; set; } = default!; + + private string? DisplayMessage => Message ?? messageFromCookie; + + protected override void OnInitialized() + { + messageFromCookie = HttpContext.Request.Cookies[IdentityRedirectManager.StatusCookieName]; + + if (messageFromCookie is not null) + { + HttpContext.Response.Cookies.Delete(IdentityRedirectManager.StatusCookieName); + } + } +} diff --git a/src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp/Components/App.razor b/src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp/Components/App.razor new file mode 100644 index 0000000000..d16714d164 --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp/Components/App.razor @@ -0,0 +1,73 @@ + + + + + + + + + + @*#if (SampleContent) + + ##endif*@ + @*#if (!InteractiveAtRoot) + + ##elseif (IndividualLocalAuth) + + ##elseif (UseServer && UseWebAssembly) + + ##elseif (UseServer) + + ##else + + ##endif*@ + + + + @*#if (!InteractiveAtRoot) + + ##elseif (IndividualLocalAuth) + + ##elseif (UseServer && UseWebAssembly) + + ##elseif (UseServer) + + ##else + + ##endif*@ + + + + +@*#if (!InteractiveAtRoot || !IndividualLocalAuth) +#elseif (UseServer && UseWebAssembly) + +@code { + [CascadingParameter] + private HttpContext HttpContext { get; set; } = default!; + + private IComponentRenderMode? RenderModeForPage => HttpContext.Request.Path.StartsWithSegments("/Account") + ? null + : InteractiveAuto; +} +#elseif (UseServer) + +@code { + [CascadingParameter] + private HttpContext HttpContext { get; set; } = default!; + + private IComponentRenderMode? RenderModeForPage => HttpContext.Request.Path.StartsWithSegments("/Account") + ? null + : InteractiveServer; +} +#else + +@code { + [CascadingParameter] + private HttpContext HttpContext { get; set; } = default!; + + private IComponentRenderMode? RenderModeForPage => HttpContext.Request.Path.StartsWithSegments("/Account") + ? null + : InteractiveWebAssembly; +} +#endif*@ diff --git a/src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp/Components/Layout/MainLayout.razor b/src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp/Components/Layout/MainLayout.razor new file mode 100644 index 0000000000..0792dcde42 --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp/Components/Layout/MainLayout.razor @@ -0,0 +1,34 @@ +@inherits LayoutComponentBase + +@*#if (SampleContent) --> + + + BlazorWeb-CSharp + + + + +
+ @Body +
+
+
+ + Documentation and demos + + About Blazor + +
+##else +
+ @Body +
+##endif*@ +@*#if (UseServer || UseWebAssembly) --> + +
+ An unhandled error has occurred. + Reload + 🗙 +
+##endif*@ diff --git a/src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp/Components/Layout/NavMenu.razor b/src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp/Components/Layout/NavMenu.razor new file mode 100644 index 0000000000..1ac6a6f28d --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp/Components/Layout/NavMenu.razor @@ -0,0 +1,64 @@ +@*#if (UseServer && !InteractiveAtRoot) --> +@rendermode InteractiveServer + +##endif*@ +@*#if (IndividualLocalAuth) +@implements IDisposable + +@inject NavigationManager NavigationManager + +##endif*@ + + +@code { + private bool expanded = true; +@*#if (IndividualLocalAuth) + private string? currentUrl; + + protected override void OnInitialized() + { + currentUrl = NavigationManager.ToBaseRelativePath(NavigationManager.Uri); + NavigationManager.LocationChanged += OnLocationChanged; + } + + private void OnLocationChanged(object? sender, LocationChangedEventArgs e) + { + currentUrl = NavigationManager.ToBaseRelativePath(e.Location); + StateHasChanged(); + } + + public void Dispose() + { + NavigationManager.LocationChanged -= OnLocationChanged; + } +##endif*@ +} diff --git a/src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp/Components/Pages/Auth.razor b/src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp/Components/Pages/Auth.razor new file mode 100644 index 0000000000..b7bbe6eb3d --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp/Components/Pages/Auth.razor @@ -0,0 +1,13 @@ +@page "/auth" + +@using Microsoft.AspNetCore.Authorization + +@attribute [Authorize] + +Auth + +

You are authenticated

+ + + Hello @context.User.Identity?.Name! + diff --git a/src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp/Components/Pages/Counter.razor b/src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp/Components/Pages/Counter.razor new file mode 100644 index 0000000000..c19f296f01 --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp/Components/Pages/Counter.razor @@ -0,0 +1,23 @@ +@page "/counter" +@*#if (!InteractiveAtRoot) --> +@rendermode InteractiveServer +##endif*@ + +Counter + +

Counter

+ +
+ Current count: @currentCount +
+ +Click me + +@code { + private int currentCount = 0; + + private void IncrementCount() + { + currentCount++; + } +} diff --git a/src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp/Components/Pages/Error.razor b/src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp/Components/Pages/Error.razor new file mode 100644 index 0000000000..576cc2d2f4 --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp/Components/Pages/Error.razor @@ -0,0 +1,36 @@ +@page "/Error" +@using System.Diagnostics + +Error + +

Error.

+

An error occurred while processing your request.

+ +@if (ShowRequestId) +{ +

+ Request ID: @RequestId +

+} + +

Development Mode

+

+ Swapping to Development environment will display more detailed information about the error that occurred. +

+

+ The Development environment shouldn't be enabled for deployed applications. + It can result in displaying sensitive information from exceptions to end users. + For local debugging, enable the Development environment by setting the ASPNETCORE_ENVIRONMENT environment variable to Development + and restarting the app. +

+ +@code{ + [CascadingParameter] + private HttpContext? HttpContext { get; set; } + + private string? RequestId { get; set; } + private bool ShowRequestId => !string.IsNullOrEmpty(RequestId); + + protected override void OnInitialized() => + RequestId = Activity.Current?.Id ?? HttpContext?.TraceIdentifier; +} diff --git a/src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp/Components/Pages/Home.razor b/src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp/Components/Pages/Home.razor new file mode 100644 index 0000000000..96714a2131 --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp/Components/Pages/Home.razor @@ -0,0 +1,7 @@ +@page "/" + +Home + +

Hello, world!

+ +Welcome to your new Fluent Blazor app. \ No newline at end of file diff --git a/src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp/Components/Pages/Weather.razor b/src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp/Components/Pages/Weather.razor new file mode 100644 index 0000000000..3986c09a4c --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp/Components/Pages/Weather.razor @@ -0,0 +1,65 @@ +@page "/weather" +@*#if (!InteractiveAtRoot) --> +@attribute [StreamRendering] +##endif*@ + +Weather + +

Weather

+ +

This component demonstrates showing data.

+ +@*#if (InteractiveAtRoot) --> +@if (forecasts == null) +{ +

Loading...

+} +else +{ + + + + + + +} +##else + + + + + + + +##endif*@ + +@code { + private IQueryable? forecasts; + + protected override async Task OnInitializedAsync() + { +@*#if (InteractiveAtRoot) --> + // Simulate asynchronous loading to demonstrate a loading indicator +##else + // Simulate asynchronous loading to demonstrate streaming rendering +##endif*@ + await Task.Delay(500); + + var startDate = DateOnly.FromDateTime(DateTime.Now); + var summaries = new[] { "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching" }; + forecasts = Enumerable.Range(1, 5).Select(index => new WeatherForecast + { + Date = startDate.AddDays(index), + TemperatureC = Random.Shared.Next(-20, 55), + Summary = summaries[Random.Shared.Next(summaries.Length)] + }).AsQueryable(); + } + + private class WeatherForecast + { + public DateOnly Date { get; set; } + public int TemperatureC { get; set; } + public string? Summary { get; set; } + public int TemperatureF => 32 + (int)(TemperatureC / 0.5556); + } +} diff --git a/src/Templates/templates/blazorweb-csharp/BlazorWeb-CSharp/Components/Routes.razor b/src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp/Components/Routes.razor similarity index 100% rename from src/Templates/templates/blazorweb-csharp/BlazorWeb-CSharp/Components/Routes.razor rename to src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp/Components/Routes.razor diff --git a/src/Templates/templates/blazorweb-csharp/BlazorWeb-CSharp/Components/_Imports.razor b/src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp/Components/_Imports.razor similarity index 100% rename from src/Templates/templates/blazorweb-csharp/BlazorWeb-CSharp/Components/_Imports.razor rename to src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp/Components/_Imports.razor diff --git a/src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp/Data/ApplicationDbContext.cs b/src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp/Data/ApplicationDbContext.cs new file mode 100644 index 0000000000..2ce33e233a --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp/Data/ApplicationDbContext.cs @@ -0,0 +1,8 @@ +using Microsoft.AspNetCore.Identity.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore; + +namespace BlazorWeb_CSharp.Data; + +public class ApplicationDbContext(DbContextOptions options) : IdentityDbContext(options) +{ +} diff --git a/src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp/Data/ApplicationUser.cs b/src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp/Data/ApplicationUser.cs new file mode 100644 index 0000000000..831bb92a4a --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp/Data/ApplicationUser.cs @@ -0,0 +1,9 @@ +using Microsoft.AspNetCore.Identity; + +namespace BlazorWeb_CSharp.Data; + +// Add profile data for application users by adding properties to the ApplicationUser class +public class ApplicationUser : IdentityUser +{ +} + diff --git a/src/Templates/templates/blazorweb-csharp/BlazorWeb-CSharp/Data/SqlLite/00000000000000_CreateIdentitySchema.Designer.cs b/src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp/Data/SqlLite/00000000000000_CreateIdentitySchema.Designer.cs similarity index 100% rename from src/Templates/templates/blazorweb-csharp/BlazorWeb-CSharp/Data/SqlLite/00000000000000_CreateIdentitySchema.Designer.cs rename to src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp/Data/SqlLite/00000000000000_CreateIdentitySchema.Designer.cs diff --git a/src/Templates/templates/blazorweb-csharp/BlazorWeb-CSharp/Data/SqlLite/00000000000000_CreateIdentitySchema.cs b/src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp/Data/SqlLite/00000000000000_CreateIdentitySchema.cs similarity index 100% rename from src/Templates/templates/blazorweb-csharp/BlazorWeb-CSharp/Data/SqlLite/00000000000000_CreateIdentitySchema.cs rename to src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp/Data/SqlLite/00000000000000_CreateIdentitySchema.cs diff --git a/src/Templates/templates/blazorweb-csharp/BlazorWeb-CSharp/Data/SqlLite/ApplicationDbContextModelSnapshot.cs b/src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp/Data/SqlLite/ApplicationDbContextModelSnapshot.cs similarity index 100% rename from src/Templates/templates/blazorweb-csharp/BlazorWeb-CSharp/Data/SqlLite/ApplicationDbContextModelSnapshot.cs rename to src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp/Data/SqlLite/ApplicationDbContextModelSnapshot.cs diff --git a/src/Templates/templates/blazorweb-csharp/BlazorWeb-CSharp/Data/SqlServer/00000000000000_CreateIdentitySchema.Designer.cs b/src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp/Data/SqlServer/00000000000000_CreateIdentitySchema.Designer.cs similarity index 100% rename from src/Templates/templates/blazorweb-csharp/BlazorWeb-CSharp/Data/SqlServer/00000000000000_CreateIdentitySchema.Designer.cs rename to src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp/Data/SqlServer/00000000000000_CreateIdentitySchema.Designer.cs diff --git a/src/Templates/templates/blazorweb-csharp/BlazorWeb-CSharp/Data/SqlServer/00000000000000_CreateIdentitySchema.cs b/src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp/Data/SqlServer/00000000000000_CreateIdentitySchema.cs similarity index 100% rename from src/Templates/templates/blazorweb-csharp/BlazorWeb-CSharp/Data/SqlServer/00000000000000_CreateIdentitySchema.cs rename to src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp/Data/SqlServer/00000000000000_CreateIdentitySchema.cs diff --git a/src/Templates/templates/blazorweb-csharp/BlazorWeb-CSharp/Data/SqlServer/ApplicationDbContextModelSnapshot.cs b/src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp/Data/SqlServer/ApplicationDbContextModelSnapshot.cs similarity index 100% rename from src/Templates/templates/blazorweb-csharp/BlazorWeb-CSharp/Data/SqlServer/ApplicationDbContextModelSnapshot.cs rename to src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp/Data/SqlServer/ApplicationDbContextModelSnapshot.cs diff --git a/src/Templates/templates/blazorweb-csharp/BlazorWeb-CSharp/Data/app.db b/src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp/Data/app.db similarity index 100% rename from src/Templates/templates/blazorweb-csharp/BlazorWeb-CSharp/Data/app.db rename to src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp/Data/app.db diff --git a/src/Templates/templates/blazorweb-csharp/BlazorWeb-CSharp/Program.Main.cs b/src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp/Program.Main.cs similarity index 100% rename from src/Templates/templates/blazorweb-csharp/BlazorWeb-CSharp/Program.Main.cs rename to src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp/Program.Main.cs diff --git a/src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp/Program.cs b/src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp/Program.cs new file mode 100644 index 0000000000..7b5804385f --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp/Program.cs @@ -0,0 +1,132 @@ +#if (IndividualLocalAuth) +using Microsoft.AspNetCore.Components.Authorization; +#if (!UseServer && !UseWebAssembly) +using Microsoft.AspNetCore.Components.Server; +#endif +using Microsoft.AspNetCore.Identity; +using Microsoft.EntityFrameworkCore; +#endif +#if (UseWebAssembly && SampleContent) +using BlazorWeb_CSharp.Client.Pages; +#endif +using BlazorWeb_CSharp.Components; +#if (IndividualLocalAuth) +using BlazorWeb_CSharp.Components.Account; +using BlazorWeb_CSharp.Data; +#endif +using Microsoft.FluentUI.AspNetCore.Components; + +var builder = WebApplication.CreateBuilder(args); + +// Add services to the container. +#if (!UseServer && !UseWebAssembly) +builder.Services.AddRazorComponents(); +#else +builder.Services.AddRazorComponents() +#if (UseServer && UseWebAssembly) + .AddInteractiveServerComponents() + .AddInteractiveWebAssemblyComponents(); +#elif (UseServer) + .AddInteractiveServerComponents(); +#elif (UseWebAssembly) + .AddInteractiveWebAssemblyComponents(); +#endif +#endif +builder.Services.AddFluentUIComponents(); + +#if (IndividualLocalAuth) +builder.Services.AddCascadingAuthenticationState(); +builder.Services.AddScoped(); +builder.Services.AddScoped(); +#if (UseServer && UseWebAssembly) +builder.Services.AddScoped(); +#elif (UseServer) +builder.Services.AddScoped(); +#elif (UseWebAssembly) +builder.Services.AddScoped(); +#else +builder.Services.AddScoped(); +#endif + +#if (!UseServer) +builder.Services.AddAuthorization(); +#endif +builder.Services.AddAuthentication(options => + { + options.DefaultScheme = IdentityConstants.ApplicationScheme; + options.DefaultSignInScheme = IdentityConstants.ExternalScheme; + }) + .AddIdentityCookies(); + +var connectionString = builder.Configuration.GetConnectionString("DefaultConnection") ?? throw new InvalidOperationException("Connection string 'DefaultConnection' not found."); +builder.Services.AddDbContext(options => +#if (UseLocalDB) + options.UseSqlServer(connectionString)); +#else + options.UseSqlite(connectionString)); +#endif +builder.Services.AddDatabaseDeveloperPageExceptionFilter(); + +builder.Services.AddIdentityCore(options => options.SignIn.RequireConfirmedAccount = true) + .AddEntityFrameworkStores() + .AddSignInManager() + .AddDefaultTokenProviders(); + +builder.Services.AddSingleton, IdentityNoOpEmailSender>(); + +#endif +var app = builder.Build(); + +// Configure the HTTP request pipeline. +#if (UseWebAssembly || IndividualLocalAuth) +if (app.Environment.IsDevelopment()) +{ +#if (UseWebAssembly) + app.UseWebAssemblyDebugging(); +#endif +#if (IndividualLocalAuth) + app.UseMigrationsEndPoint(); +#endif +} +else +#else +if (!app.Environment.IsDevelopment()) +#endif +{ + app.UseExceptionHandler("/Error", createScopeForErrors: true); +#if (HasHttpsProfile) + // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts. + app.UseHsts(); +#endif +} + +#if (HasHttpsProfile) +app.UseHttpsRedirection(); + +#endif +app.UseStaticFiles(); +app.UseAntiforgery(); + +#if (UseServer && UseWebAssembly) +app.MapRazorComponents() + .AddInteractiveServerRenderMode() + .AddInteractiveWebAssemblyRenderMode() +#elif (UseServer) +app.MapRazorComponents() + .AddInteractiveServerRenderMode(); +#elif (UseWebAssembly) +app.MapRazorComponents() + .AddInteractiveWebAssemblyRenderMode() +#else +app.MapRazorComponents(); +#endif +#if (UseWebAssembly) + .AddAdditionalAssemblies(typeof(BlazorWeb_CSharp.Client._Imports).Assembly); +#endif + +#if (IndividualLocalAuth) +// Add additional endpoints required by the Identity /Account Razor components. +app.MapAdditionalIdentityEndpoints(); + +#endif +app.Run(); diff --git a/src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp/Properties/launchSettings.json b/src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp/Properties/launchSettings.json new file mode 100644 index 0000000000..0d554caaa1 --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp/Properties/launchSettings.json @@ -0,0 +1,37 @@ +{ + "$schema": "https://json.schemastore.org/launchsettings.json", + "profiles": { + //#if (HasHttpProfile) + "http": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + //#if (UseWebAssembly) + "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}", + //#endif + "applicationUrl": "http://localhost:5500", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + //#if (HasHttpsProfile) + }, + //#else + } + //#endif + //#endif + //#if (HasHttpsProfile) + "https": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + //#if (UseWebAssembly) + "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}", + //#endif + "applicationUrl": "https://localhost:5501;http://localhost:5500", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + //#endif + } + } diff --git a/src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp/appsettings.Development.json b/src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp/appsettings.Development.json new file mode 100644 index 0000000000..0c208ae918 --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp/appsettings.Development.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + } +} diff --git a/src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp/appsettings.json b/src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp/appsettings.json new file mode 100644 index 0000000000..3a18c82e2d --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp/appsettings.json @@ -0,0 +1,18 @@ +{ +////#if (IndividualLocalAuth) +// "ConnectionStrings": { +//#if (UseLocalDB) +// "DefaultConnection": "Server=(localdb)\\mssqllocaldb;Database=aspnet-BlazorWeb_CSharp-53bc9b9d-9d6a-45d4-8429-2a2761773502;Trusted_Connection=True;MultipleActiveResultSets=true" +//#else +// "DefaultConnection": "DataSource=Data\\app.db;Cache=Shared" +//#endif +// }, +////#endif + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*" +} diff --git a/src/Templates/templates/blazorweb-csharp/BlazorWeb-CSharp/wwwroot/app.css b/src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp/wwwroot/app.css similarity index 100% rename from src/Templates/templates/blazorweb-csharp/BlazorWeb-CSharp/wwwroot/app.css rename to src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp/wwwroot/app.css diff --git a/src/Templates/templates/componentswebassembly-csharp/wwwroot/favicon.ico b/src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp/wwwroot/favicon.ico similarity index 100% rename from src/Templates/templates/componentswebassembly-csharp/wwwroot/favicon.ico rename to src/Templates/templates/blazorweb-csharp-8/BlazorWeb-CSharp/wwwroot/favicon.ico diff --git a/src/Templates/templates/blazorweb-csharp-9/.template.config/dotnetcli.host.json b/src/Templates/templates/blazorweb-csharp-9/.template.config/dotnetcli.host.json new file mode 100644 index 0000000000..9c853513c3 --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-9/.template.config/dotnetcli.host.json @@ -0,0 +1,61 @@ +{ + "$schema": "https://json.schemastore.org/dotnetcli.host", + "symbolInfo": { + "skipRestore": { + "longName": "no-restore", + "shortName": "" + }, + "InteractivityPlatform": { + "longName": "interactivity", + "shortName": "int" + }, + "AllInteractive": { + "longName": "all-interactive" + }, + "InteractivityLocation": { + "isHidden": true + }, + "Empty": { + "longName": "empty" + }, + "IncludeSampleContent": { + "isHidden": true + }, + "UseLocalDB": { + "longName": "use-local-db" + }, + "Framework": { + "longName": "framework" + }, + "kestrelHttpPort": { + "isHidden": true + }, + "kestrelHttpsPort": { + "isHidden": true + }, + "iisHttpPort": { + "isHidden": true + }, + "iisHttpsPort": { + "isHidden": true + }, + "ExcludeLaunchSettings": { + "longName": "exclude-launch-settings", + "shortName": "" + }, + "UserSecretsId": { + "isHidden": true + }, + "NoHttps": { + "longName": "no-https", + "shortName": "" + }, + "UseProgramMain": { + "longName": "use-program-main", + "shortName": "" + } + }, + "usageExamples": [ + "-int auto --auth individual --use-local-db" + ] +} diff --git a/src/Templates/templates/blazorweb-csharp-9/.template.config/icon.png b/src/Templates/templates/blazorweb-csharp-9/.template.config/icon.png new file mode 100644 index 0000000000..42fb1d1efe Binary files /dev/null and b/src/Templates/templates/blazorweb-csharp-9/.template.config/icon.png differ diff --git a/src/Templates/templates/blazorweb-csharp-9/.template.config/ide.host.json b/src/Templates/templates/blazorweb-csharp-9/.template.config/ide.host.json new file mode 100644 index 0000000000..5c5a1d00b7 --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-9/.template.config/ide.host.json @@ -0,0 +1,30 @@ +{ + "$schema": "http://json.schemastore.org/ide.host", + "order": 610, + "icon": "icon.png", + "disableHttpsSymbol": "NoHttps", + "symbolInfo": [ + { + "id": "InteractivityPlatform", + "isVisible": true, + "persistenceScope": "templateGroup" + }, + { + "id": "InteractivityLocation", + "isVisible": true, + "persistenceScope": "templateGroup" + }, + { + "id": "IncludeSampleContent", + "isVisible": true, + "persistenceScope": "shared", + "persistenceScopeName": "Microsoft" + }, + { + "id": "UseProgramMain", + "isVisible": true, + "persistenceScope": "shared", + "persistenceScopeName": "Microsoft" + } + ] +} diff --git a/src/Templates/templates/blazorweb-csharp/.template.config/localize/templatestrings.cs.json b/src/Templates/templates/blazorweb-csharp-9/.template.config/localize/templatestrings.cs.json similarity index 100% rename from src/Templates/templates/blazorweb-csharp/.template.config/localize/templatestrings.cs.json rename to src/Templates/templates/blazorweb-csharp-9/.template.config/localize/templatestrings.cs.json diff --git a/src/Templates/templates/blazorweb-csharp/.template.config/localize/templatestrings.de.json b/src/Templates/templates/blazorweb-csharp-9/.template.config/localize/templatestrings.de.json similarity index 100% rename from src/Templates/templates/blazorweb-csharp/.template.config/localize/templatestrings.de.json rename to src/Templates/templates/blazorweb-csharp-9/.template.config/localize/templatestrings.de.json diff --git a/src/Templates/templates/blazorweb-csharp/.template.config/localize/templatestrings.en.json b/src/Templates/templates/blazorweb-csharp-9/.template.config/localize/templatestrings.en.json similarity index 100% rename from src/Templates/templates/blazorweb-csharp/.template.config/localize/templatestrings.en.json rename to src/Templates/templates/blazorweb-csharp-9/.template.config/localize/templatestrings.en.json diff --git a/src/Templates/templates/blazorweb-csharp/.template.config/localize/templatestrings.es.json b/src/Templates/templates/blazorweb-csharp-9/.template.config/localize/templatestrings.es.json similarity index 100% rename from src/Templates/templates/blazorweb-csharp/.template.config/localize/templatestrings.es.json rename to src/Templates/templates/blazorweb-csharp-9/.template.config/localize/templatestrings.es.json diff --git a/src/Templates/templates/blazorweb-csharp/.template.config/localize/templatestrings.fr.json b/src/Templates/templates/blazorweb-csharp-9/.template.config/localize/templatestrings.fr.json similarity index 100% rename from src/Templates/templates/blazorweb-csharp/.template.config/localize/templatestrings.fr.json rename to src/Templates/templates/blazorweb-csharp-9/.template.config/localize/templatestrings.fr.json diff --git a/src/Templates/templates/blazorweb-csharp/.template.config/localize/templatestrings.it.json b/src/Templates/templates/blazorweb-csharp-9/.template.config/localize/templatestrings.it.json similarity index 100% rename from src/Templates/templates/blazorweb-csharp/.template.config/localize/templatestrings.it.json rename to src/Templates/templates/blazorweb-csharp-9/.template.config/localize/templatestrings.it.json diff --git a/src/Templates/templates/blazorweb-csharp/.template.config/localize/templatestrings.ja.json b/src/Templates/templates/blazorweb-csharp-9/.template.config/localize/templatestrings.ja.json similarity index 100% rename from src/Templates/templates/blazorweb-csharp/.template.config/localize/templatestrings.ja.json rename to src/Templates/templates/blazorweb-csharp-9/.template.config/localize/templatestrings.ja.json diff --git a/src/Templates/templates/blazorweb-csharp/.template.config/localize/templatestrings.ko.json b/src/Templates/templates/blazorweb-csharp-9/.template.config/localize/templatestrings.ko.json similarity index 100% rename from src/Templates/templates/blazorweb-csharp/.template.config/localize/templatestrings.ko.json rename to src/Templates/templates/blazorweb-csharp-9/.template.config/localize/templatestrings.ko.json diff --git a/src/Templates/templates/blazorweb-csharp/.template.config/localize/templatestrings.pl.json b/src/Templates/templates/blazorweb-csharp-9/.template.config/localize/templatestrings.pl.json similarity index 100% rename from src/Templates/templates/blazorweb-csharp/.template.config/localize/templatestrings.pl.json rename to src/Templates/templates/blazorweb-csharp-9/.template.config/localize/templatestrings.pl.json diff --git a/src/Templates/templates/blazorweb-csharp/.template.config/localize/templatestrings.pt-BR.json b/src/Templates/templates/blazorweb-csharp-9/.template.config/localize/templatestrings.pt-BR.json similarity index 100% rename from src/Templates/templates/blazorweb-csharp/.template.config/localize/templatestrings.pt-BR.json rename to src/Templates/templates/blazorweb-csharp-9/.template.config/localize/templatestrings.pt-BR.json diff --git a/src/Templates/templates/blazorweb-csharp/.template.config/localize/templatestrings.ru.json b/src/Templates/templates/blazorweb-csharp-9/.template.config/localize/templatestrings.ru.json similarity index 100% rename from src/Templates/templates/blazorweb-csharp/.template.config/localize/templatestrings.ru.json rename to src/Templates/templates/blazorweb-csharp-9/.template.config/localize/templatestrings.ru.json diff --git a/src/Templates/templates/blazorweb-csharp/.template.config/localize/templatestrings.tr.json b/src/Templates/templates/blazorweb-csharp-9/.template.config/localize/templatestrings.tr.json similarity index 100% rename from src/Templates/templates/blazorweb-csharp/.template.config/localize/templatestrings.tr.json rename to src/Templates/templates/blazorweb-csharp-9/.template.config/localize/templatestrings.tr.json diff --git a/src/Templates/templates/blazorweb-csharp/.template.config/localize/templatestrings.zh-Hans.json b/src/Templates/templates/blazorweb-csharp-9/.template.config/localize/templatestrings.zh-Hans.json similarity index 100% rename from src/Templates/templates/blazorweb-csharp/.template.config/localize/templatestrings.zh-Hans.json rename to src/Templates/templates/blazorweb-csharp-9/.template.config/localize/templatestrings.zh-Hans.json diff --git a/src/Templates/templates/blazorweb-csharp/.template.config/localize/templatestrings.zh-Hant.json b/src/Templates/templates/blazorweb-csharp-9/.template.config/localize/templatestrings.zh-Hant.json similarity index 100% rename from src/Templates/templates/blazorweb-csharp/.template.config/localize/templatestrings.zh-Hant.json rename to src/Templates/templates/blazorweb-csharp-9/.template.config/localize/templatestrings.zh-Hant.json diff --git a/src/Templates/templates/blazorweb-csharp/.template.config/template.json b/src/Templates/templates/blazorweb-csharp-9/.template.config/template.json similarity index 99% rename from src/Templates/templates/blazorweb-csharp/.template.config/template.json rename to src/Templates/templates/blazorweb-csharp-9/.template.config/template.json index 96129b4f6a..81e7f55dd3 100644 --- a/src/Templates/templates/blazorweb-csharp/.template.config/template.json +++ b/src/Templates/templates/blazorweb-csharp-9/.template.config/template.json @@ -204,16 +204,12 @@ "description": "The target framework for the project.", "datatype": "choice", "choices": [ - { - "choice": "net8.0", - "description": "Target net8.0" - }, { "choice": "net9.0", "description": "Target net9.0" } ], - "replaces": "net8.0", + "replaces": "net9.0", "defaultValue": "net9.0" }, "HostIdentifier": { diff --git a/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp.Client/BlazorWeb-CSharp.Client.csproj b/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp.Client/BlazorWeb-CSharp.Client.csproj new file mode 100644 index 0000000000..504d249c64 --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp.Client/BlazorWeb-CSharp.Client.csproj @@ -0,0 +1,20 @@ + + + + net9.0 + enable + enable + true + Default + BlazorWeb-CSharp.Client + $(AssemblyName.Replace(' ', '_')) + + + + + + + + + + diff --git a/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp.Client/Layout/NavMenu.razor b/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp.Client/Layout/NavMenu.razor new file mode 100644 index 0000000000..80a5626cb6 --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp.Client/Layout/NavMenu.razor @@ -0,0 +1,65 @@ +@*#if (UseServer && !InteractiveAtRoot) +@rendermode InteractiveAuto +##elseif (!InteractiveAtRoot) +@rendermode InteractiveWebAssembly +##endif*@ + +@*#if (IndividualLocalAuth) +@implements IDisposable + +@inject NavigationManager NavigationManager + +##endif*@ + + +@code { + private bool expanded = true; + + @*#if (IndividualLocalAuth) + private string? currentUrl; + + protected override void OnInitialized() + { + currentUrl = NavigationManager.ToBaseRelativePath(NavigationManager.Uri); + NavigationManager.LocationChanged += OnLocationChanged; + } + + private void OnLocationChanged(object? sender, LocationChangedEventArgs e) + { + currentUrl = NavigationManager.ToBaseRelativePath(e.Location); + StateHasChanged(); + } + + public void Dispose() + { + NavigationManager.LocationChanged -= OnLocationChanged; + } +##endif*@ +} diff --git a/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp.Client/Pages/Auth.razor b/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp.Client/Pages/Auth.razor new file mode 100644 index 0000000000..6b805cfa02 --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp.Client/Pages/Auth.razor @@ -0,0 +1,18 @@ +@page "/auth" + +@using Microsoft.AspNetCore.Authorization + +@attribute [Authorize] +@*#if (UseServer && !InteractiveAtRoot) +@rendermode InteractiveAuto +##elseif (!InteractiveAtRoot) +@rendermode InteractiveWebAssembly +##endif*@ + +Auth + +

You are authenticated

+ + + Hello @context.User.Identity?.Name! + diff --git a/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp.Client/Pages/Counter.razor b/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp.Client/Pages/Counter.razor new file mode 100644 index 0000000000..7f672c7ab7 --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp.Client/Pages/Counter.razor @@ -0,0 +1,25 @@ +@page "/counter" +@*#if (UseServer && !InteractiveAtRoot) +@rendermode InteractiveAuto +##elseif (!InteractiveAtRoot) +@rendermode InteractiveWebAssembly +##endif*@ + +Counter + +

Counter

+ +
+ Current count: @currentCount +
+ +Click me + +@code { + private int currentCount = 0; + + private void IncrementCount() + { + currentCount++; + } +} diff --git a/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp.Client/Program.Main.cs b/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp.Client/Program.Main.cs new file mode 100644 index 0000000000..20605af0f7 --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp.Client/Program.Main.cs @@ -0,0 +1,19 @@ +using Microsoft.AspNetCore.Components.WebAssembly.Hosting; + +namespace BlazorWeb_CSharp.Client; + +class Program +{ + static async Task Main(string[] args) + { + var builder = WebAssemblyHostBuilder.CreateDefault(args); + +#if (IndividualLocalAuth) + builder.Services.AddAuthorizationCore(); + builder.Services.AddCascadingAuthenticationState(); + builder.Services.AddAuthenticationStateDeserialization(); + +#endif + await builder.Build().RunAsync(); + } +} diff --git a/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp.Client/Program.cs b/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp.Client/Program.cs new file mode 100644 index 0000000000..19becf2331 --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp.Client/Program.cs @@ -0,0 +1,11 @@ +using Microsoft.AspNetCore.Components.WebAssembly.Hosting; + +var builder = WebAssemblyHostBuilder.CreateDefault(args); + +#if (IndividualLocalAuth) +builder.Services.AddAuthorizationCore(); +builder.Services.AddCascadingAuthenticationState(); +builder.Services.AddAuthenticationStateDeserialization(); + +#endif +await builder.Build().RunAsync(); diff --git a/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp.Client/_Imports.razor b/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp.Client/_Imports.razor new file mode 100644 index 0000000000..5bd14a2f73 --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp.Client/_Imports.razor @@ -0,0 +1,14 @@ +@using System.Net.Http +@using System.Net.Http.Json +@*#if (IndividualLocalAuth) +@using Microsoft.AspNetCore.Components.Authorization +##endif*@ +@using Microsoft.AspNetCore.Components.Forms +@using Microsoft.AspNetCore.Components.Routing +@using Microsoft.AspNetCore.Components.Web +@using static Microsoft.AspNetCore.Components.Web.RenderMode +@using Microsoft.AspNetCore.Components.Web.Virtualization +@using Microsoft.FluentUI.AspNetCore.Components +@using Icons = Microsoft.FluentUI.AspNetCore.Components.Icons +@using Microsoft.JSInterop +@using BlazorWeb_CSharp.Client diff --git a/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp.Client/wwwroot/appsettings.Development.json b/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp.Client/wwwroot/appsettings.Development.json new file mode 100644 index 0000000000..0c208ae918 --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp.Client/wwwroot/appsettings.Development.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + } +} diff --git a/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp.Client/wwwroot/appsettings.json b/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp.Client/wwwroot/appsettings.json new file mode 100644 index 0000000000..0c208ae918 --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp.Client/wwwroot/appsettings.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + } +} diff --git a/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp.sln b/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp.sln new file mode 100644 index 0000000000..88a77b704b --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp.sln @@ -0,0 +1,50 @@ +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.0.0 +MinimumVisualStudioVersion = 16.0.0.0 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BlazorWeb-CSharp", "BlazorWeb-CSharp\BlazorWeb-CSharp.csproj", "{650B3CE7-2E93-4CC4-9F46-466686815EAA}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BlazorWeb-CSharp.Client", "BlazorWeb-CSharp.Client\BlazorWeb-CSharp.Client.csproj", "{5990939C-7E7B-4CFA-86FF-44CA5756498A}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {5990939C-7E7B-4CFA-86FF-44CA5756498A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5990939C-7E7B-4CFA-86FF-44CA5756498A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5990939C-7E7B-4CFA-86FF-44CA5756498A}.Debug|x64.ActiveCfg = Debug|Any CPU + {5990939C-7E7B-4CFA-86FF-44CA5756498A}.Debug|x64.Build.0 = Debug|Any CPU + {5990939C-7E7B-4CFA-86FF-44CA5756498A}.Debug|x86.ActiveCfg = Debug|Any CPU + {5990939C-7E7B-4CFA-86FF-44CA5756498A}.Debug|x86.Build.0 = Debug|Any CPU + {5990939C-7E7B-4CFA-86FF-44CA5756498A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5990939C-7E7B-4CFA-86FF-44CA5756498A}.Release|Any CPU.Build.0 = Release|Any CPU + {5990939C-7E7B-4CFA-86FF-44CA5756498A}.Release|x64.ActiveCfg = Release|Any CPU + {5990939C-7E7B-4CFA-86FF-44CA5756498A}.Release|x64.Build.0 = Release|Any CPU + {5990939C-7E7B-4CFA-86FF-44CA5756498A}.Release|x86.ActiveCfg = Release|Any CPU + {5990939C-7E7B-4CFA-86FF-44CA5756498A}.Release|x86.Build.0 = Release|Any CPU + {650B3CE7-2E93-4CC4-9F46-466686815EAA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {650B3CE7-2E93-4CC4-9F46-466686815EAA}.Debug|Any CPU.Build.0 = Debug|Any CPU + {650B3CE7-2E93-4CC4-9F46-466686815EAA}.Debug|x64.ActiveCfg = Debug|Any CPU + {650B3CE7-2E93-4CC4-9F46-466686815EAA}.Debug|x64.Build.0 = Debug|Any CPU + {650B3CE7-2E93-4CC4-9F46-466686815EAA}.Debug|x86.ActiveCfg = Debug|Any CPU + {650B3CE7-2E93-4CC4-9F46-466686815EAA}.Debug|x86.Build.0 = Debug|Any CPU + {650B3CE7-2E93-4CC4-9F46-466686815EAA}.Release|Any CPU.ActiveCfg = Release|Any CPU + {650B3CE7-2E93-4CC4-9F46-466686815EAA}.Release|Any CPU.Build.0 = Release|Any CPU + {650B3CE7-2E93-4CC4-9F46-466686815EAA}.Release|x64.ActiveCfg = Release|Any CPU + {650B3CE7-2E93-4CC4-9F46-466686815EAA}.Release|x64.Build.0 = Release|Any CPU + {650B3CE7-2E93-4CC4-9F46-466686815EAA}.Release|x86.ActiveCfg = Release|Any CPU + {650B3CE7-2E93-4CC4-9F46-466686815EAA}.Release|x86.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {4C26868E-5E7C-458D-82E3-040509D0C71F} + EndGlobalSection +EndGlobal diff --git a/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/BlazorWeb-CSharp.csproj b/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/BlazorWeb-CSharp.csproj new file mode 100644 index 0000000000..45a87636af --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/BlazorWeb-CSharp.csproj @@ -0,0 +1,35 @@ + + + + net9.0 + enable + enable + aspnet-BlazorWeb-CSharp-53bc9b9d-9d6a-45d4-8429-2a2761773502 + True + BlazorWeb-CSharp + $(AssemblyName.Replace(' ', '_')) + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Templates/templates/blazorweb-csharp/BlazorWeb-CSharp/Components/Account/IdentityComponentsEndpointRouteBuilderExtensions.cs b/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Components/Account/IdentityComponentsEndpointRouteBuilderExtensions.cs similarity index 100% rename from src/Templates/templates/blazorweb-csharp/BlazorWeb-CSharp/Components/Account/IdentityComponentsEndpointRouteBuilderExtensions.cs rename to src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Components/Account/IdentityComponentsEndpointRouteBuilderExtensions.cs diff --git a/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Components/Account/IdentityNoOpEmailSender.cs b/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Components/Account/IdentityNoOpEmailSender.cs new file mode 100644 index 0000000000..1256a02a78 --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Components/Account/IdentityNoOpEmailSender.cs @@ -0,0 +1,20 @@ +using Microsoft.AspNetCore.Identity; +using Microsoft.AspNetCore.Identity.UI.Services; +using BlazorWeb_CSharp.Data; + +namespace BlazorWeb_CSharp.Components.Account; + +// Remove the "else if (EmailSender is IdentityNoOpEmailSender)" block from RegisterConfirmation.razor after updating with a real implementation. +internal sealed class IdentityNoOpEmailSender : IEmailSender +{ + private readonly IEmailSender emailSender = new NoOpEmailSender(); + + public Task SendConfirmationLinkAsync(ApplicationUser user, string email, string confirmationLink) => + emailSender.SendEmailAsync(email, "Confirm your email", $"Please confirm your account by clicking here."); + + public Task SendPasswordResetLinkAsync(ApplicationUser user, string email, string resetLink) => + emailSender.SendEmailAsync(email, "Reset your password", $"Please reset your password by clicking here."); + + public Task SendPasswordResetCodeAsync(ApplicationUser user, string email, string resetCode) => + emailSender.SendEmailAsync(email, "Reset your password", $"Please reset your password using the following code: {resetCode}"); +} diff --git a/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Components/Account/IdentityRedirectManager.cs b/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Components/Account/IdentityRedirectManager.cs new file mode 100644 index 0000000000..da85e6efd4 --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Components/Account/IdentityRedirectManager.cs @@ -0,0 +1,58 @@ +using System.Diagnostics.CodeAnalysis; +using Microsoft.AspNetCore.Components; + +namespace BlazorWeb_CSharp.Components.Account; + +internal sealed class IdentityRedirectManager(NavigationManager navigationManager) +{ + public const string StatusCookieName = "Identity.StatusMessage"; + + private static readonly CookieBuilder StatusCookieBuilder = new() + { + SameSite = SameSiteMode.Strict, + HttpOnly = true, + IsEssential = true, + MaxAge = TimeSpan.FromSeconds(5), + }; + + [DoesNotReturn] + public void RedirectTo(string? uri) + { + uri ??= ""; + + // Prevent open redirects. + if (!Uri.IsWellFormedUriString(uri, UriKind.Relative)) + { + uri = navigationManager.ToBaseRelativePath(uri); + } + + // During static rendering, NavigateTo throws a NavigationException which is handled by the framework as a redirect. + // So as long as this is called from a statically rendered Identity component, the InvalidOperationException is never thrown. + navigationManager.NavigateTo(uri); + throw new InvalidOperationException($"{nameof(IdentityRedirectManager)} can only be used during static rendering."); + } + + [DoesNotReturn] + public void RedirectTo(string uri, Dictionary queryParameters) + { + var uriWithoutQuery = navigationManager.ToAbsoluteUri(uri).GetLeftPart(UriPartial.Path); + var newUri = navigationManager.GetUriWithQueryParameters(uriWithoutQuery, queryParameters); + RedirectTo(newUri); + } + + [DoesNotReturn] + public void RedirectToWithStatus(string uri, string message, HttpContext context) + { + context.Response.Cookies.Append(StatusCookieName, message, StatusCookieBuilder.Build(context)); + RedirectTo(uri); + } + + private string CurrentPath => navigationManager.ToAbsoluteUri(navigationManager.Uri).GetLeftPart(UriPartial.Path); + + [DoesNotReturn] + public void RedirectToCurrentPage() => RedirectTo(CurrentPath); + + [DoesNotReturn] + public void RedirectToCurrentPageWithStatus(string message, HttpContext context) + => RedirectToWithStatus(CurrentPath, message, context); +} diff --git a/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Components/Account/IdentityRevalidatingAuthenticationStateProvider.cs b/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Components/Account/IdentityRevalidatingAuthenticationStateProvider.cs new file mode 100644 index 0000000000..cf8bf6d893 --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Components/Account/IdentityRevalidatingAuthenticationStateProvider.cs @@ -0,0 +1,47 @@ +using System.Security.Claims; +using Microsoft.AspNetCore.Components.Authorization; +using Microsoft.AspNetCore.Components.Server; +using Microsoft.AspNetCore.Identity; +using Microsoft.Extensions.Options; +using BlazorWeb_CSharp.Data; + +namespace BlazorWeb_CSharp.Components.Account; + +// This is a server-side AuthenticationStateProvider that revalidates the security stamp for the connected user +// every 30 minutes an interactive circuit is connected. +internal sealed class IdentityRevalidatingAuthenticationStateProvider( + ILoggerFactory loggerFactory, + IServiceScopeFactory scopeFactory, + IOptions options) + : RevalidatingServerAuthenticationStateProvider(loggerFactory) +{ + protected override TimeSpan RevalidationInterval => TimeSpan.FromMinutes(30); + + protected override async Task ValidateAuthenticationStateAsync( + AuthenticationState authenticationState, CancellationToken cancellationToken) + { + // Get the user manager from a new scope to ensure it fetches fresh data + await using var scope = scopeFactory.CreateAsyncScope(); + var userManager = scope.ServiceProvider.GetRequiredService>(); + return await ValidateSecurityStampAsync(userManager, authenticationState.User); + } + + private async Task ValidateSecurityStampAsync(UserManager userManager, ClaimsPrincipal principal) + { + var user = await userManager.GetUserAsync(principal); + if (user is null) + { + return false; + } + else if (!userManager.SupportsUserSecurityStamp) + { + return true; + } + else + { + var principalStamp = principal.FindFirstValue(options.Value.ClaimsIdentity.SecurityStampClaimType); + var userStamp = await userManager.GetSecurityStampAsync(user); + return principalStamp == userStamp; + } + } +} diff --git a/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Components/Account/IdentityUserAccessor.cs b/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Components/Account/IdentityUserAccessor.cs new file mode 100644 index 0000000000..86e027c0b6 --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Components/Account/IdentityUserAccessor.cs @@ -0,0 +1,19 @@ +using Microsoft.AspNetCore.Identity; +using BlazorWeb_CSharp.Data; + +namespace BlazorWeb_CSharp.Components.Account; + +internal sealed class IdentityUserAccessor(UserManager userManager, IdentityRedirectManager redirectManager) +{ + public async Task GetRequiredUserAsync(HttpContext context) + { + var user = await userManager.GetUserAsync(context.User); + + if (user is null) + { + redirectManager.RedirectToWithStatus("Account/InvalidUser", $"Error: Unable to load user with ID '{userManager.GetUserId(context.User)}'.", context); + } + + return user; + } +} diff --git a/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Components/Account/Pages/AccessDenied.razor b/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Components/Account/Pages/AccessDenied.razor new file mode 100644 index 0000000000..905dec3487 --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Components/Account/Pages/AccessDenied.razor @@ -0,0 +1,8 @@ +@page "/Account/AccessDenied" + +Access denied + +
+

Access denied

+

You do not have access to this resource.

+
diff --git a/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Components/Account/Pages/ConfirmEmail.razor b/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Components/Account/Pages/ConfirmEmail.razor new file mode 100644 index 0000000000..830ee204c8 --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Components/Account/Pages/ConfirmEmail.razor @@ -0,0 +1,48 @@ +@page "/Account/ConfirmEmail" + +@using System.Text +@using Microsoft.AspNetCore.Identity +@using Microsoft.AspNetCore.WebUtilities +@using BlazorWeb_CSharp.Data + +@inject UserManager UserManager +@inject IdentityRedirectManager RedirectManager + +Confirm email + +

Confirm email

+ + +@code { + private string? statusMessage; + + [CascadingParameter] + private HttpContext HttpContext { get; set; } = default!; + + [SupplyParameterFromQuery] + private string? UserId { get; set; } + + [SupplyParameterFromQuery] + private string? Code { get; set; } + + protected override async Task OnInitializedAsync() + { + if (UserId is null || Code is null) + { + RedirectManager.RedirectTo(""); + } + + var user = await UserManager.FindByIdAsync(UserId); + if (user is null) + { + HttpContext.Response.StatusCode = StatusCodes.Status404NotFound; + statusMessage = $"Error loading user with ID {UserId}"; + } + else + { + var code = Encoding.UTF8.GetString(WebEncoders.Base64UrlDecode(Code)); + var result = await UserManager.ConfirmEmailAsync(user, code); + statusMessage = result.Succeeded ? "Thank you for confirming your email." : "Error confirming your email."; + } + } +} diff --git a/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Components/Account/Pages/ConfirmEmailChange.razor b/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Components/Account/Pages/ConfirmEmailChange.razor new file mode 100644 index 0000000000..478a810def --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Components/Account/Pages/ConfirmEmailChange.razor @@ -0,0 +1,68 @@ +@page "/Account/ConfirmEmailChange" + +@using System.Text +@using Microsoft.AspNetCore.Identity +@using Microsoft.AspNetCore.WebUtilities +@using BlazorWeb_CSharp.Data + +@inject UserManager UserManager +@inject SignInManager SignInManager +@inject IdentityRedirectManager RedirectManager + +Confirm email change + +

Confirm email change

+ + + +@code { + private string? message; + + [CascadingParameter] + private HttpContext HttpContext { get; set; } = default!; + + [SupplyParameterFromQuery] + private string? UserId { get; set; } + + [SupplyParameterFromQuery] + private string? Email { get; set; } + + [SupplyParameterFromQuery] + private string? Code { get; set; } + + protected override async Task OnInitializedAsync() + { + if (UserId is null || Email is null || Code is null) + { + RedirectManager.RedirectToWithStatus( + "Account/Login", "Error: Invalid email change confirmation link.", HttpContext); + } + + var user = await UserManager.FindByIdAsync(UserId); + if (user is null) + { + message = "Unable to find user with Id '{userId}'"; + return; + } + + var code = Encoding.UTF8.GetString(WebEncoders.Base64UrlDecode(Code)); + var result = await UserManager.ChangeEmailAsync(user, Email, code); + if (!result.Succeeded) + { + message = "Error changing email."; + return; + } + + // In our UI email and user name are one and the same, so when we update the email + // we need to update the user name. + var setUserNameResult = await UserManager.SetUserNameAsync(user, Email); + if (!setUserNameResult.Succeeded) + { + message = "Error changing user name."; + return; + } + + await SignInManager.RefreshSignInAsync(user); + message = "Thank you for confirming your email change."; + } +} diff --git a/src/Templates/templates/blazorweb-csharp/BlazorWeb-CSharp/Components/Account/Pages/ExternalLogin.razor b/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Components/Account/Pages/ExternalLogin.razor similarity index 100% rename from src/Templates/templates/blazorweb-csharp/BlazorWeb-CSharp/Components/Account/Pages/ExternalLogin.razor rename to src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Components/Account/Pages/ExternalLogin.razor diff --git a/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Components/Account/Pages/ForgotPassword.razor b/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Components/Account/Pages/ForgotPassword.razor new file mode 100644 index 0000000000..1f7a264451 --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Components/Account/Pages/ForgotPassword.razor @@ -0,0 +1,64 @@ +@page "/Account/ForgotPassword" + +@using System.ComponentModel.DataAnnotations +@using System.Text +@using System.Text.Encodings.Web +@using Microsoft.AspNetCore.Identity +@using Microsoft.AspNetCore.WebUtilities +@using BlazorWeb_CSharp.Data + +@inject UserManager UserManager +@inject IEmailSender EmailSender +@inject NavigationManager NavigationManager +@inject IdentityRedirectManager RedirectManager + +Forgot your password? + +

Forgot your password?

+

Enter your email.

+
+ + + + + + + + Reset password + + + + +@code { + [SupplyParameterFromForm] + private InputModel Input { get; set; } = new(); + + private async Task OnValidSubmitAsync() + { + var user = await UserManager.FindByEmailAsync(Input.Email); + if (user is null || !(await UserManager.IsEmailConfirmedAsync(user))) + { + // Don't reveal that the user does not exist or is not confirmed + RedirectManager.RedirectTo("Account/ForgotPasswordConfirmation"); + } + + // For more information on how to enable account confirmation and password reset please + // visit https://go.microsoft.com/fwlink/?LinkID=532713 + var code = await UserManager.GeneratePasswordResetTokenAsync(user); + code = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(code)); + var callbackUrl = NavigationManager.GetUriWithQueryParameters( + NavigationManager.ToAbsoluteUri("Account/ResetPassword").AbsoluteUri, + new Dictionary { ["code"] = code }); + + await EmailSender.SendPasswordResetLinkAsync(user, Input.Email, HtmlEncoder.Default.Encode(callbackUrl)); + + RedirectManager.RedirectTo("Account/ForgotPasswordConfirmation"); + } + + private sealed class InputModel + { + [Required] + [EmailAddress] + public string Email { get; set; } = ""; + } +} diff --git a/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Components/Account/Pages/ForgotPasswordConfirmation.razor b/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Components/Account/Pages/ForgotPasswordConfirmation.razor new file mode 100644 index 0000000000..a771a3a1d4 --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Components/Account/Pages/ForgotPasswordConfirmation.razor @@ -0,0 +1,8 @@ +@page "/Account/ForgotPasswordConfirmation" + +Forgot password confirmation + +

Forgot password confirmation

+

+ Please check your email to reset your password. +

diff --git a/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Components/Account/Pages/InvalidPasswordReset.razor b/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Components/Account/Pages/InvalidPasswordReset.razor new file mode 100644 index 0000000000..561b651a4e --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Components/Account/Pages/InvalidPasswordReset.razor @@ -0,0 +1,8 @@ +@page "/Account/InvalidPasswordReset" + +Invalid password reset + +

Invalid password reset

+

+ The password reset link is invalid. +

diff --git a/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Components/Account/Pages/InvalidUser.razor b/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Components/Account/Pages/InvalidUser.razor new file mode 100644 index 0000000000..e61fe5def5 --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Components/Account/Pages/InvalidUser.razor @@ -0,0 +1,7 @@ +@page "/Account/InvalidUser" + +Invalid user + +

Invalid user

+ + diff --git a/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Components/Account/Pages/Lockout.razor b/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Components/Account/Pages/Lockout.razor new file mode 100644 index 0000000000..017e31d38b --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Components/Account/Pages/Lockout.razor @@ -0,0 +1,8 @@ +@page "/Account/Lockout" + +Locked out + +
+

Locked out

+ +
diff --git a/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Components/Account/Pages/Login.razor b/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Components/Account/Pages/Login.razor new file mode 100644 index 0000000000..d9c24c8e0e --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Components/Account/Pages/Login.razor @@ -0,0 +1,113 @@ +@page "/Account/Login" + +@using System.ComponentModel.DataAnnotations +@using Microsoft.AspNetCore.Authentication +@using Microsoft.AspNetCore.Identity +@using BlazorWeb_CSharp.Data + +@inject SignInManager SignInManager +@inject ILogger Logger +@inject NavigationManager NavigationManager +@inject IdentityRedirectManager RedirectManager + +Log in + +

Log in

+ + + + + +

Use a local account to log in.

+
+ + + + + + + + Log in +
+

+ Forgot your password? +

+

+ { ["ReturnUrl"] = ReturnUrl }))">Register as a new user +

+

+ Resend email confirmation +

+
+
+
+
+ +

Use another service to log in.

+
+ +
+
+ +@code { + private string? errorMessage; + + [CascadingParameter] + private HttpContext HttpContext { get; set; } = default!; + + [SupplyParameterFromForm] + private InputModel Input { get; set; } = new(); + + [SupplyParameterFromQuery] + private string? ReturnUrl { get; set; } + + protected override async Task OnInitializedAsync() + { + if (HttpMethods.IsGet(HttpContext.Request.Method)) + { + // Clear the existing external cookie to ensure a clean login process + await HttpContext.SignOutAsync(IdentityConstants.ExternalScheme); + } + } + + public async Task LoginUser() + { + // This doesn't count login failures towards account lockout + // To enable password failures to trigger account lockout, set lockoutOnFailure: true + var result = await SignInManager.PasswordSignInAsync(Input.Email, Input.Password, Input.RememberMe, lockoutOnFailure: false); + if (result.Succeeded) + { + Logger.LogInformation("User logged in."); + RedirectManager.RedirectTo(ReturnUrl); + } + else if (result.RequiresTwoFactor) + { + RedirectManager.RedirectTo( + "Account/LoginWith2fa", + new() { ["returnUrl"] = ReturnUrl, ["rememberMe"] = Input.RememberMe }); + } + else if (result.IsLockedOut) + { + Logger.LogWarning("User account locked out."); + RedirectManager.RedirectTo("Account/Lockout"); + } + else + { + errorMessage = "Error: Invalid login attempt."; + } + } + + private sealed class InputModel + { + [Required] + [EmailAddress] + public string Email { get; set; } = ""; + + [Required] + [DataType(DataType.Password)] + public string Password { get; set; } = ""; + + [Display(Name = "Remember me?")] + public bool RememberMe { get; set; } + } +} diff --git a/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Components/Account/Pages/LoginWith2fa.razor b/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Components/Account/Pages/LoginWith2fa.razor new file mode 100644 index 0000000000..ea4fd4ff69 --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Components/Account/Pages/LoginWith2fa.razor @@ -0,0 +1,91 @@ +@page "/Account/LoginWith2fa" + +@using System.ComponentModel.DataAnnotations +@using Microsoft.AspNetCore.Identity +@using BlazorWeb_CSharp.Data + +@inject SignInManager SignInManager +@inject UserManager UserManager +@inject IdentityRedirectManager RedirectManager +@inject ILogger Logger + +Two-factor authentication + +

Two-factor authentication

+
+ +

Your login is protected with an authenticator app. Enter your authenticator code below.

+ + + + + + + + + + + Log in + + + +

+ Don't have access to your authenticator device? You can + log in with a recovery code. +

+ +@code { + private string? message; + private ApplicationUser user = default!; + + [SupplyParameterFromForm] + private InputModel Input { get; set; } = new(); + + [SupplyParameterFromQuery] + private string? ReturnUrl { get; set; } + + [SupplyParameterFromQuery] + private bool RememberMe { get; set; } + + protected override async Task OnInitializedAsync() + { + // Ensure the user has gone through the username & password screen first + user = await SignInManager.GetTwoFactorAuthenticationUserAsync() ?? + throw new InvalidOperationException("Unable to load two-factor authentication user."); + } + + private async Task OnValidSubmitAsync() + { + var authenticatorCode = Input.TwoFactorCode!.Replace(" ", string.Empty).Replace("-", string.Empty); + var result = await SignInManager.TwoFactorAuthenticatorSignInAsync(authenticatorCode, RememberMe, Input.RememberMachine); + var userId = await UserManager.GetUserIdAsync(user); + + if (result.Succeeded) + { + Logger.LogInformation("User with ID '{UserId}' logged in with 2fa.", userId); + RedirectManager.RedirectTo(ReturnUrl); + } + else if (result.IsLockedOut) + { + Logger.LogWarning("User with ID '{UserId}' account locked out.", userId); + RedirectManager.RedirectTo("Account/Lockout"); + } + else + { + Logger.LogWarning("Invalid authenticator code entered for user with ID '{UserId}'.", userId); + message = "Error: Invalid authenticator code."; + } + } + + private sealed class InputModel + { + [Required] + [StringLength(7, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 6)] + [DataType(DataType.Text)] + [Display(Name = "Authenticator code")] + public string? TwoFactorCode { get; set; } + + [Display(Name = "Remember this machine")] + public bool RememberMachine { get; set; } + } +} diff --git a/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Components/Account/Pages/LoginWithRecoveryCode.razor b/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Components/Account/Pages/LoginWithRecoveryCode.razor new file mode 100644 index 0000000000..5b7559db1f --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Components/Account/Pages/LoginWithRecoveryCode.razor @@ -0,0 +1,81 @@ +@page "/Account/LoginWithRecoveryCode" + +@using System.ComponentModel.DataAnnotations +@using Microsoft.AspNetCore.Identity +@using BlazorWeb_CSharp.Data + +@inject SignInManager SignInManager +@inject UserManager UserManager +@inject IdentityRedirectManager RedirectManager +@inject ILogger Logger + +Recovery code verification + +

Recovery code verification

+
+ +

+ You have requested to log in with a recovery code. This login will not be remembered until you provide + an authenticator app code at log in or disable 2FA and log in again. +

+ + + + + + + + Log in + + + +@code { + private string? message; + private ApplicationUser user = default!; + + [SupplyParameterFromForm] + private InputModel Input { get; set; } = new(); + + [SupplyParameterFromQuery] + private string? ReturnUrl { get; set; } + + protected override async Task OnInitializedAsync() + { + // Ensure the user has gone through the username & password screen first + user = await SignInManager.GetTwoFactorAuthenticationUserAsync() ?? + throw new InvalidOperationException("Unable to load two-factor authentication user."); + } + + private async Task OnValidSubmitAsync() + { + var recoveryCode = Input.RecoveryCode.Replace(" ", string.Empty); + + var result = await SignInManager.TwoFactorRecoveryCodeSignInAsync(recoveryCode); + + var userId = await UserManager.GetUserIdAsync(user); + + if (result.Succeeded) + { + Logger.LogInformation("User with ID '{UserId}' logged in with a recovery code.", userId); + RedirectManager.RedirectTo(ReturnUrl); + } + else if (result.IsLockedOut) + { + Logger.LogWarning("User account locked out."); + RedirectManager.RedirectTo("Account/Lockout"); + } + else + { + Logger.LogWarning("Invalid recovery code entered for user with ID '{UserId}' ", userId); + message = "Error: Invalid recovery code entered."; + } + } + + private sealed class InputModel + { + [Required] + [DataType(DataType.Text)] + [Display(Name = "Recovery Code")] + public string RecoveryCode { get; set; } = ""; + } +} diff --git a/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Components/Account/Pages/Manage/ChangePassword.razor b/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Components/Account/Pages/Manage/ChangePassword.razor new file mode 100644 index 0000000000..2c9f4277c3 --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Components/Account/Pages/Manage/ChangePassword.razor @@ -0,0 +1,87 @@ +@page "/Account/Manage/ChangePassword" + +@using System.ComponentModel.DataAnnotations +@using Microsoft.AspNetCore.Identity +@using BlazorWeb_CSharp.Data + +@inject UserManager UserManager +@inject SignInManager SignInManager +@inject IdentityUserAccessor UserAccessor +@inject IdentityRedirectManager RedirectManager +@inject ILogger Logger + +Change password + +

Change password

+ + + + + + + + + + + + + Update password + + + + +@code { + private string? message; + private ApplicationUser user = default!; + private bool hasPassword; + + [CascadingParameter] + private HttpContext HttpContext { get; set; } = default!; + + [SupplyParameterFromForm] + private InputModel Input { get; set; } = new(); + + protected override async Task OnInitializedAsync() + { + user = await UserAccessor.GetRequiredUserAsync(HttpContext); + hasPassword = await UserManager.HasPasswordAsync(user); + if (!hasPassword) + { + RedirectManager.RedirectTo("Account/Manage/SetPassword"); + } + } + + private async Task OnValidSubmitAsync() + { + var changePasswordResult = await UserManager.ChangePasswordAsync(user, Input.OldPassword, Input.NewPassword); + if (!changePasswordResult.Succeeded) + { + message = $"Error: {string.Join(",", changePasswordResult.Errors.Select(error => error.Description))}"; + return; + } + + await SignInManager.RefreshSignInAsync(user); + Logger.LogInformation("User changed their password successfully."); + + RedirectManager.RedirectToCurrentPageWithStatus("Your password has been changed", HttpContext); + } + + private sealed class InputModel + { + [Required] + [DataType(DataType.Password)] + [Display(Name = "Current password")] + public string OldPassword { get; set; } = ""; + + [Required] + [StringLength(100, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 6)] + [DataType(DataType.Password)] + [Display(Name = "New password")] + public string NewPassword { get; set; } = ""; + + [DataType(DataType.Password)] + [Display(Name = "Confirm new password")] + [Compare("NewPassword", ErrorMessage = "The new password and confirmation password do not match.")] + public string ConfirmPassword { get; set; } = ""; + } +} diff --git a/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Components/Account/Pages/Manage/DeletePersonalData.razor b/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Components/Account/Pages/Manage/DeletePersonalData.razor new file mode 100644 index 0000000000..1e1993f08b --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Components/Account/Pages/Manage/DeletePersonalData.razor @@ -0,0 +1,83 @@ +@page "/Account/Manage/DeletePersonalData" + +@using System.ComponentModel.DataAnnotations +@using Microsoft.AspNetCore.Identity +@using BlazorWeb_CSharp.Data + +@inject UserManager UserManager +@inject SignInManager SignInManager +@inject IdentityUserAccessor UserAccessor +@inject IdentityRedirectManager RedirectManager +@inject ILogger Logger + +Delete Personal Data + + + +

Delete Personal Data

+ + + + + + + + @if (requirePassword) + { + + + } + Delete data and close my account + + + +@code { + private string? message; + private ApplicationUser user = default!; + private bool requirePassword; + + [CascadingParameter] + private HttpContext HttpContext { get; set; } = default!; + + [SupplyParameterFromForm] + private InputModel Input { get; set; } = new(); + + protected override async Task OnInitializedAsync() + { + Input ??= new(); + user = await UserAccessor.GetRequiredUserAsync(HttpContext); + requirePassword = await UserManager.HasPasswordAsync(user); + } + + private async Task OnValidSubmitAsync() + { + if (requirePassword && !await UserManager.CheckPasswordAsync(user, Input.Password)) + { + message = "Error: Incorrect password."; + return; + } + + var result = await UserManager.DeleteAsync(user); + if (!result.Succeeded) + { + throw new InvalidOperationException("Unexpected error occurred deleting user."); + } + + await SignInManager.SignOutAsync(); + + var userId = await UserManager.GetUserIdAsync(user); + Logger.LogInformation("User with ID '{UserId}' deleted themselves.", userId); + + RedirectManager.RedirectToCurrentPage(); + } + + private sealed class InputModel + { + [DataType(DataType.Password)] + public string Password { get; set; } = ""; + } +} diff --git a/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Components/Account/Pages/Manage/Disable2fa.razor b/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Components/Account/Pages/Manage/Disable2fa.razor new file mode 100644 index 0000000000..2e5533bed5 --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Components/Account/Pages/Manage/Disable2fa.razor @@ -0,0 +1,64 @@ +@page "/Account/Manage/Disable2fa" + +@using Microsoft.AspNetCore.Identity +@using BlazorWeb_CSharp.Data + +@inject UserManager UserManager +@inject IdentityUserAccessor UserAccessor +@inject IdentityRedirectManager RedirectManager +@inject ILogger Logger + +Disable two-factor authentication (2FA) + + +

Disable two-factor authentication (2FA)

+ + + +
+
+ + Disable 2FA + +
+ +@code { + private ApplicationUser user = default!; + + [CascadingParameter] + private HttpContext HttpContext { get; set; } = default!; + + protected override async Task OnInitializedAsync() + { + user = await UserAccessor.GetRequiredUserAsync(HttpContext); + + if (HttpMethods.IsGet(HttpContext.Request.Method) && !await UserManager.GetTwoFactorEnabledAsync(user)) + { + throw new InvalidOperationException("Cannot disable 2FA for user as it's not currently enabled."); + } + } + + private async Task OnSubmitAsync() + { + var disable2faResult = await UserManager.SetTwoFactorEnabledAsync(user, false); + if (!disable2faResult.Succeeded) + { + throw new InvalidOperationException("Unexpected error occurred disabling 2FA."); + } + + var userId = await UserManager.GetUserIdAsync(user); + Logger.LogInformation("User with ID '{UserId}' has disabled 2fa.", userId); + RedirectManager.RedirectToWithStatus( + "Account/Manage/TwoFactorAuthentication", + "2fa has been disabled. You can reenable 2fa when you setup an authenticator app", + HttpContext); + } +} diff --git a/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Components/Account/Pages/Manage/Email.razor b/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Components/Account/Pages/Manage/Email.razor new file mode 100644 index 0000000000..4114e2e7c1 --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Components/Account/Pages/Manage/Email.razor @@ -0,0 +1,113 @@ +@page "/Account/Manage/Email" + +@using System.ComponentModel.DataAnnotations +@using System.Text +@using System.Text.Encodings.Web +@using Microsoft.AspNetCore.Identity +@using Microsoft.AspNetCore.WebUtilities +@using BlazorWeb_CSharp.Data + +@inject UserManager UserManager +@inject IEmailSender EmailSender +@inject IdentityUserAccessor UserAccessor +@inject NavigationManager NavigationManager + +Manage email + +

Manage email

+ + + + +
+ + + + + + @if (isEmailConfirmed) + { + + + + } + else + { + + Send verification email + } + + + Change email + +
+
+ +@code { + private string? message; + private ApplicationUser user = default!; + private string? email; + private bool isEmailConfirmed; + + [CascadingParameter] + private HttpContext HttpContext { get; set; } = default!; + + [SupplyParameterFromForm(FormName = "change-email")] + private InputModel Input { get; set; } = new(); + + protected override async Task OnInitializedAsync() + { + user = await UserAccessor.GetRequiredUserAsync(HttpContext); + email = await UserManager.GetEmailAsync(user); + isEmailConfirmed = await UserManager.IsEmailConfirmedAsync(user); + + Input.NewEmail ??= email; + } + + private async Task OnValidSubmitAsync() + { + if (Input.NewEmail is null || Input.NewEmail == email) + { + message = "Your email is unchanged."; + return; + } + + var userId = await UserManager.GetUserIdAsync(user); + var code = await UserManager.GenerateChangeEmailTokenAsync(user, Input.NewEmail); + code = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(code)); + var callbackUrl = NavigationManager.GetUriWithQueryParameters( + NavigationManager.ToAbsoluteUri("Account/ConfirmEmailChange").AbsoluteUri, + new Dictionary { ["userId"] = userId, ["email"] = Input.NewEmail, ["code"] = code }); + + await EmailSender.SendConfirmationLinkAsync(user, Input.NewEmail, HtmlEncoder.Default.Encode(callbackUrl)); + + message = "Confirmation link to change email sent. Please check your email."; + } + + private async Task OnSendEmailVerificationAsync() + { + if (email is null) + { + return; + } + + var userId = await UserManager.GetUserIdAsync(user); + var code = await UserManager.GenerateEmailConfirmationTokenAsync(user); + code = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(code)); + var callbackUrl = NavigationManager.GetUriWithQueryParameters( + NavigationManager.ToAbsoluteUri("Account/ConfirmEmail").AbsoluteUri, + new Dictionary { ["userId"] = userId, ["code"] = code }); + + await EmailSender.SendConfirmationLinkAsync(user, email, HtmlEncoder.Default.Encode(callbackUrl)); + + message = "Verification email sent. Please check your email."; + } + + private sealed class InputModel + { + [Required] + [EmailAddress] + [Display(Name = "New email")] + public string? NewEmail { get; set; } + } +} diff --git a/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Components/Account/Pages/Manage/EnableAuthenticator.razor b/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Components/Account/Pages/Manage/EnableAuthenticator.razor new file mode 100644 index 0000000000..83d2cf8a79 --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Components/Account/Pages/Manage/EnableAuthenticator.razor @@ -0,0 +1,168 @@ +@page "/Account/Manage/EnableAuthenticator" + +@using System.ComponentModel.DataAnnotations +@using System.Globalization +@using System.Text +@using System.Text.Encodings.Web +@using Microsoft.AspNetCore.Identity +@using BlazorWeb_CSharp.Data + +@inject UserManager UserManager +@inject IdentityUserAccessor UserAccessor +@inject UrlEncoder UrlEncoder +@inject IdentityRedirectManager RedirectManager +@inject ILogger Logger + +Configure authenticator app + +@if (recoveryCodes is not null) +{ + +} +else +{ + +

Configure authenticator app

+ + +

To use an authenticator app go through the following steps:

+
    +
  1. +

    + Download a two-factor authenticator app like Microsoft Authenticator for + Android and + iOS or + Google Authenticator for + Android and + iOS. +

    +
  2. +
  3. +

    Scan the QR Code or enter this key @sharedKey into your two factor authenticator app. Spaces and casing do not matter.

    + +
    +
    +
  4. +
  5. +

    + Once you have scanned the QR code or input the key above, your two factor authentication app will provide you + with a unique code. Enter the code in the confirmation box below. +

    + + + + + + Verify + + +
  6. +
+
+
+} + +@code { + private const string AuthenticatorUriFormat = "otpauth://totp/{0}:{1}?secret={2}&issuer={0}&digits=6"; + + private string? message; + private ApplicationUser user = default!; + private string? sharedKey; + private string? authenticatorUri; + private IEnumerable? recoveryCodes; + + [CascadingParameter] + private HttpContext HttpContext { get; set; } = default!; + + [SupplyParameterFromForm] + private InputModel Input { get; set; } = new(); + + protected override async Task OnInitializedAsync() + { + user = await UserAccessor.GetRequiredUserAsync(HttpContext); + + await LoadSharedKeyAndQrCodeUriAsync(user); + } + + private async Task OnValidSubmitAsync() + { + // Strip spaces and hyphens + var verificationCode = Input.Code.Replace(" ", string.Empty).Replace("-", string.Empty); + + var is2faTokenValid = await UserManager.VerifyTwoFactorTokenAsync( + user, UserManager.Options.Tokens.AuthenticatorTokenProvider, verificationCode); + + if (!is2faTokenValid) + { + message = "Error: Verification code is invalid."; + return; + } + + await UserManager.SetTwoFactorEnabledAsync(user, true); + var userId = await UserManager.GetUserIdAsync(user); + Logger.LogInformation("User with ID '{UserId}' has enabled 2FA with an authenticator app.", userId); + + message = "Your authenticator app has been verified."; + + if (await UserManager.CountRecoveryCodesAsync(user) == 0) + { + recoveryCodes = await UserManager.GenerateNewTwoFactorRecoveryCodesAsync(user, 10); + } + else + { + RedirectManager.RedirectToWithStatus("Account/Manage/TwoFactorAuthentication", message, HttpContext); + } + } + + private async ValueTask LoadSharedKeyAndQrCodeUriAsync(ApplicationUser user) + { + // Load the authenticator key & QR code URI to display on the form + var unformattedKey = await UserManager.GetAuthenticatorKeyAsync(user); + if (string.IsNullOrEmpty(unformattedKey)) + { + await UserManager.ResetAuthenticatorKeyAsync(user); + unformattedKey = await UserManager.GetAuthenticatorKeyAsync(user); + } + + sharedKey = FormatKey(unformattedKey!); + + var email = await UserManager.GetEmailAsync(user); + authenticatorUri = GenerateQrCodeUri(email!, unformattedKey!); + } + + private string FormatKey(string unformattedKey) + { + var result = new StringBuilder(); + int currentPosition = 0; + while (currentPosition + 4 < unformattedKey.Length) + { + result.Append(unformattedKey.AsSpan(currentPosition, 4)).Append(' '); + currentPosition += 4; + } + if (currentPosition < unformattedKey.Length) + { + result.Append(unformattedKey.AsSpan(currentPosition)); + } + + return result.ToString().ToLowerInvariant(); + } + + private string GenerateQrCodeUri(string email, string unformattedKey) + { + return string.Format( + CultureInfo.InvariantCulture, + AuthenticatorUriFormat, + UrlEncoder.Encode("Microsoft.AspNetCore.Identity.UI"), + UrlEncoder.Encode(email), + unformattedKey); + } + + private sealed class InputModel + { + [Required] + [StringLength(7, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 6)] + [DataType(DataType.Text)] + [Display(Name = "Verification Code")] + public string Code { get; set; } = ""; + } +} diff --git a/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Components/Account/Pages/Manage/ExternalLogins.razor b/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Components/Account/Pages/Manage/ExternalLogins.razor new file mode 100644 index 0000000000..b406355fea --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Components/Account/Pages/Manage/ExternalLogins.razor @@ -0,0 +1,140 @@ +@page "/Account/Manage/ExternalLogins" + +@using Microsoft.AspNetCore.Authentication +@using Microsoft.AspNetCore.Identity +@using BlazorWeb_CSharp.Data + +@inject UserManager UserManager +@inject SignInManager SignInManager +@inject IdentityUserAccessor UserAccessor +@inject IUserStore UserStore +@inject IdentityRedirectManager RedirectManager + +Manage your external logins + + +@if (currentLogins?.Count > 0) +{ +

Registered Logins

+ + + @foreach (var login in currentLogins) + { + + + + + } + +
@login.ProviderDisplayName + @if (showRemoveButton) + { +
+ +
+ + + Remove +
+ + } + else + { + @:   + } +
+} +@if (otherLogins?.Count > 0) +{ +

Add another service to log in.

+
+
+ +
+

+ @foreach (var provider in otherLogins) + { + + @provider.DisplayName + + } +

+
+ +} + +@code { + public const string LinkLoginCallbackAction = "LinkLoginCallback"; + + private ApplicationUser user = default!; + private IList? currentLogins; + private IList? otherLogins; + private bool showRemoveButton; + + [CascadingParameter] + private HttpContext HttpContext { get; set; } = default!; + + [SupplyParameterFromForm] + private string? LoginProvider { get; set; } + + [SupplyParameterFromForm] + private string? ProviderKey { get; set; } + + [SupplyParameterFromQuery] + private string? Action { get; set; } + + protected override async Task OnInitializedAsync() + { + user = await UserAccessor.GetRequiredUserAsync(HttpContext); + currentLogins = await UserManager.GetLoginsAsync(user); + otherLogins = (await SignInManager.GetExternalAuthenticationSchemesAsync()) + .Where(auth => currentLogins.All(ul => auth.Name != ul.LoginProvider)) + .ToList(); + + string? passwordHash = null; + if (UserStore is IUserPasswordStore userPasswordStore) + { + passwordHash = await userPasswordStore.GetPasswordHashAsync(user, HttpContext.RequestAborted); + } + + showRemoveButton = passwordHash is not null || currentLogins.Count > 1; + + if (HttpMethods.IsGet(HttpContext.Request.Method) && Action == LinkLoginCallbackAction) + { + await OnGetLinkLoginCallbackAsync(); + } + } + + private async Task OnSubmitAsync() + { + var result = await UserManager.RemoveLoginAsync(user, LoginProvider!, ProviderKey!); + if (!result.Succeeded) + { + RedirectManager.RedirectToCurrentPageWithStatus("Error: The external login was not removed.", HttpContext); + } + + await SignInManager.RefreshSignInAsync(user); + RedirectManager.RedirectToCurrentPageWithStatus("The external login was removed.", HttpContext); + } + + private async Task OnGetLinkLoginCallbackAsync() + { + var userId = await UserManager.GetUserIdAsync(user); + var info = await SignInManager.GetExternalLoginInfoAsync(userId); + if (info is null) + { + RedirectManager.RedirectToCurrentPageWithStatus("Error: Could not load external login info.", HttpContext); + } + + var result = await UserManager.AddLoginAsync(user, info); + if (!result.Succeeded) + { + RedirectManager.RedirectToCurrentPageWithStatus("Error: The external login was not added. External logins can only be associated with one account.", HttpContext); + } + + // Clear the existing external cookie to ensure a clean login process + await HttpContext.SignOutAsync(IdentityConstants.ExternalScheme); + + RedirectManager.RedirectToCurrentPageWithStatus("The external login was added.", HttpContext); + } +} diff --git a/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Components/Account/Pages/Manage/GenerateRecoveryCodes.razor b/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Components/Account/Pages/Manage/GenerateRecoveryCodes.razor new file mode 100644 index 0000000000..40f8e4ecc6 --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Components/Account/Pages/Manage/GenerateRecoveryCodes.razor @@ -0,0 +1,68 @@ +@page "/Account/Manage/GenerateRecoveryCodes" + +@using Microsoft.AspNetCore.Identity +@using BlazorWeb_CSharp.Data + +@inject UserManager UserManager +@inject IdentityUserAccessor UserAccessor +@inject IdentityRedirectManager RedirectManager +@inject ILogger Logger + +Generate two-factor authentication (2FA) recovery codes + +@if (recoveryCodes is not null) +{ + +} +else +{ +

Generate two-factor authentication (2FA) recovery codes

+ +
+
+ + Generate Recovery Codes + +
+} + +@code { + private string? message; + private ApplicationUser user = default!; + private IEnumerable? recoveryCodes; + + [CascadingParameter] + private HttpContext HttpContext { get; set; } = default!; + + protected override async Task OnInitializedAsync() + { + user = await UserAccessor.GetRequiredUserAsync(HttpContext); + + var isTwoFactorEnabled = await UserManager.GetTwoFactorEnabledAsync(user); + if (!isTwoFactorEnabled) + { + throw new InvalidOperationException("Cannot generate recovery codes for user because they do not have 2FA enabled."); + } + } + + private async Task OnSubmitAsync() + { + var userId = await UserManager.GetUserIdAsync(user); + recoveryCodes = await UserManager.GenerateNewTwoFactorRecoveryCodesAsync(user, 10); + message = "You have generated new recovery codes."; + + Logger.LogInformation("User with ID '{UserId}' has generated new 2FA recovery codes.", userId); + } +} diff --git a/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Components/Account/Pages/Manage/Index.razor b/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Components/Account/Pages/Manage/Index.razor new file mode 100644 index 0000000000..700fe66760 --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Components/Account/Pages/Manage/Index.razor @@ -0,0 +1,71 @@ +@page "/Account/Manage" + +@using System.ComponentModel.DataAnnotations +@using Microsoft.AspNetCore.Identity +@using BlazorWeb_CSharp.Data + +@inject UserManager UserManager +@inject SignInManager SignInManager +@inject IdentityUserAccessor UserAccessor +@inject IdentityRedirectManager RedirectManager + +Profile + +

Profile

+ + + + + + + + + + + Save + + + + +@code { + private ApplicationUser user = default!; + private string? username; + private string? phoneNumber; + + [CascadingParameter] + private HttpContext HttpContext { get; set; } = default!; + + [SupplyParameterFromForm] + private InputModel Input { get; set; } = new(); + + protected override async Task OnInitializedAsync() + { + user = await UserAccessor.GetRequiredUserAsync(HttpContext); + username = await UserManager.GetUserNameAsync(user); + phoneNumber = await UserManager.GetPhoneNumberAsync(user); + + Input.PhoneNumber ??= phoneNumber; + } + + private async Task OnValidSubmitAsync() + { + if (Input.PhoneNumber != phoneNumber) + { + var setPhoneResult = await UserManager.SetPhoneNumberAsync(user, Input.PhoneNumber); + if (!setPhoneResult.Succeeded) + { + RedirectManager.RedirectToCurrentPageWithStatus("Error: Failed to set phone number.", HttpContext); + } + } + + await SignInManager.RefreshSignInAsync(user); + RedirectManager.RedirectToCurrentPageWithStatus("Your profile has been updated", HttpContext); + } + + private sealed class InputModel + { + [Phone] + [Display(Name = "Phone number")] + public string? PhoneNumber { get; set; } + } +} diff --git a/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Components/Account/Pages/Manage/PersonalData.razor b/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Components/Account/Pages/Manage/PersonalData.razor new file mode 100644 index 0000000000..d4ee3277e8 --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Components/Account/Pages/Manage/PersonalData.razor @@ -0,0 +1,37 @@ +@page "/Account/Manage/PersonalData" + +@inject IdentityUserAccessor UserAccessor + +Personal Data + + +

Personal Data

+ + + +

Your account contains personal data that you have given us. This page allows you to download or delete that data.

+

+ Deleting this data will permanently remove your account, and this cannot be recovered. +

+
+ + Download + + +
+ +

+ Delete +

+
+
+ +@code { + [CascadingParameter] + private HttpContext HttpContext { get; set; } = default!; + + protected override async Task OnInitializedAsync() + { + _ = await UserAccessor.GetRequiredUserAsync(HttpContext); + } +} diff --git a/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Components/Account/Pages/Manage/ResetAuthenticator.razor b/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Components/Account/Pages/Manage/ResetAuthenticator.razor new file mode 100644 index 0000000000..c4d73ded30 --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Components/Account/Pages/Manage/ResetAuthenticator.razor @@ -0,0 +1,52 @@ +@page "/Account/Manage/ResetAuthenticator" + +@using Microsoft.AspNetCore.Identity +@using BlazorWeb_CSharp.Data + +@inject UserManager UserManager +@inject SignInManager SignInManager +@inject IdentityUserAccessor UserAccessor +@inject IdentityRedirectManager RedirectManager +@inject ILogger Logger + +Reset authenticator key + + +

Reset authenticator key

+ +
+
+ + Reset authenticator key + +
+ +@code { + [CascadingParameter] + private HttpContext HttpContext { get; set; } = default!; + + private async Task OnSubmitAsync() + { + var user = await UserAccessor.GetRequiredUserAsync(HttpContext); + await UserManager.SetTwoFactorEnabledAsync(user, false); + await UserManager.ResetAuthenticatorKeyAsync(user); + var userId = await UserManager.GetUserIdAsync(user); + Logger.LogInformation("User with ID '{UserId}' has reset their authentication app key.", userId); + + await SignInManager.RefreshSignInAsync(user); + + RedirectManager.RedirectToWithStatus( + "Account/Manage/EnableAuthenticator", + "Your authenticator app key has been reset, you will need to configure your authenticator app using the new key.", + HttpContext); + } +} diff --git a/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Components/Account/Pages/Manage/SetPassword.razor b/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Components/Account/Pages/Manage/SetPassword.razor new file mode 100644 index 0000000000..9a4b714f0a --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Components/Account/Pages/Manage/SetPassword.razor @@ -0,0 +1,81 @@ +@page "/Account/Manage/SetPassword" + +@using System.ComponentModel.DataAnnotations +@using Microsoft.AspNetCore.Identity +@using BlazorWeb_CSharp.Data + +@inject UserManager UserManager +@inject SignInManager SignInManager +@inject IdentityUserAccessor UserAccessor +@inject IdentityRedirectManager RedirectManager + +Set password + +

Set your password

+ +

+ You do not have a local username/password for this site. Add a local + account so you can log in without an external login. +

+ + + + + + + + + + Set password + + + + +@code { + private string? message; + private ApplicationUser user = default!; + + [CascadingParameter] + private HttpContext HttpContext { get; set; } = default!; + + [SupplyParameterFromForm] + private InputModel Input { get; set; } = new(); + + protected override async Task OnInitializedAsync() + { + user = await UserAccessor.GetRequiredUserAsync(HttpContext); + + var hasPassword = await UserManager.HasPasswordAsync(user); + if (hasPassword) + { + RedirectManager.RedirectTo("Account/Manage/ChangePassword"); + } + } + + private async Task OnValidSubmitAsync() + { + var addPasswordResult = await UserManager.AddPasswordAsync(user, Input.NewPassword!); + if (!addPasswordResult.Succeeded) + { + message = $"Error: {string.Join(",", addPasswordResult.Errors.Select(error => error.Description))}"; + return; + } + + await SignInManager.RefreshSignInAsync(user); + RedirectManager.RedirectToCurrentPageWithStatus("Your password has been set.", HttpContext); + } + + private sealed class InputModel + { + [Required] + [StringLength(100, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 6)] + [DataType(DataType.Password)] + [Display(Name = "New password")] + public string? NewPassword { get; set; } + + [DataType(DataType.Password)] + [Display(Name = "Confirm new password")] + [Compare("NewPassword", ErrorMessage = "The new password and confirmation password do not match.")] + public string? ConfirmPassword { get; set; } + } +} diff --git a/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Components/Account/Pages/Manage/TwoFactorAuthentication.razor b/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Components/Account/Pages/Manage/TwoFactorAuthentication.razor new file mode 100644 index 0000000000..dd614486e0 --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Components/Account/Pages/Manage/TwoFactorAuthentication.razor @@ -0,0 +1,101 @@ +@page "/Account/Manage/TwoFactorAuthentication" + +@using Microsoft.AspNetCore.Http.Features +@using Microsoft.AspNetCore.Identity +@using BlazorWeb_CSharp.Data + +@inject UserManager UserManager +@inject SignInManager SignInManager +@inject IdentityUserAccessor UserAccessor +@inject IdentityRedirectManager RedirectManager + +Two-factor authentication (2FA) + + +

Two-factor authentication (2FA)

+@if (canTrack) +{ + if (is2faEnabled) + { + if (recoveryCodesLeft == 0) + { +
+ You have no recovery codes left. +

You must generate a new set of recovery codes before you can log in with a recovery code.

+
+ } + else if (recoveryCodesLeft == 1) + { +
+ You have 1 recovery code left. +

You can generate a new set of recovery codes.

+
+ } + else if (recoveryCodesLeft <= 3) + { +
+ You have @recoveryCodesLeft recovery codes left. +

You should generate a new set of recovery codes.

+
+ } + + if (isMachineRemembered) + { +
+ + Forget this browser + + } + + Disable 2FA + Reset recovery codes + } + +

Authenticator app

+ @if (!hasAuthenticator) + { + Add authenticator app + } + else + { + Set up authenticator app + Reset authenticator app + } +} +else +{ +
+ Privacy and cookie policy have not been accepted. +

You must accept the policy before you can enable two factor authentication.

+
+} + +@code { + private bool canTrack; + private bool hasAuthenticator; + private int recoveryCodesLeft; + private bool is2faEnabled; + private bool isMachineRemembered; + + [CascadingParameter] + private HttpContext HttpContext { get; set; } = default!; + + protected override async Task OnInitializedAsync() + { + var user = await UserAccessor.GetRequiredUserAsync(HttpContext); + canTrack = HttpContext.Features.Get()?.CanTrack ?? true; + hasAuthenticator = await UserManager.GetAuthenticatorKeyAsync(user) is not null; + is2faEnabled = await UserManager.GetTwoFactorEnabledAsync(user); + isMachineRemembered = await SignInManager.IsTwoFactorClientRememberedAsync(user); + recoveryCodesLeft = await UserManager.CountRecoveryCodesAsync(user); + } + + private async Task OnSubmitForgetBrowserAsync() + { + await SignInManager.ForgetTwoFactorClientAsync(); + + RedirectManager.RedirectToCurrentPageWithStatus( + "The current browser has been forgotten. When you login again from this browser you will be prompted for your 2fa code.", + HttpContext); + } +} diff --git a/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Components/Account/Pages/Manage/_Imports.razor b/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Components/Account/Pages/Manage/_Imports.razor new file mode 100644 index 0000000000..ada5bb010a --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Components/Account/Pages/Manage/_Imports.razor @@ -0,0 +1,2 @@ +@layout ManageLayout +@attribute [Microsoft.AspNetCore.Authorization.Authorize] diff --git a/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Components/Account/Pages/Register.razor b/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Components/Account/Pages/Register.razor new file mode 100644 index 0000000000..5f74e9d71f --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Components/Account/Pages/Register.razor @@ -0,0 +1,136 @@ +@page "/Account/Register" + +@using System.ComponentModel.DataAnnotations +@using System.Text +@using System.Text.Encodings.Web +@using Microsoft.AspNetCore.Identity +@using Microsoft.AspNetCore.WebUtilities +@using BlazorWeb_CSharp.Data + +@inject UserManager UserManager +@inject IUserStore UserStore +@inject SignInManager SignInManager +@inject IEmailSender EmailSender +@inject ILogger Logger +@inject NavigationManager NavigationManager +@inject IdentityRedirectManager RedirectManager + +Register + +

Register

+ + + + + + +

Create a new account.

+
+ + + + + + + + + Log in + +
+
+ +

Use another service to log in.

+
+ +
+
+ +@code { + private IEnumerable? identityErrors; + + [SupplyParameterFromForm] + private InputModel Input { get; set; } = new(); + + [SupplyParameterFromQuery] + private string? ReturnUrl { get; set; } + + private string? Message => identityErrors is null ? null : $"Error: {string.Join(", ", identityErrors.Select(error => error.Description))}"; + + public async Task RegisterUser(EditContext editContext) + { + var user = CreateUser(); + + await UserStore.SetUserNameAsync(user, Input.Email, CancellationToken.None); + var emailStore = GetEmailStore(); + await emailStore.SetEmailAsync(user, Input.Email, CancellationToken.None); + var result = await UserManager.CreateAsync(user, Input.Password); + + if (!result.Succeeded) + { + identityErrors = result.Errors; + return; + } + + Logger.LogInformation("User created a new account with password."); + + var userId = await UserManager.GetUserIdAsync(user); + var code = await UserManager.GenerateEmailConfirmationTokenAsync(user); + code = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(code)); + var callbackUrl = NavigationManager.GetUriWithQueryParameters( + NavigationManager.ToAbsoluteUri("Account/ConfirmEmail").AbsoluteUri, + new Dictionary { ["userId"] = userId, ["code"] = code, ["returnUrl"] = ReturnUrl }); + + await EmailSender.SendConfirmationLinkAsync(user, Input.Email, HtmlEncoder.Default.Encode(callbackUrl)); + + if (UserManager.Options.SignIn.RequireConfirmedAccount) + { + RedirectManager.RedirectTo( + "Account/RegisterConfirmation", + new() { ["email"] = Input.Email, ["returnUrl"] = ReturnUrl }); + } + + await SignInManager.SignInAsync(user, isPersistent: false); + RedirectManager.RedirectTo(ReturnUrl); + } + + private ApplicationUser CreateUser() + { + try + { + return Activator.CreateInstance(); + } + catch + { + throw new InvalidOperationException($"Can't create an instance of '{nameof(ApplicationUser)}'. " + + $"Ensure that '{nameof(ApplicationUser)}' is not an abstract class and has a parameterless constructor."); + } + } + + private IUserEmailStore GetEmailStore() + { + if (!UserManager.SupportsUserEmail) + { + throw new NotSupportedException("The default UI requires a user store with email support."); + } + return (IUserEmailStore)UserStore; + } + + private sealed class InputModel + { + [Required] + [EmailAddress] + [Display(Name = "Email")] + public string Email { get; set; } = ""; + + [Required] + [StringLength(100, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 6)] + [DataType(DataType.Password)] + [Display(Name = "Password")] + public string Password { get; set; } = ""; + + [DataType(DataType.Password)] + [Display(Name = "Confirm password")] + [Compare("Password", ErrorMessage = "The password and confirmation password do not match.")] + public string ConfirmPassword { get; set; } = ""; + } +} diff --git a/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Components/Account/Pages/RegisterConfirmation.razor b/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Components/Account/Pages/RegisterConfirmation.razor new file mode 100644 index 0000000000..ec74ea584e --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Components/Account/Pages/RegisterConfirmation.razor @@ -0,0 +1,68 @@ +@page "/Account/RegisterConfirmation" + +@using System.Text +@using Microsoft.AspNetCore.Identity +@using Microsoft.AspNetCore.WebUtilities +@using BlazorWeb_CSharp.Data + +@inject UserManager UserManager +@inject IEmailSender EmailSender +@inject NavigationManager NavigationManager +@inject IdentityRedirectManager RedirectManager + +Register confirmation + +

Register confirmation

+ + + +@if (emailConfirmationLink is not null) +{ +

+ This app does not currently have a real email sender registered, see these docs for how to configure a real email sender. + Normally this would be emailed: Click here to confirm your account +

+} +else +{ +

Please check your email to confirm your account.

+} + +@code { + private string? emailConfirmationLink; + private string? statusMessage; + + [CascadingParameter] + private HttpContext HttpContext { get; set; } = default!; + + [SupplyParameterFromQuery] + private string? Email { get; set; } + + [SupplyParameterFromQuery] + private string? ReturnUrl { get; set; } + + protected override async Task OnInitializedAsync() + { + if (Email is null) + { + RedirectManager.RedirectTo(""); + } + + var user = await UserManager.FindByEmailAsync(Email); + if (user is null) + { + HttpContext.Response.StatusCode = StatusCodes.Status404NotFound; + statusMessage = "Error finding user for unspecified email"; + } + else if (EmailSender is IdentityNoOpEmailSender) + { + // Once you add a real email sender, you should remove this code that lets you confirm the account + var userId = await UserManager.GetUserIdAsync(user); + var code = await UserManager.GenerateEmailConfirmationTokenAsync(user); + code = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(code)); + emailConfirmationLink = NavigationManager.GetUriWithQueryParameters( + NavigationManager.ToAbsoluteUri("Account/ConfirmEmail").AbsoluteUri, + new Dictionary { ["userId"] = userId, ["code"] = code, ["returnUrl"] = ReturnUrl }); + } + } +} diff --git a/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Components/Account/Pages/ResendEmailConfirmation.razor b/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Components/Account/Pages/ResendEmailConfirmation.razor new file mode 100644 index 0000000000..92245f75c8 --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Components/Account/Pages/ResendEmailConfirmation.razor @@ -0,0 +1,65 @@ +@page "/Account/ResendEmailConfirmation" + +@using System.ComponentModel.DataAnnotations +@using System.Text +@using System.Text.Encodings.Web +@using Microsoft.AspNetCore.Identity +@using Microsoft.AspNetCore.WebUtilities +@using BlazorWeb_CSharp.Data + +@inject UserManager UserManager +@inject IEmailSender EmailSender +@inject NavigationManager NavigationManager +@inject IdentityRedirectManager RedirectManager + +Resend email confirmation + +

Resend email confirmation

+

Enter your email.

+
+ + + + + + + + + Resend + + + + +@code { + private string? message; + + [SupplyParameterFromForm] + private InputModel Input { get; set; } = new(); + + private async Task OnValidSubmitAsync() + { + var user = await UserManager.FindByEmailAsync(Input.Email!); + if (user is null) + { + message = "Verification email sent. Please check your email."; + return; + } + + var userId = await UserManager.GetUserIdAsync(user); + var code = await UserManager.GenerateEmailConfirmationTokenAsync(user); + code = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(code)); + var callbackUrl = NavigationManager.GetUriWithQueryParameters( + NavigationManager.ToAbsoluteUri("Account/ConfirmEmail").AbsoluteUri, + new Dictionary { ["userId"] = userId, ["code"] = code }); + await EmailSender.SendConfirmationLinkAsync(user, Input.Email, HtmlEncoder.Default.Encode(callbackUrl)); + + message = "Verification email sent. Please check your email."; + } + + private sealed class InputModel + { + [Required] + [EmailAddress] + public string Email { get; set; } = ""; + } +} diff --git a/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Components/Account/Pages/ResetPassword.razor b/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Components/Account/Pages/ResetPassword.razor new file mode 100644 index 0000000000..4b07383171 --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Components/Account/Pages/ResetPassword.razor @@ -0,0 +1,94 @@ +@page "/Account/ResetPassword" + +@using System.ComponentModel.DataAnnotations +@using System.Text +@using Microsoft.AspNetCore.Identity +@using Microsoft.AspNetCore.WebUtilities +@using BlazorWeb_CSharp.Data + +@inject IdentityRedirectManager RedirectManager +@inject UserManager UserManager + +Reset password + +

Reset password

+

Reset your password.

+
+ + + + + + + + + + + + + + + Resend + + + + +@code { + private IEnumerable? identityErrors; + + [SupplyParameterFromForm] + private InputModel Input { get; set; } = new(); + + [SupplyParameterFromQuery] + private string? Code { get; set; } + + private string? Message => identityErrors is null ? null : $"Error: {string.Join(", ", identityErrors.Select(error => error.Description))}"; + + protected override void OnInitialized() + { + if (Code is null) + { + RedirectManager.RedirectTo("Account/InvalidPasswordReset"); + } + + Input.Code = Encoding.UTF8.GetString(WebEncoders.Base64UrlDecode(Code)); + } + + private async Task OnValidSubmitAsync() + { + var user = await UserManager.FindByEmailAsync(Input.Email); + if (user is null) + { + // Don't reveal that the user does not exist + RedirectManager.RedirectTo("Account/ResetPasswordConfirmation"); + } + + var result = await UserManager.ResetPasswordAsync(user, Input.Code, Input.Password); + if (result.Succeeded) + { + RedirectManager.RedirectTo("Account/ResetPasswordConfirmation"); + } + + identityErrors = result.Errors; + } + + private sealed class InputModel + { + [Required] + [EmailAddress] + public string Email { get; set; } = ""; + + [Required] + [StringLength(100, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 6)] + [DataType(DataType.Password)] + public string Password { get; set; } = ""; + + [DataType(DataType.Password)] + [Display(Name = "Confirm password")] + [Compare("Password", ErrorMessage = "The password and confirmation password do not match.")] + public string ConfirmPassword { get; set; } = ""; + + [Required] + public string Code { get; set; } = ""; + } +} diff --git a/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Components/Account/Pages/ResetPasswordConfirmation.razor b/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Components/Account/Pages/ResetPasswordConfirmation.razor new file mode 100644 index 0000000000..247e96eca3 --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Components/Account/Pages/ResetPasswordConfirmation.razor @@ -0,0 +1,7 @@ +@page "/Account/ResetPasswordConfirmation" +Reset password confirmation + +

Reset password confirmation

+

+ Your password has been reset. Please click here to log in. +

diff --git a/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Components/Account/Pages/_Imports.razor b/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Components/Account/Pages/_Imports.razor new file mode 100644 index 0000000000..ecfdacfa93 --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Components/Account/Pages/_Imports.razor @@ -0,0 +1,2 @@ +@using BlazorWeb_CSharp.Components.Account.Shared +@attribute [ExcludeFromInteractiveRouting] diff --git a/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Components/Account/Shared/ExternalLoginPicker.razor b/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Components/Account/Shared/ExternalLoginPicker.razor new file mode 100644 index 0000000000..9894a7efa1 --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Components/Account/Shared/ExternalLoginPicker.razor @@ -0,0 +1,44 @@ +@using Microsoft.AspNetCore.Authentication +@using Microsoft.AspNetCore.Identity +@using BlazorWeb_CSharp.Data + +@inject SignInManager SignInManager +@inject IdentityRedirectManager RedirectManager + +@if (externalLogins.Length == 0) +{ +
+

+ There are no external authentication services configured. See this article + about setting up this ASP.NET application to support logging in via external services. +

+
+} +else +{ +
+
+ + +

+ @foreach (var provider in externalLogins) + { + @provider.DisplayName +   + } +

+
+
+} + +@code { + private AuthenticationScheme[] externalLogins = []; + + [SupplyParameterFromQuery] + private string? ReturnUrl { get; set; } + + protected override async Task OnInitializedAsync() + { + externalLogins = (await SignInManager.GetExternalAuthenticationSchemesAsync()).ToArray(); + } +} diff --git a/src/Templates/templates/blazorweb-csharp/BlazorWeb-CSharp/Components/Account/Shared/ManageLayout.razor b/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Components/Account/Shared/ManageLayout.razor similarity index 100% rename from src/Templates/templates/blazorweb-csharp/BlazorWeb-CSharp/Components/Account/Shared/ManageLayout.razor rename to src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Components/Account/Shared/ManageLayout.razor diff --git a/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Components/Account/Shared/ManageNavMenu.razor b/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Components/Account/Shared/ManageNavMenu.razor new file mode 100644 index 0000000000..ea0cd42482 --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Components/Account/Shared/ManageNavMenu.razor @@ -0,0 +1,27 @@ +@using Microsoft.AspNetCore.Identity +@using BlazorWeb_CSharp.Data + +@inject SignInManager SignInManager + + + Profile + Email + Password + @if (hasExternalLogins) + { + External logins + } + Two-factor authentication + Personal data + + + + +@code { + private bool hasExternalLogins; + + protected override async Task OnInitializedAsync() + { + hasExternalLogins = (await SignInManager.GetExternalAuthenticationSchemesAsync()).Any(); + } +} diff --git a/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Components/Account/Shared/RedirectToLogin.razor b/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Components/Account/Shared/RedirectToLogin.razor new file mode 100644 index 0000000000..c8b8eff4af --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Components/Account/Shared/RedirectToLogin.razor @@ -0,0 +1,8 @@ +@inject NavigationManager NavigationManager + +@code { + protected override void OnInitialized() + { + NavigationManager.NavigateTo($"Account/Login?returnUrl={Uri.EscapeDataString(NavigationManager.Uri)}", forceLoad: true); + } +} diff --git a/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Components/Account/Shared/ShowRecoveryCodes.razor b/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Components/Account/Shared/ShowRecoveryCodes.razor new file mode 100644 index 0000000000..038afba67a --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Components/Account/Shared/ShowRecoveryCodes.razor @@ -0,0 +1,31 @@ + +

Recovery codes

+ + + + @foreach (var recoveryCode in RecoveryCodes) + { +
+ @recoveryCode +
+ } + +
+ +
+ + +@code { + [Parameter] + public string[] RecoveryCodes { get; set; } = []; + + [Parameter] + public string? StatusMessage { get; set; } +} diff --git a/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Components/Account/Shared/StatusMessage.razor b/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Components/Account/Shared/StatusMessage.razor new file mode 100644 index 0000000000..12cd544cfe --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Components/Account/Shared/StatusMessage.razor @@ -0,0 +1,29 @@ +@if (!string.IsNullOrEmpty(DisplayMessage)) +{ + var statusMessageClass = DisplayMessage.StartsWith("Error") ? "danger" : "success"; + +} + +@code { + private string? messageFromCookie; + + [Parameter] + public string? Message { get; set; } + + [CascadingParameter] + private HttpContext HttpContext { get; set; } = default!; + + private string? DisplayMessage => Message ?? messageFromCookie; + + protected override void OnInitialized() + { + messageFromCookie = HttpContext.Request.Cookies[IdentityRedirectManager.StatusCookieName]; + + if (messageFromCookie is not null) + { + HttpContext.Response.Cookies.Delete(IdentityRedirectManager.StatusCookieName); + } + } +} diff --git a/src/Templates/templates/blazorweb-csharp/BlazorWeb-CSharp/Components/App.razor b/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Components/App.razor similarity index 89% rename from src/Templates/templates/blazorweb-csharp/BlazorWeb-CSharp/Components/App.razor rename to src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Components/App.razor index 4200baa19d..89d95f4738 100644 --- a/src/Templates/templates/blazorweb-csharp/BlazorWeb-CSharp/Components/App.razor +++ b/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Components/App.razor @@ -5,16 +5,11 @@ - @*#if (Framework == 'net8.0') - - - ##else - ##endif*@ @*#if (SampleContent) - + ##endif*@ @*#if (!InteractiveAtRoot) @@ -74,3 +69,4 @@ HttpContext.AcceptsInteractiveRouting() ? InteractiveWebAssembly : null; } #endif*@ + diff --git a/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Components/Layout/MainLayout.razor b/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Components/Layout/MainLayout.razor new file mode 100644 index 0000000000..0792dcde42 --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Components/Layout/MainLayout.razor @@ -0,0 +1,34 @@ +@inherits LayoutComponentBase + +@*#if (SampleContent) --> + + + BlazorWeb-CSharp + + + + +
+ @Body +
+
+
+ + Documentation and demos + + About Blazor + +
+##else +
+ @Body +
+##endif*@ +@*#if (UseServer || UseWebAssembly) --> + +
+ An unhandled error has occurred. + Reload + 🗙 +
+##endif*@ diff --git a/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Components/Layout/NavMenu.razor b/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Components/Layout/NavMenu.razor new file mode 100644 index 0000000000..1ac6a6f28d --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Components/Layout/NavMenu.razor @@ -0,0 +1,64 @@ +@*#if (UseServer && !InteractiveAtRoot) --> +@rendermode InteractiveServer + +##endif*@ +@*#if (IndividualLocalAuth) +@implements IDisposable + +@inject NavigationManager NavigationManager + +##endif*@ + + +@code { + private bool expanded = true; +@*#if (IndividualLocalAuth) + private string? currentUrl; + + protected override void OnInitialized() + { + currentUrl = NavigationManager.ToBaseRelativePath(NavigationManager.Uri); + NavigationManager.LocationChanged += OnLocationChanged; + } + + private void OnLocationChanged(object? sender, LocationChangedEventArgs e) + { + currentUrl = NavigationManager.ToBaseRelativePath(e.Location); + StateHasChanged(); + } + + public void Dispose() + { + NavigationManager.LocationChanged -= OnLocationChanged; + } +##endif*@ +} diff --git a/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Components/Pages/Auth.razor b/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Components/Pages/Auth.razor new file mode 100644 index 0000000000..b7bbe6eb3d --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Components/Pages/Auth.razor @@ -0,0 +1,13 @@ +@page "/auth" + +@using Microsoft.AspNetCore.Authorization + +@attribute [Authorize] + +Auth + +

You are authenticated

+ + + Hello @context.User.Identity?.Name! + diff --git a/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Components/Pages/Counter.razor b/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Components/Pages/Counter.razor new file mode 100644 index 0000000000..c19f296f01 --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Components/Pages/Counter.razor @@ -0,0 +1,23 @@ +@page "/counter" +@*#if (!InteractiveAtRoot) --> +@rendermode InteractiveServer +##endif*@ + +Counter + +

Counter

+ +
+ Current count: @currentCount +
+ +Click me + +@code { + private int currentCount = 0; + + private void IncrementCount() + { + currentCount++; + } +} diff --git a/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Components/Pages/Error.razor b/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Components/Pages/Error.razor new file mode 100644 index 0000000000..576cc2d2f4 --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Components/Pages/Error.razor @@ -0,0 +1,36 @@ +@page "/Error" +@using System.Diagnostics + +Error + +

Error.

+

An error occurred while processing your request.

+ +@if (ShowRequestId) +{ +

+ Request ID: @RequestId +

+} + +

Development Mode

+

+ Swapping to Development environment will display more detailed information about the error that occurred. +

+

+ The Development environment shouldn't be enabled for deployed applications. + It can result in displaying sensitive information from exceptions to end users. + For local debugging, enable the Development environment by setting the ASPNETCORE_ENVIRONMENT environment variable to Development + and restarting the app. +

+ +@code{ + [CascadingParameter] + private HttpContext? HttpContext { get; set; } + + private string? RequestId { get; set; } + private bool ShowRequestId => !string.IsNullOrEmpty(RequestId); + + protected override void OnInitialized() => + RequestId = Activity.Current?.Id ?? HttpContext?.TraceIdentifier; +} diff --git a/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Components/Pages/Home.razor b/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Components/Pages/Home.razor new file mode 100644 index 0000000000..96714a2131 --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Components/Pages/Home.razor @@ -0,0 +1,7 @@ +@page "/" + +Home + +

Hello, world!

+ +Welcome to your new Fluent Blazor app. \ No newline at end of file diff --git a/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Components/Pages/Weather.razor b/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Components/Pages/Weather.razor new file mode 100644 index 0000000000..3986c09a4c --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Components/Pages/Weather.razor @@ -0,0 +1,65 @@ +@page "/weather" +@*#if (!InteractiveAtRoot) --> +@attribute [StreamRendering] +##endif*@ + +Weather + +

Weather

+ +

This component demonstrates showing data.

+ +@*#if (InteractiveAtRoot) --> +@if (forecasts == null) +{ +

Loading...

+} +else +{ + + + + + + +} +##else + + + + + + + +##endif*@ + +@code { + private IQueryable? forecasts; + + protected override async Task OnInitializedAsync() + { +@*#if (InteractiveAtRoot) --> + // Simulate asynchronous loading to demonstrate a loading indicator +##else + // Simulate asynchronous loading to demonstrate streaming rendering +##endif*@ + await Task.Delay(500); + + var startDate = DateOnly.FromDateTime(DateTime.Now); + var summaries = new[] { "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching" }; + forecasts = Enumerable.Range(1, 5).Select(index => new WeatherForecast + { + Date = startDate.AddDays(index), + TemperatureC = Random.Shared.Next(-20, 55), + Summary = summaries[Random.Shared.Next(summaries.Length)] + }).AsQueryable(); + } + + private class WeatherForecast + { + public DateOnly Date { get; set; } + public int TemperatureC { get; set; } + public string? Summary { get; set; } + public int TemperatureF => 32 + (int)(TemperatureC / 0.5556); + } +} diff --git a/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Components/Routes.razor b/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Components/Routes.razor new file mode 100644 index 0000000000..fdf2fe8b42 --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Components/Routes.razor @@ -0,0 +1,21 @@ +@*#if (IndividualLocalAuth && !UseWebAssembly) +@using BlazorWeb_CSharp.Components.Account.Shared +##endif*@ +@*#if (UseWebAssembly && !InteractiveAtRoot) + +##else + +##endif*@ + + @*#if (IndividualLocalAuth) + + + + + + ##else + + ##endif*@ + + + diff --git a/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Components/_Imports.razor b/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Components/_Imports.razor new file mode 100644 index 0000000000..9b3af88964 --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Components/_Imports.razor @@ -0,0 +1,21 @@ +@using System.Net.Http +@using System.Net.Http.Json +@*#if (IndividualLocalAuth) +@using Microsoft.AspNetCore.Components.Authorization +##endif*@ +@using Microsoft.AspNetCore.Components.Forms +@using Microsoft.AspNetCore.Components.Routing +@using Microsoft.AspNetCore.Components.Web +@using static Microsoft.AspNetCore.Components.Web.RenderMode +@using Microsoft.AspNetCore.Components.Web.Virtualization +@using Microsoft.FluentUI.AspNetCore.Components +@using Icons = Microsoft.FluentUI.AspNetCore.Components.Icons +@using Microsoft.JSInterop +@using BlazorWeb_CSharp +@*#if (UseWebAssembly) --> +@using BlazorWeb_CSharp.Client +##endif*@ +@*#if (UseWebAssembly && SampleContent) --> +@using BlazorWeb_CSharp.Client.Layout +##endif*@ +@using BlazorWeb_CSharp.Components diff --git a/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Data/ApplicationDbContext.cs b/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Data/ApplicationDbContext.cs new file mode 100644 index 0000000000..2ce33e233a --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Data/ApplicationDbContext.cs @@ -0,0 +1,8 @@ +using Microsoft.AspNetCore.Identity.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore; + +namespace BlazorWeb_CSharp.Data; + +public class ApplicationDbContext(DbContextOptions options) : IdentityDbContext(options) +{ +} diff --git a/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Data/ApplicationUser.cs b/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Data/ApplicationUser.cs new file mode 100644 index 0000000000..831bb92a4a --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Data/ApplicationUser.cs @@ -0,0 +1,9 @@ +using Microsoft.AspNetCore.Identity; + +namespace BlazorWeb_CSharp.Data; + +// Add profile data for application users by adding properties to the ApplicationUser class +public class ApplicationUser : IdentityUser +{ +} + diff --git a/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Data/SqlLite/00000000000000_CreateIdentitySchema.Designer.cs b/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Data/SqlLite/00000000000000_CreateIdentitySchema.Designer.cs new file mode 100644 index 0000000000..ba5b97c07d --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Data/SqlLite/00000000000000_CreateIdentitySchema.Designer.cs @@ -0,0 +1,268 @@ +// +using System; +using BlazorWeb_CSharp.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace BlazorWeb_CSharp.Migrations +{ + [DbContext(typeof(ApplicationDbContext))] + [Migration("00000000000000_CreateIdentitySchema")] + partial class CreateIdentitySchema + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder.HasAnnotation("ProductVersion", "8.0.0"); + + modelBuilder.Entity("BlazorWeb_CSharp.Data.ApplicationUser", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("AccessFailedCount") + .HasColumnType("INTEGER"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("TEXT"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("EmailConfirmed") + .HasColumnType("INTEGER"); + + b.Property("LockoutEnabled") + .HasColumnType("INTEGER"); + + b.Property("LockoutEnd") + .HasColumnType("TEXT"); + + b.Property("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("PasswordHash") + .HasColumnType("TEXT"); + + b.Property("PhoneNumber") + .HasColumnType("TEXT"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("INTEGER"); + + b.Property("SecurityStamp") + .HasColumnType("TEXT"); + + b.Property("TwoFactorEnabled") + .HasColumnType("INTEGER"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex"); + + b.ToTable("AspNetUsers", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("TEXT"); + + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("NormalizedName") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasDatabaseName("RoleNameIndex"); + + b.ToTable("AspNetRoles", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ClaimType") + .HasColumnType("TEXT"); + + b.Property("ClaimValue") + .HasColumnType("TEXT"); + + b.Property("RoleId") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetRoleClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ClaimType") + .HasColumnType("TEXT"); + + b.Property("ClaimValue") + .HasColumnType("TEXT"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasColumnType("TEXT"); + + b.Property("ProviderKey") + .HasColumnType("TEXT"); + + b.Property("ProviderDisplayName") + .HasColumnType("TEXT"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserLogins", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("TEXT"); + + b.Property("RoleId") + .HasColumnType("TEXT"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetUserRoles", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("TEXT"); + + b.Property("LoginProvider") + .HasColumnType("TEXT"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("Value") + .HasColumnType("TEXT"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("AspNetUserTokens", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("BlazorWeb_CSharp.Data.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("BlazorWeb_CSharp.Data.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("BlazorWeb_CSharp.Data.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("BlazorWeb_CSharp.Data.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Data/SqlLite/00000000000000_CreateIdentitySchema.cs b/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Data/SqlLite/00000000000000_CreateIdentitySchema.cs new file mode 100644 index 0000000000..bf83fc9a9f --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Data/SqlLite/00000000000000_CreateIdentitySchema.cs @@ -0,0 +1,222 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace BlazorWeb_CSharp.Migrations +{ + /// + public partial class CreateIdentitySchema : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "AspNetRoles", + columns: table => new + { + Id = table.Column(type: "TEXT", nullable: false), + Name = table.Column(type: "TEXT", maxLength: 256, nullable: true), + NormalizedName = table.Column(type: "TEXT", maxLength: 256, nullable: true), + ConcurrencyStamp = table.Column(type: "TEXT", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_AspNetRoles", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "AspNetUsers", + columns: table => new + { + Id = table.Column(type: "TEXT", nullable: false), + UserName = table.Column(type: "TEXT", maxLength: 256, nullable: true), + NormalizedUserName = table.Column(type: "TEXT", maxLength: 256, nullable: true), + Email = table.Column(type: "TEXT", maxLength: 256, nullable: true), + NormalizedEmail = table.Column(type: "TEXT", maxLength: 256, nullable: true), + EmailConfirmed = table.Column(type: "INTEGER", nullable: false), + PasswordHash = table.Column(type: "TEXT", nullable: true), + SecurityStamp = table.Column(type: "TEXT", nullable: true), + ConcurrencyStamp = table.Column(type: "TEXT", nullable: true), + PhoneNumber = table.Column(type: "TEXT", nullable: true), + PhoneNumberConfirmed = table.Column(type: "INTEGER", nullable: false), + TwoFactorEnabled = table.Column(type: "INTEGER", nullable: false), + LockoutEnd = table.Column(type: "TEXT", nullable: true), + LockoutEnabled = table.Column(type: "INTEGER", nullable: false), + AccessFailedCount = table.Column(type: "INTEGER", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_AspNetUsers", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "AspNetRoleClaims", + columns: table => new + { + Id = table.Column(type: "INTEGER", nullable: false) + .Annotation("Sqlite:Autoincrement", true), + RoleId = table.Column(type: "TEXT", nullable: false), + ClaimType = table.Column(type: "TEXT", nullable: true), + ClaimValue = table.Column(type: "TEXT", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_AspNetRoleClaims", x => x.Id); + table.ForeignKey( + name: "FK_AspNetRoleClaims_AspNetRoles_RoleId", + column: x => x.RoleId, + principalTable: "AspNetRoles", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "AspNetUserClaims", + columns: table => new + { + Id = table.Column(type: "INTEGER", nullable: false) + .Annotation("Sqlite:Autoincrement", true), + UserId = table.Column(type: "TEXT", nullable: false), + ClaimType = table.Column(type: "TEXT", nullable: true), + ClaimValue = table.Column(type: "TEXT", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_AspNetUserClaims", x => x.Id); + table.ForeignKey( + name: "FK_AspNetUserClaims_AspNetUsers_UserId", + column: x => x.UserId, + principalTable: "AspNetUsers", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "AspNetUserLogins", + columns: table => new + { + LoginProvider = table.Column(type: "TEXT", nullable: false), + ProviderKey = table.Column(type: "TEXT", nullable: false), + ProviderDisplayName = table.Column(type: "TEXT", nullable: true), + UserId = table.Column(type: "TEXT", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_AspNetUserLogins", x => new { x.LoginProvider, x.ProviderKey }); + table.ForeignKey( + name: "FK_AspNetUserLogins_AspNetUsers_UserId", + column: x => x.UserId, + principalTable: "AspNetUsers", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "AspNetUserRoles", + columns: table => new + { + UserId = table.Column(type: "TEXT", nullable: false), + RoleId = table.Column(type: "TEXT", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_AspNetUserRoles", x => new { x.UserId, x.RoleId }); + table.ForeignKey( + name: "FK_AspNetUserRoles_AspNetRoles_RoleId", + column: x => x.RoleId, + principalTable: "AspNetRoles", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_AspNetUserRoles_AspNetUsers_UserId", + column: x => x.UserId, + principalTable: "AspNetUsers", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "AspNetUserTokens", + columns: table => new + { + UserId = table.Column(type: "TEXT", nullable: false), + LoginProvider = table.Column(type: "TEXT", nullable: false), + Name = table.Column(type: "TEXT", nullable: false), + Value = table.Column(type: "TEXT", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_AspNetUserTokens", x => new { x.UserId, x.LoginProvider, x.Name }); + table.ForeignKey( + name: "FK_AspNetUserTokens_AspNetUsers_UserId", + column: x => x.UserId, + principalTable: "AspNetUsers", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateIndex( + name: "IX_AspNetRoleClaims_RoleId", + table: "AspNetRoleClaims", + column: "RoleId"); + + migrationBuilder.CreateIndex( + name: "RoleNameIndex", + table: "AspNetRoles", + column: "NormalizedName", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_AspNetUserClaims_UserId", + table: "AspNetUserClaims", + column: "UserId"); + + migrationBuilder.CreateIndex( + name: "IX_AspNetUserLogins_UserId", + table: "AspNetUserLogins", + column: "UserId"); + + migrationBuilder.CreateIndex( + name: "IX_AspNetUserRoles_RoleId", + table: "AspNetUserRoles", + column: "RoleId"); + + migrationBuilder.CreateIndex( + name: "EmailIndex", + table: "AspNetUsers", + column: "NormalizedEmail"); + + migrationBuilder.CreateIndex( + name: "UserNameIndex", + table: "AspNetUsers", + column: "NormalizedUserName", + unique: true); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "AspNetRoleClaims"); + + migrationBuilder.DropTable( + name: "AspNetUserClaims"); + + migrationBuilder.DropTable( + name: "AspNetUserLogins"); + + migrationBuilder.DropTable( + name: "AspNetUserRoles"); + + migrationBuilder.DropTable( + name: "AspNetUserTokens"); + + migrationBuilder.DropTable( + name: "AspNetRoles"); + + migrationBuilder.DropTable( + name: "AspNetUsers"); + } + } +} diff --git a/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Data/SqlLite/ApplicationDbContextModelSnapshot.cs b/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Data/SqlLite/ApplicationDbContextModelSnapshot.cs new file mode 100644 index 0000000000..930cebc185 --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Data/SqlLite/ApplicationDbContextModelSnapshot.cs @@ -0,0 +1,265 @@ +// +using System; +using BlazorWeb_CSharp.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace BlazorWeb_CSharp.Migrations +{ + [DbContext(typeof(ApplicationDbContext))] + partial class ApplicationDbContextModelSnapshot : ModelSnapshot + { + protected override void BuildModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder.HasAnnotation("ProductVersion", "8.0.0"); + + modelBuilder.Entity("BlazorWeb_CSharp.Data.ApplicationUser", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("AccessFailedCount") + .HasColumnType("INTEGER"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("TEXT"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("EmailConfirmed") + .HasColumnType("INTEGER"); + + b.Property("LockoutEnabled") + .HasColumnType("INTEGER"); + + b.Property("LockoutEnd") + .HasColumnType("TEXT"); + + b.Property("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("PasswordHash") + .HasColumnType("TEXT"); + + b.Property("PhoneNumber") + .HasColumnType("TEXT"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("INTEGER"); + + b.Property("SecurityStamp") + .HasColumnType("TEXT"); + + b.Property("TwoFactorEnabled") + .HasColumnType("INTEGER"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex"); + + b.ToTable("AspNetUsers", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("TEXT"); + + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("NormalizedName") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasDatabaseName("RoleNameIndex"); + + b.ToTable("AspNetRoles", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ClaimType") + .HasColumnType("TEXT"); + + b.Property("ClaimValue") + .HasColumnType("TEXT"); + + b.Property("RoleId") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetRoleClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ClaimType") + .HasColumnType("TEXT"); + + b.Property("ClaimValue") + .HasColumnType("TEXT"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasColumnType("TEXT"); + + b.Property("ProviderKey") + .HasColumnType("TEXT"); + + b.Property("ProviderDisplayName") + .HasColumnType("TEXT"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserLogins", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("TEXT"); + + b.Property("RoleId") + .HasColumnType("TEXT"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetUserRoles", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("TEXT"); + + b.Property("LoginProvider") + .HasColumnType("TEXT"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("Value") + .HasColumnType("TEXT"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("AspNetUserTokens", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("BlazorWeb_CSharp.Data.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("BlazorWeb_CSharp.Data.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("BlazorWeb_CSharp.Data.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("BlazorWeb_CSharp.Data.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Data/SqlServer/00000000000000_CreateIdentitySchema.Designer.cs b/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Data/SqlServer/00000000000000_CreateIdentitySchema.Designer.cs new file mode 100644 index 0000000000..34d2b6df1a --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Data/SqlServer/00000000000000_CreateIdentitySchema.Designer.cs @@ -0,0 +1,279 @@ +// +using System; +using BlazorWeb_CSharp.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace BlazorWeb_CSharp.Migrations +{ + [DbContext(typeof(ApplicationDbContext))] + [Migration("00000000000000_CreateIdentitySchema")] + partial class CreateIdentitySchema + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.0") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); + + modelBuilder.Entity("BlazorWeb_CSharp.Data.ApplicationUser", b => + { + b.Property("Id") + .HasColumnType("nvarchar(450)"); + + b.Property("AccessFailedCount") + .HasColumnType("int"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("nvarchar(max)"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("EmailConfirmed") + .HasColumnType("bit"); + + b.Property("LockoutEnabled") + .HasColumnType("bit"); + + b.Property("LockoutEnd") + .HasColumnType("datetimeoffset"); + + b.Property("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("PasswordHash") + .HasColumnType("nvarchar(max)"); + + b.Property("PhoneNumber") + .HasColumnType("nvarchar(max)"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("bit"); + + b.Property("SecurityStamp") + .HasColumnType("nvarchar(max)"); + + b.Property("TwoFactorEnabled") + .HasColumnType("bit"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex") + .HasFilter("[NormalizedUserName] IS NOT NULL"); + + b.ToTable("AspNetUsers", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .HasColumnType("nvarchar(450)"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("NormalizedName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasDatabaseName("RoleNameIndex") + .HasFilter("[NormalizedName] IS NOT NULL"); + + b.ToTable("AspNetRoles", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("nvarchar(max)"); + + b.Property("ClaimValue") + .HasColumnType("nvarchar(max)"); + + b.Property("RoleId") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetRoleClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("nvarchar(max)"); + + b.Property("ClaimValue") + .HasColumnType("nvarchar(max)"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasColumnType("nvarchar(450)"); + + b.Property("ProviderKey") + .HasColumnType("nvarchar(450)"); + + b.Property("ProviderDisplayName") + .HasColumnType("nvarchar(max)"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserLogins", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("nvarchar(450)"); + + b.Property("RoleId") + .HasColumnType("nvarchar(450)"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetUserRoles", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("nvarchar(450)"); + + b.Property("LoginProvider") + .HasColumnType("nvarchar(450)"); + + b.Property("Name") + .HasColumnType("nvarchar(450)"); + + b.Property("Value") + .HasColumnType("nvarchar(max)"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("AspNetUserTokens", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("BlazorWeb_CSharp.Data.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("BlazorWeb_CSharp.Data.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("BlazorWeb_CSharp.Data.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("BlazorWeb_CSharp.Data.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Data/SqlServer/00000000000000_CreateIdentitySchema.cs b/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Data/SqlServer/00000000000000_CreateIdentitySchema.cs new file mode 100644 index 0000000000..ec47e9f16b --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Data/SqlServer/00000000000000_CreateIdentitySchema.cs @@ -0,0 +1,224 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace BlazorWeb_CSharp.Migrations +{ + /// + public partial class CreateIdentitySchema : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "AspNetRoles", + columns: table => new + { + Id = table.Column(type: "nvarchar(450)", nullable: false), + Name = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), + NormalizedName = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), + ConcurrencyStamp = table.Column(type: "nvarchar(max)", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_AspNetRoles", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "AspNetUsers", + columns: table => new + { + Id = table.Column(type: "nvarchar(450)", nullable: false), + UserName = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), + NormalizedUserName = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), + Email = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), + NormalizedEmail = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), + EmailConfirmed = table.Column(type: "bit", nullable: false), + PasswordHash = table.Column(type: "nvarchar(max)", nullable: true), + SecurityStamp = table.Column(type: "nvarchar(max)", nullable: true), + ConcurrencyStamp = table.Column(type: "nvarchar(max)", nullable: true), + PhoneNumber = table.Column(type: "nvarchar(max)", nullable: true), + PhoneNumberConfirmed = table.Column(type: "bit", nullable: false), + TwoFactorEnabled = table.Column(type: "bit", nullable: false), + LockoutEnd = table.Column(type: "datetimeoffset", nullable: true), + LockoutEnabled = table.Column(type: "bit", nullable: false), + AccessFailedCount = table.Column(type: "int", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_AspNetUsers", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "AspNetRoleClaims", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("SqlServer:Identity", "1, 1"), + RoleId = table.Column(type: "nvarchar(450)", nullable: false), + ClaimType = table.Column(type: "nvarchar(max)", nullable: true), + ClaimValue = table.Column(type: "nvarchar(max)", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_AspNetRoleClaims", x => x.Id); + table.ForeignKey( + name: "FK_AspNetRoleClaims_AspNetRoles_RoleId", + column: x => x.RoleId, + principalTable: "AspNetRoles", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "AspNetUserClaims", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("SqlServer:Identity", "1, 1"), + UserId = table.Column(type: "nvarchar(450)", nullable: false), + ClaimType = table.Column(type: "nvarchar(max)", nullable: true), + ClaimValue = table.Column(type: "nvarchar(max)", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_AspNetUserClaims", x => x.Id); + table.ForeignKey( + name: "FK_AspNetUserClaims_AspNetUsers_UserId", + column: x => x.UserId, + principalTable: "AspNetUsers", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "AspNetUserLogins", + columns: table => new + { + LoginProvider = table.Column(type: "nvarchar(450)", nullable: false), + ProviderKey = table.Column(type: "nvarchar(450)", nullable: false), + ProviderDisplayName = table.Column(type: "nvarchar(max)", nullable: true), + UserId = table.Column(type: "nvarchar(450)", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_AspNetUserLogins", x => new { x.LoginProvider, x.ProviderKey }); + table.ForeignKey( + name: "FK_AspNetUserLogins_AspNetUsers_UserId", + column: x => x.UserId, + principalTable: "AspNetUsers", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "AspNetUserRoles", + columns: table => new + { + UserId = table.Column(type: "nvarchar(450)", nullable: false), + RoleId = table.Column(type: "nvarchar(450)", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_AspNetUserRoles", x => new { x.UserId, x.RoleId }); + table.ForeignKey( + name: "FK_AspNetUserRoles_AspNetRoles_RoleId", + column: x => x.RoleId, + principalTable: "AspNetRoles", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_AspNetUserRoles_AspNetUsers_UserId", + column: x => x.UserId, + principalTable: "AspNetUsers", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "AspNetUserTokens", + columns: table => new + { + UserId = table.Column(type: "nvarchar(450)", nullable: false), + LoginProvider = table.Column(type: "nvarchar(450)", nullable: false), + Name = table.Column(type: "nvarchar(450)", nullable: false), + Value = table.Column(type: "nvarchar(max)", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_AspNetUserTokens", x => new { x.UserId, x.LoginProvider, x.Name }); + table.ForeignKey( + name: "FK_AspNetUserTokens_AspNetUsers_UserId", + column: x => x.UserId, + principalTable: "AspNetUsers", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateIndex( + name: "IX_AspNetRoleClaims_RoleId", + table: "AspNetRoleClaims", + column: "RoleId"); + + migrationBuilder.CreateIndex( + name: "RoleNameIndex", + table: "AspNetRoles", + column: "NormalizedName", + unique: true, + filter: "[NormalizedName] IS NOT NULL"); + + migrationBuilder.CreateIndex( + name: "IX_AspNetUserClaims_UserId", + table: "AspNetUserClaims", + column: "UserId"); + + migrationBuilder.CreateIndex( + name: "IX_AspNetUserLogins_UserId", + table: "AspNetUserLogins", + column: "UserId"); + + migrationBuilder.CreateIndex( + name: "IX_AspNetUserRoles_RoleId", + table: "AspNetUserRoles", + column: "RoleId"); + + migrationBuilder.CreateIndex( + name: "EmailIndex", + table: "AspNetUsers", + column: "NormalizedEmail"); + + migrationBuilder.CreateIndex( + name: "UserNameIndex", + table: "AspNetUsers", + column: "NormalizedUserName", + unique: true, + filter: "[NormalizedUserName] IS NOT NULL"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "AspNetRoleClaims"); + + migrationBuilder.DropTable( + name: "AspNetUserClaims"); + + migrationBuilder.DropTable( + name: "AspNetUserLogins"); + + migrationBuilder.DropTable( + name: "AspNetUserRoles"); + + migrationBuilder.DropTable( + name: "AspNetUserTokens"); + + migrationBuilder.DropTable( + name: "AspNetRoles"); + + migrationBuilder.DropTable( + name: "AspNetUsers"); + } + } +} diff --git a/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Data/SqlServer/ApplicationDbContextModelSnapshot.cs b/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Data/SqlServer/ApplicationDbContextModelSnapshot.cs new file mode 100644 index 0000000000..a6b6896c65 --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Data/SqlServer/ApplicationDbContextModelSnapshot.cs @@ -0,0 +1,276 @@ +// +using System; +using BlazorWeb_CSharp.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace BlazorWeb_CSharp.Migrations +{ + [DbContext(typeof(ApplicationDbContext))] + partial class ApplicationDbContextModelSnapshot : ModelSnapshot + { + protected override void BuildModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.0") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); + + modelBuilder.Entity("BlazorWeb_CSharp.Data.ApplicationUser", b => + { + b.Property("Id") + .HasColumnType("nvarchar(450)"); + + b.Property("AccessFailedCount") + .HasColumnType("int"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("nvarchar(max)"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("EmailConfirmed") + .HasColumnType("bit"); + + b.Property("LockoutEnabled") + .HasColumnType("bit"); + + b.Property("LockoutEnd") + .HasColumnType("datetimeoffset"); + + b.Property("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("PasswordHash") + .HasColumnType("nvarchar(max)"); + + b.Property("PhoneNumber") + .HasColumnType("nvarchar(max)"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("bit"); + + b.Property("SecurityStamp") + .HasColumnType("nvarchar(max)"); + + b.Property("TwoFactorEnabled") + .HasColumnType("bit"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex") + .HasFilter("[NormalizedUserName] IS NOT NULL"); + + b.ToTable("AspNetUsers", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .HasColumnType("nvarchar(450)"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("NormalizedName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasDatabaseName("RoleNameIndex") + .HasFilter("[NormalizedName] IS NOT NULL"); + + b.ToTable("AspNetRoles", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("nvarchar(max)"); + + b.Property("ClaimValue") + .HasColumnType("nvarchar(max)"); + + b.Property("RoleId") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetRoleClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("nvarchar(max)"); + + b.Property("ClaimValue") + .HasColumnType("nvarchar(max)"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasColumnType("nvarchar(450)"); + + b.Property("ProviderKey") + .HasColumnType("nvarchar(450)"); + + b.Property("ProviderDisplayName") + .HasColumnType("nvarchar(max)"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserLogins", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("nvarchar(450)"); + + b.Property("RoleId") + .HasColumnType("nvarchar(450)"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetUserRoles", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("nvarchar(450)"); + + b.Property("LoginProvider") + .HasColumnType("nvarchar(450)"); + + b.Property("Name") + .HasColumnType("nvarchar(450)"); + + b.Property("Value") + .HasColumnType("nvarchar(max)"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("AspNetUserTokens", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("BlazorWeb_CSharp.Data.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("BlazorWeb_CSharp.Data.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("BlazorWeb_CSharp.Data.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("BlazorWeb_CSharp.Data.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Data/app.db b/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Data/app.db new file mode 100644 index 0000000000..769de58a9e Binary files /dev/null and b/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Data/app.db differ diff --git a/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Program.Main.cs b/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Program.Main.cs new file mode 100644 index 0000000000..34c9971a6b --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Program.Main.cs @@ -0,0 +1,145 @@ +#if (IndividualLocalAuth) +#if (UseServer) +using Microsoft.AspNetCore.Components.Authorization; +#endif +using Microsoft.AspNetCore.Identity; +using Microsoft.EntityFrameworkCore; +#endif +#if (UseWebAssembly && SampleContent) +using BlazorWeb_CSharp.Client.Pages; +#endif +using Microsoft.FluentUI.AspNetCore.Components; +using BlazorWeb_CSharp.Components; +#if (IndividualLocalAuth) +using BlazorWeb_CSharp.Components.Account; +using BlazorWeb_CSharp.Data; +#endif + +namespace BlazorWeb_CSharp; + +public class Program +{ + public static void Main(string[] args) + { + var builder = WebApplication.CreateBuilder(args); + + // Add services to the container. + #if (!UseServer && !UseWebAssembly) + builder.Services.AddRazorComponents(); + #else + builder.Services.AddRazorComponents() + #if (UseServer && UseWebAssembly && IndividualLocalAuth) + .AddInteractiveServerComponents() + .AddInteractiveWebAssemblyComponents() + .AddAuthenticationStateSerialization(); + #elif (UseServer && UseWebAssembly) + .AddInteractiveServerComponents() + .AddInteractiveWebAssemblyComponents(); + #elif (UseServer) + .AddInteractiveServerComponents(); + #elif (UseWebAssembly && IndividualLocalAuth) + .AddInteractiveWebAssemblyComponents() + .AddAuthenticationStateSerialization(); + #elif (UseWebAssembly) + .AddInteractiveWebAssemblyComponents(); + #endif + #endif + builder.Services.AddFluentUIComponents(); + + #if (IndividualLocalAuth) + builder.Services.AddCascadingAuthenticationState(); + builder.Services.AddScoped(); + builder.Services.AddScoped(); + #if (UseServer) + builder.Services.AddScoped(); + #endif + + builder.Services.AddAuthentication(options => + { + options.DefaultScheme = IdentityConstants.ApplicationScheme; + options.DefaultSignInScheme = IdentityConstants.ExternalScheme; + }) + .AddIdentityCookies(); + #if (!UseServer) + builder.Services.AddAuthorization(); + #endif + + var connectionString = builder.Configuration.GetConnectionString("DefaultConnection") ?? throw new InvalidOperationException("Connection string 'DefaultConnection' not found."); + builder.Services.AddDbContext(options => + #if (UseLocalDB) + options.UseSqlServer(connectionString)); + #else + options.UseSqlite(connectionString)); + #endif + builder.Services.AddDatabaseDeveloperPageExceptionFilter(); + + builder.Services.AddIdentityCore(options => options.SignIn.RequireConfirmedAccount = true) + .AddEntityFrameworkStores() + .AddSignInManager() + .AddDefaultTokenProviders(); + + builder.Services.AddSingleton, IdentityNoOpEmailSender>(); + + #endif + var app = builder.Build(); + + // Configure the HTTP request pipeline. +#if (UseWebAssembly || IndividualLocalAuth) + if (app.Environment.IsDevelopment()) + { +#if (UseWebAssembly) + app.UseWebAssemblyDebugging(); +#endif +#if (IndividualLocalAuth) + app.UseMigrationsEndPoint(); +#endif + } + else +#else + if (!app.Environment.IsDevelopment()) +#endif + { + app.UseExceptionHandler("/Error"); + #if (HasHttpsProfile) + // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts. + app.UseHsts(); + #endif + } + + #if (HasHttpsProfile) + app.UseHttpsRedirection(); + + #endif + #if (Framework == "net8.0") + app.UseStaticFiles(); + #endif + app.UseAntiforgery(); + + #if (Framework == "net9.0") + app.MapStaticAssets(); + #endif + #if (UseServer && UseWebAssembly) + app.MapRazorComponents() + .AddInteractiveServerRenderMode() + .AddInteractiveWebAssemblyRenderMode() + #elif (UseServer) + app.MapRazorComponents() + .AddInteractiveServerRenderMode(); + #elif (UseWebAssembly) + app.MapRazorComponents() + .AddInteractiveWebAssemblyRenderMode() + #else + app.MapRazorComponents(); + #endif + #if (UseWebAssembly) + .AddAdditionalAssemblies(typeof(Client._Imports).Assembly); + #endif + + #if (IndividualLocalAuth) + // Add additional endpoints required by the Identity /Account Razor components. + app.MapAdditionalIdentityEndpoints(); + + #endif + app.Run(); + } +} diff --git a/src/Templates/templates/blazorweb-csharp/BlazorWeb-CSharp/Program.cs b/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Program.cs similarity index 92% rename from src/Templates/templates/blazorweb-csharp/BlazorWeb-CSharp/Program.cs rename to src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Program.cs index c6e1e4f854..078912d732 100644 --- a/src/Templates/templates/blazorweb-csharp/BlazorWeb-CSharp/Program.cs +++ b/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Program.cs @@ -8,12 +8,12 @@ #if (UseWebAssembly && SampleContent) using BlazorWeb_CSharp.Client.Pages; #endif -using Microsoft.FluentUI.AspNetCore.Components; using BlazorWeb_CSharp.Components; #if (IndividualLocalAuth) using BlazorWeb_CSharp.Components.Account; using BlazorWeb_CSharp.Data; #endif +using Microsoft.FluentUI.AspNetCore.Components; var builder = WebApplication.CreateBuilder(args); @@ -22,21 +22,21 @@ builder.Services.AddRazorComponents(); #else builder.Services.AddRazorComponents() - #if (UseServer && UseWebAssembly && IndividualLocalAuth) +#if (UseServer && UseWebAssembly && IndividualLocalAuth) .AddInteractiveServerComponents() .AddInteractiveWebAssemblyComponents() .AddAuthenticationStateSerialization(); - #elif (UseServer && UseWebAssembly) +#elif (UseServer && UseWebAssembly) .AddInteractiveServerComponents() .AddInteractiveWebAssemblyComponents(); - #elif (UseServer) +#elif (UseServer) .AddInteractiveServerComponents(); - #elif (UseWebAssembly && IndividualLocalAuth) +#elif (UseWebAssembly && IndividualLocalAuth) .AddInteractiveWebAssemblyComponents() .AddAuthenticationStateSerialization(); - #elif (UseWebAssembly) +#elif (UseWebAssembly) .AddInteractiveWebAssemblyComponents(); - #endif +#endif #endif builder.Services.AddFluentUIComponents(); @@ -104,14 +104,10 @@ app.UseHttpsRedirection(); #endif -#if (Framework == "net8.0") -app.UseStaticFiles(); -#endif + app.UseAntiforgery(); -#if (Framework == "net9.0") app.MapStaticAssets(); -#endif #if (UseServer && UseWebAssembly) app.MapRazorComponents() .AddInteractiveServerRenderMode() diff --git a/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Properties/launchSettings.json b/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Properties/launchSettings.json new file mode 100644 index 0000000000..0d554caaa1 --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/Properties/launchSettings.json @@ -0,0 +1,37 @@ +{ + "$schema": "https://json.schemastore.org/launchsettings.json", + "profiles": { + //#if (HasHttpProfile) + "http": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + //#if (UseWebAssembly) + "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}", + //#endif + "applicationUrl": "http://localhost:5500", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + //#if (HasHttpsProfile) + }, + //#else + } + //#endif + //#endif + //#if (HasHttpsProfile) + "https": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + //#if (UseWebAssembly) + "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}", + //#endif + "applicationUrl": "https://localhost:5501;http://localhost:5500", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + //#endif + } + } diff --git a/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/appsettings.Development.json b/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/appsettings.Development.json new file mode 100644 index 0000000000..0c208ae918 --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/appsettings.Development.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + } +} diff --git a/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/appsettings.json b/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/appsettings.json new file mode 100644 index 0000000000..3a18c82e2d --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/appsettings.json @@ -0,0 +1,18 @@ +{ +////#if (IndividualLocalAuth) +// "ConnectionStrings": { +//#if (UseLocalDB) +// "DefaultConnection": "Server=(localdb)\\mssqllocaldb;Database=aspnet-BlazorWeb_CSharp-53bc9b9d-9d6a-45d4-8429-2a2761773502;Trusted_Connection=True;MultipleActiveResultSets=true" +//#else +// "DefaultConnection": "DataSource=Data\\app.db;Cache=Shared" +//#endif +// }, +////#endif + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*" +} diff --git a/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/wwwroot/app.css b/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/wwwroot/app.css new file mode 100644 index 0000000000..77cf978747 --- /dev/null +++ b/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/wwwroot/app.css @@ -0,0 +1,191 @@ +@import '_content/Microsoft.FluentUI.AspNetCore.Components/css/reboot.css'; + +body { + --body-font: "Segoe UI Variable", "Segoe UI", sans-serif; + font-family: var(--body-font); + font-size: var(--type-ramp-base-font-size); + line-height: var(--type-ramp-base-line-height); + margin: 0; +} + +.navmenu-icon { + display: none; +} + +.main { + min-height: calc(100dvh - 86px); + color: var(--neutral-foreground-rest); + align-items: stretch !important; +} + +.body-content { + align-self: stretch; + height: calc(100dvh - 86px) !important; + display: flex; +} + +.content { + padding: 0.5rem 1.5rem; + align-self: stretch !important; + width: 100%; +} + +.manage { + width: 100dvw; +} + +footer { + background: var(--neutral-layer-4); + color: var(--neutral-foreground-rest); + align-items: center; + padding: 10px 10px; +} + + footer a { + color: var(--neutral-foreground-rest); + text-decoration: none; + } + + footer a:focus { + outline: 1px dashed; + outline-offset: 3px; + } + + footer a:hover { + text-decoration: underline; + } + +.alert { + border: 1px dashed var(--accent-fill-rest); + padding: 5px; +} + + +#blazor-error-ui { + background: lightyellow; + bottom: 0; + box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.2); + display: none; + left: 0; + padding: 0.6rem 1.25rem 0.7rem 1.25rem; + position: fixed; + width: 100%; + z-index: 1000; + margin: 20px 0; +} + + #blazor-error-ui .dismiss { + cursor: pointer; + position: absolute; + right: 0.75rem; + top: 0.5rem; + } + +.blazor-error-boundary { + background: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTYiIGhlaWdodD0iNDkiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIG92ZXJmbG93PSJoaWRkZW4iPjxkZWZzPjxjbGlwUGF0aCBpZD0iY2xpcDAiPjxyZWN0IHg9IjIzNSIgeT0iNTEiIHdpZHRoPSI1NiIgaGVpZ2h0PSI0OSIvPjwvY2xpcFBhdGg+PC9kZWZzPjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMCkiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yMzUgLTUxKSI+PHBhdGggZD0iTTI2My41MDYgNTFDMjY0LjcxNyA1MSAyNjUuODEzIDUxLjQ4MzcgMjY2LjYwNiA1Mi4yNjU4TDI2Ny4wNTIgNTIuNzk4NyAyNjcuNTM5IDUzLjYyODMgMjkwLjE4NSA5Mi4xODMxIDI5MC41NDUgOTIuNzk1IDI5MC42NTYgOTIuOTk2QzI5MC44NzcgOTMuNTEzIDI5MSA5NC4wODE1IDI5MSA5NC42NzgyIDI5MSA5Ny4wNjUxIDI4OS4wMzggOTkgMjg2LjYxNyA5OUwyNDAuMzgzIDk5QzIzNy45NjMgOTkgMjM2IDk3LjA2NTEgMjM2IDk0LjY3ODIgMjM2IDk0LjM3OTkgMjM2LjAzMSA5NC4wODg2IDIzNi4wODkgOTMuODA3MkwyMzYuMzM4IDkzLjAxNjIgMjM2Ljg1OCA5Mi4xMzE0IDI1OS40NzMgNTMuNjI5NCAyNTkuOTYxIDUyLjc5ODUgMjYwLjQwNyA1Mi4yNjU4QzI2MS4yIDUxLjQ4MzcgMjYyLjI5NiA1MSAyNjMuNTA2IDUxWk0yNjMuNTg2IDY2LjAxODNDMjYwLjczNyA2Ni4wMTgzIDI1OS4zMTMgNjcuMTI0NSAyNTkuMzEzIDY5LjMzNyAyNTkuMzEzIDY5LjYxMDIgMjU5LjMzMiA2OS44NjA4IDI1OS4zNzEgNzAuMDg4N0wyNjEuNzk1IDg0LjAxNjEgMjY1LjM4IDg0LjAxNjEgMjY3LjgyMSA2OS43NDc1QzI2Ny44NiA2OS43MzA5IDI2Ny44NzkgNjkuNTg3NyAyNjcuODc5IDY5LjMxNzkgMjY3Ljg3OSA2Ny4xMTgyIDI2Ni40NDggNjYuMDE4MyAyNjMuNTg2IDY2LjAxODNaTTI2My41NzYgODYuMDU0N0MyNjEuMDQ5IDg2LjA1NDcgMjU5Ljc4NiA4Ny4zMDA1IDI1OS43ODYgODkuNzkyMSAyNTkuNzg2IDkyLjI4MzcgMjYxLjA0OSA5My41Mjk1IDI2My41NzYgOTMuNTI5NSAyNjYuMTE2IDkzLjUyOTUgMjY3LjM4NyA5Mi4yODM3IDI2Ny4zODcgODkuNzkyMSAyNjcuMzg3IDg3LjMwMDUgMjY2LjExNiA4Ni4wNTQ3IDI2My41NzYgODYuMDU0N1oiIGZpbGw9IiNGRkU1MDAiIGZpbGwtcnVsZT0iZXZlbm9kZCIvPjwvZz48L3N2Zz4=) no-repeat 1rem/1.8rem, #b32121; + padding: 1rem 1rem 1rem 3.7rem; + color: white; +} + + .blazor-error-boundary::before { + content: "An error has occurred. " + } + +.loading-progress { + position: relative; + display: block; + width: 8rem; + height: 8rem; + margin: 20vh auto 1rem auto; +} + + .loading-progress circle { + fill: none; + stroke: #e0e0e0; + stroke-width: 0.6rem; + transform-origin: 50% 50%; + transform: rotate(-90deg); + } + + .loading-progress circle:last-child { + stroke: #1b6ec2; + stroke-dasharray: calc(3.141 * var(--blazor-load-percentage, 0%) * 0.8), 500%; + transition: stroke-dasharray 0.05s ease-in-out; + } + +.loading-progress-text { + position: absolute; + text-align: center; + font-weight: bold; + inset: calc(20vh + 3.25rem) 0 auto 0.2rem; +} + + .loading-progress-text:after { + content: var(--blazor-load-percentage-text, "Loading"); + } + +code { + color: #c02d76; +} + +@media (max-width: 600px) { + .header-gutters { + margin: 0.5rem 3rem 0.5rem 1.5rem !important; + } + + [dir="rtl"] .header-gutters { + margin: 0.5rem 1.5rem 0.5rem 3rem !important; + } + + .main { + flex-direction: column !important; + row-gap: 0 !important; + } + + nav.sitenav { + width: 100%; + height: 100%; + } + + #main-menu { + width: 100% !important; + } + + #main-menu > div:first-child:is(.expander) { + display: none; + } + + .navmenu { + width: 100%; + } + + #navmenu-toggle { + appearance: none; + } + + #navmenu-toggle ~ nav { + display: none; + } + + #navmenu-toggle:checked ~ nav { + display: block; + } + + .navmenu-icon { + cursor: pointer; + z-index: 10; + display: block; + position: absolute; + top: 15px; + left: unset; + right: 20px; + width: 20px; + height: 20px; + border: none; + } + + [dir="rtl"] .navmenu-icon { + left: 20px; + right: unset; + } +} diff --git a/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/wwwroot/favicon.ico b/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/wwwroot/favicon.ico new file mode 100644 index 0000000000..e189d8e579 Binary files /dev/null and b/src/Templates/templates/blazorweb-csharp-9/BlazorWeb-CSharp/wwwroot/favicon.ico differ diff --git a/src/Templates/templates/componentswebassembly-csharp/.template.config/dotnetcli.host.json b/src/Templates/templates/componentswebassembly-csharp-10/.template.config/dotnetcli.host.json similarity index 100% rename from src/Templates/templates/componentswebassembly-csharp/.template.config/dotnetcli.host.json rename to src/Templates/templates/componentswebassembly-csharp-10/.template.config/dotnetcli.host.json diff --git a/src/Templates/templates/componentswebassembly-csharp-10/.template.config/icon.png b/src/Templates/templates/componentswebassembly-csharp-10/.template.config/icon.png new file mode 100644 index 0000000000..42fb1d1efe Binary files /dev/null and b/src/Templates/templates/componentswebassembly-csharp-10/.template.config/icon.png differ diff --git a/src/Templates/templates/componentswebassembly-csharp/.template.config/ide.host.json b/src/Templates/templates/componentswebassembly-csharp-10/.template.config/ide.host.json similarity index 100% rename from src/Templates/templates/componentswebassembly-csharp/.template.config/ide.host.json rename to src/Templates/templates/componentswebassembly-csharp-10/.template.config/ide.host.json diff --git a/src/Templates/templates/componentswebassembly-csharp-10/.template.config/localize/templatestrings.cs.json b/src/Templates/templates/componentswebassembly-csharp-10/.template.config/localize/templatestrings.cs.json new file mode 100644 index 0000000000..9db6ab19cb --- /dev/null +++ b/src/Templates/templates/componentswebassembly-csharp-10/.template.config/localize/templatestrings.cs.json @@ -0,0 +1,44 @@ +{ + "author": "Microsoft", + "name": "Samostatná aplikace Fluent Blazor WebAssembly", + "description": "Šablona projektu pro vytvoření aplikace Blazor, která běží ve WebAssembly. Tato šablona se dá využít pro webové aplikace s propracovanými dynamickými uživatelskými rozhraními (UI).", + "symbols/Framework/description": "Cílová architektura pro projekt", + "symbols/Framework/choices/net10.0/description": "Cíl net10.0", + "symbols/skipRestore/description": "Pokud se tato možnost zadá, přeskočí automatické obnovení projektu při vytvoření.", + "symbols/auth/choices/None/description": "Bez ověřování", + "symbols/auth/choices/Individual/description": "Ověřování Individual", + "symbols/auth/choices/IndividualB2C/description": "Ověřování Individual pomocí Azure AD B2C", + "symbols/auth/choices/SingleOrg/description": "Ověřování organizace pro jednoho tenanta", + "symbols/auth/description": "Typ ověřování, který se má použít", + "symbols/Authority/description": "Autorita poskytovatele OIDC (používá se se samostatným ověřováním Individual).", + "symbols/AAdB2CInstance/description": "Instance Azure Active Directory B2C, ke které se chcete připojit (používá se s ověřováním IndividualB2C).", + "symbols/SignUpSignInPolicyId/description": "ID zásad přihlašování a registrace pro tento projekt (používá se s ověřováním IndividualB2C).", + "symbols/AADInstance/description": "Instance Azure Active Directory, ke které se chcete připojit (používá se s ověřováním SingleOrg).", + "symbols/ClientId/description": "ID klienta pro tento projekt (v samostatných scénářích se používá s ověřováním IndividualB2C, SingleOrg nebo Individual).", + "symbols/Domain/description": "Doména pro tenanta adresáře (používá se s ověřováním SingleOrg nebo IndividualB2C).", + "symbols/AppIDUri/description": "Identifikátor URI ID aplikace pro serverové rozhraní API, které chceme volat (používá se s ověřováním SingleOrg nebo IndividualB2C).", + "symbols/APIClientId/description": "ID klienta pro rozhraní API, které server hostuje (používá se s IndividualB2C, SingleOrg).", + "symbols/DefaultScope/description": "Rozsah rozhraní API, který musí klient požádat o zřízení přístupového tokenu. (použijte s IndividualB2C, SingleOrg).", + "symbols/TenantId/description": "TenantId adresáře, ke kterému se chcete připojit (používá se s ověřováním SingleOrg).", + "symbols/OrgReadAccess/description": "Určuje, jestli se této aplikaci povolí přístup ke čtení adresáře (platí jenom pro ověřování SingleOrg).", + "symbols/UserSecretsId/description": "ID, které se má použít pro tajné kódy (používá se s ověřováním OrgReadAccess nebo Individual).", + "symbols/ExcludeLaunchSettings/description": "Určuje, jestli se má z vygenerované šablony vyloučit soubor launchSettings.json.", + "symbols/kestrelHttpPort/description": "Číslo portu, který se má použít pro koncový bod HTTP v souboru launchSettings.json.", + "symbols/kestrelHttpsPort/description": "Číslo portu, který se má použít pro koncový bod HTTPS v souboru launchSettings.json. Tato možnost se dá použít jenom v případě, že se nepoužije parametr no-https (no-https se bude ignorovat, pokud se použije IndividualAuth nebo OrganizationalAuth).", + "symbols/iisHttpPort/description": "Číslo portu, který se má použít pro koncový bod IIS Express HTTP v souboru launchSettings.json.", + "symbols/iisHttpsPort/description": "Číslo portu, který se má použít pro koncový bod IIS Express HTTPS v souboru launchSettings.json. Tato možnost se dá použít jenom v případě, že se nepoužije parametr no-https (no-https se bude ignorovat, pokud se použije IndividualAuth nebo OrganizationalAuth).", + "symbols/PWA/displayName": "_Progresivní webová aplikace", + "symbols/PWA/description": "Pokud je tato možnost zadaná, vytvoří progresivní webovou aplikaci (PWA), která podporuje instalaci a offline použití.", + "symbols/IncludeSampleContent/displayName": "_Zahrnout ukázkové stránky", + "symbols/IncludeSampleContent/description": "Nastavuje, jestli se mají přidávat ukázkové stránky a styly pro demonstraci základních vzorů použití.", + "symbols/Empty/description": "Nastavuje, jestli se mají vynechat ukázkové stránky a styly, které demonstrují základní vzory použití.", + "symbols/NoHttps/description": "Určuje, jestli se má protokol HTTPS vypnout. Tato možnost platí jenom v případě, že se pro --auth nepoužívají Individual, IndividualB2C, SingleOrg ani MultiOrg.", + "symbols/UseLocalDB/description": "Určuje, jestli se má použít LocalDB namísto SQLite. Tato možnost platí jenom v případě, že je zadáno --auth Individual nebo --auth IndividualB2C.", + "symbols/CalledApiUrl/description": "Adresa URL rozhraní API, která se má volat z webové aplikace. Tato možnost platí jenom v případě, že je zadáno --auth SingleOrg, --auth MultiOrg nebo --auth IndividualB2C a také je zadán hostitel ASP.NET Core.", + "symbols/CalledApiScopes/description": "Obory, které se mají požádat žádat o volání rozhraní API z webové aplikace. Tato možnost platí jenom v případě, že je zadáno --auth SingleOrg, --auth MultiOrg nebo --auth IndividualB2C a také je zadán hostitel ASP.NET Core.", + "symbols/UseProgramMain/displayName": "Nepoužívat _příkazy nejvyšší úrovně", + "symbols/UseProgramMain/description": "Určuje, jestli se má místo příkazů nejvyšší úrovně generovat explicitní třída Program a metoda Main.", + "postActions/restore/description": "Obnoví balíčky NuGet vyžadované tímto projektem.", + "postActions/restore/manualInstructions/default/text": "Spustit dotnet restore", + "postActions/open-file/description": "Otevře soubor Readme.txt v editoru." +} diff --git a/src/Templates/templates/componentswebassembly-csharp-10/.template.config/localize/templatestrings.de.json b/src/Templates/templates/componentswebassembly-csharp-10/.template.config/localize/templatestrings.de.json new file mode 100644 index 0000000000..84d3b20c5e --- /dev/null +++ b/src/Templates/templates/componentswebassembly-csharp-10/.template.config/localize/templatestrings.de.json @@ -0,0 +1,44 @@ +{ + "author": "Microsoft", + "name": "Eigenständige Fluent Blazor WebAssembly-App", + "description": "Eine Projektvorlage für das Erstellen einer Blazor-App, die in WebAssembly ausgeführt wird. Diese Vorlage kann für Web-Apps mit umfangreichen dynamischen Benutzeroberflächen verwendet werden.", + "symbols/Framework/description": "Das Zielframework für das Projekt.", + "symbols/Framework/choices/net10.0/description": "Ziel.-NET10.0", + "symbols/skipRestore/description": "Wenn angegeben, wird die automatische Wiederherstellung des Projekts beim Erstellen übersprungen.", + "symbols/auth/choices/None/description": "Keine Authentifizierung", + "symbols/auth/choices/Individual/description": "Individuelle Authentifizierung", + "symbols/auth/choices/IndividualB2C/description": "Individuelle Authentifizierung mit Azure AD B2C", + "symbols/auth/choices/SingleOrg/description": "Organisationsauthentifizierung für einen einzelnen Mandanten", + "symbols/auth/description": "Der zu verwendende Authentifizierungstyp", + "symbols/Authority/description": "Die Autorität des OIDC Anbieters (Verwendung mit eigenständiger individueller Authentifizierung).", + "symbols/AAdB2CInstance/description": "Die Azure Active Directory B2C-Instanz, mit der eine Verbindung hergestellt werden soll (mit IndividualB2C-Authentifizierung verwenden).", + "symbols/SignUpSignInPolicyId/description": "Die Anmelde und Registrierungsrichtlinien ID für dieses Projekt (mit IndividualB2C Authentifizierung verwenden).", + "symbols/AADInstance/description": "Die Azure Active Directory-Instanz, mit der eine Verbindung hergestellt werden soll (mit SingleOrg-Authentifizierung verwenden).", + "symbols/ClientId/description": "Die Client-ID für dieses Projekt (Verwendung mit IndividualB2C, SingleOrg oder Individual Auth in eigenständigen Szenarien).", + "symbols/Domain/description": "Die Domäne für den Verzeichnismandanten (mit SingleOrg oder IndividualB2C Authentifizierung verwenden).", + "symbols/AppIDUri/description": "Der App ID URI für die Server-API, die wir aufrufen möchten (mit SingleOrg- oder IndividualB2C-Authentifizierung verwenden).", + "symbols/APIClientId/description": "Die Client ID für die API, die der Server hostet (mit IndividualB2C, SingleOrg verwenden).", + "symbols/DefaultScope/description": "Der API Bereich, den der Client anfordern muss, um ein Zugriffstoken bereitzustellen. (Verwendung mit IndividualB2C, SingleOrg).", + "symbols/TenantId/description": "Die TenantId ID des Verzeichnisses, mit dem eine Verbindung hergestellt werden soll (mit SingleOrg Authentifizierung verwenden).", + "symbols/OrgReadAccess/description": "Ob dieser Anwendung Lesezugriff auf das Verzeichnis gewährt werden soll oder nicht (gilt nur für SingleOrg).", + "symbols/UserSecretsId/description": "Die für Geheimnisse zu verwendende ID (Verwendung mit OrgReadAccess oder Einzelauthentifizierung).", + "symbols/ExcludeLaunchSettings/description": "Ob launchSettings.json aus der generierten Vorlage ausgeschlossen werden soll.", + "symbols/kestrelHttpPort/description": "Portnummer, die für den HTTP Endpunkt in launchSettings.json verwendet werden soll.", + "symbols/kestrelHttpsPort/description": "Portnummer, die für den HTTPS Endpunkt in launchSettings.json verwendet werden soll. Diese Option ist nur anwendbar, wenn der Parameter no-https nicht verwendet wird (no-https wird ignoriert, wenn entweder IndividualAuth oder OrganizationalAuth verwendet wird).", + "symbols/iisHttpPort/description": "Portnummer, die für den IIS Express HTTP Endpunkt in launchSettings.json verwendet werden soll.", + "symbols/iisHttpsPort/description": "Portnummer, die für den IIS Express HTTPS Endpunkt in launchSettings.json verwendet werden soll. Diese Option ist nur anwendbar, wenn der Parameter no-https nicht verwendet wird (no-https wird ignoriert, wenn entweder IndividualAuth oder OrganizationalAuth verwendet wird).", + "symbols/PWA/displayName": "_Progressive Webanwendung", + "symbols/PWA/description": "Wenn angegeben, wird eine Progressive Web Application (PWA) erstellt, die die Installation und Offlineverwendung unterstützt.", + "symbols/IncludeSampleContent/displayName": "_Include Beispielseiten", + "symbols/IncludeSampleContent/description": "Konfiguriert, ob Beispielseiten und Stile hinzugefügt werden, um grundlegende Verwendungsmuster zu veranschaulichen.", + "symbols/Empty/description": "Konfiguriert, ob Beispielseiten und Formatierungen weggelassen werden sollen, die grundlegende Verwendungsmuster veranschaulichen.", + "symbols/NoHttps/description": "Ob HTTPS deaktiviert werden soll. Diese Option gilt nur, wenn Individual, IndividualB2C, SingleOrg oder MultiOrg nicht für --auth verwendet werden.", + "symbols/UseLocalDB/description": "Ob LocalDB anstelle von SQLite verwendet werden soll. Diese Option gilt nur, wenn --auth Individual oder --auth IndividualB2C angegeben ist.", + "symbols/CalledApiUrl/description": "URL der API, die von der Web-App aufgerufen werden soll. Diese Option gilt nur, wenn --auth SingleOrg, --auth MultiOrg oder --auth IndividualB2C ohne und ASP.NET Core-Host angegeben ist.", + "symbols/CalledApiScopes/description": "Anzufordernde Bereiche zum Aufrufen der API von der Web-App. Diese Option gilt nur, wenn --auth SingleOrg, --auth MultiOrg oder --auth IndividualB2C ohne und ASP.NET Core-Host angegeben ist.", + "symbols/UseProgramMain/displayName": "Keine Anweisungen_der obersten Ebene verwenden", + "symbols/UseProgramMain/description": "Gibt an, ob anstelle von Anweisungen der obersten Ebene eine explizite Programmklasse und eine Main-Methode generiert werden soll.", + "postActions/restore/description": "„NuGet-Pakete“ wiederherstellen, die für dieses Projekt erforderlich sind.", + "postActions/restore/manualInstructions/default/text": "„dotnet restore“ ausführen", + "postActions/open-file/description": "Öffnet Readme.txt im Editor" +} \ No newline at end of file diff --git a/src/Templates/templates/componentswebassembly-csharp-10/.template.config/localize/templatestrings.en.json b/src/Templates/templates/componentswebassembly-csharp-10/.template.config/localize/templatestrings.en.json new file mode 100644 index 0000000000..a00731b074 --- /dev/null +++ b/src/Templates/templates/componentswebassembly-csharp-10/.template.config/localize/templatestrings.en.json @@ -0,0 +1,45 @@ +{ + "author": "Microsoft", + "name": "Fluent Blazor WebAssembly Standalone App", + "description": "A project template for creating a Blazor app that runs on WebAssembly and uses the Fluent component library. This template can be used for web apps with rich dynamic user interfaces (UIs).", + "symbols/Framework/description": "The target framework for the project.", + "symbols/Framework/choices/net10.0/description": "Target net10.0", + "symbols/skipRestore/description": "If specified, skips the automatic restore of the project on create.", + "symbols/auth/choices/None/description": "No authentication", + "symbols/auth/choices/Individual/description": "Individual authentication", + "symbols/auth/choices/IndividualB2C/description": "Individual authentication with Azure AD B2C", + "symbols/auth/choices/SingleOrg/description": "Organizational authentication for a single tenant", + "symbols/auth/description": "The type of authentication to use", + "symbols/Authority/description": "The authority of the OIDC provider (use with standalone Individual auth).", + "symbols/AAdB2CInstance/description": "The Azure Active Directory B2C instance to connect to (use with IndividualB2C auth).", + "symbols/SignUpSignInPolicyId/description": "The sign-in and sign-up policy ID for this project (use with IndividualB2C auth).", + "symbols/AADInstance/description": "The Azure Active Directory instance to connect to (use with SingleOrg auth).", + "symbols/ClientId/description": "The Client ID for this project (use with IndividualB2C, SingleOrg or Individual auth in standalone scenarios).", + "symbols/Domain/description": "The domain for the directory tenant (use with SingleOrg or IndividualB2C auth).", + "symbols/AppIDUri/description": "The App ID Uri for the server API we want to call (use with SingleOrg or IndividualB2C auth).", + "symbols/APIClientId/description": "The Client ID for the API that the server hosts (use with IndividualB2C, SingleOrg).", + "symbols/DefaultScope/description": "The API scope the client needs to request to provision an access token. (use with IndividualB2C, SingleOrg).", + "symbols/TenantId/description": "The TenantId ID of the directory to connect to (use with SingleOrg auth).", + "symbols/OrgReadAccess/description": "Whether or not to allow this application read access to the directory (only applies to SingleOrg).", + "symbols/UserSecretsId/description": "The ID to use for secrets (use with OrgReadAccess or Individual auth).", + "symbols/ExcludeLaunchSettings/description": "Whether to exclude launchSettings.json from the generated template.", + "symbols/kestrelHttpPort/description": "Port number to use for the HTTP endpoint in launchSettings.json.", + "symbols/kestrelHttpsPort/description": "Port number to use for the HTTPS endpoint in launchSettings.json. This option is only applicable when the parameter no-https is not used (no-https will be ignored if either IndividualAuth or OrganizationalAuth is used).", + "symbols/iisHttpPort/description": "Port number to use for the IIS Express HTTP endpoint in launchSettings.json.", + "symbols/iisHttpsPort/description": "Port number to use for the IIS Express HTTPS endpoint in launchSettings.json. This option is only applicable when the parameter no-https is not used (no-https will be ignored if either IndividualAuth or OrganizationalAuth is used).", + "symbols/PWA/displayName": "_Progressive Web Application", + "symbols/PWA/description": "If specified, produces a Progressive Web Application (PWA) supporting installation and offline use.", + "symbols/IncludeSampleContent/displayName": "_Include sample pages", + "symbols/IncludeSampleContent/description": "Configures whether to add sample pages and styling to demonstrate basic usage patterns.", + "symbols/Empty/description": "Configures whether to omit sample pages and styling that demonstrate basic usage patterns.", + "symbols/NoHttps/description": "Whether to turn off HTTPS. This option only applies if Individual, IndividualB2C, SingleOrg, or MultiOrg aren't used for --auth.", + "symbols/UseLocalDB/description": "Whether to use LocalDB instead of SQLite. This option only applies if --auth Individual or --auth IndividualB2C is specified.", + "symbols/CalledApiUrl/description": "URL of the API to call from the web app. This option only applies if --auth SingleOrg, --auth MultiOrg or --auth IndividualB2C without and ASP.NET Core host is specified.", + "symbols/CalledApiScopes/description": "Scopes to request to call the API from the web app. This option only applies if --auth SingleOrg, --auth MultiOrg or --auth IndividualB2C without and ASP.NET Core host is specified.", + "symbols/UseProgramMain/displayName": "Do not use _top-level statements", + "_symbols/UseProgramMain/displayName.comment": "Use '_' as accelerator key when translating.", + "symbols/UseProgramMain/description": "Whether to generate an explicit Program class and Main method instead of top-level statements.", + "postActions/restore/description": "Restore NuGet packages required by this project.", + "postActions/restore/manualInstructions/default/text": "Run 'dotnet restore'", + "postActions/open-file/description": "Opens Readme.txt in the editor" +} \ No newline at end of file diff --git a/src/Templates/templates/componentswebassembly-csharp-10/.template.config/localize/templatestrings.es.json b/src/Templates/templates/componentswebassembly-csharp-10/.template.config/localize/templatestrings.es.json new file mode 100644 index 0000000000..a4f9dab4fe --- /dev/null +++ b/src/Templates/templates/componentswebassembly-csharp-10/.template.config/localize/templatestrings.es.json @@ -0,0 +1,44 @@ +{ + "author": "Microsoft", + "name": "Aplicación independiente WebAssembly de Fluent Blazor", + "description": "Plantilla de proyecto para crear una aplicación Blazor que se ejecuta en WebAssembly. Esta plantilla se puede usar para las aplicaciones web con interfaces de usuario dinámicas enriquecidas.", + "symbols/Framework/description": "Marco de destino del proyecto.", + "symbols/Framework/choices/net10.0/description": "Destino net10.0", + "symbols/skipRestore/description": "Si se especifica, se omite la restauración automática del proyecto durante la creación.", + "symbols/auth/choices/None/description": "Sin autenticación", + "symbols/auth/choices/Individual/description": "Autenticación individual", + "symbols/auth/choices/IndividualB2C/description": "Autenticación individual con Azure AD B2C", + "symbols/auth/choices/SingleOrg/description": "Autenticación organizacional para un único inquilino", + "symbols/auth/description": "El tipo de autenticación que se va a usar", + "symbols/Authority/description": "Autoridad del proveedor de OIDC (se usa con la autenticación individual independiente).", + "symbols/AAdB2CInstance/description": "Instancia de Azure Active Directory B2C a la que conectarse (se usa con la autenticación IndividualB2C).", + "symbols/SignUpSignInPolicyId/description": "Id. de la directiva de inicio de sesión y registro para este proyecto (se usa con la autenticación IndividualB2C).", + "symbols/AADInstance/description": "Instancia de Azure Active Directory a la que se va a conectar (se usa con la autenticación SingleOrg).", + "symbols/ClientId/description": "Id. de cliente de este proyecto (se usa con IndividualB2C, SingleOrg o autenticación individual en escenarios independientes).", + "symbols/Domain/description": "Dominio del inquilino de directorio (se usa con la autenticación SingleOrg o IndividualB2C).", + "symbols/AppIDUri/description": "Uri de id. de aplicación para la API de servidor a la que queremos llamar (se usa con la autenticación SingleOrg o IndividualB2C).", + "symbols/APIClientId/description": "Id. de cliente de la API que hospeda el servidor (se usa con IndividualB2C, SingleOrg).", + "symbols/DefaultScope/description": "Ámbito de API que el cliente debe solicitar para aprovisionar un token de acceso. (Se usa con IndividualB2C, SingleOrg).", + "symbols/TenantId/description": "Id. de TenantId del directorio al que se va a conectar (se usa con la autenticación SingleOrg).", + "symbols/OrgReadAccess/description": "Indica si se va a permitir o no el acceso de lectura de esta aplicación al directorio (solo se aplica a SingleOrg).", + "symbols/UserSecretsId/description": "Id. que se va a usar para los secretos (se usa con OrgReadAccess o con la autenticación individual).", + "symbols/ExcludeLaunchSettings/description": "Indica si se va a excluir launchSettings.json de la plantilla generada.", + "symbols/kestrelHttpPort/description": "Número de puerto que se va a usar para el punto de conexión HTTP en launchSettings.json.", + "symbols/kestrelHttpsPort/description": "Número de puerto que se va a usar para el punto de conexión HTTPS en launchSettings.json. Esta opción solo es aplicable cuando no se usa el parámetro no-https (no-https se omitirá si se usa IndividualAuth o OrganizationalAuth).", + "symbols/iisHttpPort/description": "Número de puerto que se va a usar para el punto de conexión HTTP de IIS Express en launchSettings.json.", + "symbols/iisHttpsPort/description": "Número de puerto que se va a usar para el punto de conexión HTTPS de IIS Express en launchSettings.json. Esta opción solo es aplicable cuando no se usa el parámetro no-https (no-https se omitirá si se usa IndividualAuth o OrganizationalAuth).", + "symbols/PWA/displayName": "_Aplicación web progresiva", + "symbols/PWA/description": "Si se especifica, produce una aplicación web progresiva (PWA) compatible con la instalación y el uso sin conexión.", + "symbols/IncludeSampleContent/displayName": "_Incluir páginas de ejemplo", + "symbols/IncludeSampleContent/description": "Configura si se van a agregar páginas de ejemplo y estilos para mostrar patrones de uso básicos.", + "symbols/Empty/description": "Configura si se omiten las páginas de ejemplo y los estilos que muestran patrones de uso básicos.", + "symbols/NoHttps/description": "Si se va a desactivar HTTPS. Esta opción solo se aplica si Individual, IndividualB2C, SingleOrg o MultiOrg no se usan para --auth.", + "symbols/UseLocalDB/description": "Indica si se va a usar LocalDB en lugar de SQLite. Esta opción solo se aplica si se especifica --auth Individual o --auth IndividualB2C.", + "symbols/CalledApiUrl/description": "Dirección URL de la API a la que se va a llamar desde la aplicación web. Esta opción solo se aplica si se especifica --auth SingleOrg, --auth MultiOrg o --auth IndividualB2C sin y se especifica ASP.NET host core.", + "symbols/CalledApiScopes/description": "Ámbitos para solicitar llamar a la API desde la aplicación web. Esta opción solo se aplica si se especifica --auth SingleOrg, --auth MultiOrg o --auth IndividualB2C sin y se especifica el host ASP.NET Core.", + "symbols/UseProgramMain/displayName": "No usar instrucciones de _nivel superior", + "symbols/UseProgramMain/description": "Indica si se debe generar una clase Program explícita y un método Main en lugar de instrucciones de nivel superior.", + "postActions/restore/description": "Restaure los paquetes NuGet necesarios para este proyecto.", + "postActions/restore/manualInstructions/default/text": "Ejecutar \"dotnet restore\"", + "postActions/open-file/description": "Abre Readme.txt en el editor" +} \ No newline at end of file diff --git a/src/Templates/templates/componentswebassembly-csharp-10/.template.config/localize/templatestrings.fr.json b/src/Templates/templates/componentswebassembly-csharp-10/.template.config/localize/templatestrings.fr.json new file mode 100644 index 0000000000..b5ae4eec7f --- /dev/null +++ b/src/Templates/templates/componentswebassembly-csharp-10/.template.config/localize/templatestrings.fr.json @@ -0,0 +1,44 @@ +{ + "author": "Microsoft", + "name": "Application Fluent Blazor WebAssembly autonome", + "description": "Modèle de projet permettant de créer une application Blazor qui s'exécute sur WebAssembly. Vous pouvez utiliser ce modèle pour les applications web ayant des IU (interfaces utilisateur) dynamiques riches.", + "symbols/Framework/description": "Framework cible du projet.", + "symbols/Framework/choices/net10.0/description": "Net10.0 cible", + "symbols/skipRestore/description": "S’il est spécifié, ignore la restauration automatique du projet lors de la création.", + "symbols/auth/choices/None/description": "Aucune authentification", + "symbols/auth/choices/Individual/description": "Authentification individuelle", + "symbols/auth/choices/IndividualB2C/description": "Authentification individuelle avec Azure AD B2C", + "symbols/auth/choices/SingleOrg/description": "Authentification organisationnelle pour un seul locataire", + "symbols/auth/description": "Type d’authentification à utiliser", + "symbols/Authority/description": "Autorité du fournisseur OIDC (à utiliser avec l’authentification individuelle autonome).", + "symbols/AAdB2CInstance/description": "Instance Azure Active Directory B2C à laquelle se connecter (à utiliser avec l’authentification IndividualB2C).", + "symbols/SignUpSignInPolicyId/description": "ID de stratégie de connexion et d’inscription pour ce projet (à utiliser avec l’authentification IndividualB2C).", + "symbols/AADInstance/description": "Instance Azure Active Directory à laquelle se connecter (à utiliser avec l’authentification SingleOrg).", + "symbols/ClientId/description": "ID client de ce projet (utilisé avec IndividualB2C, SingleOrg ou l’authentification individuelle dans les scénarios autonomes).", + "symbols/Domain/description": "Domaine du locataire d’annuaire (utilisé avec l’authentification SingleOrg ou IndividualB2C).", + "symbols/AppIDUri/description": "URI d’ID d’application pour l’API serveur que nous voulons appeler (à utiliser avec l’authentification SingleOrg ou IndividualB2C).", + "symbols/APIClientId/description": "ID client de l’API hébergée par le serveur (à utiliser avec IndividualB2C, SingleOrg).", + "symbols/DefaultScope/description": "Étendue d’API dont le client a besoin pour configurer un jeton d’accès. (à utiliser avec IndividualB2C, SingleOrg).", + "symbols/TenantId/description": "ID TenantId du répertoire auquel se connecter (à utiliser avec l’authentification SingleOrg).", + "symbols/OrgReadAccess/description": "Indique si cette application doit ou non autoriser l’accès en lecture au répertoire (s’applique uniquement à SingleOrg).", + "symbols/UserSecretsId/description": "ID à utiliser pour les secrets (à utiliser avec OrgReadAccess ou l’authentification individuelle).", + "symbols/ExcludeLaunchSettings/description": "Indique s’il faut exclure launchSettings.json du modèle généré.", + "symbols/kestrelHttpPort/description": "Numéro de port à utiliser pour le point de terminaison HTTP dans launchSettings.json.", + "symbols/kestrelHttpsPort/description": "Numéro de port à utiliser pour le point de terminaison HTTPS dans launchSettings.json. Cette option s’applique uniquement lorsque le paramètre no-https n’est pas utilisé (no-https sera ignoré si IndividualAuth ou OrganizationalAuth est utilisé).", + "symbols/iisHttpPort/description": "Numéro de port à utiliser pour le point de terminaison HTTP IIS Express dans launchSettings.json.", + "symbols/iisHttpsPort/description": "Numéro de port à utiliser pour le point de terminaison HTTPS IIS Express dans launchSettings.json. Cette option s’applique uniquement lorsque le paramètre no-https n’est pas utilisé (no-https sera ignoré si IndividualAuth ou OrganizationalAuth est utilisé).", + "symbols/PWA/displayName": "_Application web progressive", + "symbols/PWA/description": "Si ce paramètre est spécifié, produit une application web progressive (PWA) qui prend en charge l’installation et l’utilisation hors connexion.", + "symbols/IncludeSampleContent/displayName": "_Inclure des exemples de pages", + "symbols/IncludeSampleContent/description": "Configure s'il faut ajouter des exemples de pages et de style pour illustrer les modèles d'utilisation de base.", + "symbols/Empty/description": "Configure s'il faut omettre les exemples de pages et le style qui illustrent les modèles d'utilisation de base.", + "symbols/NoHttps/description": "Indique s’il faut désactiver HTTPS. Cette option s’applique uniquement si Individual, IndividualB2C, SingleOrg ou MultiOrg ne sont pas utilisés pour --auth.", + "symbols/UseLocalDB/description": "Indique s’il faut utiliser localDB au lieu de SQLite. Cette option s’applique uniquement si --auth Individual ou --auth IndividualB2C est spécifié.", + "symbols/CalledApiUrl/description": "URL de l’API à appeler à partir de l’application web. Cette option s’applique uniquement si --auth SingleOrg, --auth MultiOrg ou --auth IndividualB2C sans et ASP.NET’hôte principal est spécifié.", + "symbols/CalledApiScopes/description": "Étendues où demander l’appel de l’API à partir de l’application web. Cette option s’applique uniquement si l’hôte --auth SingleOrg, --auth MultiOrg ou --auth IndividualB2C sans et ASP.NET Core est spécifié.", + "symbols/UseProgramMain/displayName": "N’utilisez pas _d’instructions de niveau supérieur.", + "symbols/UseProgramMain/description": "Indique s’il faut générer une classe Programme explicite et une méthode Main au lieu d’instructions de niveau supérieur.", + "postActions/restore/description": "Restaurez les packages NuGet requis par ce projet.", + "postActions/restore/manualInstructions/default/text": "Exécuter « dotnet restore »", + "postActions/open-file/description": "Ouvre Readme.txt dans l’éditeur" +} \ No newline at end of file diff --git a/src/Templates/templates/componentswebassembly-csharp-10/.template.config/localize/templatestrings.it.json b/src/Templates/templates/componentswebassembly-csharp-10/.template.config/localize/templatestrings.it.json new file mode 100644 index 0000000000..16f2fe75d3 --- /dev/null +++ b/src/Templates/templates/componentswebassembly-csharp-10/.template.config/localize/templatestrings.it.json @@ -0,0 +1,44 @@ +{ + "author": "Microsoft", + "name": "App autonoma Fluent Blazor WebAssembly", + "description": "Modello di progetto per la creazione di un'app Blazor eseguita in WebAssembly. Questo modello può essere usato per app Web con interfacce utente dinamiche.", + "symbols/Framework/description": "Il framework di destinazione per il progetto.", + "symbols/Framework/choices/net10.0/description": "Destinazione net10.0", + "symbols/skipRestore/description": "Se specificato, ignora il ripristino automatico del progetto durante la creazione.", + "symbols/auth/choices/None/description": "Nessuna autenticazione", + "symbols/auth/choices/Individual/description": "Autenticazione singola", + "symbols/auth/choices/IndividualB2C/description": "Autenticazione singola con Azure AD B2C", + "symbols/auth/choices/SingleOrg/description": "Autenticazione organizzativa per un singolo tenant", + "symbols/auth/description": "Tipo di autenticazione da usare.", + "symbols/Authority/description": "Autorità del provider OIDC (usare con l'autenticazione individuale autonoma).", + "symbols/AAdB2CInstance/description": "Istanza di Azure Active Directory B2C a cui connettersi (usare con l'autenticazione IndividualB2C).", + "symbols/SignUpSignInPolicyId/description": "ID dei criteri di accesso e iscrizione per questo progetto (usare con l'autenticazione IndividualB2C).", + "symbols/AADInstance/description": "Istanza di Azure Active Directory a cui connettersi (usare con l'autenticazione SingleOrg).", + "symbols/ClientId/description": "ID client per questo progetto (usare con IndividualB2C, SingleOrg o Autenticazione individuale in scenari autonomi).", + "symbols/Domain/description": "Il dominio per il tenant della directory (da usare con l'autenticazione SingleOrg o IndividualB2C).", + "symbols/AppIDUri/description": "URI ID app per l'API server che si vuole chiamare (usare con l'autenticazione SingleOrg o IndividualB2C).", + "symbols/APIClientId/description": "ID client per l'API ospitata dal server (usare con IndividualB2C, SingleOrg).", + "symbols/DefaultScope/description": "Ambito API che il client deve richiedere per effettuare il provisioning di un token di accesso (usare con IndividualB2C, SingleOrg).", + "symbols/TenantId/description": "ID TenantId della directory a cui connettersi (usare con l'autenticazione SingleOrg).", + "symbols/OrgReadAccess/description": "Indica se consentire o meno a questa applicazione l'accesso in lettura alla directory (si applica solo a SingleOrg).", + "symbols/UserSecretsId/description": "ID da usare per i segreti (usare con OrgReadAccess o l'autenticazione singola).", + "symbols/ExcludeLaunchSettings/description": "Indica se escludere launchSettings.json dal modello generato.", + "symbols/kestrelHttpPort/description": "Numero di porta da usare per l'endpoint HTTP in launchSettings.json.", + "symbols/kestrelHttpsPort/description": "Numero di porta da usare per l'endpoint HTTPS in launchSettings.json. Questa opzione è applicabile solo quando il parametro no-https non viene usato (no-https verrà ignorato se si usa IndividualAuth o OrganizationalAuth).", + "symbols/iisHttpPort/description": "Numero di porta da usare per l'endpoint HTTP IIS Express in launchSettings.json.", + "symbols/iisHttpsPort/description": "Numero di porta da usare per l'endpoint HTTPS IIS Express in launchSettings.json. Questa opzione è applicabile solo quando il parametro no-https non viene usato (no-https verrà ignorato se si usa IndividualAuth o OrganizationalAuth).", + "symbols/PWA/displayName": "Applicazione Web _Progressive", + "symbols/PWA/description": "Se specificato, produce un'applicazione Web progressiva (PWA) che supporta l'installazione e l'uso offline.", + "symbols/IncludeSampleContent/displayName": "_Include pagine di esempio", + "symbols/IncludeSampleContent/description": "Consente di configurare se aggiungere pagine di esempio e stile per mostrare modelli di utilizzo di base.", + "symbols/Empty/description": "Consente di configurare se omettere pagine di esempio e stile che mostrano modelli di utilizzo di base.", + "symbols/NoHttps/description": "Indica se disattivare HTTPS. Questa opzione si applica solo se Individual, IndividualB2C, SingleOrg o MultiOrg non vengono usati per --auth.", + "symbols/UseLocalDB/description": "Indica se usare LocalDB invece di SQLite. Questa opzione si applica solo se è specificato --auth Individual o --auth IndividualB2C.", + "symbols/CalledApiUrl/description": "URL dell'API da chiamare dall'app Web. Questa opzione si applica solo se è specificato --auth SingleOrg, --auth MultiOrg o --auth IndividualB2C without e host ASP.NET Core.", + "symbols/CalledApiScopes/description": "Ambiti per richiedere di chiamare l'API dall'app Web. Questa opzione si applica solo se è specificato --auth SingleOrg, --auth MultiOrg o --auth IndividualB2C without e host ASP.NET Core.", + "symbols/UseProgramMain/displayName": "Non usare_istruzioni di primo livello", + "symbols/UseProgramMain/description": "Indica se generare una classe Program esplicita e un metodo Main anziché istruzioni di primo livello.", + "postActions/restore/description": "Ripristina i pacchetti NuGet richiesti da questo progetto.", + "postActions/restore/manualInstructions/default/text": "Esegui 'dotnet restore'", + "postActions/open-file/description": "Apre Readme.txt nell'editor" +} \ No newline at end of file diff --git a/src/Templates/templates/componentswebassembly-csharp-10/.template.config/localize/templatestrings.ja.json b/src/Templates/templates/componentswebassembly-csharp-10/.template.config/localize/templatestrings.ja.json new file mode 100644 index 0000000000..b55f91d07a --- /dev/null +++ b/src/Templates/templates/componentswebassembly-csharp-10/.template.config/localize/templatestrings.ja.json @@ -0,0 +1,44 @@ +{ + "author": "Microsoft", + "name": "Fluent Blazor WebAssembly スタンドアロン アプリ", + "description": "WebAssembly で実行される Blazor アプリを作成するためのプロジェクト テンプレート。このテンプレートは、高度でダイナミックなユーザー インターフェイス (UI) を備えた Web アプリに使用できます。", + "symbols/Framework/description": "プロジェクトのターゲット フレームワークです。", + "symbols/Framework/choices/net10.0/description": "ターゲット net10.0", + "symbols/skipRestore/description": "指定した場合、作成時にプロジェクトの自動復元がスキップされます。", + "symbols/auth/choices/None/description": "認証なし", + "symbols/auth/choices/Individual/description": "個別の認証", + "symbols/auth/choices/IndividualB2C/description": "Azure AD B2C を使用した個別の認証", + "symbols/auth/choices/SingleOrg/description": "単一テナントの組織認証", + "symbols/auth/description": "使用する認証の種類", + "symbols/Authority/description": "OIDC プロバイダーの権限 (個別のスタンドアロン認証で使用)。", + "symbols/AAdB2CInstance/description": "接続先の Azure Active Directory B2C インスタンス (IndividualB2C 認証で使用)。", + "symbols/SignUpSignInPolicyId/description": "このプロジェクトのサインインおよびサインアップのポリシー ID (IndividualB2C 認証で使用)。", + "symbols/AADInstance/description": "接続先の Azure Active Directory インスタンス (SingleOrg 認証で使用)。", + "symbols/ClientId/description": "このプロジェクトのクライアント ID (スタンドアロン シナリオでは、IndividualB2C、SingleOrg、または個別の認証で使用します)。", + "symbols/Domain/description": "ディレクトリ テナントのドメイン (SingleOrg または IndividualB2C 認証で使用)。", + "symbols/AppIDUri/description": "呼び出すサーバー API のアプリ ID URI (SingleOrg または IndividualB2C 認証で使用)。", + "symbols/APIClientId/description": "サーバーがホストする API のクライアント ID (IndividualB2C、SingleOrg で使用)。", + "symbols/DefaultScope/description": "アクセス トークンのプロビジョニングするためにクライアントが要求する必要がある API スコープ。(IndividualB2C、SingleOrg で使用)。", + "symbols/TenantId/description": "接続先のディレクトリの TenantId ID (SingleOrg 認証で使用)。", + "symbols/OrgReadAccess/description": "このアプリケーションにディレクトリへの読み取りアクセスを許可するかどうか (SingleOrg にのみ適用されます)。", + "symbols/UserSecretsId/description": "シークレットで使用する ID (OrgReadAccess または個別の認証で使用)。", + "symbols/ExcludeLaunchSettings/description": "生成されたテンプレートから launchSettings.json を除外するかどうか。", + "symbols/kestrelHttpPort/description": "launchSettings.json の HTTP エンドポイントに使用するポート番号。", + "symbols/kestrelHttpsPort/description": "launchSettings.json で HTTPS エンドポイントに使用するポート番号。このオプションは、HTTPS 以外のパラメーターが使用されていない場合にのみ適用されます (IndividualAuth または OrganizationalAuth が使用されている場合は、HTTPS 以外は無視されます)。", + "symbols/iisHttpPort/description": "launchSettings.json の IIS Express HTTP エンドポイントに使用するポート番号。", + "symbols/iisHttpsPort/description": "launchSettings.json で IIS Express HTTPS エンドポイントに使用するポート番号。このオプションは、HTTPS 以外のパラメーターが使用されていない場合にのみ適用されます (IndividualAuth または OrganizationalAuth が使用されている場合は、HTTPS 以外は無視されます)。", + "symbols/PWA/displayName": "プログレッシブ Web アプリケーション(_P)", + "symbols/PWA/description": "指定した場合、インストールとオフラインでの使用をサポートするプログレッシブ Web アプリケーション (PWA) が生成されます。", + "symbols/IncludeSampleContent/displayName": "サンプル ページを含める(_I)", + "symbols/IncludeSampleContent/description": "基本的な使用パターンを示すサンプル ページとスタイルを追加するかどうかを構成します。", + "symbols/Empty/description": "基本的な使用パターンを示すサンプル ページとスタイルを省略するかどうかを構成します。", + "symbols/NoHttps/description": "HTTPS をオフにするかどうか。このオプションは、Individual、IndividualB2C、SingleOrg、または MultiOrg が --auth に使用されていない場合にのみ適用されます。", + "symbols/UseLocalDB/description": "SQLite の代わりに LocalDB を使用するかどうか。このオプションは、--auth Individual または --auth IndividualB2C が指定されている場合にのみ適用されます。", + "symbols/CalledApiUrl/description": "Web アプリから呼び出す API の URL。このオプションは、--auth SingleOrg、--auth MultiOrg、または --auth IndividualB2C で、ASP.NET Core ホストが指定されていない場合にのみ適用されます。", + "symbols/CalledApiScopes/description": "Web アプリから API を呼び出すために要求するスコープ。このオプションは、--auth SingleOrg、--auth MultiOrg、または --auth IndividualB2C で、ASP.NET Core ホストが指定されていない場合にのみ適用されます。", + "symbols/UseProgramMain/displayName": "最上位レベルのステートメントを使用しない(_T)", + "symbols/UseProgramMain/description": "最上位レベルのステートメントではなく、明示的な Program クラスと Main メソッドを生成するかどうか。", + "postActions/restore/description": "このプロジェクトに必要な NuGet パッケージを復元します。", + "postActions/restore/manualInstructions/default/text": "'dotnet restore' を実行する", + "postActions/open-file/description": "エディターで Readme.txt を開きます" +} \ No newline at end of file diff --git a/src/Templates/templates/componentswebassembly-csharp-10/.template.config/localize/templatestrings.ko.json b/src/Templates/templates/componentswebassembly-csharp-10/.template.config/localize/templatestrings.ko.json new file mode 100644 index 0000000000..885e5e8e99 --- /dev/null +++ b/src/Templates/templates/componentswebassembly-csharp-10/.template.config/localize/templatestrings.ko.json @@ -0,0 +1,44 @@ +{ + "author": "Microsoft", + "name": "Fluent Blazor WebAssembly 독립 실행형 앱", + "description": "WebAssembly에서 실행되는 Blazor 앱을 만드는 데 사용되는 프로젝트 템플릿입니다. 이 템플릿을 사용하여 다양한 동적 UI(사용자 인터페이스)가 포함된 웹앱을 만들 수 있습니다.", + "symbols/Framework/description": "프로젝트에 대한 대상 프레임워크입니다.", + "symbols/Framework/choices/net10.0/description": "대상 net10.0", + "symbols/skipRestore/description": "지정된 경우, 프로젝트 생성 시 자동 복원을 건너뜁니다.", + "symbols/auth/choices/None/description": "인증 없음", + "symbols/auth/choices/Individual/description": "개별 인증", + "symbols/auth/choices/IndividualB2C/description": "Azure AD B2C를 사용한 개별 인증", + "symbols/auth/choices/SingleOrg/description": "단일 테넌트에 대한 조직 인증", + "symbols/auth/description": "사용할 인증 유형", + "symbols/Authority/description": "OIDC 제공자의 권한(독립형 개별 인증과 함께 사용).", + "symbols/AAdB2CInstance/description": "연결할 Azure Active Directory B2C 인스턴스(IndividualB2C 인증과 함께 사용).", + "symbols/SignUpSignInPolicyId/description": "이 프로젝트의 로그인 및 등록 정책 ID입니다(IndividualB2C 인증과 함께 사용).", + "symbols/AADInstance/description": "연결할 Azure Active Directory 인스턴스입니다(SingleOrg 인증과 함께 사용).", + "symbols/ClientId/description": "이 프로젝트의 클라이언트 ID입니다(독립형 시나리오에서 IndividualB2C, SingleOrg 또는 Individual auth와 함께 사용).", + "symbols/Domain/description": "디렉터리 테넌트의 도메인입니다(SingleOrg 또는 IndividualB2C 인증과 함께 사용).", + "symbols/AppIDUri/description": "호출하려는 서버 API의 앱 ID Uri입니다(SingleOrg 또는 IndividualB2C 인증과 함께 사용).", + "symbols/APIClientId/description": "서버가 호스팅하는 API의 클라이언트 ID입니다(IndividualB2C, SingleOrg와 함께 사용).", + "symbols/DefaultScope/description": "클라이언트가 액세스 토큰을 프로비전하기 위해 요청해야 하는 API 범위입니다. (IndividualB2C, SingleOrg와 함께 사용).", + "symbols/TenantId/description": "연결할 디렉터리의 TenantId ID입니다(SingleOrg 인증과 함께 사용).", + "symbols/OrgReadAccess/description": "이 애플리케이션이 디렉터리에 대한 읽기 액세스를 허용할지 여부(SingleOrg에만 적용됨).", + "symbols/UserSecretsId/description": "비밀에 사용할 ID입니다(OrgReadAccess 또는 개별 인증과 함께 사용).", + "symbols/ExcludeLaunchSettings/description": "생성된 템플릿에서 launchSettings.json을 제외할지 여부입니다.", + "symbols/kestrelHttpPort/description": "launchSettings.json의 HTTP 엔드포인트에 사용할 포트 번호입니다.", + "symbols/kestrelHttpsPort/description": "launchSettings.json의 HTTPS 엔드포인트에 사용할 포트 번호입니다. 이 옵션은 매개 변수 no-https가 사용되지 않은 경우에만 적용됩니다(IndividualAuth 또는 OrganizationalAuth가 사용되는 경우 no-https는 무시됨).", + "symbols/iisHttpPort/description": "launchSettings.json의 IIS Express HTTP 엔드포인트에 사용할 포트 번호입니다.", + "symbols/iisHttpsPort/description": "launchSettings.json의 IIS Express 엔드포인트에 사용할 포트 번호입니다. 이 옵션은 매개 변수 no-https가 사용되지 않은 경우에만 적용됩니다(IndividualAuth 또는 OrganizationalAuth가 사용되는 경우 no-https는 무시됨).", + "symbols/PWA/displayName": "프로그레시브 웹 애플리케이션(_P)", + "symbols/PWA/description": "지정된 경우 설치 및 오프라인 사용을 지원하는 PWA(프로그레시브 웹 응용 프로그램)를 생성합니다.", + "symbols/IncludeSampleContent/displayName": "샘플 페이지 포함(_I)", + "symbols/IncludeSampleContent/description": "기본 사용 패턴을 보여주기 위해 샘플 페이지 및 스타일을 추가할지 여부를 구성합니다.", + "symbols/Empty/description": "기본 사용 패턴을 보여주는 샘플 페이지 및 스타일을 생략할지 여부를 구성합니다.", + "symbols/NoHttps/description": "HTTPS를 끌지 여부입니다. 이 옵션은 Individual, IndividualB2C, SingleOrg 또는 MultiOrg가 --auth에 사용되지 않는 경우에만 적용됩니다.", + "symbols/UseLocalDB/description": "SQLite 대신 LocalDB를 사용할지 여부입니다. 이 옵션은 --auth Individual 또는 --auth IndividualB2C가 지정된 경우에만 적용됩니다.", + "symbols/CalledApiUrl/description": "웹앱에서 호출할 API의 URL입니다. 이 옵션은 --auth SingleOrg, --auth MultiOrg 또는 --auth IndividualB2C without and ASP.NET Core 호스트가 지정된 경우에만 적용됩니다.", + "symbols/CalledApiScopes/description": "웹앱에서 API 호출을 요청할 범위입니다. 이 옵션은 --auth SingleOrg, --auth MultiOrg 또는 --auth IndividualB2C without and ASP.NET Core 호스트가 지정된 경우에만 적용됩니다.", + "symbols/UseProgramMain/displayName": "최상위 문 사용 안 함(_T)", + "symbols/UseProgramMain/description": "최상위 문 대신 명시적 Program 클래스 및 Main 메서드를 생성할지 여부입니다.", + "postActions/restore/description": "이 프로젝트에 필요한 NuGet 패키지를 복원합니다.", + "postActions/restore/manualInstructions/default/text": "'dotnet restore' 실행", + "postActions/open-file/description": "편집기에서 Readme.txt를 엽니다." +} \ No newline at end of file diff --git a/src/Templates/templates/componentswebassembly-csharp-10/.template.config/localize/templatestrings.pl.json b/src/Templates/templates/componentswebassembly-csharp-10/.template.config/localize/templatestrings.pl.json new file mode 100644 index 0000000000..950865f59a --- /dev/null +++ b/src/Templates/templates/componentswebassembly-csharp-10/.template.config/localize/templatestrings.pl.json @@ -0,0 +1,44 @@ +{ + "author": "Microsoft", + "name": "Fluent Autonomiczna aplikacja zestawu WebAssembly platformy Blazor", + "description": "Szablon projektu służący do tworzenia aplikacji Blazor działającej na zestawie WebAssembly. Ten szablon może być używany dla aplikacji internetowych z rozbudowanymi, dynamicznymi interfejsami użytkowników.", + "symbols/Framework/description": "Platforma docelowa dla tego projektu.", + "symbols/Framework/choices/net10.0/description": "Docelowa platforma net10.0", + "symbols/skipRestore/description": "Jeśli ta opcja jest określona, pomija automatyczne przywracanie projektu podczas tworzenia.", + "symbols/auth/choices/None/description": "Bez uwierzytelniania", + "symbols/auth/choices/Individual/description": "Uwierzytelnianie indywidualne", + "symbols/auth/choices/IndividualB2C/description": "Indywidualne uwierzytelnianie za pomocą usługi Azure AD B2C", + "symbols/auth/choices/SingleOrg/description": "Uwierzytelnianie organizacyjne dla pojedynczej dzierżawy", + "symbols/auth/description": "Typ uwierzytelniania, który ma zostać użyty.", + "symbols/Authority/description": "Urząd dostawcy OIDC (użyj z autonomicznym indywidualnym uwierzytelnianym).", + "symbols/AAdB2CInstance/description": "Wystąpienie usługi Azure Active Directory B2C do nawiązania połączenia (użyj z uwierzytelnianym IndividualB2C).", + "symbols/SignUpSignInPolicyId/description": "Identyfikator zasad logowania i rejestracji dla tego projektu (użyj uwierzytelniania IndividualB2C).", + "symbols/AADInstance/description": "Wystąpienie usługi Azure Active Directory do nawiązania połączenia (użyj z uwierzytelnianym SingleOrg ).", + "symbols/ClientId/description": "Identyfikator klienta dla tego projektu (używany z uwierzytelnianiem IndividualB2C, SingleOrg lub Individual w scenariuszach autonomicznych).", + "symbols/Domain/description": "Domena dzierżawy katalogu (użyj uwierzytelniania SingleOrg lub IndividualB2C).", + "symbols/AppIDUri/description": "Identyfikator URI identyfikatora aplikacji dla interfejsu API serwera, który chcemy wywołać (użyj z uwierzytelnianym SingleOrg lub IndividualB2C).", + "symbols/APIClientId/description": "Identyfikator klienta dla interfejsu API hostowanego przez serwer (używany z elementem IndividualB2C, SingleOrg).", + "symbols/DefaultScope/description": "Zakres interfejsu API, który klient musi zażądać w celu aprowizacji tokenu dostępu. (użyj z uwierzytelnianiem singleB2C, SingleOrg).", + "symbols/TenantId/description": "Identyfikator TenantId katalogu, z którym ma zostać nawiązane połączenie (użyj uwierzytelniania SingleOrg).", + "symbols/OrgReadAccess/description": "Określa, czy zezwolić tej aplikacji na dostęp do odczytu do katalogu (dotyczy tylko uwierzytelniania SingleOrg).", + "symbols/UserSecretsId/description": "Identyfikator do użycia na potrzeby wpisów tajnych (używany z funkcją OrgReadAccess lub indywidualnym uwierzytelnianiem).", + "symbols/ExcludeLaunchSettings/description": "Określa, czy wykluczyć plik launchSettings.json z wygenerowanego szablonu.", + "symbols/kestrelHttpPort/description": "Numer portu do użycia dla punktu końcowego HTTP w pliku launchSettings.json.", + "symbols/kestrelHttpsPort/description": "Numer portu do użycia dla punktu końcowego HTTPS w pliku launchSettings.json. Ta opcja ma zastosowanie tylko wtedy, gdy parametr no-https nie jest używany (parametr no-https zostanie zignorowany, jeśli zostanie użyte uwierzytelnianie IndividualAuth lub OrganizationalAuth).", + "symbols/iisHttpPort/description": "Numer portu do użycia dla punktu końcowego HTTP usług IIS Express w pliku launchSettings.json.", + "symbols/iisHttpsPort/description": "Numer portu do użycia dla punktu końcowego HTTPS usług IIS Express w pliku launchSettings.json. Ta opcja ma zastosowanie tylko wtedy, gdy nie jest używany parametr no-https (jeśli zostanie użyte uwierzytelnianie IndividualAuth lub OrganizationalAuth, parametr no-https zostanie zignorowana).", + "symbols/PWA/displayName": "_Progresywna aplikacja internetowa", + "symbols/PWA/description": "Jeśli zostanie określony, tworzy progresywną aplikację internetową (PWA) obsługującą instalację i używanie w trybie offline.", + "symbols/IncludeSampleContent/displayName": "_Dołącz przykładowe strony", + "symbols/IncludeSampleContent/description": "Konfiguruje, czy dodać przykładowe strony i style w celu zademonstrowania podstawowych wzorców użycia.", + "symbols/Empty/description": "Konfiguruje, czy pomijać przykładowe strony i style demonstrujące podstawowe wzorce użycia.", + "symbols/NoHttps/description": "Określa, czy wyłączyć protokół HTTPS. Ta opcja ma zastosowanie tylko wtedy, gdy dla uwierzytelniania --auth nie są używane elementy Individual, IndividualB2C, SingleOrg lub MultiOrg.", + "symbols/UseLocalDB/description": "Określa, czy używać bazy danych LocalDB zamiast oprogramowania SQLite. Ta opcja ma zastosowanie tylko wtedy, gdy określono uwierzytelnianie --auth Individual lub --auth IndividualB2C.", + "symbols/CalledApiUrl/description": "Adres URL interfejsu API do wywołania z aplikacji internetowej. Ta opcja ma zastosowanie tylko wtedy, gdy określono uwierzytelnianie --auth SingleOrg, --auth MultiOrg lub --auth IndividualB2C bez hosta platformy ASP.NET Core.", + "symbols/CalledApiScopes/description": "Zakresy do żądania wywołania interfejsu API z aplikacji internetowej. Ta opcja ma zastosowanie tylko wtedy, gdy określono uwierzytelnianie --auth SingleOrg, --auth MultiOrg lub --auth IndividualB2C bez hosta platformy ASP.NET Core.", + "symbols/UseProgramMain/displayName": "Nie używaj ins_trukcji najwyższego poziomu", + "symbols/UseProgramMain/description": "Określa, czy wygenerować jawną klasę Program i metodę Main zamiast instrukcji najwyższego poziomu.", + "postActions/restore/description": "Przywróć pakiety NuGet wymagane przez ten projekt.", + "postActions/restore/manualInstructions/default/text": "Uruchom polecenie \"dotnet restore\"", + "postActions/open-file/description": "Otwiera plik Readme.txt w edytorze" +} \ No newline at end of file diff --git a/src/Templates/templates/componentswebassembly-csharp-10/.template.config/localize/templatestrings.pt-BR.json b/src/Templates/templates/componentswebassembly-csharp-10/.template.config/localize/templatestrings.pt-BR.json new file mode 100644 index 0000000000..ad46c62f9d --- /dev/null +++ b/src/Templates/templates/componentswebassembly-csharp-10/.template.config/localize/templatestrings.pt-BR.json @@ -0,0 +1,44 @@ +{ + "author": "Microsoft", + "name": "Aplicativo Autônomo Fluent Blazor WebAssembly", + "description": "Um modelo de projeto para criar um aplicativo Blazor que é executado no WebAssembly. Esse modelo pode ser usado para aplicativos Web com UIs (interfaces do usuário) completas e dinâmicas.", + "symbols/Framework/description": "A estrutura de destino do projeto.", + "symbols/Framework/choices/net10.0/description": "Net10.0 de destino", + "symbols/skipRestore/description": "Se especificado, ignora a restauração automática do projeto sendo criado.", + "symbols/auth/choices/None/description": "Sem autenticação", + "symbols/auth/choices/Individual/description": "Autenticação individual", + "symbols/auth/choices/IndividualB2C/description": "Autenticação individual com Azure AD B2C", + "symbols/auth/choices/SingleOrg/description": "Autenticação organizacional para um único locatário", + "symbols/auth/description": "O tipo de autenticação a ser usado", + "symbols/Authority/description": "A autoridade do provedor OIDC (use com autenticação individual autônoma).", + "symbols/AAdB2CInstance/description": "A instância do Azure Active Directory B2C à qual se conectar (use com autenticação IndividualB2C).", + "symbols/SignUpSignInPolicyId/description": "O ID da política de login e inscrição para este projeto (use com autenticação IndividualB2C).", + "symbols/AADInstance/description": "A instância do Azure Active Directory à qual se conectar (use com autenticação SingleOrg).", + "symbols/ClientId/description": "O ID do cliente para este projeto (use com autenticação IndividualB2C, SingleOrg ou Individual em cenários autônomos).", + "symbols/Domain/description": "O domínio para o locatário do diretório (use com autenticação SingleOrg ou IndividualB2C).", + "symbols/AppIDUri/description": "O App ID Uri para a API do servidor que queremos chamar (use com autenticação SingleOrg ou IndividualB2C).", + "symbols/APIClientId/description": "O ID do cliente para a API que o servidor hospeda (use com IndividualB2C, SingleOrg).", + "symbols/DefaultScope/description": "O escopo da API que o cliente precisa solicitar para provisionar um token de acesso. (use com IndividualB2C, SingleOrg).", + "symbols/TenantId/description": "O ID TenantId do diretório ao qual se conectar (use com autenticação SingleOrg).", + "symbols/OrgReadAccess/description": "Se deve ou não permitir que este aplicativo tenha acesso de leitura ao diretório (aplica-se apenas a SingleOrg).", + "symbols/UserSecretsId/description": "O ID a ser usado para segredos (use com OrgReadAccess ou Autenticação individual).", + "symbols/ExcludeLaunchSettings/description": "Se deve excluir launchSettings.json do modelo gerado.", + "symbols/kestrelHttpPort/description": "Número da porta a ser usada para o ponto de extremidade HTTP em launchSettings.json.", + "symbols/kestrelHttpsPort/description": "Número da porta a ser usada para o ponto de extremidade HTTPS em launchSettings.json. Essa opção só é aplicável quando o parâmetro no-https não é usado (no-https será ignorado se IndividualAuth ou OrganizationalAuth for usado).", + "symbols/iisHttpPort/description": "Número da porta a ser usada para o ponto de extremidade HTTP do IIS Express em launchSettings.json.", + "symbols/iisHttpsPort/description": "Número da porta a ser usada para o ponto de extremidade HTTPS do IIS Express em launchSettings.json. Essa opção só é aplicável quando o parâmetro no-https não é usado (no-https será ignorado se IndividualAuth ou OrganizationalAuth for usado).", + "symbols/PWA/displayName": "_Aplicativo da Web Progressivo", + "symbols/PWA/description": "Se especificado, produz um Progressive Web Application (PWA) com suporte para instalação e uso offline.", + "symbols/IncludeSampleContent/displayName": "_Incluir páginas de amostra", + "symbols/IncludeSampleContent/description": "Configura se deseja adicionar páginas de amostra e estilo para demonstrar padrões de uso básicos.", + "symbols/Empty/description": "Configura a omissão de páginas de amostra e estilo que demonstram padrões básicos de uso.", + "symbols/NoHttps/description": "Se o HTTPS deve ser desativado. Essa opção se aplica somente se Individual, IndividualB2C, SingleOrg ou MultiOrg não forem usados para --auth.", + "symbols/UseLocalDB/description": "Se deve usar LocalDB em vez de SQLite. Esta opção só se aplica se --auth Individual ou --auth IndividualB2C for especificado.", + "symbols/CalledApiUrl/description": "URL da API para chamar do aplicativo Web. Essa opção só se aplica se --auth SingleOrg, --auth MultiOrg ou --auth IndividualB2C sem um host ASP.NET Core for especificado.", + "symbols/CalledApiScopes/description": "Escopos a serem solicitados para chamar a API do aplicativo Web. Essa opção só se aplica se --auth SingleOrg, --auth MultiOrg ou --auth IndividualB2C sem um host ASP.NET Core for especificado.", + "symbols/UseProgramMain/displayName": "Não use ins_truções de nível superior", + "symbols/UseProgramMain/description": "Se deve gerar uma classe de Programa explícita e um método principal em vez de instruções de nível superior.", + "postActions/restore/description": "Restaure os pacotes NuGet exigidos por este projeto.", + "postActions/restore/manualInstructions/default/text": "Executar 'dotnet restore'", + "postActions/open-file/description": "Abre Readme.txt no editor" +} \ No newline at end of file diff --git a/src/Templates/templates/componentswebassembly-csharp-10/.template.config/localize/templatestrings.ru.json b/src/Templates/templates/componentswebassembly-csharp-10/.template.config/localize/templatestrings.ru.json new file mode 100644 index 0000000000..55e1762837 --- /dev/null +++ b/src/Templates/templates/componentswebassembly-csharp-10/.template.config/localize/templatestrings.ru.json @@ -0,0 +1,44 @@ +{ + "author": "Майкрософт", + "name": "Изолированное приложение Fluent Blazor WebAssembly", + "description": "Шаблон проекта для создания приложения Blazor, которое запускается в WebAssembly. Этот шаблон можно использовать для веб-приложений с полнофункциональными динамическими пользовательскими интерфейсами.", + "symbols/Framework/description": "Целевая платформа для проекта.", + "symbols/Framework/choices/net10.0/description": "Целевая платформа .NET 10.0", + "symbols/skipRestore/description": "Если установлено, автоматическое восстановление проекта при создании пропускается.", + "symbols/auth/choices/None/description": "Без проверки подлинности", + "symbols/auth/choices/Individual/description": "Индивидуальная проверка подлинности", + "symbols/auth/choices/IndividualB2C/description": "Индивидуальная проверка подлинности в Azure AD B2C", + "symbols/auth/choices/SingleOrg/description": "Проверка подлинности в организации для одного клиента", + "symbols/auth/description": "Тип используемой проверки подлинности", + "symbols/Authority/description": "Полномочия поставщика OIDC (используются с автономной проверкой подлинности Individual).", + "symbols/AAdB2CInstance/description": "Экземпляр Azure Active Directory B2C, к которому нужно подключиться (используется с проверкой подлинности IndividualB2C).", + "symbols/SignUpSignInPolicyId/description": "Идентификатор политики входа и регистрации для этого проекта (используется с проверкой подлинности IndividualB2C).", + "symbols/AADInstance/description": "Экземпляр Azure Active Directory, к которому нужно подключиться (используется с проверкой подлинности SingleOrg).", + "symbols/ClientId/description": "Идентификатор клиента для этого проекта (используется с проверкой подлинности IndividualB2C, SingleOrg или Individual в автономных сценариях).", + "symbols/Domain/description": "Домен для клиента каталога (используется с проверкой подлинности SingleOrg или IndividualB2C).", + "symbols/AppIDUri/description": "URI идентификатора приложения для API сервера, который требуется вызвать (используется с проверкой подлинности SingleOrg или IndividualB2C).", + "symbols/APIClientId/description": "Идентификатор клиента для API, размещенного на сервере (используется с IndividualB2C, SingleOrg).", + "symbols/DefaultScope/description": "Область API, необходимая клиенту для запроса на подготовку маркера доступа (используется с IndividualB2C, SingleOrg).", + "symbols/TenantId/description": "Идентификатор TenantId каталога, к которому нужно подключиться (используется с проверкой подлинности SingleOrg).", + "symbols/OrgReadAccess/description": "Следует ли предоставлять этому приложению доступ на чтение к каталогу (применяется только к SingleOrg).", + "symbols/UserSecretsId/description": "Идентификатор, используемый для секретов (используется с проверкой подлинности OrgReadAccess или Individual).", + "symbols/ExcludeLaunchSettings/description": "Следует ли исключить launchSettings.json из созданного шаблона.", + "symbols/kestrelHttpPort/description": "Номер порта, используемый для конечной точки HTTP в launchSettings.json.", + "symbols/kestrelHttpsPort/description": "Номер порта, используемый для конечной точки HTTPS в launchSettings.json. Этот параметр применим только в том случае, если no-https не используется (при использовании IndividualAuth или OrganizationalAuth no-https игнорируется).", + "symbols/iisHttpPort/description": "Номер порта, используемый для конечной точки HTTP IIS Express в launchSettings.json.", + "symbols/iisHttpsPort/description": "Номер порта, используемый для конечной точки HTTPS IIS Express в launchSettings.json. Этот параметр применим только в том случае, если no-https не используется (при использовании IndividualAuth или OrganizationalAuth no-https игнорируется).", + "symbols/PWA/displayName": "_Прогрессивное веб-приложение", + "symbols/PWA/description": "Если указывается, используется для создания прогрессивного веб-приложения (PWA), поддерживающего установку и автономное использование.", + "symbols/IncludeSampleContent/displayName": "_Включить примеры страниц", + "symbols/IncludeSampleContent/description": "Настраивает, следует ли добавлять примеры страниц и стили для демонстрации базовых шаблонов использования.", + "symbols/Empty/description": "Настраивает, следует ли пропускать примеры страниц и стили, демонстрирующие базовые шаблоны использования.", + "symbols/NoHttps/description": "Следует ли отключить HTTPS. Этот параметр применяется, только если для --auth не используются Individual, IndividualB2C, SingleOrg или MultiOrg.", + "symbols/UseLocalDB/description": "Следует ли использовать LocalDB вместо SQLite. Этот параметр применяется, только если указывается --auth Individual или --auth IndividualB2C.", + "symbols/CalledApiUrl/description": "URL-адрес API для вызова из веб-приложения. Этот параметр применяется, только если указывается --auth SingleOrg, --auth MultiOrg или --auth IndividualB2C без узла ASP.NET Core.", + "symbols/CalledApiScopes/description": "Области для запроса вызова API из веб-приложения. Этот параметр применяется, только если указывается --auth SingleOrg, --auth MultiOrg или --auth IndividualB2C без узла ASP.NET Core.", + "symbols/UseProgramMain/displayName": "Не использовать _операторы верхнего уровня", + "symbols/UseProgramMain/description": "Следует ли создавать явный класс Program и метод Main вместо операторов верхнего уровня.", + "postActions/restore/description": "Восстановление пакетов NuGet, необходимых для этого проекта.", + "postActions/restore/manualInstructions/default/text": "Выполнить команду \"dotnet restore\"", + "postActions/open-file/description": "Открывает Readme.txt в редакторе" +} \ No newline at end of file diff --git a/src/Templates/templates/componentswebassembly-csharp-10/.template.config/localize/templatestrings.tr.json b/src/Templates/templates/componentswebassembly-csharp-10/.template.config/localize/templatestrings.tr.json new file mode 100644 index 0000000000..322e248af7 --- /dev/null +++ b/src/Templates/templates/componentswebassembly-csharp-10/.template.config/localize/templatestrings.tr.json @@ -0,0 +1,44 @@ +{ + "author": "Microsoft", + "name": "Fluent Blazor WebAssembly Tek Başına Uygulaması", + "description": "WebAssembly üzerinde çalışan bir Blazor uygulaması oluşturmaya yönelik proje şablonu. Bu şablon, zengin dinamik kullanıcı arabirimlerine (UI) sahip web uygulamaları için kullanılabilir.", + "symbols/Framework/description": "Projenin hedef çerçevesi.", + "symbols/Framework/choices/net10.0/description": "Hedef net10.0", + "symbols/skipRestore/description": "Belirtilirse, oluşturma sırasında projenin otomatik geri yüklenmesini atlar.", + "symbols/auth/choices/None/description": "Kimlik doğrulaması yok", + "symbols/auth/choices/Individual/description": "Bireysel kimlik doğrulaması", + "symbols/auth/choices/IndividualB2C/description": "Azure AD B2C ile bireysel kimlik doğrulaması", + "symbols/auth/choices/SingleOrg/description": "Tek bir kiracı için kuruluş kimlik doğrulaması", + "symbols/auth/description": "Kullanılacak kimlik doğrulaması türü", + "symbols/Authority/description": "OIDC sağlayıcısının yetkisi (tek başına Bireysel kimlik doğrulaması ile kullanın).", + "symbols/AAdB2CInstance/description": "Bağlanılacak Azure Active Directory B2C örneği (IndividualB2C kimlik doğrulaması ile kullanın).", + "symbols/SignUpSignInPolicyId/description": "Bu proje için oturum açma ve kaydolma ilkesi kimliği (IndividualB2C kimlik doğrulaması ile kullanın).", + "symbols/AADInstance/description": "Bağlanılacak Azure Active Directory örneği (SingleOrg kimlik doğrulaması ile kullanın).", + "symbols/ClientId/description": "Bu projenin İstemci Kimliği (tek başına senaryolarda IndividualB2C, SingleOrg veya Bireysel kimlik doğrulaması ile kullanın).", + "symbols/Domain/description": "Dizin kiracısı için etki alanı (SingleOrg veya IndividualB2C kimlik doğrulamasıyla kullanın).", + "symbols/AppIDUri/description": "Çağrılmak istenen sunucu API'sinin Uygulama Kimliği Uri'si (SingleOrg veya IndividualB2C kimlik doğrulaması ile kullanın).", + "symbols/APIClientId/description": "Sunucunun barındırmış olduğu API için İstemci Kimliği (IndividualB2C, SingleOrg ile kullanın).", + "symbols/DefaultScope/description": "İstemcinin erişim belirteci sağlamak amacıyla istekte bulunmak için ihtiyacı olan API kapsamı. (IndividualB2C, SingleOrg ile kullanın).", + "symbols/TenantId/description": "Bağlanılacak dizinin TenantId kimliği (SingleOrg kimlik doğrulamasıyla kullanın).", + "symbols/OrgReadAccess/description": "Bu uygulamanın dizin okuma erişimine izin verilip verilmeyeceği (yalnızca SingleOrg için geçerlidir).", + "symbols/UserSecretsId/description": "Gizli diziler için kullanılan kimlik (OrgReadAccess veya Bireysel kimlik doğrulaması ile kullanın).", + "symbols/ExcludeLaunchSettings/description": "launchSettings.json öğesinin oluşturulan şablondan dışlanıp dışlanmayacağı.", + "symbols/kestrelHttpPort/description": "launchSettings.json içinde HTTP uç noktası için kullanılacak bağlantı noktası numarası.", + "symbols/kestrelHttpsPort/description": "launchSettings.json içinde HTTPS uç noktası için kullanılacak bağlantı noktası numarası. Bu seçenek yalnızca no-https parametresi kullanılmazsa uygulanabilir (IndividualAuth veya OrganizationalAuth kullanılırsa no-https yoksayılır).", + "symbols/iisHttpPort/description": "launchSettings.json içinde IIS Express HTTP uç noktası için kullanılacak bağlantı noktası numarası.", + "symbols/iisHttpsPort/description": "launchSettings.json içinde IIS Express HTTPS uç noktası için kullanılacak bağlantı noktası numarası. Bu seçenek yalnızca no-https parametresi kullanılmazsa uygulanabilir (IndividualAuth veya OrganizationalAuth kullanılırsa no-https yoksayılır).", + "symbols/PWA/displayName": "_Aşamalı Web Uygulaması", + "symbols/PWA/description": "Belirtilmişse, yükleme ve çevrimdışı kullanımı destekleyen bir Aşamalı Web Uygulaması (PWA) oluşturur.", + "symbols/IncludeSampleContent/displayName": "Örnek _sayfalar ekle", + "symbols/IncludeSampleContent/description": "Temel kullanım düzenlerini göstermek için örnek sayfaların ve stil oluşturma özelliklerinin eklenip eklenmeyeceğini yapılandırır.", + "symbols/Empty/description": "Temel kullanım düzenlerini gösteren örnek sayfaların ve stil oluşturma özelliklerinin atlanıp atlanmayacağını yapılandırır.", + "symbols/NoHttps/description": "HTTPS'nin kapatılıp kapatılmayacağı. Bu seçenek yalnızca Bireysel, IndividualB2C, SingleOrg veya MultiOrg -- auth için kullanılmazsa geçerlidir.", + "symbols/UseLocalDB/description": "SQLite yerine LocalDB'nin kullanılıp kullanılmayacağı. Bu seçenek yalnızca --auth Individual veya --auth IndividualB2C belirtilirse geçerlidir.", + "symbols/CalledApiUrl/description": "Web uygulamasından çağrılan API URL'si. Bu seçenek yalnızca --auth SingleOrg, --auth MultiOrg veya --auth IndividualB2C without ve ASP.NET Core konağı belirtilirse geçerlidir.", + "symbols/CalledApiScopes/description": "Web uygulamasından API çağırma istek kapsamları. Bu seçenek yalnızca --auth SingleOrg, --auth MultiOrg veya --auth IndividualB2C without ve ASP.NET Core konağı belirtilirse geçerlidir.", + "symbols/UseProgramMain/displayName": "_Üst düzey deyimler kullanmayın", + "symbols/UseProgramMain/description": "Üst düzey deyimler yerine açık bir Program sınıfı ve Ana yöntem oluşturup oluşturulmayacağını belirtir.", + "postActions/restore/description": "Bu projenin gerektirdiği NuGet paketlerini geri yükleyin.", + "postActions/restore/manualInstructions/default/text": "'dotnet restore' çalıştır", + "postActions/open-file/description": "Benioku.txt dosyasını düzenleyicide açar" +} \ No newline at end of file diff --git a/src/Templates/templates/componentswebassembly-csharp-10/.template.config/localize/templatestrings.zh-Hans.json b/src/Templates/templates/componentswebassembly-csharp-10/.template.config/localize/templatestrings.zh-Hans.json new file mode 100644 index 0000000000..7b90e4ed3d --- /dev/null +++ b/src/Templates/templates/componentswebassembly-csharp-10/.template.config/localize/templatestrings.zh-Hans.json @@ -0,0 +1,44 @@ +{ + "author": "Microsoft", + "name": "Fluent Blazor WebAssembly 独立应用", + "description": "用于创建在 WebAssembly 上运行的 Blazor 应用的项目模板。此模板可用于具有丰富动态用户界面(UI)的 Web 应用。", + "symbols/Framework/description": "项目的目标框架。", + "symbols/Framework/choices/net10.0/description": "目标 net10.0", + "symbols/skipRestore/description": "如果指定,则在创建时跳过项目的自动还原。", + "symbols/auth/choices/None/description": "无身份验证", + "symbols/auth/choices/Individual/description": "个人身份验证", + "symbols/auth/choices/IndividualB2C/description": "使用 Azure AD B2C 进行个人身份验证", + "symbols/auth/choices/SingleOrg/description": "单个租户的组织身份验证", + "symbols/auth/description": "要使用的身份验证类型", + "symbols/Authority/description": "OIDC 提供程序的颁发机构(与独立个人身份验证一起使用)。", + "symbols/AAdB2CInstance/description": "要连接到的 Azure Active Directory B2C 实例(与 IndividualB2C 身份验证一起使用)。", + "symbols/SignUpSignInPolicyId/description": "此项目的登录和注册策略 ID (与 IndividualB2C 身份验证一起使用)。", + "symbols/AADInstance/description": "要连接到的 Azure Active Directory 实例(与 SingleOrg 身份验证一起使用)。", + "symbols/ClientId/description": "此项目的客户端 ID (在独立方案中与 IndividualB2C、SingleOrg 或个人身份验证一起使用)。", + "symbols/Domain/description": "目录租户的域(与 SingleOrg 或 IndividualB2C 身份验证一起使用)。", + "symbols/AppIDUri/description": "要调用的服务器 API 的应用 ID URI (与 SingleOrg 或 IndividualB2C 身份验证一起使用)。", + "symbols/APIClientId/description": "服务器托管的 API 的客户端 ID (与 IndividualB2C、SingleOrg 一起使用)。", + "symbols/DefaultScope/description": "客户端预配访问令牌需要请求的 API 范围。(与 IndividualB2C、SingleOrg 一起使用)。", + "symbols/TenantId/description": "要连接到的目录的 TenantId ID (与 SingleOrg 身份验证一起使用)。", + "symbols/OrgReadAccess/description": "是否允许此应用程序对目录进行读取访问(仅适用于 SingleOrg)。", + "symbols/UserSecretsId/description": "用于机密的 ID (与 OrgReadAccess 或个人身份验证一起使用)。", + "symbols/ExcludeLaunchSettings/description": "是否从生成的模板中排除 launchSettings.json。", + "symbols/kestrelHttpPort/description": "要用于 launchSettings.json 中 HTTP 终结点的端口号。", + "symbols/kestrelHttpsPort/description": "要用于 launchSettings.json 中 HTTPS 终结点的端口号。仅当不使用参数 no-https 时,此选项才适用(如果使用 IndividualAuth 或 OrganizationalAuth,则将忽略 no-https)。", + "symbols/iisHttpPort/description": "要用于 launchSettings.json 中 IIS Express HTTP 终结点的端口号。", + "symbols/iisHttpsPort/description": "要用于 launchSettings.json 中 IIS Express HTTPS 终结点的端口号。仅当不使用参数 no-https 时,此选项才适用(如果使用 IndividualAuth 或 OrganizationalAuth,则将忽略 no-https)。", + "symbols/PWA/displayName": "渐进式 Web 应用程序(_P)", + "symbols/PWA/description": "如果指定,则生成支持安装和脱机使用的渐进式 Web 应用程序(PWA)。", + "symbols/IncludeSampleContent/displayName": "包含示例页(_I)", + "symbols/IncludeSampleContent/description": "配置是否添加示例页和样式以演示基本使用模式。", + "symbols/Empty/description": "配置是否忽略演示基本使用模式的示例页和样式。", + "symbols/NoHttps/description": "是否禁用 HTTPS。仅当 Individual、IndividualB2C、SingleOrg 或 MultiOrg 不用于 --auth 时,此选项才适用。", + "symbols/UseLocalDB/description": "是否使用 LocalDB 而不是 SQLite。仅当指定了 --auth Individual 或 --auth IndividualB2C 时,此选项才适用。", + "symbols/CalledApiUrl/description": "要从 Web 应用调用的 API 的 URL。仅当指定了 --auth SingleOrg、--auth MultiOrg 或不带 ASP.NET Core 主机的 --auth IndividualB2C 时,此选项才适用。", + "symbols/CalledApiScopes/description": "请求从 Web 应用调用 API 的范围。仅当指定了 --auth SingleOrg、--auth MultiOrg 或不带 ASP.NET Core 主机的 --auth IndividualB2C 时,此选项才适用。", + "symbols/UseProgramMain/displayName": "不使用顶级语句(_T)", + "symbols/UseProgramMain/description": "是否生成显式程序类和主方法,而不是顶级语句。", + "postActions/restore/description": "还原此项目所需的 NuGet 包。", + "postActions/restore/manualInstructions/default/text": "运行 \"dotnet restore\"", + "postActions/open-file/description": "在编辑器中打开 Readme.txt" +} \ No newline at end of file diff --git a/src/Templates/templates/componentswebassembly-csharp-10/.template.config/localize/templatestrings.zh-Hant.json b/src/Templates/templates/componentswebassembly-csharp-10/.template.config/localize/templatestrings.zh-Hant.json new file mode 100644 index 0000000000..6a522adaec --- /dev/null +++ b/src/Templates/templates/componentswebassembly-csharp-10/.template.config/localize/templatestrings.zh-Hant.json @@ -0,0 +1,44 @@ +{ + "author": "Microsoft", + "name": "Fluent Blazor WebAssembly 獨立應用程式", + "description": "用來建立在 WebAssembly 上執行之 Blazor 應用程式的專案範本。此範本可用於具有豐富動態使用者介面 (UI) 的 Web 應用程式。", + "symbols/Framework/description": "專案的目標 Framework。", + "symbols/Framework/choices/net10.0/description": "目標 net10.0", + "symbols/skipRestore/description": "若指定,會在建立時跳過專案的自動還原。", + "symbols/auth/choices/None/description": "沒有驗證", + "symbols/auth/choices/Individual/description": "個別驗證", + "symbols/auth/choices/IndividualB2C/description": "具有 Azure AD B2C 的個別驗證", + "symbols/auth/choices/SingleOrg/description": "單一租用戶的組織驗證", + "symbols/auth/description": "要使用的驗證類型。", + "symbols/Authority/description": "OIDC 提供者的授權(搭配獨立個別驗證使用)。", + "symbols/AAdB2CInstance/description": "要連線的 Azure Active Directory B2C 執行個體 (搭配 IndividualB2C 驗證使用)。", + "symbols/SignUpSignInPolicyId/description": "此專案的登入和註冊原則識別碼 (搭配 IndividualB2C 驗證使用)。", + "symbols/AADInstance/description": "要連線到的 Azure Active Directory 執行個體 (搭配 SingleOrg 驗證使用)。", + "symbols/ClientId/description": "此專案的用戶端識別碼 (搭配獨立案例中的 IndividualB2C、SingleOrg 或 Individual 驗證使用)。", + "symbols/Domain/description": "目錄租用戶的網域 (搭配 SingleOrg 或 IndividualB2C 驗證使用)。", + "symbols/AppIDUri/description": "要呼叫的伺服器 API App ID URI (搭配 SingleOrg 或 IndividualB2C 驗證使用)。", + "symbols/APIClientId/description": "伺服器裝載之 API 的用戶端識別碼 (搭配 IndividualB2C、SingleOrg 使用)。", + "symbols/DefaultScope/description": "用戶端佈建存取權杖所需的 API 範圍。(搭配 IndividualB2C、SingleOrg 使用)。", + "symbols/TenantId/description": "要連線到目錄的 TenantId 識別碼 (搭配 SingleOrg 驗證使用)。", + "symbols/OrgReadAccess/description": "是否要允許此應用程式讀取存取目錄 (只適用於 SingleOrg)。", + "symbols/UserSecretsId/description": "用於祕密的識別碼 (搭配 OrgReadAccess 或個別驗證使用)。", + "symbols/ExcludeLaunchSettings/description": "是否要從產生的範本排除 launchSettings.json。", + "symbols/kestrelHttpPort/description": "launchSettings.json 中 HTTP 端點要使用的連接埠號碼。", + "symbols/kestrelHttpsPort/description": "launchSettings.json 中 HTTPS 端點要使用的連接埠號碼。只有在未使用參數 no-https 時,才適用此選項 (如果使用 IndividualAuth 或 OrganizationalAuth,則會忽略 no-https)。", + "symbols/iisHttpPort/description": "launchSettings.json 中 IIS Express HTTP 端點要使用的連接埠號碼。", + "symbols/iisHttpsPort/description": "launchSettings.json 中 IIS Express HTTPS 端點要使用的連接埠號碼。只有在未使用參數 no-https 時,才適用此選項 (如果使用 IndividualAuth 或 OrganizationalAuth,則會忽略 no-https)。", + "symbols/PWA/displayName": "漸進式 Web 應用程式(_P)", + "symbols/PWA/description": "若指定,會產生漸進式 Web 應用程式 (PWA) 支援安裝與離線使用。", + "symbols/IncludeSampleContent/displayName": "包含範例頁面(_I)", + "symbols/IncludeSampleContent/description": "設定是否要新增範例頁面和樣式,以示範基本使用模式。", + "symbols/Empty/description": "設定是否要省略範例頁面和樣式,其示範基本使用模式。", + "symbols/NoHttps/description": "是否要關閉 HTTPS。只有當 Individual、IndividualB2C、SingleOrg 或 MultiOrg 未用於 --auth 時,才適用此選項。", + "symbols/UseLocalDB/description": "是否使用 LocalDB 而非 SQLite。只有在已指定 --auth Individual 或 --auth IndividualB2C 時,才適用此選項。", + "symbols/CalledApiUrl/description": "要從 Web 應用程式呼叫的 API URL。只有在已指定 --auth SingleOrg、--auth MultiOrg 或 --auth IndividualB2C 時 (不含 ASP.NET Core 主機),才適用此選項。", + "symbols/CalledApiScopes/description": "要求從 Web 應用程式呼叫 API 的範圍。只有在已指定 --auth SingleOrg、--auth MultiOrg 或 --auth IndividualB2C 時 (不含 ASP.NET Core 主機),才適用此選項。", + "symbols/UseProgramMain/displayName": "不要使用最上層陳述式(_T)", + "symbols/UseProgramMain/description": "是否要產生明確的 Program 類別和 Main 方法,而非最上層語句。", + "postActions/restore/description": "還原此專案所需的 NuGet 套件。", + "postActions/restore/manualInstructions/default/text": "執行 'dotnet restore'", + "postActions/open-file/description": "在編輯器中開啟 Readme.txt" +} \ No newline at end of file diff --git a/src/Templates/templates/componentswebassembly-csharp-10/.template.config/template.json b/src/Templates/templates/componentswebassembly-csharp-10/.template.config/template.json new file mode 100644 index 0000000000..1d371dea9a --- /dev/null +++ b/src/Templates/templates/componentswebassembly-csharp-10/.template.config/template.json @@ -0,0 +1,495 @@ +{ + "$schema": "https://json.schemastore.org/template", + "author": "Microsoft", + "classifications": [ + "Web", + "Fluent", + "Blazor", + "WebAssembly", + "PWA" + ], + "name": "Fluent Blazor WebAssembly Standalone App", + "defaultName": "BlazorApp", + "description": "A project template for creating a Blazor app that runs on WebAssembly and uses the Fluent component library. This template can be used for web apps with rich dynamic user interfaces (UIs).", + "groupIdentity": "Microsoft.Web.Fluent.Blazor.Wasm", + "precedence": "10002", + "guids": [ + "4C26868E-5E7C-458D-82E3-040509D0C71F", + "5990939C-7E7B-4CFA-86FF-44CA5756498A", + "650B3CE7-2E93-4CC4-9F46-466686815EAA", + "0AFFA7FD-4E37-4636-AB91-3753E746DB98", + "53bc9b9d-9d6a-45d4-8429-2a2761773502" // Client ID + ], + "identity": "Microsoft.Web.Fluent.Blazor.Wasm.CSharp.10.0", + "thirdPartyNotices": "https://aka.ms/aspnetcore/10.0-third-party-notices", + "preferNameDirectory": true, + "primaryOutputs": [ + { + "path": "ComponentsWebAssembly-CSharp.csproj" + } + ], + "shortName": "fluentblazorwasm", + "sourceName": "ComponentsWebAssembly-CSharp", + "sources": [ + { + "source": "./", + "target": "./", + "exclude": [ + ".template.config/**" + ], + "copyOnly": [ + "**/wwwroot/css/**" + ], + "modifiers": [ + { + "condition": "(!PWA)", + "exclude": [ + "wwwroot/service-worker*.js", + "wwwroot/manifest.webmanifest", + "wwwroot/icon-512.png" + ] + }, + { + "condition": "(!UseProgramMain)", + "exclude": [ + "Program.Main.cs" + ] + }, + { + "condition": "(UseProgramMain)", + "exclude": [ + "Program.cs" + ], + "rename": { + "Program.Main.cs": "Program.cs" + } + }, + { + "condition": "(!IndividualLocalAuth)", + "exclude": [ + "wwwroot/appsettings.Development.json" + ] + }, + { + "condition": "(NoAuth)", + "rename": { + "Layout/MainLayout.NoAuth.razor": "Layout/MainLayout.razor" + }, + "exclude": [ + "Pages/Authentication.razor", + "Layout/LoginDisplay.razor", + "Layout/LoginDisplay.razor.css", + "Layout/MainLayout.Auth.razor", + "Layout/RedirectToLogin.razor", + "wwwroot/appsettings.Development.json", + "wwwroot/appsettings.json" + ] + }, + { + "condition": "(!NoAuth)", + "rename": { + "Layout/MainLayout.Auth.razor": "Layout/MainLayout.razor" + }, + "exclude": [ + "Layout/MainLayout.NoAuth.razor" + ] + }, + { + "condition": "(!GenerateApi)", + "rename": { + "Layout/NavMenu.NoApi.razor": "Layout/NavMenu.razor" + }, + "exclude": [ + "Pages/CallWebApi.razor", + "Layout/NavMenu.CallsWebApi.razor" + ] + }, + { + "condition": "(GenerateApi)", + "rename": { + "Layout/NavMenu.CallsWebApi.razor": "Layout/NavMenu.razor" + }, + "exclude": [ + "Layout/NavMenu.NoApi.razor" + ] + }, + { + "condition": "(ExcludeLaunchSettings)", + "exclude": [ + "Properties/launchSettings.json" + ] + }, + { + "condition": "(!SampleContent)", + "exclude": [ + "Pages/Counter.*", + "Pages/Weather.*", + "Layout/MainLayout.razor.css", + "Layout/NavMenu.*", + "wwwroot/favicon.png", + "wwwroot/sample-data/weather.json" + ] + } + ] + } + ], + "symbols": { + "Framework": { + "type": "parameter", + "description": "The target framework for the project.", + "datatype": "choice", + "choices": [ + { + "choice": "net10.0", + "description": "Target net10.0" + } + ], + "replaces": "net10.0", + "defaultValue": "net10.0" + }, + "HostIdentifier": { + "type": "bind", + "binding": "HostIdentifier" + }, + "skipRestore": { + "type": "parameter", + "datatype": "bool", + "description": "If specified, skips the automatic restore of the project on create.", + "defaultValue": "false" + }, + "auth": { + "type": "parameter", + "datatype": "choice", + "choices": [ + { + "choice": "None", + "description": "No authentication" + }, + { + "choice": "Individual", + "description": "Individual authentication" + }, + { + "choice": "IndividualB2C", + "description": "Individual authentication with Azure AD B2C" + }, + { + "choice": "SingleOrg", + "description": "Organizational authentication for a single tenant" + } + ], + "defaultValue": "None", + "description": "The type of authentication to use" + }, + "Authority": { + "type": "parameter", + "datatype": "string", + "replaces": "https://login.microsoftonline.com/", + "description": "The authority of the OIDC provider (use with standalone Individual auth)." + }, + "MissingAuthority": { + "type": "computed", + "value": "(IndividualAuth && Authority == \"https://login.microsoftonline.com/\" && ClientId == \"33333333-3333-3333-33333333333333333\")" + }, + "AAdB2CInstance": { + "type": "parameter", + "datatype": "string", + "replaces": "https:////aadB2CInstance.b2clogin.com/", + "description": "The Azure Active Directory B2C instance to connect to (use with IndividualB2C auth)." + }, + "SignUpSignInPolicyId": { + "type": "parameter", + "datatype": "string", + "defaultValue": "b2c_1_susi", + "replaces": "MySignUpSignInPolicyId", + "description": "The sign-in and sign-up policy ID for this project (use with IndividualB2C auth)." + }, + "AADInstance": { + "type": "parameter", + "datatype": "string", + "defaultValue": "https://login.microsoftonline.com/", + "replaces": "https:////login.microsoftonline.com/", + "description": "The Azure Active Directory instance to connect to (use with SingleOrg auth)." + }, + "ClientId": { + "type": "parameter", + "datatype": "string", + "replaces": "33333333-3333-3333-33333333333333333", + "description": "The Client ID for this project (use with IndividualB2C, SingleOrg or Individual auth in standalone scenarios)." + }, + "Domain": { + "type": "parameter", + "datatype": "string", + "replaces": "qualified.domain.name", + "description": "The domain for the directory tenant (use with SingleOrg or IndividualB2C auth)." + }, + "AppIDUri": { + "type": "parameter", + "datatype": "string", + "replaces": "api.id.uri", + "description": "The App ID Uri for the server API we want to call (use with SingleOrg or IndividualB2C auth)." + }, + "APIClientId": { + "type": "parameter", + "datatype": "string", + "replaces": "11111111-1111-1111-11111111111111111", + "description": "The Client ID for the API that the server hosts (use with IndividualB2C, SingleOrg)." + }, + "DefaultScope": { + "type": "parameter", + "datatype": "string", + "replaces": "api-scope", + "defaultValue": "access_as_user", + "description": "The API scope the client needs to request to provision an access token. (use with IndividualB2C, SingleOrg)." + }, + "TenantId": { + "type": "parameter", + "datatype": "string", + "replaces": "22222222-2222-2222-2222-222222222222", + "description": "The TenantId ID of the directory to connect to (use with SingleOrg auth)." + }, + "OrgReadAccess": { + "type": "parameter", + "datatype": "bool", + "defaultValue": "false", + "description": "Whether or not to allow this application read access to the directory (only applies to SingleOrg)." + }, + "UserSecretsId": { + "type": "parameter", + "datatype": "string", + "replaces": "aspnet-BlazorServerWeb-CSharp-53bc9b9d-9d6a-45d4-8429-2a2761773502", + "defaultValue": "aspnet-BlazorServerWeb-CSharp-53bc9b9d-9d6a-45d4-8429-2a2761773502", + "description": "The ID to use for secrets (use with OrgReadAccess or Individual auth)." + }, + "ExcludeLaunchSettings": { + "type": "parameter", + "datatype": "bool", + "defaultValue": "false", + "description": "Whether to exclude launchSettings.json from the generated template." + }, + "kestrelHttpPort": { + "type": "parameter", + "datatype": "integer", + "description": "Port number to use for the HTTP endpoint in launchSettings.json." + }, + "kestrelHttpPortGenerated": { + "type": "generated", + "generator": "port", + "parameters": { + "low": 5000, + "high": 5300 + } + }, + "kestrelHttpPortReplacer": { + "type": "generated", + "generator": "coalesce", + "parameters": { + "sourceVariableName": "kestrelHttpPort", + "fallbackVariableName": "kestrelHttpPortGenerated" + }, + "replaces": "5000" + }, + "kestrelHttpsPort": { + "type": "parameter", + "datatype": "integer", + "description": "Port number to use for the HTTPS endpoint in launchSettings.json. This option is only applicable when the parameter no-https is not used (no-https will be ignored if either IndividualAuth or OrganizationalAuth is used)." + }, + "kestrelHttpsPortGenerated": { + "type": "generated", + "generator": "port", + "parameters": { + "low": 7000, + "high": 7300 + } + }, + "kestrelHttpsPortReplacer": { + "type": "generated", + "generator": "coalesce", + "parameters": { + "sourceVariableName": "kestrelHttpsPort", + "fallbackVariableName": "kestrelHttpsPortGenerated" + }, + "replaces": "5001" + }, + "iisHttpPort": { + "type": "parameter", + "datatype": "integer", + "description": "Port number to use for the IIS Express HTTP endpoint in launchSettings.json." + }, + "iisHttpPortGenerated": { + "type": "generated", + "generator": "port" + }, + "iisHttpPortReplacer": { + "type": "generated", + "generator": "coalesce", + "parameters": { + "sourceVariableName": "iisHttpPort", + "fallbackVariableName": "iisHttpPortGenerated" + }, + "replaces": "8080" + }, + "iisHttpsPort": { + "type": "parameter", + "datatype": "integer", + "description": "Port number to use for the IIS Express HTTPS endpoint in launchSettings.json. This option is only applicable when the parameter no-https is not used (no-https will be ignored if either IndividualAuth or OrganizationalAuth is used)." + }, + "iisHttpsPortGenerated": { + "type": "generated", + "generator": "port", + "parameters": { + "low": 44300, + "high": 44399 + } + }, + "iisHttpsPortReplacer": { + "type": "generated", + "generator": "coalesce", + "parameters": { + "sourceVariableName": "iisHttpsPort", + "fallbackVariableName": "iisHttpsPortGenerated" + }, + "replaces": "44300" + }, + "PWA": { + "type": "parameter", + "datatype": "bool", + "defaultValue": "false", + "displayName": "_Progressive Web Application", + "description": "If specified, produces a Progressive Web Application (PWA) supporting installation and offline use." + }, + "OrganizationalAuth": { + "type": "computed", + "value": "(auth == \"SingleOrg\" || auth == \"MultiOrg\")" + }, + "MultiOrgAuth": { + "type": "computed", + "value": "(auth == \"MultiOrg\")" + }, + "SingleOrgAuth": { + "type": "computed", + "value": "(auth == \"SingleOrg\")" + }, + "IndividualLocalAuth": { + "type": "computed", + "value": "(auth == \"Individual\")" + }, + "IndividualAuth": { + "type": "computed", + "value": "(auth == \"Individual\" || auth == \"IndividualB2C\")" + }, + "IndividualB2CAuth": { + "type": "computed", + "value": "(auth == \"IndividualB2C\")" + }, + "IncludeSampleContent": { + "type": "parameter", + "datatype": "bool", + "defaultValue": "true", + "displayName": "_Include sample pages", + "description": "Configures whether to add sample pages and styling to demonstrate basic usage patterns." + }, + "Empty": { + "type": "parameter", + "datatype": "bool", + "defaultValue": "false", + "description": "Configures whether to omit sample pages and styling that demonstrate basic usage patterns." + }, + "SampleContent": { + "type": "computed", + "value": "(((IncludeSampleContent && (HostIdentifier != \"dotnetcli\" && HostIdentifier != \"dotnetcli-preview\"))) || ((!Empty && (HostIdentifier == \"dotnetcli\" || HostIdentifier == \"dotnetcli-preview\"))))" + }, + "NoAuth": { + "type": "computed", + "value": "(!(IndividualAuth || OrganizationalAuth))" + }, + "RequiresHttps": { + "type": "computed", + "value": "(OrganizationalAuth || IndividualAuth)" + }, + "HasHttpProfile": { + "type": "computed", + "value": "(!RequiresHttps)" + }, + "HasHttpsProfile": { + "type": "computed", + "value": "(RequiresHttps || !NoHttps)" + }, + "NoHttps": { + "type": "parameter", + "datatype": "bool", + "defaultValue": "false", + "description": "Whether to turn off HTTPS. This option only applies if Individual, IndividualB2C, SingleOrg, or MultiOrg aren't used for --auth." + }, + "UseLocalDB": { + "type": "parameter", + "datatype": "bool", + "defaultValue": "false", + "description": "Whether to use LocalDB instead of SQLite. This option only applies if --auth Individual or --auth IndividualB2C is specified." + }, + "copyrightYear": { + "type": "generated", + "generator": "now", + "replaces": "copyrightYear", + "parameters": { + "format": "yyyy" + } + }, + "CalledApiUrl": { + "type": "parameter", + "datatype": "string", + "replaces": "[WebApiUrl]", + "defaultValue": "https://graph.microsoft.com/v1.0", + "description": "URL of the API to call from the web app. This option only applies if --auth SingleOrg, --auth MultiOrg or --auth IndividualB2C without and ASP.NET Core host is specified." + }, + "CalledApiScopes": { + "type": "parameter", + "datatype": "string", + "replaces": "user.read", + "description": "Scopes to request to call the API from the web app. This option only applies if --auth SingleOrg, --auth MultiOrg or --auth IndividualB2C without and ASP.NET Core host is specified." + }, + "GenerateApi": { + "type": "computed", + "value": "(IndividualB2CAuth && (CalledApiUrl != \"https://graph.microsoft.com/v1.0\" || CalledApiScopes != \"user.read\"))" + }, + "UseProgramMain": { + "type": "parameter", + "datatype": "bool", + "defaultValue": "false", + "displayName": "Do not use _top-level statements", + "description": "Whether to generate an explicit Program class and Main method instead of top-level statements." + } + }, + "tags": { + "language": "C#", + "type": "project" + }, + "postActions": [ + { + "id": "restore", + "condition": "(!skipRestore)", + "description": "Restore NuGet packages required by this project.", + "manualInstructions": [ + { + "text": "Run 'dotnet restore'" + } + ], + "args": { + "files": ["ComponentsWebAssembly-CSharp.csproj"] + }, + "actionId": "210D431B-A78B-4D2F-B762-4ED3E3EA9025", + "continueOnError": true + }, + { + "id": "open-file", + "condition": "(IndividualLocalAuth && HostIdentifier != \"dotnetcli\" && HostIdentifier != \"dotnetcli-preview\")", + "description": "Opens Readme.txt in the editor", + "manualInstructions": [], + "actionId": "84C0DA21-51C8-4541-9940-6CA19AF04EE6", + "args": { + "files": "0" + }, + "continueOnError": true + } + ] +} diff --git a/src/Templates/templates/componentswebassembly-csharp-10/App.razor b/src/Templates/templates/componentswebassembly-csharp-10/App.razor new file mode 100644 index 0000000000..228b01e1ec --- /dev/null +++ b/src/Templates/templates/componentswebassembly-csharp-10/App.razor @@ -0,0 +1,38 @@ +@*#if (NoAuth) + + + + + + Not found + +

Sorry, there's nothing at this address.

+
+
+
+#else + + + + + + @if (context.User.Identity?.IsAuthenticated != true) + { + + } + else + { +

You are not authorized to access this resource.

+ } +
+
+
+ + Not found + +

Sorry, there's nothing at this address.

+
+
+
+
+#endif*@ diff --git a/src/Templates/templates/componentswebassembly-csharp-10/ComponentsWebAssembly-CSharp.csproj b/src/Templates/templates/componentswebassembly-csharp-10/ComponentsWebAssembly-CSharp.csproj new file mode 100644 index 0000000000..9fad344ccf --- /dev/null +++ b/src/Templates/templates/componentswebassembly-csharp-10/ComponentsWebAssembly-CSharp.csproj @@ -0,0 +1,28 @@ + + + + net10.0 + enable + enable + true + + service-worker-assets.js + + + + + + + + + + + + + + + + + + + diff --git a/src/Templates/templates/componentswebassembly-csharp/Layout/LoginDisplay.razor b/src/Templates/templates/componentswebassembly-csharp-10/Layout/LoginDisplay.razor similarity index 100% rename from src/Templates/templates/componentswebassembly-csharp/Layout/LoginDisplay.razor rename to src/Templates/templates/componentswebassembly-csharp-10/Layout/LoginDisplay.razor diff --git a/src/Templates/templates/componentswebassembly-csharp/Layout/LoginDisplay.razor.css b/src/Templates/templates/componentswebassembly-csharp-10/Layout/LoginDisplay.razor.css similarity index 100% rename from src/Templates/templates/componentswebassembly-csharp/Layout/LoginDisplay.razor.css rename to src/Templates/templates/componentswebassembly-csharp-10/Layout/LoginDisplay.razor.css diff --git a/src/Templates/templates/componentswebassembly-csharp/Layout/MainLayout.Auth.razor b/src/Templates/templates/componentswebassembly-csharp-10/Layout/MainLayout.Auth.razor similarity index 100% rename from src/Templates/templates/componentswebassembly-csharp/Layout/MainLayout.Auth.razor rename to src/Templates/templates/componentswebassembly-csharp-10/Layout/MainLayout.Auth.razor diff --git a/src/Templates/templates/componentswebassembly-csharp/Layout/MainLayout.NoAuth.razor b/src/Templates/templates/componentswebassembly-csharp-10/Layout/MainLayout.NoAuth.razor similarity index 100% rename from src/Templates/templates/componentswebassembly-csharp/Layout/MainLayout.NoAuth.razor rename to src/Templates/templates/componentswebassembly-csharp-10/Layout/MainLayout.NoAuth.razor diff --git a/src/Templates/templates/componentswebassembly-csharp/Layout/NavMenu.CallsWebApi.razor b/src/Templates/templates/componentswebassembly-csharp-10/Layout/NavMenu.CallsWebApi.razor similarity index 100% rename from src/Templates/templates/componentswebassembly-csharp/Layout/NavMenu.CallsWebApi.razor rename to src/Templates/templates/componentswebassembly-csharp-10/Layout/NavMenu.CallsWebApi.razor diff --git a/src/Templates/templates/componentswebassembly-csharp/Layout/NavMenu.NoApi.razor b/src/Templates/templates/componentswebassembly-csharp-10/Layout/NavMenu.NoApi.razor similarity index 100% rename from src/Templates/templates/componentswebassembly-csharp/Layout/NavMenu.NoApi.razor rename to src/Templates/templates/componentswebassembly-csharp-10/Layout/NavMenu.NoApi.razor diff --git a/src/Templates/templates/componentswebassembly-csharp/Layout/RedirectToLogin.razor b/src/Templates/templates/componentswebassembly-csharp-10/Layout/RedirectToLogin.razor similarity index 100% rename from src/Templates/templates/componentswebassembly-csharp/Layout/RedirectToLogin.razor rename to src/Templates/templates/componentswebassembly-csharp-10/Layout/RedirectToLogin.razor diff --git a/src/Templates/templates/componentswebassembly-csharp/Pages/Authentication.razor b/src/Templates/templates/componentswebassembly-csharp-10/Pages/Authentication.razor similarity index 100% rename from src/Templates/templates/componentswebassembly-csharp/Pages/Authentication.razor rename to src/Templates/templates/componentswebassembly-csharp-10/Pages/Authentication.razor diff --git a/src/Templates/templates/componentswebassembly-csharp/Pages/Counter.razor b/src/Templates/templates/componentswebassembly-csharp-10/Pages/Counter.razor similarity index 100% rename from src/Templates/templates/componentswebassembly-csharp/Pages/Counter.razor rename to src/Templates/templates/componentswebassembly-csharp-10/Pages/Counter.razor diff --git a/src/Templates/templates/componentswebassembly-csharp/Pages/Home.razor b/src/Templates/templates/componentswebassembly-csharp-10/Pages/Home.razor similarity index 100% rename from src/Templates/templates/componentswebassembly-csharp/Pages/Home.razor rename to src/Templates/templates/componentswebassembly-csharp-10/Pages/Home.razor diff --git a/src/Templates/templates/componentswebassembly-csharp-10/Pages/NotFound.razor b/src/Templates/templates/componentswebassembly-csharp-10/Pages/NotFound.razor new file mode 100644 index 0000000000..917ada1d23 --- /dev/null +++ b/src/Templates/templates/componentswebassembly-csharp-10/Pages/NotFound.razor @@ -0,0 +1,5 @@ +@page "/not-found" +@layout MainLayout + +

Not Found

+

Sorry, the content you are looking for does not exist.

\ No newline at end of file diff --git a/src/Templates/templates/componentswebassembly-csharp/Pages/Weather.razor b/src/Templates/templates/componentswebassembly-csharp-10/Pages/Weather.razor similarity index 100% rename from src/Templates/templates/componentswebassembly-csharp/Pages/Weather.razor rename to src/Templates/templates/componentswebassembly-csharp-10/Pages/Weather.razor diff --git a/src/Templates/templates/componentswebassembly-csharp/Program.Main.cs b/src/Templates/templates/componentswebassembly-csharp-10/Program.Main.cs similarity index 100% rename from src/Templates/templates/componentswebassembly-csharp/Program.Main.cs rename to src/Templates/templates/componentswebassembly-csharp-10/Program.Main.cs diff --git a/src/Templates/templates/componentswebassembly-csharp/Program.cs b/src/Templates/templates/componentswebassembly-csharp-10/Program.cs similarity index 100% rename from src/Templates/templates/componentswebassembly-csharp/Program.cs rename to src/Templates/templates/componentswebassembly-csharp-10/Program.cs diff --git a/src/Templates/templates/componentswebassembly-csharp/Properties/launchSettings.json b/src/Templates/templates/componentswebassembly-csharp-10/Properties/launchSettings.json similarity index 100% rename from src/Templates/templates/componentswebassembly-csharp/Properties/launchSettings.json rename to src/Templates/templates/componentswebassembly-csharp-10/Properties/launchSettings.json diff --git a/src/Templates/templates/componentswebassembly-csharp-10/_Imports.razor b/src/Templates/templates/componentswebassembly-csharp-10/_Imports.razor new file mode 100644 index 0000000000..f873e82b97 --- /dev/null +++ b/src/Templates/templates/componentswebassembly-csharp-10/_Imports.razor @@ -0,0 +1,15 @@ +@using System.Net.Http +@using System.Net.Http.Json +@*#if (!NoAuth) +@using Microsoft.AspNetCore.Components.Authorization +#endif*@ +@using Microsoft.AspNetCore.Components.Forms +@using Microsoft.AspNetCore.Components.Routing +@using Microsoft.AspNetCore.Components.Web +@using Microsoft.AspNetCore.Components.Web.Virtualization +@using Microsoft.AspNetCore.Components.WebAssembly.Http +@using Microsoft.FluentUI.AspNetCore.Components +@using Icons = Microsoft.FluentUI.AspNetCore.Components.Icons +@using Microsoft.JSInterop +@using ComponentsWebAssembly_CSharp +@using ComponentsWebAssembly_CSharp.Layout diff --git a/src/Templates/templates/componentswebassembly-csharp/wwwroot/appsettings.Development.json b/src/Templates/templates/componentswebassembly-csharp-10/wwwroot/appsettings.Development.json similarity index 100% rename from src/Templates/templates/componentswebassembly-csharp/wwwroot/appsettings.Development.json rename to src/Templates/templates/componentswebassembly-csharp-10/wwwroot/appsettings.Development.json diff --git a/src/Templates/templates/componentswebassembly-csharp/wwwroot/appsettings.json b/src/Templates/templates/componentswebassembly-csharp-10/wwwroot/appsettings.json similarity index 100% rename from src/Templates/templates/componentswebassembly-csharp/wwwroot/appsettings.json rename to src/Templates/templates/componentswebassembly-csharp-10/wwwroot/appsettings.json diff --git a/src/Templates/templates/componentswebassembly-csharp/wwwroot/css/app.css b/src/Templates/templates/componentswebassembly-csharp-10/wwwroot/css/app.css similarity index 100% rename from src/Templates/templates/componentswebassembly-csharp/wwwroot/css/app.css rename to src/Templates/templates/componentswebassembly-csharp-10/wwwroot/css/app.css diff --git a/src/Templates/templates/componentswebassembly-csharp-10/wwwroot/favicon.ico b/src/Templates/templates/componentswebassembly-csharp-10/wwwroot/favicon.ico new file mode 100644 index 0000000000..e189d8e579 Binary files /dev/null and b/src/Templates/templates/componentswebassembly-csharp-10/wwwroot/favicon.ico differ diff --git a/src/Templates/templates/componentswebassembly-csharp/wwwroot/icon-192.png b/src/Templates/templates/componentswebassembly-csharp-10/wwwroot/icon-192.png similarity index 100% rename from src/Templates/templates/componentswebassembly-csharp/wwwroot/icon-192.png rename to src/Templates/templates/componentswebassembly-csharp-10/wwwroot/icon-192.png diff --git a/src/Templates/templates/componentswebassembly-csharp/wwwroot/icon-512.png b/src/Templates/templates/componentswebassembly-csharp-10/wwwroot/icon-512.png similarity index 100% rename from src/Templates/templates/componentswebassembly-csharp/wwwroot/icon-512.png rename to src/Templates/templates/componentswebassembly-csharp-10/wwwroot/icon-512.png diff --git a/src/Templates/templates/componentswebassembly-csharp-10/wwwroot/index.html b/src/Templates/templates/componentswebassembly-csharp-10/wwwroot/index.html new file mode 100644 index 0000000000..1d5f326cde --- /dev/null +++ b/src/Templates/templates/componentswebassembly-csharp-10/wwwroot/index.html @@ -0,0 +1,53 @@ + + + + + + + ComponentsWebAssembly-CSharp + + + + + + + + + + + + + + + + + + + +
+ + + + +
+
+ +
+ An unhandled error has occurred. + Reload + 🗙 +
+ + + + + + + + + + + + + diff --git a/src/Templates/templates/componentswebassembly-csharp/wwwroot/manifest.webmanifest b/src/Templates/templates/componentswebassembly-csharp-10/wwwroot/manifest.webmanifest similarity index 100% rename from src/Templates/templates/componentswebassembly-csharp/wwwroot/manifest.webmanifest rename to src/Templates/templates/componentswebassembly-csharp-10/wwwroot/manifest.webmanifest diff --git a/src/Templates/templates/componentswebassembly-csharp/wwwroot/sample-data/weather.json b/src/Templates/templates/componentswebassembly-csharp-10/wwwroot/sample-data/weather.json similarity index 100% rename from src/Templates/templates/componentswebassembly-csharp/wwwroot/sample-data/weather.json rename to src/Templates/templates/componentswebassembly-csharp-10/wwwroot/sample-data/weather.json diff --git a/src/Templates/templates/componentswebassembly-csharp/wwwroot/service-worker.js b/src/Templates/templates/componentswebassembly-csharp-10/wwwroot/service-worker.js similarity index 100% rename from src/Templates/templates/componentswebassembly-csharp/wwwroot/service-worker.js rename to src/Templates/templates/componentswebassembly-csharp-10/wwwroot/service-worker.js diff --git a/src/Templates/templates/componentswebassembly-csharp-10/wwwroot/service-worker.published.js b/src/Templates/templates/componentswebassembly-csharp-10/wwwroot/service-worker.published.js new file mode 100644 index 0000000000..51a0e5c7a4 --- /dev/null +++ b/src/Templates/templates/componentswebassembly-csharp-10/wwwroot/service-worker.published.js @@ -0,0 +1,55 @@ +// Caution! Be sure you understand the caveats before publishing an application with +// offline support. See https://aka.ms/blazor-offline-considerations + +self.importScripts('./service-worker-assets.js'); +self.addEventListener('install', event => event.waitUntil(onInstall(event))); +self.addEventListener('activate', event => event.waitUntil(onActivate(event))); +self.addEventListener('fetch', event => event.respondWith(onFetch(event))); + +const cacheNamePrefix = 'offline-cache-'; +const cacheName = `${cacheNamePrefix}${self.assetsManifest.version}`; +const offlineAssetsInclude = [ /\.dll$/, /\.pdb$/, /\.wasm/, /\.html/, /\.js$/, /\.json$/, /\.css$/, /\.woff$/, /\.png$/, /\.jpe?g$/, /\.gif$/, /\.ico$/, /\.blat$/, /\.dat$/, /\.webmanifest$/ ]; +const offlineAssetsExclude = [ /^service-worker\.js$/ ]; + +// Replace with your base path if you are hosting on a subfolder. Ensure there is a trailing '/'. +const base = "/"; +const baseUrl = new URL(base, self.origin); +const manifestUrlList = self.assetsManifest.assets.map(asset => new URL(asset.url, baseUrl).href); + +async function onInstall(event) { + console.info('Service worker: Install'); + + // Fetch and cache all matching items from the assets manifest + const assetsRequests = self.assetsManifest.assets + .filter(asset => offlineAssetsInclude.some(pattern => pattern.test(asset.url))) + .filter(asset => !offlineAssetsExclude.some(pattern => pattern.test(asset.url))) + .map(asset => new Request(asset.url, { integrity: asset.hash, cache: 'no-cache' })); + await caches.open(cacheName).then(cache => cache.addAll(assetsRequests)); +} + +async function onActivate(event) { + console.info('Service worker: Activate'); + + // Delete unused caches + const cacheKeys = await caches.keys(); + await Promise.all(cacheKeys + .filter(key => key.startsWith(cacheNamePrefix) && key !== cacheName) + .map(key => caches.delete(key))); +} + +async function onFetch(event) { + let cachedResponse = null; + if (event.request.method === 'GET') { + // For all navigation requests, try to serve index.html from cache, + // unless that request is for an offline resource. + // If you need some URLs to be server-rendered, edit the following check to exclude those URLs + const shouldServeIndexHtml = event.request.mode === 'navigate' + && !manifestUrlList.some(url => url === event.request.url); + + const request = shouldServeIndexHtml ? 'index.html' : event.request; + const cache = await caches.open(cacheName); + cachedResponse = await cache.match(request); + } + + return cachedResponse || fetch(event.request); +} diff --git a/src/Templates/templates/componentswebassembly-csharp-8/.template.config/dotnetcli.host.json b/src/Templates/templates/componentswebassembly-csharp-8/.template.config/dotnetcli.host.json new file mode 100644 index 0000000000..c68a8f73fb --- /dev/null +++ b/src/Templates/templates/componentswebassembly-csharp-8/.template.config/dotnetcli.host.json @@ -0,0 +1,107 @@ +{ + "$schema": "https://json.schemastore.org/dotnetcli.host", + "symbolInfo": { + "skipRestore": { + "longName": "no-restore", + "shortName": "" + }, + "PWA": { + "longName": "pwa" + }, + "Framework": { + "longName": "framework" + }, + "Empty": { + "longName": "empty" + }, + "IncludeSampleContent": { + "isHidden": true + }, + "UseLocalDB": { + "longName": "use-local-db" + }, + "AADInstance": { + "longName": "aad-instance", + "shortName": "" + }, + "AAdB2CInstance": { + "longName": "aad-b2c-instance", + "shortName": "" + }, + "SignUpSignInPolicyId": { + "longName": "susi-policy-id", + "shortName": "ssp" + }, + "OrgReadAccess": { + "longName": "org-read-access", + "shortName": "r" + }, + "ClientId": { + "longName": "client-id", + "shortName": "" + }, + "AppIDUri": { + "longName": "app-id-uri", + "shortName": "" + }, + "APIClientId": { + "longName": "api-client-id", + "shortName": "" + }, + "Domain": { + "longName": "domain", + "shortName": "" + }, + "TenantId": { + "longName": "tenant-id", + "shortName": "" + }, + "DefaultScope": { + "longName": "default-scope", + "shortName": "s" + }, + "Authority": { + "longName": "authority", + "shortName": "" + }, + "kestrelHttpPort": { + "isHidden": true + }, + "kestrelHttpsPort": { + "isHidden": true + }, + "iisHttpPort": { + "isHidden": true + }, + "iisHttpsPort": { + "isHidden": true + }, + "ExcludeLaunchSettings": { + "longName": "exclude-launch-settings", + "shortName": "" + }, + "UserSecretsId": { + "isHidden": true + }, + "NoHttps": { + "longName": "no-https", + "shortName": "" + }, + "CalledApiUrl": { + "longName": "called-api-url", + "shortName": "" + }, + "CalledApiScopes": { + "longName": "called-api-scopes", + "shortName": "" + }, + "CallsMicrosoftGraph": { + "longName": "calls-graph", + "shortName": "" + }, + "UseProgramMain": { + "longName": "use-program-main", + "shortName": "" + } + } +} diff --git a/src/Templates/templates/componentswebassembly-csharp-8/.template.config/icon.png b/src/Templates/templates/componentswebassembly-csharp-8/.template.config/icon.png new file mode 100644 index 0000000000..42fb1d1efe Binary files /dev/null and b/src/Templates/templates/componentswebassembly-csharp-8/.template.config/icon.png differ diff --git a/src/Templates/templates/componentswebassembly-csharp-8/.template.config/ide.host.json b/src/Templates/templates/componentswebassembly-csharp-8/.template.config/ide.host.json new file mode 100644 index 0000000000..1d7dbb0d8b --- /dev/null +++ b/src/Templates/templates/componentswebassembly-csharp-8/.template.config/ide.host.json @@ -0,0 +1,25 @@ +{ + "$schema": "https://json.schemastore.org/ide.host", + "order": 610, + "icon": "icon.png", + "disableHttpsSymbol": "NoHttps", + "symbolInfo": [ + { + "id": "PWA", + "isVisible": true, + "persistenceScope": "templateGroup" + }, + { + "id": "UseProgramMain", + "isVisible": true, + "persistenceScope": "shared", + "persistenceScopeName": "Microsoft" + }, + { + "id": "IncludeSampleContent", + "isVisible": true, + "persistenceScope": "shared", + "persistenceScopeName": "Microsoft" + } + ] +} diff --git a/src/Templates/templates/componentswebassembly-csharp-8/.template.config/localize/templatestrings.cs.json b/src/Templates/templates/componentswebassembly-csharp-8/.template.config/localize/templatestrings.cs.json new file mode 100644 index 0000000000..97fc3f01cb --- /dev/null +++ b/src/Templates/templates/componentswebassembly-csharp-8/.template.config/localize/templatestrings.cs.json @@ -0,0 +1,44 @@ +{ + "author": "Microsoft", + "name": "Samostatná aplikace Fluent Blazor WebAssembly", + "description": "Šablona projektu pro vytvoření aplikace Blazor, která běží ve WebAssembly. Tato šablona se dá využít pro webové aplikace s propracovanými dynamickými uživatelskými rozhraními (UI).", + "symbols/Framework/description": "Cílová architektura pro projekt", + "symbols/Framework/choices/net8.0/description": "Cílový net8.0", + "symbols/skipRestore/description": "Pokud se tato možnost zadá, přeskočí automatické obnovení projektu při vytvoření.", + "symbols/auth/choices/None/description": "Bez ověřování", + "symbols/auth/choices/Individual/description": "Ověřování Individual", + "symbols/auth/choices/IndividualB2C/description": "Ověřování Individual pomocí Azure AD B2C", + "symbols/auth/choices/SingleOrg/description": "Ověřování organizace pro jednoho tenanta", + "symbols/auth/description": "Typ ověřování, který se má použít", + "symbols/Authority/description": "Autorita poskytovatele OIDC (používá se se samostatným ověřováním Individual).", + "symbols/AAdB2CInstance/description": "Instance Azure Active Directory B2C, ke které se chcete připojit (používá se s ověřováním IndividualB2C).", + "symbols/SignUpSignInPolicyId/description": "ID zásad přihlašování a registrace pro tento projekt (používá se s ověřováním IndividualB2C).", + "symbols/AADInstance/description": "Instance Azure Active Directory, ke které se chcete připojit (používá se s ověřováním SingleOrg).", + "symbols/ClientId/description": "ID klienta pro tento projekt (v samostatných scénářích se používá s ověřováním IndividualB2C, SingleOrg nebo Individual).", + "symbols/Domain/description": "Doména pro tenanta adresáře (používá se s ověřováním SingleOrg nebo IndividualB2C).", + "symbols/AppIDUri/description": "Identifikátor URI ID aplikace pro serverové rozhraní API, které chceme volat (používá se s ověřováním SingleOrg nebo IndividualB2C).", + "symbols/APIClientId/description": "ID klienta pro rozhraní API, které server hostuje (používá se s IndividualB2C, SingleOrg).", + "symbols/DefaultScope/description": "Rozsah rozhraní API, který musí klient požádat o zřízení přístupového tokenu. (použijte s IndividualB2C, SingleOrg).", + "symbols/TenantId/description": "TenantId adresáře, ke kterému se chcete připojit (používá se s ověřováním SingleOrg).", + "symbols/OrgReadAccess/description": "Určuje, jestli se této aplikaci povolí přístup ke čtení adresáře (platí jenom pro ověřování SingleOrg).", + "symbols/UserSecretsId/description": "ID, které se má použít pro tajné kódy (používá se s ověřováním OrgReadAccess nebo Individual).", + "symbols/ExcludeLaunchSettings/description": "Určuje, jestli se má z vygenerované šablony vyloučit soubor launchSettings.json.", + "symbols/kestrelHttpPort/description": "Číslo portu, který se má použít pro koncový bod HTTP v souboru launchSettings.json.", + "symbols/kestrelHttpsPort/description": "Číslo portu, který se má použít pro koncový bod HTTPS v souboru launchSettings.json. Tato možnost se dá použít jenom v případě, že se nepoužije parametr no-https (no-https se bude ignorovat, pokud se použije IndividualAuth nebo OrganizationalAuth).", + "symbols/iisHttpPort/description": "Číslo portu, který se má použít pro koncový bod IIS Express HTTP v souboru launchSettings.json.", + "symbols/iisHttpsPort/description": "Číslo portu, který se má použít pro koncový bod IIS Express HTTPS v souboru launchSettings.json. Tato možnost se dá použít jenom v případě, že se nepoužije parametr no-https (no-https se bude ignorovat, pokud se použije IndividualAuth nebo OrganizationalAuth).", + "symbols/PWA/displayName": "_Progresivní webová aplikace", + "symbols/PWA/description": "Pokud je tato možnost zadaná, vytvoří progresivní webovou aplikaci (PWA), která podporuje instalaci a offline použití.", + "symbols/IncludeSampleContent/displayName": "_Zahrnout ukázkové stránky", + "symbols/IncludeSampleContent/description": "Nastavuje, jestli se mají přidávat ukázkové stránky a styly pro demonstraci základních vzorů použití.", + "symbols/Empty/description": "Nastavuje, jestli se mají vynechat ukázkové stránky a styly, které demonstrují základní vzory použití.", + "symbols/NoHttps/description": "Určuje, jestli se má protokol HTTPS vypnout. Tato možnost platí jenom v případě, že se pro --auth nepoužívají Individual, IndividualB2C, SingleOrg ani MultiOrg.", + "symbols/UseLocalDB/description": "Určuje, jestli se má použít LocalDB namísto SQLite. Tato možnost platí jenom v případě, že je zadáno --auth Individual nebo --auth IndividualB2C.", + "symbols/CalledApiUrl/description": "Adresa URL rozhraní API, která se má volat z webové aplikace. Tato možnost platí jenom v případě, že je zadáno --auth SingleOrg, --auth MultiOrg nebo --auth IndividualB2C a také je zadán hostitel ASP.NET Core.", + "symbols/CalledApiScopes/description": "Obory, které se mají požádat žádat o volání rozhraní API z webové aplikace. Tato možnost platí jenom v případě, že je zadáno --auth SingleOrg, --auth MultiOrg nebo --auth IndividualB2C a také je zadán hostitel ASP.NET Core.", + "symbols/UseProgramMain/displayName": "Nepoužívat _příkazy nejvyšší úrovně", + "symbols/UseProgramMain/description": "Určuje, jestli se má místo příkazů nejvyšší úrovně generovat explicitní třída Program a metoda Main.", + "postActions/restore/description": "Obnoví balíčky NuGet vyžadované tímto projektem.", + "postActions/restore/manualInstructions/default/text": "Spustit dotnet restore", + "postActions/open-file/description": "Otevře soubor Readme.txt v editoru." +} diff --git a/src/Templates/templates/componentswebassembly-csharp-8/.template.config/localize/templatestrings.de.json b/src/Templates/templates/componentswebassembly-csharp-8/.template.config/localize/templatestrings.de.json new file mode 100644 index 0000000000..b12be28554 --- /dev/null +++ b/src/Templates/templates/componentswebassembly-csharp-8/.template.config/localize/templatestrings.de.json @@ -0,0 +1,44 @@ +{ + "author": "Microsoft", + "name": "Eigenständige Fluent Blazor WebAssembly-App", + "description": "Eine Projektvorlage für das Erstellen einer Blazor-App, die in WebAssembly ausgeführt wird. Diese Vorlage kann für Web-Apps mit umfangreichen dynamischen Benutzeroberflächen verwendet werden.", + "symbols/Framework/description": "Das Zielframework für das Projekt.", + "symbols/Framework/choices/net8.0/description": "Ziel net8.0", + "symbols/skipRestore/description": "Wenn angegeben, wird die automatische Wiederherstellung des Projekts beim Erstellen übersprungen.", + "symbols/auth/choices/None/description": "Keine Authentifizierung", + "symbols/auth/choices/Individual/description": "Individuelle Authentifizierung", + "symbols/auth/choices/IndividualB2C/description": "Individuelle Authentifizierung mit Azure AD B2C", + "symbols/auth/choices/SingleOrg/description": "Organisationsauthentifizierung für einen einzelnen Mandanten", + "symbols/auth/description": "Der zu verwendende Authentifizierungstyp", + "symbols/Authority/description": "Die Autorität des OIDC Anbieters (Verwendung mit eigenständiger individueller Authentifizierung).", + "symbols/AAdB2CInstance/description": "Die Azure Active Directory B2C-Instanz, mit der eine Verbindung hergestellt werden soll (mit IndividualB2C-Authentifizierung verwenden).", + "symbols/SignUpSignInPolicyId/description": "Die Anmelde und Registrierungsrichtlinien ID für dieses Projekt (mit IndividualB2C Authentifizierung verwenden).", + "symbols/AADInstance/description": "Die Azure Active Directory-Instanz, mit der eine Verbindung hergestellt werden soll (mit SingleOrg-Authentifizierung verwenden).", + "symbols/ClientId/description": "Die Client-ID für dieses Projekt (Verwendung mit IndividualB2C, SingleOrg oder Individual Auth in eigenständigen Szenarien).", + "symbols/Domain/description": "Die Domäne für den Verzeichnismandanten (mit SingleOrg oder IndividualB2C Authentifizierung verwenden).", + "symbols/AppIDUri/description": "Der App ID URI für die Server-API, die wir aufrufen möchten (mit SingleOrg- oder IndividualB2C-Authentifizierung verwenden).", + "symbols/APIClientId/description": "Die Client ID für die API, die der Server hostet (mit IndividualB2C, SingleOrg verwenden).", + "symbols/DefaultScope/description": "Der API Bereich, den der Client anfordern muss, um ein Zugriffstoken bereitzustellen. (Verwendung mit IndividualB2C, SingleOrg).", + "symbols/TenantId/description": "Die TenantId ID des Verzeichnisses, mit dem eine Verbindung hergestellt werden soll (mit SingleOrg Authentifizierung verwenden).", + "symbols/OrgReadAccess/description": "Ob dieser Anwendung Lesezugriff auf das Verzeichnis gewährt werden soll oder nicht (gilt nur für SingleOrg).", + "symbols/UserSecretsId/description": "Die für Geheimnisse zu verwendende ID (Verwendung mit OrgReadAccess oder Einzelauthentifizierung).", + "symbols/ExcludeLaunchSettings/description": "Ob launchSettings.json aus der generierten Vorlage ausgeschlossen werden soll.", + "symbols/kestrelHttpPort/description": "Portnummer, die für den HTTP Endpunkt in launchSettings.json verwendet werden soll.", + "symbols/kestrelHttpsPort/description": "Portnummer, die für den HTTPS Endpunkt in launchSettings.json verwendet werden soll. Diese Option ist nur anwendbar, wenn der Parameter no-https nicht verwendet wird (no-https wird ignoriert, wenn entweder IndividualAuth oder OrganizationalAuth verwendet wird).", + "symbols/iisHttpPort/description": "Portnummer, die für den IIS Express HTTP Endpunkt in launchSettings.json verwendet werden soll.", + "symbols/iisHttpsPort/description": "Portnummer, die für den IIS Express HTTPS Endpunkt in launchSettings.json verwendet werden soll. Diese Option ist nur anwendbar, wenn der Parameter no-https nicht verwendet wird (no-https wird ignoriert, wenn entweder IndividualAuth oder OrganizationalAuth verwendet wird).", + "symbols/PWA/displayName": "_Progressive Webanwendung", + "symbols/PWA/description": "Wenn angegeben, wird eine Progressive Web Application (PWA) erstellt, die die Installation und Offlineverwendung unterstützt.", + "symbols/IncludeSampleContent/displayName": "_Include Beispielseiten", + "symbols/IncludeSampleContent/description": "Konfiguriert, ob Beispielseiten und Stile hinzugefügt werden, um grundlegende Verwendungsmuster zu veranschaulichen.", + "symbols/Empty/description": "Konfiguriert, ob Beispielseiten und Formatierungen weggelassen werden sollen, die grundlegende Verwendungsmuster veranschaulichen.", + "symbols/NoHttps/description": "Ob HTTPS deaktiviert werden soll. Diese Option gilt nur, wenn Individual, IndividualB2C, SingleOrg oder MultiOrg nicht für --auth verwendet werden.", + "symbols/UseLocalDB/description": "Ob LocalDB anstelle von SQLite verwendet werden soll. Diese Option gilt nur, wenn --auth Individual oder --auth IndividualB2C angegeben ist.", + "symbols/CalledApiUrl/description": "URL der API, die von der Web-App aufgerufen werden soll. Diese Option gilt nur, wenn --auth SingleOrg, --auth MultiOrg oder --auth IndividualB2C ohne und ASP.NET Core-Host angegeben ist.", + "symbols/CalledApiScopes/description": "Anzufordernde Bereiche zum Aufrufen der API von der Web-App. Diese Option gilt nur, wenn --auth SingleOrg, --auth MultiOrg oder --auth IndividualB2C ohne und ASP.NET Core-Host angegeben ist.", + "symbols/UseProgramMain/displayName": "Keine Anweisungen_der obersten Ebene verwenden", + "symbols/UseProgramMain/description": "Gibt an, ob anstelle von Anweisungen der obersten Ebene eine explizite Programmklasse und eine Main-Methode generiert werden soll.", + "postActions/restore/description": "„NuGet-Pakete“ wiederherstellen, die für dieses Projekt erforderlich sind.", + "postActions/restore/manualInstructions/default/text": "„dotnet restore“ ausführen", + "postActions/open-file/description": "Öffnet Readme.txt im Editor" +} diff --git a/src/Templates/templates/componentswebassembly-csharp-8/.template.config/localize/templatestrings.en.json b/src/Templates/templates/componentswebassembly-csharp-8/.template.config/localize/templatestrings.en.json new file mode 100644 index 0000000000..01311427bb --- /dev/null +++ b/src/Templates/templates/componentswebassembly-csharp-8/.template.config/localize/templatestrings.en.json @@ -0,0 +1,45 @@ +{ + "author": "Microsoft", + "name": "Fluent Blazor WebAssembly Standalone App", + "description": "A project template for creating a Blazor app that runs on WebAssembly and uses the Fluent component library. This template can be used for web apps with rich dynamic user interfaces (UIs).", + "symbols/Framework/description": "The target framework for the project.", + "symbols/Framework/choices/net8.0/description": "Target net8.0", + "symbols/skipRestore/description": "If specified, skips the automatic restore of the project on create.", + "symbols/auth/choices/None/description": "No authentication", + "symbols/auth/choices/Individual/description": "Individual authentication", + "symbols/auth/choices/IndividualB2C/description": "Individual authentication with Azure AD B2C", + "symbols/auth/choices/SingleOrg/description": "Organizational authentication for a single tenant", + "symbols/auth/description": "The type of authentication to use", + "symbols/Authority/description": "The authority of the OIDC provider (use with standalone Individual auth).", + "symbols/AAdB2CInstance/description": "The Azure Active Directory B2C instance to connect to (use with IndividualB2C auth).", + "symbols/SignUpSignInPolicyId/description": "The sign-in and sign-up policy ID for this project (use with IndividualB2C auth).", + "symbols/AADInstance/description": "The Azure Active Directory instance to connect to (use with SingleOrg auth).", + "symbols/ClientId/description": "The Client ID for this project (use with IndividualB2C, SingleOrg or Individual auth in standalone scenarios).", + "symbols/Domain/description": "The domain for the directory tenant (use with SingleOrg or IndividualB2C auth).", + "symbols/AppIDUri/description": "The App ID Uri for the server API we want to call (use with SingleOrg or IndividualB2C auth).", + "symbols/APIClientId/description": "The Client ID for the API that the server hosts (use with IndividualB2C, SingleOrg).", + "symbols/DefaultScope/description": "The API scope the client needs to request to provision an access token. (use with IndividualB2C, SingleOrg).", + "symbols/TenantId/description": "The TenantId ID of the directory to connect to (use with SingleOrg auth).", + "symbols/OrgReadAccess/description": "Whether or not to allow this application read access to the directory (only applies to SingleOrg).", + "symbols/UserSecretsId/description": "The ID to use for secrets (use with OrgReadAccess or Individual auth).", + "symbols/ExcludeLaunchSettings/description": "Whether to exclude launchSettings.json from the generated template.", + "symbols/kestrelHttpPort/description": "Port number to use for the HTTP endpoint in launchSettings.json.", + "symbols/kestrelHttpsPort/description": "Port number to use for the HTTPS endpoint in launchSettings.json. This option is only applicable when the parameter no-https is not used (no-https will be ignored if either IndividualAuth or OrganizationalAuth is used).", + "symbols/iisHttpPort/description": "Port number to use for the IIS Express HTTP endpoint in launchSettings.json.", + "symbols/iisHttpsPort/description": "Port number to use for the IIS Express HTTPS endpoint in launchSettings.json. This option is only applicable when the parameter no-https is not used (no-https will be ignored if either IndividualAuth or OrganizationalAuth is used).", + "symbols/PWA/displayName": "_Progressive Web Application", + "symbols/PWA/description": "If specified, produces a Progressive Web Application (PWA) supporting installation and offline use.", + "symbols/IncludeSampleContent/displayName": "_Include sample pages", + "symbols/IncludeSampleContent/description": "Configures whether to add sample pages and styling to demonstrate basic usage patterns.", + "symbols/Empty/description": "Configures whether to omit sample pages and styling that demonstrate basic usage patterns.", + "symbols/NoHttps/description": "Whether to turn off HTTPS. This option only applies if Individual, IndividualB2C, SingleOrg, or MultiOrg aren't used for --auth.", + "symbols/UseLocalDB/description": "Whether to use LocalDB instead of SQLite. This option only applies if --auth Individual or --auth IndividualB2C is specified.", + "symbols/CalledApiUrl/description": "URL of the API to call from the web app. This option only applies if --auth SingleOrg, --auth MultiOrg or --auth IndividualB2C without and ASP.NET Core host is specified.", + "symbols/CalledApiScopes/description": "Scopes to request to call the API from the web app. This option only applies if --auth SingleOrg, --auth MultiOrg or --auth IndividualB2C without and ASP.NET Core host is specified.", + "symbols/UseProgramMain/displayName": "Do not use _top-level statements", + "_symbols/UseProgramMain/displayName.comment": "Use '_' as accelerator key when translating.", + "symbols/UseProgramMain/description": "Whether to generate an explicit Program class and Main method instead of top-level statements.", + "postActions/restore/description": "Restore NuGet packages required by this project.", + "postActions/restore/manualInstructions/default/text": "Run 'dotnet restore'", + "postActions/open-file/description": "Opens Readme.txt in the editor" +} diff --git a/src/Templates/templates/componentswebassembly-csharp-8/.template.config/localize/templatestrings.es.json b/src/Templates/templates/componentswebassembly-csharp-8/.template.config/localize/templatestrings.es.json new file mode 100644 index 0000000000..8d18968033 --- /dev/null +++ b/src/Templates/templates/componentswebassembly-csharp-8/.template.config/localize/templatestrings.es.json @@ -0,0 +1,44 @@ +{ + "author": "Microsoft", + "name": "Aplicación independiente WebAssembly de Fluent Blazor", + "description": "Plantilla de proyecto para crear una aplicación Blazor que se ejecuta en WebAssembly. Esta plantilla se puede usar para las aplicaciones web con interfaces de usuario dinámicas enriquecidas.", + "symbols/Framework/description": "Marco de destino del proyecto.", + "symbols/Framework/choices/net8.0/description": "net8.0 de destino", + "symbols/skipRestore/description": "Si se especifica, se omite la restauración automática del proyecto durante la creación.", + "symbols/auth/choices/None/description": "Sin autenticación", + "symbols/auth/choices/Individual/description": "Autenticación individual", + "symbols/auth/choices/IndividualB2C/description": "Autenticación individual con Azure AD B2C", + "symbols/auth/choices/SingleOrg/description": "Autenticación organizacional para un único inquilino", + "symbols/auth/description": "El tipo de autenticación que se va a usar", + "symbols/Authority/description": "Autoridad del proveedor de OIDC (se usa con la autenticación individual independiente).", + "symbols/AAdB2CInstance/description": "Instancia de Azure Active Directory B2C a la que conectarse (se usa con la autenticación IndividualB2C).", + "symbols/SignUpSignInPolicyId/description": "Id. de la directiva de inicio de sesión y registro para este proyecto (se usa con la autenticación IndividualB2C).", + "symbols/AADInstance/description": "Instancia de Azure Active Directory a la que se va a conectar (se usa con la autenticación SingleOrg).", + "symbols/ClientId/description": "Id. de cliente de este proyecto (se usa con IndividualB2C, SingleOrg o autenticación individual en escenarios independientes).", + "symbols/Domain/description": "Dominio del inquilino de directorio (se usa con la autenticación SingleOrg o IndividualB2C).", + "symbols/AppIDUri/description": "Uri de id. de aplicación para la API de servidor a la que queremos llamar (se usa con la autenticación SingleOrg o IndividualB2C).", + "symbols/APIClientId/description": "Id. de cliente de la API que hospeda el servidor (se usa con IndividualB2C, SingleOrg).", + "symbols/DefaultScope/description": "Ámbito de API que el cliente debe solicitar para aprovisionar un token de acceso. (Se usa con IndividualB2C, SingleOrg).", + "symbols/TenantId/description": "Id. de TenantId del directorio al que se va a conectar (se usa con la autenticación SingleOrg).", + "symbols/OrgReadAccess/description": "Indica si se va a permitir o no el acceso de lectura de esta aplicación al directorio (solo se aplica a SingleOrg).", + "symbols/UserSecretsId/description": "Id. que se va a usar para los secretos (se usa con OrgReadAccess o con la autenticación individual).", + "symbols/ExcludeLaunchSettings/description": "Indica si se va a excluir launchSettings.json de la plantilla generada.", + "symbols/kestrelHttpPort/description": "Número de puerto que se va a usar para el punto de conexión HTTP en launchSettings.json.", + "symbols/kestrelHttpsPort/description": "Número de puerto que se va a usar para el punto de conexión HTTPS en launchSettings.json. Esta opción solo es aplicable cuando no se usa el parámetro no-https (no-https se omitirá si se usa IndividualAuth o OrganizationalAuth).", + "symbols/iisHttpPort/description": "Número de puerto que se va a usar para el punto de conexión HTTP de IIS Express en launchSettings.json.", + "symbols/iisHttpsPort/description": "Número de puerto que se va a usar para el punto de conexión HTTPS de IIS Express en launchSettings.json. Esta opción solo es aplicable cuando no se usa el parámetro no-https (no-https se omitirá si se usa IndividualAuth o OrganizationalAuth).", + "symbols/PWA/displayName": "_Aplicación web progresiva", + "symbols/PWA/description": "Si se especifica, produce una aplicación web progresiva (PWA) compatible con la instalación y el uso sin conexión.", + "symbols/IncludeSampleContent/displayName": "_Incluir páginas de ejemplo", + "symbols/IncludeSampleContent/description": "Configura si se van a agregar páginas de ejemplo y estilos para mostrar patrones de uso básicos.", + "symbols/Empty/description": "Configura si se omiten las páginas de ejemplo y los estilos que muestran patrones de uso básicos.", + "symbols/NoHttps/description": "Si se va a desactivar HTTPS. Esta opción solo se aplica si Individual, IndividualB2C, SingleOrg o MultiOrg no se usan para --auth.", + "symbols/UseLocalDB/description": "Indica si se va a usar LocalDB en lugar de SQLite. Esta opción solo se aplica si se especifica --auth Individual o --auth IndividualB2C.", + "symbols/CalledApiUrl/description": "Dirección URL de la API a la que se va a llamar desde la aplicación web. Esta opción solo se aplica si se especifica --auth SingleOrg, --auth MultiOrg o --auth IndividualB2C sin y se especifica ASP.NET host core.", + "symbols/CalledApiScopes/description": "Ámbitos para solicitar llamar a la API desde la aplicación web. Esta opción solo se aplica si se especifica --auth SingleOrg, --auth MultiOrg o --auth IndividualB2C sin y se especifica el host ASP.NET Core.", + "symbols/UseProgramMain/displayName": "No usar instrucciones de _nivel superior", + "symbols/UseProgramMain/description": "Indica si se debe generar una clase Program explícita y un método Main en lugar de instrucciones de nivel superior.", + "postActions/restore/description": "Restaure los paquetes NuGet necesarios para este proyecto.", + "postActions/restore/manualInstructions/default/text": "Ejecutar \"dotnet restore\"", + "postActions/open-file/description": "Abre Readme.txt en el editor" +} diff --git a/src/Templates/templates/componentswebassembly-csharp-8/.template.config/localize/templatestrings.fr.json b/src/Templates/templates/componentswebassembly-csharp-8/.template.config/localize/templatestrings.fr.json new file mode 100644 index 0000000000..bf240f7555 --- /dev/null +++ b/src/Templates/templates/componentswebassembly-csharp-8/.template.config/localize/templatestrings.fr.json @@ -0,0 +1,44 @@ +{ + "author": "Microsoft", + "name": "Application Fluent Blazor WebAssembly autonome", + "description": "Modèle de projet permettant de créer une application Blazor qui s'exécute sur WebAssembly. Vous pouvez utiliser ce modèle pour les applications web ayant des IU (interfaces utilisateur) dynamiques riches.", + "symbols/Framework/description": "Framework cible du projet.", + "symbols/Framework/choices/net8.0/description": "Cible net8.0", + "symbols/skipRestore/description": "S’il est spécifié, ignore la restauration automatique du projet lors de la création.", + "symbols/auth/choices/None/description": "Aucune authentification", + "symbols/auth/choices/Individual/description": "Authentification individuelle", + "symbols/auth/choices/IndividualB2C/description": "Authentification individuelle avec Azure AD B2C", + "symbols/auth/choices/SingleOrg/description": "Authentification organisationnelle pour un seul locataire", + "symbols/auth/description": "Type d’authentification à utiliser", + "symbols/Authority/description": "Autorité du fournisseur OIDC (à utiliser avec l’authentification individuelle autonome).", + "symbols/AAdB2CInstance/description": "Instance Azure Active Directory B2C à laquelle se connecter (à utiliser avec l’authentification IndividualB2C).", + "symbols/SignUpSignInPolicyId/description": "ID de stratégie de connexion et d’inscription pour ce projet (à utiliser avec l’authentification IndividualB2C).", + "symbols/AADInstance/description": "Instance Azure Active Directory à laquelle se connecter (à utiliser avec l’authentification SingleOrg).", + "symbols/ClientId/description": "ID client de ce projet (utilisé avec IndividualB2C, SingleOrg ou l’authentification individuelle dans les scénarios autonomes).", + "symbols/Domain/description": "Domaine du locataire d’annuaire (utilisé avec l’authentification SingleOrg ou IndividualB2C).", + "symbols/AppIDUri/description": "URI d’ID d’application pour l’API serveur que nous voulons appeler (à utiliser avec l’authentification SingleOrg ou IndividualB2C).", + "symbols/APIClientId/description": "ID client de l’API hébergée par le serveur (à utiliser avec IndividualB2C, SingleOrg).", + "symbols/DefaultScope/description": "Étendue d’API dont le client a besoin pour configurer un jeton d’accès. (à utiliser avec IndividualB2C, SingleOrg).", + "symbols/TenantId/description": "ID TenantId du répertoire auquel se connecter (à utiliser avec l’authentification SingleOrg).", + "symbols/OrgReadAccess/description": "Indique si cette application doit ou non autoriser l’accès en lecture au répertoire (s’applique uniquement à SingleOrg).", + "symbols/UserSecretsId/description": "ID à utiliser pour les secrets (à utiliser avec OrgReadAccess ou l’authentification individuelle).", + "symbols/ExcludeLaunchSettings/description": "Indique s’il faut exclure launchSettings.json du modèle généré.", + "symbols/kestrelHttpPort/description": "Numéro de port à utiliser pour le point de terminaison HTTP dans launchSettings.json.", + "symbols/kestrelHttpsPort/description": "Numéro de port à utiliser pour le point de terminaison HTTPS dans launchSettings.json. Cette option s’applique uniquement lorsque le paramètre no-https n’est pas utilisé (no-https sera ignoré si IndividualAuth ou OrganizationalAuth est utilisé).", + "symbols/iisHttpPort/description": "Numéro de port à utiliser pour le point de terminaison HTTP IIS Express dans launchSettings.json.", + "symbols/iisHttpsPort/description": "Numéro de port à utiliser pour le point de terminaison HTTPS IIS Express dans launchSettings.json. Cette option s’applique uniquement lorsque le paramètre no-https n’est pas utilisé (no-https sera ignoré si IndividualAuth ou OrganizationalAuth est utilisé).", + "symbols/PWA/displayName": "_Application web progressive", + "symbols/PWA/description": "Si ce paramètre est spécifié, produit une application web progressive (PWA) qui prend en charge l’installation et l’utilisation hors connexion.", + "symbols/IncludeSampleContent/displayName": "_Inclure des exemples de pages", + "symbols/IncludeSampleContent/description": "Configure s'il faut ajouter des exemples de pages et de style pour illustrer les modèles d'utilisation de base.", + "symbols/Empty/description": "Configure s'il faut omettre les exemples de pages et le style qui illustrent les modèles d'utilisation de base.", + "symbols/NoHttps/description": "Indique s’il faut désactiver HTTPS. Cette option s’applique uniquement si Individual, IndividualB2C, SingleOrg ou MultiOrg ne sont pas utilisés pour --auth.", + "symbols/UseLocalDB/description": "Indique s’il faut utiliser localDB au lieu de SQLite. Cette option s’applique uniquement si --auth Individual ou --auth IndividualB2C est spécifié.", + "symbols/CalledApiUrl/description": "URL de l’API à appeler à partir de l’application web. Cette option s’applique uniquement si --auth SingleOrg, --auth MultiOrg ou --auth IndividualB2C sans et ASP.NET’hôte principal est spécifié.", + "symbols/CalledApiScopes/description": "Étendues où demander l’appel de l’API à partir de l’application web. Cette option s’applique uniquement si l’hôte --auth SingleOrg, --auth MultiOrg ou --auth IndividualB2C sans et ASP.NET Core est spécifié.", + "symbols/UseProgramMain/displayName": "N’utilisez pas _d’instructions de niveau supérieur.", + "symbols/UseProgramMain/description": "Indique s’il faut générer une classe Programme explicite et une méthode Main au lieu d’instructions de niveau supérieur.", + "postActions/restore/description": "Restaurez les packages NuGet requis par ce projet.", + "postActions/restore/manualInstructions/default/text": "Exécuter « dotnet restore »", + "postActions/open-file/description": "Ouvre Readme.txt dans l’éditeur" +} diff --git a/src/Templates/templates/componentswebassembly-csharp-8/.template.config/localize/templatestrings.it.json b/src/Templates/templates/componentswebassembly-csharp-8/.template.config/localize/templatestrings.it.json new file mode 100644 index 0000000000..4751ac326b --- /dev/null +++ b/src/Templates/templates/componentswebassembly-csharp-8/.template.config/localize/templatestrings.it.json @@ -0,0 +1,44 @@ +{ + "author": "Microsoft", + "name": "App autonoma Fluent Blazor WebAssembly", + "description": "Modello di progetto per la creazione di un'app Blazor eseguita in WebAssembly. Questo modello può essere usato per app Web con interfacce utente dinamiche.", + "symbols/Framework/description": "Il framework di destinazione per il progetto.", + "symbols/Framework/choices/net8.0/description": "Destinazione net8.0", + "symbols/skipRestore/description": "Se specificato, ignora il ripristino automatico del progetto durante la creazione.", + "symbols/auth/choices/None/description": "Nessuna autenticazione", + "symbols/auth/choices/Individual/description": "Autenticazione singola", + "symbols/auth/choices/IndividualB2C/description": "Autenticazione singola con Azure AD B2C", + "symbols/auth/choices/SingleOrg/description": "Autenticazione organizzativa per un singolo tenant", + "symbols/auth/description": "Tipo di autenticazione da usare.", + "symbols/Authority/description": "Autorità del provider OIDC (usare con l'autenticazione individuale autonoma).", + "symbols/AAdB2CInstance/description": "Istanza di Azure Active Directory B2C a cui connettersi (usare con l'autenticazione IndividualB2C).", + "symbols/SignUpSignInPolicyId/description": "ID dei criteri di accesso e iscrizione per questo progetto (usare con l'autenticazione IndividualB2C).", + "symbols/AADInstance/description": "Istanza di Azure Active Directory a cui connettersi (usare con l'autenticazione SingleOrg).", + "symbols/ClientId/description": "ID client per questo progetto (usare con IndividualB2C, SingleOrg o Autenticazione individuale in scenari autonomi).", + "symbols/Domain/description": "Il dominio per il tenant della directory (da usare con l'autenticazione SingleOrg o IndividualB2C).", + "symbols/AppIDUri/description": "URI ID app per l'API server che si vuole chiamare (usare con l'autenticazione SingleOrg o IndividualB2C).", + "symbols/APIClientId/description": "ID client per l'API ospitata dal server (usare con IndividualB2C, SingleOrg).", + "symbols/DefaultScope/description": "Ambito API che il client deve richiedere per effettuare il provisioning di un token di accesso (usare con IndividualB2C, SingleOrg).", + "symbols/TenantId/description": "ID TenantId della directory a cui connettersi (usare con l'autenticazione SingleOrg).", + "symbols/OrgReadAccess/description": "Indica se consentire o meno a questa applicazione l'accesso in lettura alla directory (si applica solo a SingleOrg).", + "symbols/UserSecretsId/description": "ID da usare per i segreti (usare con OrgReadAccess o l'autenticazione singola).", + "symbols/ExcludeLaunchSettings/description": "Indica se escludere launchSettings.json dal modello generato.", + "symbols/kestrelHttpPort/description": "Numero di porta da usare per l'endpoint HTTP in launchSettings.json.", + "symbols/kestrelHttpsPort/description": "Numero di porta da usare per l'endpoint HTTPS in launchSettings.json. Questa opzione è applicabile solo quando il parametro no-https non viene usato (no-https verrà ignorato se si usa IndividualAuth o OrganizationalAuth).", + "symbols/iisHttpPort/description": "Numero di porta da usare per l'endpoint HTTP IIS Express in launchSettings.json.", + "symbols/iisHttpsPort/description": "Numero di porta da usare per l'endpoint HTTPS IIS Express in launchSettings.json. Questa opzione è applicabile solo quando il parametro no-https non viene usato (no-https verrà ignorato se si usa IndividualAuth o OrganizationalAuth).", + "symbols/PWA/displayName": "Applicazione Web _Progressive", + "symbols/PWA/description": "Se specificato, produce un'applicazione Web progressiva (PWA) che supporta l'installazione e l'uso offline.", + "symbols/IncludeSampleContent/displayName": "_Include pagine di esempio", + "symbols/IncludeSampleContent/description": "Consente di configurare se aggiungere pagine di esempio e stile per mostrare modelli di utilizzo di base.", + "symbols/Empty/description": "Consente di configurare se omettere pagine di esempio e stile che mostrano modelli di utilizzo di base.", + "symbols/NoHttps/description": "Indica se disattivare HTTPS. Questa opzione si applica solo se Individual, IndividualB2C, SingleOrg o MultiOrg non vengono usati per --auth.", + "symbols/UseLocalDB/description": "Indica se usare LocalDB invece di SQLite. Questa opzione si applica solo se è specificato --auth Individual o --auth IndividualB2C.", + "symbols/CalledApiUrl/description": "URL dell'API da chiamare dall'app Web. Questa opzione si applica solo se è specificato --auth SingleOrg, --auth MultiOrg o --auth IndividualB2C without e host ASP.NET Core.", + "symbols/CalledApiScopes/description": "Ambiti per richiedere di chiamare l'API dall'app Web. Questa opzione si applica solo se è specificato --auth SingleOrg, --auth MultiOrg o --auth IndividualB2C without e host ASP.NET Core.", + "symbols/UseProgramMain/displayName": "Non usare_istruzioni di primo livello", + "symbols/UseProgramMain/description": "Indica se generare una classe Program esplicita e un metodo Main anziché istruzioni di primo livello.", + "postActions/restore/description": "Ripristina i pacchetti NuGet richiesti da questo progetto.", + "postActions/restore/manualInstructions/default/text": "Esegui 'dotnet restore'", + "postActions/open-file/description": "Apre Readme.txt nell'editor" +} diff --git a/src/Templates/templates/componentswebassembly-csharp-8/.template.config/localize/templatestrings.ja.json b/src/Templates/templates/componentswebassembly-csharp-8/.template.config/localize/templatestrings.ja.json new file mode 100644 index 0000000000..437d460b43 --- /dev/null +++ b/src/Templates/templates/componentswebassembly-csharp-8/.template.config/localize/templatestrings.ja.json @@ -0,0 +1,44 @@ +{ + "author": "Microsoft", + "name": "Fluent Blazor WebAssembly スタンドアロン アプリ", + "description": "WebAssembly で実行される Blazor アプリを作成するためのプロジェクト テンプレート。このテンプレートは、高度でダイナミックなユーザー インターフェイス (UI) を備えた Web アプリに使用できます。", + "symbols/Framework/description": "プロジェクトのターゲット フレームワークです。", + "symbols/Framework/choices/net8.0/description": "ターゲット net8.0", + "symbols/skipRestore/description": "指定した場合、作成時にプロジェクトの自動復元がスキップされます。", + "symbols/auth/choices/None/description": "認証なし", + "symbols/auth/choices/Individual/description": "個別の認証", + "symbols/auth/choices/IndividualB2C/description": "Azure AD B2C を使用した個別の認証", + "symbols/auth/choices/SingleOrg/description": "単一テナントの組織認証", + "symbols/auth/description": "使用する認証の種類", + "symbols/Authority/description": "OIDC プロバイダーの権限 (個別のスタンドアロン認証で使用)。", + "symbols/AAdB2CInstance/description": "接続先の Azure Active Directory B2C インスタンス (IndividualB2C 認証で使用)。", + "symbols/SignUpSignInPolicyId/description": "このプロジェクトのサインインおよびサインアップのポリシー ID (IndividualB2C 認証で使用)。", + "symbols/AADInstance/description": "接続先の Azure Active Directory インスタンス (SingleOrg 認証で使用)。", + "symbols/ClientId/description": "このプロジェクトのクライアント ID (スタンドアロン シナリオでは、IndividualB2C、SingleOrg、または個別の認証で使用します)。", + "symbols/Domain/description": "ディレクトリ テナントのドメイン (SingleOrg または IndividualB2C 認証で使用)。", + "symbols/AppIDUri/description": "呼び出すサーバー API のアプリ ID URI (SingleOrg または IndividualB2C 認証で使用)。", + "symbols/APIClientId/description": "サーバーがホストする API のクライアント ID (IndividualB2C、SingleOrg で使用)。", + "symbols/DefaultScope/description": "アクセス トークンのプロビジョニングするためにクライアントが要求する必要がある API スコープ。(IndividualB2C、SingleOrg で使用)。", + "symbols/TenantId/description": "接続先のディレクトリの TenantId ID (SingleOrg 認証で使用)。", + "symbols/OrgReadAccess/description": "このアプリケーションにディレクトリへの読み取りアクセスを許可するかどうか (SingleOrg にのみ適用されます)。", + "symbols/UserSecretsId/description": "シークレットで使用する ID (OrgReadAccess または個別の認証で使用)。", + "symbols/ExcludeLaunchSettings/description": "生成されたテンプレートから launchSettings.json を除外するかどうか。", + "symbols/kestrelHttpPort/description": "launchSettings.json の HTTP エンドポイントに使用するポート番号。", + "symbols/kestrelHttpsPort/description": "launchSettings.json で HTTPS エンドポイントに使用するポート番号。このオプションは、HTTPS 以外のパラメーターが使用されていない場合にのみ適用されます (IndividualAuth または OrganizationalAuth が使用されている場合は、HTTPS 以外は無視されます)。", + "symbols/iisHttpPort/description": "launchSettings.json の IIS Express HTTP エンドポイントに使用するポート番号。", + "symbols/iisHttpsPort/description": "launchSettings.json で IIS Express HTTPS エンドポイントに使用するポート番号。このオプションは、HTTPS 以外のパラメーターが使用されていない場合にのみ適用されます (IndividualAuth または OrganizationalAuth が使用されている場合は、HTTPS 以外は無視されます)。", + "symbols/PWA/displayName": "プログレッシブ Web アプリケーション(_P)", + "symbols/PWA/description": "指定した場合、インストールとオフラインでの使用をサポートするプログレッシブ Web アプリケーション (PWA) が生成されます。", + "symbols/IncludeSampleContent/displayName": "サンプル ページを含める(_I)", + "symbols/IncludeSampleContent/description": "基本的な使用パターンを示すサンプル ページとスタイルを追加するかどうかを構成します。", + "symbols/Empty/description": "基本的な使用パターンを示すサンプル ページとスタイルを省略するかどうかを構成します。", + "symbols/NoHttps/description": "HTTPS をオフにするかどうか。このオプションは、Individual、IndividualB2C、SingleOrg、または MultiOrg が --auth に使用されていない場合にのみ適用されます。", + "symbols/UseLocalDB/description": "SQLite の代わりに LocalDB を使用するかどうか。このオプションは、--auth Individual または --auth IndividualB2C が指定されている場合にのみ適用されます。", + "symbols/CalledApiUrl/description": "Web アプリから呼び出す API の URL。このオプションは、--auth SingleOrg、--auth MultiOrg、または --auth IndividualB2C で、ASP.NET Core ホストが指定されていない場合にのみ適用されます。", + "symbols/CalledApiScopes/description": "Web アプリから API を呼び出すために要求するスコープ。このオプションは、--auth SingleOrg、--auth MultiOrg、または --auth IndividualB2C で、ASP.NET Core ホストが指定されていない場合にのみ適用されます。", + "symbols/UseProgramMain/displayName": "最上位レベルのステートメントを使用しない(_T)", + "symbols/UseProgramMain/description": "最上位レベルのステートメントではなく、明示的な Program クラスと Main メソッドを生成するかどうか。", + "postActions/restore/description": "このプロジェクトに必要な NuGet パッケージを復元します。", + "postActions/restore/manualInstructions/default/text": "'dotnet restore' を実行する", + "postActions/open-file/description": "エディターで Readme.txt を開きます" +} diff --git a/src/Templates/templates/componentswebassembly-csharp-8/.template.config/localize/templatestrings.ko.json b/src/Templates/templates/componentswebassembly-csharp-8/.template.config/localize/templatestrings.ko.json new file mode 100644 index 0000000000..f0c7578360 --- /dev/null +++ b/src/Templates/templates/componentswebassembly-csharp-8/.template.config/localize/templatestrings.ko.json @@ -0,0 +1,44 @@ +{ + "author": "Microsoft", + "name": "Fluent Blazor WebAssembly 독립 실행형 앱", + "description": "WebAssembly에서 실행되는 Blazor 앱을 만드는 데 사용되는 프로젝트 템플릿입니다. 이 템플릿을 사용하여 다양한 동적 UI(사용자 인터페이스)가 포함된 웹앱을 만들 수 있습니다.", + "symbols/Framework/description": "프로젝트에 대한 대상 프레임워크입니다.", + "symbols/Framework/choices/net8.0/description": "대상 net8.0", + "symbols/skipRestore/description": "지정된 경우, 프로젝트 생성 시 자동 복원을 건너뜁니다.", + "symbols/auth/choices/None/description": "인증 없음", + "symbols/auth/choices/Individual/description": "개별 인증", + "symbols/auth/choices/IndividualB2C/description": "Azure AD B2C를 사용한 개별 인증", + "symbols/auth/choices/SingleOrg/description": "단일 테넌트에 대한 조직 인증", + "symbols/auth/description": "사용할 인증 유형", + "symbols/Authority/description": "OIDC 제공자의 권한(독립형 개별 인증과 함께 사용).", + "symbols/AAdB2CInstance/description": "연결할 Azure Active Directory B2C 인스턴스(IndividualB2C 인증과 함께 사용).", + "symbols/SignUpSignInPolicyId/description": "이 프로젝트의 로그인 및 등록 정책 ID입니다(IndividualB2C 인증과 함께 사용).", + "symbols/AADInstance/description": "연결할 Azure Active Directory 인스턴스입니다(SingleOrg 인증과 함께 사용).", + "symbols/ClientId/description": "이 프로젝트의 클라이언트 ID입니다(독립형 시나리오에서 IndividualB2C, SingleOrg 또는 Individual auth와 함께 사용).", + "symbols/Domain/description": "디렉터리 테넌트의 도메인입니다(SingleOrg 또는 IndividualB2C 인증과 함께 사용).", + "symbols/AppIDUri/description": "호출하려는 서버 API의 앱 ID Uri입니다(SingleOrg 또는 IndividualB2C 인증과 함께 사용).", + "symbols/APIClientId/description": "서버가 호스팅하는 API의 클라이언트 ID입니다(IndividualB2C, SingleOrg와 함께 사용).", + "symbols/DefaultScope/description": "클라이언트가 액세스 토큰을 프로비전하기 위해 요청해야 하는 API 범위입니다. (IndividualB2C, SingleOrg와 함께 사용).", + "symbols/TenantId/description": "연결할 디렉터리의 TenantId ID입니다(SingleOrg 인증과 함께 사용).", + "symbols/OrgReadAccess/description": "이 애플리케이션이 디렉터리에 대한 읽기 액세스를 허용할지 여부(SingleOrg에만 적용됨).", + "symbols/UserSecretsId/description": "비밀에 사용할 ID입니다(OrgReadAccess 또는 개별 인증과 함께 사용).", + "symbols/ExcludeLaunchSettings/description": "생성된 템플릿에서 launchSettings.json을 제외할지 여부입니다.", + "symbols/kestrelHttpPort/description": "launchSettings.json의 HTTP 엔드포인트에 사용할 포트 번호입니다.", + "symbols/kestrelHttpsPort/description": "launchSettings.json의 HTTPS 엔드포인트에 사용할 포트 번호입니다. 이 옵션은 매개 변수 no-https가 사용되지 않은 경우에만 적용됩니다(IndividualAuth 또는 OrganizationalAuth가 사용되는 경우 no-https는 무시됨).", + "symbols/iisHttpPort/description": "launchSettings.json의 IIS Express HTTP 엔드포인트에 사용할 포트 번호입니다.", + "symbols/iisHttpsPort/description": "launchSettings.json의 IIS Express 엔드포인트에 사용할 포트 번호입니다. 이 옵션은 매개 변수 no-https가 사용되지 않은 경우에만 적용됩니다(IndividualAuth 또는 OrganizationalAuth가 사용되는 경우 no-https는 무시됨).", + "symbols/PWA/displayName": "프로그레시브 웹 애플리케이션(_P)", + "symbols/PWA/description": "지정된 경우 설치 및 오프라인 사용을 지원하는 PWA(프로그레시브 웹 응용 프로그램)를 생성합니다.", + "symbols/IncludeSampleContent/displayName": "샘플 페이지 포함(_I)", + "symbols/IncludeSampleContent/description": "기본 사용 패턴을 보여주기 위해 샘플 페이지 및 스타일을 추가할지 여부를 구성합니다.", + "symbols/Empty/description": "기본 사용 패턴을 보여주는 샘플 페이지 및 스타일을 생략할지 여부를 구성합니다.", + "symbols/NoHttps/description": "HTTPS를 끌지 여부입니다. 이 옵션은 Individual, IndividualB2C, SingleOrg 또는 MultiOrg가 --auth에 사용되지 않는 경우에만 적용됩니다.", + "symbols/UseLocalDB/description": "SQLite 대신 LocalDB를 사용할지 여부입니다. 이 옵션은 --auth Individual 또는 --auth IndividualB2C가 지정된 경우에만 적용됩니다.", + "symbols/CalledApiUrl/description": "웹앱에서 호출할 API의 URL입니다. 이 옵션은 --auth SingleOrg, --auth MultiOrg 또는 --auth IndividualB2C without and ASP.NET Core 호스트가 지정된 경우에만 적용됩니다.", + "symbols/CalledApiScopes/description": "웹앱에서 API 호출을 요청할 범위입니다. 이 옵션은 --auth SingleOrg, --auth MultiOrg 또는 --auth IndividualB2C without and ASP.NET Core 호스트가 지정된 경우에만 적용됩니다.", + "symbols/UseProgramMain/displayName": "최상위 문 사용 안 함(_T)", + "symbols/UseProgramMain/description": "최상위 문 대신 명시적 Program 클래스 및 Main 메서드를 생성할지 여부입니다.", + "postActions/restore/description": "이 프로젝트에 필요한 NuGet 패키지를 복원합니다.", + "postActions/restore/manualInstructions/default/text": "'dotnet restore' 실행", + "postActions/open-file/description": "편집기에서 Readme.txt를 엽니다." +} diff --git a/src/Templates/templates/componentswebassembly-csharp-8/.template.config/localize/templatestrings.pl.json b/src/Templates/templates/componentswebassembly-csharp-8/.template.config/localize/templatestrings.pl.json new file mode 100644 index 0000000000..f13593728c --- /dev/null +++ b/src/Templates/templates/componentswebassembly-csharp-8/.template.config/localize/templatestrings.pl.json @@ -0,0 +1,44 @@ +{ + "author": "Microsoft", + "name": "Fluent Autonomiczna aplikacja zestawu WebAssembly platformy Blazor", + "description": "Szablon projektu służący do tworzenia aplikacji Blazor działającej na zestawie WebAssembly. Ten szablon może być używany dla aplikacji internetowych z rozbudowanymi, dynamicznymi interfejsami użytkowników.", + "symbols/Framework/description": "Platforma docelowa dla tego projektu.", + "symbols/Framework/choices/net8.0/description": "Docelowa platforma net8.0", + "symbols/skipRestore/description": "Jeśli ta opcja jest określona, pomija automatyczne przywracanie projektu podczas tworzenia.", + "symbols/auth/choices/None/description": "Bez uwierzytelniania", + "symbols/auth/choices/Individual/description": "Uwierzytelnianie indywidualne", + "symbols/auth/choices/IndividualB2C/description": "Indywidualne uwierzytelnianie za pomocą usługi Azure AD B2C", + "symbols/auth/choices/SingleOrg/description": "Uwierzytelnianie organizacyjne dla pojedynczej dzierżawy", + "symbols/auth/description": "Typ uwierzytelniania, który ma zostać użyty.", + "symbols/Authority/description": "Urząd dostawcy OIDC (użyj z autonomicznym indywidualnym uwierzytelnianym).", + "symbols/AAdB2CInstance/description": "Wystąpienie usługi Azure Active Directory B2C do nawiązania połączenia (użyj z uwierzytelnianym IndividualB2C).", + "symbols/SignUpSignInPolicyId/description": "Identyfikator zasad logowania i rejestracji dla tego projektu (użyj uwierzytelniania IndividualB2C).", + "symbols/AADInstance/description": "Wystąpienie usługi Azure Active Directory do nawiązania połączenia (użyj z uwierzytelnianym SingleOrg ).", + "symbols/ClientId/description": "Identyfikator klienta dla tego projektu (używany z uwierzytelnianiem IndividualB2C, SingleOrg lub Individual w scenariuszach autonomicznych).", + "symbols/Domain/description": "Domena dzierżawy katalogu (użyj uwierzytelniania SingleOrg lub IndividualB2C).", + "symbols/AppIDUri/description": "Identyfikator URI identyfikatora aplikacji dla interfejsu API serwera, który chcemy wywołać (użyj z uwierzytelnianym SingleOrg lub IndividualB2C).", + "symbols/APIClientId/description": "Identyfikator klienta dla interfejsu API hostowanego przez serwer (używany z elementem IndividualB2C, SingleOrg).", + "symbols/DefaultScope/description": "Zakres interfejsu API, który klient musi zażądać w celu aprowizacji tokenu dostępu. (użyj z uwierzytelnianiem singleB2C, SingleOrg).", + "symbols/TenantId/description": "Identyfikator TenantId katalogu, z którym ma zostać nawiązane połączenie (użyj uwierzytelniania SingleOrg).", + "symbols/OrgReadAccess/description": "Określa, czy zezwolić tej aplikacji na dostęp do odczytu do katalogu (dotyczy tylko uwierzytelniania SingleOrg).", + "symbols/UserSecretsId/description": "Identyfikator do użycia na potrzeby wpisów tajnych (używany z funkcją OrgReadAccess lub indywidualnym uwierzytelnianiem).", + "symbols/ExcludeLaunchSettings/description": "Określa, czy wykluczyć plik launchSettings.json z wygenerowanego szablonu.", + "symbols/kestrelHttpPort/description": "Numer portu do użycia dla punktu końcowego HTTP w pliku launchSettings.json.", + "symbols/kestrelHttpsPort/description": "Numer portu do użycia dla punktu końcowego HTTPS w pliku launchSettings.json. Ta opcja ma zastosowanie tylko wtedy, gdy parametr no-https nie jest używany (parametr no-https zostanie zignorowany, jeśli zostanie użyte uwierzytelnianie IndividualAuth lub OrganizationalAuth).", + "symbols/iisHttpPort/description": "Numer portu do użycia dla punktu końcowego HTTP usług IIS Express w pliku launchSettings.json.", + "symbols/iisHttpsPort/description": "Numer portu do użycia dla punktu końcowego HTTPS usług IIS Express w pliku launchSettings.json. Ta opcja ma zastosowanie tylko wtedy, gdy nie jest używany parametr no-https (jeśli zostanie użyte uwierzytelnianie IndividualAuth lub OrganizationalAuth, parametr no-https zostanie zignorowana).", + "symbols/PWA/displayName": "_Progresywna aplikacja internetowa", + "symbols/PWA/description": "Jeśli zostanie określony, tworzy progresywną aplikację internetową (PWA) obsługującą instalację i używanie w trybie offline.", + "symbols/IncludeSampleContent/displayName": "_Dołącz przykładowe strony", + "symbols/IncludeSampleContent/description": "Konfiguruje, czy dodać przykładowe strony i style w celu zademonstrowania podstawowych wzorców użycia.", + "symbols/Empty/description": "Konfiguruje, czy pomijać przykładowe strony i style demonstrujące podstawowe wzorce użycia.", + "symbols/NoHttps/description": "Określa, czy wyłączyć protokół HTTPS. Ta opcja ma zastosowanie tylko wtedy, gdy dla uwierzytelniania --auth nie są używane elementy Individual, IndividualB2C, SingleOrg lub MultiOrg.", + "symbols/UseLocalDB/description": "Określa, czy używać bazy danych LocalDB zamiast oprogramowania SQLite. Ta opcja ma zastosowanie tylko wtedy, gdy określono uwierzytelnianie --auth Individual lub --auth IndividualB2C.", + "symbols/CalledApiUrl/description": "Adres URL interfejsu API do wywołania z aplikacji internetowej. Ta opcja ma zastosowanie tylko wtedy, gdy określono uwierzytelnianie --auth SingleOrg, --auth MultiOrg lub --auth IndividualB2C bez hosta platformy ASP.NET Core.", + "symbols/CalledApiScopes/description": "Zakresy do żądania wywołania interfejsu API z aplikacji internetowej. Ta opcja ma zastosowanie tylko wtedy, gdy określono uwierzytelnianie --auth SingleOrg, --auth MultiOrg lub --auth IndividualB2C bez hosta platformy ASP.NET Core.", + "symbols/UseProgramMain/displayName": "Nie używaj ins_trukcji najwyższego poziomu", + "symbols/UseProgramMain/description": "Określa, czy wygenerować jawną klasę Program i metodę Main zamiast instrukcji najwyższego poziomu.", + "postActions/restore/description": "Przywróć pakiety NuGet wymagane przez ten projekt.", + "postActions/restore/manualInstructions/default/text": "Uruchom polecenie \"dotnet restore\"", + "postActions/open-file/description": "Otwiera plik Readme.txt w edytorze" +} diff --git a/src/Templates/templates/componentswebassembly-csharp-8/.template.config/localize/templatestrings.pt-BR.json b/src/Templates/templates/componentswebassembly-csharp-8/.template.config/localize/templatestrings.pt-BR.json new file mode 100644 index 0000000000..2163431d40 --- /dev/null +++ b/src/Templates/templates/componentswebassembly-csharp-8/.template.config/localize/templatestrings.pt-BR.json @@ -0,0 +1,44 @@ +{ + "author": "Microsoft", + "name": "Aplicativo Autônomo Fluent Blazor WebAssembly", + "description": "Um modelo de projeto para criar um aplicativo Blazor que é executado no WebAssembly. Esse modelo pode ser usado para aplicativos Web com UIs (interfaces do usuário) completas e dinâmicas.", + "symbols/Framework/description": "A estrutura de destino do projeto.", + "symbols/Framework/choices/net8.0/description": "net8.0 de destino", + "symbols/skipRestore/description": "Se especificado, ignora a restauração automática do projeto sendo criado.", + "symbols/auth/choices/None/description": "Sem autenticação", + "symbols/auth/choices/Individual/description": "Autenticação individual", + "symbols/auth/choices/IndividualB2C/description": "Autenticação individual com Azure AD B2C", + "symbols/auth/choices/SingleOrg/description": "Autenticação organizacional para um único locatário", + "symbols/auth/description": "O tipo de autenticação a ser usado", + "symbols/Authority/description": "A autoridade do provedor OIDC (use com autenticação individual autônoma).", + "symbols/AAdB2CInstance/description": "A instância do Azure Active Directory B2C à qual se conectar (use com autenticação IndividualB2C).", + "symbols/SignUpSignInPolicyId/description": "O ID da política de login e inscrição para este projeto (use com autenticação IndividualB2C).", + "symbols/AADInstance/description": "A instância do Azure Active Directory à qual se conectar (use com autenticação SingleOrg).", + "symbols/ClientId/description": "O ID do cliente para este projeto (use com autenticação IndividualB2C, SingleOrg ou Individual em cenários autônomos).", + "symbols/Domain/description": "O domínio para o locatário do diretório (use com autenticação SingleOrg ou IndividualB2C).", + "symbols/AppIDUri/description": "O App ID Uri para a API do servidor que queremos chamar (use com autenticação SingleOrg ou IndividualB2C).", + "symbols/APIClientId/description": "O ID do cliente para a API que o servidor hospeda (use com IndividualB2C, SingleOrg).", + "symbols/DefaultScope/description": "O escopo da API que o cliente precisa solicitar para provisionar um token de acesso. (use com IndividualB2C, SingleOrg).", + "symbols/TenantId/description": "O ID TenantId do diretório ao qual se conectar (use com autenticação SingleOrg).", + "symbols/OrgReadAccess/description": "Se deve ou não permitir que este aplicativo tenha acesso de leitura ao diretório (aplica-se apenas a SingleOrg).", + "symbols/UserSecretsId/description": "O ID a ser usado para segredos (use com OrgReadAccess ou Autenticação individual).", + "symbols/ExcludeLaunchSettings/description": "Se deve excluir launchSettings.json do modelo gerado.", + "symbols/kestrelHttpPort/description": "Número da porta a ser usada para o ponto de extremidade HTTP em launchSettings.json.", + "symbols/kestrelHttpsPort/description": "Número da porta a ser usada para o ponto de extremidade HTTPS em launchSettings.json. Essa opção só é aplicável quando o parâmetro no-https não é usado (no-https será ignorado se IndividualAuth ou OrganizationalAuth for usado).", + "symbols/iisHttpPort/description": "Número da porta a ser usada para o ponto de extremidade HTTP do IIS Express em launchSettings.json.", + "symbols/iisHttpsPort/description": "Número da porta a ser usada para o ponto de extremidade HTTPS do IIS Express em launchSettings.json. Essa opção só é aplicável quando o parâmetro no-https não é usado (no-https será ignorado se IndividualAuth ou OrganizationalAuth for usado).", + "symbols/PWA/displayName": "_Aplicativo da Web Progressivo", + "symbols/PWA/description": "Se especificado, produz um Progressive Web Application (PWA) com suporte para instalação e uso offline.", + "symbols/IncludeSampleContent/displayName": "_Incluir páginas de amostra", + "symbols/IncludeSampleContent/description": "Configura se deseja adicionar páginas de amostra e estilo para demonstrar padrões de uso básicos.", + "symbols/Empty/description": "Configura a omissão de páginas de amostra e estilo que demonstram padrões básicos de uso.", + "symbols/NoHttps/description": "Se o HTTPS deve ser desativado. Essa opção se aplica somente se Individual, IndividualB2C, SingleOrg ou MultiOrg não forem usados para --auth.", + "symbols/UseLocalDB/description": "Se deve usar LocalDB em vez de SQLite. Esta opção só se aplica se --auth Individual ou --auth IndividualB2C for especificado.", + "symbols/CalledApiUrl/description": "URL da API para chamar do aplicativo Web. Essa opção só se aplica se --auth SingleOrg, --auth MultiOrg ou --auth IndividualB2C sem um host ASP.NET Core for especificado.", + "symbols/CalledApiScopes/description": "Escopos a serem solicitados para chamar a API do aplicativo Web. Essa opção só se aplica se --auth SingleOrg, --auth MultiOrg ou --auth IndividualB2C sem um host ASP.NET Core for especificado.", + "symbols/UseProgramMain/displayName": "Não use ins_truções de nível superior", + "symbols/UseProgramMain/description": "Se deve gerar uma classe de Programa explícita e um método principal em vez de instruções de nível superior.", + "postActions/restore/description": "Restaure os pacotes NuGet exigidos por este projeto.", + "postActions/restore/manualInstructions/default/text": "Executar 'dotnet restore'", + "postActions/open-file/description": "Abre Readme.txt no editor" +} diff --git a/src/Templates/templates/componentswebassembly-csharp-8/.template.config/localize/templatestrings.ru.json b/src/Templates/templates/componentswebassembly-csharp-8/.template.config/localize/templatestrings.ru.json new file mode 100644 index 0000000000..a2eff1d9df --- /dev/null +++ b/src/Templates/templates/componentswebassembly-csharp-8/.template.config/localize/templatestrings.ru.json @@ -0,0 +1,44 @@ +{ + "author": "Майкрософт", + "name": "Изолированное приложение Fluent Blazor WebAssembly", + "description": "Шаблон проекта для создания приложения Blazor, которое запускается в WebAssembly. Этот шаблон можно использовать для веб-приложений с полнофункциональными динамическими пользовательскими интерфейсами.", + "symbols/Framework/description": "Целевая платформа для проекта.", + "symbols/Framework/choices/net8.0/description": "Целевая net8.0", + "symbols/skipRestore/description": "Если установлено, автоматическое восстановление проекта при создании пропускается.", + "symbols/auth/choices/None/description": "Без проверки подлинности", + "symbols/auth/choices/Individual/description": "Индивидуальная проверка подлинности", + "symbols/auth/choices/IndividualB2C/description": "Индивидуальная проверка подлинности в Azure AD B2C", + "symbols/auth/choices/SingleOrg/description": "Проверка подлинности в организации для одного клиента", + "symbols/auth/description": "Тип используемой проверки подлинности", + "symbols/Authority/description": "Полномочия поставщика OIDC (используются с автономной проверкой подлинности Individual).", + "symbols/AAdB2CInstance/description": "Экземпляр Azure Active Directory B2C, к которому нужно подключиться (используется с проверкой подлинности IndividualB2C).", + "symbols/SignUpSignInPolicyId/description": "Идентификатор политики входа и регистрации для этого проекта (используется с проверкой подлинности IndividualB2C).", + "symbols/AADInstance/description": "Экземпляр Azure Active Directory, к которому нужно подключиться (используется с проверкой подлинности SingleOrg).", + "symbols/ClientId/description": "Идентификатор клиента для этого проекта (используется с проверкой подлинности IndividualB2C, SingleOrg или Individual в автономных сценариях).", + "symbols/Domain/description": "Домен для клиента каталога (используется с проверкой подлинности SingleOrg или IndividualB2C).", + "symbols/AppIDUri/description": "URI идентификатора приложения для API сервера, который требуется вызвать (используется с проверкой подлинности SingleOrg или IndividualB2C).", + "symbols/APIClientId/description": "Идентификатор клиента для API, размещенного на сервере (используется с IndividualB2C, SingleOrg).", + "symbols/DefaultScope/description": "Область API, необходимая клиенту для запроса на подготовку маркера доступа (используется с IndividualB2C, SingleOrg).", + "symbols/TenantId/description": "Идентификатор TenantId каталога, к которому нужно подключиться (используется с проверкой подлинности SingleOrg).", + "symbols/OrgReadAccess/description": "Следует ли предоставлять этому приложению доступ на чтение к каталогу (применяется только к SingleOrg).", + "symbols/UserSecretsId/description": "Идентификатор, используемый для секретов (используется с проверкой подлинности OrgReadAccess или Individual).", + "symbols/ExcludeLaunchSettings/description": "Следует ли исключить launchSettings.json из созданного шаблона.", + "symbols/kestrelHttpPort/description": "Номер порта, используемый для конечной точки HTTP в launchSettings.json.", + "symbols/kestrelHttpsPort/description": "Номер порта, используемый для конечной точки HTTPS в launchSettings.json. Этот параметр применим только в том случае, если no-https не используется (при использовании IndividualAuth или OrganizationalAuth no-https игнорируется).", + "symbols/iisHttpPort/description": "Номер порта, используемый для конечной точки HTTP IIS Express в launchSettings.json.", + "symbols/iisHttpsPort/description": "Номер порта, используемый для конечной точки HTTPS IIS Express в launchSettings.json. Этот параметр применим только в том случае, если no-https не используется (при использовании IndividualAuth или OrganizationalAuth no-https игнорируется).", + "symbols/PWA/displayName": "_Прогрессивное веб-приложение", + "symbols/PWA/description": "Если указывается, используется для создания прогрессивного веб-приложения (PWA), поддерживающего установку и автономное использование.", + "symbols/IncludeSampleContent/displayName": "_Включить примеры страниц", + "symbols/IncludeSampleContent/description": "Настраивает, следует ли добавлять примеры страниц и стили для демонстрации базовых шаблонов использования.", + "symbols/Empty/description": "Настраивает, следует ли пропускать примеры страниц и стили, демонстрирующие базовые шаблоны использования.", + "symbols/NoHttps/description": "Следует ли отключить HTTPS. Этот параметр применяется, только если для --auth не используются Individual, IndividualB2C, SingleOrg или MultiOrg.", + "symbols/UseLocalDB/description": "Следует ли использовать LocalDB вместо SQLite. Этот параметр применяется, только если указывается --auth Individual или --auth IndividualB2C.", + "symbols/CalledApiUrl/description": "URL-адрес API для вызова из веб-приложения. Этот параметр применяется, только если указывается --auth SingleOrg, --auth MultiOrg или --auth IndividualB2C без узла ASP.NET Core.", + "symbols/CalledApiScopes/description": "Области для запроса вызова API из веб-приложения. Этот параметр применяется, только если указывается --auth SingleOrg, --auth MultiOrg или --auth IndividualB2C без узла ASP.NET Core.", + "symbols/UseProgramMain/displayName": "Не использовать _операторы верхнего уровня", + "symbols/UseProgramMain/description": "Следует ли создавать явный класс Program и метод Main вместо операторов верхнего уровня.", + "postActions/restore/description": "Восстановление пакетов NuGet, необходимых для этого проекта.", + "postActions/restore/manualInstructions/default/text": "Выполнить команду \"dotnet restore\"", + "postActions/open-file/description": "Открывает Readme.txt в редакторе" +} diff --git a/src/Templates/templates/componentswebassembly-csharp-8/.template.config/localize/templatestrings.tr.json b/src/Templates/templates/componentswebassembly-csharp-8/.template.config/localize/templatestrings.tr.json new file mode 100644 index 0000000000..e7064d69a0 --- /dev/null +++ b/src/Templates/templates/componentswebassembly-csharp-8/.template.config/localize/templatestrings.tr.json @@ -0,0 +1,44 @@ +{ + "author": "Microsoft", + "name": "Fluent Blazor WebAssembly Tek Başına Uygulaması", + "description": "WebAssembly üzerinde çalışan bir Blazor uygulaması oluşturmaya yönelik proje şablonu. Bu şablon, zengin dinamik kullanıcı arabirimlerine (UI) sahip web uygulamaları için kullanılabilir.", + "symbols/Framework/description": "Projenin hedef çerçevesi.", + "symbols/Framework/choices/net8.0/description": "Hedef net8.0", + "symbols/skipRestore/description": "Belirtilirse, oluşturma sırasında projenin otomatik geri yüklenmesini atlar.", + "symbols/auth/choices/None/description": "Kimlik doğrulaması yok", + "symbols/auth/choices/Individual/description": "Bireysel kimlik doğrulaması", + "symbols/auth/choices/IndividualB2C/description": "Azure AD B2C ile bireysel kimlik doğrulaması", + "symbols/auth/choices/SingleOrg/description": "Tek bir kiracı için kuruluş kimlik doğrulaması", + "symbols/auth/description": "Kullanılacak kimlik doğrulaması türü", + "symbols/Authority/description": "OIDC sağlayıcısının yetkisi (tek başına Bireysel kimlik doğrulaması ile kullanın).", + "symbols/AAdB2CInstance/description": "Bağlanılacak Azure Active Directory B2C örneği (IndividualB2C kimlik doğrulaması ile kullanın).", + "symbols/SignUpSignInPolicyId/description": "Bu proje için oturum açma ve kaydolma ilkesi kimliği (IndividualB2C kimlik doğrulaması ile kullanın).", + "symbols/AADInstance/description": "Bağlanılacak Azure Active Directory örneği (SingleOrg kimlik doğrulaması ile kullanın).", + "symbols/ClientId/description": "Bu projenin İstemci Kimliği (tek başına senaryolarda IndividualB2C, SingleOrg veya Bireysel kimlik doğrulaması ile kullanın).", + "symbols/Domain/description": "Dizin kiracısı için etki alanı (SingleOrg veya IndividualB2C kimlik doğrulamasıyla kullanın).", + "symbols/AppIDUri/description": "Çağrılmak istenen sunucu API'sinin Uygulama Kimliği Uri'si (SingleOrg veya IndividualB2C kimlik doğrulaması ile kullanın).", + "symbols/APIClientId/description": "Sunucunun barındırmış olduğu API için İstemci Kimliği (IndividualB2C, SingleOrg ile kullanın).", + "symbols/DefaultScope/description": "İstemcinin erişim belirteci sağlamak amacıyla istekte bulunmak için ihtiyacı olan API kapsamı. (IndividualB2C, SingleOrg ile kullanın).", + "symbols/TenantId/description": "Bağlanılacak dizinin TenantId kimliği (SingleOrg kimlik doğrulamasıyla kullanın).", + "symbols/OrgReadAccess/description": "Bu uygulamanın dizin okuma erişimine izin verilip verilmeyeceği (yalnızca SingleOrg için geçerlidir).", + "symbols/UserSecretsId/description": "Gizli diziler için kullanılan kimlik (OrgReadAccess veya Bireysel kimlik doğrulaması ile kullanın).", + "symbols/ExcludeLaunchSettings/description": "launchSettings.json öğesinin oluşturulan şablondan dışlanıp dışlanmayacağı.", + "symbols/kestrelHttpPort/description": "launchSettings.json içinde HTTP uç noktası için kullanılacak bağlantı noktası numarası.", + "symbols/kestrelHttpsPort/description": "launchSettings.json içinde HTTPS uç noktası için kullanılacak bağlantı noktası numarası. Bu seçenek yalnızca no-https parametresi kullanılmazsa uygulanabilir (IndividualAuth veya OrganizationalAuth kullanılırsa no-https yoksayılır).", + "symbols/iisHttpPort/description": "launchSettings.json içinde IIS Express HTTP uç noktası için kullanılacak bağlantı noktası numarası.", + "symbols/iisHttpsPort/description": "launchSettings.json içinde IIS Express HTTPS uç noktası için kullanılacak bağlantı noktası numarası. Bu seçenek yalnızca no-https parametresi kullanılmazsa uygulanabilir (IndividualAuth veya OrganizationalAuth kullanılırsa no-https yoksayılır).", + "symbols/PWA/displayName": "_Aşamalı Web Uygulaması", + "symbols/PWA/description": "Belirtilmişse, yükleme ve çevrimdışı kullanımı destekleyen bir Aşamalı Web Uygulaması (PWA) oluşturur.", + "symbols/IncludeSampleContent/displayName": "Örnek _sayfalar ekle", + "symbols/IncludeSampleContent/description": "Temel kullanım düzenlerini göstermek için örnek sayfaların ve stil oluşturma özelliklerinin eklenip eklenmeyeceğini yapılandırır.", + "symbols/Empty/description": "Temel kullanım düzenlerini gösteren örnek sayfaların ve stil oluşturma özelliklerinin atlanıp atlanmayacağını yapılandırır.", + "symbols/NoHttps/description": "HTTPS'nin kapatılıp kapatılmayacağı. Bu seçenek yalnızca Bireysel, IndividualB2C, SingleOrg veya MultiOrg -- auth için kullanılmazsa geçerlidir.", + "symbols/UseLocalDB/description": "SQLite yerine LocalDB'nin kullanılıp kullanılmayacağı. Bu seçenek yalnızca --auth Individual veya --auth IndividualB2C belirtilirse geçerlidir.", + "symbols/CalledApiUrl/description": "Web uygulamasından çağrılan API URL'si. Bu seçenek yalnızca --auth SingleOrg, --auth MultiOrg veya --auth IndividualB2C without ve ASP.NET Core konağı belirtilirse geçerlidir.", + "symbols/CalledApiScopes/description": "Web uygulamasından API çağırma istek kapsamları. Bu seçenek yalnızca --auth SingleOrg, --auth MultiOrg veya --auth IndividualB2C without ve ASP.NET Core konağı belirtilirse geçerlidir.", + "symbols/UseProgramMain/displayName": "_Üst düzey deyimler kullanmayın", + "symbols/UseProgramMain/description": "Üst düzey deyimler yerine açık bir Program sınıfı ve Ana yöntem oluşturup oluşturulmayacağını belirtir.", + "postActions/restore/description": "Bu projenin gerektirdiği NuGet paketlerini geri yükleyin.", + "postActions/restore/manualInstructions/default/text": "'dotnet restore' çalıştır", + "postActions/open-file/description": "Benioku.txt dosyasını düzenleyicide açar" +} diff --git a/src/Templates/templates/componentswebassembly-csharp-8/.template.config/localize/templatestrings.zh-Hans.json b/src/Templates/templates/componentswebassembly-csharp-8/.template.config/localize/templatestrings.zh-Hans.json new file mode 100644 index 0000000000..81b5c94b1e --- /dev/null +++ b/src/Templates/templates/componentswebassembly-csharp-8/.template.config/localize/templatestrings.zh-Hans.json @@ -0,0 +1,44 @@ +{ + "author": "Microsoft", + "name": "Fluent Blazor WebAssembly 独立应用", + "description": "用于创建在 WebAssembly 上运行的 Blazor 应用的项目模板。此模板可用于具有丰富动态用户界面(UI)的 Web 应用。", + "symbols/Framework/description": "项目的目标框架。", + "symbols/Framework/choices/net8.0/description": "目标 net8.0", + "symbols/skipRestore/description": "如果指定,则在创建时跳过项目的自动还原。", + "symbols/auth/choices/None/description": "无身份验证", + "symbols/auth/choices/Individual/description": "个人身份验证", + "symbols/auth/choices/IndividualB2C/description": "使用 Azure AD B2C 进行个人身份验证", + "symbols/auth/choices/SingleOrg/description": "单个租户的组织身份验证", + "symbols/auth/description": "要使用的身份验证类型", + "symbols/Authority/description": "OIDC 提供程序的颁发机构(与独立个人身份验证一起使用)。", + "symbols/AAdB2CInstance/description": "要连接到的 Azure Active Directory B2C 实例(与 IndividualB2C 身份验证一起使用)。", + "symbols/SignUpSignInPolicyId/description": "此项目的登录和注册策略 ID (与 IndividualB2C 身份验证一起使用)。", + "symbols/AADInstance/description": "要连接到的 Azure Active Directory 实例(与 SingleOrg 身份验证一起使用)。", + "symbols/ClientId/description": "此项目的客户端 ID (在独立方案中与 IndividualB2C、SingleOrg 或个人身份验证一起使用)。", + "symbols/Domain/description": "目录租户的域(与 SingleOrg 或 IndividualB2C 身份验证一起使用)。", + "symbols/AppIDUri/description": "要调用的服务器 API 的应用 ID URI (与 SingleOrg 或 IndividualB2C 身份验证一起使用)。", + "symbols/APIClientId/description": "服务器托管的 API 的客户端 ID (与 IndividualB2C、SingleOrg 一起使用)。", + "symbols/DefaultScope/description": "客户端预配访问令牌需要请求的 API 范围。(与 IndividualB2C、SingleOrg 一起使用)。", + "symbols/TenantId/description": "要连接到的目录的 TenantId ID (与 SingleOrg 身份验证一起使用)。", + "symbols/OrgReadAccess/description": "是否允许此应用程序对目录进行读取访问(仅适用于 SingleOrg)。", + "symbols/UserSecretsId/description": "用于机密的 ID (与 OrgReadAccess 或个人身份验证一起使用)。", + "symbols/ExcludeLaunchSettings/description": "是否从生成的模板中排除 launchSettings.json。", + "symbols/kestrelHttpPort/description": "要用于 launchSettings.json 中 HTTP 终结点的端口号。", + "symbols/kestrelHttpsPort/description": "要用于 launchSettings.json 中 HTTPS 终结点的端口号。仅当不使用参数 no-https 时,此选项才适用(如果使用 IndividualAuth 或 OrganizationalAuth,则将忽略 no-https)。", + "symbols/iisHttpPort/description": "要用于 launchSettings.json 中 IIS Express HTTP 终结点的端口号。", + "symbols/iisHttpsPort/description": "要用于 launchSettings.json 中 IIS Express HTTPS 终结点的端口号。仅当不使用参数 no-https 时,此选项才适用(如果使用 IndividualAuth 或 OrganizationalAuth,则将忽略 no-https)。", + "symbols/PWA/displayName": "渐进式 Web 应用程序(_P)", + "symbols/PWA/description": "如果指定,则生成支持安装和脱机使用的渐进式 Web 应用程序(PWA)。", + "symbols/IncludeSampleContent/displayName": "包含示例页(_I)", + "symbols/IncludeSampleContent/description": "配置是否添加示例页和样式以演示基本使用模式。", + "symbols/Empty/description": "配置是否忽略演示基本使用模式的示例页和样式。", + "symbols/NoHttps/description": "是否禁用 HTTPS。仅当 Individual、IndividualB2C、SingleOrg 或 MultiOrg 不用于 --auth 时,此选项才适用。", + "symbols/UseLocalDB/description": "是否使用 LocalDB 而不是 SQLite。仅当指定了 --auth Individual 或 --auth IndividualB2C 时,此选项才适用。", + "symbols/CalledApiUrl/description": "要从 Web 应用调用的 API 的 URL。仅当指定了 --auth SingleOrg、--auth MultiOrg 或不带 ASP.NET Core 主机的 --auth IndividualB2C 时,此选项才适用。", + "symbols/CalledApiScopes/description": "请求从 Web 应用调用 API 的范围。仅当指定了 --auth SingleOrg、--auth MultiOrg 或不带 ASP.NET Core 主机的 --auth IndividualB2C 时,此选项才适用。", + "symbols/UseProgramMain/displayName": "不使用顶级语句(_T)", + "symbols/UseProgramMain/description": "是否生成显式程序类和主方法,而不是顶级语句。", + "postActions/restore/description": "还原此项目所需的 NuGet 包。", + "postActions/restore/manualInstructions/default/text": "运行 \"dotnet restore\"", + "postActions/open-file/description": "在编辑器中打开 Readme.txt" +} diff --git a/src/Templates/templates/componentswebassembly-csharp-8/.template.config/localize/templatestrings.zh-Hant.json b/src/Templates/templates/componentswebassembly-csharp-8/.template.config/localize/templatestrings.zh-Hant.json new file mode 100644 index 0000000000..8550047e67 --- /dev/null +++ b/src/Templates/templates/componentswebassembly-csharp-8/.template.config/localize/templatestrings.zh-Hant.json @@ -0,0 +1,44 @@ +{ + "author": "Microsoft", + "name": "Fluent Blazor WebAssembly 獨立應用程式", + "description": "用來建立在 WebAssembly 上執行之 Blazor 應用程式的專案範本。此範本可用於具有豐富動態使用者介面 (UI) 的 Web 應用程式。", + "symbols/Framework/description": "專案的目標 Framework。", + "symbols/Framework/choices/net8.0/description": "目標 net8.0", + "symbols/skipRestore/description": "若指定,會在建立時跳過專案的自動還原。", + "symbols/auth/choices/None/description": "沒有驗證", + "symbols/auth/choices/Individual/description": "個別驗證", + "symbols/auth/choices/IndividualB2C/description": "具有 Azure AD B2C 的個別驗證", + "symbols/auth/choices/SingleOrg/description": "單一租用戶的組織驗證", + "symbols/auth/description": "要使用的驗證類型。", + "symbols/Authority/description": "OIDC 提供者的授權(搭配獨立個別驗證使用)。", + "symbols/AAdB2CInstance/description": "要連線的 Azure Active Directory B2C 執行個體 (搭配 IndividualB2C 驗證使用)。", + "symbols/SignUpSignInPolicyId/description": "此專案的登入和註冊原則識別碼 (搭配 IndividualB2C 驗證使用)。", + "symbols/AADInstance/description": "要連線到的 Azure Active Directory 執行個體 (搭配 SingleOrg 驗證使用)。", + "symbols/ClientId/description": "此專案的用戶端識別碼 (搭配獨立案例中的 IndividualB2C、SingleOrg 或 Individual 驗證使用)。", + "symbols/Domain/description": "目錄租用戶的網域 (搭配 SingleOrg 或 IndividualB2C 驗證使用)。", + "symbols/AppIDUri/description": "要呼叫的伺服器 API App ID URI (搭配 SingleOrg 或 IndividualB2C 驗證使用)。", + "symbols/APIClientId/description": "伺服器裝載之 API 的用戶端識別碼 (搭配 IndividualB2C、SingleOrg 使用)。", + "symbols/DefaultScope/description": "用戶端佈建存取權杖所需的 API 範圍。(搭配 IndividualB2C、SingleOrg 使用)。", + "symbols/TenantId/description": "要連線到目錄的 TenantId 識別碼 (搭配 SingleOrg 驗證使用)。", + "symbols/OrgReadAccess/description": "是否要允許此應用程式讀取存取目錄 (只適用於 SingleOrg)。", + "symbols/UserSecretsId/description": "用於祕密的識別碼 (搭配 OrgReadAccess 或個別驗證使用)。", + "symbols/ExcludeLaunchSettings/description": "是否要從產生的範本排除 launchSettings.json。", + "symbols/kestrelHttpPort/description": "launchSettings.json 中 HTTP 端點要使用的連接埠號碼。", + "symbols/kestrelHttpsPort/description": "launchSettings.json 中 HTTPS 端點要使用的連接埠號碼。只有在未使用參數 no-https 時,才適用此選項 (如果使用 IndividualAuth 或 OrganizationalAuth,則會忽略 no-https)。", + "symbols/iisHttpPort/description": "launchSettings.json 中 IIS Express HTTP 端點要使用的連接埠號碼。", + "symbols/iisHttpsPort/description": "launchSettings.json 中 IIS Express HTTPS 端點要使用的連接埠號碼。只有在未使用參數 no-https 時,才適用此選項 (如果使用 IndividualAuth 或 OrganizationalAuth,則會忽略 no-https)。", + "symbols/PWA/displayName": "漸進式 Web 應用程式(_P)", + "symbols/PWA/description": "若指定,會產生漸進式 Web 應用程式 (PWA) 支援安裝與離線使用。", + "symbols/IncludeSampleContent/displayName": "包含範例頁面(_I)", + "symbols/IncludeSampleContent/description": "設定是否要新增範例頁面和樣式,以示範基本使用模式。", + "symbols/Empty/description": "設定是否要省略範例頁面和樣式,其示範基本使用模式。", + "symbols/NoHttps/description": "是否要關閉 HTTPS。只有當 Individual、IndividualB2C、SingleOrg 或 MultiOrg 未用於 --auth 時,才適用此選項。", + "symbols/UseLocalDB/description": "是否使用 LocalDB 而非 SQLite。只有在已指定 --auth Individual 或 --auth IndividualB2C 時,才適用此選項。", + "symbols/CalledApiUrl/description": "要從 Web 應用程式呼叫的 API URL。只有在已指定 --auth SingleOrg、--auth MultiOrg 或 --auth IndividualB2C 時 (不含 ASP.NET Core 主機),才適用此選項。", + "symbols/CalledApiScopes/description": "要求從 Web 應用程式呼叫 API 的範圍。只有在已指定 --auth SingleOrg、--auth MultiOrg 或 --auth IndividualB2C 時 (不含 ASP.NET Core 主機),才適用此選項。", + "symbols/UseProgramMain/displayName": "不要使用最上層陳述式(_T)", + "symbols/UseProgramMain/description": "是否要產生明確的 Program 類別和 Main 方法,而非最上層語句。", + "postActions/restore/description": "還原此專案所需的 NuGet 套件。", + "postActions/restore/manualInstructions/default/text": "執行 'dotnet restore'", + "postActions/open-file/description": "在編輯器中開啟 Readme.txt" +} diff --git a/src/Templates/templates/componentswebassembly-csharp-8/.template.config/template.json b/src/Templates/templates/componentswebassembly-csharp-8/.template.config/template.json new file mode 100644 index 0000000000..6c5b2b9206 --- /dev/null +++ b/src/Templates/templates/componentswebassembly-csharp-8/.template.config/template.json @@ -0,0 +1,495 @@ +{ + "$schema": "https://json.schemastore.org/template", + "author": "Microsoft", + "classifications": [ + "Web", + "Fluent", + "Blazor", + "WebAssembly", + "PWA" + ], + "name": "Fluent Blazor WebAssembly Standalone App", + "defaultName": "BlazorApp", + "description": "A project template for creating a Blazor app that runs on WebAssembly and uses the Fluent component library. This template can be used for web apps with rich dynamic user interfaces (UIs).", + "groupIdentity": "Microsoft.Web.Fluent.Blazor.Wasm", + "precedence": "8002", + "guids": [ + "4C26868E-5E7C-458D-82E3-040509D0C71F", + "5990939C-7E7B-4CFA-86FF-44CA5756498A", + "650B3CE7-2E93-4CC4-9F46-466686815EAA", + "0AFFA7FD-4E37-4636-AB91-3753E746DB98", + "53bc9b9d-9d6a-45d4-8429-2a2761773502" // Client ID + ], + "identity": "Microsoft.Web.Fluent.Blazor.Wasm.CSharp.8.0", + "thirdPartyNotices": "https://aka.ms/aspnetcore/8.0-third-party-notices", + "preferNameDirectory": true, + "primaryOutputs": [ + { + "path": "ComponentsWebAssembly-CSharp.csproj" + } + ], + "shortName": "fluentblazorwasm", + "sourceName": "ComponentsWebAssembly-CSharp", + "sources": [ + { + "source": "./", + "target": "./", + "exclude": [ + ".template.config/**" + ], + "copyOnly": [ + "**/wwwroot/css/**" + ], + "modifiers": [ + { + "condition": "(!PWA)", + "exclude": [ + "wwwroot/service-worker*.js", + "wwwroot/manifest.webmanifest", + "wwwroot/icon-512.png" + ] + }, + { + "condition": "(!UseProgramMain)", + "exclude": [ + "Program.Main.cs" + ] + }, + { + "condition": "(UseProgramMain)", + "exclude": [ + "Program.cs" + ], + "rename": { + "Program.Main.cs": "Program.cs" + } + }, + { + "condition": "(!IndividualLocalAuth)", + "exclude": [ + "wwwroot/appsettings.Development.json" + ] + }, + { + "condition": "(NoAuth)", + "rename": { + "Layout/MainLayout.NoAuth.razor": "Layout/MainLayout.razor" + }, + "exclude": [ + "Pages/Authentication.razor", + "Layout/LoginDisplay.razor", + "Layout/LoginDisplay.razor.css", + "Layout/MainLayout.Auth.razor", + "Layout/RedirectToLogin.razor", + "wwwroot/appsettings.Development.json", + "wwwroot/appsettings.json" + ] + }, + { + "condition": "(!NoAuth)", + "rename": { + "Layout/MainLayout.Auth.razor": "Layout/MainLayout.razor" + }, + "exclude": [ + "Layout/MainLayout.NoAuth.razor" + ] + }, + { + "condition": "(!GenerateApi)", + "rename": { + "Layout/NavMenu.NoApi.razor": "Layout/NavMenu.razor" + }, + "exclude": [ + "Pages/CallWebApi.razor", + "Layout/NavMenu.CallsWebApi.razor" + ] + }, + { + "condition": "(GenerateApi)", + "rename": { + "Layout/NavMenu.CallsWebApi.razor": "Layout/NavMenu.razor" + }, + "exclude": [ + "Layout/NavMenu.NoApi.razor" + ] + }, + { + "condition": "(ExcludeLaunchSettings)", + "exclude": [ + "Properties/launchSettings.json" + ] + }, + { + "condition": "(!SampleContent)", + "exclude": [ + "Pages/Counter.*", + "Pages/Weather.*", + "Layout/MainLayout.razor.css", + "Layout/NavMenu.*", + "wwwroot/favicon.png", + "wwwroot/sample-data/weather.json" + ] + } + ] + } + ], + "symbols": { + "Framework": { + "type": "parameter", + "description": "The target framework for the project.", + "datatype": "choice", + "choices": [ + { + "choice": "net8.0", + "description": "Target net8.0" + } + ], + "replaces": "net8.0", + "defaultValue": "net8.0" + }, + "HostIdentifier": { + "type": "bind", + "binding": "HostIdentifier" + }, + "skipRestore": { + "type": "parameter", + "datatype": "bool", + "description": "If specified, skips the automatic restore of the project on create.", + "defaultValue": "false" + }, + "auth": { + "type": "parameter", + "datatype": "choice", + "choices": [ + { + "choice": "None", + "description": "No authentication" + }, + { + "choice": "Individual", + "description": "Individual authentication" + }, + { + "choice": "IndividualB2C", + "description": "Individual authentication with Azure AD B2C" + }, + { + "choice": "SingleOrg", + "description": "Organizational authentication for a single tenant" + } + ], + "defaultValue": "None", + "description": "The type of authentication to use" + }, + "Authority": { + "type": "parameter", + "datatype": "string", + "replaces": "https://login.microsoftonline.com/", + "description": "The authority of the OIDC provider (use with standalone Individual auth)." + }, + "MissingAuthority": { + "type": "computed", + "value": "(IndividualAuth && Authority == \"https://login.microsoftonline.com/\" && ClientId == \"33333333-3333-3333-33333333333333333\")" + }, + "AAdB2CInstance": { + "type": "parameter", + "datatype": "string", + "replaces": "https:////aadB2CInstance.b2clogin.com/", + "description": "The Azure Active Directory B2C instance to connect to (use with IndividualB2C auth)." + }, + "SignUpSignInPolicyId": { + "type": "parameter", + "datatype": "string", + "defaultValue": "b2c_1_susi", + "replaces": "MySignUpSignInPolicyId", + "description": "The sign-in and sign-up policy ID for this project (use with IndividualB2C auth)." + }, + "AADInstance": { + "type": "parameter", + "datatype": "string", + "defaultValue": "https://login.microsoftonline.com/", + "replaces": "https:////login.microsoftonline.com/", + "description": "The Azure Active Directory instance to connect to (use with SingleOrg auth)." + }, + "ClientId": { + "type": "parameter", + "datatype": "string", + "replaces": "33333333-3333-3333-33333333333333333", + "description": "The Client ID for this project (use with IndividualB2C, SingleOrg or Individual auth in standalone scenarios)." + }, + "Domain": { + "type": "parameter", + "datatype": "string", + "replaces": "qualified.domain.name", + "description": "The domain for the directory tenant (use with SingleOrg or IndividualB2C auth)." + }, + "AppIDUri": { + "type": "parameter", + "datatype": "string", + "replaces": "api.id.uri", + "description": "The App ID Uri for the server API we want to call (use with SingleOrg or IndividualB2C auth)." + }, + "APIClientId": { + "type": "parameter", + "datatype": "string", + "replaces": "11111111-1111-1111-11111111111111111", + "description": "The Client ID for the API that the server hosts (use with IndividualB2C, SingleOrg)." + }, + "DefaultScope": { + "type": "parameter", + "datatype": "string", + "replaces": "api-scope", + "defaultValue": "access_as_user", + "description": "The API scope the client needs to request to provision an access token. (use with IndividualB2C, SingleOrg)." + }, + "TenantId": { + "type": "parameter", + "datatype": "string", + "replaces": "22222222-2222-2222-2222-222222222222", + "description": "The TenantId ID of the directory to connect to (use with SingleOrg auth)." + }, + "OrgReadAccess": { + "type": "parameter", + "datatype": "bool", + "defaultValue": "false", + "description": "Whether or not to allow this application read access to the directory (only applies to SingleOrg)." + }, + "UserSecretsId": { + "type": "parameter", + "datatype": "string", + "replaces": "aspnet-BlazorServerWeb-CSharp-53bc9b9d-9d6a-45d4-8429-2a2761773502", + "defaultValue": "aspnet-BlazorServerWeb-CSharp-53bc9b9d-9d6a-45d4-8429-2a2761773502", + "description": "The ID to use for secrets (use with OrgReadAccess or Individual auth)." + }, + "ExcludeLaunchSettings": { + "type": "parameter", + "datatype": "bool", + "defaultValue": "false", + "description": "Whether to exclude launchSettings.json from the generated template." + }, + "kestrelHttpPort": { + "type": "parameter", + "datatype": "integer", + "description": "Port number to use for the HTTP endpoint in launchSettings.json." + }, + "kestrelHttpPortGenerated": { + "type": "generated", + "generator": "port", + "parameters": { + "low": 5000, + "high": 5300 + } + }, + "kestrelHttpPortReplacer": { + "type": "generated", + "generator": "coalesce", + "parameters": { + "sourceVariableName": "kestrelHttpPort", + "fallbackVariableName": "kestrelHttpPortGenerated" + }, + "replaces": "5000" + }, + "kestrelHttpsPort": { + "type": "parameter", + "datatype": "integer", + "description": "Port number to use for the HTTPS endpoint in launchSettings.json. This option is only applicable when the parameter no-https is not used (no-https will be ignored if either IndividualAuth or OrganizationalAuth is used)." + }, + "kestrelHttpsPortGenerated": { + "type": "generated", + "generator": "port", + "parameters": { + "low": 7000, + "high": 7300 + } + }, + "kestrelHttpsPortReplacer": { + "type": "generated", + "generator": "coalesce", + "parameters": { + "sourceVariableName": "kestrelHttpsPort", + "fallbackVariableName": "kestrelHttpsPortGenerated" + }, + "replaces": "5001" + }, + "iisHttpPort": { + "type": "parameter", + "datatype": "integer", + "description": "Port number to use for the IIS Express HTTP endpoint in launchSettings.json." + }, + "iisHttpPortGenerated": { + "type": "generated", + "generator": "port" + }, + "iisHttpPortReplacer": { + "type": "generated", + "generator": "coalesce", + "parameters": { + "sourceVariableName": "iisHttpPort", + "fallbackVariableName": "iisHttpPortGenerated" + }, + "replaces": "8080" + }, + "iisHttpsPort": { + "type": "parameter", + "datatype": "integer", + "description": "Port number to use for the IIS Express HTTPS endpoint in launchSettings.json. This option is only applicable when the parameter no-https is not used (no-https will be ignored if either IndividualAuth or OrganizationalAuth is used)." + }, + "iisHttpsPortGenerated": { + "type": "generated", + "generator": "port", + "parameters": { + "low": 44300, + "high": 44399 + } + }, + "iisHttpsPortReplacer": { + "type": "generated", + "generator": "coalesce", + "parameters": { + "sourceVariableName": "iisHttpsPort", + "fallbackVariableName": "iisHttpsPortGenerated" + }, + "replaces": "44300" + }, + "PWA": { + "type": "parameter", + "datatype": "bool", + "defaultValue": "false", + "displayName": "_Progressive Web Application", + "description": "If specified, produces a Progressive Web Application (PWA) supporting installation and offline use." + }, + "OrganizationalAuth": { + "type": "computed", + "value": "(auth == \"SingleOrg\" || auth == \"MultiOrg\")" + }, + "MultiOrgAuth": { + "type": "computed", + "value": "(auth == \"MultiOrg\")" + }, + "SingleOrgAuth": { + "type": "computed", + "value": "(auth == \"SingleOrg\")" + }, + "IndividualLocalAuth": { + "type": "computed", + "value": "(auth == \"Individual\")" + }, + "IndividualAuth": { + "type": "computed", + "value": "(auth == \"Individual\" || auth == \"IndividualB2C\")" + }, + "IndividualB2CAuth": { + "type": "computed", + "value": "(auth == \"IndividualB2C\")" + }, + "IncludeSampleContent": { + "type": "parameter", + "datatype": "bool", + "defaultValue": "true", + "displayName": "_Include sample pages", + "description": "Configures whether to add sample pages and styling to demonstrate basic usage patterns." + }, + "Empty": { + "type": "parameter", + "datatype": "bool", + "defaultValue": "false", + "description": "Configures whether to omit sample pages and styling that demonstrate basic usage patterns." + }, + "SampleContent": { + "type": "computed", + "value": "(((IncludeSampleContent && (HostIdentifier != \"dotnetcli\" && HostIdentifier != \"dotnetcli-preview\"))) || ((!Empty && (HostIdentifier == \"dotnetcli\" || HostIdentifier == \"dotnetcli-preview\"))))" + }, + "NoAuth": { + "type": "computed", + "value": "(!(IndividualAuth || OrganizationalAuth))" + }, + "RequiresHttps": { + "type": "computed", + "value": "(OrganizationalAuth || IndividualAuth)" + }, + "HasHttpProfile": { + "type": "computed", + "value": "(!RequiresHttps)" + }, + "HasHttpsProfile": { + "type": "computed", + "value": "(RequiresHttps || !NoHttps)" + }, + "NoHttps": { + "type": "parameter", + "datatype": "bool", + "defaultValue": "false", + "description": "Whether to turn off HTTPS. This option only applies if Individual, IndividualB2C, SingleOrg, or MultiOrg aren't used for --auth." + }, + "UseLocalDB": { + "type": "parameter", + "datatype": "bool", + "defaultValue": "false", + "description": "Whether to use LocalDB instead of SQLite. This option only applies if --auth Individual or --auth IndividualB2C is specified." + }, + "copyrightYear": { + "type": "generated", + "generator": "now", + "replaces": "copyrightYear", + "parameters": { + "format": "yyyy" + } + }, + "CalledApiUrl": { + "type": "parameter", + "datatype": "string", + "replaces": "[WebApiUrl]", + "defaultValue": "https://graph.microsoft.com/v1.0", + "description": "URL of the API to call from the web app. This option only applies if --auth SingleOrg, --auth MultiOrg or --auth IndividualB2C without and ASP.NET Core host is specified." + }, + "CalledApiScopes": { + "type": "parameter", + "datatype": "string", + "replaces": "user.read", + "description": "Scopes to request to call the API from the web app. This option only applies if --auth SingleOrg, --auth MultiOrg or --auth IndividualB2C without and ASP.NET Core host is specified." + }, + "GenerateApi": { + "type": "computed", + "value": "(IndividualB2CAuth && (CalledApiUrl != \"https://graph.microsoft.com/v1.0\" || CalledApiScopes != \"user.read\"))" + }, + "UseProgramMain": { + "type": "parameter", + "datatype": "bool", + "defaultValue": "false", + "displayName": "Do not use _top-level statements", + "description": "Whether to generate an explicit Program class and Main method instead of top-level statements." + } + }, + "tags": { + "language": "C#", + "type": "project" + }, + "postActions": [ + { + "id": "restore", + "condition": "(!skipRestore)", + "description": "Restore NuGet packages required by this project.", + "manualInstructions": [ + { + "text": "Run 'dotnet restore'" + } + ], + "args": { + "files": ["ComponentsWebAssembly-CSharp.csproj"] + }, + "actionId": "210D431B-A78B-4D2F-B762-4ED3E3EA9025", + "continueOnError": true + }, + { + "id": "open-file", + "condition": "(IndividualLocalAuth && HostIdentifier != \"dotnetcli\" && HostIdentifier != \"dotnetcli-preview\")", + "description": "Opens Readme.txt in the editor", + "manualInstructions": [], + "actionId": "84C0DA21-51C8-4541-9940-6CA19AF04EE6", + "args": { + "files": "0" + }, + "continueOnError": true + } + ] +} diff --git a/src/Templates/templates/componentswebassembly-csharp/App.razor b/src/Templates/templates/componentswebassembly-csharp-8/App.razor similarity index 100% rename from src/Templates/templates/componentswebassembly-csharp/App.razor rename to src/Templates/templates/componentswebassembly-csharp-8/App.razor diff --git a/src/Templates/templates/componentswebassembly-csharp/ComponentsWebAssembly-CSharp.csproj b/src/Templates/templates/componentswebassembly-csharp-8/ComponentsWebAssembly-CSharp.csproj similarity index 53% rename from src/Templates/templates/componentswebassembly-csharp/ComponentsWebAssembly-CSharp.csproj rename to src/Templates/templates/componentswebassembly-csharp-8/ComponentsWebAssembly-CSharp.csproj index d57b2e2dc2..3203bca5e2 100644 --- a/src/Templates/templates/componentswebassembly-csharp/ComponentsWebAssembly-CSharp.csproj +++ b/src/Templates/templates/componentswebassembly-csharp-8/ComponentsWebAssembly-CSharp.csproj @@ -10,14 +10,10 @@ - - - - - - - - + + + + diff --git a/src/Templates/templates/componentswebassembly-csharp-8/Layout/LoginDisplay.razor b/src/Templates/templates/componentswebassembly-csharp-8/Layout/LoginDisplay.razor new file mode 100644 index 0000000000..6e9bd3ce75 --- /dev/null +++ b/src/Templates/templates/componentswebassembly-csharp-8/Layout/LoginDisplay.razor @@ -0,0 +1,19 @@ +@using Microsoft.AspNetCore.Components.WebAssembly.Authentication +@inject NavigationManager Navigation + + + + Hello, @context.User.Identity?.Name! + Log out + + + Log in + + + +@code{ + public void BeginLogOut() + { + Navigation.NavigateToLogout("authentication/logout"); + } +} diff --git a/src/Templates/templates/componentswebassembly-csharp-8/Layout/LoginDisplay.razor.css b/src/Templates/templates/componentswebassembly-csharp-8/Layout/LoginDisplay.razor.css new file mode 100644 index 0000000000..d2534014f6 --- /dev/null +++ b/src/Templates/templates/componentswebassembly-csharp-8/Layout/LoginDisplay.razor.css @@ -0,0 +1,3 @@ +a { + color: var(--fill-color); +} \ No newline at end of file diff --git a/src/Templates/templates/componentswebassembly-csharp-8/Layout/MainLayout.Auth.razor b/src/Templates/templates/componentswebassembly-csharp-8/Layout/MainLayout.Auth.razor new file mode 100644 index 0000000000..a2e199dca2 --- /dev/null +++ b/src/Templates/templates/componentswebassembly-csharp-8/Layout/MainLayout.Auth.razor @@ -0,0 +1,36 @@ +@inherits LayoutComponentBase + +@*#if (SampleContent) --> + + + ComponentsWebAssembly-CSharp + + + + + + + + +
+ @Body +
+
+ +
@ex.Message
+
+
+
+
+ + Documentation and demos + + About Blazor + +
+##else + +
+ @Body +
+##endif*@ diff --git a/src/Templates/templates/componentswebassembly-csharp-8/Layout/MainLayout.NoAuth.razor b/src/Templates/templates/componentswebassembly-csharp-8/Layout/MainLayout.NoAuth.razor new file mode 100644 index 0000000000..bf933f8d61 --- /dev/null +++ b/src/Templates/templates/componentswebassembly-csharp-8/Layout/MainLayout.NoAuth.razor @@ -0,0 +1,33 @@ +@inherits LayoutComponentBase + +@*#if (SampleContent) --> + + + ComponentsWebAssembly-CSharp + + + + + + +
+ @Body +
+
+ +
@ex.Message
+
+
+
+
+ + Documentation and demos + + About Blazor + +
+##else +
+ @Body +
+##endif*@ diff --git a/src/Templates/templates/componentswebassembly-csharp-8/Layout/NavMenu.CallsWebApi.razor b/src/Templates/templates/componentswebassembly-csharp-8/Layout/NavMenu.CallsWebApi.razor new file mode 100644 index 0000000000..7525b45e17 --- /dev/null +++ b/src/Templates/templates/componentswebassembly-csharp-8/Layout/NavMenu.CallsWebApi.razor @@ -0,0 +1,16 @@ + + +@code { + private bool expanded = true; +} diff --git a/src/Templates/templates/componentswebassembly-csharp-8/Layout/NavMenu.NoApi.razor b/src/Templates/templates/componentswebassembly-csharp-8/Layout/NavMenu.NoApi.razor new file mode 100644 index 0000000000..eb68069ab5 --- /dev/null +++ b/src/Templates/templates/componentswebassembly-csharp-8/Layout/NavMenu.NoApi.razor @@ -0,0 +1,15 @@ + + +@code { + private bool expanded = true; +} diff --git a/src/Templates/templates/componentswebassembly-csharp-8/Layout/RedirectToLogin.razor b/src/Templates/templates/componentswebassembly-csharp-8/Layout/RedirectToLogin.razor new file mode 100644 index 0000000000..a1cf400328 --- /dev/null +++ b/src/Templates/templates/componentswebassembly-csharp-8/Layout/RedirectToLogin.razor @@ -0,0 +1,9 @@ +@using Microsoft.AspNetCore.Components.WebAssembly.Authentication +@inject NavigationManager Navigation + +@code { + protected override void OnInitialized() + { + Navigation.NavigateToLogin("authentication/login"); + } +} diff --git a/src/Templates/templates/componentswebassembly-csharp-8/Pages/Authentication.razor b/src/Templates/templates/componentswebassembly-csharp-8/Pages/Authentication.razor new file mode 100644 index 0000000000..6c74356731 --- /dev/null +++ b/src/Templates/templates/componentswebassembly-csharp-8/Pages/Authentication.razor @@ -0,0 +1,7 @@ +@page "/authentication/{action}" +@using Microsoft.AspNetCore.Components.WebAssembly.Authentication + + +@code{ + [Parameter] public string? Action { get; set; } +} diff --git a/src/Templates/templates/componentswebassembly-csharp-8/Pages/Counter.razor b/src/Templates/templates/componentswebassembly-csharp-8/Pages/Counter.razor new file mode 100644 index 0000000000..39561f28df --- /dev/null +++ b/src/Templates/templates/componentswebassembly-csharp-8/Pages/Counter.razor @@ -0,0 +1,18 @@ +@page "/counter" + +Counter + +

Counter

+ +

Current count: @currentCount

+ +Click me + +@code { + private int currentCount = 0; + + private void IncrementCount() + { + currentCount++; + } +} diff --git a/src/Templates/templates/componentswebassembly-csharp-8/Pages/Home.razor b/src/Templates/templates/componentswebassembly-csharp-8/Pages/Home.razor new file mode 100644 index 0000000000..c3a993b9d2 --- /dev/null +++ b/src/Templates/templates/componentswebassembly-csharp-8/Pages/Home.razor @@ -0,0 +1,13 @@ +@page "/" + +Home + +

Hello, world!

+ +@*#if (MissingAuthority) +
+ Before authentication will function correctly, you must configure your provider details in Program.cs +
+ +#endif*@ +Welcome to your new Fluent Blazor app. diff --git a/src/Templates/templates/componentswebassembly-csharp-8/Pages/Weather.razor b/src/Templates/templates/componentswebassembly-csharp-8/Pages/Weather.razor new file mode 100644 index 0000000000..17ce7ad0d7 --- /dev/null +++ b/src/Templates/templates/componentswebassembly-csharp-8/Pages/Weather.razor @@ -0,0 +1,42 @@ +@page "/weather" +@inject HttpClient Http + +Weather + +

Weather

+ +

This component demonstrates fetching data from the server.

+ +@if (forecasts == null) +{ +

Loading...

+} +else +{ + + + + + + +} + +@code { + private IQueryable? forecasts; + + protected override async Task OnInitializedAsync() + { + forecasts = (await Http.GetFromJsonAsync>("sample-data/weather.json"))!.AsQueryable(); + } + + public class WeatherForecast + { + public DateOnly Date { get; set; } + + public int TemperatureC { get; set; } + + public string? Summary { get; set; } + + public int TemperatureF => 32 + (int)(TemperatureC / 0.5556); + } +} diff --git a/src/Templates/templates/componentswebassembly-csharp-8/Program.Main.cs b/src/Templates/templates/componentswebassembly-csharp-8/Program.Main.cs new file mode 100644 index 0000000000..48de417353 --- /dev/null +++ b/src/Templates/templates/componentswebassembly-csharp-8/Program.Main.cs @@ -0,0 +1,45 @@ +using Microsoft.AspNetCore.Components.Web; +using Microsoft.AspNetCore.Components.WebAssembly.Hosting; +using Microsoft.FluentUI.AspNetCore.Components; + +namespace ComponentsWebAssembly_CSharp; + +public class Program +{ + public static async Task Main(string[] args) + { + var builder = WebAssemblyHostBuilder.CreateDefault(args); + builder.RootComponents.Add("#app"); + builder.RootComponents.Add("head::after"); + + builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) }); + builder.Services.AddFluentUIComponents(); + #if(!NoAuth) + + #endif + #if (IndividualLocalAuth) + builder.Services.AddOidcAuthentication(options => + { + #if(MissingAuthority) + // Configure your authentication provider options here. + // For more information, see https://aka.ms/blazor-standalone-auth + #endif + builder.Configuration.Bind("Local", options.ProviderOptions); + }); + #endif + #if (IndividualB2CAuth) + builder.Services.AddMsalAuthentication(options => + { + builder.Configuration.Bind("AzureAdB2C", options.ProviderOptions.Authentication); + }); + #endif + #if(OrganizationalAuth) + builder.Services.AddMsalAuthentication(options => + { + builder.Configuration.Bind("AzureAd", options.ProviderOptions.Authentication); + }); + #endif + + await builder.Build().RunAsync(); + } +} diff --git a/src/Templates/templates/componentswebassembly-csharp-8/Program.cs b/src/Templates/templates/componentswebassembly-csharp-8/Program.cs new file mode 100644 index 0000000000..b49dc8dd1c --- /dev/null +++ b/src/Templates/templates/componentswebassembly-csharp-8/Program.cs @@ -0,0 +1,38 @@ +using Microsoft.AspNetCore.Components.Web; +using Microsoft.AspNetCore.Components.WebAssembly.Hosting; +using Microsoft.FluentUI.AspNetCore.Components; +using ComponentsWebAssembly_CSharp; + +var builder = WebAssemblyHostBuilder.CreateDefault(args); +builder.RootComponents.Add("#app"); +builder.RootComponents.Add("head::after"); + +builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) }); +builder.Services.AddFluentUIComponents(); +#if (!NoAuth) + +#endif +#if (IndividualLocalAuth) +builder.Services.AddOidcAuthentication(options => +{ + #if(MissingAuthority) + // Configure your authentication provider options here. + // For more information, see https://aka.ms/blazor-standalone-auth + #endif + builder.Configuration.Bind("Local", options.ProviderOptions); +}); +#endif +#if (IndividualB2CAuth) +builder.Services.AddMsalAuthentication(options => +{ + builder.Configuration.Bind("AzureAdB2C", options.ProviderOptions.Authentication); +}); +#endif +#if(OrganizationalAuth) +builder.Services.AddMsalAuthentication(options => +{ + builder.Configuration.Bind("AzureAd", options.ProviderOptions.Authentication); +}); +#endif + +await builder.Build().RunAsync(); diff --git a/src/Templates/templates/componentswebassembly-csharp-8/Properties/launchSettings.json b/src/Templates/templates/componentswebassembly-csharp-8/Properties/launchSettings.json new file mode 100644 index 0000000000..167c237f02 --- /dev/null +++ b/src/Templates/templates/componentswebassembly-csharp-8/Properties/launchSettings.json @@ -0,0 +1,33 @@ +{ + "$schema": "https://json.schemastore.org/launchsettings.json", + "profiles": { + //#if (HasHttpProfile) + "http": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}", + "applicationUrl": "http://localhost:5000", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + //#if (HasHttpsProfile) + }, + //#else + } + //#endif + //#endif + //#if (HasHttpsProfile) + "https": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}", + "applicationUrl": "https://localhost:5001;http://localhost:5000", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + //#endif + } +} diff --git a/src/Templates/templates/componentswebassembly-csharp/_Imports.razor b/src/Templates/templates/componentswebassembly-csharp-8/_Imports.razor similarity index 100% rename from src/Templates/templates/componentswebassembly-csharp/_Imports.razor rename to src/Templates/templates/componentswebassembly-csharp-8/_Imports.razor diff --git a/src/Templates/templates/componentswebassembly-csharp-8/wwwroot/appsettings.Development.json b/src/Templates/templates/componentswebassembly-csharp-8/wwwroot/appsettings.Development.json new file mode 100644 index 0000000000..daafa127f0 --- /dev/null +++ b/src/Templates/templates/componentswebassembly-csharp-8/wwwroot/appsettings.Development.json @@ -0,0 +1,8 @@ +{ + ////#if (IndividualLocalAuth) + //"Local": { + // "Authority": "https:////login.microsoftonline.com/", + // "ClientId": "33333333-3333-3333-33333333333333333" + //} + ////#endif +} diff --git a/src/Templates/templates/componentswebassembly-csharp-8/wwwroot/appsettings.json b/src/Templates/templates/componentswebassembly-csharp-8/wwwroot/appsettings.json new file mode 100644 index 0000000000..e485918eea --- /dev/null +++ b/src/Templates/templates/componentswebassembly-csharp-8/wwwroot/appsettings.json @@ -0,0 +1,27 @@ +{ + ////#if (IndividualLocalAuth) + //"Local": { + // "Authority": "https:////login.microsoftonline.com/", + // "ClientId": "33333333-3333-3333-33333333333333333" + //} + ////#endif + ////#if (IndividualB2CAuth) + //"AzureAdB2C": { + // "Authority": "https:////aadB2CInstance.b2clogin.com/qualified.domain.name/MySignUpSignInPolicyId", + // "ClientId": "33333333-3333-3333-33333333333333333", + // "ValidateAuthority": false + //} + ////#endif + ////#if (OrganizationalAuth) + ///* + The following identity settings need to be configured + before the project can be successfully executed. + For more info see https:////aka.ms/dotnet-template-ms-identity-platform + //*/ + //"AzureAd": { + // "Authority": "https:////login.microsoftonline.com/22222222-2222-2222-2222-222222222222", + // "ClientId": "33333333-3333-3333-33333333333333333", + // "ValidateAuthority": true + //} + ////#endif +} diff --git a/src/Templates/templates/componentswebassembly-csharp-8/wwwroot/css/app.css b/src/Templates/templates/componentswebassembly-csharp-8/wwwroot/css/app.css new file mode 100644 index 0000000000..2d7b409d64 --- /dev/null +++ b/src/Templates/templates/componentswebassembly-csharp-8/wwwroot/css/app.css @@ -0,0 +1,187 @@ +@import '_content/Microsoft.FluentUI.AspNetCore.Components/css/reboot.css'; + +body { + --body-font: "Segoe UI Variable", "Segoe UI", sans-serif; + font-family: var(--body-font); + font-size: var(--type-ramp-base-font-size); + line-height: var(--type-ramp-base-line-height); + margin: 0; +} + +.navmenu-icon { + display: none; +} + +.main { + min-height: calc(100dvh - 86px); + color: var(--neutral-foreground-rest); + align-items: stretch !important; +} + +.body-content { + align-self: stretch; + height: calc(100dvh - 86px) !important; + display: flex; +} + +.content { + padding: 0.5rem 1.5rem; + align-self: stretch !important; + width: 100%; +} + +footer { + background: var(--neutral-layer-4); + color: var(--neutral-foreground-rest); + align-items: center; + padding: 10px 10px; +} + + footer a { + color: var(--neutral-foreground-rest); + text-decoration: none; + } + + footer a:focus { + outline: 1px dashed; + outline-offset: 3px; + } + + footer a:hover { + text-decoration: underline; + } + +.alert { + border: 1px dashed var(--accent-fill-rest); + padding: 5px; +} + + +#blazor-error-ui { + background: lightyellow; + bottom: 0; + box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.2); + display: none; + left: 0; + padding: 0.6rem 1.25rem 0.7rem 1.25rem; + position: fixed; + width: 100%; + z-index: 1000; + margin: 20px 0; +} + + #blazor-error-ui .dismiss { + cursor: pointer; + position: absolute; + right: 0.75rem; + top: 0.5rem; + } + +.blazor-error-boundary { + background: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTYiIGhlaWdodD0iNDkiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIG92ZXJmbG93PSJoaWRkZW4iPjxkZWZzPjxjbGlwUGF0aCBpZD0iY2xpcDAiPjxyZWN0IHg9IjIzNSIgeT0iNTEiIHdpZHRoPSI1NiIgaGVpZ2h0PSI0OSIvPjwvY2xpcFBhdGg+PC9kZWZzPjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMCkiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yMzUgLTUxKSI+PHBhdGggZD0iTTI2My41MDYgNTFDMjY0LjcxNyA1MSAyNjUuODEzIDUxLjQ4MzcgMjY2LjYwNiA1Mi4yNjU4TDI2Ny4wNTIgNTIuNzk4NyAyNjcuNTM5IDUzLjYyODMgMjkwLjE4NSA5Mi4xODMxIDI5MC41NDUgOTIuNzk1IDI5MC42NTYgOTIuOTk2QzI5MC44NzcgOTMuNTEzIDI5MSA5NC4wODE1IDI5MSA5NC42NzgyIDI5MSA5Ny4wNjUxIDI4OS4wMzggOTkgMjg2LjYxNyA5OUwyNDAuMzgzIDk5QzIzNy45NjMgOTkgMjM2IDk3LjA2NTEgMjM2IDk0LjY3ODIgMjM2IDk0LjM3OTkgMjM2LjAzMSA5NC4wODg2IDIzNi4wODkgOTMuODA3MkwyMzYuMzM4IDkzLjAxNjIgMjM2Ljg1OCA5Mi4xMzE0IDI1OS40NzMgNTMuNjI5NCAyNTkuOTYxIDUyLjc5ODUgMjYwLjQwNyA1Mi4yNjU4QzI2MS4yIDUxLjQ4MzcgMjYyLjI5NiA1MSAyNjMuNTA2IDUxWk0yNjMuNTg2IDY2LjAxODNDMjYwLjczNyA2Ni4wMTgzIDI1OS4zMTMgNjcuMTI0NSAyNTkuMzEzIDY5LjMzNyAyNTkuMzEzIDY5LjYxMDIgMjU5LjMzMiA2OS44NjA4IDI1OS4zNzEgNzAuMDg4N0wyNjEuNzk1IDg0LjAxNjEgMjY1LjM4IDg0LjAxNjEgMjY3LjgyMSA2OS43NDc1QzI2Ny44NiA2OS43MzA5IDI2Ny44NzkgNjkuNTg3NyAyNjcuODc5IDY5LjMxNzkgMjY3Ljg3OSA2Ny4xMTgyIDI2Ni40NDggNjYuMDE4MyAyNjMuNTg2IDY2LjAxODNaTTI2My41NzYgODYuMDU0N0MyNjEuMDQ5IDg2LjA1NDcgMjU5Ljc4NiA4Ny4zMDA1IDI1OS43ODYgODkuNzkyMSAyNTkuNzg2IDkyLjI4MzcgMjYxLjA0OSA5My41Mjk1IDI2My41NzYgOTMuNTI5NSAyNjYuMTE2IDkzLjUyOTUgMjY3LjM4NyA5Mi4yODM3IDI2Ny4zODcgODkuNzkyMSAyNjcuMzg3IDg3LjMwMDUgMjY2LjExNiA4Ni4wNTQ3IDI2My41NzYgODYuMDU0N1oiIGZpbGw9IiNGRkU1MDAiIGZpbGwtcnVsZT0iZXZlbm9kZCIvPjwvZz48L3N2Zz4=) no-repeat 1rem/1.8rem, #b32121; + padding: 1rem 1rem 1rem 3.7rem; + color: white; +} + + .blazor-error-boundary::before { + content: "An error has occurred. " + } + +.loading-progress { + position: relative; + display: block; + width: 8rem; + height: 8rem; + margin: 20vh auto 1rem auto; +} + + .loading-progress circle { + fill: none; + stroke: #e0e0e0; + stroke-width: 0.6rem; + transform-origin: 50% 50%; + transform: rotate(-90deg); + } + + .loading-progress circle:last-child { + stroke: #1b6ec2; + stroke-dasharray: calc(3.141 * var(--blazor-load-percentage, 0%) * 0.8), 500%; + transition: stroke-dasharray 0.05s ease-in-out; + } + +.loading-progress-text { + position: absolute; + text-align: center; + font-weight: bold; + inset: calc(20vh + 3.25rem) 0 auto 0.2rem; +} + + .loading-progress-text:after { + content: var(--blazor-load-percentage-text, "Loading"); + } + +code { + color: #c02d76; +} + +@media (max-width: 600px) { + .header-gutters { + margin: 0.5rem 3rem 0.5rem 1.5rem !important; + } + + [dir="rtl"] .header-gutters { + margin: 0.5rem 1.5rem 0.5rem 3rem !important; + } + + .main { + flex-direction: column !important; + row-gap: 0 !important; + } + + nav.sitenav { + width: 100%; + height: 100%; + } + + #main-menu { + width: 100% !important; + } + + #main-menu > div:first-child:is(.expander) { + display: none; + } + + .navmenu { + width: 100%; + } + + #navmenu-toggle { + appearance: none; + } + + #navmenu-toggle ~ nav { + display: none; + } + + #navmenu-toggle:checked ~ nav { + display: block; + } + + .navmenu-icon { + cursor: pointer; + z-index: 10; + display: block; + position: absolute; + top: 15px; + left: unset; + right: 20px; + width: 20px; + height: 20px; + border: none; + } + + [dir="rtl"] .navmenu-icon { + left: 20px; + right: unset; + } +} diff --git a/src/Templates/templates/componentswebassembly-csharp-8/wwwroot/favicon.ico b/src/Templates/templates/componentswebassembly-csharp-8/wwwroot/favicon.ico new file mode 100644 index 0000000000..e189d8e579 Binary files /dev/null and b/src/Templates/templates/componentswebassembly-csharp-8/wwwroot/favicon.ico differ diff --git a/src/Templates/templates/componentswebassembly-csharp-8/wwwroot/icon-192.png b/src/Templates/templates/componentswebassembly-csharp-8/wwwroot/icon-192.png new file mode 100644 index 0000000000..166f56da76 Binary files /dev/null and b/src/Templates/templates/componentswebassembly-csharp-8/wwwroot/icon-192.png differ diff --git a/src/Templates/templates/componentswebassembly-csharp-8/wwwroot/icon-512.png b/src/Templates/templates/componentswebassembly-csharp-8/wwwroot/icon-512.png new file mode 100644 index 0000000000..c2dd4842dc Binary files /dev/null and b/src/Templates/templates/componentswebassembly-csharp-8/wwwroot/icon-512.png differ diff --git a/src/Templates/templates/componentswebassembly-csharp/wwwroot/index.html b/src/Templates/templates/componentswebassembly-csharp-8/wwwroot/index.html similarity index 100% rename from src/Templates/templates/componentswebassembly-csharp/wwwroot/index.html rename to src/Templates/templates/componentswebassembly-csharp-8/wwwroot/index.html diff --git a/src/Templates/templates/componentswebassembly-csharp-8/wwwroot/manifest.webmanifest b/src/Templates/templates/componentswebassembly-csharp-8/wwwroot/manifest.webmanifest new file mode 100644 index 0000000000..dea89b5f81 --- /dev/null +++ b/src/Templates/templates/componentswebassembly-csharp-8/wwwroot/manifest.webmanifest @@ -0,0 +1,22 @@ +{ + "name": "ComponentsWebAssembly-CSharp", + "short_name": "ComponentsWebAssembly-CSharp", + "id": "./", + "start_url": "./", + "display": "standalone", + "background_color": "#ffffff", + "theme_color": "#03173d", + "prefer_related_applications": false, + "icons": [ + { + "src": "icon-512.png", + "type": "image/png", + "sizes": "512x512" + }, + { + "src": "icon-192.png", + "type": "image/png", + "sizes": "192x192" + } + ] +} diff --git a/src/Templates/templates/componentswebassembly-csharp-8/wwwroot/sample-data/weather.json b/src/Templates/templates/componentswebassembly-csharp-8/wwwroot/sample-data/weather.json new file mode 100644 index 0000000000..b7459733f9 --- /dev/null +++ b/src/Templates/templates/componentswebassembly-csharp-8/wwwroot/sample-data/weather.json @@ -0,0 +1,27 @@ +[ + { + "date": "2022-01-06", + "temperatureC": 1, + "summary": "Freezing" + }, + { + "date": "2022-01-07", + "temperatureC": 14, + "summary": "Bracing" + }, + { + "date": "2022-01-08", + "temperatureC": -13, + "summary": "Freezing" + }, + { + "date": "2022-01-09", + "temperatureC": -16, + "summary": "Balmy" + }, + { + "date": "2022-01-10", + "temperatureC": -2, + "summary": "Chilly" + } +] diff --git a/src/Templates/templates/componentswebassembly-csharp-8/wwwroot/service-worker.js b/src/Templates/templates/componentswebassembly-csharp-8/wwwroot/service-worker.js new file mode 100644 index 0000000000..fe614daee0 --- /dev/null +++ b/src/Templates/templates/componentswebassembly-csharp-8/wwwroot/service-worker.js @@ -0,0 +1,4 @@ +// In development, always fetch from the network and do not enable offline support. +// This is because caching would make development more difficult (changes would not +// be reflected on the first load after each change). +self.addEventListener('fetch', () => { }); diff --git a/src/Templates/templates/componentswebassembly-csharp/wwwroot/service-worker.published.js b/src/Templates/templates/componentswebassembly-csharp-8/wwwroot/service-worker.published.js similarity index 100% rename from src/Templates/templates/componentswebassembly-csharp/wwwroot/service-worker.published.js rename to src/Templates/templates/componentswebassembly-csharp-8/wwwroot/service-worker.published.js diff --git a/src/Templates/templates/componentswebassembly-csharp-9/.template.config/dotnetcli.host.json b/src/Templates/templates/componentswebassembly-csharp-9/.template.config/dotnetcli.host.json new file mode 100644 index 0000000000..c68a8f73fb --- /dev/null +++ b/src/Templates/templates/componentswebassembly-csharp-9/.template.config/dotnetcli.host.json @@ -0,0 +1,107 @@ +{ + "$schema": "https://json.schemastore.org/dotnetcli.host", + "symbolInfo": { + "skipRestore": { + "longName": "no-restore", + "shortName": "" + }, + "PWA": { + "longName": "pwa" + }, + "Framework": { + "longName": "framework" + }, + "Empty": { + "longName": "empty" + }, + "IncludeSampleContent": { + "isHidden": true + }, + "UseLocalDB": { + "longName": "use-local-db" + }, + "AADInstance": { + "longName": "aad-instance", + "shortName": "" + }, + "AAdB2CInstance": { + "longName": "aad-b2c-instance", + "shortName": "" + }, + "SignUpSignInPolicyId": { + "longName": "susi-policy-id", + "shortName": "ssp" + }, + "OrgReadAccess": { + "longName": "org-read-access", + "shortName": "r" + }, + "ClientId": { + "longName": "client-id", + "shortName": "" + }, + "AppIDUri": { + "longName": "app-id-uri", + "shortName": "" + }, + "APIClientId": { + "longName": "api-client-id", + "shortName": "" + }, + "Domain": { + "longName": "domain", + "shortName": "" + }, + "TenantId": { + "longName": "tenant-id", + "shortName": "" + }, + "DefaultScope": { + "longName": "default-scope", + "shortName": "s" + }, + "Authority": { + "longName": "authority", + "shortName": "" + }, + "kestrelHttpPort": { + "isHidden": true + }, + "kestrelHttpsPort": { + "isHidden": true + }, + "iisHttpPort": { + "isHidden": true + }, + "iisHttpsPort": { + "isHidden": true + }, + "ExcludeLaunchSettings": { + "longName": "exclude-launch-settings", + "shortName": "" + }, + "UserSecretsId": { + "isHidden": true + }, + "NoHttps": { + "longName": "no-https", + "shortName": "" + }, + "CalledApiUrl": { + "longName": "called-api-url", + "shortName": "" + }, + "CalledApiScopes": { + "longName": "called-api-scopes", + "shortName": "" + }, + "CallsMicrosoftGraph": { + "longName": "calls-graph", + "shortName": "" + }, + "UseProgramMain": { + "longName": "use-program-main", + "shortName": "" + } + } +} diff --git a/src/Templates/templates/componentswebassembly-csharp-9/.template.config/icon.png b/src/Templates/templates/componentswebassembly-csharp-9/.template.config/icon.png new file mode 100644 index 0000000000..42fb1d1efe Binary files /dev/null and b/src/Templates/templates/componentswebassembly-csharp-9/.template.config/icon.png differ diff --git a/src/Templates/templates/componentswebassembly-csharp-9/.template.config/ide.host.json b/src/Templates/templates/componentswebassembly-csharp-9/.template.config/ide.host.json new file mode 100644 index 0000000000..1d7dbb0d8b --- /dev/null +++ b/src/Templates/templates/componentswebassembly-csharp-9/.template.config/ide.host.json @@ -0,0 +1,25 @@ +{ + "$schema": "https://json.schemastore.org/ide.host", + "order": 610, + "icon": "icon.png", + "disableHttpsSymbol": "NoHttps", + "symbolInfo": [ + { + "id": "PWA", + "isVisible": true, + "persistenceScope": "templateGroup" + }, + { + "id": "UseProgramMain", + "isVisible": true, + "persistenceScope": "shared", + "persistenceScopeName": "Microsoft" + }, + { + "id": "IncludeSampleContent", + "isVisible": true, + "persistenceScope": "shared", + "persistenceScopeName": "Microsoft" + } + ] +} diff --git a/src/Templates/templates/componentswebassembly-csharp/.template.config/localize/templatestrings.cs.json b/src/Templates/templates/componentswebassembly-csharp-9/.template.config/localize/templatestrings.cs.json similarity index 100% rename from src/Templates/templates/componentswebassembly-csharp/.template.config/localize/templatestrings.cs.json rename to src/Templates/templates/componentswebassembly-csharp-9/.template.config/localize/templatestrings.cs.json diff --git a/src/Templates/templates/componentswebassembly-csharp/.template.config/localize/templatestrings.de.json b/src/Templates/templates/componentswebassembly-csharp-9/.template.config/localize/templatestrings.de.json similarity index 100% rename from src/Templates/templates/componentswebassembly-csharp/.template.config/localize/templatestrings.de.json rename to src/Templates/templates/componentswebassembly-csharp-9/.template.config/localize/templatestrings.de.json diff --git a/src/Templates/templates/componentswebassembly-csharp/.template.config/localize/templatestrings.en.json b/src/Templates/templates/componentswebassembly-csharp-9/.template.config/localize/templatestrings.en.json similarity index 100% rename from src/Templates/templates/componentswebassembly-csharp/.template.config/localize/templatestrings.en.json rename to src/Templates/templates/componentswebassembly-csharp-9/.template.config/localize/templatestrings.en.json diff --git a/src/Templates/templates/componentswebassembly-csharp/.template.config/localize/templatestrings.es.json b/src/Templates/templates/componentswebassembly-csharp-9/.template.config/localize/templatestrings.es.json similarity index 100% rename from src/Templates/templates/componentswebassembly-csharp/.template.config/localize/templatestrings.es.json rename to src/Templates/templates/componentswebassembly-csharp-9/.template.config/localize/templatestrings.es.json diff --git a/src/Templates/templates/componentswebassembly-csharp/.template.config/localize/templatestrings.fr.json b/src/Templates/templates/componentswebassembly-csharp-9/.template.config/localize/templatestrings.fr.json similarity index 100% rename from src/Templates/templates/componentswebassembly-csharp/.template.config/localize/templatestrings.fr.json rename to src/Templates/templates/componentswebassembly-csharp-9/.template.config/localize/templatestrings.fr.json diff --git a/src/Templates/templates/componentswebassembly-csharp/.template.config/localize/templatestrings.it.json b/src/Templates/templates/componentswebassembly-csharp-9/.template.config/localize/templatestrings.it.json similarity index 100% rename from src/Templates/templates/componentswebassembly-csharp/.template.config/localize/templatestrings.it.json rename to src/Templates/templates/componentswebassembly-csharp-9/.template.config/localize/templatestrings.it.json diff --git a/src/Templates/templates/componentswebassembly-csharp/.template.config/localize/templatestrings.ja.json b/src/Templates/templates/componentswebassembly-csharp-9/.template.config/localize/templatestrings.ja.json similarity index 100% rename from src/Templates/templates/componentswebassembly-csharp/.template.config/localize/templatestrings.ja.json rename to src/Templates/templates/componentswebassembly-csharp-9/.template.config/localize/templatestrings.ja.json diff --git a/src/Templates/templates/componentswebassembly-csharp/.template.config/localize/templatestrings.ko.json b/src/Templates/templates/componentswebassembly-csharp-9/.template.config/localize/templatestrings.ko.json similarity index 100% rename from src/Templates/templates/componentswebassembly-csharp/.template.config/localize/templatestrings.ko.json rename to src/Templates/templates/componentswebassembly-csharp-9/.template.config/localize/templatestrings.ko.json diff --git a/src/Templates/templates/componentswebassembly-csharp/.template.config/localize/templatestrings.pl.json b/src/Templates/templates/componentswebassembly-csharp-9/.template.config/localize/templatestrings.pl.json similarity index 100% rename from src/Templates/templates/componentswebassembly-csharp/.template.config/localize/templatestrings.pl.json rename to src/Templates/templates/componentswebassembly-csharp-9/.template.config/localize/templatestrings.pl.json diff --git a/src/Templates/templates/componentswebassembly-csharp/.template.config/localize/templatestrings.pt-BR.json b/src/Templates/templates/componentswebassembly-csharp-9/.template.config/localize/templatestrings.pt-BR.json similarity index 100% rename from src/Templates/templates/componentswebassembly-csharp/.template.config/localize/templatestrings.pt-BR.json rename to src/Templates/templates/componentswebassembly-csharp-9/.template.config/localize/templatestrings.pt-BR.json diff --git a/src/Templates/templates/componentswebassembly-csharp/.template.config/localize/templatestrings.ru.json b/src/Templates/templates/componentswebassembly-csharp-9/.template.config/localize/templatestrings.ru.json similarity index 100% rename from src/Templates/templates/componentswebassembly-csharp/.template.config/localize/templatestrings.ru.json rename to src/Templates/templates/componentswebassembly-csharp-9/.template.config/localize/templatestrings.ru.json diff --git a/src/Templates/templates/componentswebassembly-csharp/.template.config/localize/templatestrings.tr.json b/src/Templates/templates/componentswebassembly-csharp-9/.template.config/localize/templatestrings.tr.json similarity index 100% rename from src/Templates/templates/componentswebassembly-csharp/.template.config/localize/templatestrings.tr.json rename to src/Templates/templates/componentswebassembly-csharp-9/.template.config/localize/templatestrings.tr.json diff --git a/src/Templates/templates/componentswebassembly-csharp/.template.config/localize/templatestrings.zh-Hans.json b/src/Templates/templates/componentswebassembly-csharp-9/.template.config/localize/templatestrings.zh-Hans.json similarity index 100% rename from src/Templates/templates/componentswebassembly-csharp/.template.config/localize/templatestrings.zh-Hans.json rename to src/Templates/templates/componentswebassembly-csharp-9/.template.config/localize/templatestrings.zh-Hans.json diff --git a/src/Templates/templates/componentswebassembly-csharp/.template.config/localize/templatestrings.zh-Hant.json b/src/Templates/templates/componentswebassembly-csharp-9/.template.config/localize/templatestrings.zh-Hant.json similarity index 100% rename from src/Templates/templates/componentswebassembly-csharp/.template.config/localize/templatestrings.zh-Hant.json rename to src/Templates/templates/componentswebassembly-csharp-9/.template.config/localize/templatestrings.zh-Hant.json diff --git a/src/Templates/templates/componentswebassembly-csharp/.template.config/template.json b/src/Templates/templates/componentswebassembly-csharp-9/.template.config/template.json similarity index 99% rename from src/Templates/templates/componentswebassembly-csharp/.template.config/template.json rename to src/Templates/templates/componentswebassembly-csharp-9/.template.config/template.json index a3303558f3..6d09431c87 100644 --- a/src/Templates/templates/componentswebassembly-csharp/.template.config/template.json +++ b/src/Templates/templates/componentswebassembly-csharp-9/.template.config/template.json @@ -139,16 +139,12 @@ "description": "The target framework for the project.", "datatype": "choice", "choices": [ - { - "choice": "net8.0", - "description": "Target net8.0" - }, { "choice": "net9.0", "description": "Target net9.0" } ], - "replaces": "net8.0", + "replaces": "net9.0", "defaultValue": "net9.0" }, "HostIdentifier": { diff --git a/src/Templates/templates/componentswebassembly-csharp-9/App.razor b/src/Templates/templates/componentswebassembly-csharp-9/App.razor new file mode 100644 index 0000000000..1e17e8b76e --- /dev/null +++ b/src/Templates/templates/componentswebassembly-csharp-9/App.razor @@ -0,0 +1,38 @@ +@*#if (NoAuth) + + + + + + Not found + +

Sorry, there's nothing at this address.

+
+
+
+#else + + + + + + @if (context.User.Identity?.IsAuthenticated != true) + { + + } + else + { +

You are not authorized to access this resource.

+ } +
+
+
+ + Not found + +

Sorry, there's nothing at this address.

+
+
+
+
+#endif*@ diff --git a/src/Templates/templates/componentswebassembly-csharp-9/ComponentsWebAssembly-CSharp.csproj b/src/Templates/templates/componentswebassembly-csharp-9/ComponentsWebAssembly-CSharp.csproj new file mode 100644 index 0000000000..9d8780d0ec --- /dev/null +++ b/src/Templates/templates/componentswebassembly-csharp-9/ComponentsWebAssembly-CSharp.csproj @@ -0,0 +1,27 @@ + + + + net9.0 + enable + enable + + service-worker-assets.js + + + + + + + + + + + + + + + + + + + diff --git a/src/Templates/templates/componentswebassembly-csharp-9/Layout/LoginDisplay.razor b/src/Templates/templates/componentswebassembly-csharp-9/Layout/LoginDisplay.razor new file mode 100644 index 0000000000..6e9bd3ce75 --- /dev/null +++ b/src/Templates/templates/componentswebassembly-csharp-9/Layout/LoginDisplay.razor @@ -0,0 +1,19 @@ +@using Microsoft.AspNetCore.Components.WebAssembly.Authentication +@inject NavigationManager Navigation + + + + Hello, @context.User.Identity?.Name! + Log out + + + Log in + + + +@code{ + public void BeginLogOut() + { + Navigation.NavigateToLogout("authentication/logout"); + } +} diff --git a/src/Templates/templates/componentswebassembly-csharp-9/Layout/LoginDisplay.razor.css b/src/Templates/templates/componentswebassembly-csharp-9/Layout/LoginDisplay.razor.css new file mode 100644 index 0000000000..d2534014f6 --- /dev/null +++ b/src/Templates/templates/componentswebassembly-csharp-9/Layout/LoginDisplay.razor.css @@ -0,0 +1,3 @@ +a { + color: var(--fill-color); +} \ No newline at end of file diff --git a/src/Templates/templates/componentswebassembly-csharp-9/Layout/MainLayout.Auth.razor b/src/Templates/templates/componentswebassembly-csharp-9/Layout/MainLayout.Auth.razor new file mode 100644 index 0000000000..a2e199dca2 --- /dev/null +++ b/src/Templates/templates/componentswebassembly-csharp-9/Layout/MainLayout.Auth.razor @@ -0,0 +1,36 @@ +@inherits LayoutComponentBase + +@*#if (SampleContent) --> + + + ComponentsWebAssembly-CSharp + + + + + + + + +
+ @Body +
+
+ +
@ex.Message
+
+
+
+
+ + Documentation and demos + + About Blazor + +
+##else + +
+ @Body +
+##endif*@ diff --git a/src/Templates/templates/componentswebassembly-csharp-9/Layout/MainLayout.NoAuth.razor b/src/Templates/templates/componentswebassembly-csharp-9/Layout/MainLayout.NoAuth.razor new file mode 100644 index 0000000000..bf933f8d61 --- /dev/null +++ b/src/Templates/templates/componentswebassembly-csharp-9/Layout/MainLayout.NoAuth.razor @@ -0,0 +1,33 @@ +@inherits LayoutComponentBase + +@*#if (SampleContent) --> + + + ComponentsWebAssembly-CSharp + + + + + + +
+ @Body +
+
+ +
@ex.Message
+
+
+
+
+ + Documentation and demos + + About Blazor + +
+##else +
+ @Body +
+##endif*@ diff --git a/src/Templates/templates/componentswebassembly-csharp-9/Layout/NavMenu.CallsWebApi.razor b/src/Templates/templates/componentswebassembly-csharp-9/Layout/NavMenu.CallsWebApi.razor new file mode 100644 index 0000000000..7525b45e17 --- /dev/null +++ b/src/Templates/templates/componentswebassembly-csharp-9/Layout/NavMenu.CallsWebApi.razor @@ -0,0 +1,16 @@ + + +@code { + private bool expanded = true; +} diff --git a/src/Templates/templates/componentswebassembly-csharp-9/Layout/NavMenu.NoApi.razor b/src/Templates/templates/componentswebassembly-csharp-9/Layout/NavMenu.NoApi.razor new file mode 100644 index 0000000000..eb68069ab5 --- /dev/null +++ b/src/Templates/templates/componentswebassembly-csharp-9/Layout/NavMenu.NoApi.razor @@ -0,0 +1,15 @@ + + +@code { + private bool expanded = true; +} diff --git a/src/Templates/templates/componentswebassembly-csharp-9/Layout/RedirectToLogin.razor b/src/Templates/templates/componentswebassembly-csharp-9/Layout/RedirectToLogin.razor new file mode 100644 index 0000000000..a1cf400328 --- /dev/null +++ b/src/Templates/templates/componentswebassembly-csharp-9/Layout/RedirectToLogin.razor @@ -0,0 +1,9 @@ +@using Microsoft.AspNetCore.Components.WebAssembly.Authentication +@inject NavigationManager Navigation + +@code { + protected override void OnInitialized() + { + Navigation.NavigateToLogin("authentication/login"); + } +} diff --git a/src/Templates/templates/componentswebassembly-csharp-9/Pages/Authentication.razor b/src/Templates/templates/componentswebassembly-csharp-9/Pages/Authentication.razor new file mode 100644 index 0000000000..6c74356731 --- /dev/null +++ b/src/Templates/templates/componentswebassembly-csharp-9/Pages/Authentication.razor @@ -0,0 +1,7 @@ +@page "/authentication/{action}" +@using Microsoft.AspNetCore.Components.WebAssembly.Authentication + + +@code{ + [Parameter] public string? Action { get; set; } +} diff --git a/src/Templates/templates/componentswebassembly-csharp-9/Pages/Counter.razor b/src/Templates/templates/componentswebassembly-csharp-9/Pages/Counter.razor new file mode 100644 index 0000000000..39561f28df --- /dev/null +++ b/src/Templates/templates/componentswebassembly-csharp-9/Pages/Counter.razor @@ -0,0 +1,18 @@ +@page "/counter" + +Counter + +

Counter

+ +

Current count: @currentCount

+ +Click me + +@code { + private int currentCount = 0; + + private void IncrementCount() + { + currentCount++; + } +} diff --git a/src/Templates/templates/componentswebassembly-csharp-9/Pages/Home.razor b/src/Templates/templates/componentswebassembly-csharp-9/Pages/Home.razor new file mode 100644 index 0000000000..c3a993b9d2 --- /dev/null +++ b/src/Templates/templates/componentswebassembly-csharp-9/Pages/Home.razor @@ -0,0 +1,13 @@ +@page "/" + +Home + +

Hello, world!

+ +@*#if (MissingAuthority) +
+ Before authentication will function correctly, you must configure your provider details in Program.cs +
+ +#endif*@ +Welcome to your new Fluent Blazor app. diff --git a/src/Templates/templates/componentswebassembly-csharp-9/Pages/Weather.razor b/src/Templates/templates/componentswebassembly-csharp-9/Pages/Weather.razor new file mode 100644 index 0000000000..17ce7ad0d7 --- /dev/null +++ b/src/Templates/templates/componentswebassembly-csharp-9/Pages/Weather.razor @@ -0,0 +1,42 @@ +@page "/weather" +@inject HttpClient Http + +Weather + +

Weather

+ +

This component demonstrates fetching data from the server.

+ +@if (forecasts == null) +{ +

Loading...

+} +else +{ + + + + + + +} + +@code { + private IQueryable? forecasts; + + protected override async Task OnInitializedAsync() + { + forecasts = (await Http.GetFromJsonAsync>("sample-data/weather.json"))!.AsQueryable(); + } + + public class WeatherForecast + { + public DateOnly Date { get; set; } + + public int TemperatureC { get; set; } + + public string? Summary { get; set; } + + public int TemperatureF => 32 + (int)(TemperatureC / 0.5556); + } +} diff --git a/src/Templates/templates/componentswebassembly-csharp-9/Program.Main.cs b/src/Templates/templates/componentswebassembly-csharp-9/Program.Main.cs new file mode 100644 index 0000000000..48de417353 --- /dev/null +++ b/src/Templates/templates/componentswebassembly-csharp-9/Program.Main.cs @@ -0,0 +1,45 @@ +using Microsoft.AspNetCore.Components.Web; +using Microsoft.AspNetCore.Components.WebAssembly.Hosting; +using Microsoft.FluentUI.AspNetCore.Components; + +namespace ComponentsWebAssembly_CSharp; + +public class Program +{ + public static async Task Main(string[] args) + { + var builder = WebAssemblyHostBuilder.CreateDefault(args); + builder.RootComponents.Add("#app"); + builder.RootComponents.Add("head::after"); + + builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) }); + builder.Services.AddFluentUIComponents(); + #if(!NoAuth) + + #endif + #if (IndividualLocalAuth) + builder.Services.AddOidcAuthentication(options => + { + #if(MissingAuthority) + // Configure your authentication provider options here. + // For more information, see https://aka.ms/blazor-standalone-auth + #endif + builder.Configuration.Bind("Local", options.ProviderOptions); + }); + #endif + #if (IndividualB2CAuth) + builder.Services.AddMsalAuthentication(options => + { + builder.Configuration.Bind("AzureAdB2C", options.ProviderOptions.Authentication); + }); + #endif + #if(OrganizationalAuth) + builder.Services.AddMsalAuthentication(options => + { + builder.Configuration.Bind("AzureAd", options.ProviderOptions.Authentication); + }); + #endif + + await builder.Build().RunAsync(); + } +} diff --git a/src/Templates/templates/componentswebassembly-csharp-9/Program.cs b/src/Templates/templates/componentswebassembly-csharp-9/Program.cs new file mode 100644 index 0000000000..b49dc8dd1c --- /dev/null +++ b/src/Templates/templates/componentswebassembly-csharp-9/Program.cs @@ -0,0 +1,38 @@ +using Microsoft.AspNetCore.Components.Web; +using Microsoft.AspNetCore.Components.WebAssembly.Hosting; +using Microsoft.FluentUI.AspNetCore.Components; +using ComponentsWebAssembly_CSharp; + +var builder = WebAssemblyHostBuilder.CreateDefault(args); +builder.RootComponents.Add("#app"); +builder.RootComponents.Add("head::after"); + +builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) }); +builder.Services.AddFluentUIComponents(); +#if (!NoAuth) + +#endif +#if (IndividualLocalAuth) +builder.Services.AddOidcAuthentication(options => +{ + #if(MissingAuthority) + // Configure your authentication provider options here. + // For more information, see https://aka.ms/blazor-standalone-auth + #endif + builder.Configuration.Bind("Local", options.ProviderOptions); +}); +#endif +#if (IndividualB2CAuth) +builder.Services.AddMsalAuthentication(options => +{ + builder.Configuration.Bind("AzureAdB2C", options.ProviderOptions.Authentication); +}); +#endif +#if(OrganizationalAuth) +builder.Services.AddMsalAuthentication(options => +{ + builder.Configuration.Bind("AzureAd", options.ProviderOptions.Authentication); +}); +#endif + +await builder.Build().RunAsync(); diff --git a/src/Templates/templates/componentswebassembly-csharp-9/Properties/launchSettings.json b/src/Templates/templates/componentswebassembly-csharp-9/Properties/launchSettings.json new file mode 100644 index 0000000000..167c237f02 --- /dev/null +++ b/src/Templates/templates/componentswebassembly-csharp-9/Properties/launchSettings.json @@ -0,0 +1,33 @@ +{ + "$schema": "https://json.schemastore.org/launchsettings.json", + "profiles": { + //#if (HasHttpProfile) + "http": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}", + "applicationUrl": "http://localhost:5000", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + //#if (HasHttpsProfile) + }, + //#else + } + //#endif + //#endif + //#if (HasHttpsProfile) + "https": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}", + "applicationUrl": "https://localhost:5001;http://localhost:5000", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + //#endif + } +} diff --git a/src/Templates/templates/componentswebassembly-csharp-9/_Imports.razor b/src/Templates/templates/componentswebassembly-csharp-9/_Imports.razor new file mode 100644 index 0000000000..9c56f4e218 --- /dev/null +++ b/src/Templates/templates/componentswebassembly-csharp-9/_Imports.razor @@ -0,0 +1,16 @@ +@using System.Net.Http +@using System.Net.Http.Json +@*#if (!NoAuth) +@using Microsoft.AspNetCore.Components.Authorization +#endif*@ +@using Microsoft.AspNetCore.Components.Forms +@using Microsoft.AspNetCore.Components.Routing +@using Microsoft.AspNetCore.Components.Web +@using Microsoft.AspNetCore.Components.Web.Virtualization +@using Microsoft.AspNetCore.Components.WebAssembly.Http +@using Microsoft.FluentUI.AspNetCore.Components +@using Icons = Microsoft.FluentUI.AspNetCore.Components.Icons + +@using Microsoft.JSInterop +@using ComponentsWebAssembly_CSharp +@using ComponentsWebAssembly_CSharp.Layout diff --git a/src/Templates/templates/componentswebassembly-csharp-9/wwwroot/appsettings.Development.json b/src/Templates/templates/componentswebassembly-csharp-9/wwwroot/appsettings.Development.json new file mode 100644 index 0000000000..daafa127f0 --- /dev/null +++ b/src/Templates/templates/componentswebassembly-csharp-9/wwwroot/appsettings.Development.json @@ -0,0 +1,8 @@ +{ + ////#if (IndividualLocalAuth) + //"Local": { + // "Authority": "https:////login.microsoftonline.com/", + // "ClientId": "33333333-3333-3333-33333333333333333" + //} + ////#endif +} diff --git a/src/Templates/templates/componentswebassembly-csharp-9/wwwroot/appsettings.json b/src/Templates/templates/componentswebassembly-csharp-9/wwwroot/appsettings.json new file mode 100644 index 0000000000..e485918eea --- /dev/null +++ b/src/Templates/templates/componentswebassembly-csharp-9/wwwroot/appsettings.json @@ -0,0 +1,27 @@ +{ + ////#if (IndividualLocalAuth) + //"Local": { + // "Authority": "https:////login.microsoftonline.com/", + // "ClientId": "33333333-3333-3333-33333333333333333" + //} + ////#endif + ////#if (IndividualB2CAuth) + //"AzureAdB2C": { + // "Authority": "https:////aadB2CInstance.b2clogin.com/qualified.domain.name/MySignUpSignInPolicyId", + // "ClientId": "33333333-3333-3333-33333333333333333", + // "ValidateAuthority": false + //} + ////#endif + ////#if (OrganizationalAuth) + ///* + The following identity settings need to be configured + before the project can be successfully executed. + For more info see https:////aka.ms/dotnet-template-ms-identity-platform + //*/ + //"AzureAd": { + // "Authority": "https:////login.microsoftonline.com/22222222-2222-2222-2222-222222222222", + // "ClientId": "33333333-3333-3333-33333333333333333", + // "ValidateAuthority": true + //} + ////#endif +} diff --git a/src/Templates/templates/componentswebassembly-csharp-9/wwwroot/css/app.css b/src/Templates/templates/componentswebassembly-csharp-9/wwwroot/css/app.css new file mode 100644 index 0000000000..2d7b409d64 --- /dev/null +++ b/src/Templates/templates/componentswebassembly-csharp-9/wwwroot/css/app.css @@ -0,0 +1,187 @@ +@import '_content/Microsoft.FluentUI.AspNetCore.Components/css/reboot.css'; + +body { + --body-font: "Segoe UI Variable", "Segoe UI", sans-serif; + font-family: var(--body-font); + font-size: var(--type-ramp-base-font-size); + line-height: var(--type-ramp-base-line-height); + margin: 0; +} + +.navmenu-icon { + display: none; +} + +.main { + min-height: calc(100dvh - 86px); + color: var(--neutral-foreground-rest); + align-items: stretch !important; +} + +.body-content { + align-self: stretch; + height: calc(100dvh - 86px) !important; + display: flex; +} + +.content { + padding: 0.5rem 1.5rem; + align-self: stretch !important; + width: 100%; +} + +footer { + background: var(--neutral-layer-4); + color: var(--neutral-foreground-rest); + align-items: center; + padding: 10px 10px; +} + + footer a { + color: var(--neutral-foreground-rest); + text-decoration: none; + } + + footer a:focus { + outline: 1px dashed; + outline-offset: 3px; + } + + footer a:hover { + text-decoration: underline; + } + +.alert { + border: 1px dashed var(--accent-fill-rest); + padding: 5px; +} + + +#blazor-error-ui { + background: lightyellow; + bottom: 0; + box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.2); + display: none; + left: 0; + padding: 0.6rem 1.25rem 0.7rem 1.25rem; + position: fixed; + width: 100%; + z-index: 1000; + margin: 20px 0; +} + + #blazor-error-ui .dismiss { + cursor: pointer; + position: absolute; + right: 0.75rem; + top: 0.5rem; + } + +.blazor-error-boundary { + background: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTYiIGhlaWdodD0iNDkiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIG92ZXJmbG93PSJoaWRkZW4iPjxkZWZzPjxjbGlwUGF0aCBpZD0iY2xpcDAiPjxyZWN0IHg9IjIzNSIgeT0iNTEiIHdpZHRoPSI1NiIgaGVpZ2h0PSI0OSIvPjwvY2xpcFBhdGg+PC9kZWZzPjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMCkiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yMzUgLTUxKSI+PHBhdGggZD0iTTI2My41MDYgNTFDMjY0LjcxNyA1MSAyNjUuODEzIDUxLjQ4MzcgMjY2LjYwNiA1Mi4yNjU4TDI2Ny4wNTIgNTIuNzk4NyAyNjcuNTM5IDUzLjYyODMgMjkwLjE4NSA5Mi4xODMxIDI5MC41NDUgOTIuNzk1IDI5MC42NTYgOTIuOTk2QzI5MC44NzcgOTMuNTEzIDI5MSA5NC4wODE1IDI5MSA5NC42NzgyIDI5MSA5Ny4wNjUxIDI4OS4wMzggOTkgMjg2LjYxNyA5OUwyNDAuMzgzIDk5QzIzNy45NjMgOTkgMjM2IDk3LjA2NTEgMjM2IDk0LjY3ODIgMjM2IDk0LjM3OTkgMjM2LjAzMSA5NC4wODg2IDIzNi4wODkgOTMuODA3MkwyMzYuMzM4IDkzLjAxNjIgMjM2Ljg1OCA5Mi4xMzE0IDI1OS40NzMgNTMuNjI5NCAyNTkuOTYxIDUyLjc5ODUgMjYwLjQwNyA1Mi4yNjU4QzI2MS4yIDUxLjQ4MzcgMjYyLjI5NiA1MSAyNjMuNTA2IDUxWk0yNjMuNTg2IDY2LjAxODNDMjYwLjczNyA2Ni4wMTgzIDI1OS4zMTMgNjcuMTI0NSAyNTkuMzEzIDY5LjMzNyAyNTkuMzEzIDY5LjYxMDIgMjU5LjMzMiA2OS44NjA4IDI1OS4zNzEgNzAuMDg4N0wyNjEuNzk1IDg0LjAxNjEgMjY1LjM4IDg0LjAxNjEgMjY3LjgyMSA2OS43NDc1QzI2Ny44NiA2OS43MzA5IDI2Ny44NzkgNjkuNTg3NyAyNjcuODc5IDY5LjMxNzkgMjY3Ljg3OSA2Ny4xMTgyIDI2Ni40NDggNjYuMDE4MyAyNjMuNTg2IDY2LjAxODNaTTI2My41NzYgODYuMDU0N0MyNjEuMDQ5IDg2LjA1NDcgMjU5Ljc4NiA4Ny4zMDA1IDI1OS43ODYgODkuNzkyMSAyNTkuNzg2IDkyLjI4MzcgMjYxLjA0OSA5My41Mjk1IDI2My41NzYgOTMuNTI5NSAyNjYuMTE2IDkzLjUyOTUgMjY3LjM4NyA5Mi4yODM3IDI2Ny4zODcgODkuNzkyMSAyNjcuMzg3IDg3LjMwMDUgMjY2LjExNiA4Ni4wNTQ3IDI2My41NzYgODYuMDU0N1oiIGZpbGw9IiNGRkU1MDAiIGZpbGwtcnVsZT0iZXZlbm9kZCIvPjwvZz48L3N2Zz4=) no-repeat 1rem/1.8rem, #b32121; + padding: 1rem 1rem 1rem 3.7rem; + color: white; +} + + .blazor-error-boundary::before { + content: "An error has occurred. " + } + +.loading-progress { + position: relative; + display: block; + width: 8rem; + height: 8rem; + margin: 20vh auto 1rem auto; +} + + .loading-progress circle { + fill: none; + stroke: #e0e0e0; + stroke-width: 0.6rem; + transform-origin: 50% 50%; + transform: rotate(-90deg); + } + + .loading-progress circle:last-child { + stroke: #1b6ec2; + stroke-dasharray: calc(3.141 * var(--blazor-load-percentage, 0%) * 0.8), 500%; + transition: stroke-dasharray 0.05s ease-in-out; + } + +.loading-progress-text { + position: absolute; + text-align: center; + font-weight: bold; + inset: calc(20vh + 3.25rem) 0 auto 0.2rem; +} + + .loading-progress-text:after { + content: var(--blazor-load-percentage-text, "Loading"); + } + +code { + color: #c02d76; +} + +@media (max-width: 600px) { + .header-gutters { + margin: 0.5rem 3rem 0.5rem 1.5rem !important; + } + + [dir="rtl"] .header-gutters { + margin: 0.5rem 1.5rem 0.5rem 3rem !important; + } + + .main { + flex-direction: column !important; + row-gap: 0 !important; + } + + nav.sitenav { + width: 100%; + height: 100%; + } + + #main-menu { + width: 100% !important; + } + + #main-menu > div:first-child:is(.expander) { + display: none; + } + + .navmenu { + width: 100%; + } + + #navmenu-toggle { + appearance: none; + } + + #navmenu-toggle ~ nav { + display: none; + } + + #navmenu-toggle:checked ~ nav { + display: block; + } + + .navmenu-icon { + cursor: pointer; + z-index: 10; + display: block; + position: absolute; + top: 15px; + left: unset; + right: 20px; + width: 20px; + height: 20px; + border: none; + } + + [dir="rtl"] .navmenu-icon { + left: 20px; + right: unset; + } +} diff --git a/src/Templates/templates/componentswebassembly-csharp-9/wwwroot/favicon.ico b/src/Templates/templates/componentswebassembly-csharp-9/wwwroot/favicon.ico new file mode 100644 index 0000000000..e189d8e579 Binary files /dev/null and b/src/Templates/templates/componentswebassembly-csharp-9/wwwroot/favicon.ico differ diff --git a/src/Templates/templates/componentswebassembly-csharp-9/wwwroot/icon-192.png b/src/Templates/templates/componentswebassembly-csharp-9/wwwroot/icon-192.png new file mode 100644 index 0000000000..166f56da76 Binary files /dev/null and b/src/Templates/templates/componentswebassembly-csharp-9/wwwroot/icon-192.png differ diff --git a/src/Templates/templates/componentswebassembly-csharp-9/wwwroot/icon-512.png b/src/Templates/templates/componentswebassembly-csharp-9/wwwroot/icon-512.png new file mode 100644 index 0000000000..c2dd4842dc Binary files /dev/null and b/src/Templates/templates/componentswebassembly-csharp-9/wwwroot/icon-512.png differ diff --git a/src/Templates/templates/componentswebassembly-csharp-9/wwwroot/index.html b/src/Templates/templates/componentswebassembly-csharp-9/wwwroot/index.html new file mode 100644 index 0000000000..52ecb61676 --- /dev/null +++ b/src/Templates/templates/componentswebassembly-csharp-9/wwwroot/index.html @@ -0,0 +1,51 @@ + + + + + + + ComponentsWebAssembly-CSharp + + + + + + + + + + + + + + + + + +
+ + + + +
+
+ +
+ An unhandled error has occurred. + Reload + 🗙 +
+ + + + + + + + + + + + + diff --git a/src/Templates/templates/componentswebassembly-csharp-9/wwwroot/manifest.webmanifest b/src/Templates/templates/componentswebassembly-csharp-9/wwwroot/manifest.webmanifest new file mode 100644 index 0000000000..dea89b5f81 --- /dev/null +++ b/src/Templates/templates/componentswebassembly-csharp-9/wwwroot/manifest.webmanifest @@ -0,0 +1,22 @@ +{ + "name": "ComponentsWebAssembly-CSharp", + "short_name": "ComponentsWebAssembly-CSharp", + "id": "./", + "start_url": "./", + "display": "standalone", + "background_color": "#ffffff", + "theme_color": "#03173d", + "prefer_related_applications": false, + "icons": [ + { + "src": "icon-512.png", + "type": "image/png", + "sizes": "512x512" + }, + { + "src": "icon-192.png", + "type": "image/png", + "sizes": "192x192" + } + ] +} diff --git a/src/Templates/templates/componentswebassembly-csharp-9/wwwroot/sample-data/weather.json b/src/Templates/templates/componentswebassembly-csharp-9/wwwroot/sample-data/weather.json new file mode 100644 index 0000000000..b7459733f9 --- /dev/null +++ b/src/Templates/templates/componentswebassembly-csharp-9/wwwroot/sample-data/weather.json @@ -0,0 +1,27 @@ +[ + { + "date": "2022-01-06", + "temperatureC": 1, + "summary": "Freezing" + }, + { + "date": "2022-01-07", + "temperatureC": 14, + "summary": "Bracing" + }, + { + "date": "2022-01-08", + "temperatureC": -13, + "summary": "Freezing" + }, + { + "date": "2022-01-09", + "temperatureC": -16, + "summary": "Balmy" + }, + { + "date": "2022-01-10", + "temperatureC": -2, + "summary": "Chilly" + } +] diff --git a/src/Templates/templates/componentswebassembly-csharp-9/wwwroot/service-worker.js b/src/Templates/templates/componentswebassembly-csharp-9/wwwroot/service-worker.js new file mode 100644 index 0000000000..fe614daee0 --- /dev/null +++ b/src/Templates/templates/componentswebassembly-csharp-9/wwwroot/service-worker.js @@ -0,0 +1,4 @@ +// In development, always fetch from the network and do not enable offline support. +// This is because caching would make development more difficult (changes would not +// be reflected on the first load after each change). +self.addEventListener('fetch', () => { }); diff --git a/src/Templates/templates/componentswebassembly-csharp-9/wwwroot/service-worker.published.js b/src/Templates/templates/componentswebassembly-csharp-9/wwwroot/service-worker.published.js new file mode 100644 index 0000000000..1f7f543fa5 --- /dev/null +++ b/src/Templates/templates/componentswebassembly-csharp-9/wwwroot/service-worker.published.js @@ -0,0 +1,55 @@ +// Caution! Be sure you understand the caveats before publishing an application with +// offline support. See https://aka.ms/blazor-offline-considerations + +self.importScripts('./service-worker-assets.js'); +self.addEventListener('install', event => event.waitUntil(onInstall(event))); +self.addEventListener('activate', event => event.waitUntil(onActivate(event))); +self.addEventListener('fetch', event => event.respondWith(onFetch(event))); + +const cacheNamePrefix = 'offline-cache-'; +const cacheName = `${cacheNamePrefix}${self.assetsManifest.version}`; +const offlineAssetsInclude = [ /\.dll$/, /\.pdb$/, /\.wasm/, /\.html/, /\.js$/, /\.json$/, /\.css$/, /\.woff$/, /\.png$/, /\.jpe?g$/, /\.gif$/, /\.ico$/, /\.blat$/, /\.dat$/ ]; +const offlineAssetsExclude = [ /^service-worker\.js$/ ]; + +// Replace with your base path if you are hosting on a subfolder. Ensure there is a trailing '/'. +const base = "/"; +const baseUrl = new URL(base, self.origin); +const manifestUrlList = self.assetsManifest.assets.map(asset => new URL(asset.url, baseUrl).href); + +async function onInstall(event) { + console.info('Service worker: Install'); + + // Fetch and cache all matching items from the assets manifest + const assetsRequests = self.assetsManifest.assets + .filter(asset => offlineAssetsInclude.some(pattern => pattern.test(asset.url))) + .filter(asset => !offlineAssetsExclude.some(pattern => pattern.test(asset.url))) + .map(asset => new Request(asset.url, { integrity: asset.hash, cache: 'no-cache' })); + await caches.open(cacheName).then(cache => cache.addAll(assetsRequests)); +} + +async function onActivate(event) { + console.info('Service worker: Activate'); + + // Delete unused caches + const cacheKeys = await caches.keys(); + await Promise.all(cacheKeys + .filter(key => key.startsWith(cacheNamePrefix) && key !== cacheName) + .map(key => caches.delete(key))); +} + +async function onFetch(event) { + let cachedResponse = null; + if (event.request.method === 'GET') { + // For all navigation requests, try to serve index.html from cache, + // unless that request is for an offline resource. + // If you need some URLs to be server-rendered, edit the following check to exclude those URLs + const shouldServeIndexHtml = event.request.mode === 'navigate' + && !manifestUrlList.some(url => url === event.request.url); + + const request = shouldServeIndexHtml ? 'index.html' : event.request; + const cache = await caches.open(cacheName); + cachedResponse = await cache.match(request); + } + + return cachedResponse || fetch(event.request); +} diff --git a/src/Templates/templates/maui-blazor-solution/MauiApp.1.Shared/MauiApp.1.Shared.csproj b/src/Templates/templates/maui-blazor-solution/MauiApp.1.Shared/MauiApp.1.Shared.csproj index bb40f653d6..7d39174de6 100644 --- a/src/Templates/templates/maui-blazor-solution/MauiApp.1.Shared/MauiApp.1.Shared.csproj +++ b/src/Templates/templates/maui-blazor-solution/MauiApp.1.Shared/MauiApp.1.Shared.csproj @@ -11,7 +11,7 @@ - + diff --git a/src/Templates/templates/maui-blazor-solution/MauiApp.1.Web.Client/MauiApp.1.Web.Client.csproj b/src/Templates/templates/maui-blazor-solution/MauiApp.1.Web.Client/MauiApp.1.Web.Client.csproj index a14cb361f4..ca2b15723c 100644 --- a/src/Templates/templates/maui-blazor-solution/MauiApp.1.Web.Client/MauiApp.1.Web.Client.csproj +++ b/src/Templates/templates/maui-blazor-solution/MauiApp.1.Web.Client/MauiApp.1.Web.Client.csproj @@ -9,8 +9,8 @@ - - + + diff --git a/src/Templates/templates/maui-blazor-solution/MauiApp.1.Web/MauiApp.1.Web.csproj b/src/Templates/templates/maui-blazor-solution/MauiApp.1.Web/MauiApp.1.Web.csproj index f1ed16745c..cdd1e350e3 100644 --- a/src/Templates/templates/maui-blazor-solution/MauiApp.1.Web/MauiApp.1.Web.csproj +++ b/src/Templates/templates/maui-blazor-solution/MauiApp.1.Web/MauiApp.1.Web.csproj @@ -18,12 +18,12 @@ - - - - - - + + + + + + diff --git a/src/Templates/templates/maui-blazor-solution/MauiApp.1/MauiApp.1.csproj b/src/Templates/templates/maui-blazor-solution/MauiApp.1/MauiApp.1.csproj index 606aab425c..1d2e246943 100644 --- a/src/Templates/templates/maui-blazor-solution/MauiApp.1/MauiApp.1.csproj +++ b/src/Templates/templates/maui-blazor-solution/MauiApp.1/MauiApp.1.csproj @@ -63,11 +63,12 @@ - + - + +