mirror of https://github.com/abpframework/abp.git
3751 changed files with 1161 additions and 787996 deletions
@ -0,0 +1,3 @@ |
|||
# 如何对MVC / Razor页面应用程序使用Azure Active Directory身份验证 |
|||
|
|||
TODO... |
|||
@ -0,0 +1,113 @@ |
|||
# 如何为MVC / Razor页面应用程序自定义登录页面 |
|||
|
|||
当你使用[应用程序启动模板](../Startup-Templates/Application.md)创建了一个新的应用程序, 登录页面的源代码并不在你的解决方案中,所以你不能直接更改. 它来自[账户模块](../Modules/Account.md),使用[NuGet包](https://www.nuget.org/packages/Volo.Abp.Account.Web)引用. |
|||
|
|||
本文介绍了如何为自己的应用程序自定义登录页面. |
|||
|
|||
## 创建登录 PageModel |
|||
|
|||
创建一个新的类继承账户模块的[LoginModel](https://github.com/abpframework/abp/blob/037ef9abe024c03c1f89ab6c933710bcfe3f5c93/modules/account/src/Volo.Abp.Account.Web/Pages/Account/Login.cshtml.cs). |
|||
|
|||
````csharp |
|||
public class CustomLoginModel : LoginModel |
|||
{ |
|||
public CustomLoginModel( |
|||
Microsoft.AspNetCore.Authentication.IAuthenticationSchemeProvider schemeProvider, |
|||
Microsoft.Extensions.Options.IOptions<Volo.Abp.Account.Web.AbpAccountOptions> accountOptions) |
|||
: base(schemeProvider, accountOptions) |
|||
{ |
|||
} |
|||
} |
|||
```` |
|||
|
|||
> 在这里命令约定很重要. 如果你的类名不是以 `LoginModel` 结束,你需要手动在[依赖注入](../Dependency-Injection.md)系统替换 `LoginModel`. |
|||
|
|||
然后你可以覆盖任何方法并添加用户界面需要的新方法和属性. |
|||
|
|||
## 重写登录页面UI |
|||
|
|||
在 **Pages** 目录下创建名为 **Account** 的文件夹,并在这个文件夹中创建 `Login.cshtml` ,借助[虚拟文件系统](../Virtual-File-System.md)它会自动覆盖账户模块的页面文件. |
|||
|
|||
自定义页面一个很好的开始是复制它的源代码. [点击这里](https://github.com/abpframework/abp/blob/dev/modules/account/src/Volo.Abp.Account.Web/Pages/Account/Login.cshtml)找到登录页面的源码. 在编写本文档时,源代码如下: |
|||
|
|||
````xml |
|||
@page |
|||
@using Volo.Abp.Account.Settings |
|||
@using Volo.Abp.Settings |
|||
@model Acme.BookStore.Web.Pages.Account.CustomLoginModel |
|||
@inherits Volo.Abp.Account.Web.Pages.Account.AccountPage |
|||
@inject Volo.Abp.Settings.ISettingProvider SettingProvider |
|||
@if (Model.EnableLocalLogin) |
|||
{ |
|||
<div class="card mt-3 shadow-sm rounded"> |
|||
<div class="card-body p-5"> |
|||
<h4>@L["Login"]</h4> |
|||
@if (await SettingProvider.IsTrueAsync(AccountSettingNames.IsSelfRegistrationEnabled)) |
|||
{ |
|||
<strong> |
|||
@L["AreYouANewUser"] |
|||
<a href="@Url.Page("./Register", new {returnUrl = Model.ReturnUrl, returnUrlHash = Model.ReturnUrlHash})" class="text-decoration-none">@L["Register"]</a> |
|||
</strong> |
|||
} |
|||
<form method="post" class="mt-4"> |
|||
<input asp-for="ReturnUrl" /> |
|||
<input asp-for="ReturnUrlHash" /> |
|||
<div class="form-group"> |
|||
<label asp-for="LoginInput.UserNameOrEmailAddress"></label> |
|||
<input asp-for="LoginInput.UserNameOrEmailAddress" class="form-control" /> |
|||
<span asp-validation-for="LoginInput.UserNameOrEmailAddress" class="text-danger"></span> |
|||
</div> |
|||
<div class="form-group"> |
|||
<label asp-for="LoginInput.Password"></label> |
|||
<input asp-for="LoginInput.Password" class="form-control" /> |
|||
<span asp-validation-for="LoginInput.Password" class="text-danger"></span> |
|||
</div> |
|||
<div class="form-check"> |
|||
<label asp-for="LoginInput.RememberMe" class="form-check-label"> |
|||
<input asp-for="LoginInput.RememberMe" class="form-check-input" /> |
|||
@Html.DisplayNameFor(m => m.LoginInput.RememberMe) |
|||
</label> |
|||
</div> |
|||
<abp-button type="submit" button-type="Primary" name="Action" value="Login" class="btn-block btn-lg mt-3">@L["Login"]</abp-button> |
|||
</form> |
|||
</div> |
|||
|
|||
<div class="card-footer text-center border-0"> |
|||
<abp-button type="button" button-type="Link" name="Action" value="Cancel" class="px-2 py-0">@L["Cancel"]</abp-button> @* TODO: Only show if identity server is used *@ |
|||
</div> |
|||
</div> |
|||
} |
|||
|
|||
@if (Model.VisibleExternalProviders.Any()) |
|||
{ |
|||
<div class="col-md-6"> |
|||
<h4>@L["UseAnotherServiceToLogIn"]</h4> |
|||
<form asp-page="./Login" asp-page-handler="ExternalLogin" asp-route-returnUrl="@Model.ReturnUrl" asp-route-returnUrlHash="@Model.ReturnUrlHash" method="post"> |
|||
<input asp-for="ReturnUrl" /> |
|||
<input asp-for="ReturnUrlHash" /> |
|||
@foreach (var provider in Model.VisibleExternalProviders) |
|||
{ |
|||
<button type="submit" class="btn btn-primary" name="provider" value="@provider.AuthenticationScheme" title="@L["GivenTenantIsNotAvailable", provider.DisplayName]">@provider.DisplayName</button> |
|||
} |
|||
</form> |
|||
</div> |
|||
} |
|||
|
|||
@if (!Model.EnableLocalLogin && !Model.VisibleExternalProviders.Any()) |
|||
{ |
|||
<div class="alert alert-warning"> |
|||
<strong>@L["InvalidLoginRequest"]</strong> |
|||
@L["ThereAreNoLoginSchemesConfiguredForThisClient"] |
|||
</div> |
|||
} |
|||
```` |
|||
|
|||
只需更改 `@model` 为 `Acme.BookStore.Web.Pages.Account.CustomLoginModel` 使用自定义的 `PageModel` 类. 你可以做任何应用程序需要的更改. |
|||
|
|||
## 本文的源代码 |
|||
|
|||
你可以在[这里](https://github.com/abpframework/abp-samples/tree/master/aspnet-core/Authentication-Customization)找到已完成的示例源码. |
|||
|
|||
## 另请参阅 |
|||
|
|||
* [ASP.NET Core (MVC / Razor Pages) 用户界面自定义指南](../UI/AspNetCore/Customization-User-Interface.md). |
|||
@ -0,0 +1,3 @@ |
|||
# 如何为ABP应用程序定制SignIn Manager |
|||
|
|||
TODO... |
|||
@ -0,0 +1,9 @@ |
|||
# "如何" 指南 |
|||
|
|||
本部分包含一些常见问题的 "如何" 指南. 尽管其中是一些常见的开发任务和ABP并不直接相关,但我们认为有一些具体的示例可以直接与基于ABP的应用程序一起使用. |
|||
|
|||
## Authentication |
|||
|
|||
* [如何为MVC / Razor页面应用程序自定义登录页面](Customize-Login-Page-MVC.md) |
|||
* [如何对MVC / Razor页面应用程序使用Azure Active Directory身份验证](Azure-Active-Directory-Authentication-MVC.md) |
|||
* [如何为ABP应用程序定制SignIn Manager](Customize-SignIn-Manager.md) |
|||
|
After Width: | Height: | Size: 52 KiB |
|
After Width: | Height: | Size: 120 KiB |
|
After Width: | Height: | Size: 76 KiB |
|
After Width: | Height: | Size: 28 KiB |
|
After Width: | Height: | Size: 88 KiB |
@ -0,0 +1,34 @@ |
|||
using JetBrains.Annotations; |
|||
|
|||
namespace Volo.Abp.Authorization.Permissions |
|||
{ |
|||
public static class PermissionDefinitionContextExtensions |
|||
{ |
|||
/// <summary>
|
|||
/// Finds and disables a permission with the given <paramref name="name"/>.
|
|||
/// Returns false if given permission was not found.
|
|||
/// </summary>
|
|||
/// <param name="context">Permission definition context</param>
|
|||
/// <param name="name">Name of the permission</param>
|
|||
/// <returns>
|
|||
/// Returns true if given permission was found.
|
|||
/// Returns false if given permission was not found.
|
|||
/// </returns>
|
|||
public static bool TryDisablePermission( |
|||
[NotNull] this IPermissionDefinitionContext context, |
|||
[NotNull] string name) |
|||
{ |
|||
Check.NotNull(context, nameof(context)); |
|||
Check.NotNull(name, nameof(name)); |
|||
|
|||
var permission = context.GetPermissionOrNull(name); |
|||
if (permission == null) |
|||
{ |
|||
return false; |
|||
} |
|||
|
|||
permission.IsEnabled = false; |
|||
return true; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,9 @@ |
|||
namespace Volo.Abp.AuditLogging |
|||
{ |
|||
public class EntityChangeWithUsername |
|||
{ |
|||
public EntityChange EntityChange { get; set; } |
|||
|
|||
public string UserName { get; set; } |
|||
} |
|||
} |
|||
@ -0,0 +1,52 @@ |
|||
import { ConfigState } from '@abp/ng.core'; |
|||
import { Component } from '@angular/core'; |
|||
import { createComponentFactory, Spectator } from '@ngneat/spectator'; |
|||
import { NgxValidateCoreModule, validatePassword } from '@ngx-validate/core'; |
|||
import { NgxsModule, Store } from '@ngxs/store'; |
|||
import { HttpClient } from '@angular/common/http'; |
|||
import { getPasswordValidators } from '../utils'; |
|||
import { Validators } from '@angular/forms'; |
|||
|
|||
@Component({ template: '', selector: 'abp-dummy' }) |
|||
class DummyComponent {} |
|||
|
|||
describe('ValidationUtils', () => { |
|||
let spectator: Spectator<DummyComponent>; |
|||
const createComponent = createComponentFactory({ |
|||
component: DummyComponent, |
|||
imports: [NgxsModule.forRoot([ConfigState]), NgxValidateCoreModule.forRoot()], |
|||
mocks: [HttpClient], |
|||
}); |
|||
|
|||
beforeEach(() => (spectator = createComponent())); |
|||
|
|||
describe('#getPasswordValidators', () => { |
|||
it('should return password valdiators', () => { |
|||
const store = spectator.get(Store); |
|||
store.reset({ |
|||
ConfigState: { |
|||
setting: { |
|||
values: { |
|||
'Abp.Identity.Password.RequiredLength': '6', |
|||
'Abp.Identity.Password.RequiredUniqueChars': '1', |
|||
'Abp.Identity.Password.RequireNonAlphanumeric': 'True', |
|||
'Abp.Identity.Password.RequireLowercase': 'True', |
|||
'Abp.Identity.Password.RequireUppercase': 'True', |
|||
'Abp.Identity.Password.RequireDigit': 'True', |
|||
}, |
|||
}, |
|||
}, |
|||
}); |
|||
const validators = getPasswordValidators(store); |
|||
const expectedValidators = [ |
|||
validatePassword(['number', 'small', 'capital', 'special']), |
|||
Validators.minLength(6), |
|||
Validators.maxLength(128), |
|||
]; |
|||
|
|||
validators.forEach((validator, index) => { |
|||
expect(validator.toString()).toBe(expectedValidators[index].toString()); |
|||
}); |
|||
}); |
|||
}); |
|||
}); |
|||
@ -1,2 +1,3 @@ |
|||
export * from './widget-utils'; |
|||
export * from './date-parser-formatter'; |
|||
export * from './validation-utils'; |
|||
|
|||
@ -0,0 +1,45 @@ |
|||
import { Store } from '@ngxs/store'; |
|||
import { ABP, ConfigState } from '@abp/ng.core'; |
|||
import { PasswordRules, validatePassword } from '@ngx-validate/core'; |
|||
import { Validators, ValidatorFn } from '@angular/forms'; |
|||
|
|||
const { minLength, maxLength } = Validators; |
|||
|
|||
export function getPasswordValidators(store: Store): ValidatorFn[] { |
|||
const getRule = getRuleFn(store); |
|||
|
|||
const passwordRulesArr = [] as PasswordRules; |
|||
let requiredLength = 1; |
|||
|
|||
if (getRule('RequireDigit') === 'true') { |
|||
passwordRulesArr.push('number'); |
|||
} |
|||
|
|||
if (getRule('RequireLowercase') === 'true') { |
|||
passwordRulesArr.push('small'); |
|||
} |
|||
|
|||
if (getRule('RequireUppercase') === 'true') { |
|||
passwordRulesArr.push('capital'); |
|||
} |
|||
|
|||
if (getRule('RequireNonAlphanumeric') === 'true') { |
|||
passwordRulesArr.push('special'); |
|||
} |
|||
|
|||
if (Number.isInteger(+getRule('RequiredLength'))) { |
|||
requiredLength = +getRule('RequiredLength'); |
|||
} |
|||
|
|||
return [validatePassword(passwordRulesArr), minLength(requiredLength), maxLength(128)]; |
|||
} |
|||
|
|||
function getRuleFn(store: Store) { |
|||
return (key: string) => { |
|||
const passwordRules: ABP.Dictionary<string> = store.selectSnapshot( |
|||
ConfigState.getSettings('Identity.Password'), |
|||
); |
|||
|
|||
return (passwordRules[`Abp.Identity.Password.${key}`] || '').toLowerCase(); |
|||
}; |
|||
} |
|||
@ -1,25 +0,0 @@ |
|||
|
|||
Microsoft Visual Studio Solution File, Format Version 12.00 |
|||
# Visual Studio Version 16 |
|||
VisualStudioVersion = 16.0.29326.143 |
|||
MinimumVisualStudioVersion = 10.0.40219.1 |
|||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BasicAspNetCoreApplication", "BasicAspNetCoreApplication\BasicAspNetCoreApplication.csproj", "{069D79DF-2340-4101-9B6E-32FEEC6893A8}" |
|||
EndProject |
|||
Global |
|||
GlobalSection(SolutionConfigurationPlatforms) = preSolution |
|||
Debug|Any CPU = Debug|Any CPU |
|||
Release|Any CPU = Release|Any CPU |
|||
EndGlobalSection |
|||
GlobalSection(ProjectConfigurationPlatforms) = postSolution |
|||
{069D79DF-2340-4101-9B6E-32FEEC6893A8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU |
|||
{069D79DF-2340-4101-9B6E-32FEEC6893A8}.Debug|Any CPU.Build.0 = Debug|Any CPU |
|||
{069D79DF-2340-4101-9B6E-32FEEC6893A8}.Release|Any CPU.ActiveCfg = Release|Any CPU |
|||
{069D79DF-2340-4101-9B6E-32FEEC6893A8}.Release|Any CPU.Build.0 = Release|Any CPU |
|||
EndGlobalSection |
|||
GlobalSection(SolutionProperties) = preSolution |
|||
HideSolutionNode = FALSE |
|||
EndGlobalSection |
|||
GlobalSection(ExtensibilityGlobals) = postSolution |
|||
SolutionGuid = {9AC9422D-35CC-4A8E-9F5B-D695565E9B45} |
|||
EndGlobalSection |
|||
EndGlobal |
|||
@ -1,33 +0,0 @@ |
|||
using Microsoft.AspNetCore.Builder; |
|||
using Microsoft.Extensions.Hosting; |
|||
using Volo.Abp; |
|||
using Volo.Abp.AspNetCore.Mvc; |
|||
using Volo.Abp.Autofac; |
|||
using Volo.Abp.Modularity; |
|||
|
|||
namespace BasicAspNetCoreApplication |
|||
{ |
|||
[DependsOn(typeof(AbpAspNetCoreMvcModule))] |
|||
[DependsOn(typeof(AbpAutofacModule))] //Add dependency to ABP Autofac module
|
|||
public class AppModule : AbpModule |
|||
{ |
|||
public override void OnApplicationInitialization(ApplicationInitializationContext context) |
|||
{ |
|||
var app = context.GetApplicationBuilder(); |
|||
var env = context.GetEnvironment(); |
|||
|
|||
if (env.IsDevelopment()) |
|||
{ |
|||
app.UseDeveloperExceptionPage(); |
|||
} |
|||
else |
|||
{ |
|||
app.UseExceptionHandler("/Error"); |
|||
} |
|||
|
|||
app.UseStaticFiles(); |
|||
app.UseRouting(); |
|||
app.UseMvcWithDefaultRouteAndArea(); |
|||
} |
|||
} |
|||
} |
|||
@ -1,16 +0,0 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk.Web"> |
|||
|
|||
<PropertyGroup> |
|||
<TargetFramework>netcoreapp3.1</TargetFramework> |
|||
</PropertyGroup> |
|||
|
|||
<ItemGroup> |
|||
<ProjectReference Include="..\..\..\framework\src\Volo.Abp.Autofac\Volo.Abp.Autofac.csproj" /> |
|||
<ProjectReference Include="..\..\..\framework\src\Volo.Abp.AspNetCore.Mvc\Volo.Abp.AspNetCore.Mvc.csproj" /> |
|||
</ItemGroup> |
|||
|
|||
<ItemGroup> |
|||
<Folder Include="Controllers\" /> |
|||
</ItemGroup> |
|||
|
|||
</Project> |
|||
@ -1,26 +0,0 @@ |
|||
@page |
|||
@model ErrorModel |
|||
@{ |
|||
ViewData["Title"] = "Error"; |
|||
} |
|||
|
|||
<h1 class="text-danger">Error.</h1> |
|||
<h2 class="text-danger">An error occurred while processing your request.</h2> |
|||
|
|||
@if (Model.ShowRequestId) |
|||
{ |
|||
<p> |
|||
<strong>Request ID:</strong> <code>@Model.RequestId</code> |
|||
</p> |
|||
} |
|||
|
|||
<h3>Development Mode</h3> |
|||
<p> |
|||
Swapping to the <strong>Development</strong> environment displays detailed information about the error that occurred. |
|||
</p> |
|||
<p> |
|||
<strong>The Development environment shouldn't be enabled for deployed applications.</strong> |
|||
It can result in displaying sensitive information from exceptions to end users. |
|||
For local debugging, enable the <strong>Development</strong> environment by setting the <strong>ASPNETCORE_ENVIRONMENT</strong> environment variable to <strong>Development</strong> |
|||
and restarting the app. |
|||
</p> |
|||
@ -1,31 +0,0 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Diagnostics; |
|||
using System.Linq; |
|||
using System.Threading.Tasks; |
|||
using Microsoft.AspNetCore.Mvc; |
|||
using Microsoft.AspNetCore.Mvc.RazorPages; |
|||
using Microsoft.Extensions.Logging; |
|||
|
|||
namespace BasicAspNetCoreApplication.Pages |
|||
{ |
|||
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)] |
|||
public class ErrorModel : PageModel |
|||
{ |
|||
public string RequestId { get; set; } |
|||
|
|||
public bool ShowRequestId => !string.IsNullOrEmpty(RequestId); |
|||
|
|||
private readonly ILogger<ErrorModel> _logger; |
|||
|
|||
public ErrorModel(ILogger<ErrorModel> logger) |
|||
{ |
|||
_logger = logger; |
|||
} |
|||
|
|||
public void OnGet() |
|||
{ |
|||
RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier; |
|||
} |
|||
} |
|||
} |
|||
@ -1,10 +0,0 @@ |
|||
@page |
|||
@model IndexModel |
|||
@{ |
|||
ViewData["Title"] = "Home page"; |
|||
} |
|||
|
|||
<div class="text-center"> |
|||
<h1 class="display-4">Welcome</h1> |
|||
<p>Learn about <a href="https://docs.microsoft.com/aspnet/core">building Web apps with ASP.NET Core</a>.</p> |
|||
</div> |
|||
@ -1,25 +0,0 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Threading.Tasks; |
|||
using Microsoft.AspNetCore.Mvc; |
|||
using Microsoft.AspNetCore.Mvc.RazorPages; |
|||
using Microsoft.Extensions.Logging; |
|||
|
|||
namespace BasicAspNetCoreApplication.Pages |
|||
{ |
|||
public class IndexModel : PageModel |
|||
{ |
|||
private readonly ILogger<IndexModel> _logger; |
|||
|
|||
public IndexModel(ILogger<IndexModel> logger) |
|||
{ |
|||
_logger = logger; |
|||
} |
|||
|
|||
public void OnGet() |
|||
{ |
|||
|
|||
} |
|||
} |
|||
} |
|||
@ -1,8 +0,0 @@ |
|||
@page |
|||
@model PrivacyModel |
|||
@{ |
|||
ViewData["Title"] = "Privacy Policy"; |
|||
} |
|||
<h1>@ViewData["Title"]</h1> |
|||
|
|||
<p>Use this page to detail your site's privacy policy.</p> |
|||
@ -1,24 +0,0 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Threading.Tasks; |
|||
using Microsoft.AspNetCore.Mvc; |
|||
using Microsoft.AspNetCore.Mvc.RazorPages; |
|||
using Microsoft.Extensions.Logging; |
|||
|
|||
namespace BasicAspNetCoreApplication.Pages |
|||
{ |
|||
public class PrivacyModel : PageModel |
|||
{ |
|||
private readonly ILogger<PrivacyModel> _logger; |
|||
|
|||
public PrivacyModel(ILogger<PrivacyModel> logger) |
|||
{ |
|||
_logger = logger; |
|||
} |
|||
|
|||
public void OnGet() |
|||
{ |
|||
} |
|||
} |
|||
} |
|||
@ -1,50 +0,0 @@ |
|||
<!DOCTYPE html> |
|||
<html lang="en"> |
|||
<head> |
|||
<meta charset="utf-8" /> |
|||
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> |
|||
<title>@ViewData["Title"] - BasicAspNetCoreApplication</title> |
|||
<link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.min.css" /> |
|||
<link rel="stylesheet" href="~/css/site.css" /> |
|||
</head> |
|||
<body> |
|||
<header> |
|||
<nav class="navbar navbar-expand-sm navbar-toggleable-sm navbar-light bg-white border-bottom box-shadow mb-3"> |
|||
<div class="container"> |
|||
<a class="navbar-brand" asp-area="" asp-page="/Index">BasicAspNetCoreApplication</a> |
|||
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target=".navbar-collapse" aria-controls="navbarSupportedContent" |
|||
aria-expanded="false" aria-label="Toggle navigation"> |
|||
<span class="navbar-toggler-icon"></span> |
|||
</button> |
|||
<div class="navbar-collapse collapse d-sm-inline-flex flex-sm-row-reverse"> |
|||
<ul class="navbar-nav flex-grow-1"> |
|||
<li class="nav-item"> |
|||
<a class="nav-link text-dark" asp-area="" asp-page="/Index">Home</a> |
|||
</li> |
|||
<li class="nav-item"> |
|||
<a class="nav-link text-dark" asp-area="" asp-page="/Privacy">Privacy</a> |
|||
</li> |
|||
</ul> |
|||
</div> |
|||
</div> |
|||
</nav> |
|||
</header> |
|||
<div class="container"> |
|||
<main role="main" class="pb-3"> |
|||
@RenderBody() |
|||
</main> |
|||
</div> |
|||
|
|||
<footer class="border-top footer text-muted"> |
|||
<div class="container"> |
|||
© 2019 - BasicAspNetCoreApplication - <a asp-area="" asp-page="/Privacy">Privacy</a> |
|||
</div> |
|||
</footer> |
|||
|
|||
<script src="~/lib/jquery/dist/jquery.min.js"></script> |
|||
<script src="~/lib/bootstrap/dist/js/bootstrap.bundle.min.js"></script> |
|||
<script src="~/js/site.js" asp-append-version="true"></script> |
|||
|
|||
@RenderSection("Scripts", required: false) |
|||
</body> |
|||
</html> |
|||
@ -1,2 +0,0 @@ |
|||
<script src="~/lib/jquery-validation/dist/jquery.validate.min.js"></script> |
|||
<script src="~/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.min.js"></script> |
|||
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue