feat: add support for excerpts on blog posts / tutorial articles
This commit is contained in:
parent
cd6bbec1a5
commit
879ff6a295
@ -16,6 +16,9 @@ internal sealed class BlogPost : IBlogPost
|
||||
/// <inheritdoc />
|
||||
public bool EnableComments { get; internal set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public string? Excerpt { get; internal set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public Guid Id { get; private set; } = Guid.NewGuid();
|
||||
|
||||
|
@ -20,6 +20,7 @@ internal sealed class BlogPostConfiguration : IEntityTypeConfiguration<BlogPost>
|
||||
builder.Property(e => e.Updated).IsRequired(false);
|
||||
builder.Property(e => e.Title).HasMaxLength(255).IsRequired();
|
||||
builder.Property(e => e.Body).IsRequired();
|
||||
builder.Property(e => e.Excerpt).HasMaxLength(512).IsRequired(false);
|
||||
builder.Property(e => e.IsRedirect).IsRequired();
|
||||
builder.Property(e => e.RedirectUrl).HasConversion<UriToStringConverter>().HasMaxLength(255).IsRequired(false);
|
||||
builder.Property(e => e.EnableComments).IsRequired();
|
||||
|
@ -25,6 +25,12 @@ public interface IBlogPost
|
||||
/// </value>
|
||||
bool EnableComments { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the excerpt of this post, if it has one.
|
||||
/// </summary>
|
||||
/// <value>The excerpt, or <see langword="null" /> if this post has no excerpt.</value>
|
||||
string? Excerpt { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the ID of the post.
|
||||
/// </summary>
|
||||
|
@ -16,6 +16,7 @@ internal sealed class TutorialArticleConfiguration : IEntityTypeConfiguration<Tu
|
||||
|
||||
builder.Property(e => e.Id).IsRequired();
|
||||
builder.Property(e => e.Folder).IsRequired();
|
||||
builder.Property(e => e.Excerpt).HasMaxLength(512).IsRequired(false);
|
||||
builder.Property(e => e.Published).IsRequired();
|
||||
builder.Property(e => e.Updated);
|
||||
builder.Property(e => e.Slug).IsRequired();
|
||||
|
@ -11,6 +11,12 @@ public interface ITutorialArticle
|
||||
/// <value>The body.</value>
|
||||
string Body { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the excerpt of this article, if it has one.
|
||||
/// </summary>
|
||||
/// <value>The excerpt, or <see langword="null" /> if this article has no excerpt.</value>
|
||||
string? Excerpt { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the ID of the folder this article is contained within.
|
||||
/// </summary>
|
||||
|
@ -8,6 +8,9 @@ internal sealed class TutorialArticle : IEquatable<TutorialArticle>, ITutorialAr
|
||||
/// <inheritdoc />
|
||||
public string Body { get; private set; } = string.Empty;
|
||||
|
||||
/// <inheritdoc />
|
||||
public string? Excerpt { get; private set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public int Folder { get; private set; }
|
||||
|
||||
@ -15,7 +18,7 @@ internal sealed class TutorialArticle : IEquatable<TutorialArticle>, ITutorialAr
|
||||
public int Id { get; private set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public int? NextPart { get; }
|
||||
public int? NextPart { get; private set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public Uri? PreviewImageUrl { get; private set; }
|
||||
|
@ -90,6 +90,12 @@ internal sealed class BlogPostService : IBlogPostService
|
||||
/// <inheritdoc />
|
||||
public string RenderExcerpt(IBlogPost post, out bool wasTrimmed)
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(post.Excerpt))
|
||||
{
|
||||
wasTrimmed = false;
|
||||
return Markdig.Markdown.ToHtml(post.Excerpt, _markdownPipeline);
|
||||
}
|
||||
|
||||
string body = post.Body;
|
||||
int moreIndex = body.IndexOf("<!--more-->", StringComparison.Ordinal);
|
||||
|
||||
|
@ -67,6 +67,17 @@ public interface ITutorialService
|
||||
/// <returns>The rendered HTML of the article.</returns>
|
||||
string RenderArticle(ITutorialArticle article);
|
||||
|
||||
/// <summary>
|
||||
/// Renders the excerpt of the specified article.
|
||||
/// </summary>
|
||||
/// <param name="article">The article whose excerpt to render.</param>
|
||||
/// <param name="wasTrimmed">
|
||||
/// When this method returns, contains <see langword="true" /> if the excerpt was trimmed; otherwise,
|
||||
/// <see langword="false" />.
|
||||
/// </param>
|
||||
/// <returns>The rendered HTML of the article's excerpt.</returns>
|
||||
string RenderExcerpt(ITutorialArticle article, out bool wasTrimmed);
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to find an article by its ID.
|
||||
/// </summary>
|
||||
|
@ -1,5 +1,6 @@
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using Cysharp.Text;
|
||||
using Humanizer;
|
||||
using Markdig;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using OliverBooth.Data;
|
||||
@ -108,6 +109,29 @@ internal sealed class TutorialService : ITutorialService
|
||||
return Markdig.Markdown.ToHtml(article.Body, _markdownPipeline);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public string RenderExcerpt(ITutorialArticle article, out bool wasTrimmed)
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(article.Excerpt))
|
||||
{
|
||||
wasTrimmed = false;
|
||||
return Markdig.Markdown.ToHtml(article.Excerpt, _markdownPipeline);
|
||||
}
|
||||
|
||||
string body = article.Body;
|
||||
int moreIndex = body.IndexOf("<!--more-->", StringComparison.Ordinal);
|
||||
|
||||
if (moreIndex == -1)
|
||||
{
|
||||
string excerpt = body.Truncate(255, "...");
|
||||
wasTrimmed = body.Length > 255;
|
||||
return Markdig.Markdown.ToHtml(excerpt, _markdownPipeline);
|
||||
}
|
||||
|
||||
wasTrimmed = true;
|
||||
return Markdig.Markdown.ToHtml(body[..moreIndex], _markdownPipeline);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool TryGetArticle(int id, [NotNullWhen(true)] out ITutorialArticle? article)
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user