Skip to content

Commit

Permalink
Merge branch 'release/v7.26.0'
Browse files Browse the repository at this point in the history
  • Loading branch information
cleftheris committed Jul 24, 2024
2 parents 294c76e + 7ad3850 commit eb64368
Show file tree
Hide file tree
Showing 22 changed files with 4,840 additions and 27 deletions.
2 changes: 1 addition & 1 deletion src/Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<PropertyGroup Label="Assembly Common">
<Copyright>Copyright (c) 2018 Indice</Copyright>
<TargetFrameworks>net6.0;net7.0;net8.0</TargetFrameworks>
<VersionPrefixBase>7.25</VersionPrefixBase>
<VersionPrefixBase>7.26</VersionPrefixBase>
<VersionPrefixCore>$(VersionPrefixBase).0</VersionPrefixCore>
<VersionPrefixIdentity>$(VersionPrefixBase).0</VersionPrefixIdentity>
<VersionPrefixIdentityUI>$(VersionPrefixBase).0</VersionPrefixIdentityUI>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
#if NET8_0_OR_GREATER
#nullable enable
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Routing.Constraints;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Localization;
using Microsoft.Extensions.Options;
using System.Diagnostics.CodeAnalysis;
using System.Reflection;

namespace Microsoft.AspNetCore.Routing;

/// <summary>
/// Extension methods to configure the Translations json endpoint.
/// </summary>
public static class TranslationsGraphFeatureExtensions
{

/// <summary>
/// Adds translation dependencies. This will configure a resex file key value pair as source and produce a json.
/// </summary>
/// <param name="services">The service collection</param>
/// <param name="configureAction">The action to configure the translations endpoint source of key value pairs</param>
/// <returns>The service collection for further configuration</returns>
public static IServiceCollection AddTranslationGraph(this IServiceCollection services, Action<TranslationsGraphOptions>? configureAction = null) {
services.AddLocalization();
services.Configure<RouteOptions>(options => options.ConstraintMap.Add("culture", typeof(CultureRouteConstraint)));
var options = new TranslationsGraphOptions();
configureAction?.Invoke(options);
services.Configure<TranslationsGraphOptions>((o) => {
o.TranslationsBaseName = options.TranslationsBaseName;
o.TranslationsLocation = options.TranslationsLocation;
o.EndpointRoutePattern = options.EndpointRoutePattern;
});
return services;
}

/// <summary>
/// Maps the Json Translations endpoint.
/// </summary>
/// <param name="routes">The endpoint route builder</param>
/// <returns>The builder for further configureation</returns>

public static IEndpointRouteBuilder MapTranslationGraph(this IEndpointRouteBuilder routes) {
var options = routes.ServiceProvider.GetRequiredService<IOptions<TranslationsGraphOptions>>().Value;
routes.MapGet(options.EndpointRoutePattern, (string lang, IStringLocalizerFactory factory) => {
var strings = factory.Create(options.TranslationsBaseName, options.TranslationsLocation);
return TypedResults.Ok(strings.ToObjectGraph(new System.Globalization.CultureInfo(lang)));
});
return routes;
}
}

/// <summary>
/// Translation json options. Will be used to configure <see cref="TranslationsGraphFeatureExtensions"/>
/// </summary>
public class TranslationsGraphOptions
{
/// <summary>
/// A dot dlimited path to the folder containing the Resex file with the translations key values. Defaults to <strong>"Resources.UiTranslations"</strong>
/// </summary>
public string TranslationsBaseName { get; set; } = "Resources.UiTranslations";

/// <summary>
/// The assembly name containing the translation resex files as embeded resources. Defaults to <strong>Assembly.GetEntryAssembly()!.GetName().Name!</strong>
/// </summary>
public string TranslationsLocation { get; set; } = Assembly.GetEntryAssembly()!.GetName().Name!;
/// <summary>
/// The endpoint route pattern defaults to <strong>"/translations.{lang:culture}.json"</strong>. If changes are made to the path we must paintain the lang parameter.
/// </summary>
[StringSyntax("Route")]
public string EndpointRoutePattern { get; set; } = "/translations.{lang:culture}.json";
}
#nullable disable
#endif
14 changes: 14 additions & 0 deletions src/Indice.AspNetCore/Http/Routing/CultureRouteConstraint.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
namespace Microsoft.AspNetCore.Routing.Constraints;


