Wave/Wave/Components/Pages/ArticleView.razor

92 lines
3.4 KiB
Plaintext

@page "/article/{id:guid}"
@using Microsoft.EntityFrameworkCore
@using Wave.Data
@using System.Security.Claims
@using Microsoft.AspNetCore.Identity
@using System.Diagnostics.CodeAnalysis
@inject IDbContextFactory<ApplicationDbContext> ContextFactory;
@inject RoleManager<IdentityRole> RoleManager
@inject IStringLocalizer<ArticleView> Localizer
<PageTitle>@(TitlePrefix + Article.Title)</PageTitle>
<ErrorBoundary>
<ChildContent>
<AuthorizeView Policy="ArticleEditOrReviewPermissions">
<Authorized>
<ArticleComponent Article="@GetArticleProtected(context.User)" />
</Authorized>
<NotAuthorized>
<ArticleComponent Article="@GetArticlePublic()" />
</NotAuthorized>
</AuthorizeView>
</ChildContent>
<ErrorContent>
<h1 class="text-3xl lg:text-5xl font-light mb-6">@Localizer["NotFound_Title"]</h1>
<p class="my-3">@Localizer["NotFound_Description"]</p>
@if (Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT")?.ToLower() == "development") {
<p>@context.Message</p>
}
</ErrorContent>
</ErrorBoundary>
@code {
[CascadingParameter(Name = "TitlePrefix")]
private string TitlePrefix { get; set; } = default!;
[Parameter]
public Guid Id { get; set; }
private Article Article { get; set; } = default!;
private Article GetArticlePublic() {
if (Article.Status >= ArticleStatus.Published && Article.PublishDate <= DateTimeOffset.UtcNow) {
return Article;
}
throw new ApplicationException("Article is not public.");
}
[SuppressMessage("ReSharper", "ConvertIfStatementToSwitchStatement")]
private Article GetArticleProtected(ClaimsPrincipal principal) {
// Admins always get access
if (principal.IsInRole("Admin")) {
return Article;
}
// You cannot access your own drafts
if (Article.Status is ArticleStatus.Draft) {
if (Article.Author.Id == principal.FindFirst("Id")!.Value) {
return Article;
}
throw new ApplicationException("Cannot access draft article without being author or admin.");
}
// InReview Articles can only be accessed by reviewers
if (Article.Status is ArticleStatus.InReview) {
if (principal.IsInRole("Reviewer")) {
return Article;
}
throw new ApplicationException("Cannot access in-review article without being a reviewer or admin.");
}
throw new ApplicationException("User does not have access to this article.");
}
protected override async Task OnInitializedAsync() {
// We need blocking calls here, bc otherwise Blazor will execute Render in parallel,
// running into a null pointer on the Article property and panicking
// ReSharper disable once MethodHasAsyncOverload
await using var context = ContextFactory.CreateDbContext();
// ReSharper disable once MethodHasAsyncOverload
var now = DateTimeOffset.UtcNow;
Article = context.Set<Article>()
.Include(a => a.Author)
.Include(a => a.Reviewer)
// .Where(a => a.Status >= ArticleStatus.Published && a.PublishDate <= now)
.First(a => a.Id == Id);
if (Article is null) throw new ApplicationException("Article not found.");
}
}