diff --git a/Wave/Components/Pages/Partials/ArticleEditorPartial.razor b/Wave/Components/Pages/Partials/ArticleEditorPartial.razor
index f3fea71..e76fc5b 100644
--- a/Wave/Components/Pages/Partials/ArticleEditorPartial.razor
+++ b/Wave/Components/Pages/Partials/ArticleEditorPartial.razor
@@ -188,24 +188,13 @@
Article.PublishDate = Model.PublishDate.Value;
if (Article.Status is ArticleStatus.Published && Article.PublishDate < DateTimeOffset.Now) {
// can't change slugs when the article is public
- } else if (!string.IsNullOrWhiteSpace(Model.Slug) || string.IsNullOrWhiteSpace(Article.Slug)) {
- string baseSlug = Model.Slug ?? Article.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;
- Article.Slug = slug[..Math.Min(slug.Length, 64 - escapeTrimOvershoot)];
+ } else {
+ Article.UpdateSlug(Model.Slug);
Model.Slug = Article.Slug;
}
Article.LastModified = DateTimeOffset.UtcNow;
- Article.BodyHtml = MarkdownUtilities.Parse(Article.Body);
- Article.BodyPlain = HtmlUtilities.GetPlainText(Article.BodyHtml);
+ Article.UpdateBody();
await using var context = await ContextFactory.CreateDbContextAsync();
diff --git a/Wave/Data/Article.cs b/Wave/Data/Article.cs
index 2711ab4..0cba687 100644
--- a/Wave/Data/Article.cs
+++ b/Wave/Data/Article.cs
@@ -1,46 +1,69 @@
using System.ComponentModel.DataAnnotations;
+using Wave.Utilities;
namespace Wave.Data;
public enum ArticleStatus {
- Draft = 0,
- InReview = 1,
- Published = 2
+ Draft = 0,
+ InReview = 1,
+ Published = 2
}
// TODO:: Add tags for MVP ?
// TODO:: Archive System (Notice / Redirect to new content?) (Deprecation date?)
public class Article : ISoftDelete {
- [Key]
- public Guid Id { get; set; }
- public bool IsDeleted { get; set; }
+ [Key]
+ public Guid Id { get; set; }
+ public bool IsDeleted { get; set; }
- // Computed
- public bool CanBePublic { get; set; }
+ // Computed
+ public bool CanBePublic { get; set; }
- [MaxLength(256)]
- public required string Title { get; set; }
- public required string Body { get; set; }
- public string BodyHtml { get; set; } = string.Empty;
- public string BodyPlain { get; set; } = string.Empty;
+ [MaxLength(256)]
+ public required string Title { get; set; }
+ // ReSharper disable thrice EntityFramework.ModelValidation.UnlimitedStringLength
+ public required string Body { get; set; }
+ public string BodyHtml { get; set; } = string.Empty;
+ public string BodyPlain { get; set; } = string.Empty;
- [MaxLength(64)]
- public string Slug { get; set; } = string.Empty;
+ [MaxLength(64)]
+ public string Slug { get; set; } = string.Empty;
- public required ApplicationUser Author { get; set; }
- public ApplicationUser? Reviewer { get; set; }
+ public required ApplicationUser Author { get; set; }
+ public ApplicationUser? Reviewer { get; set; }
- public ArticleStatus Status { get; set; }
- public DateTimeOffset CreationDate { get; set; } = DateTimeOffset.Now;
- public DateTimeOffset PublishDate { get; set; } = DateTimeOffset.MaxValue;
- public DateTimeOffset? LastModified { get; set; }
+ public ArticleStatus Status { get; set; }
+ public DateTimeOffset CreationDate { get; set; } = DateTimeOffset.Now;
+ public DateTimeOffset PublishDate { get; set; } = DateTimeOffset.MaxValue;
+ public DateTimeOffset? LastModified { get; set; }
- ///
- /// Returns LastModified if it's after the articles PublishDate, otherwise gives you the PublishDate
- ///
- public DateTimeOffset LastPublicChange => LastModified > PublishDate ? LastModified.Value : PublishDate;
+ ///
+ /// Returns LastModified if it's after the articles PublishDate, otherwise gives you the PublishDate
+ ///
+ public DateTimeOffset LastPublicChange => LastModified > PublishDate ? LastModified.Value : PublishDate;
- public IList Categories { get; } = [];
- public IList Images { get; } = [];
+ public IList Categories { get; } = [];
+ public IList 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();
+ }
}
\ No newline at end of file