/// <summary>
/// Culture route constratint.
/// </summary>
public class CultureRouteConstraint : RegexRouteConstraint
{
/// <summary>
/// Constructor
/// </summary>
public CultureRouteConstraint()
: base(@"^[a-zA-Z]{2}(\-[a-zA-Z]{2})?$") { }
}
2 changes: 1 addition & 1 deletion src/Indice.AspNetCore/Indice.AspNetCore.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
<PackageReference Include="Indice.Services" Version="$(VersionPrefixCore)" />
<PackageReference Include="Markdig" Version="0.36.0" />
<PackageReference Include="SixLabors.ImageSharp.Drawing" Version="1.0.0" />
<PackageReference Include="SixLabors.ImageSharp" Version="2.1.8" />
<PackageReference Include="SixLabors.ImageSharp" Version="2.1.9" />
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerGen" Version="6.5.0" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'net6.0'">
Expand Down
16 changes: 13 additions & 3 deletions src/Indice.AspNetCore/Middleware/ProxyDebugMiddleware.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
using Microsoft.AspNetCore.Builder;
#nullable enable
using System.Diagnostics.CodeAnalysis;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.HttpOverrides;
using Microsoft.AspNetCore.Routing;
Expand All @@ -23,8 +25,15 @@ public class DebugProxyOptions
/// <param name="endpoints">The <see cref="IEndpointRouteBuilder"/> to extend</param>
/// <param name="pattern">The route pattern ie /proxy-debug.</param>
/// <param name="configure">Configure action.</param>
/// <remarks>Defaults to endpoint path <strong>/proxy-debug</strong></remarks>
/// <returns></returns>
public static IEndpointConventionBuilder MapProxyDebug(this IEndpointRouteBuilder endpoints, string pattern, Action<DebugProxyOptions> configure = null) {
public static IEndpointConventionBuilder MapProxyDebug(this IEndpointRouteBuilder endpoints,
#if NET7_0_OR_GREATER
[StringSyntax("Route")] string pattern = "/proxy-debug",
#else
string pattern = "/proxy-debug",
#endif
Action<DebugProxyOptions>? configure = null) {
var options = new DebugProxyOptions();
configure?.Invoke(options);
var app = endpoints.CreateApplicationBuilder();
Expand All @@ -48,4 +57,5 @@ public static IEndpointConventionBuilder MapProxyDebug(this IEndpointRouteBuilde
var pipeline = app.Build();
return endpoints.MapGet(pattern, pipeline);
}
}
}
#nullable disable
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<Project Sdk="Microsoft.VisualStudio.JavaScript.Sdk/1.0.784122">
<Project Sdk="Microsoft.VisualStudio.JavaScript.Sdk/1.0.1289302">
<PropertyGroup>
<StartupCommand>npm start</StartupCommand>
<BuildCommand>npm run build:template</BuildCommand>
Expand Down
2 changes: 1 addition & 1 deletion src/Indice.Features.Cases.AspNetCore/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [7.23.3] - 2024-07-12
## [7.25.1] - 2024-07-22
### Added
- Added `PatchCaseMetadata` to AdminCasesController, so you can now update a case's metadata.
- Also added `PatchCaseMetadata` to the IAdminCaseService, so you can now update a case's metadata from your code.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@
<Description>Indice Case Management System</Description>
<RootNamespace>Indice.Features.Cases</RootNamespace>
<Authors>Thanos Panousis, Dimitris Karkanas, Christos Asvestopoulos, Hermes Krouskos</Authors>
<PackageReleaseNotes>Initial release for case management feature.</PackageReleaseNotes>
<PackageReleaseNotes>
- Added PatchCaseMetadata to AdminCasesController, so you can now update a case's metadata.
- Also added PatchCaseMetadata to the IAdminCaseService, so you can now update a case's metadata from your code.
</PackageReleaseNotes>
<PackageTags>AspNetCore;CasesManagement;Cases</PackageTags>
<VersionPrefix>$(VersionPrefixCases)</VersionPrefix>
<!--<Version>7.23.0-beta02</Version>-->
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<Project Sdk="Microsoft.VisualStudio.JavaScript.Sdk/1.0.784122">
<Project Sdk="Microsoft.VisualStudio.JavaScript.Sdk/1.0.1289302">
<PropertyGroup>
<StartupCommand>npm start</StartupCommand>
<BuildCommand>npm run build:template</BuildCommand>
Expand Down
5 changes: 5 additions & 0 deletions src/Indice.Features.Identity.UI/IdentityUIOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,11 @@ public class IdentityUIOptions
public List<HomePageLink> HomepageLinks { get; } = new List<HomePageLink>() {
new HomePageLink("Admin","~/admin", CssClass:"admin", VisibilityPredicate: user => user.IsAdmin())
};
/// <summary>
/// Should show the Add Email page before sending the confirmation email prompt (in case of pernding confirmation login) or Confirm emai immediately.
/// </summary>
/// <remarks>Useful when user store is from a migrated database and we need to force users to add an email where an email is not present. Defaults to true.</remarks>
public bool ShowAddEmailPrompt { get; set; } = true;

/// <summary>Adds a homepage link to the a service definition cards.</summary>
/// <param name="displayName">The label.</param>
Expand Down
7 changes: 5 additions & 2 deletions src/Indice.Features.Identity.UI/Pages/AddEmail.cs
Original file line number Diff line number Diff line change
Expand Up @@ -58,11 +58,14 @@ public virtual async Task<IActionResult> OnGetAsync([FromQuery] string? returnUr
if (UserManager.StateProvider.CurrentState != UserState.RequiresEmailVerification) {
return Redirect(GetRedirectUrl(UserManager.StateProvider.CurrentState, returnUrl) ?? "/");
}
Input.Email = user.Email;
Input.ReturnUrl = returnUrl;
if (!UiOptions.ShowAddEmailPrompt) {
return await OnPostAsync(returnUrl);
}
TempData.Put(TempDataKey, new ExtendedValidationTempDataModel {
Alert = AlertModel.Info(_localizer["Please enter your email address so we can verify it before we continue."])
});
Input.Email = user.Email;
Input.ReturnUrl = returnUrl;
return Page();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
</div>
<div class="form-group">
<div class="form-row m-0 w-100">
<div class="@(tempData.DisableForm ? "col-sm-6" : String.Empty) col-12 py-1">
<div indice-if="Model.UiOptions.ShowAddEmailPrompt" class="@(tempData.DisableForm ? "col-sm-6" : string.Empty) col-12 py-1">
<button class="btn btn-info btn-block" type="submit" disabled="@tempData.DisableForm">@Localizer["Save"]</button>
</div>
@if (tempData.DisableForm || !string.IsNullOrEmpty(tempData.NextStepUrl))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
</div>
<div class="mb-3">
<div class="row m-0 w-100">
<div class="@(tempData.DisableForm ? "col-sm-6" : String.Empty) col-12 py-1">
<div indice-if="Model.UiOptions.ShowAddEmailPrompt" class="@(tempData.DisableForm ? "col-sm-6" : string.Empty) col-12 py-1">
<button class="btn btn-info w-100" type="submit" disabled="@tempData.DisableForm">@Localizer["Save"]</button>
</div>
@if (tempData.DisableForm || !string.IsNullOrEmpty(tempData.NextStepUrl))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@
</main>
</div>
<script type="text/javascript" src="~/lib/jquery/dist/jquery.min.js" csp-nonce="true"></script>
<script type="text/javascript" src="~/lib/popper.js/dist/umd/popper.min.js" csp-nonce="true"></script>
<script type="text/javascript" src="~/lib/popper.js/dist/umd/index.min.js" csp-nonce="true"></script>
<script type="text/javascript" src="~/lib/bootstrap/dist/js/bootstrap.bundle.min.js" csp-nonce="true"></script>
<script type="text/javascript" src="~/js/site.js" csp-nonce="true" asp-append-version="true"></script>
@RenderSection("scripts", required: false)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
<span asp-validation-for="Input.Email" class="text-danger"></span>
</div>
<div class="row justify-items-center place-items-center">
<div class="@(tempData.DisableForm ? "col-span-6" : "col-span-12" ) flex flex-col">
<div indice-if="Model.UiOptions.ShowAddEmailPrompt" class="@(tempData.DisableForm ? "col-span-6" : "col-span-12" ) flex flex-col">
<button class="btn btn-primary" type="submit" disabled="@tempData.DisableForm">@Localizer["Save"]</button>
</div>
@if (tempData.DisableForm || !string.IsNullOrEmpty(tempData.NextStepUrl))
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<Project Sdk="Microsoft.VisualStudio.JavaScript.Sdk/1.0.784122">
<Project Sdk="Microsoft.VisualStudio.JavaScript.Sdk/1.0.1289302">
<PropertyGroup>
<StartupCommand>npm start</StartupCommand>
<BuildCommand>npm run build:template</BuildCommand>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<Project Sdk="Microsoft.VisualStudio.JavaScript.Sdk/1.0.784122">
<Project Sdk="Microsoft.VisualStudio.JavaScript.Sdk/1.0.1289302">
<PropertyGroup>
<StartupCommand>npm start</StartupCommand>
<BuildCommand>npm run build:template</BuildCommand>
Expand Down
107 changes: 107 additions & 0 deletions test/Indice.Services.Tests/EndpointTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
#if NET8_0_OR_GREATER
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.TestHost;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Xunit;
using Xunit.Abstractions;
using Microsoft.AspNetCore.Routing;

namespace Indice.Services.Tests;
public class EndpointTests : IAsyncDisposable
{
// Constants
private const string BASE_URL = "https://server";
// Private fields
private readonly HttpClient _httpClient;
private readonly ITestOutputHelper _output;
private ServiceProvider _serviceProvider;

public EndpointTests(ITestOutputHelper output) {
_output = output;
var builder = new WebHostBuilder();
builder.ConfigureAppConfiguration(builder => {
builder.AddInMemoryCollection(new Dictionary<string, string> {
["MySection:MyKey"] = "TestValue"
});
});
builder.ConfigureServices(services => {
var configuration = services.BuildServiceProvider().GetService<IConfiguration>();
services.AddTransient<IEventDispatcherFactory, DefaultEventDispatcherFactory>();
services.AddRouting();

services.AddTranslationGraph((o) => {
o.TranslationsBaseName = "Resources.TranslationsApi";
o.TranslationsLocation = typeof(EndpointTests).Assembly.GetName().Name;
});
_serviceProvider = services.BuildServiceProvider();
});
builder.Configure(app => {
app.UseRouting();
app.UseEndpoints(e => e.MapTranslationGraph());
});
var server = new TestServer(builder);
var handler = server.CreateHandler();
_httpClient = new HttpClient(handler) {
BaseAddress = new Uri(BASE_URL)
};
}

#region Facts
[Fact]
public async Task Test_GetTranslatio_English() {
//Create the Campaign
var translationResponse = await _httpClient.GetAsync("/translations.en.json");
var translationResponseJson = await translationResponse.Content.ReadAsStringAsync();
if (!translationResponse.IsSuccessStatusCode) {
_output.WriteLine(translationResponseJson);
}

Assert.True(translationResponse.IsSuccessStatusCode);
Assert.NotEmpty(translationResponseJson);
}
[Fact]
public async Task Test_GetTranslatio_Greek() {
//Create the Campaign
var translationResponse = await _httpClient.GetAsync("/translations.el.json");
var translationResponseJson = await translationResponse.Content.ReadAsStringAsync();
if (!translationResponse.IsSuccessStatusCode) {
_output.WriteLine(translationResponseJson);
}

Assert.True(translationResponse.IsSuccessStatusCode);
Assert.NotEmpty(translationResponseJson);
}
[Fact]
public async Task Test_GetTranslation_For_Culture_Not_Exists() {
//Create the Campaign
var translationResponse = await _httpClient.GetAsync("/translations.ru.json");
var translationResponseJson = await translationResponse.Content.ReadAsStringAsync();
if (!translationResponse.IsSuccessStatusCode) {
_output.WriteLine(translationResponseJson);
}

Assert.True(translationResponse.IsSuccessStatusCode);
Assert.NotEmpty(translationResponseJson);
}
[Fact]
public async Task Test_GetTranslation_For_Invalid_Culture() {
//Create the Campaign
var translationResponse = await _httpClient.GetAsync("/translations.invalid.json");
Assert.False(translationResponse.IsSuccessStatusCode);
}
#endregion

public async ValueTask DisposeAsync() {
await _serviceProvider.DisposeAsync();
}
}


#endif
Loading

0 comments on commit eb64368

Please sign in to comment.