Improved article body formatting

This commit is contained in:
Mia Rose Winter 2024-04-30 14:48:58 +02:00
parent e816b9fb43
commit e1cab9b53f
Signed by: miawinter
GPG key ID: 4B6F6A83178F595E
2 changed files with 53 additions and 41 deletions

View file

@ -188,24 +188,13 @@
Article.PublishDate = Model.PublishDate.Value; Article.PublishDate = Model.PublishDate.Value;
if (Article.Status is ArticleStatus.Published && Article.PublishDate < DateTimeOffset.Now) { if (Article.Status is ArticleStatus.Published && Article.PublishDate < DateTimeOffset.Now) {
// can't change slugs when the article is public // can't change slugs when the article is public
} else if (!string.IsNullOrWhiteSpace(Model.Slug) || string.IsNullOrWhiteSpace(Article.Slug)) { } else {
string baseSlug = Model.Slug ?? Article.Title; Article.UpdateSlug(Model.Slug);
baseSlug = baseSlug.ToLowerInvariant()[..Math.Min(64, baseSlug.Length)];
string slug = Uri.EscapeDataString(baseSlug).Replace("-", "+").Replace("%20", "-");
// if our escaping increases the slug length, there is a chance it ends with an escape
// character, so if this overshoot is not divisible by 3, then we risk cutting of the
// escape character, so we need to remove it in it's entirely if that's the case
int escapeTrimOvershoot = Math.Max(0, 3 - (slug.Length - baseSlug.Length) % 3);
// if the slug already fits 64 character, there will be no cutoff in the next operation anyway,
// so we don't need to fix what is described in the previous comment
if (slug.Length <= 64) escapeTrimOvershoot = 0;
Article.Slug = slug[..Math.Min(slug.Length, 64 - escapeTrimOvershoot)];
Model.Slug = Article.Slug; Model.Slug = Article.Slug;
} }
Article.LastModified = DateTimeOffset.UtcNow; Article.LastModified = DateTimeOffset.UtcNow;
Article.BodyHtml = MarkdownUtilities.Parse(Article.Body); Article.UpdateBody();
Article.BodyPlain = HtmlUtilities.GetPlainText(Article.BodyHtml);
await using var context = await ContextFactory.CreateDbContextAsync(); await using var context = await ContextFactory.CreateDbContextAsync();

View file

@ -1,46 +1,69 @@
using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations;
using Wave.Utilities;
namespace Wave.Data; namespace Wave.Data;
public enum ArticleStatus { public enum ArticleStatus {
Draft = 0, Draft = 0,
InReview = 1, InReview = 1,
Published = 2 Published = 2
} }
// TODO:: Add tags for MVP ? // TODO:: Add tags for MVP ?
// TODO:: Archive System (Notice / Redirect to new content?) (Deprecation date?) // TODO:: Archive System (Notice / Redirect to new content?) (Deprecation date?)
public class Article : ISoftDelete { public class Article : ISoftDelete {
[Key] [Key]
public Guid Id { get; set; } public Guid Id { get; set; }
public bool IsDeleted { get; set; } public bool IsDeleted { get; set; }
// Computed // Computed
public bool CanBePublic { get; set; } public bool CanBePublic { get; set; }
[MaxLength(256)] [MaxLength(256)]
public required string Title { get; set; } public required string Title { get; set; }
public required string Body { get; set; } // ReSharper disable thrice EntityFramework.ModelValidation.UnlimitedStringLength
public string BodyHtml { get; set; } = string.Empty; public required string Body { get; set; }
public string BodyPlain { get; set; } = string.Empty; public string BodyHtml { get; set; } = string.Empty;
public string BodyPlain { get; set; } = string.Empty;
[MaxLength(64)] [MaxLength(64)]
public string Slug { get; set; } = string.Empty; public string Slug { get; set; } = string.Empty;
public required ApplicationUser Author { get; set; } public required ApplicationUser Author { get; set; }
public ApplicationUser? Reviewer { get; set; } public ApplicationUser? Reviewer { get; set; }
public ArticleStatus Status { get; set; } public ArticleStatus Status { get; set; }
public DateTimeOffset CreationDate { get; set; } = DateTimeOffset.Now; public DateTimeOffset CreationDate { get; set; } = DateTimeOffset.Now;
public DateTimeOffset PublishDate { get; set; } = DateTimeOffset.MaxValue; public DateTimeOffset PublishDate { get; set; } = DateTimeOffset.MaxValue;
public DateTimeOffset? LastModified { get; set; } public DateTimeOffset? LastModified { get; set; }
/// <summary> /// <summary>
/// Returns LastModified if it's after the articles PublishDate, otherwise gives you the PublishDate /// Returns LastModified if it's after the articles PublishDate, otherwise gives you the PublishDate
/// </summary> /// </summary>
public DateTimeOffset LastPublicChange => LastModified > PublishDate ? LastModified.Value : PublishDate; public DateTimeOffset LastPublicChange => LastModified > PublishDate ? LastModified.Value : PublishDate;
public IList<Category> Categories { get; } = []; public IList<Category> Categories { get; } = [];
public IList<ArticleImage> Images { get; } = []; public IList<ArticleImage> Images { get; } = [];
public void UpdateSlug(string? potentialNewSlug) {
if (string.IsNullOrWhiteSpace(potentialNewSlug) && !string.IsNullOrWhiteSpace(Slug)) return;
string baseSlug = potentialNewSlug ?? Title;
baseSlug = baseSlug.ToLowerInvariant()[..Math.Min(64, baseSlug.Length)];
string slug = Uri.EscapeDataString(baseSlug).Replace("-", "+").Replace("%20", "-");
// if our escaping increases the slug length, there is a chance it ends with an escape
// character, so if this overshoot is not divisible by 3, then we risk cutting of the
// escape character, so we need to remove it in it's entirely if that's the case
int escapeTrimOvershoot = Math.Max(0, 3 - (slug.Length - baseSlug.Length) % 3);
// if the slug already fits 64 character, there will be no cutoff in the next operation anyway,
// so we don't need to fix what is described in the previous comment
if (slug.Length <= 64) escapeTrimOvershoot = 0;
Slug = slug[..Math.Min(slug.Length, 64 - escapeTrimOvershoot)];
}
public void UpdateBody() {
BodyHtml = MarkdownUtilities.Parse(Body).Trim();
BodyPlain = HtmlUtilities.GetPlainText(BodyHtml).Trim();
}
} }