-
Notifications
You must be signed in to change notification settings - Fork 10.4k
[main] [release/6.0] Initial Bootstrap v4 SxS support #36397
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
cd653ae
8d49af8
b3fb863
aeb80a2
4fdf7ad
3533884
87d5bcf
05c35f4
8e79939
dfcddf9
48112ca
d154f41
5be8eb2
20010c3
eb5c985
bf6180f
5bb3914
89a7cb7
b971d3f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,10 +1,15 @@ | ||
// Licensed to the .NET Foundation under one or more agreements. | ||
// The .NET Foundation licenses this file to you under the MIT license. | ||
|
||
using System.Reflection; | ||
using System.Linq; | ||
using Microsoft.AspNetCore.Identity.UI; | ||
using Microsoft.AspNetCore.Identity.UI.Services; | ||
using Microsoft.AspNetCore.Mvc.ApplicationParts; | ||
using Microsoft.Extensions.DependencyInjection; | ||
using Microsoft.Extensions.DependencyInjection.Extensions; | ||
using Microsoft.AspNetCore.Mvc.Razor.Compilation; | ||
using Microsoft.AspNetCore.Hosting; | ||
|
||
namespace Microsoft.AspNetCore.Identity | ||
{ | ||
|
@@ -27,7 +32,29 @@ public static class IdentityBuilderUIExtensions | |
public static IdentityBuilder AddDefaultUI(this IdentityBuilder builder) | ||
{ | ||
builder.AddSignInManager(); | ||
builder.Services.AddMvc(); | ||
builder.Services | ||
.AddMvc() | ||
.ConfigureApplicationPartManager(apm => | ||
{ | ||
// We try to resolve the UI framework that was used by looking at the entry assembly. | ||
// When an app runs, the entry assembly will point to the built app. In some rare cases | ||
// (functional testing) the app assembly will be different, and we'll try to locate it through | ||
// the same mechanism that MVC uses today. | ||
// Finally, if for some reason we aren't able to find the assembly, we'll use our default value | ||
// (Bootstrap5) | ||
if (!TryResolveUIFramework(Assembly.GetEntryAssembly(), out var framework) && | ||
!TryResolveUIFramework(GetApplicationAssembly(builder), out framework)) | ||
{ | ||
framework = default; | ||
} | ||
|
||
var parts = new ConsolidatedAssemblyApplicationPartFactory().GetApplicationParts(typeof(IdentityBuilderUIExtensions).Assembly); | ||
foreach (var part in parts) | ||
{ | ||
apm.ApplicationParts.Add(part); | ||
} | ||
apm.FeatureProviders.Add(new ViewVersionFeatureProvider(framework)); | ||
}); | ||
|
||
builder.Services.ConfigureOptions( | ||
typeof(IdentityDefaultUIConfigureOptions<>) | ||
|
@@ -36,5 +63,96 @@ public static IdentityBuilder AddDefaultUI(this IdentityBuilder builder) | |
|
||
return builder; | ||
} | ||
|
||
private static Assembly GetApplicationAssembly(IdentityBuilder builder) | ||
{ | ||
// This is the same logic that MVC follows to find the application assembly. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @HaoK Can you link to the existing logic? Having trouble finding it by copy/pasting the source here. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Its in release/6.0 only right now, as I'm unable to port this to main so far https://github.com/dotnet/aspnetcore/blob/release/6.0/src/Identity/UI/src/IdentityBuilderUIExtensions.cs#L67 |
||
var environment = builder.Services.Where(d => d.ServiceType == typeof(IWebHostEnvironment)).ToArray(); | ||
var applicationName = ((IWebHostEnvironment)environment.LastOrDefault()?.ImplementationInstance) | ||
.ApplicationName; | ||
|
||
if (applicationName == null) | ||
{ | ||
return null; | ||
} | ||
var appAssembly = Assembly.Load(applicationName); | ||
return appAssembly; | ||
} | ||
|
||
private static bool TryResolveUIFramework(Assembly assembly, out UIFramework uiFramework) | ||
{ | ||
uiFramework = default; | ||
|
||
var metadata = assembly?.GetCustomAttributes<UIFrameworkAttribute>() | ||
.SingleOrDefault()?.UIFramework; // Bootstrap5 is the default | ||
if (metadata == null) | ||
{ | ||
return false; | ||
} | ||
|
||
// If we find the metadata there must be a valid framework here. | ||
if (!Enum.TryParse(metadata, ignoreCase: true, out uiFramework)) | ||
{ | ||
var enumValues = string.Join(", ", Enum.GetNames(typeof(UIFramework)).Select(v => $"'{v}'")); | ||
throw new InvalidOperationException( | ||
$"Found an invalid value for the 'IdentityUIFrameworkVersion'. Valid values are {enumValues}"); | ||
} | ||
|
||
return true; | ||
} | ||
|
||
internal class ViewVersionFeatureProvider : IApplicationFeatureProvider<ViewsFeature> | ||
{ | ||
private readonly UIFramework _framework; | ||
|
||
public ViewVersionFeatureProvider(UIFramework framework) => _framework = framework; | ||
|
||
public void PopulateFeature(IEnumerable<ApplicationPart> parts, ViewsFeature feature) | ||
{ | ||
var viewsToRemove = new List<CompiledViewDescriptor>(); | ||
foreach (var descriptor in feature.ViewDescriptors) | ||
{ | ||
if (IsIdentityUIView(descriptor)) | ||
{ | ||
switch (_framework) | ||
{ | ||
case UIFramework.Bootstrap4: | ||
if (descriptor.Type.FullName.Contains("V5")) | ||
{ | ||
// Remove V5 views | ||
viewsToRemove.Add(descriptor); | ||
} | ||
else | ||
{ | ||
// Fix up paths to eliminate version subdir | ||
descriptor.RelativePath = descriptor.RelativePath.Replace("V4/", ""); | ||
} | ||
break; | ||
case UIFramework.Bootstrap5: | ||
if (descriptor.Type.FullName.Contains("V4")) | ||
{ | ||
// Remove V4 views | ||
viewsToRemove.Add(descriptor); | ||
} | ||
else | ||
{ | ||
// Fix up paths to eliminate version subdir | ||
descriptor.RelativePath = descriptor.RelativePath.Replace("V5/", ""); | ||
} | ||
break; | ||
default: | ||
throw new InvalidOperationException($"Unknown framework: {_framework}"); | ||
} | ||
} | ||
} | ||
|
||
foreach (var descriptorToRemove in viewsToRemove) | ||
{ | ||
feature.ViewDescriptors.Remove(descriptorToRemove); | ||
} | ||
} | ||
|
||
private static bool IsIdentityUIView(CompiledViewDescriptor desc) => desc.RelativePath.StartsWith("/Areas/Identity", StringComparison.OrdinalIgnoreCase) && desc.Type.Assembly == typeof(IdentityBuilderUIExtensions).Assembly; | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
cc @captainsafia @halter73 this is the other piece of code in the framework that does a bad thing 😄