Fix resend email confirmation (#14118)
This commit is contained in:
parent
b617083148
commit
41195138b1
|
|
@ -207,6 +207,22 @@ namespace Microsoft.AspNetCore.Identity.UI.V3.Pages.Account.Internal
|
|||
}
|
||||
}
|
||||
[Microsoft.AspNetCore.Authorization.AllowAnonymousAttribute]
|
||||
public abstract partial class ResendEmailConfirmationModel : Microsoft.AspNetCore.Mvc.RazorPages.PageModel
|
||||
{
|
||||
protected ResendEmailConfirmationModel() { }
|
||||
[Microsoft.AspNetCore.Mvc.BindPropertyAttribute]
|
||||
public Microsoft.AspNetCore.Identity.UI.V3.Pages.Account.Internal.ResendEmailConfirmationModel.InputModel Input { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute] set { } }
|
||||
public virtual void OnGet() { }
|
||||
public virtual System.Threading.Tasks.Task<Microsoft.AspNetCore.Mvc.IActionResult> OnPostAsync() { throw null; }
|
||||
public partial class InputModel
|
||||
{
|
||||
public InputModel() { }
|
||||
[System.ComponentModel.DataAnnotations.EmailAddressAttribute]
|
||||
[System.ComponentModel.DataAnnotations.RequiredAttribute]
|
||||
public string Email { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute] set { } }
|
||||
}
|
||||
}
|
||||
[Microsoft.AspNetCore.Authorization.AllowAnonymousAttribute]
|
||||
public partial class ResetPasswordConfirmationModel : Microsoft.AspNetCore.Mvc.RazorPages.PageModel
|
||||
{
|
||||
public ResetPasswordConfirmationModel() { }
|
||||
|
|
@ -657,6 +673,22 @@ namespace Microsoft.AspNetCore.Identity.UI.V4.Pages.Account.Internal
|
|||
}
|
||||
}
|
||||
[Microsoft.AspNetCore.Authorization.AllowAnonymousAttribute]
|
||||
public abstract partial class ResendEmailConfirmationModel : Microsoft.AspNetCore.Mvc.RazorPages.PageModel
|
||||
{
|
||||
protected ResendEmailConfirmationModel() { }
|
||||
[Microsoft.AspNetCore.Mvc.BindPropertyAttribute]
|
||||
public Microsoft.AspNetCore.Identity.UI.V4.Pages.Account.Internal.ResendEmailConfirmationModel.InputModel Input { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute] set { } }
|
||||
public virtual void OnGet() { }
|
||||
public virtual System.Threading.Tasks.Task<Microsoft.AspNetCore.Mvc.IActionResult> OnPostAsync() { throw null; }
|
||||
public partial class InputModel
|
||||
{
|
||||
public InputModel() { }
|
||||
[System.ComponentModel.DataAnnotations.EmailAddressAttribute]
|
||||
[System.ComponentModel.DataAnnotations.RequiredAttribute]
|
||||
public string Email { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute] set { } }
|
||||
}
|
||||
}
|
||||
[Microsoft.AspNetCore.Authorization.AllowAnonymousAttribute]
|
||||
public partial class ResetPasswordConfirmationModel : Microsoft.AspNetCore.Mvc.RazorPages.PageModel
|
||||
{
|
||||
public ResetPasswordConfirmationModel() { }
|
||||
|
|
|
|||
|
|
@ -41,6 +41,9 @@
|
|||
<p>
|
||||
<a asp-page="./Register" asp-route-returnUrl="@Model.ReturnUrl">Register as a new user</a>
|
||||
</p>
|
||||
<p>
|
||||
<a id="resend-confirmation" asp-page="./ResendEmailConfirmation">Resend email confirmation</a>
|
||||
</p>
|
||||
</div>
|
||||
</form>
|
||||
</section>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,26 @@
|
|||
@page
|
||||
@model ResendEmailConfirmationModel
|
||||
@{
|
||||
ViewData["Title"] = "Resend email confirmation";
|
||||
}
|
||||
|
||||
<h2>@ViewData["Title"]</h2>
|
||||
<h4>Enter your email.</h4>
|
||||
<hr />
|
||||
<div class="row">
|
||||
<div class="col-md-4">
|
||||
<form method="post">
|
||||
<div asp-validation-summary="All" class="text-danger"></div>
|
||||
<div class="form-group">
|
||||
<label asp-for="Input.Email"></label>
|
||||
<input asp-for="Input.Email" class="form-control" />
|
||||
<span asp-validation-for="Input.Email" class="text-danger"></span>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-default">Resend</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@section Scripts {
|
||||
<partial name="_ValidationScriptsPartial" />
|
||||
}
|
||||
|
|
@ -0,0 +1,106 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Text;
|
||||
using System.Text.Encodings.Web;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Identity.UI.Services;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||
using Microsoft.AspNetCore.WebUtilities;
|
||||
|
||||
namespace Microsoft.AspNetCore.Identity.UI.V3.Pages.Account.Internal
|
||||
{
|
||||
/// <summary>
|
||||
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
[AllowAnonymous]
|
||||
[IdentityDefaultUI(typeof(ResendEmailConfirmationModel<>))]
|
||||
public abstract class ResendEmailConfirmationModel : PageModel
|
||||
{
|
||||
/// <summary>
|
||||
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
[BindProperty]
|
||||
public InputModel Input { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
public class InputModel
|
||||
{
|
||||
/// <summary>
|
||||
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
[Required]
|
||||
[EmailAddress]
|
||||
public string Email { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
public virtual void OnGet() => throw new NotImplementedException();
|
||||
|
||||
/// <summary>
|
||||
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
public virtual Task<IActionResult> OnPostAsync() => throw new NotImplementedException();
|
||||
}
|
||||
|
||||
internal class ResendEmailConfirmationModel<TUser> : ResendEmailConfirmationModel where TUser : class
|
||||
{
|
||||
private readonly UserManager<TUser> _userManager;
|
||||
private readonly IEmailSender _emailSender;
|
||||
|
||||
public ResendEmailConfirmationModel(UserManager<TUser> userManager, IEmailSender emailSender)
|
||||
{
|
||||
_userManager = userManager;
|
||||
_emailSender = emailSender;
|
||||
}
|
||||
|
||||
public override void OnGet()
|
||||
{
|
||||
}
|
||||
|
||||
public override async Task<IActionResult> OnPostAsync()
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return Page();
|
||||
}
|
||||
|
||||
var user = await _userManager.FindByEmailAsync(Input.Email);
|
||||
if (user == null)
|
||||
{
|
||||
ModelState.AddModelError(string.Empty, "Verification email sent. Please check your email.");
|
||||
return Page();
|
||||
}
|
||||
|
||||
var userId = await _userManager.GetUserIdAsync(user);
|
||||
var code = await _userManager.GenerateEmailConfirmationTokenAsync(user);
|
||||
code = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(code));
|
||||
var callbackUrl = Url.Page(
|
||||
"/Account/ConfirmEmail",
|
||||
pageHandler: null,
|
||||
values: new { userId = userId, code = code },
|
||||
protocol: Request.Scheme);
|
||||
await _emailSender.SendEmailAsync(
|
||||
Input.Email,
|
||||
"Confirm your email",
|
||||
$"Please confirm your account by <a href='{HtmlEncoder.Default.Encode(callbackUrl)}'>clicking here</a>.");
|
||||
|
||||
ModelState.AddModelError(string.Empty, "Verification email sent. Please check your email.");
|
||||
return Page();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -41,6 +41,9 @@
|
|||
<p>
|
||||
<a asp-page="./Register" asp-route-returnUrl="@Model.ReturnUrl">Register as a new user</a>
|
||||
</p>
|
||||
<p>
|
||||
<a id="resend-confirmation" asp-page="./ResendEmailConfirmation">Resend email confirmation</a>
|
||||
</p>
|
||||
</div>
|
||||
</form>
|
||||
</section>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,26 @@
|
|||
@page
|
||||
@model ResendEmailConfirmationModel
|
||||
@{
|
||||
ViewData["Title"] = "Resend email confirmation";
|
||||
}
|
||||
|
||||
<h1>@ViewData["Title"]</h1>
|
||||
<h4>Enter your email.</h4>
|
||||
<hr />
|
||||
<div class="row">
|
||||
<div class="col-md-4">
|
||||
<form method="post">
|
||||
<div asp-validation-summary="All" class="text-danger"></div>
|
||||
<div class="form-group">
|
||||
<label asp-for="Input.Email"></label>
|
||||
<input asp-for="Input.Email" class="form-control" />
|
||||
<span asp-validation-for="Input.Email" class="text-danger"></span>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary">Resend</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@section Scripts {
|
||||
<partial name="_ValidationScriptsPartial" />
|
||||
}
|
||||
|
|
@ -0,0 +1,106 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Text;
|
||||
using System.Text.Encodings.Web;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Identity.UI.Services;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||
using Microsoft.AspNetCore.WebUtilities;
|
||||
|
||||
namespace Microsoft.AspNetCore.Identity.UI.V4.Pages.Account.Internal
|
||||
{
|
||||
/// <summary>
|
||||
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
[AllowAnonymous]
|
||||
[IdentityDefaultUI(typeof(ResendEmailConfirmationModel<>))]
|
||||
public abstract class ResendEmailConfirmationModel : PageModel
|
||||
{
|
||||
/// <summary>
|
||||
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
[BindProperty]
|
||||
public InputModel Input { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
public class InputModel
|
||||
{
|
||||
/// <summary>
|
||||
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
[Required]
|
||||
[EmailAddress]
|
||||
public string Email { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
public virtual void OnGet() => throw new NotImplementedException();
|
||||
|
||||
/// <summary>
|
||||
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
|
||||
/// directly from your code. This API may change or be removed in future releases.
|
||||
/// </summary>
|
||||
public virtual Task<IActionResult> OnPostAsync() => throw new NotImplementedException();
|
||||
}
|
||||
|
||||
internal class ResendEmailConfirmationModel<TUser> : ResendEmailConfirmationModel where TUser : class
|
||||
{
|
||||
private readonly UserManager<TUser> _userManager;
|
||||
private readonly IEmailSender _emailSender;
|
||||
|
||||
public ResendEmailConfirmationModel(UserManager<TUser> userManager, IEmailSender emailSender)
|
||||
{
|
||||
_userManager = userManager;
|
||||
_emailSender = emailSender;
|
||||
}
|
||||
|
||||
public override void OnGet()
|
||||
{
|
||||
}
|
||||
|
||||
public override async Task<IActionResult> OnPostAsync()
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return Page();
|
||||
}
|
||||
|
||||
var user = await _userManager.FindByEmailAsync(Input.Email);
|
||||
if (user == null)
|
||||
{
|
||||
ModelState.AddModelError(string.Empty, "Verification email sent. Please check your email.");
|
||||
return Page();
|
||||
}
|
||||
|
||||
var userId = await _userManager.GetUserIdAsync(user);
|
||||
var code = await _userManager.GenerateEmailConfirmationTokenAsync(user);
|
||||
code = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(code));
|
||||
var callbackUrl = Url.Page(
|
||||
"/Account/ConfirmEmail",
|
||||
pageHandler: null,
|
||||
values: new { userId = userId, code = code },
|
||||
protocol: Request.Scheme);
|
||||
await _emailSender.SendEmailAsync(
|
||||
Input.Email,
|
||||
"Confirm your email",
|
||||
$"Please confirm your account by <a href='{HtmlEncoder.Default.Encode(callbackUrl)}'>clicking here</a>.");
|
||||
|
||||
ModelState.AddModelError(string.Empty, "Verification email sent. Please check your email.");
|
||||
return Page();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2,8 +2,12 @@
|
|||
<configuration>
|
||||
<system.webServer>
|
||||
<handlers>
|
||||
<add name="aspNetCore" path="*" verb="*" modules="AspNetCoreModule" resourceType="Unspecified" />
|
||||
<add name="aspNetCore" path="*" verb="*" modules="AspNetCoreModuleV2" resourceType="Unspecified" />
|
||||
</handlers>
|
||||
<aspNetCore processPath="%LAUNCHER_PATH%" arguments="%LAUNCHER_ARGS%" stdoutLogEnabled="false" stdoutLogFile=".\logs\stdout" forwardWindowsAuthToken="false" />
|
||||
<aspNetCore processPath="%LAUNCHER_PATH%" arguments="%LAUNCHER_ARGS%" stdoutLogEnabled="false" stdoutLogFile=".\logs\stdout" forwardWindowsAuthToken="false">
|
||||
<environmentVariables>
|
||||
<environmentVariable name="ASPNETCORE_ENVIRONMENT" value="Development" />
|
||||
</environmentVariables>
|
||||
</aspNetCore>
|
||||
</system.webServer>
|
||||
</configuration>
|
||||
|
|
@ -230,6 +230,33 @@ namespace Microsoft.AspNetCore.Identity.FunctionalTests
|
|||
await UserStories.LoginExistingUserAsync(newClient, userName, password);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CanResendConfirmingEmail()
|
||||
{
|
||||
// Arrange
|
||||
var emailSender = new ContosoEmailSender();
|
||||
void ConfigureTestServices(IServiceCollection services) => services
|
||||
.SetupTestEmailSender(emailSender)
|
||||
.SetupEmailRequired();
|
||||
|
||||
var server = ServerFactory.WithWebHostBuilder(whb => whb.ConfigureServices(ConfigureTestServices));
|
||||
|
||||
var client = server.CreateClient();
|
||||
var newClient = server.CreateClient();
|
||||
|
||||
var userName = $"{Guid.NewGuid()}@example.com";
|
||||
var password = $"!Test.Password1$";
|
||||
|
||||
var loggedIn = await UserStories.RegisterNewUserAsync(client, userName, password);
|
||||
|
||||
// Act & Assert
|
||||
// Use a new client to simulate a new browser session.
|
||||
await UserStories.ResendConfirmEmailAsync(server.CreateClient(), userName);
|
||||
Assert.Equal(2, emailSender.SentEmails.Count);
|
||||
var email = emailSender.SentEmails.Last();
|
||||
await UserStories.ConfirmEmailAsync(email, newClient);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CanLogInAfterConfirmingEmail_WithGlobalAuthorizeFilter()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ namespace Microsoft.AspNetCore.Identity.FunctionalTests.Account
|
|||
{
|
||||
private readonly IHtmlFormElement _loginForm;
|
||||
private readonly IHtmlAnchorElement _forgotPasswordLink;
|
||||
private readonly IHtmlAnchorElement _reconfirmLink;
|
||||
private readonly IHtmlFormElement _externalLoginForm;
|
||||
private readonly IHtmlElement _contosoButton;
|
||||
private readonly IHtmlElement _loginButton;
|
||||
|
|
@ -26,6 +27,7 @@ namespace Microsoft.AspNetCore.Identity.FunctionalTests.Account
|
|||
_loginForm = HtmlAssert.HasForm("#account", login);
|
||||
_loginButton = HtmlAssert.HasElement("#login-submit", login);
|
||||
_forgotPasswordLink = HtmlAssert.HasLink("#forgot-password", login);
|
||||
_reconfirmLink = HtmlAssert.HasLink("#resend-confirmation", login);
|
||||
if (Context.ContosoLoginEnabled)
|
||||
{
|
||||
_externalLoginForm = HtmlAssert.HasForm("#external-account", login);
|
||||
|
|
@ -52,6 +54,14 @@ namespace Microsoft.AspNetCore.Identity.FunctionalTests.Account
|
|||
return new ForgotPassword(Client, forgotPassword, Context);
|
||||
}
|
||||
|
||||
public async Task<ResendEmailConfirmation> ClickReconfirmEmailLinkAsync()
|
||||
{
|
||||
var response = await Client.GetAsync(_reconfirmLink.Href);
|
||||
var forgotPassword = await ResponseAssert.IsHtmlDocumentAsync(response);
|
||||
|
||||
return new ResendEmailConfirmation(Client, forgotPassword, Context);
|
||||
}
|
||||
|
||||
public async Task<Index> LoginValidUserAsync(string userName, string password)
|
||||
{
|
||||
var loggedIn = await SendLoginForm(userName, password);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,26 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using AngleSharp.Dom.Html;
|
||||
|
||||
namespace Microsoft.AspNetCore.Identity.FunctionalTests.Account
|
||||
{
|
||||
public class ResendEmailConfirmation : DefaultUIPage
|
||||
{
|
||||
private readonly IHtmlFormElement _resendForm;
|
||||
|
||||
public ResendEmailConfirmation(HttpClient client, IHtmlDocument document, DefaultUIContext context) : base(client, document, context)
|
||||
{
|
||||
_resendForm = HtmlAssert.HasForm(document);
|
||||
}
|
||||
|
||||
public Task<HttpResponseMessage> ResendAsync(string email)
|
||||
=> Client.SendAsync(_resendForm, new Dictionary<string, string>
|
||||
{
|
||||
["Input_Email"] = email
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -194,6 +194,16 @@ namespace Microsoft.AspNetCore.Identity.FunctionalTests
|
|||
.WithConfirmedEmail());
|
||||
}
|
||||
|
||||
internal static async Task ResendConfirmEmailAsync(HttpClient client, string email)
|
||||
{
|
||||
var index = await Index.CreateAsync(client);
|
||||
var login = await index.ClickLoginLinkAsync();
|
||||
var reconfirm = await login.ClickReconfirmEmailLinkAsync();
|
||||
var response = await reconfirm.ResendAsync(email);
|
||||
ResponseAssert.IsOK(response);
|
||||
Assert.Contains("Verification email sent.", await response.Content.ReadAsStringAsync());
|
||||
}
|
||||
|
||||
internal static async Task<ForgotPasswordConfirmation> ForgotPasswordAsync(HttpClient client, string userName)
|
||||
{
|
||||
var index = await Index.CreateAsync(client);
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ namespace Templates.Test.Helpers
|
|||
public const string LoginUrl = "/Identity/Account/Login";
|
||||
public const string RegisterUrl = "/Identity/Account/Register";
|
||||
public const string ForgotPassword = "/Identity/Account/ForgotPassword";
|
||||
public const string ResendEmailConfirmation = "/Identity/Account/ResendEmailConfirmation";
|
||||
public const string ExternalArticle = "https://go.microsoft.com/fwlink/?LinkID=532715";
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -182,6 +182,7 @@ namespace Templates.Test
|
|||
PageUrls.PrivacyUrl,
|
||||
PageUrls.ForgotPassword,
|
||||
PageUrls.RegisterUrl,
|
||||
PageUrls.ResendEmailConfirmation,
|
||||
PageUrls.ExternalArticle,
|
||||
PageUrls.PrivacyUrl }
|
||||
},
|
||||
|
|
|
|||
|
|
@ -172,6 +172,7 @@ namespace Templates.Test
|
|||
PageUrls.PrivacyUrl,
|
||||
PageUrls.ForgotPassword,
|
||||
PageUrls.RegisterUrl,
|
||||
PageUrls.ResendEmailConfirmation,
|
||||
PageUrls.ExternalArticle,
|
||||
PageUrls.PrivacyUrl }
|
||||
},
|
||||
|
|
|
|||
Loading…
Reference in New Issue