Skip to content

Commit

Permalink
Update template app with recent changes (#127)
Browse files Browse the repository at this point in the history
* Simplify the Display Message constructor

* Update JavaScript libraries

* Fix primary button color

* Fix menu bar for x-small screens

* Consolidate maintenance item edit warning

* Update navbar styles

* Update table hover styles

* Explicitly set the HTTPS port in production; see:
    - https://learn.microsoft.com/en-us/dotnet/core/compatibility/aspnet-core/6.0/middleware-ambiguous-https-ports-exception
    - https://learn.microsoft.com/en-us/aspnet/core/security/enforcing-ssl?view=aspnetcore-8.0#port-configuration-1

* Update Program.cs and associated builder files

* Update NuGet packages

* Fix Display Message unit tests
  • Loading branch information
dougwaldron authored Nov 6, 2024
1 parent 637fccb commit 6c23f92
Show file tree
Hide file tree
Showing 24 changed files with 194 additions and 164 deletions.
50 changes: 25 additions & 25 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -4,57 +4,57 @@
</PropertyGroup>
<ItemGroup>
<PackageVersion Include="AutoMapper" Version="13.0.1" />
<PackageVersion Include="ClosedXML" Version="0.102.2" />
<PackageVersion Include="ClosedXML" Version="0.104.1" />
<PackageVersion Include="Dapper" Version="2.1.35" />
<PackageVersion Include="FluentValidation.AspNetCore" Version="11.3.0" />
<PackageVersion Include="GaEpd.AppLibrary" Version="5.2.1" />
<PackageVersion Include="GaEpd.AppLibrary" Version="5.3.1" />
<PackageVersion Include="GaEpd.FileService" Version="3.1.1" />
<PackageVersion Include="HtmlSanitizer" Version="8.0.865" />
<PackageVersion Include="JetBrains.Annotations" Version="2023.3.0" />
<PackageVersion Include="LigerShark.WebOptimizer.Core" Version="3.0.405" />
<PackageVersion Include="MailKit" Version="4.5.0" />
<PackageVersion Include="Markdig" Version="0.37.0" />
<PackageVersion Include="HtmlSanitizer" Version="8.1.870" />
<PackageVersion Include="JetBrains.Annotations" Version="2024.3.0" />
<PackageVersion Include="LigerShark.WebOptimizer.Core" Version="3.0.426" />
<PackageVersion Include="MailKit" Version="4.8.0" />
<PackageVersion Include="Markdig" Version="0.38.0" />
<PackageVersion Include="Markdig.Signed" Version="0.37.0" />
<PackageVersion Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="8.0.4" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.4">
<PackageVersion Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="8.0.10" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.10">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageVersion>
<PackageVersion Include="Microsoft.EntityFrameworkCore.InMemory" Version="8.0.4" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.SqlServer" Version="8.0.4" />
<PackageVersion Include="Microsoft.EntityFrameworkCore" Version="8.0.4" />
<PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="8.0.0" />
<PackageVersion Include="Microsoft.Extensions.Identity.Stores" Version="8.0.4" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.InMemory" Version="8.0.10" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.SqlServer" Version="8.0.10" />
<PackageVersion Include="Microsoft.EntityFrameworkCore" Version="8.0.10" />
<PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="8.0.1" />
<PackageVersion Include="Microsoft.Extensions.Identity.Stores" Version="8.0.10" />
<PackageVersion Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="8.0.0" />
<PackageVersion Include="Microsoft.Identity.Web" Version="2.18.1" />
<PackageVersion Include="Microsoft.Identity.Web" Version="3.3.1" />
<PackageVersion Include="Microsoft.Web.LibraryManager.Build" Version="2.1.175" />
<PackageVersion Include="Mindscape.Raygun4Net.AspNetCore" Version="10.1.1" />
<PackageVersion Include="NetEscapades.AspNetCore.SecurityHeaders" Version="0.21.0" />
<PackageVersion Include="Mindscape.Raygun4Net.AspNetCore" Version="11.1.1" />
<PackageVersion Include="NetEscapades.AspNetCore.SecurityHeaders" Version="0.24.0" />
<PackageVersion Include="SixLabors.ImageSharp" Version="3.1.4" />
<PackageVersion Include="SonarAnalyzer.CSharp" Version="9.24.0.89429">
<PackageVersion Include="SonarAnalyzer.CSharp" Version="9.32.0.97167">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageVersion>
<PackageVersion Include="Swashbuckle.AspNetCore" Version="6.5.0" />
<PackageVersion Include="Swashbuckle.AspNetCore" Version="6.9.0" />
</ItemGroup>
<ItemGroup>
<PackageVersion Include="coverlet.collector" Version="6.0.2">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageVersion>
<PackageVersion Include="EfCore.TestSupport" Version="8.0.1" />
<PackageVersion Include="FluentAssertions" Version="6.12.0" />
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.9.0" />
<PackageVersion Include="NSubstitute" Version="5.1.0" />
<PackageVersion Include="FluentAssertions" Version="6.12.1" />
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.11.1" />
<PackageVersion Include="NSubstitute" Version="5.3.0" />
<PackageVersion Include="NSubstitute.Analyzers.CSharp" Version="1.0.17">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageVersion>
<PackageVersion Include="NUnit.Analyzers" Version="4.2.0">
<PackageVersion Include="NUnit.Analyzers" Version="4.3.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageVersion>
<PackageVersion Include="NUnit" Version="4.1.0" />
<PackageVersion Include="NUnit3TestAdapter" Version="4.5.0" />
<PackageVersion Include="NUnit" Version="4.2.2" />
<PackageVersion Include="NUnit3TestAdapter" Version="4.6.0" />
</ItemGroup>
</Project>
14 changes: 14 additions & 0 deletions src/AppServices/Permissions/Policies.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.Extensions.DependencyInjection;
using MyApp.AppServices.Permissions.Requirements;

namespace MyApp.AppServices.Permissions;
Expand Down Expand Up @@ -26,6 +27,19 @@ namespace MyApp.AppServices.Permissions;

public static class Policies
{
// These policies are for use in PageModel class attributes, e.g.:
// [Authorize(Policy = nameof(Policies.ActiveUser))]

public static void AddAuthorizationPolicies(this IServiceCollection services)
{
services.AddAuthorizationBuilder()
.AddPolicy(nameof(ActiveUser), ActiveUser)
.AddPolicy(nameof(Manager), Manager)
.AddPolicy(nameof(SiteMaintainer), SiteMaintainer)
.AddPolicy(nameof(StaffUser), StaffUser)
.AddPolicy(nameof(UserAdministrator), UserAdministrator);
}

// Default policy builder
private static AuthorizationPolicyBuilder ActiveUserPolicyBuilder => new AuthorizationPolicyBuilder()
.RequireAuthenticatedUser().AddRequirements(new ActiveUserRequirement());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,19 +9,11 @@
namespace MyApp.AppServices.RegisterServices;

[SuppressMessage("Major Code Smell", "S125:Sections of code should not be commented out")]
public static class AuthorizationPolicies
public static class AuthorizationHandlers
{
public static void AddAuthorizationPolicies(this IServiceCollection services)
public static void AddAuthorizationHandlers(this IServiceCollection services)
{
// These policies are for use in PageModel class attributes, e.g.:
// [Authorize(Policy = nameof(Policies.ActiveUser))]

services.AddAuthorizationBuilder()
.AddPolicy(nameof(Policies.ActiveUser), Policies.ActiveUser)
.AddPolicy(nameof(Policies.Manager), Policies.Manager)
.AddPolicy(nameof(Policies.SiteMaintainer), Policies.SiteMaintainer)
.AddPolicy(nameof(Policies.StaffUser), Policies.StaffUser)
.AddPolicy(nameof(Policies.UserAdministrator), Policies.UserAdministrator);
services.AddAuthorizationPolicies();

// Resource/operation-based permission handlers, e.g.:
// var canAssign = await authorization.Succeeded(User, entryView, WorkEntryOperation.EditWorkEntry);
Expand Down
8 changes: 3 additions & 5 deletions src/AppServices/RegisterServices/Validators.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,7 @@ namespace MyApp.AppServices.RegisterServices;

public static class Validators
{
public static void AddValidators(this IServiceCollection services)
{
// Add all validators
services.AddValidatorsFromAssemblyContaining(typeof(RegisterAppServices));
}
// Add all validators
public static void AddValidators(this IServiceCollection services) =>
services.AddValidatorsFromAssemblyContaining(typeof(Validators));
}
2 changes: 1 addition & 1 deletion src/WebApp/Models/DisplayMessage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

namespace MyApp.WebApp.Models;

public record DisplayMessage(DisplayMessage.AlertContext Context, string Message, List<string> Details)
public record DisplayMessage(DisplayMessage.AlertContext Context, string Message, List<string>? Details = null)
{
[JsonIgnore]
public string AlertClass => Context switch
Expand Down
2 changes: 1 addition & 1 deletion src/WebApp/Pages/Account/Index.cshtml
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
{
<a asp-page="Edit" class="btn btn-outline-primary me-2">Edit Profile</a>
}
<form class="form-inline d-inline-block" asp-page="Logout" method="post">
<form class="d-inline-block" asp-page="Logout" method="post">
<button type="submit" class="btn btn-danger">Sign out</button>
</form>
</div>
Expand Down
12 changes: 1 addition & 11 deletions src/WebApp/Pages/Admin/Maintenance/EntryTypes/Edit.cshtml
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,7 @@

<h1>Edit @EditModel.ThisOption.SingularName</h1>
<hr />

<div class="alert alert-warning d-flex align-items-center" role="alert">
<svg class="bi me-3" role="img" aria-label="Warning:">
<use href="@Url.Content("~/images/app-icons.svg")#app-icon-exclamation-triangle-fill"></use>
</svg>
<div>
Warning: Changing the name of @(EditModel.ThisOption.StartsWithVowelSound ? "an" : "a")
@EditModel.ThisOption.SingularName will affect all of its existing uses. Before proceeding, consider
whether it would be better to deactivate this one and create a new one.
</div>
</div>
<partial name="Admin/Maintenance/_MaintenanceItemEditWarning" model="@EditModel.ThisOption" />

<h2>@Model.OriginalName</h2>

Expand Down
12 changes: 1 addition & 11 deletions src/WebApp/Pages/Admin/Maintenance/Offices/Edit.cshtml
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,7 @@

<h1>Edit @EditModel.ThisOption.SingularName</h1>
<hr />

<div class="alert alert-warning d-flex align-items-center" role="alert">
<svg class="bi me-3" role="img" aria-label="Warning:">
<use href="@Url.Content("~/images/app-icons.svg")#app-icon-exclamation-triangle-fill"></use>
</svg>
<div>
Warning: Changing the name of @(EditModel.ThisOption.StartsWithVowelSound ? "an" : "a")
@EditModel.ThisOption.SingularName will affect all of its existing uses. Before proceeding, consider
whether it would be better to deactivate this one and create a new one.
</div>
</div>
<partial name="Admin/Maintenance/_MaintenanceItemEditWarning" model="@EditModel.ThisOption" />

<h2>@Model.OriginalName</h2>

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
@model MaintenanceOption

<div class="alert alert-warning d-flex align-items-center" role="alert">
<svg class="bi me-3" role="img" aria-label="Warning:">
<use href="/images/app-icons.svg#app-icon-exclamation-triangle-fill"></use>
</svg>
<div>
Warning: Changing the name of @(Model.StartsWithVowelSound ? "an" : "a")
@Model.SingularName will affect all of its existing uses. Before proceeding, consider
whether it would be better to deactivate this one and create a new one.
</div>
</div>
12 changes: 6 additions & 6 deletions src/WebApp/Pages/Shared/Components/MainMenu/Default.cshtml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
}

<header role="banner">
<nav id="main-nav" class="navbar nav-underline navbar-expand-sm text-bg-brand bg-gradient border-bottom shadow-sm mb-3 d-print-none">
<nav id="main-nav" class="navbar nav-underline navbar-expand-sm bg-gradient border-bottom shadow-sm mb-3 d-print-none">
<div class="container">
@if (Model.IsStaffUser)
{
Expand All @@ -20,9 +20,9 @@
</a>
<a class="navbar-brand nav-link me-2" asp-page="/Index" title="MY_APP_NAME">MY_APP</a>
}
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#main-nav-content"
<button class="navbar-toggler ms-2" type="button" data-bs-toggle="collapse" data-bs-target="#main-nav-content"
aria-controls="main-nav-content" aria-haspopup="menu" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span> <span class="align-middle">Menu</span>
<span class="navbar-toggler-icon"></span> <span class="align-middle menu-text">Menu</span>
</button>

<div class="navbar-collapse collapse d-sm-inline-flex" id="main-nav-content">
Expand Down Expand Up @@ -87,7 +87,7 @@
<hr class="dropdown-divider">
</li>
<li>
<form class="form-inline py-0 px-2" asp-page="/Account/Logout" method="post">
<form class="py-0 px-2" asp-page="/Account/Logout" method="post">
<button type="submit" class="btn btn-outline-danger d-block w-100 px-4 py-2">Sign out</button>
</form>
</li>
Expand All @@ -107,9 +107,9 @@
<svg class="bi me-1 theme-icon-active">
<use href="@iconsFile#app-icon-circle-half"></use>
</svg>
<span class="visually-hidden" id="bd-theme-text">Toggle theme</span>
<span class="ms-2 visually-hidden" id="bd-theme-text">Toggle theme</span>
</button>
<ul class="dropdown-menu dropdown-menu-end" aria-labelledby="bd-theme-text">
<ul class="dropdown-menu dropdown-menu-end" aria-labelledby="bd-theme">
<li>
<button type="button" class="dropdown-item d-flex align-items-center active" data-bs-theme-value="light">
<svg class="bi me-2 opacity-50">
Expand Down
2 changes: 1 addition & 1 deletion src/WebApp/Pages/Shared/_AlertPartial.cshtml
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@

<div>
@Model.Message
@if (Model.Details.Any(s => !string.IsNullOrEmpty(s)))
@if (Model.Details != null && Model.Details.Exists(s => !string.IsNullOrEmpty(s)))
{
@:The following warnings were generated:
<ul class="pt-1 mb-0 ps-3">
Expand Down
4 changes: 2 additions & 2 deletions src/WebApp/Pages/Shared/_ValidationScriptsPartial.cshtml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<script src="~/lib/jquery-validate/dist/jquery.validate.min.js"
integrity="sha512-3YmIbcnlj47HgW2BQGRbLVbzReKt4cASLxa3Z8QY9rohHbqLsuKeccNqFD2wNMfATqYcHCVxGKh5T/7tCWFIRw=="></script>
<script src="~/lib/jquery-validate/jquery.validate.min.js"
integrity="sha512-KFHXdr2oObHKI9w4Hv1XPKc898mE4kgYx58oqsc/JqqdLMDI4YjOLzom+EMlW8HFUd0QfjfAvxSL6sEq/a42fQ=="></script>
<script src="~/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.min.js"
integrity="sha512-xq+Vm8jC94ynOikewaQXMEkJIOBp7iArs3IhFWSWdRT3Pq8wFz46p+ZDFAR7kHnSFf+zUv52B3prRYnbDRdgog=="></script>
<script>
Expand Down
13 changes: 8 additions & 5 deletions src/WebApp/Platform/AppConfiguration/DataPersistence.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ namespace MyApp.WebApp.Platform.AppConfiguration;

public static class DataPersistence
{
public static void AddDataPersistence(this IServiceCollection services, ConfigurationManager configuration)
public static void AddDataPersistence(this IServiceCollection services, ConfigurationManager configuration,
IWebHostEnvironment environment)
{
// When configured, use in-memory data; otherwise use a SQL Server database.
if (AppSettings.DevSettings.UseInMemoryData)
Expand All @@ -41,11 +42,13 @@ public static void AddDataPersistence(this IServiceCollection services, Configur
else
{
// Entity Framework context
services.AddDbContext<AppDbContext>(dbContextOpts =>
services.AddDbContext<AppDbContext>(dbBuilder =>
{
dbContextOpts.UseSqlServer(connectionString, sqlServerOpts => sqlServerOpts.EnableRetryOnFailure());
dbContextOpts.ConfigureWarnings(builder =>
builder.Throw(RelationalEventId.MultipleCollectionIncludeWarning));
dbBuilder
.UseSqlServer(connectionString, sqlServerOpts => sqlServerOpts.EnableRetryOnFailure())
.ConfigureWarnings(builder => builder.Throw(RelationalEventId.MultipleCollectionIncludeWarning));

if (environment.IsDevelopment()) dbBuilder.EnableSensitiveDataLogging();
});

// Dapper DB connection
Expand Down
18 changes: 12 additions & 6 deletions src/WebApp/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,18 +31,24 @@
builder.Services.AddDataProtection().PersistKeysToFileSystem(Directory.CreateDirectory(keysFolder));

// Configure authorization policies.
builder.Services.AddAuthorizationPolicies();
builder.Services.AddAuthorizationHandlers();

// Configure UI services.
builder.Services.AddRazorPages();

var isDevelopment = builder.Environment.IsDevelopment();

// Starting value for HSTS max age is five minutes to allow for debugging.
// For more info on updating HSTS max age value for production, see:
// https://gaepdit.github.io/web-apps/use-https.html#how-to-enable-hsts
if (!builder.Environment.IsDevelopment())
if (!isDevelopment)
{
builder.Services.AddHsts(options => options.MaxAge = TimeSpan.FromMinutes(300))
.AddHttpsRedirection(options => options.RedirectStatusCode = StatusCodes.Status308PermanentRedirect);
.AddHttpsRedirection(options =>
{
options.HttpsPort = 443;
options.RedirectStatusCode = StatusCodes.Status308PermanentRedirect;
});
}

// Configure application monitoring.
Expand Down Expand Up @@ -70,7 +76,7 @@
builder.Services.AddValidators();

// Add data stores.
builder.Services.AddDataPersistence(builder.Configuration);
builder.Services.AddDataPersistence(builder.Configuration, builder.Environment);
builder.Services.AddFileServices(builder.Configuration);

// Initialize database.
Expand All @@ -96,7 +102,7 @@
});

// Configure bundling and minification.
builder.Services.AddWebOptimizer();
builder.Services.AddWebOptimizer(minifyJavaScript: !isDevelopment);

//Add simple cache.
builder.Services.AddMemoryCache();
Expand All @@ -105,7 +111,7 @@
var app = builder.Build();

// Configure error handling.
if (app.Environment.IsDevelopment()) app.UseDeveloperExceptionPage(); // Development
if (isDevelopment) app.UseDeveloperExceptionPage(); // Development
else app.UseExceptionHandler("/Error"); // Production or Staging

// Configure security HTTP headers
Expand Down
Loading

0 comments on commit 6c23f92

Please sign in to comment.