From 6ec4103a3a84117a4072cc7ba0af38b0fadca818 Mon Sep 17 00:00:00 2001 From: Oliver Booth Date: Sun, 5 May 2024 02:18:20 +0100 Subject: [PATCH] refactor: separate Markdig extensions from project Also introduces .Common project to house common references and types --- .../Data/Blog/IBlogAuthor.cs | 2 +- .../Data/Blog/IBlogPost.cs | 2 +- .../Data/Blog/ILegacyComment.cs | 2 +- .../Data/Blog/IUser.cs | 2 +- .../Data/Mastodon/AttachmentType.cs | 2 +- .../Data/Mastodon/IMastodonStatus.cs | 31 +++ .../Data/Mastodon/MediaAttachment.cs | 2 +- .../Data/Visibility.cs | 2 +- .../Data/Web/BookState.cs | 2 +- .../Data/Web/IBlacklistEntry.cs | 2 +- .../Data/Web/IBook.cs | 2 +- .../Data/Web/ICodeSnippet.cs | 2 +- .../Data/Web/IProgrammingLanguage.cs | 2 +- .../Data/Web/IProject.cs | 2 +- .../Data/Web/ITemplate.cs | 2 +- .../Data/Web/ITutorialArticle.cs | 2 +- .../Data/Web/ITutorialFolder.cs | 2 +- .../Data/Web/ProjectStatus.cs | 2 +- OliverBooth.Common/OliverBooth.Common.csproj | 16 ++ .../Services/IBlogPostService.cs | 4 +- .../Services/IBlogUserService.cs | 4 +- .../Services/ICodeSnippetService.cs | 4 +- .../Services/IContactService.cs | 4 +- .../Services/IMastodonService.cs | 6 +- .../Services/IProgrammingLanguageService.cs | 14 ++ .../Services/IProjectService.cs | 4 +- .../Services/IReadingListService.cs | 4 +- .../Services/ITutorialService.cs | 8 +- .../Markdown/Callout/CalloutBlock.cs | 43 +++++ .../Markdown/Callout/CalloutExtension.cs | 32 ++++ .../Markdown/Callout/CalloutInlineParser.cs | 176 ++++++++++++++++++ .../Markdown/Callout/CalloutRenderer.cs | 119 ++++++++++++ .../Markdown/MarkdownExtensions.cs | 21 +++ .../Markdown/Template/TemplateExtension.cs | 4 +- .../Markdown/Template/TemplateInline.cs | 2 +- .../Markdown/Template/TemplateInlineParser.cs | 2 +- .../Markdown/Template/TemplateRenderer.cs | 4 +- .../Markdown/Timestamp/TimestampExtension.cs | 25 +++ .../Markdown/Timestamp/TimestampFormat.cs | 42 +++++ .../Markdown/Timestamp/TimestampInline.cs | 21 +++ .../Timestamp/TimestampInlineParser.cs | 91 +++++++++ .../Markdown/Timestamp/TimestampRenderer.cs | 55 ++++++ .../MarkdownPipelineExtensions.cs | 38 ++++ .../OliverBooth.Extensions.Markdig.csproj | 13 ++ .../Services/ITemplateService.cs | 6 +- OliverBooth.sln | 12 ++ .../Controllers/Blog/BlogApiController.cs | 4 +- OliverBooth/Controllers/Blog/RssController.cs | 4 +- OliverBooth/Controllers/FormattedBlacklist.cs | 4 +- OliverBooth/Data/Blog/BlogPost.cs | 2 + .../Configuration/BlogPostConfiguration.cs | 1 + OliverBooth/Data/Blog/LegacyComment.cs | 1 + OliverBooth/Data/Blog/User.cs | 1 + OliverBooth/Data/Mastodon/MastodonStatus.cs | 26 +-- OliverBooth/Data/Web/BlacklistEntry.cs | 2 + OliverBooth/Data/Web/Book.cs | 1 + OliverBooth/Data/Web/CodeSnippet.cs | 2 + .../Web/Configuration/BookConfiguration.cs | 1 + .../Web/Configuration/ProjectConfiguration.cs | 1 + .../TutorialArticleConfiguration.cs | 1 + .../TutorialFolderConfiguration.cs | 1 + OliverBooth/Data/Web/ProgrammingLanguage.cs | 2 + OliverBooth/Data/Web/Project.cs | 2 + OliverBooth/Data/Web/Template.cs | 2 + OliverBooth/Data/Web/TutorialArticle.cs | 2 + OliverBooth/Data/Web/TutorialFolder.cs | 3 + OliverBooth/Extensions/HtmlUtility.cs | 6 +- .../Template/CodeSnippetTemplateRenderer.cs | 5 +- .../Template/CustomTemplateRenderer.cs | 1 + OliverBooth/OliverBooth.csproj | 9 +- OliverBooth/Pages/Blog/Article.cshtml | 7 +- OliverBooth/Pages/Blog/Article.cshtml.cs | 4 +- OliverBooth/Pages/Blog/Index.cshtml | 18 +- OliverBooth/Pages/Blog/Index.cshtml.cs | 4 +- OliverBooth/Pages/Blog/RawArticle.cshtml.cs | 4 +- OliverBooth/Pages/Books.cshtml | 2 +- OliverBooth/Pages/Books.cshtml.cs | 4 +- OliverBooth/Pages/Contact/Blacklist.cshtml | 5 +- OliverBooth/Pages/Contact/Index.cshtml | 1 + OliverBooth/Pages/Contact/Result.cshtml | 1 + OliverBooth/Pages/Index.cshtml | 1 + .../Pages/Privacy/FiveOClockSomewhere.cshtml | 1 + OliverBooth/Pages/Privacy/GooglePlay.cshtml | 1 + OliverBooth/Pages/Privacy/Index.cshtml | 1 + OliverBooth/Pages/Projects/Index.cshtml | 5 +- OliverBooth/Pages/Projects/Project.cshtml | 5 +- OliverBooth/Pages/Projects/Project.cshtml.cs | 4 +- OliverBooth/Pages/Shared/_Layout.cshtml | 7 +- .../Pages/Shared/_MinimalLayout.cshtml | 1 + OliverBooth/Pages/Tutorials/Article.cshtml | 8 +- OliverBooth/Pages/Tutorials/Article.cshtml.cs | 4 +- OliverBooth/Pages/Tutorials/Index.cshtml | 7 +- OliverBooth/Pages/Tutorials/Index.cshtml.cs | 4 +- OliverBooth/Program.cs | 7 +- OliverBooth/Services/BlogPostService.cs | 4 +- OliverBooth/Services/BlogUserService.cs | 2 + OliverBooth/Services/CodeSnippetService.cs | 2 + OliverBooth/Services/ContactService.cs | 2 + OliverBooth/Services/MastodonService.cs | 4 +- .../Services/ProgrammingLanguageService.cs | 14 +- OliverBooth/Services/ProjectService.cs | 2 + OliverBooth/Services/ReadingListService.cs | 2 + OliverBooth/Services/TemplateService.cs | 5 +- OliverBooth/Services/TutorialService.cs | 5 +- 104 files changed, 932 insertions(+), 139 deletions(-) rename {OliverBooth => OliverBooth.Common}/Data/Blog/IBlogAuthor.cs (95%) rename {OliverBooth => OliverBooth.Common}/Data/Blog/IBlogPost.cs (98%) rename {OliverBooth => OliverBooth.Common}/Data/Blog/ILegacyComment.cs (97%) rename {OliverBooth => OliverBooth.Common}/Data/Blog/IUser.cs (97%) rename {OliverBooth => OliverBooth.Common}/Data/Mastodon/AttachmentType.cs (66%) create mode 100644 OliverBooth.Common/Data/Mastodon/IMastodonStatus.cs rename {OliverBooth => OliverBooth.Common}/Data/Mastodon/MediaAttachment.cs (92%) rename {OliverBooth => OliverBooth.Common}/Data/Visibility.cs (94%) rename {OliverBooth => OliverBooth.Common}/Data/Web/BookState.cs (90%) rename {OliverBooth => OliverBooth.Common}/Data/Web/IBlacklistEntry.cs (93%) rename {OliverBooth => OliverBooth.Common}/Data/Web/IBook.cs (95%) rename {OliverBooth => OliverBooth.Common}/Data/Web/ICodeSnippet.cs (93%) rename {OliverBooth => OliverBooth.Common}/Data/Web/IProgrammingLanguage.cs (92%) rename {OliverBooth => OliverBooth.Common}/Data/Web/IProject.cs (98%) rename {OliverBooth => OliverBooth.Common}/Data/Web/ITemplate.cs (92%) rename {OliverBooth => OliverBooth.Common}/Data/Web/ITutorialArticle.cs (98%) rename {OliverBooth => OliverBooth.Common}/Data/Web/ITutorialFolder.cs (96%) rename {OliverBooth => OliverBooth.Common}/Data/Web/ProjectStatus.cs (92%) create mode 100644 OliverBooth.Common/OliverBooth.Common.csproj rename {OliverBooth => OliverBooth.Common}/Services/IBlogPostService.cs (98%) rename {OliverBooth => OliverBooth.Common}/Services/IBlogUserService.cs (90%) rename {OliverBooth => OliverBooth.Common}/Services/ICodeSnippetService.cs (95%) rename {OliverBooth => OliverBooth.Common}/Services/IContactService.cs (77%) rename {OliverBooth => OliverBooth.Common}/Services/IMastodonService.cs (59%) create mode 100644 OliverBooth.Common/Services/IProgrammingLanguageService.cs rename {OliverBooth => OliverBooth.Common}/Services/IProjectService.cs (97%) rename {OliverBooth => OliverBooth.Common}/Services/IReadingListService.cs (85%) rename {OliverBooth => OliverBooth.Common}/Services/ITutorialService.cs (97%) create mode 100644 OliverBooth.Extensions.Markdig/Markdown/Callout/CalloutBlock.cs create mode 100644 OliverBooth.Extensions.Markdig/Markdown/Callout/CalloutExtension.cs create mode 100644 OliverBooth.Extensions.Markdig/Markdown/Callout/CalloutInlineParser.cs create mode 100644 OliverBooth.Extensions.Markdig/Markdown/Callout/CalloutRenderer.cs create mode 100644 OliverBooth.Extensions.Markdig/Markdown/MarkdownExtensions.cs rename {OliverBooth => OliverBooth.Extensions.Markdig}/Markdown/Template/TemplateExtension.cs (90%) rename {OliverBooth => OliverBooth.Extensions.Markdig}/Markdown/Template/TemplateInline.cs (95%) rename {OliverBooth => OliverBooth.Extensions.Markdig}/Markdown/Template/TemplateInlineParser.cs (99%) rename {OliverBooth => OliverBooth.Extensions.Markdig}/Markdown/Template/TemplateRenderer.cs (88%) create mode 100644 OliverBooth.Extensions.Markdig/Markdown/Timestamp/TimestampExtension.cs create mode 100644 OliverBooth.Extensions.Markdig/Markdown/Timestamp/TimestampFormat.cs create mode 100644 OliverBooth.Extensions.Markdig/Markdown/Timestamp/TimestampInline.cs create mode 100644 OliverBooth.Extensions.Markdig/Markdown/Timestamp/TimestampInlineParser.cs create mode 100644 OliverBooth.Extensions.Markdig/Markdown/Timestamp/TimestampRenderer.cs create mode 100644 OliverBooth.Extensions.Markdig/MarkdownPipelineExtensions.cs create mode 100644 OliverBooth.Extensions.Markdig/OliverBooth.Extensions.Markdig.csproj rename {OliverBooth => OliverBooth.Extensions.Markdig}/Services/ITemplateService.cs (94%) diff --git a/OliverBooth/Data/Blog/IBlogAuthor.cs b/OliverBooth.Common/Data/Blog/IBlogAuthor.cs similarity index 95% rename from OliverBooth/Data/Blog/IBlogAuthor.cs rename to OliverBooth.Common/Data/Blog/IBlogAuthor.cs index 3f1c3e1..279da26 100644 --- a/OliverBooth/Data/Blog/IBlogAuthor.cs +++ b/OliverBooth.Common/Data/Blog/IBlogAuthor.cs @@ -1,4 +1,4 @@ -namespace OliverBooth.Data.Blog; +namespace OliverBooth.Common.Data.Blog; /// /// Represents the author of a blog post. diff --git a/OliverBooth/Data/Blog/IBlogPost.cs b/OliverBooth.Common/Data/Blog/IBlogPost.cs similarity index 98% rename from OliverBooth/Data/Blog/IBlogPost.cs rename to OliverBooth.Common/Data/Blog/IBlogPost.cs index bc1a2e5..093220c 100644 --- a/OliverBooth/Data/Blog/IBlogPost.cs +++ b/OliverBooth.Common/Data/Blog/IBlogPost.cs @@ -1,4 +1,4 @@ -namespace OliverBooth.Data.Blog; +namespace OliverBooth.Common.Data.Blog; /// /// Represents a blog post. diff --git a/OliverBooth/Data/Blog/ILegacyComment.cs b/OliverBooth.Common/Data/Blog/ILegacyComment.cs similarity index 97% rename from OliverBooth/Data/Blog/ILegacyComment.cs rename to OliverBooth.Common/Data/Blog/ILegacyComment.cs index 2f43dc2..2a5cc99 100644 --- a/OliverBooth/Data/Blog/ILegacyComment.cs +++ b/OliverBooth.Common/Data/Blog/ILegacyComment.cs @@ -1,4 +1,4 @@ -namespace OliverBooth.Data.Blog; +namespace OliverBooth.Common.Data.Blog; /// /// Represents a comment that was posted on a legacy comment framework. diff --git a/OliverBooth/Data/Blog/IUser.cs b/OliverBooth.Common/Data/Blog/IUser.cs similarity index 97% rename from OliverBooth/Data/Blog/IUser.cs rename to OliverBooth.Common/Data/Blog/IUser.cs index 912db14..80af1b8 100644 --- a/OliverBooth/Data/Blog/IUser.cs +++ b/OliverBooth.Common/Data/Blog/IUser.cs @@ -1,4 +1,4 @@ -namespace OliverBooth.Data.Blog; +namespace OliverBooth.Common.Data.Blog; /// /// Represents a user which can log in to the blog. diff --git a/OliverBooth/Data/Mastodon/AttachmentType.cs b/OliverBooth.Common/Data/Mastodon/AttachmentType.cs similarity index 66% rename from OliverBooth/Data/Mastodon/AttachmentType.cs rename to OliverBooth.Common/Data/Mastodon/AttachmentType.cs index 6d0b5e1..701db4e 100644 --- a/OliverBooth/Data/Mastodon/AttachmentType.cs +++ b/OliverBooth.Common/Data/Mastodon/AttachmentType.cs @@ -1,4 +1,4 @@ -namespace OliverBooth.Data.Mastodon; +namespace OliverBooth.Common.Data.Mastodon; public enum AttachmentType { diff --git a/OliverBooth.Common/Data/Mastodon/IMastodonStatus.cs b/OliverBooth.Common/Data/Mastodon/IMastodonStatus.cs new file mode 100644 index 0000000..f7bdc95 --- /dev/null +++ b/OliverBooth.Common/Data/Mastodon/IMastodonStatus.cs @@ -0,0 +1,31 @@ +namespace OliverBooth.Common.Data.Mastodon; + +/// +/// Represents a status on Mastodon. +/// +public interface IMastodonStatus +{ + /// + /// Gets the content of the status. + /// + /// The content. + string Content { get; } + + /// + /// Gets the date and time at which this status was posted. + /// + /// The post timestamp. + DateTimeOffset CreatedAt { get; } + + /// + /// Gets the media attachments for this status. + /// + /// The media attachments. + IReadOnlyList MediaAttachments { get; } + + /// + /// Gets the original URI of the status. + /// + /// The original URI. + Uri OriginalUri { get; } +} diff --git a/OliverBooth/Data/Mastodon/MediaAttachment.cs b/OliverBooth.Common/Data/Mastodon/MediaAttachment.cs similarity index 92% rename from OliverBooth/Data/Mastodon/MediaAttachment.cs rename to OliverBooth.Common/Data/Mastodon/MediaAttachment.cs index ddc3307..117be81 100644 --- a/OliverBooth/Data/Mastodon/MediaAttachment.cs +++ b/OliverBooth.Common/Data/Mastodon/MediaAttachment.cs @@ -1,4 +1,4 @@ -namespace OliverBooth.Data.Mastodon; +namespace OliverBooth.Common.Data.Mastodon; public sealed class MediaAttachment { diff --git a/OliverBooth/Data/Visibility.cs b/OliverBooth.Common/Data/Visibility.cs similarity index 94% rename from OliverBooth/Data/Visibility.cs rename to OliverBooth.Common/Data/Visibility.cs index 17b3e87..5bd0e92 100644 --- a/OliverBooth/Data/Visibility.cs +++ b/OliverBooth.Common/Data/Visibility.cs @@ -1,4 +1,4 @@ -namespace OliverBooth.Data; +namespace OliverBooth.Common.Data; /// /// An enumeration of the possible visibilities of a blog post. diff --git a/OliverBooth/Data/Web/BookState.cs b/OliverBooth.Common/Data/Web/BookState.cs similarity index 90% rename from OliverBooth/Data/Web/BookState.cs rename to OliverBooth.Common/Data/Web/BookState.cs index 637415a..29b8312 100644 --- a/OliverBooth/Data/Web/BookState.cs +++ b/OliverBooth.Common/Data/Web/BookState.cs @@ -1,4 +1,4 @@ -namespace OliverBooth.Data.Web; +namespace OliverBooth.Common.Data.Web; /// /// Represents the state of a book. diff --git a/OliverBooth/Data/Web/IBlacklistEntry.cs b/OliverBooth.Common/Data/Web/IBlacklistEntry.cs similarity index 93% rename from OliverBooth/Data/Web/IBlacklistEntry.cs rename to OliverBooth.Common/Data/Web/IBlacklistEntry.cs index 00516fb..6c55acc 100644 --- a/OliverBooth/Data/Web/IBlacklistEntry.cs +++ b/OliverBooth.Common/Data/Web/IBlacklistEntry.cs @@ -1,4 +1,4 @@ -namespace OliverBooth.Data.Web; +namespace OliverBooth.Common.Data.Web; /// /// Represents an entry in the blacklist. diff --git a/OliverBooth/Data/Web/IBook.cs b/OliverBooth.Common/Data/Web/IBook.cs similarity index 95% rename from OliverBooth/Data/Web/IBook.cs rename to OliverBooth.Common/Data/Web/IBook.cs index 5c71d85..77f5856 100644 --- a/OliverBooth/Data/Web/IBook.cs +++ b/OliverBooth.Common/Data/Web/IBook.cs @@ -1,4 +1,4 @@ -namespace OliverBooth.Data.Web; +namespace OliverBooth.Common.Data.Web; /// /// Represents a book. diff --git a/OliverBooth/Data/Web/ICodeSnippet.cs b/OliverBooth.Common/Data/Web/ICodeSnippet.cs similarity index 93% rename from OliverBooth/Data/Web/ICodeSnippet.cs rename to OliverBooth.Common/Data/Web/ICodeSnippet.cs index ba1d324..29ec491 100644 --- a/OliverBooth/Data/Web/ICodeSnippet.cs +++ b/OliverBooth.Common/Data/Web/ICodeSnippet.cs @@ -1,4 +1,4 @@ -namespace OliverBooth.Data.Web; +namespace OliverBooth.Common.Data.Web; /// /// Represents a code snippet. diff --git a/OliverBooth/Data/Web/IProgrammingLanguage.cs b/OliverBooth.Common/Data/Web/IProgrammingLanguage.cs similarity index 92% rename from OliverBooth/Data/Web/IProgrammingLanguage.cs rename to OliverBooth.Common/Data/Web/IProgrammingLanguage.cs index 039f31a..464b943 100644 --- a/OliverBooth/Data/Web/IProgrammingLanguage.cs +++ b/OliverBooth.Common/Data/Web/IProgrammingLanguage.cs @@ -1,4 +1,4 @@ -namespace OliverBooth.Data.Web; +namespace OliverBooth.Common.Data.Web; /// /// Represents a programming language. diff --git a/OliverBooth/Data/Web/IProject.cs b/OliverBooth.Common/Data/Web/IProject.cs similarity index 98% rename from OliverBooth/Data/Web/IProject.cs rename to OliverBooth.Common/Data/Web/IProject.cs index f2cc0d8..0b41719 100644 --- a/OliverBooth/Data/Web/IProject.cs +++ b/OliverBooth.Common/Data/Web/IProject.cs @@ -1,4 +1,4 @@ -namespace OliverBooth.Data.Web; +namespace OliverBooth.Common.Data.Web; /// /// Represents a project. diff --git a/OliverBooth/Data/Web/ITemplate.cs b/OliverBooth.Common/Data/Web/ITemplate.cs similarity index 92% rename from OliverBooth/Data/Web/ITemplate.cs rename to OliverBooth.Common/Data/Web/ITemplate.cs index 50ad893..b3bd1e5 100644 --- a/OliverBooth/Data/Web/ITemplate.cs +++ b/OliverBooth.Common/Data/Web/ITemplate.cs @@ -1,4 +1,4 @@ -namespace OliverBooth.Data.Web; +namespace OliverBooth.Common.Data.Web; /// /// Represents a template. diff --git a/OliverBooth/Data/Web/ITutorialArticle.cs b/OliverBooth.Common/Data/Web/ITutorialArticle.cs similarity index 98% rename from OliverBooth/Data/Web/ITutorialArticle.cs rename to OliverBooth.Common/Data/Web/ITutorialArticle.cs index a53b289..7d27e63 100644 --- a/OliverBooth/Data/Web/ITutorialArticle.cs +++ b/OliverBooth.Common/Data/Web/ITutorialArticle.cs @@ -1,4 +1,4 @@ -namespace OliverBooth.Data.Web; +namespace OliverBooth.Common.Data.Web; /// /// Represents a tutorial article. diff --git a/OliverBooth/Data/Web/ITutorialFolder.cs b/OliverBooth.Common/Data/Web/ITutorialFolder.cs similarity index 96% rename from OliverBooth/Data/Web/ITutorialFolder.cs rename to OliverBooth.Common/Data/Web/ITutorialFolder.cs index 65a3cd2..0544d85 100644 --- a/OliverBooth/Data/Web/ITutorialFolder.cs +++ b/OliverBooth.Common/Data/Web/ITutorialFolder.cs @@ -1,4 +1,4 @@ -namespace OliverBooth.Data.Web; +namespace OliverBooth.Common.Data.Web; /// /// Represents a folder for tutorial articles. diff --git a/OliverBooth/Data/Web/ProjectStatus.cs b/OliverBooth.Common/Data/Web/ProjectStatus.cs similarity index 92% rename from OliverBooth/Data/Web/ProjectStatus.cs rename to OliverBooth.Common/Data/Web/ProjectStatus.cs index a3cbf49..a7e4cb1 100644 --- a/OliverBooth/Data/Web/ProjectStatus.cs +++ b/OliverBooth.Common/Data/Web/ProjectStatus.cs @@ -1,6 +1,6 @@ using System.ComponentModel; -namespace OliverBooth.Data.Web; +namespace OliverBooth.Common.Data.Web; /// /// Represents the status of a project. diff --git a/OliverBooth.Common/OliverBooth.Common.csproj b/OliverBooth.Common/OliverBooth.Common.csproj new file mode 100644 index 0000000..f127848 --- /dev/null +++ b/OliverBooth.Common/OliverBooth.Common.csproj @@ -0,0 +1,16 @@ + + + + net8.0 + enable + enable + + + + + + + + + + diff --git a/OliverBooth/Services/IBlogPostService.cs b/OliverBooth.Common/Services/IBlogPostService.cs similarity index 98% rename from OliverBooth/Services/IBlogPostService.cs rename to OliverBooth.Common/Services/IBlogPostService.cs index 5947f19..29b13e3 100644 --- a/OliverBooth/Services/IBlogPostService.cs +++ b/OliverBooth.Common/Services/IBlogPostService.cs @@ -1,7 +1,7 @@ using System.Diagnostics.CodeAnalysis; -using OliverBooth.Data.Blog; +using OliverBooth.Common.Data.Blog; -namespace OliverBooth.Services; +namespace OliverBooth.Common.Services; /// /// Represents a service for managing blog posts. diff --git a/OliverBooth/Services/IBlogUserService.cs b/OliverBooth.Common/Services/IBlogUserService.cs similarity index 90% rename from OliverBooth/Services/IBlogUserService.cs rename to OliverBooth.Common/Services/IBlogUserService.cs index 0a117ae..83577e3 100644 --- a/OliverBooth/Services/IBlogUserService.cs +++ b/OliverBooth.Common/Services/IBlogUserService.cs @@ -1,7 +1,7 @@ using System.Diagnostics.CodeAnalysis; -using OliverBooth.Data.Blog; +using OliverBooth.Common.Data.Blog; -namespace OliverBooth.Services; +namespace OliverBooth.Common.Services; /// /// Represents a service for managing users. diff --git a/OliverBooth/Services/ICodeSnippetService.cs b/OliverBooth.Common/Services/ICodeSnippetService.cs similarity index 95% rename from OliverBooth/Services/ICodeSnippetService.cs rename to OliverBooth.Common/Services/ICodeSnippetService.cs index b81cb88..d065ce0 100644 --- a/OliverBooth/Services/ICodeSnippetService.cs +++ b/OliverBooth.Common/Services/ICodeSnippetService.cs @@ -1,7 +1,7 @@ using System.Diagnostics.CodeAnalysis; -using OliverBooth.Data.Web; +using OliverBooth.Common.Data.Web; -namespace OliverBooth.Services; +namespace OliverBooth.Common.Services; /// /// Represents a service which can fetch multi-language code snippets. diff --git a/OliverBooth/Services/IContactService.cs b/OliverBooth.Common/Services/IContactService.cs similarity index 77% rename from OliverBooth/Services/IContactService.cs rename to OliverBooth.Common/Services/IContactService.cs index 800a3e7..38b1e9c 100644 --- a/OliverBooth/Services/IContactService.cs +++ b/OliverBooth.Common/Services/IContactService.cs @@ -1,6 +1,6 @@ -using OliverBooth.Data.Web; +using OliverBooth.Common.Data.Web; -namespace OliverBooth.Services; +namespace OliverBooth.Common.Services; /// /// Represents a service for managing contact information. diff --git a/OliverBooth/Services/IMastodonService.cs b/OliverBooth.Common/Services/IMastodonService.cs similarity index 59% rename from OliverBooth/Services/IMastodonService.cs rename to OliverBooth.Common/Services/IMastodonService.cs index 2126d46..dc38a51 100644 --- a/OliverBooth/Services/IMastodonService.cs +++ b/OliverBooth.Common/Services/IMastodonService.cs @@ -1,6 +1,6 @@ -using OliverBooth.Data.Mastodon; +using OliverBooth.Common.Data.Mastodon; -namespace OliverBooth.Services; +namespace OliverBooth.Common.Services; public interface IMastodonService { @@ -8,5 +8,5 @@ public interface IMastodonService /// Gets the latest status posted to Mastodon. /// /// The latest status. - MastodonStatus GetLatestStatus(); + IMastodonStatus GetLatestStatus(); } \ No newline at end of file diff --git a/OliverBooth.Common/Services/IProgrammingLanguageService.cs b/OliverBooth.Common/Services/IProgrammingLanguageService.cs new file mode 100644 index 0000000..dc0acd0 --- /dev/null +++ b/OliverBooth.Common/Services/IProgrammingLanguageService.cs @@ -0,0 +1,14 @@ +namespace OliverBooth.Common.Services; + +/// +/// Represents a service which can perform programming language lookup. +/// +public interface IProgrammingLanguageService +{ + /// + /// Returns the human-readable name of a language. + /// + /// The alias of the language. + /// The human-readable name, or if the name could not be found. + string GetLanguageName(string alias); +} diff --git a/OliverBooth/Services/IProjectService.cs b/OliverBooth.Common/Services/IProjectService.cs similarity index 97% rename from OliverBooth/Services/IProjectService.cs rename to OliverBooth.Common/Services/IProjectService.cs index 16ae771..66cdbaa 100644 --- a/OliverBooth/Services/IProjectService.cs +++ b/OliverBooth.Common/Services/IProjectService.cs @@ -1,7 +1,7 @@ using System.Diagnostics.CodeAnalysis; -using OliverBooth.Data.Web; +using OliverBooth.Common.Data.Web; -namespace OliverBooth.Services; +namespace OliverBooth.Common.Services; /// /// Represents a service for interacting with projects. diff --git a/OliverBooth/Services/IReadingListService.cs b/OliverBooth.Common/Services/IReadingListService.cs similarity index 85% rename from OliverBooth/Services/IReadingListService.cs rename to OliverBooth.Common/Services/IReadingListService.cs index 78cc4f2..6f59791 100644 --- a/OliverBooth/Services/IReadingListService.cs +++ b/OliverBooth.Common/Services/IReadingListService.cs @@ -1,6 +1,6 @@ -using OliverBooth.Data.Web; +using OliverBooth.Common.Data.Web; -namespace OliverBooth.Services; +namespace OliverBooth.Common.Services; /// /// Represents a service which fetches books from the reading list. diff --git a/OliverBooth/Services/ITutorialService.cs b/OliverBooth.Common/Services/ITutorialService.cs similarity index 97% rename from OliverBooth/Services/ITutorialService.cs rename to OliverBooth.Common/Services/ITutorialService.cs index 1366172..11ddc62 100644 --- a/OliverBooth/Services/ITutorialService.cs +++ b/OliverBooth.Common/Services/ITutorialService.cs @@ -1,9 +1,9 @@ using System.Diagnostics.CodeAnalysis; -using OliverBooth.Data; -using OliverBooth.Data.Blog; -using OliverBooth.Data.Web; +using OliverBooth.Common.Data; +using OliverBooth.Common.Data.Blog; +using OliverBooth.Common.Data.Web; -namespace OliverBooth.Services; +namespace OliverBooth.Common.Services; /// /// Represents a service which can retrieve tutorial articles. diff --git a/OliverBooth.Extensions.Markdig/Markdown/Callout/CalloutBlock.cs b/OliverBooth.Extensions.Markdig/Markdown/Callout/CalloutBlock.cs new file mode 100644 index 0000000..fdf484a --- /dev/null +++ b/OliverBooth.Extensions.Markdig/Markdown/Callout/CalloutBlock.cs @@ -0,0 +1,43 @@ +using Markdig.Helpers; +using Markdig.Syntax; + +namespace OliverBooth.Extensions.Markdig.Markdown.Callout; + +/// +/// Represents a callout block. +/// +internal sealed class CalloutBlock : QuoteBlock +{ + /// + /// Initializes a new instance of the class. + /// + /// The type of the callout. + public CalloutBlock(StringSlice type) : base(null) + { + Type = type; + } + + /// + /// Gets or sets a value indicating whether this callout is foldable. + /// + /// if this callout is foldable; otherwise, . + public bool Foldable { get; set; } + + /// + /// Gets or sets the title of the callout. + /// + /// The title of the callout. + public StringSlice Title { get; set; } + + /// + /// Gets or sets the trailing whitespace trivia. + /// + /// The trailing whitespace trivia. + public StringSlice TrailingWhitespaceTrivia { get; set; } + + /// + /// Gets or sets the type of the callout. + /// + /// The type of the callout. + public StringSlice Type { get; set; } +} diff --git a/OliverBooth.Extensions.Markdig/Markdown/Callout/CalloutExtension.cs b/OliverBooth.Extensions.Markdig/Markdown/Callout/CalloutExtension.cs new file mode 100644 index 0000000..eda4718 --- /dev/null +++ b/OliverBooth.Extensions.Markdig/Markdown/Callout/CalloutExtension.cs @@ -0,0 +1,32 @@ +using Markdig; +using Markdig.Parsers.Inlines; +using Markdig.Renderers; +using Markdig.Renderers.Html; + +namespace OliverBooth.Extensions.Markdig.Markdown.Callout; + +/// +/// Extension for adding Obsidian-style callouts to a Markdown pipeline. +/// +internal sealed class CalloutExtension : IMarkdownExtension +{ + /// + public void Setup(MarkdownPipelineBuilder pipeline) + { + var parser = pipeline.InlineParsers.Find(); + if (parser is null) + { + pipeline.InlineParsers.InsertBefore(new CalloutInlineParser()); + } + } + + /// + public void Setup(MarkdownPipeline pipeline, IMarkdownRenderer renderer) + { + var blockRenderer = renderer.ObjectRenderers.FindExact(); + if (blockRenderer is null) + { + renderer.ObjectRenderers.InsertBefore(new CalloutRenderer(pipeline)); + } + } +} diff --git a/OliverBooth.Extensions.Markdig/Markdown/Callout/CalloutInlineParser.cs b/OliverBooth.Extensions.Markdig/Markdown/Callout/CalloutInlineParser.cs new file mode 100644 index 0000000..9434011 --- /dev/null +++ b/OliverBooth.Extensions.Markdig/Markdown/Callout/CalloutInlineParser.cs @@ -0,0 +1,176 @@ +using System.Reflection; +using Cysharp.Text; +using Markdig.Helpers; +using Markdig.Parsers; +using Markdig.Renderers.Html; +using Markdig.Syntax; + +namespace OliverBooth.Extensions.Markdig.Markdown.Callout; + +/// +/// An inline parser for Obsidian-style callouts ([!NOTE] etc.) +/// +internal sealed class CalloutInlineParser : InlineParser +{ + // ugly hack to access internal method + private static readonly MethodInfo ReplaceParentContainerMethod = + typeof(InlineProcessor).GetMethod("ReplaceParentContainer", BindingFlags.Instance | BindingFlags.NonPublic)!; + + /// + /// Initializes a new instance of the class. + /// + public CalloutInlineParser() + { + OpeningCharacters = ['[']; + } + + /// + public override bool Match(InlineProcessor processor, ref StringSlice slice) + { + // We expect the alert to be the first child of a quote block. Example: + // > [!NOTE] + // > This is a note + if (processor.Block is not ParagraphBlock { Parent: QuoteBlock quoteBlock } paragraphBlock || + paragraphBlock.Inline?.FirstChild != null) + { + return false; + } + + StringSlice cache = slice; + char current = slice.NextChar(); + + if (current != '!') + { + slice = cache; + return false; + } + + current = slice.NextChar(); // skip ! + + int start = slice.Start; + int end = start; + + while (current.IsAlphaUpper()) + { + end = slice.Start; + current = slice.NextChar(); + } + + if (current != ']' || start == end) + { + slice = cache; + return false; + } + + var type = new StringSlice(slice.Text, start, end); + current = slice.NextChar(); // skip ] + start = slice.Start; + + bool fold = false; + if (current == '-') + { + fold = true; + current = slice.NextChar(); // skip - + start = slice.Start; + } + + ReadTitle(current, ref slice, out StringSlice title, out end); + + var callout = new CalloutBlock(type) + { + Foldable = fold, + Span = quoteBlock.Span, + TrailingWhitespaceTrivia = new StringSlice(slice.Text, start, end), + Line = quoteBlock.Line, + Column = quoteBlock.Column, + Title = title + }; + + AddAttributes(callout, type); + ReplaceQuoteBlock(processor, quoteBlock, callout); + return true; + } + + private static void ReadTitle(char startChar, ref StringSlice slice, out StringSlice title, out int end) + { + using Utf16ValueStringBuilder builder = ZString.CreateStringBuilder(); + + char current = startChar; + while (true) + { + if (current is not ('\0' or '\r' or '\n')) + { + builder.Append(current); + current = slice.NextChar(); + continue; + } + + end = slice.Start; + if (HandleCharacter(ref slice, ref end, ref current)) + { + continue; + } + + break; + } + + title = new StringSlice(builder.ToString(), 0, builder.Length); + } + + private static bool HandleCharacter(ref StringSlice slice, ref int end, ref char current) + { + switch (current) + { + case '\r': + current = slice.NextChar(); // skip \r + + if (current is not ('\0' or '\n')) + { + return true; + } + + end = slice.Start; + if (current == '\n') + { + slice.NextChar(); // skip \n + } + + break; + + case '\n': + slice.NextChar(); // skip \n + break; + } + + return false; + } + + private static void AddAttributes(IMarkdownObject callout, StringSlice type) + { + HtmlAttributes attributes = callout.GetAttributes(); + attributes.AddClass("callout"); + attributes.AddProperty("data-callout", type.AsSpan().ToString().ToLowerInvariant()); + } + + private static void ReplaceQuoteBlock(InlineProcessor processor, QuoteBlock quoteBlock, CalloutBlock callout) + { + ContainerBlock? parentQuoteBlock = quoteBlock.Parent; + if (parentQuoteBlock is null) + { + return; + } + + int indexOfQuoteBlock = parentQuoteBlock.IndexOf(quoteBlock); + parentQuoteBlock[indexOfQuoteBlock] = callout; + + while (quoteBlock.Count > 0) + { + var block = quoteBlock[0]; + quoteBlock.RemoveAt(0); + callout.Add(block); + } + + ReplaceParentContainerMethod.Invoke(processor, [quoteBlock, callout]); + // ReplaceParentContainer(processor, quoteBlock, callout); + } +} diff --git a/OliverBooth.Extensions.Markdig/Markdown/Callout/CalloutRenderer.cs b/OliverBooth.Extensions.Markdig/Markdown/Callout/CalloutRenderer.cs new file mode 100644 index 0000000..49dd2f2 --- /dev/null +++ b/OliverBooth.Extensions.Markdig/Markdown/Callout/CalloutRenderer.cs @@ -0,0 +1,119 @@ +using HtmlAgilityPack; +using Humanizer; +using Markdig; +using Markdig.Renderers; +using Markdig.Renderers.Html; + +namespace OliverBooth.Extensions.Markdig.Markdown.Callout; + +/// +/// Represents an HTML renderer which renders a . +/// +internal sealed class CalloutRenderer : HtmlObjectRenderer +{ + private readonly MarkdownPipeline _pipeline; + + private static readonly Dictionary CalloutTypes = new() + { + ["NOTE"] = "pencil", + ["ABSTRACT"] = "clipboard-list", + ["INFO"] = "info", + ["TODO"] = "circle-check", + ["TIP"] = "flame", + ["IMPORTANT"] = "flame", + ["SUCCESS"] = "check", + ["QUESTION"] = "circle-help", + ["WARNING"] = "triangle-alert", + ["FAILURE"] = "x", + ["DANGER"] = "zap", + ["BUG"] = "bug", + ["EXAMPLE"] = "list", + ["CITE"] = "quote", + ["UPDATE"] = "calendar-check", + }; + + public CalloutRenderer(MarkdownPipeline pipeline) + { + _pipeline = pipeline; + } + + /// + protected override void Write(HtmlRenderer renderer, CalloutBlock block) + { + renderer.EnsureLine(); + if (renderer.EnableHtmlForBlock) + { + RenderAsHtml(renderer, block, _pipeline); + } + else + { + RenderAsText(renderer, block); + } + + renderer.EnsureLine(); + } + + private static void RenderAsHtml(HtmlRenderer renderer, CalloutBlock block, MarkdownPipeline pipeline) + { + string title = block.Title.Text; + ReadOnlySpan type = block.Type.AsSpan(); + Span upperType = stackalloc char[type.Length]; + type.ToUpperInvariant(upperType); + + if (!CalloutTypes.TryGetValue(upperType.ToString(), out string? lucideClass)) + { + lucideClass = "pencil"; + } + + var typeString = type.ToString().ToLowerInvariant(); + + renderer.Write($"
'); + renderer.Write("
"); + + string calloutTitle = title.Length == 0 ? typeString.Humanize(LetterCasing.Sentence) : title; + WriteTitle(renderer, pipeline, calloutTitle); + + if (block.Foldable) + { + renderer.Write(""); + } + + renderer.WriteLine("
"); + renderer.Write("
"); + renderer.WriteChildren(block); + renderer.WriteLine("
"); + renderer.WriteLine("
"); + renderer.EnsureLine(); + } + + private static void WriteTitle(TextRendererBase renderer, MarkdownPipeline pipeline, string calloutTitle) + { + string html = global::Markdig.Markdown.ToHtml(calloutTitle, pipeline); + var document = new HtmlDocument(); + document.LoadHtml(html); + if (document.DocumentNode.FirstChild is { Name: "p" } child) + { + // ugly hack to remove

