Implemented OIDC
This commit is contained in:
		
							parent
							
								
									116dd93da2
								
							
						
					
					
						commit
						a9269ece30
					
				| 
						 | 
				
			
			@ -8,7 +8,7 @@ internal sealed class IdentityUserAccessor(
 | 
			
		|||
    IdentityRedirectManager redirectManager) {
 | 
			
		||||
    public async Task<ApplicationUser> GetRequiredUserAsync(HttpContext context) {
 | 
			
		||||
        var user = await userManager.GetUserAsync(context.User);
 | 
			
		||||
 | 
			
		||||
        
 | 
			
		||||
        if (user is null) {
 | 
			
		||||
            redirectManager.RedirectToWithStatus("Account/InvalidUser",
 | 
			
		||||
                $"Error: Unable to load user with ID '{userManager.GetUserId(context.User)}'.", context);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -18,7 +18,8 @@
 | 
			
		|||
 | 
			
		||||
<BoardComponent CenterContent="true">
 | 
			
		||||
    <BoardCardComponent Heading="@Localizer["Title"]">
 | 
			
		||||
        <StatusMessage Message="@Message" />
 | 
			
		||||
	    <StatusMessage Message="@Message" />
 | 
			
		||||
        <a class="btn btn-primary w-full mt-3" href="/Account/Login">@Localizer["Login_Label"]</a>
 | 
			
		||||
    </BoardCardComponent>
 | 
			
		||||
</BoardComponent>
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -4,192 +4,197 @@
 | 
			
		|||
@using System.Security.Claims
 | 
			
		||||
@using System.Text
 | 
			
		||||
@using System.Text.Encodings.Web
 | 
			
		||||
@using Humanizer
 | 
			
		||||
@using Microsoft.AspNetCore.Identity
 | 
			
		||||
@using Microsoft.AspNetCore.WebUtilities
 | 
			
		||||
@using Wave.Data
 | 
			
		||||
 | 
			
		||||
@inject SignInManager<ApplicationUser> SignInManager
 | 
			
		||||
@inject UserManager<ApplicationUser> UserManager
 | 
			
		||||
@inject RoleManager<IdentityRole> RoleManager
 | 
			
		||||
@inject IUserStore<ApplicationUser> UserStore
 | 
			
		||||
@inject IEmailSender<ApplicationUser> EmailSender
 | 
			
		||||
@inject NavigationManager NavigationManager
 | 
			
		||||
@inject IdentityRedirectManager RedirectManager
 | 
			
		||||
@inject ILogger<ExternalLogin> Logger
 | 
			
		||||
@inject IStringLocalizer<ExternalLogin> Localizer
 | 
			
		||||
 | 
			
		||||
<PageTitle>Register</PageTitle>
 | 
			
		||||
<PageTitle>@Localizer["Title"]</PageTitle>
 | 
			
		||||
 | 
			
		||||
<StatusMessage Message="@message" />
 | 
			
		||||
<h1>Register</h1>
 | 
			
		||||
<h2>Associate your @ProviderDisplayName account.</h2>
 | 
			
		||||
<hr />
 | 
			
		||||
 | 
			
		||||
<div  class="alert alert-info">
 | 
			
		||||
    You've successfully authenticated with <strong>@ProviderDisplayName</strong>.
 | 
			
		||||
    Please enter an email address for this site below and click the Register button to finish
 | 
			
		||||
    logging in.
 | 
			
		||||
</div>
 | 
			
		||||
<BoardComponent CenterContent="true">
 | 
			
		||||
	<BoardCardComponent Heading="@Localizer["Title"]">
 | 
			
		||||
		<Alert CanRemove="false" Type="Alert.MessageType.Information">
 | 
			
		||||
			<p>
 | 
			
		||||
				@string.Format(Localizer["Message"], ProviderDisplayName)
 | 
			
		||||
			</p>
 | 
			
		||||
		</Alert>
 | 
			
		||||
		<EditForm Model="Input" OnValidSubmit="OnValidSubmitAsync" FormName="confirmation" method="post">
 | 
			
		||||
			<DataAnnotationsValidator/>
 | 
			
		||||
			<InputLabelComponent LabelText="@Localizer["Email_Label"]" For="() => Input.Email">
 | 
			
		||||
				<InputText @bind-Value="Input.Email" class="input input-bordered w-full"
 | 
			
		||||
				           autocomplete="email" placeholder="@Localizer["Email_Placeholder"]" />
 | 
			
		||||
			</InputLabelComponent>
 | 
			
		||||
 | 
			
		||||
<div class="row">
 | 
			
		||||
    <div class="col-md-4">
 | 
			
		||||
        <EditForm Model="Input" OnValidSubmit="OnValidSubmitAsync" FormName="confirmation" method="post">
 | 
			
		||||
            <DataAnnotationsValidator />
 | 
			
		||||
            <ValidationSummary class="text-danger" role="alert" />
 | 
			
		||||
            <div class="form-floating mb-3">
 | 
			
		||||
                <InputText @bind-Value="Input.Email" class="form-control" autocomplete="email" placeholder="Please enter your email." />
 | 
			
		||||
                <label for="email" class="form-label">Email</label>
 | 
			
		||||
                <ValidationMessage For="() => Input.Email" />
 | 
			
		||||
            </div>
 | 
			
		||||
            <button type="submit" class="w-100 btn btn-lg btn-primary">Register</button>
 | 
			
		||||
        </EditForm>
 | 
			
		||||
    </div>
 | 
			
		||||
</div>
 | 
			
		||||
			<button type="submit" class="btn btn-primary w-full">
 | 
			
		||||
				@Localizer["Submit"]
 | 
			
		||||
			</button>
 | 
			
		||||
		</EditForm>
 | 
			
		||||
	</BoardCardComponent>
 | 
			
		||||
</BoardComponent>
 | 
			
		||||
 | 
			
		||||
@code {
 | 
			
		||||
    public const string LoginCallbackAction = "LoginCallback";
 | 
			
		||||
	public const string LoginCallbackAction = "LoginCallback";
 | 
			
		||||
 | 
			
		||||
    private string? message;
 | 
			
		||||
    private ExternalLoginInfo externalLoginInfo = default!;
 | 
			
		||||
	private string? message;
 | 
			
		||||
	private ExternalLoginInfo externalLoginInfo = default!;
 | 
			
		||||
 | 
			
		||||
    [CascadingParameter]
 | 
			
		||||
    private HttpContext HttpContext { get; set; } = default!;
 | 
			
		||||
	[CascadingParameter] 
 | 
			
		||||
	private HttpContext HttpContext { get; set; } = default!;
 | 
			
		||||
 | 
			
		||||
    [SupplyParameterFromForm]
 | 
			
		||||
    private InputModel Input { get; set; } = new();
 | 
			
		||||
	[SupplyParameterFromForm] 
 | 
			
		||||
	private InputModel Input { get; set; } = new();
 | 
			
		||||
 | 
			
		||||
    [SupplyParameterFromQuery]
 | 
			
		||||
    private string? RemoteError { get; set; }
 | 
			
		||||
	[SupplyParameterFromQuery] 
 | 
			
		||||
	private string? RemoteError { get; set; }
 | 
			
		||||
 | 
			
		||||
    [SupplyParameterFromQuery]
 | 
			
		||||
    private string? ReturnUrl { get; set; }
 | 
			
		||||
	[SupplyParameterFromQuery] 
 | 
			
		||||
	private string? ReturnUrl { get; set; }
 | 
			
		||||
 | 
			
		||||
    [SupplyParameterFromQuery]
 | 
			
		||||
    private string? Action { get; set; }
 | 
			
		||||
	[SupplyParameterFromQuery] 
 | 
			
		||||
	private string? Action { get; set; }
 | 
			
		||||
 | 
			
		||||
    private string? ProviderDisplayName => externalLoginInfo.ProviderDisplayName;
 | 
			
		||||
	private string? ProviderDisplayName => externalLoginInfo.ProviderDisplayName;
 | 
			
		||||
 | 
			
		||||
    protected override async Task OnInitializedAsync()
 | 
			
		||||
    {
 | 
			
		||||
        if (RemoteError is not null)
 | 
			
		||||
        {
 | 
			
		||||
            RedirectManager.RedirectToWithStatus("Account/Login", $"Error from external provider: {RemoteError}", HttpContext);
 | 
			
		||||
        }
 | 
			
		||||
	protected override async Task OnInitializedAsync() {
 | 
			
		||||
		if (RemoteError is not null) {
 | 
			
		||||
			RedirectManager.RedirectToWithStatus("Account/Login", $"Error from external provider: {RemoteError}", HttpContext);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
        var info = await SignInManager.GetExternalLoginInfoAsync();
 | 
			
		||||
        if (info is null)
 | 
			
		||||
        {
 | 
			
		||||
            RedirectManager.RedirectToWithStatus("Account/Login", "Error loading external login information.", HttpContext);
 | 
			
		||||
        }
 | 
			
		||||
		var info = await SignInManager.GetExternalLoginInfoAsync();
 | 
			
		||||
		if (info is null) {
 | 
			
		||||
			RedirectManager.RedirectToWithStatus("Account/Login", "Error loading external login information.", HttpContext);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
        externalLoginInfo = info;
 | 
			
		||||
		externalLoginInfo = info;
 | 
			
		||||
 | 
			
		||||
        if (HttpMethods.IsGet(HttpContext.Request.Method))
 | 
			
		||||
        {
 | 
			
		||||
            if (Action == LoginCallbackAction)
 | 
			
		||||
            {
 | 
			
		||||
                await OnLoginCallbackAsync();
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
		if (HttpMethods.IsGet(HttpContext.Request.Method)) {
 | 
			
		||||
			if (Action == LoginCallbackAction) {
 | 
			
		||||
				await OnLoginCallbackAsync();
 | 
			
		||||
				return;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
            // We should only reach this page via the login callback, so redirect back to
 | 
			
		||||
            // the login page if we get here some other way.
 | 
			
		||||
            RedirectManager.RedirectTo("Account/Login");
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
			// We should only reach this page via the login callback, so redirect back to
 | 
			
		||||
			// the login page if we get here some other way.
 | 
			
		||||
			RedirectManager.RedirectTo("Account/Login");
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
    private async Task OnLoginCallbackAsync()
 | 
			
		||||
    {
 | 
			
		||||
        // Sign in the user with this external login provider if the user already has a login.
 | 
			
		||||
        var result = await SignInManager.ExternalLoginSignInAsync(
 | 
			
		||||
            externalLoginInfo.LoginProvider,
 | 
			
		||||
            externalLoginInfo.ProviderKey,
 | 
			
		||||
            isPersistent: false,
 | 
			
		||||
            bypassTwoFactor: true);
 | 
			
		||||
	private async Task OnLoginCallbackAsync() {
 | 
			
		||||
		// Sign in the user with this external login provider if the user already has a login.
 | 
			
		||||
		var result = await SignInManager.ExternalLoginSignInAsync(
 | 
			
		||||
			externalLoginInfo.LoginProvider,
 | 
			
		||||
			externalLoginInfo.ProviderKey,
 | 
			
		||||
			isPersistent: false,
 | 
			
		||||
			bypassTwoFactor: true);
 | 
			
		||||
 | 
			
		||||
        if (result.Succeeded)
 | 
			
		||||
        {
 | 
			
		||||
            Logger.LogInformation(
 | 
			
		||||
                "{Name} logged in with {LoginProvider} provider.",
 | 
			
		||||
                externalLoginInfo.Principal.Identity?.Name,
 | 
			
		||||
                externalLoginInfo.LoginProvider);
 | 
			
		||||
            RedirectManager.RedirectTo(ReturnUrl);
 | 
			
		||||
        }
 | 
			
		||||
        else if (result.IsLockedOut)
 | 
			
		||||
        {
 | 
			
		||||
            RedirectManager.RedirectTo("Account/Lockout");
 | 
			
		||||
        }
 | 
			
		||||
		if (result.Succeeded) {
 | 
			
		||||
			Logger.LogInformation(
 | 
			
		||||
				"{Name} logged in with {LoginProvider} provider.",
 | 
			
		||||
				externalLoginInfo.Principal.Identity?.Name,
 | 
			
		||||
				externalLoginInfo.LoginProvider);
 | 
			
		||||
			RedirectManager.RedirectTo(ReturnUrl);
 | 
			
		||||
		} else if (result.IsLockedOut) {
 | 
			
		||||
			RedirectManager.RedirectTo("Account/Lockout");
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
        // If the user does not have an account, then ask the user to create an account.
 | 
			
		||||
        if (externalLoginInfo.Principal.HasClaim(c => c.Type == ClaimTypes.Email))
 | 
			
		||||
        {
 | 
			
		||||
            Input.Email = externalLoginInfo.Principal.FindFirstValue(ClaimTypes.Email) ?? "";
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
		// If the user does not have an account, then ask the user to create an account.
 | 
			
		||||
		if (externalLoginInfo.Principal.Claims.FirstOrDefault(c => c.Type is ClaimTypes.Email or "email") is {} claim) {
 | 
			
		||||
			Input.Email = claim.Value;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
    private async Task OnValidSubmitAsync()
 | 
			
		||||
    {
 | 
			
		||||
        var emailStore = GetEmailStore();
 | 
			
		||||
        var user = CreateUser();
 | 
			
		||||
	private async Task OnValidSubmitAsync() {
 | 
			
		||||
		var emailStore = GetEmailStore();
 | 
			
		||||
		var user = CreateUser();
 | 
			
		||||
 | 
			
		||||
        await UserStore.SetUserNameAsync(user, Input.Email, CancellationToken.None);
 | 
			
		||||
        await emailStore.SetEmailAsync(user, Input.Email, CancellationToken.None);
 | 
			
		||||
		await UserStore.SetUserNameAsync(user, Input.Email, CancellationToken.None);
 | 
			
		||||
		await emailStore.SetEmailAsync(user, Input.Email, CancellationToken.None);
 | 
			
		||||
 | 
			
		||||
        var result = await UserManager.CreateAsync(user);
 | 
			
		||||
        if (result.Succeeded)
 | 
			
		||||
        {
 | 
			
		||||
            result = await UserManager.AddLoginAsync(user, externalLoginInfo);
 | 
			
		||||
            if (result.Succeeded)
 | 
			
		||||
            {
 | 
			
		||||
                Logger.LogInformation("User created an account using {Name} provider.", externalLoginInfo.LoginProvider);
 | 
			
		||||
		var info = await SignInManager.GetExternalLoginInfoAsync();
 | 
			
		||||
		if (info?.Principal.Claims.FirstOrDefault(c => c.Type is ClaimTypes.Name or ClaimTypes.GivenName or "name") is {} nameClaim) {
 | 
			
		||||
			user.FullName = nameClaim.Value;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
                var userId = await UserManager.GetUserIdAsync(user);
 | 
			
		||||
                var code = await UserManager.GenerateEmailConfirmationTokenAsync(user);
 | 
			
		||||
                code = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(code));
 | 
			
		||||
		var result = await UserManager.CreateAsync(user);
 | 
			
		||||
		if (result.Succeeded) {
 | 
			
		||||
			result = await UserManager.AddLoginAsync(user, externalLoginInfo);
 | 
			
		||||
			if (result.Succeeded) {
 | 
			
		||||
				Logger.LogInformation("User created an account using {Name} provider.", externalLoginInfo.LoginProvider);
 | 
			
		||||
 | 
			
		||||
                var callbackUrl = NavigationManager.GetUriWithQueryParameters(
 | 
			
		||||
                    NavigationManager.ToAbsoluteUri("Account/ConfirmEmail").AbsoluteUri,
 | 
			
		||||
                    new Dictionary<string, object?> { ["userId"] = userId, ["code"] = code });
 | 
			
		||||
                await EmailSender.SendConfirmationLinkAsync(user, Input.Email, HtmlEncoder.Default.Encode(callbackUrl));
 | 
			
		||||
				string userId = await UserManager.GetUserIdAsync(user);
 | 
			
		||||
				string code = await UserManager.GenerateEmailConfirmationTokenAsync(user);
 | 
			
		||||
				code = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(code));
 | 
			
		||||
 | 
			
		||||
                // If account confirmation is required, we need to show the link if we don't have a real email sender
 | 
			
		||||
                if (UserManager.Options.SignIn.RequireConfirmedAccount)
 | 
			
		||||
                {
 | 
			
		||||
                    RedirectManager.RedirectTo("Account/RegisterConfirmation", new() { ["email"] = Input.Email });
 | 
			
		||||
                }
 | 
			
		||||
				string callbackUrl = NavigationManager.GetUriWithQueryParameters(
 | 
			
		||||
					NavigationManager.ToAbsoluteUri("Account/ConfirmEmail").AbsoluteUri,
 | 
			
		||||
					new Dictionary<string, object?> {["userId"] = userId, ["code"] = code});
 | 
			
		||||
				await EmailSender.SendConfirmationLinkAsync(user, Input.Email, HtmlEncoder.Default.Encode(callbackUrl));
 | 
			
		||||
 | 
			
		||||
                await SignInManager.SignInAsync(user, isPersistent: false, externalLoginInfo.LoginProvider);
 | 
			
		||||
                RedirectManager.RedirectTo(ReturnUrl);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
				if (info?.Principal.Claims.FirstOrDefault(c => c.Type is ClaimTypes.Role or "roles") is { } roleClaim) {
 | 
			
		||||
					var roles = roleClaim.Value.Split(",").Select(s => s.Trim());
 | 
			
		||||
 | 
			
		||||
        message = $"Error: {string.Join(",", result.Errors.Select(error => error.Description))}";
 | 
			
		||||
    }
 | 
			
		||||
					foreach (string role in roles) {
 | 
			
		||||
						try {
 | 
			
		||||
							string r = role.Titleize();
 | 
			
		||||
							if (r is "Author" or "Reviewer" or "Moderator" or "Admin" && 
 | 
			
		||||
							    !await RoleManager.RoleExistsAsync(r) && 
 | 
			
		||||
							    !(await RoleManager.CreateAsync(new IdentityRole(r))).Succeeded) {
 | 
			
		||||
								Logger.LogError("Failed to create role {role}. Failed to assign it to user {name}.", r, user.UserName);
 | 
			
		||||
								continue;
 | 
			
		||||
							}
 | 
			
		||||
 | 
			
		||||
    private ApplicationUser CreateUser()
 | 
			
		||||
    {
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            return Activator.CreateInstance<ApplicationUser>();
 | 
			
		||||
        }
 | 
			
		||||
        catch
 | 
			
		||||
        {
 | 
			
		||||
            throw new InvalidOperationException($"Can't create an instance of '{nameof(ApplicationUser)}'. " +
 | 
			
		||||
                $"Ensure that '{nameof(ApplicationUser)}' is not an abstract class and has a parameterless constructor");
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
							await UserManager.AddToRoleAsync(user, r);
 | 
			
		||||
						} catch (Exception ex) {
 | 
			
		||||
							Logger.LogWarning(ex, "Failed to add newly created user {name} to role {role}.", user.UserName, role);
 | 
			
		||||
						}
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
    private IUserEmailStore<ApplicationUser> GetEmailStore()
 | 
			
		||||
    {
 | 
			
		||||
        if (!UserManager.SupportsUserEmail)
 | 
			
		||||
        {
 | 
			
		||||
            throw new NotSupportedException("The default UI requires a user store with email support.");
 | 
			
		||||
        }
 | 
			
		||||
        return (IUserEmailStore<ApplicationUser>)UserStore;
 | 
			
		||||
    }
 | 
			
		||||
				// If account confirmation is required, we need to show the link if we don't have a real email sender
 | 
			
		||||
				if (UserManager.Options.SignIn.RequireConfirmedAccount) {
 | 
			
		||||
					RedirectManager.RedirectTo("Account/RegisterConfirmation", new() { ["email"] = Input.Email });
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
    private sealed class InputModel
 | 
			
		||||
    {
 | 
			
		||||
        [Required]
 | 
			
		||||
        [EmailAddress]
 | 
			
		||||
        public string Email { get; set; } = "";
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
				await SignInManager.SignInAsync(user, isPersistent: false, externalLoginInfo.LoginProvider);
 | 
			
		||||
				RedirectManager.RedirectTo(ReturnUrl);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		message = $"Error: {string.Join(",", result.Errors.Select(error => error.Description))}";
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	private ApplicationUser CreateUser() {
 | 
			
		||||
		try {
 | 
			
		||||
			return Activator.CreateInstance<ApplicationUser>();
 | 
			
		||||
		} catch {
 | 
			
		||||
			throw new InvalidOperationException($"Can't create an instance of '{nameof(ApplicationUser)}'. " +
 | 
			
		||||
			                                    $"Ensure that '{nameof(ApplicationUser)}' is not an abstract class and has a parameterless constructor");
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	private IUserEmailStore<ApplicationUser> GetEmailStore() {
 | 
			
		||||
		if (!UserManager.SupportsUserEmail) {
 | 
			
		||||
			throw new NotSupportedException("The default UI requires a user store with email support.");
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		return (IUserEmailStore<ApplicationUser>) UserStore;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	private sealed class InputModel {
 | 
			
		||||
		[Required] [EmailAddress] 
 | 
			
		||||
		public string Email { get; set; } = "";
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -20,54 +20,49 @@
 | 
			
		|||
<StatusMessage Message="@_errorMessage" />
 | 
			
		||||
 | 
			
		||||
<BoardComponent CenterContent="true">
 | 
			
		||||
    <BoardCardComponent Heading="@Localizer["Title"]">
 | 
			
		||||
        <EditForm Model="Input" method="post" OnValidSubmit="LoginUser" FormName="login" class="w-full">
 | 
			
		||||
            <DataAnnotationsValidator />
 | 
			
		||||
	<BoardCardComponent Heading="@Localizer["Title"]">
 | 
			
		||||
		<EditForm Model="Input" method="post" OnValidSubmit="LoginUser" FormName="login" class="w-full">
 | 
			
		||||
			<DataAnnotationsValidator />
 | 
			
		||||
 | 
			
		||||
            <InputLabelComponent LabelText="@Localizer["Email_Label"]" For="() => Input.Email">
 | 
			
		||||
                <InputText @bind-Value="Input.Email" class="input input-bordered w-full" autocomplete="username"
 | 
			
		||||
                           required aria-required="true" placeholder="@Localizer["Email_Placeholder"]" />
 | 
			
		||||
            </InputLabelComponent>
 | 
			
		||||
            <InputLabelComponent LabelText="@Localizer["Password_Label"]" For="() => Input.Password">
 | 
			
		||||
                <InputText @bind-Value="Input.Password" class="input input-bordered w-full" autocomplete="current-password" type="password"
 | 
			
		||||
                           required aria-required="true" placeholder="@Localizer["Password_Placeholder"]" />
 | 
			
		||||
            </InputLabelComponent>
 | 
			
		||||
            <div class="form-control">
 | 
			
		||||
                <label class="label cursor-pointer">
 | 
			
		||||
                    <span class="label-text">@Localizer["RememberMe_Label"]</span>
 | 
			
		||||
                    <InputCheckbox @bind-Value="Input.RememberMe" class="checkbox" />
 | 
			
		||||
                </label>
 | 
			
		||||
            </div>
 | 
			
		||||
			<InputLabelComponent LabelText="@Localizer["Email_Label"]" For="() => Input.Email">
 | 
			
		||||
				<InputText @bind-Value="Input.Email" class="input input-bordered w-full" autocomplete="username"
 | 
			
		||||
				           required aria-required="true" placeholder="@Localizer["Email_Placeholder"]" />
 | 
			
		||||
			</InputLabelComponent>
 | 
			
		||||
			<InputLabelComponent LabelText="@Localizer["Password_Label"]" For="() => Input.Password">
 | 
			
		||||
				<InputText @bind-Value="Input.Password" class="input input-bordered w-full" autocomplete="current-password" type="password"
 | 
			
		||||
				           required aria-required="true" placeholder="@Localizer["Password_Placeholder"]" />
 | 
			
		||||
			</InputLabelComponent>
 | 
			
		||||
			<div class="form-control">
 | 
			
		||||
				<label class="label cursor-pointer">
 | 
			
		||||
					<span class="label-text">@Localizer["RememberMe_Label"]</span>
 | 
			
		||||
					<InputCheckbox @bind-Value="Input.RememberMe" class="checkbox" />
 | 
			
		||||
				</label>
 | 
			
		||||
			</div>
 | 
			
		||||
 | 
			
		||||
            <button type="submit" class="btn btn-primary w-full">
 | 
			
		||||
                @Localizer["Submit"]
 | 
			
		||||
            </button>
 | 
			
		||||
        </EditForm>
 | 
			
		||||
        <ul class="mt-3 flex flex-col gap-1 text-center">
 | 
			
		||||
            <li>
 | 
			
		||||
                <a class="hover:link" href="Account/ForgotPassword">
 | 
			
		||||
                    @Localizer["ResetPassword_Label"]
 | 
			
		||||
                </a>
 | 
			
		||||
            </li>
 | 
			
		||||
            <li>
 | 
			
		||||
                <a class="hover:link" href="@(NavigationManager.GetUriWithQueryParameters("Account/Register", new Dictionary<string, object?> { ["ReturnUrl"] = ReturnUrl }))">
 | 
			
		||||
                    @Localizer["Register_Label"]
 | 
			
		||||
                </a>
 | 
			
		||||
            </li>
 | 
			
		||||
            <li>
 | 
			
		||||
                <a class="hover:link" href="Account/ResendEmailConfirmation">
 | 
			
		||||
                    @Localizer["ResendMailConfirmation_Label"]
 | 
			
		||||
                </a>
 | 
			
		||||
            </li>
 | 
			
		||||
        </ul>
 | 
			
		||||
    </BoardCardComponent>
 | 
			
		||||
    <!--
 | 
			
		||||
    <section class="w-80 max-w-xs">
 | 
			
		||||
        <h2 class="text-2xl lg:text-4xl mb-3">Use another service to log in.</h2>
 | 
			
		||||
        <hr />
 | 
			
		||||
        <ExternalLoginPicker />
 | 
			
		||||
    </section>
 | 
			
		||||
    -->
 | 
			
		||||
			<button type="submit" class="btn btn-primary w-full">
 | 
			
		||||
				@Localizer["Submit"]
 | 
			
		||||
			</button>
 | 
			
		||||
		</EditForm>
 | 
			
		||||
		<ul class="mt-3 flex flex-col gap-1 text-center">
 | 
			
		||||
			<li>
 | 
			
		||||
				<a class="hover:link" href="Account/ForgotPassword">
 | 
			
		||||
					@Localizer["ResetPassword_Label"]
 | 
			
		||||
				</a>
 | 
			
		||||
			</li>
 | 
			
		||||
			<li>
 | 
			
		||||
				<a class="hover:link" href="@(NavigationManager.GetUriWithQueryParameters("Account/Register", new Dictionary<string, object?> { ["ReturnUrl"] = ReturnUrl }))">
 | 
			
		||||
					@Localizer["Register_Label"]
 | 
			
		||||
				</a>
 | 
			
		||||
			</li>
 | 
			
		||||
			<li>
 | 
			
		||||
				<a class="hover:link" href="Account/ResendEmailConfirmation">
 | 
			
		||||
					@Localizer["ResendMailConfirmation_Label"]
 | 
			
		||||
				</a>
 | 
			
		||||
			</li>
 | 
			
		||||
		</ul>
 | 
			
		||||
	</BoardCardComponent>
 | 
			
		||||
    
 | 
			
		||||
	<ExternalLoginPicker />
 | 
			
		||||
</BoardComponent>
 | 
			
		||||
 | 
			
		||||
@code {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -3,6 +3,7 @@
 | 
			
		|||
@using System.ComponentModel.DataAnnotations
 | 
			
		||||
@using Microsoft.AspNetCore.Identity
 | 
			
		||||
@using Wave.Data
 | 
			
		||||
@using Wave.Utilities
 | 
			
		||||
 | 
			
		||||
@inject UserManager<ApplicationUser> UserManager
 | 
			
		||||
@inject SignInManager<ApplicationUser> SignInManager
 | 
			
		||||
| 
						 | 
				
			
			@ -10,66 +11,69 @@
 | 
			
		|||
@inject IdentityRedirectManager RedirectManager
 | 
			
		||||
@inject ILogger<DeletePersonalData> Logger
 | 
			
		||||
@inject IStringLocalizer<DeletePersonalData> Localizer
 | 
			
		||||
@inject IMessageDisplay Message
 | 
			
		||||
 | 
			
		||||
<PageTitle>@Localizer["Title"]</PageTitle>
 | 
			
		||||
 | 
			
		||||
<StatusMessage Message="@message" />
 | 
			
		||||
 | 
			
		||||
<BoardComponent>
 | 
			
		||||
    <BoardCardComponent Heading="@Localizer["Title"]">
 | 
			
		||||
        <Alert Type="Alert.MessageType.Warning" CanRemove="false">
 | 
			
		||||
            <strong class="font-bold">@Localizer["Delete_FinalWarning"]</strong>
 | 
			
		||||
        </Alert>
 | 
			
		||||
        
 | 
			
		||||
        <EditForm Model="Input" FormName="delete-user" OnValidSubmit="OnValidSubmitAsync" method="post" class="w-full">
 | 
			
		||||
            <DataAnnotationsValidator />
 | 
			
		||||
            <ValidationSummary class="text-error" role="alert" />
 | 
			
		||||
            @if (requirePassword) {
 | 
			
		||||
                <InputLabelComponent LabelText="@Localizer["Delete_Password_Label"]" For="() => Input.Password">
 | 
			
		||||
                    <InputText type="password" @bind-Value="Input.Password" class="input input-bordered w-full" required
 | 
			
		||||
                               autocomplete="current-password" placeholder="@Localizer["Delete_Password_Placeholder"]" />
 | 
			
		||||
                </InputLabelComponent>
 | 
			
		||||
            }
 | 
			
		||||
            <button class="btn btn-lg btn-error w-full" type="submit">@Localizer["Delete_FinalSubmit"]</button>
 | 
			
		||||
        </EditForm>
 | 
			
		||||
    </BoardCardComponent>
 | 
			
		||||
	<BoardCardComponent Heading="@Localizer["Title"]">
 | 
			
		||||
		<Alert Type="Alert.MessageType.Warning" CanRemove="false">
 | 
			
		||||
			<strong class="font-bold">@Localizer["Delete_FinalWarning"]</strong>
 | 
			
		||||
		</Alert>
 | 
			
		||||
 | 
			
		||||
		<EditForm Model="Input" FormName="delete-user" OnValidSubmit="OnValidSubmitAsync" method="post" class="w-full mt-3">
 | 
			
		||||
			<DataAnnotationsValidator/>
 | 
			
		||||
			@if (RequirePassword) {
 | 
			
		||||
				<InputLabelComponent LabelText="@Localizer["Delete_Password_Label"]" For="() => Input.Password">
 | 
			
		||||
					<InputText type="password" @bind-Value="Input.Password" class="input input-bordered w-full" required
 | 
			
		||||
					           autocomplete="current-password" placeholder="@Localizer["Delete_Password_Placeholder"]"/>
 | 
			
		||||
				</InputLabelComponent>
 | 
			
		||||
			} else {
 | 
			
		||||
				<!-- our model binding fails without zero bound properties -->
 | 
			
		||||
				<InputText type="hidden" @bind-Value="Input.Password" />
 | 
			
		||||
			}
 | 
			
		||||
			<button class="btn btn-lg btn-error w-full" type="submit">@Localizer["Delete_FinalSubmit"]</button>
 | 
			
		||||
		</EditForm>
 | 
			
		||||
	</BoardCardComponent>
 | 
			
		||||
</BoardComponent>
 | 
			
		||||
 | 
			
		||||
@code {
 | 
			
		||||
    private string? message;
 | 
			
		||||
    private ApplicationUser user = default!;
 | 
			
		||||
    private bool requirePassword;
 | 
			
		||||
	private ApplicationUser User { get; set; } = default!;
 | 
			
		||||
	private bool RequirePassword { get; set; }
 | 
			
		||||
 | 
			
		||||
    [CascadingParameter] 
 | 
			
		||||
    private HttpContext HttpContext { get; set; } = default!;
 | 
			
		||||
    [SupplyParameterFromForm] 
 | 
			
		||||
    private InputModel Input { get; set; } = new();
 | 
			
		||||
	[CascadingParameter] private HttpContext HttpContext { get; set; } = default!;
 | 
			
		||||
 | 
			
		||||
    protected override async Task OnInitializedAsync() {
 | 
			
		||||
        user = await UserAccessor.GetRequiredUserAsync(HttpContext);
 | 
			
		||||
        requirePassword = await UserManager.HasPasswordAsync(user);
 | 
			
		||||
    }
 | 
			
		||||
	[SupplyParameterFromForm(FormName = "delete-user")]
 | 
			
		||||
	private InputModel Input { get; set; } = new();
 | 
			
		||||
 | 
			
		||||
    private async Task OnValidSubmitAsync() {
 | 
			
		||||
        if (requirePassword && !await UserManager.CheckPasswordAsync(user, Input.Password)) {
 | 
			
		||||
            message = Localizer["Delete_ErrorWrongPassword"];
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
	protected override async Task OnInitializedAsync() {
 | 
			
		||||
		User = await UserAccessor.GetRequiredUserAsync(HttpContext);
 | 
			
		||||
		RequirePassword = await UserManager.HasPasswordAsync(User);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
        var result = await UserManager.DeleteAsync(user);
 | 
			
		||||
        if (!result.Succeeded) {
 | 
			
		||||
            throw new InvalidOperationException(Localizer["Delete_ErrorUnknown"]);
 | 
			
		||||
        }
 | 
			
		||||
	private async Task OnValidSubmitAsync() {
 | 
			
		||||
		if (RequirePassword && !await UserManager.CheckPasswordAsync(User, Input.Password)) {
 | 
			
		||||
			Message.ShowError(Localizer["Delete_ErrorWrongPassword"]);
 | 
			
		||||
			return;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
        await SignInManager.SignOutAsync();
 | 
			
		||||
		var result = await UserManager.DeleteAsync(User);
 | 
			
		||||
		if (!result.Succeeded) {
 | 
			
		||||
			throw new InvalidOperationException(Localizer["Delete_ErrorUnknown"]);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
        string userId = await UserManager.GetUserIdAsync(user);
 | 
			
		||||
        Logger.LogInformation("User with ID '{UserId}' deleted themselves.", userId);
 | 
			
		||||
		await SignInManager.SignOutAsync();
 | 
			
		||||
 | 
			
		||||
        RedirectManager.RedirectToWithStatus("/", Localizer["Delete_Success"], HttpContext);
 | 
			
		||||
    }
 | 
			
		||||
		string userId = await UserManager.GetUserIdAsync(User);
 | 
			
		||||
		Logger.LogInformation("User with ID '{UserId}' deleted themselves.", userId);
 | 
			
		||||
 | 
			
		||||
    private sealed class InputModel {
 | 
			
		||||
        [DataType(DataType.Password)] public string Password { get; set; } = "";
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
		Message.ShowSuccess(Localizer["Delete_Success"]);
 | 
			
		||||
		RedirectManager.RedirectTo("/");
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	private sealed class InputModel {
 | 
			
		||||
		[DataType(DataType.Password)] 
 | 
			
		||||
		public string Password { get; set; } = "";
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -9,132 +9,129 @@
 | 
			
		|||
@inject IdentityUserAccessor UserAccessor
 | 
			
		||||
@inject IUserStore<ApplicationUser> UserStore
 | 
			
		||||
@inject IdentityRedirectManager RedirectManager
 | 
			
		||||
@inject IStringLocalizer<ExternalLogin> Localizer
 | 
			
		||||
 | 
			
		||||
<PageTitle>Manage your external logins</PageTitle>
 | 
			
		||||
<PageTitle>@Localizer["Title"]</PageTitle>
 | 
			
		||||
 | 
			
		||||
<StatusMessage />
 | 
			
		||||
@if (currentLogins?.Count > 0)
 | 
			
		||||
{
 | 
			
		||||
    <h3>Registered Logins</h3>
 | 
			
		||||
    <table class="table">
 | 
			
		||||
        <tbody>
 | 
			
		||||
            @foreach (var login in currentLogins)
 | 
			
		||||
            {
 | 
			
		||||
                <tr>
 | 
			
		||||
                    <td>@login.ProviderDisplayName</td>
 | 
			
		||||
                    <td>
 | 
			
		||||
                        @if (showRemoveButton)
 | 
			
		||||
                        {
 | 
			
		||||
                            <form @formname="@($"remove-login-{login.LoginProvider}")" @onsubmit="OnSubmitAsync" method="post">
 | 
			
		||||
                                <AntiforgeryToken />
 | 
			
		||||
                                <div>
 | 
			
		||||
                                    <input type="hidden" name="@nameof(LoginProvider)" value="@login.LoginProvider" />
 | 
			
		||||
                                    <input type="hidden" name="@nameof(ProviderKey)" value="@login.ProviderKey" />
 | 
			
		||||
                                    <button type="submit" class="btn btn-primary" title="Remove this @login.ProviderDisplayName login from your account">Remove</button>
 | 
			
		||||
                                </div>
 | 
			
		||||
                            </form>
 | 
			
		||||
                        }
 | 
			
		||||
                        else
 | 
			
		||||
                        {
 | 
			
		||||
                            @:  
 | 
			
		||||
                        }
 | 
			
		||||
                    </td>
 | 
			
		||||
                </tr>
 | 
			
		||||
            }
 | 
			
		||||
        </tbody>
 | 
			
		||||
    </table>
 | 
			
		||||
}
 | 
			
		||||
@if (otherLogins?.Count > 0)
 | 
			
		||||
{
 | 
			
		||||
    <h4>Add another service to log in.</h4>
 | 
			
		||||
    <hr />
 | 
			
		||||
    <form class="form-horizontal" action="Account/Manage/LinkExternalLogin" method="post">
 | 
			
		||||
        <AntiforgeryToken />
 | 
			
		||||
        <div>
 | 
			
		||||
            <p>
 | 
			
		||||
                @foreach (var provider in otherLogins)
 | 
			
		||||
                {
 | 
			
		||||
                    <button type="submit" class="btn btn-primary" name="Provider" value="@provider.Name" title="Log in using your @provider.DisplayName account">
 | 
			
		||||
                        @provider.DisplayName
 | 
			
		||||
                    </button>
 | 
			
		||||
                }
 | 
			
		||||
            </p>
 | 
			
		||||
        </div>
 | 
			
		||||
    </form>
 | 
			
		||||
<StatusMessage/>
 | 
			
		||||
 | 
			
		||||
@if (CurrentLogins?.Count > 0) {
 | 
			
		||||
	<BoardComponent>
 | 
			
		||||
		<BoardCardComponent Heading="@Localizer["Title"]">
 | 
			
		||||
			<div class="overflow-x-auto">
 | 
			
		||||
				<table class="table">
 | 
			
		||||
					<thead>
 | 
			
		||||
					<tr>
 | 
			
		||||
						<th>Provider</th>
 | 
			
		||||
						<th></th>
 | 
			
		||||
					</tr>
 | 
			
		||||
					</thead>
 | 
			
		||||
					<tbody>
 | 
			
		||||
					@foreach (var login in CurrentLogins) {
 | 
			
		||||
						<tr>
 | 
			
		||||
							<td>@login.ProviderDisplayName</td>
 | 
			
		||||
							<td>
 | 
			
		||||
								@if (ShowRemoveButton) {
 | 
			
		||||
									<form @formname="@($"remove-login-{login.LoginProvider}")" @onsubmit="OnSubmitAsync" method="post">
 | 
			
		||||
										<AntiforgeryToken/>
 | 
			
		||||
										<div>
 | 
			
		||||
											<input type="hidden" name="@nameof(LoginProvider)" value="@login.LoginProvider"/>
 | 
			
		||||
											<input type="hidden" name="@nameof(ProviderKey)" value="@login.ProviderKey"/>
 | 
			
		||||
											<button type="submit" class="btn btn-primary" title="Remove this @login.ProviderDisplayName login from your account">Remove</button>
 | 
			
		||||
										</div>
 | 
			
		||||
									</form>
 | 
			
		||||
								} else {
 | 
			
		||||
									@:  
 | 
			
		||||
								}
 | 
			
		||||
							</td>
 | 
			
		||||
						</tr>
 | 
			
		||||
					}
 | 
			
		||||
					</tbody>
 | 
			
		||||
				</table>
 | 
			
		||||
			</div>
 | 
			
		||||
		</BoardCardComponent>
 | 
			
		||||
		@if (OtherLogins?.Count > 0) {
 | 
			
		||||
			<BoardCardComponent Heading="Add another service to log in.">
 | 
			
		||||
				<form class="form-horizontal" action="Account/Manage/LinkExternalLogin" method="post">
 | 
			
		||||
					<AntiforgeryToken/>
 | 
			
		||||
					<div>
 | 
			
		||||
						<p>
 | 
			
		||||
							@foreach (var provider in OtherLogins) {
 | 
			
		||||
								<button type="submit" class="btn btn-primary" name="Provider" value="@provider.Name" title="Log in using your @provider.DisplayName account">
 | 
			
		||||
									@provider.DisplayName
 | 
			
		||||
								</button>
 | 
			
		||||
							}
 | 
			
		||||
						</p>
 | 
			
		||||
					</div>
 | 
			
		||||
				</form>
 | 
			
		||||
			</BoardCardComponent>
 | 
			
		||||
		}
 | 
			
		||||
	</BoardComponent>
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@code {
 | 
			
		||||
    public const string LinkLoginCallbackAction = "LinkLoginCallback";
 | 
			
		||||
	public const string LinkLoginCallbackAction = "LinkLoginCallback";
 | 
			
		||||
 | 
			
		||||
    private ApplicationUser user = default!;
 | 
			
		||||
    private IList<UserLoginInfo>? currentLogins;
 | 
			
		||||
    private IList<AuthenticationScheme>? otherLogins;
 | 
			
		||||
    private bool showRemoveButton;
 | 
			
		||||
	private ApplicationUser User { get; set; } = default!;
 | 
			
		||||
	private IList<UserLoginInfo>? CurrentLogins { get; set; }
 | 
			
		||||
	private IList<AuthenticationScheme>? OtherLogins { get; set; }
 | 
			
		||||
	private bool ShowRemoveButton { get; set; }
 | 
			
		||||
 | 
			
		||||
    [CascadingParameter]
 | 
			
		||||
	[CascadingParameter] 
 | 
			
		||||
    private HttpContext HttpContext { get; set; } = default!;
 | 
			
		||||
 | 
			
		||||
    [SupplyParameterFromForm]
 | 
			
		||||
	[SupplyParameterFromForm] 
 | 
			
		||||
    private string? LoginProvider { get; set; }
 | 
			
		||||
 | 
			
		||||
    [SupplyParameterFromForm]
 | 
			
		||||
	[SupplyParameterFromForm] 
 | 
			
		||||
    private string? ProviderKey { get; set; }
 | 
			
		||||
 | 
			
		||||
    [SupplyParameterFromQuery]
 | 
			
		||||
	[SupplyParameterFromQuery] 
 | 
			
		||||
    private string? Action { get; set; }
 | 
			
		||||
 | 
			
		||||
    protected override async Task OnInitializedAsync()
 | 
			
		||||
    {
 | 
			
		||||
        user = await UserAccessor.GetRequiredUserAsync(HttpContext);
 | 
			
		||||
        currentLogins = await UserManager.GetLoginsAsync(user);
 | 
			
		||||
        otherLogins = (await SignInManager.GetExternalAuthenticationSchemesAsync())
 | 
			
		||||
            .Where(auth => currentLogins.All(ul => auth.Name != ul.LoginProvider))
 | 
			
		||||
            .ToList();
 | 
			
		||||
	protected override async Task OnInitializedAsync() {
 | 
			
		||||
		User = await UserAccessor.GetRequiredUserAsync(HttpContext);
 | 
			
		||||
		CurrentLogins = await UserManager.GetLoginsAsync(User);
 | 
			
		||||
		OtherLogins = (await SignInManager.GetExternalAuthenticationSchemesAsync())
 | 
			
		||||
			.Where(auth => CurrentLogins.All(ul => auth.Name != ul.LoginProvider))
 | 
			
		||||
			.ToList();
 | 
			
		||||
 | 
			
		||||
        string? passwordHash = null;
 | 
			
		||||
        if (UserStore is IUserPasswordStore<ApplicationUser> userPasswordStore)
 | 
			
		||||
        {
 | 
			
		||||
            passwordHash = await userPasswordStore.GetPasswordHashAsync(user, HttpContext.RequestAborted);
 | 
			
		||||
        }
 | 
			
		||||
		string? passwordHash = null;
 | 
			
		||||
		if (UserStore is IUserPasswordStore<ApplicationUser> userPasswordStore) {
 | 
			
		||||
			passwordHash = await userPasswordStore.GetPasswordHashAsync(User, HttpContext.RequestAborted);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
        showRemoveButton = passwordHash is not null || currentLogins.Count > 1;
 | 
			
		||||
		ShowRemoveButton = passwordHash is not null || CurrentLogins.Count > 1;
 | 
			
		||||
 | 
			
		||||
        if (HttpMethods.IsGet(HttpContext.Request.Method) && Action == LinkLoginCallbackAction)
 | 
			
		||||
        {
 | 
			
		||||
            await OnGetLinkLoginCallbackAsync();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
		if (HttpMethods.IsGet(HttpContext.Request.Method) && Action == LinkLoginCallbackAction) {
 | 
			
		||||
			await OnGetLinkLoginCallbackAsync();
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
    private async Task OnSubmitAsync()
 | 
			
		||||
    {
 | 
			
		||||
        var result = await UserManager.RemoveLoginAsync(user, LoginProvider!, ProviderKey!);
 | 
			
		||||
        if (!result.Succeeded)
 | 
			
		||||
        {
 | 
			
		||||
            RedirectManager.RedirectToCurrentPageWithStatus("Error: The external login was not removed.", HttpContext);
 | 
			
		||||
        }
 | 
			
		||||
	private async Task OnSubmitAsync() {
 | 
			
		||||
		var result = await UserManager.RemoveLoginAsync(User, LoginProvider!, ProviderKey!);
 | 
			
		||||
		if (!result.Succeeded) {
 | 
			
		||||
			RedirectManager.RedirectToCurrentPageWithStatus("Error: The external login was not removed.", HttpContext);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
        await SignInManager.RefreshSignInAsync(user);
 | 
			
		||||
        RedirectManager.RedirectToCurrentPageWithStatus("The external login was removed.", HttpContext);
 | 
			
		||||
    }
 | 
			
		||||
		await SignInManager.RefreshSignInAsync(User);
 | 
			
		||||
		RedirectManager.RedirectToCurrentPageWithStatus("The external login was removed.", HttpContext);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
    private async Task OnGetLinkLoginCallbackAsync()
 | 
			
		||||
    {
 | 
			
		||||
        var userId = await UserManager.GetUserIdAsync(user);
 | 
			
		||||
        var info = await SignInManager.GetExternalLoginInfoAsync(userId);
 | 
			
		||||
        if (info is null)
 | 
			
		||||
        {
 | 
			
		||||
            RedirectManager.RedirectToCurrentPageWithStatus("Error: Could not load external login info.", HttpContext);
 | 
			
		||||
        }
 | 
			
		||||
	private async Task OnGetLinkLoginCallbackAsync() {
 | 
			
		||||
		var userId = await UserManager.GetUserIdAsync(User);
 | 
			
		||||
		var info = await SignInManager.GetExternalLoginInfoAsync(userId);
 | 
			
		||||
		if (info is null) {
 | 
			
		||||
			RedirectManager.RedirectToCurrentPageWithStatus("Error: Could not load external login info.", HttpContext);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
        var result = await UserManager.AddLoginAsync(user, info);
 | 
			
		||||
        if (!result.Succeeded)
 | 
			
		||||
        {
 | 
			
		||||
            RedirectManager.RedirectToCurrentPageWithStatus("Error: The external login was not added. External logins can only be associated with one account.", HttpContext);
 | 
			
		||||
        }
 | 
			
		||||
		var result = await UserManager.AddLoginAsync(User, info);
 | 
			
		||||
		if (!result.Succeeded) {
 | 
			
		||||
			RedirectManager.RedirectToCurrentPageWithStatus("Error: The external login was not added. External logins can only be associated with one account.", HttpContext);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
        // Clear the existing external cookie to ensure a clean login process
 | 
			
		||||
        await HttpContext.SignOutAsync(IdentityConstants.ExternalScheme);
 | 
			
		||||
		// Clear the existing external cookie to ensure a clean login process
 | 
			
		||||
		await HttpContext.SignOutAsync(IdentityConstants.ExternalScheme);
 | 
			
		||||
 | 
			
		||||
        RedirectManager.RedirectToCurrentPageWithStatus("The external login was added.", HttpContext);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
		RedirectManager.RedirectToCurrentPageWithStatus("The external login was added.", HttpContext);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -8,80 +8,72 @@
 | 
			
		|||
@inject SignInManager<ApplicationUser> SignInManager
 | 
			
		||||
@inject IdentityUserAccessor UserAccessor
 | 
			
		||||
@inject IdentityRedirectManager RedirectManager
 | 
			
		||||
@inject IStringLocalizer<SetPassword> Localizer
 | 
			
		||||
 | 
			
		||||
<PageTitle>Set password</PageTitle>
 | 
			
		||||
<PageTitle>@Localizer["Title"]</PageTitle>
 | 
			
		||||
 | 
			
		||||
<h3>Set your password</h3>
 | 
			
		||||
<StatusMessage Message="@message" />
 | 
			
		||||
<p class="text-info">
 | 
			
		||||
    You do not have a local username/password for this site. Add a local
 | 
			
		||||
    account so you can log in without an external login.
 | 
			
		||||
</p>
 | 
			
		||||
<div class="row">
 | 
			
		||||
    <div class="col-md-6">
 | 
			
		||||
        <EditForm Model="Input" FormName="set-password" OnValidSubmit="OnValidSubmitAsync" method="post">
 | 
			
		||||
            <DataAnnotationsValidator />
 | 
			
		||||
            <ValidationSummary class="text-danger" role="alert" />
 | 
			
		||||
            <div class="form-floating mb-3">
 | 
			
		||||
                <InputText type="password" @bind-Value="Input.NewPassword" class="form-control" autocomplete="new-password" placeholder="Please enter your new password." />
 | 
			
		||||
                <label for="new-password" class="form-label">New password</label>
 | 
			
		||||
                <ValidationMessage For="() => Input.NewPassword" class="text-danger" />
 | 
			
		||||
            </div>
 | 
			
		||||
            <div class="form-floating mb-3">
 | 
			
		||||
                <InputText type="password" @bind-Value="Input.ConfirmPassword" class="form-control" autocomplete="new-password" placeholder="Please confirm your new password." />
 | 
			
		||||
                <label for="confirm-password" class="form-label">Confirm password</label>
 | 
			
		||||
                <ValidationMessage For="() => Input.ConfirmPassword" class="text-danger" />
 | 
			
		||||
            </div>
 | 
			
		||||
            <button type="submit" class="w-100 btn btn-lg btn-primary">Set password</button>
 | 
			
		||||
        </EditForm>
 | 
			
		||||
     </div>
 | 
			
		||||
</div>
 | 
			
		||||
<StatusMessage Message="@Message"/>
 | 
			
		||||
<BoardComponent>
 | 
			
		||||
	<BoardCardComponent Heading="@Localizer["Title"]">
 | 
			
		||||
		<Alert CanRemove="false" Type="Alert.MessageType.Information">
 | 
			
		||||
			<p>@Localizer["Message"]</p>
 | 
			
		||||
		</Alert>
 | 
			
		||||
		<EditForm Model="Input" FormName="set-password" OnValidSubmit="OnValidSubmitAsync" method="post">
 | 
			
		||||
			<DataAnnotationsValidator/>
 | 
			
		||||
			<InputLabelComponent LabelText="@Localizer["NewPassword_Label"]" For="() => Input.NewPassword">
 | 
			
		||||
				<InputText type="password" @bind-Value="Input.NewPassword" class="input input-bordered w-full" autocomplete="new-password"
 | 
			
		||||
				           required aria-required="true" placeholder="@Localizer["NewPassword_Placeholder"]"/>
 | 
			
		||||
			</InputLabelComponent>
 | 
			
		||||
			<InputLabelComponent LabelText="@Localizer["ConfirmPassword_Label"]" For="() => Input.ConfirmPassword">
 | 
			
		||||
				<InputText type="password" @bind-Value="Input.ConfirmPassword" class="input input-bordered w-full" autocomplete="new-password"
 | 
			
		||||
				           required aria-required="true" placeholder="@Localizer["ConfirmPassword_Placeholder"]"/>
 | 
			
		||||
			</InputLabelComponent>
 | 
			
		||||
			<button type="submit" class="btn btn-primary w-full">
 | 
			
		||||
				@Localizer["ChangePassword_Submit"]
 | 
			
		||||
			</button>
 | 
			
		||||
		</EditForm>
 | 
			
		||||
	</BoardCardComponent>
 | 
			
		||||
</BoardComponent>
 | 
			
		||||
 | 
			
		||||
@code {
 | 
			
		||||
    private string? message;
 | 
			
		||||
    private ApplicationUser user = default!;
 | 
			
		||||
	private string? Message { get; set; }
 | 
			
		||||
	private ApplicationUser User { get; set; } = default!;
 | 
			
		||||
 | 
			
		||||
    [CascadingParameter]
 | 
			
		||||
    private HttpContext HttpContext { get; set; } = default!;
 | 
			
		||||
	[CascadingParameter] private HttpContext HttpContext { get; set; } = default!;
 | 
			
		||||
 | 
			
		||||
    [SupplyParameterFromForm]
 | 
			
		||||
    private InputModel Input { get; set; } = new();
 | 
			
		||||
	[SupplyParameterFromForm] private InputModel Input { get; set; } = new();
 | 
			
		||||
 | 
			
		||||
    protected override async Task OnInitializedAsync()
 | 
			
		||||
    {
 | 
			
		||||
        user = await UserAccessor.GetRequiredUserAsync(HttpContext);
 | 
			
		||||
	protected override async Task OnInitializedAsync() {
 | 
			
		||||
		User = await UserAccessor.GetRequiredUserAsync(HttpContext);
 | 
			
		||||
 | 
			
		||||
        var hasPassword = await UserManager.HasPasswordAsync(user);
 | 
			
		||||
        if (hasPassword)
 | 
			
		||||
        {
 | 
			
		||||
            RedirectManager.RedirectTo("Account/Manage/ChangePassword");
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
		bool hasPassword = await UserManager.HasPasswordAsync(User);
 | 
			
		||||
		if (hasPassword) {
 | 
			
		||||
			RedirectManager.RedirectTo("Account/Manage/ChangePassword");
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
    private async Task OnValidSubmitAsync()
 | 
			
		||||
    {
 | 
			
		||||
        var addPasswordResult = await UserManager.AddPasswordAsync(user, Input.NewPassword!);
 | 
			
		||||
        if (!addPasswordResult.Succeeded)
 | 
			
		||||
        {
 | 
			
		||||
            message = $"Error: {string.Join(",", addPasswordResult.Errors.Select(error => error.Description))}";
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
	private async Task OnValidSubmitAsync() {
 | 
			
		||||
		var addPasswordResult = await UserManager.AddPasswordAsync(User, Input.NewPassword!);
 | 
			
		||||
		if (!addPasswordResult.Succeeded) {
 | 
			
		||||
			Message = $"Error: {string.Join(",", addPasswordResult.Errors.Select(error => error.Description))}";
 | 
			
		||||
			return;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
        await SignInManager.RefreshSignInAsync(user);
 | 
			
		||||
        RedirectManager.RedirectToCurrentPageWithStatus("Your password has been set.", HttpContext);
 | 
			
		||||
    }
 | 
			
		||||
		await SignInManager.RefreshSignInAsync(User);
 | 
			
		||||
		RedirectManager.RedirectToCurrentPageWithStatus("Your password has been set.", HttpContext);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
    private sealed class InputModel
 | 
			
		||||
    {
 | 
			
		||||
        [Required]
 | 
			
		||||
        [StringLength(100, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 6)]
 | 
			
		||||
        [DataType(DataType.Password)]
 | 
			
		||||
        [Display(Name = "New password")]
 | 
			
		||||
        public string? NewPassword { get; set; }
 | 
			
		||||
	private sealed class InputModel {
 | 
			
		||||
		[Required]
 | 
			
		||||
		[StringLength(100, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 6)]
 | 
			
		||||
		[DataType(DataType.Password)]
 | 
			
		||||
		[Display(Name = "New password")]
 | 
			
		||||
		public string? NewPassword { get; set; }
 | 
			
		||||
 | 
			
		||||
        [DataType(DataType.Password)]
 | 
			
		||||
        [Display(Name = "Confirm new password")]
 | 
			
		||||
        [Compare("NewPassword", ErrorMessage = "The new password and confirmation password do not match.")]
 | 
			
		||||
        public string? ConfirmPassword { get; set; }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
		[DataType(DataType.Password)]
 | 
			
		||||
		[Display(Name = "Confirm new password")]
 | 
			
		||||
		[Compare("NewPassword", ErrorMessage = "The new password and confirmation password do not match.")]
 | 
			
		||||
		public string? ConfirmPassword { get; set; }
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -9,64 +9,59 @@
 | 
			
		|||
@inject IEmailSender<ApplicationUser> EmailSender
 | 
			
		||||
@inject NavigationManager NavigationManager
 | 
			
		||||
@inject IdentityRedirectManager RedirectManager
 | 
			
		||||
@inject IStringLocalizer<RegisterConfirmation> Localizer
 | 
			
		||||
 | 
			
		||||
<HeadContent>
 | 
			
		||||
    <meta name="robots" content="noindex,nofollow">
 | 
			
		||||
	<meta name="robots" content="noindex,nofollow">
 | 
			
		||||
</HeadContent>
 | 
			
		||||
 | 
			
		||||
<PageTitle>Register confirmation</PageTitle>
 | 
			
		||||
<PageTitle>@Localizer["Title"] </PageTitle>
 | 
			
		||||
 | 
			
		||||
<h1>Register confirmation</h1>
 | 
			
		||||
<StatusMessage Message="@statusMessage"/>
 | 
			
		||||
 | 
			
		||||
<StatusMessage Message="@statusMessage" />
 | 
			
		||||
 | 
			
		||||
@if (emailConfirmationLink is not null)
 | 
			
		||||
{
 | 
			
		||||
    <p>
 | 
			
		||||
        This app does not currently have a real email sender registered, see <a href="https://aka.ms/aspaccountconf">these docs</a> for how to configure a real email sender.
 | 
			
		||||
        Normally this would be emailed: <a href="@emailConfirmationLink">Click here to confirm your account</a>
 | 
			
		||||
    </p>
 | 
			
		||||
}
 | 
			
		||||
else
 | 
			
		||||
{
 | 
			
		||||
    <p>Please check your email to confirm your account.</p>
 | 
			
		||||
}
 | 
			
		||||
<BoardComponent CenterContent="true">
 | 
			
		||||
	<BoardCardComponent Heading="@Localizer["Title"]">
 | 
			
		||||
		<Alert CanRemove="false" Type="Alert.MessageType.Success">
 | 
			
		||||
			@if (emailConfirmationLink is not null) {
 | 
			
		||||
				<p>
 | 
			
		||||
					This app does not currently have a real email sender registered, see <a href="https://aka.ms/aspaccountconf">these docs</a> for how to configure a real email sender.
 | 
			
		||||
					Normally this would be emailed: <a href="@emailConfirmationLink">Click here to confirm your account</a>
 | 
			
		||||
				</p>
 | 
			
		||||
			} else {
 | 
			
		||||
				<p>@Localizer["Message"]</p>
 | 
			
		||||
			}
 | 
			
		||||
		</Alert>
 | 
			
		||||
	</BoardCardComponent>
 | 
			
		||||
</BoardComponent>
 | 
			
		||||
 | 
			
		||||
@code {
 | 
			
		||||
    private string? emailConfirmationLink;
 | 
			
		||||
    private string? statusMessage;
 | 
			
		||||
	private string? emailConfirmationLink;
 | 
			
		||||
	private string? statusMessage;
 | 
			
		||||
 | 
			
		||||
    [CascadingParameter]
 | 
			
		||||
    private HttpContext HttpContext { get; set; } = default!;
 | 
			
		||||
	[CascadingParameter] private HttpContext HttpContext { get; set; } = default!;
 | 
			
		||||
 | 
			
		||||
    [SupplyParameterFromQuery]
 | 
			
		||||
    private string? Email { get; set; }
 | 
			
		||||
	[SupplyParameterFromQuery] private string? Email { get; set; }
 | 
			
		||||
 | 
			
		||||
    [SupplyParameterFromQuery]
 | 
			
		||||
    private string? ReturnUrl { get; set; }
 | 
			
		||||
	[SupplyParameterFromQuery] private string? ReturnUrl { get; set; }
 | 
			
		||||
 | 
			
		||||
    protected override async Task OnInitializedAsync()
 | 
			
		||||
    {
 | 
			
		||||
        if (Email is null)
 | 
			
		||||
        {
 | 
			
		||||
            RedirectManager.RedirectTo("");
 | 
			
		||||
        }
 | 
			
		||||
	protected override async Task OnInitializedAsync() {
 | 
			
		||||
		if (Email is null) {
 | 
			
		||||
			RedirectManager.RedirectTo("");
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
        var user = await UserManager.FindByEmailAsync(Email);
 | 
			
		||||
        if (user is null)
 | 
			
		||||
        {
 | 
			
		||||
            HttpContext.Response.StatusCode = StatusCodes.Status404NotFound;
 | 
			
		||||
            statusMessage = "Error finding user for unspecified email";
 | 
			
		||||
        }
 | 
			
		||||
        else if (EmailSender is IdentityNoOpEmailSender)
 | 
			
		||||
        {
 | 
			
		||||
            // Once you add a real email sender, you should remove this code that lets you confirm the account
 | 
			
		||||
            var userId = await UserManager.GetUserIdAsync(user);
 | 
			
		||||
            var code = await UserManager.GenerateEmailConfirmationTokenAsync(user);
 | 
			
		||||
            code = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(code));
 | 
			
		||||
            emailConfirmationLink = NavigationManager.GetUriWithQueryParameters(
 | 
			
		||||
                NavigationManager.ToAbsoluteUri("Account/ConfirmEmail").AbsoluteUri,
 | 
			
		||||
                new Dictionary<string, object?> { ["userId"] = userId, ["code"] = code, ["returnUrl"] = ReturnUrl });
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
		var user = await UserManager.FindByEmailAsync(Email);
 | 
			
		||||
		if (user is null) {
 | 
			
		||||
			HttpContext.Response.StatusCode = StatusCodes.Status404NotFound;
 | 
			
		||||
			statusMessage = "Error finding user for unspecified email";
 | 
			
		||||
		} else if (EmailSender is IdentityNoOpEmailSender) {
 | 
			
		||||
			// Once you add a real email sender, you should remove this code that lets you confirm the account
 | 
			
		||||
			var userId = await UserManager.GetUserIdAsync(user);
 | 
			
		||||
			var code = await UserManager.GenerateEmailConfirmationTokenAsync(user);
 | 
			
		||||
			code = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(code));
 | 
			
		||||
			emailConfirmationLink = NavigationManager.GetUriWithQueryParameters(
 | 
			
		||||
				NavigationManager.ToAbsoluteUri("Account/ConfirmEmail").AbsoluteUri,
 | 
			
		||||
				new Dictionary<string, object?> {["userId"] = userId, ["code"] = code, ["returnUrl"] = ReturnUrl});
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -4,40 +4,42 @@
 | 
			
		|||
 | 
			
		||||
@inject SignInManager<ApplicationUser> SignInManager
 | 
			
		||||
@inject IdentityRedirectManager RedirectManager
 | 
			
		||||
@inject IStringLocalizer<ExternalLoginPicker> Localizer
 | 
			
		||||
 | 
			
		||||
@if (externalLogins.Length == 0)
 | 
			
		||||
{
 | 
			
		||||
    <div>
 | 
			
		||||
        <p>
 | 
			
		||||
            There are no external authentication services configured. See this <a href="https://go.microsoft.com/fwlink/?LinkID=532715">article
 | 
			
		||||
            about setting up this ASP.NET application to support logging in via external services</a>.
 | 
			
		||||
        </p>
 | 
			
		||||
    </div>
 | 
			
		||||
}
 | 
			
		||||
else
 | 
			
		||||
{
 | 
			
		||||
    <form class="form-horizontal" action="Account/PerformExternalLogin" method="post">
 | 
			
		||||
        <div>
 | 
			
		||||
            <AntiforgeryToken />
 | 
			
		||||
            <input type="hidden" name="ReturnUrl" value="@ReturnUrl" />
 | 
			
		||||
            <p>
 | 
			
		||||
                @foreach (var provider in externalLogins)
 | 
			
		||||
                {
 | 
			
		||||
                    <button type="submit" class="btn btn-primary" name="provider" value="@provider.Name" title="Log in using your @provider.DisplayName account">@provider.DisplayName</button>
 | 
			
		||||
                }
 | 
			
		||||
            </p>
 | 
			
		||||
        </div>
 | 
			
		||||
    </form>
 | 
			
		||||
@if (externalLogins.Length > 0) {
 | 
			
		||||
	<BoardCardComponent Heading="@Localizer["Title"]">
 | 
			
		||||
		<form class="form-horizontal" action="Account/PerformExternalLogin" method="post">
 | 
			
		||||
			<div class="flex flex-col space-y-3">
 | 
			
		||||
				<AntiforgeryToken/>
 | 
			
		||||
				<input type="hidden" name="ReturnUrl" value="@ReturnUrl"/>
 | 
			
		||||
				<p>
 | 
			
		||||
					@foreach (var provider in externalLogins) {
 | 
			
		||||
						if (provider.Name is "OpenIdConnect") {
 | 
			
		||||
							<button type="submit" class="btn btn-wide btn-primary" name="provider" value="@provider.Name" title="@Localizer["OpenId_Tooltip"]">
 | 
			
		||||
								@Localizer["OpenId_Label"]
 | 
			
		||||
								<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
 | 
			
		||||
									<path stroke-linecap="round" stroke-linejoin="round" d="M15.75 9V5.25A2.25 2.25 0 0 0 13.5 3h-6a2.25 2.25 0 0 0-2.25 2.25v13.5A2.25 2.25 0 0 0 7.5 21h6a2.25 2.25 0 0 0 2.25-2.25V15m3 0 3-3m0 0-3-3m3 3H9" />
 | 
			
		||||
								</svg>
 | 
			
		||||
							</button>
 | 
			
		||||
						} else {
 | 
			
		||||
							<button type="submit" class="btn btn-wide btn-primary" name="provider" value="@provider.Name" title="Log in using your @provider.DisplayName account">
 | 
			
		||||
								@provider.DisplayName
 | 
			
		||||
							</button>
 | 
			
		||||
						}
 | 
			
		||||
					}
 | 
			
		||||
				</p>
 | 
			
		||||
			</div>
 | 
			
		||||
		</form>
 | 
			
		||||
	</BoardCardComponent>
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@code {
 | 
			
		||||
    private AuthenticationScheme[] externalLogins = [];
 | 
			
		||||
	private AuthenticationScheme[] externalLogins = [];
 | 
			
		||||
 | 
			
		||||
    [SupplyParameterFromQuery]
 | 
			
		||||
    private string? ReturnUrl { get; set; }
 | 
			
		||||
	[SupplyParameterFromQuery] private string? ReturnUrl { get; set; }
 | 
			
		||||
 | 
			
		||||
    protected override async Task OnInitializedAsync()
 | 
			
		||||
    {
 | 
			
		||||
        externalLogins = (await SignInManager.GetExternalAuthenticationSchemesAsync()).ToArray();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
	protected override async Task OnInitializedAsync() {
 | 
			
		||||
		externalLogins = (await SignInManager.GetExternalAuthenticationSchemesAsync()).ToArray();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										7
									
								
								Wave/Data/OidcConfiguration.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								Wave/Data/OidcConfiguration.cs
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,7 @@
 | 
			
		|||
namespace Wave.Data;
 | 
			
		||||
 | 
			
		||||
public class OidcConfiguration {
 | 
			
		||||
	public string Authority { get; set; } = string.Empty;
 | 
			
		||||
	public string ClientId { get; set; } = string.Empty;
 | 
			
		||||
	public string ClientSecret { get; set; } = string.Empty;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -11,7 +11,7 @@ public class UserClaimsFactory(
 | 
			
		|||
	: UserClaimsPrincipalFactory<ApplicationUser, IdentityRole>(userManager, roleManager, options) {
 | 
			
		||||
	protected override async Task<ClaimsIdentity> GenerateClaimsAsync(ApplicationUser user) {
 | 
			
		||||
		var principal = await base.GenerateClaimsAsync(user);
 | 
			
		||||
		principal.AddClaim(new Claim("Id", user.Id));
 | 
			
		||||
		// principal.AddClaim(new Claim("Id", user.Id));
 | 
			
		||||
		principal.AddClaim(new Claim("FullName", user.Name));
 | 
			
		||||
		return principal;
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -5,7 +5,6 @@
 | 
			
		|||
using Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption;
 | 
			
		||||
using Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel;
 | 
			
		||||
using Microsoft.AspNetCore.Identity;
 | 
			
		||||
using Microsoft.AspNetCore.Identity.UI.Services;
 | 
			
		||||
using Microsoft.AspNetCore.StaticFiles;
 | 
			
		||||
using Microsoft.EntityFrameworkCore;
 | 
			
		||||
using Microsoft.Extensions.Caching.Distributed;
 | 
			
		||||
| 
						 | 
				
			
			@ -19,6 +18,7 @@
 | 
			
		|||
using Wave.Data;
 | 
			
		||||
using Wave.Services;
 | 
			
		||||
using Wave.Utilities;
 | 
			
		||||
using Microsoft.IdentityModel.Protocols.OpenIdConnect;
 | 
			
		||||
 | 
			
		||||
string humanReadableVersion = Assembly.GetEntryAssembly()?
 | 
			
		||||
	.GetCustomAttribute<AssemblyInformationalVersionAttribute>()?
 | 
			
		||||
| 
						 | 
				
			
			@ -94,7 +94,7 @@
 | 
			
		|||
	.AddPolicy("ArticleEditOrReviewPermissions", p => p.RequireRole("Author", "Reviewer", "Admin"))
 | 
			
		||||
	
 | 
			
		||||
	.AddPolicy("EmailApi", p => p.RequireClaim("EmailApi")
 | 
			
		||||
		.AddAuthenticationSchemes(ApiKeyDefaults.AuthenticationScheme));
 | 
			
		||||
	.AddAuthenticationSchemes(ApiKeyDefaults.AuthenticationScheme));
 | 
			
		||||
builder.Services.AddAuthentication(options => {
 | 
			
		||||
		options.DefaultScheme = IdentityConstants.ApplicationScheme;
 | 
			
		||||
		options.DefaultSignInScheme = IdentityConstants.ExternalScheme;
 | 
			
		||||
| 
						 | 
				
			
			@ -103,6 +103,30 @@
 | 
			
		|||
		options.Realm = "Wave API";
 | 
			
		||||
	})
 | 
			
		||||
	.AddIdentityCookies();
 | 
			
		||||
if (builder.Configuration.GetSection("Oidc").Get<OidcConfiguration>() is {} oidc && !string.IsNullOrWhiteSpace(oidc.Authority)) {
 | 
			
		||||
	builder.Services.AddAuthentication(options => {
 | 
			
		||||
		options.DefaultScheme = IdentityConstants.ApplicationScheme;
 | 
			
		||||
		options.DefaultSignInScheme = IdentityConstants.ExternalScheme;
 | 
			
		||||
	}).AddOpenIdConnect(options => {
 | 
			
		||||
		options.SignInScheme = IdentityConstants.ExternalScheme;
 | 
			
		||||
 | 
			
		||||
		options.Scope.Add(OpenIdConnectScope.OpenIdProfile);
 | 
			
		||||
		options.Scope.Add(OpenIdConnectScope.OfflineAccess);
 | 
			
		||||
		options.Authority = oidc.Authority;
 | 
			
		||||
 | 
			
		||||
		options.ClientId = oidc.ClientId;
 | 
			
		||||
		options.ClientSecret = oidc.ClientSecret;
 | 
			
		||||
		options.ResponseType = OpenIdConnectResponseType.Code;
 | 
			
		||||
 | 
			
		||||
		options.MapInboundClaims = false;
 | 
			
		||||
		options.TokenValidationParameters.NameClaimType = "name";
 | 
			
		||||
		options.TokenValidationParameters.RoleClaimType = "role";
 | 
			
		||||
 | 
			
		||||
		options.CallbackPath = new PathString("/signin-oidc");
 | 
			
		||||
		options.SignedOutCallbackPath = new PathString("/signout-callback-oidc");
 | 
			
		||||
		options.RemoteSignOutPath = new PathString("/signout-oidc");
 | 
			
		||||
	});
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#endregion
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -114,7 +138,10 @@
 | 
			
		|||
	options.UseNpgsql(connectionString));
 | 
			
		||||
builder.Services.AddDatabaseDeveloperPageExceptionFilter();
 | 
			
		||||
 | 
			
		||||
builder.Services.AddIdentityCore<ApplicationUser>(options => options.SignIn.RequireConfirmedAccount = true)
 | 
			
		||||
builder.Services.AddIdentityCore<ApplicationUser>(options => {
 | 
			
		||||
		options.SignIn.RequireConfirmedAccount = true;
 | 
			
		||||
		options.ClaimsIdentity.UserIdClaimType = "Id";
 | 
			
		||||
	})
 | 
			
		||||
	.AddRoles<IdentityRole>()
 | 
			
		||||
	.AddEntityFrameworkStores<ApplicationDbContext>()
 | 
			
		||||
	.AddSignInManager()
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -110,4 +110,7 @@
 | 
			
		|||
  <data name="Success" xml:space="preserve">
 | 
			
		||||
    <value>Vielen Dank fürs Bestätigen ihrer E-Mail Adresse.</value>
 | 
			
		||||
  </data>
 | 
			
		||||
    <data name="Login_Label" xml:space="preserve">
 | 
			
		||||
        <value>Anmelden</value>
 | 
			
		||||
    </data>
 | 
			
		||||
</root>
 | 
			
		||||
| 
						 | 
				
			
			@ -110,4 +110,7 @@
 | 
			
		|||
  <data name="Success" xml:space="preserve">
 | 
			
		||||
    <value>Thank you for confirming your email.</value>
 | 
			
		||||
  </data>
 | 
			
		||||
    <data name="Login_Label" xml:space="preserve">
 | 
			
		||||
        <value>Log In</value>
 | 
			
		||||
    </data>
 | 
			
		||||
</root>
 | 
			
		||||
							
								
								
									
										117
									
								
								Wave/Resources/Components/Account/Pages/ExternalLogin.de-DE.resx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										117
									
								
								Wave/Resources/Components/Account/Pages/ExternalLogin.de-DE.resx
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,117 @@
 | 
			
		|||
<?xml version="1.0" encoding="utf-8"?>
 | 
			
		||||
<root>
 | 
			
		||||
	<!-- 
 | 
			
		||||
		Microsoft ResX Schema
 | 
			
		||||
 | 
			
		||||
		Version 1.3
 | 
			
		||||
 | 
			
		||||
		The primary goals of this format is to allow a simple XML format 
 | 
			
		||||
		that is mostly human readable. The generation and parsing of the 
 | 
			
		||||
		various data types are done through the TypeConverter classes 
 | 
			
		||||
		associated with the data types.
 | 
			
		||||
 | 
			
		||||
		Example:
 | 
			
		||||
 | 
			
		||||
		... ado.net/XML headers & schema ...
 | 
			
		||||
		<resheader name="resmimetype">text/microsoft-resx</resheader>
 | 
			
		||||
		<resheader name="version">1.3</resheader>
 | 
			
		||||
		<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
 | 
			
		||||
		<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
 | 
			
		||||
		<data name="Name1">this is my long string</data>
 | 
			
		||||
		<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
 | 
			
		||||
		<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
 | 
			
		||||
			[base64 mime encoded serialized .NET Framework object]
 | 
			
		||||
		</data>
 | 
			
		||||
		<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
 | 
			
		||||
			[base64 mime encoded string representing a byte array form of the .NET Framework object]
 | 
			
		||||
		</data>
 | 
			
		||||
 | 
			
		||||
		There are any number of "resheader" rows that contain simple 
 | 
			
		||||
		name/value pairs.
 | 
			
		||||
 | 
			
		||||
		Each data row contains a name, and value. The row also contains a 
 | 
			
		||||
		type or mimetype. Type corresponds to a .NET class that support 
 | 
			
		||||
		text/value conversion through the TypeConverter architecture. 
 | 
			
		||||
		Classes that don't support this are serialized and stored with the 
 | 
			
		||||
		mimetype set.
 | 
			
		||||
 | 
			
		||||
		The mimetype is used for serialized objects, and tells the 
 | 
			
		||||
		ResXResourceReader how to depersist the object. This is currently not 
 | 
			
		||||
		extensible. For a given mimetype the value must be set accordingly:
 | 
			
		||||
 | 
			
		||||
		Note - application/x-microsoft.net.object.binary.base64 is the format 
 | 
			
		||||
		that the ResXResourceWriter will generate, however the reader can 
 | 
			
		||||
		read any of the formats listed below.
 | 
			
		||||
 | 
			
		||||
		mimetype: application/x-microsoft.net.object.binary.base64
 | 
			
		||||
		value   : The object must be serialized with 
 | 
			
		||||
			: System.Serialization.Formatters.Binary.BinaryFormatter
 | 
			
		||||
			: and then encoded with base64 encoding.
 | 
			
		||||
 | 
			
		||||
		mimetype: application/x-microsoft.net.object.soap.base64
 | 
			
		||||
		value   : The object must be serialized with 
 | 
			
		||||
			: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
 | 
			
		||||
			: and then encoded with base64 encoding.
 | 
			
		||||
 | 
			
		||||
		mimetype: application/x-microsoft.net.object.bytearray.base64
 | 
			
		||||
		value   : The object must be serialized into a byte array 
 | 
			
		||||
			: using a System.ComponentModel.TypeConverter
 | 
			
		||||
			: and then encoded with base64 encoding.
 | 
			
		||||
	-->
 | 
			
		||||
	
 | 
			
		||||
	<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
 | 
			
		||||
		<xsd:element name="root" msdata:IsDataSet="true">
 | 
			
		||||
			<xsd:complexType>
 | 
			
		||||
				<xsd:choice maxOccurs="unbounded">
 | 
			
		||||
					<xsd:element name="data">
 | 
			
		||||
						<xsd:complexType>
 | 
			
		||||
							<xsd:sequence>
 | 
			
		||||
								<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
 | 
			
		||||
								<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
 | 
			
		||||
							</xsd:sequence>
 | 
			
		||||
							<xsd:attribute name="name" type="xsd:string" msdata:Ordinal="1" />
 | 
			
		||||
							<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
 | 
			
		||||
							<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
 | 
			
		||||
						</xsd:complexType>
 | 
			
		||||
					</xsd:element>
 | 
			
		||||
					<xsd:element name="resheader">
 | 
			
		||||
						<xsd:complexType>
 | 
			
		||||
							<xsd:sequence>
 | 
			
		||||
								<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
 | 
			
		||||
							</xsd:sequence>
 | 
			
		||||
							<xsd:attribute name="name" type="xsd:string" use="required" />
 | 
			
		||||
						</xsd:complexType>
 | 
			
		||||
					</xsd:element>
 | 
			
		||||
				</xsd:choice>
 | 
			
		||||
			</xsd:complexType>
 | 
			
		||||
		</xsd:element>
 | 
			
		||||
	</xsd:schema>
 | 
			
		||||
	<resheader name="resmimetype">
 | 
			
		||||
		<value>text/microsoft-resx</value>
 | 
			
		||||
	</resheader>
 | 
			
		||||
	<resheader name="version">
 | 
			
		||||
		<value>1.3</value>
 | 
			
		||||
	</resheader>
 | 
			
		||||
	<resheader name="reader">
 | 
			
		||||
		<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
 | 
			
		||||
	</resheader>
 | 
			
		||||
	<resheader name="writer">
 | 
			
		||||
		<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
 | 
			
		||||
	</resheader>
 | 
			
		||||
    <data name="Message" xml:space="preserve">
 | 
			
		||||
        <value>Sie haben sich erfolgreich mit {0} angemeldet.
 | 
			
		||||
Bitte geben Sie nun eine E-Mail Addresse für diese Webseite ein.</value>
 | 
			
		||||
    </data>
 | 
			
		||||
    <data name="Email_Label" xml:space="preserve">
 | 
			
		||||
        <value>E-Mail</value>
 | 
			
		||||
    </data>
 | 
			
		||||
    <data name="Email_Placeholder" xml:space="preserve">
 | 
			
		||||
        <value>name@example.de</value>
 | 
			
		||||
    </data>
 | 
			
		||||
    <data name="Submit" xml:space="preserve">
 | 
			
		||||
        <value>Registrieren</value>
 | 
			
		||||
    </data>
 | 
			
		||||
    <data name="Title" xml:space="preserve">
 | 
			
		||||
        <value>Benutzerkonto Erstellen</value>
 | 
			
		||||
    </data>
 | 
			
		||||
</root>
 | 
			
		||||
							
								
								
									
										101
									
								
								Wave/Resources/Components/Account/Pages/ExternalLogin.en-GB.resx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										101
									
								
								Wave/Resources/Components/Account/Pages/ExternalLogin.en-GB.resx
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,101 @@
 | 
			
		|||
<?xml version="1.0" encoding="utf-8"?>
 | 
			
		||||
<root>
 | 
			
		||||
	<!-- 
 | 
			
		||||
		Microsoft ResX Schema
 | 
			
		||||
 | 
			
		||||
		Version 1.3
 | 
			
		||||
 | 
			
		||||
		The primary goals of this format is to allow a simple XML format 
 | 
			
		||||
		that is mostly human readable. The generation and parsing of the 
 | 
			
		||||
		various data types are done through the TypeConverter classes 
 | 
			
		||||
		associated with the data types.
 | 
			
		||||
 | 
			
		||||
		Example:
 | 
			
		||||
 | 
			
		||||
		... ado.net/XML headers & schema ...
 | 
			
		||||
		<resheader name="resmimetype">text/microsoft-resx</resheader>
 | 
			
		||||
		<resheader name="version">1.3</resheader>
 | 
			
		||||
		<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
 | 
			
		||||
		<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
 | 
			
		||||
		<data name="Name1">this is my long string</data>
 | 
			
		||||
		<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
 | 
			
		||||
		<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
 | 
			
		||||
			[base64 mime encoded serialized .NET Framework object]
 | 
			
		||||
		</data>
 | 
			
		||||
		<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
 | 
			
		||||
			[base64 mime encoded string representing a byte array form of the .NET Framework object]
 | 
			
		||||
		</data>
 | 
			
		||||
 | 
			
		||||
		There are any number of "resheader" rows that contain simple 
 | 
			
		||||
		name/value pairs.
 | 
			
		||||
 | 
			
		||||
		Each data row contains a name, and value. The row also contains a 
 | 
			
		||||
		type or mimetype. Type corresponds to a .NET class that support 
 | 
			
		||||
		text/value conversion through the TypeConverter architecture. 
 | 
			
		||||
		Classes that don't support this are serialized and stored with the 
 | 
			
		||||
		mimetype set.
 | 
			
		||||
 | 
			
		||||
		The mimetype is used for serialized objects, and tells the 
 | 
			
		||||
		ResXResourceReader how to depersist the object. This is currently not 
 | 
			
		||||
		extensible. For a given mimetype the value must be set accordingly:
 | 
			
		||||
 | 
			
		||||
		Note - application/x-microsoft.net.object.binary.base64 is the format 
 | 
			
		||||
		that the ResXResourceWriter will generate, however the reader can 
 | 
			
		||||
		read any of the formats listed below.
 | 
			
		||||
 | 
			
		||||
		mimetype: application/x-microsoft.net.object.binary.base64
 | 
			
		||||
		value   : The object must be serialized with 
 | 
			
		||||
			: System.Serialization.Formatters.Binary.BinaryFormatter
 | 
			
		||||
			: and then encoded with base64 encoding.
 | 
			
		||||
 | 
			
		||||
		mimetype: application/x-microsoft.net.object.soap.base64
 | 
			
		||||
		value   : The object must be serialized with 
 | 
			
		||||
			: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
 | 
			
		||||
			: and then encoded with base64 encoding.
 | 
			
		||||
 | 
			
		||||
		mimetype: application/x-microsoft.net.object.bytearray.base64
 | 
			
		||||
		value   : The object must be serialized into a byte array 
 | 
			
		||||
			: using a System.ComponentModel.TypeConverter
 | 
			
		||||
			: and then encoded with base64 encoding.
 | 
			
		||||
	-->
 | 
			
		||||
	
 | 
			
		||||
	<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
 | 
			
		||||
		<xsd:element name="root" msdata:IsDataSet="true">
 | 
			
		||||
			<xsd:complexType>
 | 
			
		||||
				<xsd:choice maxOccurs="unbounded">
 | 
			
		||||
					<xsd:element name="data">
 | 
			
		||||
						<xsd:complexType>
 | 
			
		||||
							<xsd:sequence>
 | 
			
		||||
								<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
 | 
			
		||||
								<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
 | 
			
		||||
							</xsd:sequence>
 | 
			
		||||
							<xsd:attribute name="name" type="xsd:string" msdata:Ordinal="1" />
 | 
			
		||||
							<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
 | 
			
		||||
							<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
 | 
			
		||||
						</xsd:complexType>
 | 
			
		||||
					</xsd:element>
 | 
			
		||||
					<xsd:element name="resheader">
 | 
			
		||||
						<xsd:complexType>
 | 
			
		||||
							<xsd:sequence>
 | 
			
		||||
								<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
 | 
			
		||||
							</xsd:sequence>
 | 
			
		||||
							<xsd:attribute name="name" type="xsd:string" use="required" />
 | 
			
		||||
						</xsd:complexType>
 | 
			
		||||
					</xsd:element>
 | 
			
		||||
				</xsd:choice>
 | 
			
		||||
			</xsd:complexType>
 | 
			
		||||
		</xsd:element>
 | 
			
		||||
	</xsd:schema>
 | 
			
		||||
	<resheader name="resmimetype">
 | 
			
		||||
		<value>text/microsoft-resx</value>
 | 
			
		||||
	</resheader>
 | 
			
		||||
	<resheader name="version">
 | 
			
		||||
		<value>1.3</value>
 | 
			
		||||
	</resheader>
 | 
			
		||||
	<resheader name="reader">
 | 
			
		||||
		<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
 | 
			
		||||
	</resheader>
 | 
			
		||||
	<resheader name="writer">
 | 
			
		||||
		<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
 | 
			
		||||
	</resheader>
 | 
			
		||||
</root>
 | 
			
		||||
							
								
								
									
										117
									
								
								Wave/Resources/Components/Account/Pages/ExternalLogin.resx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										117
									
								
								Wave/Resources/Components/Account/Pages/ExternalLogin.resx
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,117 @@
 | 
			
		|||
<?xml version="1.0" encoding="utf-8"?>
 | 
			
		||||
<root>
 | 
			
		||||
	<!-- 
 | 
			
		||||
		Microsoft ResX Schema
 | 
			
		||||
 | 
			
		||||
		Version 1.3
 | 
			
		||||
 | 
			
		||||
		The primary goals of this format is to allow a simple XML format 
 | 
			
		||||
		that is mostly human readable. The generation and parsing of the 
 | 
			
		||||
		various data types are done through the TypeConverter classes 
 | 
			
		||||
		associated with the data types.
 | 
			
		||||
 | 
			
		||||
		Example:
 | 
			
		||||
 | 
			
		||||
		... ado.net/XML headers & schema ...
 | 
			
		||||
		<resheader name="resmimetype">text/microsoft-resx</resheader>
 | 
			
		||||
		<resheader name="version">1.3</resheader>
 | 
			
		||||
		<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
 | 
			
		||||
		<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
 | 
			
		||||
		<data name="Name1">this is my long string</data>
 | 
			
		||||
		<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
 | 
			
		||||
		<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
 | 
			
		||||
			[base64 mime encoded serialized .NET Framework object]
 | 
			
		||||
		</data>
 | 
			
		||||
		<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
 | 
			
		||||
			[base64 mime encoded string representing a byte array form of the .NET Framework object]
 | 
			
		||||
		</data>
 | 
			
		||||
 | 
			
		||||
		There are any number of "resheader" rows that contain simple 
 | 
			
		||||
		name/value pairs.
 | 
			
		||||
 | 
			
		||||
		Each data row contains a name, and value. The row also contains a 
 | 
			
		||||
		type or mimetype. Type corresponds to a .NET class that support 
 | 
			
		||||
		text/value conversion through the TypeConverter architecture. 
 | 
			
		||||
		Classes that don't support this are serialized and stored with the 
 | 
			
		||||
		mimetype set.
 | 
			
		||||
 | 
			
		||||
		The mimetype is used for serialized objects, and tells the 
 | 
			
		||||
		ResXResourceReader how to depersist the object. This is currently not 
 | 
			
		||||
		extensible. For a given mimetype the value must be set accordingly:
 | 
			
		||||
 | 
			
		||||
		Note - application/x-microsoft.net.object.binary.base64 is the format 
 | 
			
		||||
		that the ResXResourceWriter will generate, however the reader can 
 | 
			
		||||
		read any of the formats listed below.
 | 
			
		||||
 | 
			
		||||
		mimetype: application/x-microsoft.net.object.binary.base64
 | 
			
		||||
		value   : The object must be serialized with 
 | 
			
		||||
			: System.Serialization.Formatters.Binary.BinaryFormatter
 | 
			
		||||
			: and then encoded with base64 encoding.
 | 
			
		||||
 | 
			
		||||
		mimetype: application/x-microsoft.net.object.soap.base64
 | 
			
		||||
		value   : The object must be serialized with 
 | 
			
		||||
			: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
 | 
			
		||||
			: and then encoded with base64 encoding.
 | 
			
		||||
 | 
			
		||||
		mimetype: application/x-microsoft.net.object.bytearray.base64
 | 
			
		||||
		value   : The object must be serialized into a byte array 
 | 
			
		||||
			: using a System.ComponentModel.TypeConverter
 | 
			
		||||
			: and then encoded with base64 encoding.
 | 
			
		||||
	-->
 | 
			
		||||
	
 | 
			
		||||
	<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
 | 
			
		||||
		<xsd:element name="root" msdata:IsDataSet="true">
 | 
			
		||||
			<xsd:complexType>
 | 
			
		||||
				<xsd:choice maxOccurs="unbounded">
 | 
			
		||||
					<xsd:element name="data">
 | 
			
		||||
						<xsd:complexType>
 | 
			
		||||
							<xsd:sequence>
 | 
			
		||||
								<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
 | 
			
		||||
								<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
 | 
			
		||||
							</xsd:sequence>
 | 
			
		||||
							<xsd:attribute name="name" type="xsd:string" msdata:Ordinal="1" />
 | 
			
		||||
							<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
 | 
			
		||||
							<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
 | 
			
		||||
						</xsd:complexType>
 | 
			
		||||
					</xsd:element>
 | 
			
		||||
					<xsd:element name="resheader">
 | 
			
		||||
						<xsd:complexType>
 | 
			
		||||
							<xsd:sequence>
 | 
			
		||||
								<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
 | 
			
		||||
							</xsd:sequence>
 | 
			
		||||
							<xsd:attribute name="name" type="xsd:string" use="required" />
 | 
			
		||||
						</xsd:complexType>
 | 
			
		||||
					</xsd:element>
 | 
			
		||||
				</xsd:choice>
 | 
			
		||||
			</xsd:complexType>
 | 
			
		||||
		</xsd:element>
 | 
			
		||||
	</xsd:schema>
 | 
			
		||||
	<resheader name="resmimetype">
 | 
			
		||||
		<value>text/microsoft-resx</value>
 | 
			
		||||
	</resheader>
 | 
			
		||||
	<resheader name="version">
 | 
			
		||||
		<value>1.3</value>
 | 
			
		||||
	</resheader>
 | 
			
		||||
	<resheader name="reader">
 | 
			
		||||
		<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
 | 
			
		||||
	</resheader>
 | 
			
		||||
	<resheader name="writer">
 | 
			
		||||
		<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
 | 
			
		||||
	</resheader>
 | 
			
		||||
    <data name="Title" xml:space="preserve">
 | 
			
		||||
        <value>Create Account</value>
 | 
			
		||||
    </data>
 | 
			
		||||
    <data name="Message" xml:space="preserve">
 | 
			
		||||
        <value>You've successfully authenticated with {0}.
 | 
			
		||||
Please enter an email address for this site below.</value>
 | 
			
		||||
    </data>
 | 
			
		||||
    <data name="Email_Label" xml:space="preserve">
 | 
			
		||||
        <value>Email</value>
 | 
			
		||||
    </data>
 | 
			
		||||
    <data name="Email_Placeholder" xml:space="preserve">
 | 
			
		||||
        <value>name@example.com</value>
 | 
			
		||||
    </data>
 | 
			
		||||
    <data name="Submit" xml:space="preserve">
 | 
			
		||||
        <value>Register</value>
 | 
			
		||||
    </data>
 | 
			
		||||
</root>
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,122 @@
 | 
			
		|||
<?xml version="1.0" encoding="utf-8"?>
 | 
			
		||||
<root>
 | 
			
		||||
	<!-- 
 | 
			
		||||
		Microsoft ResX Schema
 | 
			
		||||
 | 
			
		||||
		Version 1.3
 | 
			
		||||
 | 
			
		||||
		The primary goals of this format is to allow a simple XML format 
 | 
			
		||||
		that is mostly human readable. The generation and parsing of the 
 | 
			
		||||
		various data types are done through the TypeConverter classes 
 | 
			
		||||
		associated with the data types.
 | 
			
		||||
 | 
			
		||||
		Example:
 | 
			
		||||
 | 
			
		||||
		... ado.net/XML headers & schema ...
 | 
			
		||||
		<resheader name="resmimetype">text/microsoft-resx</resheader>
 | 
			
		||||
		<resheader name="version">1.3</resheader>
 | 
			
		||||
		<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
 | 
			
		||||
		<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
 | 
			
		||||
		<data name="Name1">this is my long string</data>
 | 
			
		||||
		<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
 | 
			
		||||
		<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
 | 
			
		||||
			[base64 mime encoded serialized .NET Framework object]
 | 
			
		||||
		</data>
 | 
			
		||||
		<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
 | 
			
		||||
			[base64 mime encoded string representing a byte array form of the .NET Framework object]
 | 
			
		||||
		</data>
 | 
			
		||||
 | 
			
		||||
		There are any number of "resheader" rows that contain simple 
 | 
			
		||||
		name/value pairs.
 | 
			
		||||
 | 
			
		||||
		Each data row contains a name, and value. The row also contains a 
 | 
			
		||||
		type or mimetype. Type corresponds to a .NET class that support 
 | 
			
		||||
		text/value conversion through the TypeConverter architecture. 
 | 
			
		||||
		Classes that don't support this are serialized and stored with the 
 | 
			
		||||
		mimetype set.
 | 
			
		||||
 | 
			
		||||
		The mimetype is used for serialized objects, and tells the 
 | 
			
		||||
		ResXResourceReader how to depersist the object. This is currently not 
 | 
			
		||||
		extensible. For a given mimetype the value must be set accordingly:
 | 
			
		||||
 | 
			
		||||
		Note - application/x-microsoft.net.object.binary.base64 is the format 
 | 
			
		||||
		that the ResXResourceWriter will generate, however the reader can 
 | 
			
		||||
		read any of the formats listed below.
 | 
			
		||||
 | 
			
		||||
		mimetype: application/x-microsoft.net.object.binary.base64
 | 
			
		||||
		value   : The object must be serialized with 
 | 
			
		||||
			: System.Serialization.Formatters.Binary.BinaryFormatter
 | 
			
		||||
			: and then encoded with base64 encoding.
 | 
			
		||||
 | 
			
		||||
		mimetype: application/x-microsoft.net.object.soap.base64
 | 
			
		||||
		value   : The object must be serialized with 
 | 
			
		||||
			: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
 | 
			
		||||
			: and then encoded with base64 encoding.
 | 
			
		||||
 | 
			
		||||
		mimetype: application/x-microsoft.net.object.bytearray.base64
 | 
			
		||||
		value   : The object must be serialized into a byte array 
 | 
			
		||||
			: using a System.ComponentModel.TypeConverter
 | 
			
		||||
			: and then encoded with base64 encoding.
 | 
			
		||||
	-->
 | 
			
		||||
	
 | 
			
		||||
	<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
 | 
			
		||||
		<xsd:element name="root" msdata:IsDataSet="true">
 | 
			
		||||
			<xsd:complexType>
 | 
			
		||||
				<xsd:choice maxOccurs="unbounded">
 | 
			
		||||
					<xsd:element name="data">
 | 
			
		||||
						<xsd:complexType>
 | 
			
		||||
							<xsd:sequence>
 | 
			
		||||
								<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
 | 
			
		||||
								<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
 | 
			
		||||
							</xsd:sequence>
 | 
			
		||||
							<xsd:attribute name="name" type="xsd:string" msdata:Ordinal="1" />
 | 
			
		||||
							<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
 | 
			
		||||
							<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
 | 
			
		||||
						</xsd:complexType>
 | 
			
		||||
					</xsd:element>
 | 
			
		||||
					<xsd:element name="resheader">
 | 
			
		||||
						<xsd:complexType>
 | 
			
		||||
							<xsd:sequence>
 | 
			
		||||
								<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
 | 
			
		||||
							</xsd:sequence>
 | 
			
		||||
							<xsd:attribute name="name" type="xsd:string" use="required" />
 | 
			
		||||
						</xsd:complexType>
 | 
			
		||||
					</xsd:element>
 | 
			
		||||
				</xsd:choice>
 | 
			
		||||
			</xsd:complexType>
 | 
			
		||||
		</xsd:element>
 | 
			
		||||
	</xsd:schema>
 | 
			
		||||
	<resheader name="resmimetype">
 | 
			
		||||
		<value>text/microsoft-resx</value>
 | 
			
		||||
	</resheader>
 | 
			
		||||
	<resheader name="version">
 | 
			
		||||
		<value>1.3</value>
 | 
			
		||||
	</resheader>
 | 
			
		||||
	<resheader name="reader">
 | 
			
		||||
		<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
 | 
			
		||||
	</resheader>
 | 
			
		||||
	<resheader name="writer">
 | 
			
		||||
		<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
 | 
			
		||||
	</resheader>
 | 
			
		||||
    <data name="Title" xml:space="preserve">
 | 
			
		||||
        <value>Passwort Erstellen</value>
 | 
			
		||||
    </data>
 | 
			
		||||
    <data name="Message" xml:space="preserve">
 | 
			
		||||
        <value>Sie haben momentan kein lokales Benutzerkonto/Passwort für diese Seite. Fügen Sie ein lokales Konto hinzu um sich ohne externen Dienst anmelden zu können.</value>
 | 
			
		||||
    </data>
 | 
			
		||||
    <data name="ConfirmPassword_Label" xml:space="preserve">
 | 
			
		||||
        <value>Neues Passwort wiederholen</value>
 | 
			
		||||
    </data>
 | 
			
		||||
    <data name="ConfirmPassword_Placeholder" xml:space="preserve">
 | 
			
		||||
        <value>Bestätigen Sie ihr neues Passwort</value>
 | 
			
		||||
    </data>
 | 
			
		||||
    <data name="NewPassword_Label" xml:space="preserve">
 | 
			
		||||
        <value>Neues Passwort</value>
 | 
			
		||||
    </data>
 | 
			
		||||
    <data name="NewPassword_Placeholder" xml:space="preserve">
 | 
			
		||||
        <value>Geben Sie ihr neues Passwort ein</value>
 | 
			
		||||
    </data>
 | 
			
		||||
    <data name="ChangePassword_Submit" xml:space="preserve">
 | 
			
		||||
        <value>Passwort Ändern</value>
 | 
			
		||||
    </data>
 | 
			
		||||
</root>
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,101 @@
 | 
			
		|||
<?xml version="1.0" encoding="utf-8"?>
 | 
			
		||||
<root>
 | 
			
		||||
	<!-- 
 | 
			
		||||
		Microsoft ResX Schema
 | 
			
		||||
 | 
			
		||||
		Version 1.3
 | 
			
		||||
 | 
			
		||||
		The primary goals of this format is to allow a simple XML format 
 | 
			
		||||
		that is mostly human readable. The generation and parsing of the 
 | 
			
		||||
		various data types are done through the TypeConverter classes 
 | 
			
		||||
		associated with the data types.
 | 
			
		||||
 | 
			
		||||
		Example:
 | 
			
		||||
 | 
			
		||||
		... ado.net/XML headers & schema ...
 | 
			
		||||
		<resheader name="resmimetype">text/microsoft-resx</resheader>
 | 
			
		||||
		<resheader name="version">1.3</resheader>
 | 
			
		||||
		<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
 | 
			
		||||
		<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
 | 
			
		||||
		<data name="Name1">this is my long string</data>
 | 
			
		||||
		<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
 | 
			
		||||
		<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
 | 
			
		||||
			[base64 mime encoded serialized .NET Framework object]
 | 
			
		||||
		</data>
 | 
			
		||||
		<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
 | 
			
		||||
			[base64 mime encoded string representing a byte array form of the .NET Framework object]
 | 
			
		||||
		</data>
 | 
			
		||||
 | 
			
		||||
		There are any number of "resheader" rows that contain simple 
 | 
			
		||||
		name/value pairs.
 | 
			
		||||
 | 
			
		||||
		Each data row contains a name, and value. The row also contains a 
 | 
			
		||||
		type or mimetype. Type corresponds to a .NET class that support 
 | 
			
		||||
		text/value conversion through the TypeConverter architecture. 
 | 
			
		||||
		Classes that don't support this are serialized and stored with the 
 | 
			
		||||
		mimetype set.
 | 
			
		||||
 | 
			
		||||
		The mimetype is used for serialized objects, and tells the 
 | 
			
		||||
		ResXResourceReader how to depersist the object. This is currently not 
 | 
			
		||||
		extensible. For a given mimetype the value must be set accordingly:
 | 
			
		||||
 | 
			
		||||
		Note - application/x-microsoft.net.object.binary.base64 is the format 
 | 
			
		||||
		that the ResXResourceWriter will generate, however the reader can 
 | 
			
		||||
		read any of the formats listed below.
 | 
			
		||||
 | 
			
		||||
		mimetype: application/x-microsoft.net.object.binary.base64
 | 
			
		||||
		value   : The object must be serialized with 
 | 
			
		||||
			: System.Serialization.Formatters.Binary.BinaryFormatter
 | 
			
		||||
			: and then encoded with base64 encoding.
 | 
			
		||||
 | 
			
		||||
		mimetype: application/x-microsoft.net.object.soap.base64
 | 
			
		||||
		value   : The object must be serialized with 
 | 
			
		||||
			: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
 | 
			
		||||
			: and then encoded with base64 encoding.
 | 
			
		||||
 | 
			
		||||
		mimetype: application/x-microsoft.net.object.bytearray.base64
 | 
			
		||||
		value   : The object must be serialized into a byte array 
 | 
			
		||||
			: using a System.ComponentModel.TypeConverter
 | 
			
		||||
			: and then encoded with base64 encoding.
 | 
			
		||||
	-->
 | 
			
		||||
	
 | 
			
		||||
	<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
 | 
			
		||||
		<xsd:element name="root" msdata:IsDataSet="true">
 | 
			
		||||
			<xsd:complexType>
 | 
			
		||||
				<xsd:choice maxOccurs="unbounded">
 | 
			
		||||
					<xsd:element name="data">
 | 
			
		||||
						<xsd:complexType>
 | 
			
		||||
							<xsd:sequence>
 | 
			
		||||
								<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
 | 
			
		||||
								<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
 | 
			
		||||
							</xsd:sequence>
 | 
			
		||||
							<xsd:attribute name="name" type="xsd:string" msdata:Ordinal="1" />
 | 
			
		||||
							<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
 | 
			
		||||
							<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
 | 
			
		||||
						</xsd:complexType>
 | 
			
		||||
					</xsd:element>
 | 
			
		||||
					<xsd:element name="resheader">
 | 
			
		||||
						<xsd:complexType>
 | 
			
		||||
							<xsd:sequence>
 | 
			
		||||
								<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
 | 
			
		||||
							</xsd:sequence>
 | 
			
		||||
							<xsd:attribute name="name" type="xsd:string" use="required" />
 | 
			
		||||
						</xsd:complexType>
 | 
			
		||||
					</xsd:element>
 | 
			
		||||
				</xsd:choice>
 | 
			
		||||
			</xsd:complexType>
 | 
			
		||||
		</xsd:element>
 | 
			
		||||
	</xsd:schema>
 | 
			
		||||
	<resheader name="resmimetype">
 | 
			
		||||
		<value>text/microsoft-resx</value>
 | 
			
		||||
	</resheader>
 | 
			
		||||
	<resheader name="version">
 | 
			
		||||
		<value>1.3</value>
 | 
			
		||||
	</resheader>
 | 
			
		||||
	<resheader name="reader">
 | 
			
		||||
		<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
 | 
			
		||||
	</resheader>
 | 
			
		||||
	<resheader name="writer">
 | 
			
		||||
		<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
 | 
			
		||||
	</resheader>
 | 
			
		||||
</root>
 | 
			
		||||
							
								
								
									
										122
									
								
								Wave/Resources/Components/Account/Pages/Manage/SetPassword.resx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										122
									
								
								Wave/Resources/Components/Account/Pages/Manage/SetPassword.resx
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,122 @@
 | 
			
		|||
<?xml version="1.0" encoding="utf-8"?>
 | 
			
		||||
<root>
 | 
			
		||||
	<!-- 
 | 
			
		||||
		Microsoft ResX Schema
 | 
			
		||||
 | 
			
		||||
		Version 1.3
 | 
			
		||||
 | 
			
		||||
		The primary goals of this format is to allow a simple XML format 
 | 
			
		||||
		that is mostly human readable. The generation and parsing of the 
 | 
			
		||||
		various data types are done through the TypeConverter classes 
 | 
			
		||||
		associated with the data types.
 | 
			
		||||
 | 
			
		||||
		Example:
 | 
			
		||||
 | 
			
		||||
		... ado.net/XML headers & schema ...
 | 
			
		||||
		<resheader name="resmimetype">text/microsoft-resx</resheader>
 | 
			
		||||
		<resheader name="version">1.3</resheader>
 | 
			
		||||
		<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
 | 
			
		||||
		<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
 | 
			
		||||
		<data name="Name1">this is my long string</data>
 | 
			
		||||
		<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
 | 
			
		||||
		<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
 | 
			
		||||
			[base64 mime encoded serialized .NET Framework object]
 | 
			
		||||
		</data>
 | 
			
		||||
		<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
 | 
			
		||||
			[base64 mime encoded string representing a byte array form of the .NET Framework object]
 | 
			
		||||
		</data>
 | 
			
		||||
 | 
			
		||||
		There are any number of "resheader" rows that contain simple 
 | 
			
		||||
		name/value pairs.
 | 
			
		||||
 | 
			
		||||
		Each data row contains a name, and value. The row also contains a 
 | 
			
		||||
		type or mimetype. Type corresponds to a .NET class that support 
 | 
			
		||||
		text/value conversion through the TypeConverter architecture. 
 | 
			
		||||
		Classes that don't support this are serialized and stored with the 
 | 
			
		||||
		mimetype set.
 | 
			
		||||
 | 
			
		||||
		The mimetype is used for serialized objects, and tells the 
 | 
			
		||||
		ResXResourceReader how to depersist the object. This is currently not 
 | 
			
		||||
		extensible. For a given mimetype the value must be set accordingly:
 | 
			
		||||
 | 
			
		||||
		Note - application/x-microsoft.net.object.binary.base64 is the format 
 | 
			
		||||
		that the ResXResourceWriter will generate, however the reader can 
 | 
			
		||||
		read any of the formats listed below.
 | 
			
		||||
 | 
			
		||||
		mimetype: application/x-microsoft.net.object.binary.base64
 | 
			
		||||
		value   : The object must be serialized with 
 | 
			
		||||
			: System.Serialization.Formatters.Binary.BinaryFormatter
 | 
			
		||||
			: and then encoded with base64 encoding.
 | 
			
		||||
 | 
			
		||||
		mimetype: application/x-microsoft.net.object.soap.base64
 | 
			
		||||
		value   : The object must be serialized with 
 | 
			
		||||
			: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
 | 
			
		||||
			: and then encoded with base64 encoding.
 | 
			
		||||
 | 
			
		||||
		mimetype: application/x-microsoft.net.object.bytearray.base64
 | 
			
		||||
		value   : The object must be serialized into a byte array 
 | 
			
		||||
			: using a System.ComponentModel.TypeConverter
 | 
			
		||||
			: and then encoded with base64 encoding.
 | 
			
		||||
	-->
 | 
			
		||||
	
 | 
			
		||||
	<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
 | 
			
		||||
		<xsd:element name="root" msdata:IsDataSet="true">
 | 
			
		||||
			<xsd:complexType>
 | 
			
		||||
				<xsd:choice maxOccurs="unbounded">
 | 
			
		||||
					<xsd:element name="data">
 | 
			
		||||
						<xsd:complexType>
 | 
			
		||||
							<xsd:sequence>
 | 
			
		||||
								<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
 | 
			
		||||
								<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
 | 
			
		||||
							</xsd:sequence>
 | 
			
		||||
							<xsd:attribute name="name" type="xsd:string" msdata:Ordinal="1" />
 | 
			
		||||
							<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
 | 
			
		||||
							<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
 | 
			
		||||
						</xsd:complexType>
 | 
			
		||||
					</xsd:element>
 | 
			
		||||
					<xsd:element name="resheader">
 | 
			
		||||
						<xsd:complexType>
 | 
			
		||||
							<xsd:sequence>
 | 
			
		||||
								<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
 | 
			
		||||
							</xsd:sequence>
 | 
			
		||||
							<xsd:attribute name="name" type="xsd:string" use="required" />
 | 
			
		||||
						</xsd:complexType>
 | 
			
		||||
					</xsd:element>
 | 
			
		||||
				</xsd:choice>
 | 
			
		||||
			</xsd:complexType>
 | 
			
		||||
		</xsd:element>
 | 
			
		||||
	</xsd:schema>
 | 
			
		||||
	<resheader name="resmimetype">
 | 
			
		||||
		<value>text/microsoft-resx</value>
 | 
			
		||||
	</resheader>
 | 
			
		||||
	<resheader name="version">
 | 
			
		||||
		<value>1.3</value>
 | 
			
		||||
	</resheader>
 | 
			
		||||
	<resheader name="reader">
 | 
			
		||||
		<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
 | 
			
		||||
	</resheader>
 | 
			
		||||
	<resheader name="writer">
 | 
			
		||||
		<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
 | 
			
		||||
	</resheader>
 | 
			
		||||
    <data name="Title" xml:space="preserve">
 | 
			
		||||
        <value>Create Password</value>
 | 
			
		||||
    </data>
 | 
			
		||||
    <data name="Message" xml:space="preserve">
 | 
			
		||||
        <value>You do not have a local username/password for this site. Add a local account, so you can log in without an external login.</value>
 | 
			
		||||
    </data>
 | 
			
		||||
    <data name="NewPassword_Label" xml:space="preserve">
 | 
			
		||||
        <value>New password</value>
 | 
			
		||||
    </data>
 | 
			
		||||
    <data name="NewPassword_Placeholder" xml:space="preserve">
 | 
			
		||||
        <value>Enter your new password</value>
 | 
			
		||||
    </data>
 | 
			
		||||
    <data name="ConfirmPassword_Label" xml:space="preserve">
 | 
			
		||||
        <value>Confirm new password</value>
 | 
			
		||||
    </data>
 | 
			
		||||
    <data name="ConfirmPassword_Placeholder" xml:space="preserve">
 | 
			
		||||
        <value>Confirm your new Password</value>
 | 
			
		||||
    </data>
 | 
			
		||||
    <data name="ChangePassword_Submit" xml:space="preserve">
 | 
			
		||||
        <value>Change Password</value>
 | 
			
		||||
    </data>
 | 
			
		||||
</root>
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,107 @@
 | 
			
		|||
<?xml version="1.0" encoding="utf-8"?>
 | 
			
		||||
<root>
 | 
			
		||||
	<!-- 
 | 
			
		||||
		Microsoft ResX Schema
 | 
			
		||||
 | 
			
		||||
		Version 1.3
 | 
			
		||||
 | 
			
		||||
		The primary goals of this format is to allow a simple XML format 
 | 
			
		||||
		that is mostly human readable. The generation and parsing of the 
 | 
			
		||||
		various data types are done through the TypeConverter classes 
 | 
			
		||||
		associated with the data types.
 | 
			
		||||
 | 
			
		||||
		Example:
 | 
			
		||||
 | 
			
		||||
		... ado.net/XML headers & schema ...
 | 
			
		||||
		<resheader name="resmimetype">text/microsoft-resx</resheader>
 | 
			
		||||
		<resheader name="version">1.3</resheader>
 | 
			
		||||
		<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
 | 
			
		||||
		<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
 | 
			
		||||
		<data name="Name1">this is my long string</data>
 | 
			
		||||
		<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
 | 
			
		||||
		<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
 | 
			
		||||
			[base64 mime encoded serialized .NET Framework object]
 | 
			
		||||
		</data>
 | 
			
		||||
		<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
 | 
			
		||||
			[base64 mime encoded string representing a byte array form of the .NET Framework object]
 | 
			
		||||
		</data>
 | 
			
		||||
 | 
			
		||||
		There are any number of "resheader" rows that contain simple 
 | 
			
		||||
		name/value pairs.
 | 
			
		||||
 | 
			
		||||
		Each data row contains a name, and value. The row also contains a 
 | 
			
		||||
		type or mimetype. Type corresponds to a .NET class that support 
 | 
			
		||||
		text/value conversion through the TypeConverter architecture. 
 | 
			
		||||
		Classes that don't support this are serialized and stored with the 
 | 
			
		||||
		mimetype set.
 | 
			
		||||
 | 
			
		||||
		The mimetype is used for serialized objects, and tells the 
 | 
			
		||||
		ResXResourceReader how to depersist the object. This is currently not 
 | 
			
		||||
		extensible. For a given mimetype the value must be set accordingly:
 | 
			
		||||
 | 
			
		||||
		Note - application/x-microsoft.net.object.binary.base64 is the format 
 | 
			
		||||
		that the ResXResourceWriter will generate, however the reader can 
 | 
			
		||||
		read any of the formats listed below.
 | 
			
		||||
 | 
			
		||||
		mimetype: application/x-microsoft.net.object.binary.base64
 | 
			
		||||
		value   : The object must be serialized with 
 | 
			
		||||
			: System.Serialization.Formatters.Binary.BinaryFormatter
 | 
			
		||||
			: and then encoded with base64 encoding.
 | 
			
		||||
 | 
			
		||||
		mimetype: application/x-microsoft.net.object.soap.base64
 | 
			
		||||
		value   : The object must be serialized with 
 | 
			
		||||
			: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
 | 
			
		||||
			: and then encoded with base64 encoding.
 | 
			
		||||
 | 
			
		||||
		mimetype: application/x-microsoft.net.object.bytearray.base64
 | 
			
		||||
		value   : The object must be serialized into a byte array 
 | 
			
		||||
			: using a System.ComponentModel.TypeConverter
 | 
			
		||||
			: and then encoded with base64 encoding.
 | 
			
		||||
	-->
 | 
			
		||||
	
 | 
			
		||||
	<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
 | 
			
		||||
		<xsd:element name="root" msdata:IsDataSet="true">
 | 
			
		||||
			<xsd:complexType>
 | 
			
		||||
				<xsd:choice maxOccurs="unbounded">
 | 
			
		||||
					<xsd:element name="data">
 | 
			
		||||
						<xsd:complexType>
 | 
			
		||||
							<xsd:sequence>
 | 
			
		||||
								<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
 | 
			
		||||
								<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
 | 
			
		||||
							</xsd:sequence>
 | 
			
		||||
							<xsd:attribute name="name" type="xsd:string" msdata:Ordinal="1" />
 | 
			
		||||
							<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
 | 
			
		||||
							<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
 | 
			
		||||
						</xsd:complexType>
 | 
			
		||||
					</xsd:element>
 | 
			
		||||
					<xsd:element name="resheader">
 | 
			
		||||
						<xsd:complexType>
 | 
			
		||||
							<xsd:sequence>
 | 
			
		||||
								<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
 | 
			
		||||
							</xsd:sequence>
 | 
			
		||||
							<xsd:attribute name="name" type="xsd:string" use="required" />
 | 
			
		||||
						</xsd:complexType>
 | 
			
		||||
					</xsd:element>
 | 
			
		||||
				</xsd:choice>
 | 
			
		||||
			</xsd:complexType>
 | 
			
		||||
		</xsd:element>
 | 
			
		||||
	</xsd:schema>
 | 
			
		||||
	<resheader name="resmimetype">
 | 
			
		||||
		<value>text/microsoft-resx</value>
 | 
			
		||||
	</resheader>
 | 
			
		||||
	<resheader name="version">
 | 
			
		||||
		<value>1.3</value>
 | 
			
		||||
	</resheader>
 | 
			
		||||
	<resheader name="reader">
 | 
			
		||||
		<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
 | 
			
		||||
	</resheader>
 | 
			
		||||
	<resheader name="writer">
 | 
			
		||||
		<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
 | 
			
		||||
	</resheader>
 | 
			
		||||
    <data name="Title" xml:space="preserve">
 | 
			
		||||
        <value>Registrierung Bestätigen</value>
 | 
			
		||||
    </data>
 | 
			
		||||
    <data name="Message" xml:space="preserve">
 | 
			
		||||
        <value>Bitte öffnen Sie Ihre E-Mails und bestätigen Sie ihr Benutzerkonto. Sie können sich nicht Anmelden bevor Ihr Konto bestätigt wurde.</value>
 | 
			
		||||
    </data>
 | 
			
		||||
</root>
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,101 @@
 | 
			
		|||
<?xml version="1.0" encoding="utf-8"?>
 | 
			
		||||
<root>
 | 
			
		||||
	<!-- 
 | 
			
		||||
		Microsoft ResX Schema
 | 
			
		||||
 | 
			
		||||
		Version 1.3
 | 
			
		||||
 | 
			
		||||
		The primary goals of this format is to allow a simple XML format 
 | 
			
		||||
		that is mostly human readable. The generation and parsing of the 
 | 
			
		||||
		various data types are done through the TypeConverter classes 
 | 
			
		||||
		associated with the data types.
 | 
			
		||||
 | 
			
		||||
		Example:
 | 
			
		||||
 | 
			
		||||
		... ado.net/XML headers & schema ...
 | 
			
		||||
		<resheader name="resmimetype">text/microsoft-resx</resheader>
 | 
			
		||||
		<resheader name="version">1.3</resheader>
 | 
			
		||||
		<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
 | 
			
		||||
		<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
 | 
			
		||||
		<data name="Name1">this is my long string</data>
 | 
			
		||||
		<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
 | 
			
		||||
		<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
 | 
			
		||||
			[base64 mime encoded serialized .NET Framework object]
 | 
			
		||||
		</data>
 | 
			
		||||
		<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
 | 
			
		||||
			[base64 mime encoded string representing a byte array form of the .NET Framework object]
 | 
			
		||||
		</data>
 | 
			
		||||
 | 
			
		||||
		There are any number of "resheader" rows that contain simple 
 | 
			
		||||
		name/value pairs.
 | 
			
		||||
 | 
			
		||||
		Each data row contains a name, and value. The row also contains a 
 | 
			
		||||
		type or mimetype. Type corresponds to a .NET class that support 
 | 
			
		||||
		text/value conversion through the TypeConverter architecture. 
 | 
			
		||||
		Classes that don't support this are serialized and stored with the 
 | 
			
		||||
		mimetype set.
 | 
			
		||||
 | 
			
		||||
		The mimetype is used for serialized objects, and tells the 
 | 
			
		||||
		ResXResourceReader how to depersist the object. This is currently not 
 | 
			
		||||
		extensible. For a given mimetype the value must be set accordingly:
 | 
			
		||||
 | 
			
		||||
		Note - application/x-microsoft.net.object.binary.base64 is the format 
 | 
			
		||||
		that the ResXResourceWriter will generate, however the reader can 
 | 
			
		||||
		read any of the formats listed below.
 | 
			
		||||
 | 
			
		||||
		mimetype: application/x-microsoft.net.object.binary.base64
 | 
			
		||||
		value   : The object must be serialized with 
 | 
			
		||||
			: System.Serialization.Formatters.Binary.BinaryFormatter
 | 
			
		||||
			: and then encoded with base64 encoding.
 | 
			
		||||
 | 
			
		||||
		mimetype: application/x-microsoft.net.object.soap.base64
 | 
			
		||||
		value   : The object must be serialized with 
 | 
			
		||||
			: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
 | 
			
		||||
			: and then encoded with base64 encoding.
 | 
			
		||||
 | 
			
		||||
		mimetype: application/x-microsoft.net.object.bytearray.base64
 | 
			
		||||
		value   : The object must be serialized into a byte array 
 | 
			
		||||
			: using a System.ComponentModel.TypeConverter
 | 
			
		||||
			: and then encoded with base64 encoding.
 | 
			
		||||
	-->
 | 
			
		||||
	
 | 
			
		||||
	<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
 | 
			
		||||
		<xsd:element name="root" msdata:IsDataSet="true">
 | 
			
		||||
			<xsd:complexType>
 | 
			
		||||
				<xsd:choice maxOccurs="unbounded">
 | 
			
		||||
					<xsd:element name="data">
 | 
			
		||||
						<xsd:complexType>
 | 
			
		||||
							<xsd:sequence>
 | 
			
		||||
								<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
 | 
			
		||||
								<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
 | 
			
		||||
							</xsd:sequence>
 | 
			
		||||
							<xsd:attribute name="name" type="xsd:string" msdata:Ordinal="1" />
 | 
			
		||||
							<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
 | 
			
		||||
							<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
 | 
			
		||||
						</xsd:complexType>
 | 
			
		||||
					</xsd:element>
 | 
			
		||||
					<xsd:element name="resheader">
 | 
			
		||||
						<xsd:complexType>
 | 
			
		||||
							<xsd:sequence>
 | 
			
		||||
								<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
 | 
			
		||||
							</xsd:sequence>
 | 
			
		||||
							<xsd:attribute name="name" type="xsd:string" use="required" />
 | 
			
		||||
						</xsd:complexType>
 | 
			
		||||
					</xsd:element>
 | 
			
		||||
				</xsd:choice>
 | 
			
		||||
			</xsd:complexType>
 | 
			
		||||
		</xsd:element>
 | 
			
		||||
	</xsd:schema>
 | 
			
		||||
	<resheader name="resmimetype">
 | 
			
		||||
		<value>text/microsoft-resx</value>
 | 
			
		||||
	</resheader>
 | 
			
		||||
	<resheader name="version">
 | 
			
		||||
		<value>1.3</value>
 | 
			
		||||
	</resheader>
 | 
			
		||||
	<resheader name="reader">
 | 
			
		||||
		<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
 | 
			
		||||
	</resheader>
 | 
			
		||||
	<resheader name="writer">
 | 
			
		||||
		<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
 | 
			
		||||
	</resheader>
 | 
			
		||||
</root>
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,107 @@
 | 
			
		|||
<?xml version="1.0" encoding="utf-8"?>
 | 
			
		||||
<root>
 | 
			
		||||
	<!-- 
 | 
			
		||||
		Microsoft ResX Schema
 | 
			
		||||
 | 
			
		||||
		Version 1.3
 | 
			
		||||
 | 
			
		||||
		The primary goals of this format is to allow a simple XML format 
 | 
			
		||||
		that is mostly human readable. The generation and parsing of the 
 | 
			
		||||
		various data types are done through the TypeConverter classes 
 | 
			
		||||
		associated with the data types.
 | 
			
		||||
 | 
			
		||||
		Example:
 | 
			
		||||
 | 
			
		||||
		... ado.net/XML headers & schema ...
 | 
			
		||||
		<resheader name="resmimetype">text/microsoft-resx</resheader>
 | 
			
		||||
		<resheader name="version">1.3</resheader>
 | 
			
		||||
		<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
 | 
			
		||||
		<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
 | 
			
		||||
		<data name="Name1">this is my long string</data>
 | 
			
		||||
		<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
 | 
			
		||||
		<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
 | 
			
		||||
			[base64 mime encoded serialized .NET Framework object]
 | 
			
		||||
		</data>
 | 
			
		||||
		<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
 | 
			
		||||
			[base64 mime encoded string representing a byte array form of the .NET Framework object]
 | 
			
		||||
		</data>
 | 
			
		||||
 | 
			
		||||
		There are any number of "resheader" rows that contain simple 
 | 
			
		||||
		name/value pairs.
 | 
			
		||||
 | 
			
		||||
		Each data row contains a name, and value. The row also contains a 
 | 
			
		||||
		type or mimetype. Type corresponds to a .NET class that support 
 | 
			
		||||
		text/value conversion through the TypeConverter architecture. 
 | 
			
		||||
		Classes that don't support this are serialized and stored with the 
 | 
			
		||||
		mimetype set.
 | 
			
		||||
 | 
			
		||||
		The mimetype is used for serialized objects, and tells the 
 | 
			
		||||
		ResXResourceReader how to depersist the object. This is currently not 
 | 
			
		||||
		extensible. For a given mimetype the value must be set accordingly:
 | 
			
		||||
 | 
			
		||||
		Note - application/x-microsoft.net.object.binary.base64 is the format 
 | 
			
		||||
		that the ResXResourceWriter will generate, however the reader can 
 | 
			
		||||
		read any of the formats listed below.
 | 
			
		||||
 | 
			
		||||
		mimetype: application/x-microsoft.net.object.binary.base64
 | 
			
		||||
		value   : The object must be serialized with 
 | 
			
		||||
			: System.Serialization.Formatters.Binary.BinaryFormatter
 | 
			
		||||
			: and then encoded with base64 encoding.
 | 
			
		||||
 | 
			
		||||
		mimetype: application/x-microsoft.net.object.soap.base64
 | 
			
		||||
		value   : The object must be serialized with 
 | 
			
		||||
			: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
 | 
			
		||||
			: and then encoded with base64 encoding.
 | 
			
		||||
 | 
			
		||||
		mimetype: application/x-microsoft.net.object.bytearray.base64
 | 
			
		||||
		value   : The object must be serialized into a byte array 
 | 
			
		||||
			: using a System.ComponentModel.TypeConverter
 | 
			
		||||
			: and then encoded with base64 encoding.
 | 
			
		||||
	-->
 | 
			
		||||
	
 | 
			
		||||
	<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
 | 
			
		||||
		<xsd:element name="root" msdata:IsDataSet="true">
 | 
			
		||||
			<xsd:complexType>
 | 
			
		||||
				<xsd:choice maxOccurs="unbounded">
 | 
			
		||||
					<xsd:element name="data">
 | 
			
		||||
						<xsd:complexType>
 | 
			
		||||
							<xsd:sequence>
 | 
			
		||||
								<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
 | 
			
		||||
								<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
 | 
			
		||||
							</xsd:sequence>
 | 
			
		||||
							<xsd:attribute name="name" type="xsd:string" msdata:Ordinal="1" />
 | 
			
		||||
							<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
 | 
			
		||||
							<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
 | 
			
		||||
						</xsd:complexType>
 | 
			
		||||
					</xsd:element>
 | 
			
		||||
					<xsd:element name="resheader">
 | 
			
		||||
						<xsd:complexType>
 | 
			
		||||
							<xsd:sequence>
 | 
			
		||||
								<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
 | 
			
		||||
							</xsd:sequence>
 | 
			
		||||
							<xsd:attribute name="name" type="xsd:string" use="required" />
 | 
			
		||||
						</xsd:complexType>
 | 
			
		||||
					</xsd:element>
 | 
			
		||||
				</xsd:choice>
 | 
			
		||||
			</xsd:complexType>
 | 
			
		||||
		</xsd:element>
 | 
			
		||||
	</xsd:schema>
 | 
			
		||||
	<resheader name="resmimetype">
 | 
			
		||||
		<value>text/microsoft-resx</value>
 | 
			
		||||
	</resheader>
 | 
			
		||||
	<resheader name="version">
 | 
			
		||||
		<value>1.3</value>
 | 
			
		||||
	</resheader>
 | 
			
		||||
	<resheader name="reader">
 | 
			
		||||
		<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
 | 
			
		||||
	</resheader>
 | 
			
		||||
	<resheader name="writer">
 | 
			
		||||
		<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
 | 
			
		||||
	</resheader>
 | 
			
		||||
    <data name="Title" xml:space="preserve">
 | 
			
		||||
        <value>Confirm Registration</value>
 | 
			
		||||
    </data>
 | 
			
		||||
    <data name="Message" xml:space="preserve">
 | 
			
		||||
        <value>Please check your email to confirm your account. You cannot log in before your account has been confirmed.</value>
 | 
			
		||||
    </data>
 | 
			
		||||
</root>
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,107 @@
 | 
			
		|||
<?xml version="1.0" encoding="utf-8"?>
 | 
			
		||||
<root>
 | 
			
		||||
	<!-- 
 | 
			
		||||
		Microsoft ResX Schema
 | 
			
		||||
 | 
			
		||||
		Version 1.3
 | 
			
		||||
 | 
			
		||||
		The primary goals of this format is to allow a simple XML format 
 | 
			
		||||
		that is mostly human readable. The generation and parsing of the 
 | 
			
		||||
		various data types are done through the TypeConverter classes 
 | 
			
		||||
		associated with the data types.
 | 
			
		||||
 | 
			
		||||
		Example:
 | 
			
		||||
 | 
			
		||||
		... ado.net/XML headers & schema ...
 | 
			
		||||
		<resheader name="resmimetype">text/microsoft-resx</resheader>
 | 
			
		||||
		<resheader name="version">1.3</resheader>
 | 
			
		||||
		<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
 | 
			
		||||
		<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
 | 
			
		||||
		<data name="Name1">this is my long string</data>
 | 
			
		||||
		<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
 | 
			
		||||
		<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
 | 
			
		||||
			[base64 mime encoded serialized .NET Framework object]
 | 
			
		||||
		</data>
 | 
			
		||||
		<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
 | 
			
		||||
			[base64 mime encoded string representing a byte array form of the .NET Framework object]
 | 
			
		||||
		</data>
 | 
			
		||||
 | 
			
		||||
		There are any number of "resheader" rows that contain simple 
 | 
			
		||||
		name/value pairs.
 | 
			
		||||
 | 
			
		||||
		Each data row contains a name, and value. The row also contains a 
 | 
			
		||||
		type or mimetype. Type corresponds to a .NET class that support 
 | 
			
		||||
		text/value conversion through the TypeConverter architecture. 
 | 
			
		||||
		Classes that don't support this are serialized and stored with the 
 | 
			
		||||
		mimetype set.
 | 
			
		||||
 | 
			
		||||
		The mimetype is used for serialized objects, and tells the 
 | 
			
		||||
		ResXResourceReader how to depersist the object. This is currently not 
 | 
			
		||||
		extensible. For a given mimetype the value must be set accordingly:
 | 
			
		||||
 | 
			
		||||
		Note - application/x-microsoft.net.object.binary.base64 is the format 
 | 
			
		||||
		that the ResXResourceWriter will generate, however the reader can 
 | 
			
		||||
		read any of the formats listed below.
 | 
			
		||||
 | 
			
		||||
		mimetype: application/x-microsoft.net.object.binary.base64
 | 
			
		||||
		value   : The object must be serialized with 
 | 
			
		||||
			: System.Serialization.Formatters.Binary.BinaryFormatter
 | 
			
		||||
			: and then encoded with base64 encoding.
 | 
			
		||||
 | 
			
		||||
		mimetype: application/x-microsoft.net.object.soap.base64
 | 
			
		||||
		value   : The object must be serialized with 
 | 
			
		||||
			: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
 | 
			
		||||
			: and then encoded with base64 encoding.
 | 
			
		||||
 | 
			
		||||
		mimetype: application/x-microsoft.net.object.bytearray.base64
 | 
			
		||||
		value   : The object must be serialized into a byte array 
 | 
			
		||||
			: using a System.ComponentModel.TypeConverter
 | 
			
		||||
			: and then encoded with base64 encoding.
 | 
			
		||||
	-->
 | 
			
		||||
	
 | 
			
		||||
	<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
 | 
			
		||||
		<xsd:element name="root" msdata:IsDataSet="true">
 | 
			
		||||
			<xsd:complexType>
 | 
			
		||||
				<xsd:choice maxOccurs="unbounded">
 | 
			
		||||
					<xsd:element name="data">
 | 
			
		||||
						<xsd:complexType>
 | 
			
		||||
							<xsd:sequence>
 | 
			
		||||
								<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
 | 
			
		||||
								<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
 | 
			
		||||
							</xsd:sequence>
 | 
			
		||||
							<xsd:attribute name="name" type="xsd:string" msdata:Ordinal="1" />
 | 
			
		||||
							<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
 | 
			
		||||
							<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
 | 
			
		||||
						</xsd:complexType>
 | 
			
		||||
					</xsd:element>
 | 
			
		||||
					<xsd:element name="resheader">
 | 
			
		||||
						<xsd:complexType>
 | 
			
		||||
							<xsd:sequence>
 | 
			
		||||
								<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
 | 
			
		||||
							</xsd:sequence>
 | 
			
		||||
							<xsd:attribute name="name" type="xsd:string" use="required" />
 | 
			
		||||
						</xsd:complexType>
 | 
			
		||||
					</xsd:element>
 | 
			
		||||
				</xsd:choice>
 | 
			
		||||
			</xsd:complexType>
 | 
			
		||||
		</xsd:element>
 | 
			
		||||
	</xsd:schema>
 | 
			
		||||
	<resheader name="resmimetype">
 | 
			
		||||
		<value>text/microsoft-resx</value>
 | 
			
		||||
	</resheader>
 | 
			
		||||
	<resheader name="version">
 | 
			
		||||
		<value>1.3</value>
 | 
			
		||||
	</resheader>
 | 
			
		||||
	<resheader name="reader">
 | 
			
		||||
		<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
 | 
			
		||||
	</resheader>
 | 
			
		||||
	<resheader name="writer">
 | 
			
		||||
		<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
 | 
			
		||||
	</resheader>
 | 
			
		||||
    <data name="Title" xml:space="preserve">
 | 
			
		||||
        <value>Anmelden über externen Dienst</value>
 | 
			
		||||
    </data>
 | 
			
		||||
    <data name="OpenId_Tooltip" xml:space="preserve">
 | 
			
		||||
        <value>Melden Sie sich über ihren OpenID Connect Dienst an</value>
 | 
			
		||||
    </data>
 | 
			
		||||
</root>
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,101 @@
 | 
			
		|||
<?xml version="1.0" encoding="utf-8"?>
 | 
			
		||||
<root>
 | 
			
		||||
	<!-- 
 | 
			
		||||
		Microsoft ResX Schema
 | 
			
		||||
 | 
			
		||||
		Version 1.3
 | 
			
		||||
 | 
			
		||||
		The primary goals of this format is to allow a simple XML format 
 | 
			
		||||
		that is mostly human readable. The generation and parsing of the 
 | 
			
		||||
		various data types are done through the TypeConverter classes 
 | 
			
		||||
		associated with the data types.
 | 
			
		||||
 | 
			
		||||
		Example:
 | 
			
		||||
 | 
			
		||||
		... ado.net/XML headers & schema ...
 | 
			
		||||
		<resheader name="resmimetype">text/microsoft-resx</resheader>
 | 
			
		||||
		<resheader name="version">1.3</resheader>
 | 
			
		||||
		<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
 | 
			
		||||
		<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
 | 
			
		||||
		<data name="Name1">this is my long string</data>
 | 
			
		||||
		<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
 | 
			
		||||
		<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
 | 
			
		||||
			[base64 mime encoded serialized .NET Framework object]
 | 
			
		||||
		</data>
 | 
			
		||||
		<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
 | 
			
		||||
			[base64 mime encoded string representing a byte array form of the .NET Framework object]
 | 
			
		||||
		</data>
 | 
			
		||||
 | 
			
		||||
		There are any number of "resheader" rows that contain simple 
 | 
			
		||||
		name/value pairs.
 | 
			
		||||
 | 
			
		||||
		Each data row contains a name, and value. The row also contains a 
 | 
			
		||||
		type or mimetype. Type corresponds to a .NET class that support 
 | 
			
		||||
		text/value conversion through the TypeConverter architecture. 
 | 
			
		||||
		Classes that don't support this are serialized and stored with the 
 | 
			
		||||
		mimetype set.
 | 
			
		||||
 | 
			
		||||
		The mimetype is used for serialized objects, and tells the 
 | 
			
		||||
		ResXResourceReader how to depersist the object. This is currently not 
 | 
			
		||||
		extensible. For a given mimetype the value must be set accordingly:
 | 
			
		||||
 | 
			
		||||
		Note - application/x-microsoft.net.object.binary.base64 is the format 
 | 
			
		||||
		that the ResXResourceWriter will generate, however the reader can 
 | 
			
		||||
		read any of the formats listed below.
 | 
			
		||||
 | 
			
		||||
		mimetype: application/x-microsoft.net.object.binary.base64
 | 
			
		||||
		value   : The object must be serialized with 
 | 
			
		||||
			: System.Serialization.Formatters.Binary.BinaryFormatter
 | 
			
		||||
			: and then encoded with base64 encoding.
 | 
			
		||||
 | 
			
		||||
		mimetype: application/x-microsoft.net.object.soap.base64
 | 
			
		||||
		value   : The object must be serialized with 
 | 
			
		||||
			: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
 | 
			
		||||
			: and then encoded with base64 encoding.
 | 
			
		||||
 | 
			
		||||
		mimetype: application/x-microsoft.net.object.bytearray.base64
 | 
			
		||||
		value   : The object must be serialized into a byte array 
 | 
			
		||||
			: using a System.ComponentModel.TypeConverter
 | 
			
		||||
			: and then encoded with base64 encoding.
 | 
			
		||||
	-->
 | 
			
		||||
	
 | 
			
		||||
	<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
 | 
			
		||||
		<xsd:element name="root" msdata:IsDataSet="true">
 | 
			
		||||
			<xsd:complexType>
 | 
			
		||||
				<xsd:choice maxOccurs="unbounded">
 | 
			
		||||
					<xsd:element name="data">
 | 
			
		||||
						<xsd:complexType>
 | 
			
		||||
							<xsd:sequence>
 | 
			
		||||
								<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
 | 
			
		||||
								<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
 | 
			
		||||
							</xsd:sequence>
 | 
			
		||||
							<xsd:attribute name="name" type="xsd:string" msdata:Ordinal="1" />
 | 
			
		||||
							<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
 | 
			
		||||
							<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
 | 
			
		||||
						</xsd:complexType>
 | 
			
		||||
					</xsd:element>
 | 
			
		||||
					<xsd:element name="resheader">
 | 
			
		||||
						<xsd:complexType>
 | 
			
		||||
							<xsd:sequence>
 | 
			
		||||
								<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
 | 
			
		||||
							</xsd:sequence>
 | 
			
		||||
							<xsd:attribute name="name" type="xsd:string" use="required" />
 | 
			
		||||
						</xsd:complexType>
 | 
			
		||||
					</xsd:element>
 | 
			
		||||
				</xsd:choice>
 | 
			
		||||
			</xsd:complexType>
 | 
			
		||||
		</xsd:element>
 | 
			
		||||
	</xsd:schema>
 | 
			
		||||
	<resheader name="resmimetype">
 | 
			
		||||
		<value>text/microsoft-resx</value>
 | 
			
		||||
	</resheader>
 | 
			
		||||
	<resheader name="version">
 | 
			
		||||
		<value>1.3</value>
 | 
			
		||||
	</resheader>
 | 
			
		||||
	<resheader name="reader">
 | 
			
		||||
		<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
 | 
			
		||||
	</resheader>
 | 
			
		||||
	<resheader name="writer">
 | 
			
		||||
		<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
 | 
			
		||||
	</resheader>
 | 
			
		||||
</root>
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,110 @@
 | 
			
		|||
<?xml version="1.0" encoding="utf-8"?>
 | 
			
		||||
<root>
 | 
			
		||||
	<!-- 
 | 
			
		||||
		Microsoft ResX Schema
 | 
			
		||||
 | 
			
		||||
		Version 1.3
 | 
			
		||||
 | 
			
		||||
		The primary goals of this format is to allow a simple XML format 
 | 
			
		||||
		that is mostly human readable. The generation and parsing of the 
 | 
			
		||||
		various data types are done through the TypeConverter classes 
 | 
			
		||||
		associated with the data types.
 | 
			
		||||
 | 
			
		||||
		Example:
 | 
			
		||||
 | 
			
		||||
		... ado.net/XML headers & schema ...
 | 
			
		||||
		<resheader name="resmimetype">text/microsoft-resx</resheader>
 | 
			
		||||
		<resheader name="version">1.3</resheader>
 | 
			
		||||
		<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
 | 
			
		||||
		<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
 | 
			
		||||
		<data name="Name1">this is my long string</data>
 | 
			
		||||
		<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
 | 
			
		||||
		<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
 | 
			
		||||
			[base64 mime encoded serialized .NET Framework object]
 | 
			
		||||
		</data>
 | 
			
		||||
		<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
 | 
			
		||||
			[base64 mime encoded string representing a byte array form of the .NET Framework object]
 | 
			
		||||
		</data>
 | 
			
		||||
 | 
			
		||||
		There are any number of "resheader" rows that contain simple 
 | 
			
		||||
		name/value pairs.
 | 
			
		||||
 | 
			
		||||
		Each data row contains a name, and value. The row also contains a 
 | 
			
		||||
		type or mimetype. Type corresponds to a .NET class that support 
 | 
			
		||||
		text/value conversion through the TypeConverter architecture. 
 | 
			
		||||
		Classes that don't support this are serialized and stored with the 
 | 
			
		||||
		mimetype set.
 | 
			
		||||
 | 
			
		||||
		The mimetype is used for serialized objects, and tells the 
 | 
			
		||||
		ResXResourceReader how to depersist the object. This is currently not 
 | 
			
		||||
		extensible. For a given mimetype the value must be set accordingly:
 | 
			
		||||
 | 
			
		||||
		Note - application/x-microsoft.net.object.binary.base64 is the format 
 | 
			
		||||
		that the ResXResourceWriter will generate, however the reader can 
 | 
			
		||||
		read any of the formats listed below.
 | 
			
		||||
 | 
			
		||||
		mimetype: application/x-microsoft.net.object.binary.base64
 | 
			
		||||
		value   : The object must be serialized with 
 | 
			
		||||
			: System.Serialization.Formatters.Binary.BinaryFormatter
 | 
			
		||||
			: and then encoded with base64 encoding.
 | 
			
		||||
 | 
			
		||||
		mimetype: application/x-microsoft.net.object.soap.base64
 | 
			
		||||
		value   : The object must be serialized with 
 | 
			
		||||
			: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
 | 
			
		||||
			: and then encoded with base64 encoding.
 | 
			
		||||
 | 
			
		||||
		mimetype: application/x-microsoft.net.object.bytearray.base64
 | 
			
		||||
		value   : The object must be serialized into a byte array 
 | 
			
		||||
			: using a System.ComponentModel.TypeConverter
 | 
			
		||||
			: and then encoded with base64 encoding.
 | 
			
		||||
	-->
 | 
			
		||||
	
 | 
			
		||||
	<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
 | 
			
		||||
		<xsd:element name="root" msdata:IsDataSet="true">
 | 
			
		||||
			<xsd:complexType>
 | 
			
		||||
				<xsd:choice maxOccurs="unbounded">
 | 
			
		||||
					<xsd:element name="data">
 | 
			
		||||
						<xsd:complexType>
 | 
			
		||||
							<xsd:sequence>
 | 
			
		||||
								<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
 | 
			
		||||
								<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
 | 
			
		||||
							</xsd:sequence>
 | 
			
		||||
							<xsd:attribute name="name" type="xsd:string" msdata:Ordinal="1" />
 | 
			
		||||
							<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
 | 
			
		||||
							<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
 | 
			
		||||
						</xsd:complexType>
 | 
			
		||||
					</xsd:element>
 | 
			
		||||
					<xsd:element name="resheader">
 | 
			
		||||
						<xsd:complexType>
 | 
			
		||||
							<xsd:sequence>
 | 
			
		||||
								<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
 | 
			
		||||
							</xsd:sequence>
 | 
			
		||||
							<xsd:attribute name="name" type="xsd:string" use="required" />
 | 
			
		||||
						</xsd:complexType>
 | 
			
		||||
					</xsd:element>
 | 
			
		||||
				</xsd:choice>
 | 
			
		||||
			</xsd:complexType>
 | 
			
		||||
		</xsd:element>
 | 
			
		||||
	</xsd:schema>
 | 
			
		||||
	<resheader name="resmimetype">
 | 
			
		||||
		<value>text/microsoft-resx</value>
 | 
			
		||||
	</resheader>
 | 
			
		||||
	<resheader name="version">
 | 
			
		||||
		<value>1.3</value>
 | 
			
		||||
	</resheader>
 | 
			
		||||
	<resheader name="reader">
 | 
			
		||||
		<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
 | 
			
		||||
	</resheader>
 | 
			
		||||
	<resheader name="writer">
 | 
			
		||||
		<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
 | 
			
		||||
	</resheader>
 | 
			
		||||
    <data name="Title" xml:space="preserve">
 | 
			
		||||
        <value>Log In using external service</value>
 | 
			
		||||
    </data>
 | 
			
		||||
    <data name="OpenId_Label" xml:space="preserve">
 | 
			
		||||
        <value>OpenID Connect</value>
 | 
			
		||||
    </data>
 | 
			
		||||
    <data name="OpenId_Tooltip" xml:space="preserve">
 | 
			
		||||
        <value>Log In using OpenID Connect provider</value>
 | 
			
		||||
    </data>
 | 
			
		||||
</root>
 | 
			
		||||
| 
						 | 
				
			
			@ -17,6 +17,7 @@
 | 
			
		|||
    <PackageReference Include="Magick.NET-Q8-AnyCPU" Version="13.5.0" />
 | 
			
		||||
    <PackageReference Include="MailKit" Version="4.3.0" />
 | 
			
		||||
    <PackageReference Include="Markdig" Version="0.34.0" />
 | 
			
		||||
    <PackageReference Include="Microsoft.AspNetCore.Authentication.OpenIdConnect" Version="8.0.2" />
 | 
			
		||||
    <PackageReference Include="Microsoft.AspNetCore.DataProtection.StackExchangeRedis" Version="8.0.1" />
 | 
			
		||||
    <PackageReference Include="Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore" Version="8.0.0" />
 | 
			
		||||
    <PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="8.0.0" />
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										2
									
								
								Wave/wwwroot/css/main.min.css
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								Wave/wwwroot/css/main.min.css
									
									
									
									
										vendored
									
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
		Loading…
	
		Reference in a new issue