feat: add <meta> tag creation abstraction

This commit is contained in:
Oliver Booth 2024-04-27 15:36:13 +01:00
parent b119861eee
commit cd6bbec1a5
Signed by: oliverbooth
GPG Key ID: E60B570D1B7557B5
2 changed files with 120 additions and 9 deletions

View File

@ -0,0 +1,118 @@
using System.Web;
using Cysharp.Text;
using OliverBooth.Data.Blog;
using OliverBooth.Services;
namespace OliverBooth.Extensions;
/// <summary>
/// Provides helper methods for generating HTML tags
/// </summary>
public static class HtmlUtility
{
/// <summary>
/// Creates <c>&lt;meta&gt;</c> embed tags by pulling data from the specified blog post.
/// </summary>
/// <param name="post">The blog post whose metadata should be retrieved.</param>
/// <param name="blogPostService">The <see cref="IBlogPostService" /> injected by the page.</param>
/// <returns>A string containing a collection of <c>&lt;meta&gt;</c> embed tags.</returns>
/// <exception cref="ArgumentNullException">
/// <para><paramref name="post" /> is <see langword="null" />.</para>
/// -or-
/// <para><paramref name="blogPostService" /> is <see langword="null" />.</para>
/// </exception>
public static string CreateMetaTagsFromPost(IBlogPost post, IBlogPostService blogPostService)
{
if (post is null)
{
throw new ArgumentNullException(nameof(post));
}
if (blogPostService is null)
{
throw new ArgumentNullException(nameof(blogPostService));
}
string excerpt = blogPostService.RenderExcerpt(post, out _);
var tags = new Dictionary<string, string>
{
["title"] = post.Title,
["description"] = excerpt,
["author"] = post.Author.DisplayName
};
return CreateMetaTags(tags);
}
/// <summary>
/// Creates <c>&lt;meta&gt;</c> embed tags by pulling data from the specified dictionary.
/// </summary>
/// <param name="tags">
/// A dictionary containing the tag values. This dictionary should be in the form:
///
/// <list type="table">
/// <listheader>
/// <term>Key</term>
/// <description>Description</description>
/// </listheader>
///
/// <item>
/// <term>description</term>
/// <description>
/// The value to apply to the <c>description</c>, <c>og:description</c>, and <c>twitter:description</c>, tags.
/// </description>
/// </item>
///
/// <item>
/// <term>author</term>
/// <description>The value to apply to the <c>og:site_name</c>, and <c>twitter:creator</c>, tags.</description>
/// </item>
///
/// <item>
/// <term>title</term>
/// <description>
/// The value to apply to the <c>title</c>, <c>og:title</c>, and <c>twitter:title</c>, tags.
/// </description>
/// </item>
/// </list>
///
/// Any other values contained with the dictionary are ignored.
/// </param>
/// <returns>A string containing a collection of <c>&lt;meta&gt;</c> embed tags.</returns>
/// <exception cref="ArgumentNullException"><paramref name="tags" /> is <see langword="null" />.</exception>
public static string CreateMetaTags(IReadOnlyDictionary<string, string> tags)
{
if (tags is null)
{
throw new ArgumentNullException(nameof(tags));
}
using Utf8ValueStringBuilder builder = ZString.CreateUtf8StringBuilder();
builder.AppendLine("""<meta property="og:type" content="article">""");
if (tags.TryGetValue("description", out string? description))
{
description = HttpUtility.HtmlEncode(description);
builder.AppendLine($"""<meta name="description" content="{description}">""");
builder.AppendLine($"""<meta property="og:description" content="{description}">""");
builder.AppendLine($"""<meta property="twitter:description" content="{description}">""");
}
if (tags.TryGetValue("author", out string? author))
{
author = HttpUtility.HtmlEncode(author);
builder.AppendLine($"""<meta property="og:site_name" content="{author}">""");
builder.AppendLine($"""<meta property="twitter:creator" content="{author}">""");
}
if (tags.TryGetValue("title", out string? title))
{
title = HttpUtility.HtmlEncode(title);
builder.AppendLine($"""<meta name="title" content="{title}">""");
builder.AppendLine($"""<meta property="og:title" content="{title}">""");
builder.AppendLine($"""<meta property="twitter:title" content="{title}">""");
}
return builder.ToString();
}
}

View File

@ -1,4 +1,5 @@
@using OliverBooth.Data.Blog @using OliverBooth.Data.Blog
@using OliverBooth.Extensions
@using OliverBooth.Services @using OliverBooth.Services
@inject IBlogPostService BlogPostService @inject IBlogPostService BlogPostService
@{ @{
@ -28,15 +29,7 @@
} }
@if (ViewData["Post"] is IBlogPost post) @if (ViewData["Post"] is IBlogPost post)
{ {
string excerpt = BlogPostService.RenderExcerpt(post, out bool trimmed); @Html.Raw(HtmlUtility.CreateMetaTagsFromPost(post, BlogPostService))
<meta name="title" content="@post.Title">
<meta name="description" content="@excerpt">
<meta property="og:title" content="@post.Title">
<meta property="og:description" content="@excerpt">
<meta property="og:type" content="article">
<meta property="twitter:title" content="@post.Title">
<meta property="twitter:creator" content="@post.Author.DisplayName">
<meta property="twitter:description" content="@excerpt">
} }
else else
{ {