tag generated by Markdig + document.DocumentNode.InnerHtml = child.InnerHtml; + } + + document.Save(renderer.Writer); + } + + private static void RenderAsText(HtmlRenderer renderer, CalloutBlock block) + { + string title = block.Title.Text; + ReadOnlySpan type = block.Type.AsSpan(); + renderer.WriteLine(title.Length == 0 ? type.ToString().ToUpperInvariant() : title.ToUpperInvariant()); + renderer.WriteChildren(block); + renderer.EnsureLine(); + } +} diff --git a/OliverBooth.Extensions.Markdig/Markdown/MarkdownExtensions.cs b/OliverBooth.Extensions.Markdig/Markdown/MarkdownExtensions.cs new file mode 100644 index 0000000..cd54de4 --- /dev/null +++ b/OliverBooth.Extensions.Markdig/Markdown/MarkdownExtensions.cs @@ -0,0 +1,21 @@ +using Markdig; +using OliverBooth.Extensions.Markdig.Markdown.Callout; + +namespace OliverBooth.Extensions.Markdig.Markdown; + +///

+/// Extension methods for . +/// +internal static class MarkdownExtensions +{ + /// + /// Uses this extension to enable Obsidian-style callouts. + /// + /// The pipeline. + /// The modified pipeline. + public static MarkdownPipelineBuilder UseCallouts(this MarkdownPipelineBuilder pipeline) + { + pipeline.Extensions.AddIfNotAlready(); + return pipeline; + } +} diff --git a/OliverBooth/Markdown/Template/TemplateExtension.cs b/OliverBooth.Extensions.Markdig/Markdown/Template/TemplateExtension.cs similarity index 90% rename from OliverBooth/Markdown/Template/TemplateExtension.cs rename to OliverBooth.Extensions.Markdig/Markdown/Template/TemplateExtension.cs index ec1ea0d..c160735 100644 --- a/OliverBooth/Markdown/Template/TemplateExtension.cs +++ b/OliverBooth.Extensions.Markdig/Markdown/Template/TemplateExtension.cs @@ -1,8 +1,8 @@ using Markdig; using Markdig.Renderers; -using OliverBooth.Services; +using OliverBooth.Extensions.Markdig.Services; -namespace OliverBooth.Markdown.Template; +namespace OliverBooth.Extensions.Markdig.Markdown.Template; /// /// Represents a Markdown extension that adds support for MediaWiki-style templates. diff --git a/OliverBooth/Markdown/Template/TemplateInline.cs b/OliverBooth.Extensions.Markdig/Markdown/Template/TemplateInline.cs similarity index 95% rename from OliverBooth/Markdown/Template/TemplateInline.cs rename to OliverBooth.Extensions.Markdig/Markdown/Template/TemplateInline.cs index a50f3b8..6b19845 100644 --- a/OliverBooth/Markdown/Template/TemplateInline.cs +++ b/OliverBooth.Extensions.Markdig/Markdown/Template/TemplateInline.cs @@ -1,6 +1,6 @@ using Markdig.Syntax.Inlines; -namespace OliverBooth.Markdown.Template; +namespace OliverBooth.Extensions.Markdig.Markdown.Template; /// /// Represents a Markdown inline element that represents a MediaWiki-style template. diff --git a/OliverBooth/Markdown/Template/TemplateInlineParser.cs b/OliverBooth.Extensions.Markdig/Markdown/Template/TemplateInlineParser.cs similarity index 99% rename from OliverBooth/Markdown/Template/TemplateInlineParser.cs rename to OliverBooth.Extensions.Markdig/Markdown/Template/TemplateInlineParser.cs index 57385b3..953f415 100644 --- a/OliverBooth/Markdown/Template/TemplateInlineParser.cs +++ b/OliverBooth.Extensions.Markdig/Markdown/Template/TemplateInlineParser.cs @@ -2,7 +2,7 @@ using Cysharp.Text; using Markdig.Helpers; using Markdig.Parsers; -namespace OliverBooth.Markdown.Template; +namespace OliverBooth.Extensions.Markdig.Markdown.Template; /// /// Represents a Markdown inline parser that handles MediaWiki-style templates. diff --git a/OliverBooth/Markdown/Template/TemplateRenderer.cs b/OliverBooth.Extensions.Markdig/Markdown/Template/TemplateRenderer.cs similarity index 88% rename from OliverBooth/Markdown/Template/TemplateRenderer.cs rename to OliverBooth.Extensions.Markdig/Markdown/Template/TemplateRenderer.cs index 9e70fb2..1c71480 100644 --- a/OliverBooth/Markdown/Template/TemplateRenderer.cs +++ b/OliverBooth.Extensions.Markdig/Markdown/Template/TemplateRenderer.cs @@ -1,8 +1,8 @@ using Markdig.Renderers; using Markdig.Renderers.Html; -using OliverBooth.Services; +using OliverBooth.Extensions.Markdig.Services; -namespace OliverBooth.Markdown.Template; +namespace OliverBooth.Extensions.Markdig.Markdown.Template; /// /// Represents a Markdown object renderer that handles elements. diff --git a/OliverBooth.Extensions.Markdig/Markdown/Timestamp/TimestampExtension.cs b/OliverBooth.Extensions.Markdig/Markdown/Timestamp/TimestampExtension.cs new file mode 100644 index 0000000..0a9dac3 --- /dev/null +++ b/OliverBooth.Extensions.Markdig/Markdown/Timestamp/TimestampExtension.cs @@ -0,0 +1,25 @@ +using Markdig; +using Markdig.Renderers; + +namespace OliverBooth.Extensions.Markdig.Markdown.Timestamp; + +/// +/// Represents a Markdig extension that supports Discord-style timestamps. +/// +public class TimestampExtension : IMarkdownExtension +{ + /// + public void Setup(MarkdownPipelineBuilder pipeline) + { + pipeline.InlineParsers.AddIfNotAlready(); + } + + /// + public void Setup(MarkdownPipeline pipeline, IMarkdownRenderer renderer) + { + if (renderer is HtmlRenderer htmlRenderer) + { + htmlRenderer.ObjectRenderers.AddIfNotAlready(); + } + } +} diff --git a/OliverBooth.Extensions.Markdig/Markdown/Timestamp/TimestampFormat.cs b/OliverBooth.Extensions.Markdig/Markdown/Timestamp/TimestampFormat.cs new file mode 100644 index 0000000..49530be --- /dev/null +++ b/OliverBooth.Extensions.Markdig/Markdown/Timestamp/TimestampFormat.cs @@ -0,0 +1,42 @@ +namespace OliverBooth.Extensions.Markdig.Markdown.Timestamp; + +/// +/// An enumeration of timestamp formats. +/// +public enum TimestampFormat +{ + /// + /// Short time format. Example: 12:00 + /// + ShortTime = 't', + + /// + /// Long time format. Example: 12:00:00 + /// + LongTime = 'T', + + /// + /// Short date format. Example: 1/1/2000 + /// + ShortDate = 'd', + + /// + /// Long date format. Example: 1 January 2000 + /// + LongDate = 'D', + + /// + /// Short date/time format. Example: 1 January 2000 at 12:00 + /// + LongDateShortTime = 'f', + + /// + /// Long date/time format. Example: Saturday, 1 January 2000 at 12:00 + /// + LongDateTime = 'F', + + /// + /// Relative date/time format. Example: 1 second ago + /// + Relative = 'R', +} diff --git a/OliverBooth.Extensions.Markdig/Markdown/Timestamp/TimestampInline.cs b/OliverBooth.Extensions.Markdig/Markdown/Timestamp/TimestampInline.cs new file mode 100644 index 0000000..d780ad9 --- /dev/null +++ b/OliverBooth.Extensions.Markdig/Markdown/Timestamp/TimestampInline.cs @@ -0,0 +1,21 @@ +using Markdig.Syntax.Inlines; + +namespace OliverBooth.Extensions.Markdig.Markdown.Timestamp; + +/// +/// Represents a Markdown inline element that contains a timestamp. +/// +public sealed class TimestampInline : Inline +{ + /// + /// Gets or sets the format. + /// + /// The format. + public TimestampFormat Format { get; set; } + + /// + /// Gets or sets the timestamp. + /// + /// The timestamp. + public DateTimeOffset Timestamp { get; set; } +} diff --git a/OliverBooth.Extensions.Markdig/Markdown/Timestamp/TimestampInlineParser.cs b/OliverBooth.Extensions.Markdig/Markdown/Timestamp/TimestampInlineParser.cs new file mode 100644 index 0000000..ceaaec5 --- /dev/null +++ b/OliverBooth.Extensions.Markdig/Markdown/Timestamp/TimestampInlineParser.cs @@ -0,0 +1,91 @@ +using Markdig.Helpers; +using Markdig.Parsers; + +namespace OliverBooth.Extensions.Markdig.Markdown.Timestamp; + +/// +/// Represents a Markdown inline parser that matches Discord-style timestamps. +/// +public sealed class TimestampInlineParser : InlineParser +{ + /// + /// Initializes a new instance of the class. + /// + public TimestampInlineParser() + { + OpeningCharacters = new[] { '<' }; + } + + /// + public override bool Match(InlineProcessor processor, ref StringSlice slice) + { + // Previous char must be a space + if (!slice.PeekCharExtra(-1).IsWhiteSpaceOrZero()) + { + return false; + } + + ReadOnlySpan span = slice.Text.AsSpan(slice.Start, slice.Length); + + if (!TryConsumeTimestamp(span, out ReadOnlySpan rawTimestamp, out char format)) + { + return false; + } + + if (!long.TryParse(rawTimestamp, out long timestamp)) + { + return false; + } + + bool hasFormat = format != '\0'; + processor.Inline = new TimestampInline + { + Format = (TimestampFormat)format, + Timestamp = DateTimeOffset.FromUnixTimeSeconds(timestamp) + }; + + int paddingCount = hasFormat ? 6 : 4; // or optionally + slice.Start += rawTimestamp.Length + paddingCount; + return true; + } + + private bool TryConsumeTimestamp(ReadOnlySpan source, + out ReadOnlySpan timestamp, + out char format) + { + timestamp = default; + format = default; + + if (!source.StartsWith("') == -1) + { + timestamp = default; + return false; + } + + int delimiterIndex = timestamp.IndexOf(':'); + if (delimiterIndex == 0) + { + // invalid format + timestamp = default; + return false; + } + + if (delimiterIndex == -1) + { + // no format, default to relative + format = 'R'; + timestamp = timestamp[..^1]; // trim > + } + else + { + // use specified format + format = timestamp[^2]; + timestamp = timestamp[..^3]; + } + + return true; + } +} diff --git a/OliverBooth.Extensions.Markdig/Markdown/Timestamp/TimestampRenderer.cs b/OliverBooth.Extensions.Markdig/Markdown/Timestamp/TimestampRenderer.cs new file mode 100644 index 0000000..ceda5ac --- /dev/null +++ b/OliverBooth.Extensions.Markdig/Markdown/Timestamp/TimestampRenderer.cs @@ -0,0 +1,55 @@ +using System.ComponentModel; +using Humanizer; +using Markdig.Renderers; +using Markdig.Renderers.Html; + +namespace OliverBooth.Extensions.Markdig.Markdown.Timestamp; + +/// +/// Represents a Markdown object renderer that renders elements. +/// +public sealed class TimestampRenderer : HtmlObjectRenderer +{ + /// + protected override void Write(HtmlRenderer renderer, TimestampInline obj) + { + DateTimeOffset timestamp = obj.Timestamp; + TimestampFormat format = obj.Format; + + renderer.Write(""); + + switch (format) + { + case TimestampFormat.LongDate: + renderer.Write(timestamp.ToString("d MMMM yyyy")); + break; + + case TimestampFormat.LongDateShortTime: + renderer.Write(timestamp.ToString(@"d MMMM yyyy \a\t HH:mm")); + break; + + case TimestampFormat.LongDateTime: + renderer.Write(timestamp.ToString(@"dddd, d MMMM yyyy \a\t HH:mm")); + break; + + case TimestampFormat.Relative: + renderer.Write(timestamp.Humanize()); + break; + + case var _ when !Enum.IsDefined(format): + throw new InvalidEnumArgumentException(nameof(format), (int)format, typeof(TimestampFormat)); + + default: + renderer.Write(timestamp.ToString(((char)format).ToString())); + break; + } + + renderer.Write(""); + } +} diff --git a/OliverBooth.Extensions.Markdig/MarkdownPipelineExtensions.cs b/OliverBooth.Extensions.Markdig/MarkdownPipelineExtensions.cs new file mode 100644 index 0000000..20ea173 --- /dev/null +++ b/OliverBooth.Extensions.Markdig/MarkdownPipelineExtensions.cs @@ -0,0 +1,38 @@ +using Markdig; +using OliverBooth.Extensions.Markdig.Markdown.Template; +using OliverBooth.Extensions.Markdig.Services; + +namespace OliverBooth.Extensions.Markdig; + +/// +/// Extension methods for . +/// +public static class MarkdownPipelineExtensions +{ + /// + /// Enables the use of Wiki-style templates in this pipeline. + /// + /// The Markdig markdown pipeline builder. + /// The template service responsible for fetching and rendering templates. + /// The modified Markdig markdown pipeline builder. + /// + /// is . + /// -or- + /// is . + /// + public static MarkdownPipelineBuilder UseTemplates(this MarkdownPipelineBuilder builder, ITemplateService templateService) + { + if (builder is null) + { + throw new ArgumentNullException(nameof(builder)); + } + + if (templateService is null) + { + throw new ArgumentNullException(nameof(templateService)); + } + + builder.Use(new TemplateExtension(templateService)); + return builder; + } +} diff --git a/OliverBooth.Extensions.Markdig/OliverBooth.Extensions.Markdig.csproj b/OliverBooth.Extensions.Markdig/OliverBooth.Extensions.Markdig.csproj new file mode 100644 index 0000000..9a76817 --- /dev/null +++ b/OliverBooth.Extensions.Markdig/OliverBooth.Extensions.Markdig.csproj @@ -0,0 +1,13 @@ + + + + net8.0 + enable + enable + + + + + + + diff --git a/OliverBooth/Services/ITemplateService.cs b/OliverBooth.Extensions.Markdig/Services/ITemplateService.cs similarity index 94% rename from OliverBooth/Services/ITemplateService.cs rename to OliverBooth.Extensions.Markdig/Services/ITemplateService.cs index 5fa9239..30c8b86 100644 --- a/OliverBooth/Services/ITemplateService.cs +++ b/OliverBooth.Extensions.Markdig/Services/ITemplateService.cs @@ -1,8 +1,8 @@ using System.Diagnostics.CodeAnalysis; -using OliverBooth.Data.Web; -using OliverBooth.Markdown.Template; +using OliverBooth.Common.Data.Web; +using OliverBooth.Extensions.Markdig.Markdown.Template; -namespace OliverBooth.Services; +namespace OliverBooth.Extensions.Markdig.Services; /// /// Represents a service that renders MediaWiki-style templates. diff --git a/OliverBooth.sln b/OliverBooth.sln index e2ba2dd..ea184df 100644 --- a/OliverBooth.sln +++ b/OliverBooth.sln @@ -13,6 +13,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution global.json = global.json EndProjectSection EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OliverBooth.Extensions.Markdig", "OliverBooth.Extensions.Markdig\OliverBooth.Extensions.Markdig.csproj", "{3B012CD2-3201-41A0-BEF9-8E0B6247BB7E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OliverBooth.Common", "OliverBooth.Common\OliverBooth.Common.csproj", "{AD231E0F-FAED-4661-963F-EB22F858E148}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -23,6 +27,14 @@ Global {A58A6FA3-480C-400B-822A-3786741BF39C}.Debug|Any CPU.Build.0 = Debug|Any CPU {A58A6FA3-480C-400B-822A-3786741BF39C}.Release|Any CPU.ActiveCfg = Release|Any CPU {A58A6FA3-480C-400B-822A-3786741BF39C}.Release|Any CPU.Build.0 = Release|Any CPU + {3B012CD2-3201-41A0-BEF9-8E0B6247BB7E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3B012CD2-3201-41A0-BEF9-8E0B6247BB7E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3B012CD2-3201-41A0-BEF9-8E0B6247BB7E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3B012CD2-3201-41A0-BEF9-8E0B6247BB7E}.Release|Any CPU.Build.0 = Release|Any CPU + {AD231E0F-FAED-4661-963F-EB22F858E148}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {AD231E0F-FAED-4661-963F-EB22F858E148}.Debug|Any CPU.Build.0 = Debug|Any CPU + {AD231E0F-FAED-4661-963F-EB22F858E148}.Release|Any CPU.ActiveCfg = Release|Any CPU + {AD231E0F-FAED-4661-963F-EB22F858E148}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(NestedProjects) = preSolution EndGlobalSection diff --git a/OliverBooth/Controllers/Blog/BlogApiController.cs b/OliverBooth/Controllers/Blog/BlogApiController.cs index 21eb070..98674e3 100644 --- a/OliverBooth/Controllers/Blog/BlogApiController.cs +++ b/OliverBooth/Controllers/Blog/BlogApiController.cs @@ -1,7 +1,7 @@ using Humanizer; using Microsoft.AspNetCore.Mvc; -using OliverBooth.Data.Blog; -using OliverBooth.Services; +using OliverBooth.Common.Data.Blog; +using OliverBooth.Common.Services; namespace OliverBooth.Controllers.Blog; diff --git a/OliverBooth/Controllers/Blog/RssController.cs b/OliverBooth/Controllers/Blog/RssController.cs index 83b1a08..04cd16d 100644 --- a/OliverBooth/Controllers/Blog/RssController.cs +++ b/OliverBooth/Controllers/Blog/RssController.cs @@ -1,8 +1,8 @@ using System.Xml.Serialization; using Microsoft.AspNetCore.Mvc; -using OliverBooth.Data.Blog; +using OliverBooth.Common.Data.Blog; +using OliverBooth.Common.Services; using OliverBooth.Data.Blog.Rss; -using OliverBooth.Services; namespace OliverBooth.Controllers.Blog; diff --git a/OliverBooth/Controllers/FormattedBlacklist.cs b/OliverBooth/Controllers/FormattedBlacklist.cs index bce6143..5186ec9 100644 --- a/OliverBooth/Controllers/FormattedBlacklist.cs +++ b/OliverBooth/Controllers/FormattedBlacklist.cs @@ -1,7 +1,7 @@ using System.Text; using Microsoft.AspNetCore.Mvc; -using OliverBooth.Data.Web; -using OliverBooth.Services; +using OliverBooth.Common.Data.Web; +using OliverBooth.Common.Services; namespace OliverBooth.Controllers; diff --git a/OliverBooth/Data/Blog/BlogPost.cs b/OliverBooth/Data/Blog/BlogPost.cs index 59067b5..f84292a 100644 --- a/OliverBooth/Data/Blog/BlogPost.cs +++ b/OliverBooth/Data/Blog/BlogPost.cs @@ -1,4 +1,6 @@ using System.ComponentModel.DataAnnotations.Schema; +using OliverBooth.Common.Data; +using OliverBooth.Common.Data.Blog; using SmartFormat; namespace OliverBooth.Data.Blog; diff --git a/OliverBooth/Data/Blog/Configuration/BlogPostConfiguration.cs b/OliverBooth/Data/Blog/Configuration/BlogPostConfiguration.cs index 1ecde2f..fd7085b 100644 --- a/OliverBooth/Data/Blog/Configuration/BlogPostConfiguration.cs +++ b/OliverBooth/Data/Blog/Configuration/BlogPostConfiguration.cs @@ -1,6 +1,7 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Metadata.Builders; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using OliverBooth.Common.Data; namespace OliverBooth.Data.Blog.Configuration; diff --git a/OliverBooth/Data/Blog/LegacyComment.cs b/OliverBooth/Data/Blog/LegacyComment.cs index a020979..cd8b615 100644 --- a/OliverBooth/Data/Blog/LegacyComment.cs +++ b/OliverBooth/Data/Blog/LegacyComment.cs @@ -1,4 +1,5 @@ using System.Web; +using OliverBooth.Common.Data.Blog; namespace OliverBooth.Data.Blog; diff --git a/OliverBooth/Data/Blog/User.cs b/OliverBooth/Data/Blog/User.cs index dcd50ab..1b6ce40 100644 --- a/OliverBooth/Data/Blog/User.cs +++ b/OliverBooth/Data/Blog/User.cs @@ -2,6 +2,7 @@ using System.ComponentModel.DataAnnotations.Schema; using System.Security.Cryptography; using System.Text; using Cysharp.Text; +using OliverBooth.Common.Data.Blog; namespace OliverBooth.Data.Blog; diff --git a/OliverBooth/Data/Mastodon/MastodonStatus.cs b/OliverBooth/Data/Mastodon/MastodonStatus.cs index a4d3ebf..4276c02 100644 --- a/OliverBooth/Data/Mastodon/MastodonStatus.cs +++ b/OliverBooth/Data/Mastodon/MastodonStatus.cs @@ -1,34 +1,24 @@ using System.Text.Json.Serialization; +using OliverBooth.Common.Data.Mastodon; namespace OliverBooth.Data.Mastodon; -public sealed class MastodonStatus +/// +internal sealed class MastodonStatus : IMastodonStatus { - /// - /// Gets the content of the status. - /// - /// The content. + /// [JsonPropertyName("content")] public string Content { get; set; } = string.Empty; - /// - /// Gets the date and time at which this status was posted. - /// - /// The post timestamp. + /// [JsonPropertyName("created_at")] public DateTimeOffset CreatedAt { get; set; } - /// - /// Gets the media attachments for this status. - /// - /// The media attachments. + /// [JsonPropertyName("media_attachments")] - public IReadOnlyList MediaAttachments { get; set; } = ArraySegment.Empty; + public IReadOnlyList MediaAttachments { get; set; } = ArraySegment.Empty; - /// - /// Gets the original URI of the status. - /// - /// The original URI. + /// [JsonPropertyName("url")] public Uri OriginalUri { get; set; } = null!; } diff --git a/OliverBooth/Data/Web/BlacklistEntry.cs b/OliverBooth/Data/Web/BlacklistEntry.cs index 374d9c2..a0f68d3 100644 --- a/OliverBooth/Data/Web/BlacklistEntry.cs +++ b/OliverBooth/Data/Web/BlacklistEntry.cs @@ -1,3 +1,5 @@ +using OliverBooth.Common.Data.Web; + namespace OliverBooth.Data.Web; /// diff --git a/OliverBooth/Data/Web/Book.cs b/OliverBooth/Data/Web/Book.cs index 278aa4e..10e41dd 100644 --- a/OliverBooth/Data/Web/Book.cs +++ b/OliverBooth/Data/Web/Book.cs @@ -1,4 +1,5 @@ using NetBarcode; +using OliverBooth.Common.Data.Web; using SixLabors.ImageSharp; using SixLabors.ImageSharp.Formats.Png; using SixLabors.ImageSharp.Processing; diff --git a/OliverBooth/Data/Web/CodeSnippet.cs b/OliverBooth/Data/Web/CodeSnippet.cs index 19d29c1..13fb751 100644 --- a/OliverBooth/Data/Web/CodeSnippet.cs +++ b/OliverBooth/Data/Web/CodeSnippet.cs @@ -1,3 +1,5 @@ +using OliverBooth.Common.Data.Web; + namespace OliverBooth.Data.Web; /// diff --git a/OliverBooth/Data/Web/Configuration/BookConfiguration.cs b/OliverBooth/Data/Web/Configuration/BookConfiguration.cs index f27a1d2..fca84f9 100644 --- a/OliverBooth/Data/Web/Configuration/BookConfiguration.cs +++ b/OliverBooth/Data/Web/Configuration/BookConfiguration.cs @@ -1,6 +1,7 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Metadata.Builders; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using OliverBooth.Common.Data.Web; namespace OliverBooth.Data.Web.Configuration; diff --git a/OliverBooth/Data/Web/Configuration/ProjectConfiguration.cs b/OliverBooth/Data/Web/Configuration/ProjectConfiguration.cs index 08ef5d9..8949763 100644 --- a/OliverBooth/Data/Web/Configuration/ProjectConfiguration.cs +++ b/OliverBooth/Data/Web/Configuration/ProjectConfiguration.cs @@ -1,6 +1,7 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Metadata.Builders; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using OliverBooth.Common.Data.Web; namespace OliverBooth.Data.Web.Configuration; diff --git a/OliverBooth/Data/Web/Configuration/TutorialArticleConfiguration.cs b/OliverBooth/Data/Web/Configuration/TutorialArticleConfiguration.cs index b86a5b5..4caf190 100644 --- a/OliverBooth/Data/Web/Configuration/TutorialArticleConfiguration.cs +++ b/OliverBooth/Data/Web/Configuration/TutorialArticleConfiguration.cs @@ -1,6 +1,7 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Metadata.Builders; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using OliverBooth.Common.Data; namespace OliverBooth.Data.Web.Configuration; diff --git a/OliverBooth/Data/Web/Configuration/TutorialFolderConfiguration.cs b/OliverBooth/Data/Web/Configuration/TutorialFolderConfiguration.cs index af1eef2..136d9c3 100644 --- a/OliverBooth/Data/Web/Configuration/TutorialFolderConfiguration.cs +++ b/OliverBooth/Data/Web/Configuration/TutorialFolderConfiguration.cs @@ -1,6 +1,7 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Metadata.Builders; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using OliverBooth.Common.Data; namespace OliverBooth.Data.Web.Configuration; diff --git a/OliverBooth/Data/Web/ProgrammingLanguage.cs b/OliverBooth/Data/Web/ProgrammingLanguage.cs index 53746d9..9a4135f 100644 --- a/OliverBooth/Data/Web/ProgrammingLanguage.cs +++ b/OliverBooth/Data/Web/ProgrammingLanguage.cs @@ -1,3 +1,5 @@ +using OliverBooth.Common.Data.Web; + namespace OliverBooth.Data.Web; /// diff --git a/OliverBooth/Data/Web/Project.cs b/OliverBooth/Data/Web/Project.cs index 05761a7..37b6b9e 100644 --- a/OliverBooth/Data/Web/Project.cs +++ b/OliverBooth/Data/Web/Project.cs @@ -1,3 +1,5 @@ +using OliverBooth.Common.Data.Web; + namespace OliverBooth.Data.Web; /// diff --git a/OliverBooth/Data/Web/Template.cs b/OliverBooth/Data/Web/Template.cs index cf029a1..29355c5 100644 --- a/OliverBooth/Data/Web/Template.cs +++ b/OliverBooth/Data/Web/Template.cs @@ -1,3 +1,5 @@ +using OliverBooth.Common.Data.Web; + namespace OliverBooth.Data.Web; /// diff --git a/OliverBooth/Data/Web/TutorialArticle.cs b/OliverBooth/Data/Web/TutorialArticle.cs index 43ef096..a1f9801 100644 --- a/OliverBooth/Data/Web/TutorialArticle.cs +++ b/OliverBooth/Data/Web/TutorialArticle.cs @@ -1,4 +1,6 @@ using System.ComponentModel.DataAnnotations.Schema; +using OliverBooth.Common.Data; +using OliverBooth.Common.Data.Web; namespace OliverBooth.Data.Web; diff --git a/OliverBooth/Data/Web/TutorialFolder.cs b/OliverBooth/Data/Web/TutorialFolder.cs index 26c5bc1..1f69ef6 100644 --- a/OliverBooth/Data/Web/TutorialFolder.cs +++ b/OliverBooth/Data/Web/TutorialFolder.cs @@ -1,3 +1,6 @@ +using OliverBooth.Common.Data; +using OliverBooth.Common.Data.Web; + namespace OliverBooth.Data.Web; /// diff --git a/OliverBooth/Extensions/HtmlUtility.cs b/OliverBooth/Extensions/HtmlUtility.cs index 94e3d6a..556cb6d 100644 --- a/OliverBooth/Extensions/HtmlUtility.cs +++ b/OliverBooth/Extensions/HtmlUtility.cs @@ -1,8 +1,8 @@ using System.Web; using Cysharp.Text; -using OliverBooth.Data.Blog; -using OliverBooth.Data.Web; -using OliverBooth.Services; +using OliverBooth.Common.Data.Blog; +using OliverBooth.Common.Data.Web; +using OliverBooth.Common.Services; namespace OliverBooth.Extensions; diff --git a/OliverBooth/Markdown/Template/CodeSnippetTemplateRenderer.cs b/OliverBooth/Markdown/Template/CodeSnippetTemplateRenderer.cs index e809801..f93abd8 100644 --- a/OliverBooth/Markdown/Template/CodeSnippetTemplateRenderer.cs +++ b/OliverBooth/Markdown/Template/CodeSnippetTemplateRenderer.cs @@ -1,8 +1,9 @@ using System.Diagnostics; using System.Text; using Markdig; -using OliverBooth.Data.Web; -using OliverBooth.Services; +using OliverBooth.Common.Data.Web; +using OliverBooth.Common.Services; +using OliverBooth.Extensions.Markdig.Markdown.Template; namespace OliverBooth.Markdown.Template; diff --git a/OliverBooth/Markdown/Template/CustomTemplateRenderer.cs b/OliverBooth/Markdown/Template/CustomTemplateRenderer.cs index 1d3f122..9fbbc2a 100644 --- a/OliverBooth/Markdown/Template/CustomTemplateRenderer.cs +++ b/OliverBooth/Markdown/Template/CustomTemplateRenderer.cs @@ -1,5 +1,6 @@ using Microsoft.EntityFrameworkCore; using OliverBooth.Data.Web; +using OliverBooth.Extensions.Markdig.Markdown.Template; namespace OliverBooth.Markdown.Template; diff --git a/OliverBooth/OliverBooth.csproj b/OliverBooth/OliverBooth.csproj index 2922e7a..463e9d9 100644 --- a/OliverBooth/OliverBooth.csproj +++ b/OliverBooth/OliverBooth.csproj @@ -30,11 +30,8 @@ - - - @@ -48,7 +45,11 @@ - + + + + + diff --git a/OliverBooth/Pages/Blog/Article.cshtml b/OliverBooth/Pages/Blog/Article.cshtml index bdc3ee5..e34da53 100644 --- a/OliverBooth/Pages/Blog/Article.cshtml +++ b/OliverBooth/Pages/Blog/Article.cshtml @@ -1,9 +1,10 @@ @page "/blog/{year:int}/{month:int}/{day:int}/{slug}" @using Humanizer @using Markdig -@using OliverBooth.Data -@using OliverBooth.Data.Blog -@using OliverBooth.Services +@using Microsoft.AspNetCore.Mvc.TagHelpers +@using OliverBooth.Common.Data +@using OliverBooth.Common.Data.Blog +@using OliverBooth.Common.Services @inject IBlogPostService BlogPostService @inject MarkdownPipeline MarkdownPipeline @model Article diff --git a/OliverBooth/Pages/Blog/Article.cshtml.cs b/OliverBooth/Pages/Blog/Article.cshtml.cs index 17a9337..64d6cc8 100644 --- a/OliverBooth/Pages/Blog/Article.cshtml.cs +++ b/OliverBooth/Pages/Blog/Article.cshtml.cs @@ -1,8 +1,8 @@ using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.RazorPages; using Microsoft.Extensions.Primitives; -using OliverBooth.Data.Blog; -using OliverBooth.Services; +using OliverBooth.Common.Data.Blog; +using OliverBooth.Common.Services; using BC = BCrypt.Net.BCrypt; namespace OliverBooth.Pages.Blog; diff --git a/OliverBooth/Pages/Blog/Index.cshtml b/OliverBooth/Pages/Blog/Index.cshtml index 0229989..23024e7 100644 --- a/OliverBooth/Pages/Blog/Index.cshtml +++ b/OliverBooth/Pages/Blog/Index.cshtml @@ -1,13 +1,13 @@ @page @using Humanizer -@using OliverBooth.Data.Mastodon -@using OliverBooth.Services +@using OliverBooth.Common.Data.Mastodon +@using OliverBooth.Common.Services @model Index @inject IMastodonService MastodonService @{ ViewData["Title"] = "Blog"; - MastodonStatus latestStatus = MastodonService.GetLatestStatus(); + IMastodonStatus latestStatus = MastodonService.GetLatestStatus(); }
@@ -18,16 +18,22 @@ switch (attachment.Type) { case AttachmentType.Audio: -

+

+ +

break; case AttachmentType.Video: -

+

+ +

break; case AttachmentType.Image: case AttachmentType.GifV: -

+

+ +

break; } } diff --git a/OliverBooth/Pages/Blog/Index.cshtml.cs b/OliverBooth/Pages/Blog/Index.cshtml.cs index 97104b6..5fa0b34 100644 --- a/OliverBooth/Pages/Blog/Index.cshtml.cs +++ b/OliverBooth/Pages/Blog/Index.cshtml.cs @@ -1,7 +1,7 @@ using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.RazorPages; -using OliverBooth.Data.Blog; -using OliverBooth.Services; +using OliverBooth.Common.Data.Blog; +using OliverBooth.Common.Services; namespace OliverBooth.Pages.Blog; diff --git a/OliverBooth/Pages/Blog/RawArticle.cshtml.cs b/OliverBooth/Pages/Blog/RawArticle.cshtml.cs index 1fc34eb..adbd374 100644 --- a/OliverBooth/Pages/Blog/RawArticle.cshtml.cs +++ b/OliverBooth/Pages/Blog/RawArticle.cshtml.cs @@ -1,8 +1,8 @@ using Cysharp.Text; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.RazorPages; -using OliverBooth.Data.Blog; -using OliverBooth.Services; +using OliverBooth.Common.Data.Blog; +using OliverBooth.Common.Services; namespace OliverBooth.Pages.Blog; diff --git a/OliverBooth/Pages/Books.cshtml b/OliverBooth/Pages/Books.cshtml index dd83d7e..f7bbb79 100644 --- a/OliverBooth/Pages/Books.cshtml +++ b/OliverBooth/Pages/Books.cshtml @@ -1,5 +1,5 @@ @page -@using OliverBooth.Data.Web +@using OliverBooth.Common.Data.Web @model OliverBooth.Pages.Books @{ ViewData["Title"] = "Reading List"; diff --git a/OliverBooth/Pages/Books.cshtml.cs b/OliverBooth/Pages/Books.cshtml.cs index 6cbc5af..6668ed3 100644 --- a/OliverBooth/Pages/Books.cshtml.cs +++ b/OliverBooth/Pages/Books.cshtml.cs @@ -1,6 +1,6 @@ using Microsoft.AspNetCore.Mvc.RazorPages; -using OliverBooth.Data.Web; -using OliverBooth.Services; +using OliverBooth.Common.Data.Web; +using OliverBooth.Common.Services; namespace OliverBooth.Pages; diff --git a/OliverBooth/Pages/Contact/Blacklist.cshtml b/OliverBooth/Pages/Contact/Blacklist.cshtml index a0101ac..20feb7a 100644 --- a/OliverBooth/Pages/Contact/Blacklist.cshtml +++ b/OliverBooth/Pages/Contact/Blacklist.cshtml @@ -1,6 +1,7 @@ @page -@using OliverBooth.Data.Web -@using OliverBooth.Services +@using Microsoft.AspNetCore.Mvc.TagHelpers +@using OliverBooth.Common.Data.Web +@using OliverBooth.Common.Services @inject IContactService ContactService @{ ViewData["Title"] = "Blacklist"; diff --git a/OliverBooth/Pages/Contact/Index.cshtml b/OliverBooth/Pages/Contact/Index.cshtml index 12d5f70..95bdd86 100644 --- a/OliverBooth/Pages/Contact/Index.cshtml +++ b/OliverBooth/Pages/Contact/Index.cshtml @@ -1,4 +1,5 @@ @page +@using Microsoft.AspNetCore.Mvc.TagHelpers @{ ViewData["Title"] = "Contact"; } diff --git a/OliverBooth/Pages/Contact/Result.cshtml b/OliverBooth/Pages/Contact/Result.cshtml index 65de504..bc5b79d 100644 --- a/OliverBooth/Pages/Contact/Result.cshtml +++ b/OliverBooth/Pages/Contact/Result.cshtml @@ -1,4 +1,5 @@ @page +@using Microsoft.AspNetCore.Mvc.TagHelpers @model OliverBooth.Pages.Contact.Result @{ diff --git a/OliverBooth/Pages/Index.cshtml b/OliverBooth/Pages/Index.cshtml index 0f85b37..89821cc 100644 --- a/OliverBooth/Pages/Index.cshtml +++ b/OliverBooth/Pages/Index.cshtml @@ -1,4 +1,5 @@ @page +@using Microsoft.AspNetCore.Mvc.TagHelpers
diff --git a/OliverBooth/Pages/Privacy/FiveOClockSomewhere.cshtml b/OliverBooth/Pages/Privacy/FiveOClockSomewhere.cshtml index 5c5365a..07fe6a5 100644 --- a/OliverBooth/Pages/Privacy/FiveOClockSomewhere.cshtml +++ b/OliverBooth/Pages/Privacy/FiveOClockSomewhere.cshtml @@ -1,4 +1,5 @@ @page "/privacy/five-oclock-somewhere" +@using Microsoft.AspNetCore.Mvc.TagHelpers @{ ViewData["Title"] = "It's 5 O'Clock Somewhere Privacy Policy"; } diff --git a/OliverBooth/Pages/Privacy/GooglePlay.cshtml b/OliverBooth/Pages/Privacy/GooglePlay.cshtml index 37caca9..9d340c5 100644 --- a/OliverBooth/Pages/Privacy/GooglePlay.cshtml +++ b/OliverBooth/Pages/Privacy/GooglePlay.cshtml @@ -1,4 +1,5 @@ @page "/privacy/google-play" +@using Microsoft.AspNetCore.Mvc.TagHelpers @{ ViewData["Title"] = "Google Play Privacy Policy"; } diff --git a/OliverBooth/Pages/Privacy/Index.cshtml b/OliverBooth/Pages/Privacy/Index.cshtml index d4afbd3..d5ef608 100644 --- a/OliverBooth/Pages/Privacy/Index.cshtml +++ b/OliverBooth/Pages/Privacy/Index.cshtml @@ -1,4 +1,5 @@ @page +@using Microsoft.AspNetCore.Mvc.TagHelpers @{ ViewData["Title"] = "Privacy Policy"; } diff --git a/OliverBooth/Pages/Projects/Index.cshtml b/OliverBooth/Pages/Projects/Index.cshtml index 90468e5..a9e8f77 100644 --- a/OliverBooth/Pages/Projects/Index.cshtml +++ b/OliverBooth/Pages/Projects/Index.cshtml @@ -1,6 +1,7 @@ @page -@using OliverBooth.Data.Web -@using OliverBooth.Services +@using Microsoft.AspNetCore.Mvc.TagHelpers +@using OliverBooth.Common.Data.Web +@using OliverBooth.Common.Services @inject IProjectService ProjectService @{ ViewData["Title"] = "Projects"; diff --git a/OliverBooth/Pages/Projects/Project.cshtml b/OliverBooth/Pages/Projects/Project.cshtml index b9c9933..80141dc 100644 --- a/OliverBooth/Pages/Projects/Project.cshtml +++ b/OliverBooth/Pages/Projects/Project.cshtml @@ -1,7 +1,8 @@ @page "/project/{slug}" @using Markdig -@using OliverBooth.Data.Web -@using OliverBooth.Services +@using Microsoft.AspNetCore.Mvc.TagHelpers +@using OliverBooth.Common.Data.Web +@using OliverBooth.Common.Services @model Project @inject IProjectService ProjectService @inject MarkdownPipeline MarkdownPipeline diff --git a/OliverBooth/Pages/Projects/Project.cshtml.cs b/OliverBooth/Pages/Projects/Project.cshtml.cs index 87782ec..a762e6d 100644 --- a/OliverBooth/Pages/Projects/Project.cshtml.cs +++ b/OliverBooth/Pages/Projects/Project.cshtml.cs @@ -1,6 +1,6 @@ using Microsoft.AspNetCore.Mvc.RazorPages; -using OliverBooth.Data.Web; -using OliverBooth.Services; +using OliverBooth.Common.Data.Web; +using OliverBooth.Common.Services; namespace OliverBooth.Pages.Projects; diff --git a/OliverBooth/Pages/Shared/_Layout.cshtml b/OliverBooth/Pages/Shared/_Layout.cshtml index db84eee..4622e8f 100644 --- a/OliverBooth/Pages/Shared/_Layout.cshtml +++ b/OliverBooth/Pages/Shared/_Layout.cshtml @@ -1,7 +1,8 @@ -@using OliverBooth.Data.Blog -@using OliverBooth.Data.Web +@using Microsoft.AspNetCore.Mvc.TagHelpers +@using OliverBooth.Common.Data.Blog +@using OliverBooth.Common.Data.Web +@using OliverBooth.Common.Services @using OliverBooth.Extensions -@using OliverBooth.Services @inject IBlogPostService BlogPostService @inject ITutorialService TutorialService @{ diff --git a/OliverBooth/Pages/Shared/_MinimalLayout.cshtml b/OliverBooth/Pages/Shared/_MinimalLayout.cshtml index 48fd1a8..b40e15f 100644 --- a/OliverBooth/Pages/Shared/_MinimalLayout.cshtml +++ b/OliverBooth/Pages/Shared/_MinimalLayout.cshtml @@ -1,3 +1,4 @@ +@using Microsoft.AspNetCore.Mvc.TagHelpers diff --git a/OliverBooth/Pages/Tutorials/Article.cshtml b/OliverBooth/Pages/Tutorials/Article.cshtml index 2970273..0433dbb 100644 --- a/OliverBooth/Pages/Tutorials/Article.cshtml +++ b/OliverBooth/Pages/Tutorials/Article.cshtml @@ -2,10 +2,10 @@ @using Humanizer @using Markdig @using Microsoft.AspNetCore.Mvc.TagHelpers -@using OliverBooth.Data -@using OliverBooth.Data.Blog -@using OliverBooth.Data.Web -@using OliverBooth.Services +@using OliverBooth.Common.Data +@using OliverBooth.Common.Data.Blog +@using OliverBooth.Common.Data.Web +@using OliverBooth.Common.Services @inject ITutorialService TutorialService @inject MarkdownPipeline MarkdownPipeline @model Article diff --git a/OliverBooth/Pages/Tutorials/Article.cshtml.cs b/OliverBooth/Pages/Tutorials/Article.cshtml.cs index 7f5f6c7..2b87435 100644 --- a/OliverBooth/Pages/Tutorials/Article.cshtml.cs +++ b/OliverBooth/Pages/Tutorials/Article.cshtml.cs @@ -1,7 +1,7 @@ using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.RazorPages; -using OliverBooth.Data.Web; -using OliverBooth.Services; +using OliverBooth.Common.Data.Web; +using OliverBooth.Common.Services; namespace OliverBooth.Pages.Tutorials; diff --git a/OliverBooth/Pages/Tutorials/Index.cshtml b/OliverBooth/Pages/Tutorials/Index.cshtml index aaa2346..06f7a7a 100644 --- a/OliverBooth/Pages/Tutorials/Index.cshtml +++ b/OliverBooth/Pages/Tutorials/Index.cshtml @@ -1,8 +1,9 @@ @page "/tutorials/{**slug}" @using System.Text -@using OliverBooth.Data -@using OliverBooth.Data.Web -@using OliverBooth.Services +@using Microsoft.AspNetCore.Mvc.TagHelpers +@using OliverBooth.Common.Data +@using OliverBooth.Common.Data.Web +@using OliverBooth.Common.Services @model Index @inject ITutorialService TutorialService @{ diff --git a/OliverBooth/Pages/Tutorials/Index.cshtml.cs b/OliverBooth/Pages/Tutorials/Index.cshtml.cs index ac08919..23892fd 100644 --- a/OliverBooth/Pages/Tutorials/Index.cshtml.cs +++ b/OliverBooth/Pages/Tutorials/Index.cshtml.cs @@ -1,7 +1,7 @@ using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.RazorPages; -using OliverBooth.Data.Web; -using OliverBooth.Services; +using OliverBooth.Common.Data.Web; +using OliverBooth.Common.Services; namespace OliverBooth.Pages.Tutorials; diff --git a/OliverBooth/Program.cs b/OliverBooth/Program.cs index 6360e7a..006a3b7 100644 --- a/OliverBooth/Program.cs +++ b/OliverBooth/Program.cs @@ -1,11 +1,12 @@ using AspNetCore.ReCaptcha; using Markdig; +using OliverBooth.Common.Services; using OliverBooth.Data.Blog; using OliverBooth.Data.Web; using OliverBooth.Extensions; +using OliverBooth.Extensions.Markdig; +using OliverBooth.Extensions.Markdig.Services; using OliverBooth.Markdown; -using OliverBooth.Markdown.Callout; -using OliverBooth.Markdown.Template; using OliverBooth.Markdown.Timestamp; using OliverBooth.Services; using Serilog; @@ -25,7 +26,7 @@ builder.Logging.AddSerilog(); builder.Services.AddSingleton(provider => new MarkdownPipelineBuilder() .Use() - .Use(new TemplateExtension(provider.GetRequiredService())) + .UseTemplates(provider.GetRequiredService()) // we have our own "alert blocks" .UseCallouts() diff --git a/OliverBooth/Services/BlogPostService.cs b/OliverBooth/Services/BlogPostService.cs index 38985d3..2190f09 100644 --- a/OliverBooth/Services/BlogPostService.cs +++ b/OliverBooth/Services/BlogPostService.cs @@ -2,7 +2,9 @@ using System.Diagnostics.CodeAnalysis; using Humanizer; using Markdig; using Microsoft.EntityFrameworkCore; -using OliverBooth.Data; +using OliverBooth.Common.Data; +using OliverBooth.Common.Data.Blog; +using OliverBooth.Common.Services; using OliverBooth.Data.Blog; namespace OliverBooth.Services; diff --git a/OliverBooth/Services/BlogUserService.cs b/OliverBooth/Services/BlogUserService.cs index 9b797eb..f7e9137 100644 --- a/OliverBooth/Services/BlogUserService.cs +++ b/OliverBooth/Services/BlogUserService.cs @@ -1,6 +1,8 @@ using System.Collections.Concurrent; using System.Diagnostics.CodeAnalysis; using Microsoft.EntityFrameworkCore; +using OliverBooth.Common.Data.Blog; +using OliverBooth.Common.Services; using OliverBooth.Data.Blog; namespace OliverBooth.Services; diff --git a/OliverBooth/Services/CodeSnippetService.cs b/OliverBooth/Services/CodeSnippetService.cs index 643f6ea..0c1f6d6 100644 --- a/OliverBooth/Services/CodeSnippetService.cs +++ b/OliverBooth/Services/CodeSnippetService.cs @@ -1,5 +1,7 @@ using System.Diagnostics.CodeAnalysis; using Microsoft.EntityFrameworkCore; +using OliverBooth.Common.Data.Web; +using OliverBooth.Common.Services; using OliverBooth.Data.Web; namespace OliverBooth.Services; diff --git a/OliverBooth/Services/ContactService.cs b/OliverBooth/Services/ContactService.cs index de2b591..21cb751 100644 --- a/OliverBooth/Services/ContactService.cs +++ b/OliverBooth/Services/ContactService.cs @@ -1,4 +1,6 @@ using Microsoft.EntityFrameworkCore; +using OliverBooth.Common.Data.Web; +using OliverBooth.Common.Services; using OliverBooth.Data.Web; namespace OliverBooth.Services; diff --git a/OliverBooth/Services/MastodonService.cs b/OliverBooth/Services/MastodonService.cs index 121a6cc..b169e2b 100644 --- a/OliverBooth/Services/MastodonService.cs +++ b/OliverBooth/Services/MastodonService.cs @@ -1,6 +1,8 @@ using System.Text.Json; using System.Text.Json.Serialization; using HtmlAgilityPack; +using OliverBooth.Common.Data.Mastodon; +using OliverBooth.Common.Services; using OliverBooth.Data.Mastodon; namespace OliverBooth.Services; @@ -21,7 +23,7 @@ internal sealed class MastodonService : IMastodonService } /// - public MastodonStatus GetLatestStatus() + public IMastodonStatus GetLatestStatus() { string token = Environment.GetEnvironmentVariable("MASTODON_TOKEN") ?? string.Empty; string account = Environment.GetEnvironmentVariable("MASTODON_ACCOUNT") ?? string.Empty; diff --git a/OliverBooth/Services/ProgrammingLanguageService.cs b/OliverBooth/Services/ProgrammingLanguageService.cs index 3f82bbc..d0f1c4d 100644 --- a/OliverBooth/Services/ProgrammingLanguageService.cs +++ b/OliverBooth/Services/ProgrammingLanguageService.cs @@ -1,21 +1,9 @@ using Microsoft.EntityFrameworkCore; +using OliverBooth.Common.Services; using OliverBooth.Data.Web; namespace OliverBooth.Services; -/// -/// Represents a service which can perform programming language lookup. -/// -public interface IProgrammingLanguageService -{ - /// - /// Returns the human-readable name of a language. - /// - /// The alias of the language. - /// The human-readable name, or if the name could not be found. - string GetLanguageName(string alias); -} - /// internal sealed class ProgrammingLanguageService : IProgrammingLanguageService { diff --git a/OliverBooth/Services/ProjectService.cs b/OliverBooth/Services/ProjectService.cs index 6f75ca5..75eff1b 100644 --- a/OliverBooth/Services/ProjectService.cs +++ b/OliverBooth/Services/ProjectService.cs @@ -2,6 +2,8 @@ using System.Diagnostics.CodeAnalysis; using Humanizer; using Markdig; using Microsoft.EntityFrameworkCore; +using OliverBooth.Common.Data.Web; +using OliverBooth.Common.Services; using OliverBooth.Data.Web; namespace OliverBooth.Services; diff --git a/OliverBooth/Services/ReadingListService.cs b/OliverBooth/Services/ReadingListService.cs index 54da5fb..705f056 100644 --- a/OliverBooth/Services/ReadingListService.cs +++ b/OliverBooth/Services/ReadingListService.cs @@ -1,4 +1,6 @@ using Microsoft.EntityFrameworkCore; +using OliverBooth.Common.Data.Web; +using OliverBooth.Common.Services; using OliverBooth.Data.Web; namespace OliverBooth.Services; diff --git a/OliverBooth/Services/TemplateService.cs b/OliverBooth/Services/TemplateService.cs index 623a1fd..90bc47a 100644 --- a/OliverBooth/Services/TemplateService.cs +++ b/OliverBooth/Services/TemplateService.cs @@ -1,9 +1,10 @@ using System.Buffers.Binary; -using System.Collections.Concurrent; using System.Diagnostics.CodeAnalysis; -using Markdig; using Microsoft.EntityFrameworkCore; +using OliverBooth.Common.Data.Web; using OliverBooth.Data.Web; +using OliverBooth.Extensions.Markdig.Markdown.Template; +using OliverBooth.Extensions.Markdig.Services; using OliverBooth.Formatting; using OliverBooth.Markdown.Template; using SmartFormat; diff --git a/OliverBooth/Services/TutorialService.cs b/OliverBooth/Services/TutorialService.cs index 3fd21ed..4a1e387 100644 --- a/OliverBooth/Services/TutorialService.cs +++ b/OliverBooth/Services/TutorialService.cs @@ -3,7 +3,10 @@ using Cysharp.Text; using Humanizer; using Markdig; using Microsoft.EntityFrameworkCore; -using OliverBooth.Data; +using OliverBooth.Common.Data; +using OliverBooth.Common.Data.Blog; +using OliverBooth.Common.Data.Web; +using OliverBooth.Common.Services; using OliverBooth.Data.Blog; using OliverBooth.Data.Web;