Compare commits
9 Commits
bb0483d4ae
...
2844904723
Author | SHA1 | Date | |
---|---|---|---|
2844904723 | |||
19a398d694 | |||
eb2edcb3f6 | |||
2d4d6d3823 | |||
0ecef1a547 | |||
c2deccafae | |||
3c6a2209c2 | |||
31794a1238 | |||
69e1279a8b |
@ -3,10 +3,11 @@ using Microsoft.AspNetCore.Mvc;
|
|||||||
using OliverBooth.Data.Blog;
|
using OliverBooth.Data.Blog;
|
||||||
using OliverBooth.Services;
|
using OliverBooth.Services;
|
||||||
|
|
||||||
namespace OliverBooth.Controllers;
|
namespace OliverBooth.Areas.Api.Controllers;
|
||||||
|
|
||||||
[Controller]
|
[Controller]
|
||||||
[Route("/api/blog")]
|
[Area("api")]
|
||||||
|
[Route("blog")]
|
||||||
public sealed class BlogApiController : ControllerBase
|
public sealed class BlogApiController : ControllerBase
|
||||||
{
|
{
|
||||||
private readonly BlogService _blogService;
|
private readonly BlogService _blogService;
|
||||||
@ -52,8 +53,8 @@ public sealed class BlogApiController : ControllerBase
|
|||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet("author/{id:int}")]
|
[HttpGet("author/{id:guid}")]
|
||||||
public IActionResult GetAuthor(int id)
|
public IActionResult GetAuthor(Guid id)
|
||||||
{
|
{
|
||||||
if (!ValidateReferer()) return NotFound();
|
if (!ValidateReferer()) return NotFound();
|
||||||
if (!_blogService.TryGetAuthor(id, out Author? author)) return NotFound();
|
if (!_blogService.TryGetAuthor(id, out Author? author)) return NotFound();
|
@ -2,7 +2,7 @@
|
|||||||
@using Humanizer
|
@using Humanizer
|
||||||
@using OliverBooth.Data.Blog
|
@using OliverBooth.Data.Blog
|
||||||
@using OliverBooth.Services
|
@using OliverBooth.Services
|
||||||
@model OliverBooth.Pages.Blog.Article
|
@model OliverBooth.Areas.Blog.Pages.Article
|
||||||
@inject BlogService BlogService
|
@inject BlogService BlogService
|
||||||
|
|
||||||
@if (Model.Post is not { } post)
|
@if (Model.Post is not { } post)
|
||||||
@ -64,7 +64,7 @@
|
|||||||
this.page.url = "@post.GetDisqusUrl()";
|
this.page.url = "@post.GetDisqusUrl()";
|
||||||
this.page.identifier = "@post.GetDisqusIdentifier()";
|
this.page.identifier = "@post.GetDisqusIdentifier()";
|
||||||
this.page.title = "@post.Title";
|
this.page.title = "@post.Title";
|
||||||
this.page.postId = "@(post.WordPressId ?? post.Id)";
|
this.page.postId = "@(post.WordPressId?.ToString() ?? post.Id.ToString())";
|
||||||
};
|
};
|
||||||
|
|
||||||
(function() {
|
(function() {
|
@ -3,11 +3,12 @@ using Microsoft.AspNetCore.Mvc.RazorPages;
|
|||||||
using OliverBooth.Data.Blog;
|
using OliverBooth.Data.Blog;
|
||||||
using OliverBooth.Services;
|
using OliverBooth.Services;
|
||||||
|
|
||||||
namespace OliverBooth.Pages.Blog;
|
namespace OliverBooth.Areas.Blog.Pages;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents the page model for the <c>Article</c> page.
|
/// Represents the page model for the <c>Article</c> page.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[Area("blog")]
|
||||||
public class Article : PageModel
|
public class Article : PageModel
|
||||||
{
|
{
|
||||||
private readonly BlogService _blogService;
|
private readonly BlogService _blogService;
|
@ -1,5 +1,5 @@
|
|||||||
@page
|
@page
|
||||||
@model OliverBooth.Pages.Blog.Index
|
@model OliverBooth.Areas.Blog.Pages.Index
|
||||||
|
|
||||||
@{
|
@{
|
||||||
ViewData["Title"] = "Blog";
|
ViewData["Title"] = "Blog";
|
@ -3,8 +3,9 @@ using Microsoft.AspNetCore.Mvc.RazorPages;
|
|||||||
using OliverBooth.Data.Blog;
|
using OliverBooth.Data.Blog;
|
||||||
using OliverBooth.Services;
|
using OliverBooth.Services;
|
||||||
|
|
||||||
namespace OliverBooth.Pages.Blog;
|
namespace OliverBooth.Areas.Blog.Pages;
|
||||||
|
|
||||||
|
[Area("blog")]
|
||||||
public class Index : PageModel
|
public class Index : PageModel
|
||||||
{
|
{
|
||||||
private readonly BlogService _blogService;
|
private readonly BlogService _blogService;
|
||||||
@ -14,7 +15,7 @@ public class Index : PageModel
|
|||||||
_blogService = blogService;
|
_blogService = blogService;
|
||||||
}
|
}
|
||||||
|
|
||||||
public IActionResult OnGet([FromQuery(Name = "pid")] int? postId = null,
|
public IActionResult OnGet([FromQuery(Name = "pid")] Guid? postId = null,
|
||||||
[FromQuery(Name = "p")] int? wpPostId = null)
|
[FromQuery(Name = "p")] int? wpPostId = null)
|
||||||
{
|
{
|
||||||
if (postId.HasValue == wpPostId.HasValue)
|
if (postId.HasValue == wpPostId.HasValue)
|
||||||
@ -25,7 +26,7 @@ public class Index : PageModel
|
|||||||
return postId.HasValue ? HandleNewRoute(postId.Value) : HandleWordPressRoute(wpPostId!.Value);
|
return postId.HasValue ? HandleNewRoute(postId.Value) : HandleWordPressRoute(wpPostId!.Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
private IActionResult HandleNewRoute(int postId)
|
private IActionResult HandleNewRoute(Guid postId)
|
||||||
{
|
{
|
||||||
return _blogService.TryGetBlogPost(postId, out BlogPost? post) ? RedirectToPost(post) : NotFound();
|
return _blogService.TryGetBlogPost(postId, out BlogPost? post) ? RedirectToPost(post) : NotFound();
|
||||||
}
|
}
|
@ -1,2 +1,2 @@
|
|||||||
@page "/blog/{year:int}/{month:int}/{day:int}/{slug}/raw"
|
@page "/blog/{year:int}/{month:int}/{day:int}/{slug}/raw"
|
||||||
@model OliverBooth.Pages.Blog.RawArticle
|
@model OliverBooth.Areas.Blog.Pages.RawArticle
|
@ -4,11 +4,12 @@ using Microsoft.AspNetCore.Mvc.RazorPages;
|
|||||||
using OliverBooth.Data.Blog;
|
using OliverBooth.Data.Blog;
|
||||||
using OliverBooth.Services;
|
using OliverBooth.Services;
|
||||||
|
|
||||||
namespace OliverBooth.Pages.Blog;
|
namespace OliverBooth.Areas.Blog.Pages;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents the page model for the <c>RawArticle</c> page.
|
/// Represents the page model for the <c>RawArticle</c> page.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[Area("blog")]
|
||||||
public class RawArticle : PageModel
|
public class RawArticle : PageModel
|
||||||
{
|
{
|
||||||
private readonly BlogService _blogService;
|
private readonly BlogService _blogService;
|
||||||
@ -22,12 +23,6 @@ public class RawArticle : PageModel
|
|||||||
_blogService = blogService;
|
_blogService = blogService;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the requested blog post.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>The requested blog post.</value>
|
|
||||||
public BlogPost Post { get; private set; } = null!;
|
|
||||||
|
|
||||||
public IActionResult OnGet(int year, int month, int day, string slug)
|
public IActionResult OnGet(int year, int month, int day, string slug)
|
||||||
{
|
{
|
||||||
if (!_blogService.TryGetBlogPost(year, month, day, slug, out BlogPost? post))
|
if (!_blogService.TryGetBlogPost(year, month, day, slug, out BlogPost? post))
|
3
OliverBooth/Areas/Blog/Pages/_ViewStart.cshtml
Normal file
3
OliverBooth/Areas/Blog/Pages/_ViewStart.cshtml
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
@{
|
||||||
|
Layout = "_Layout";
|
||||||
|
}
|
@ -48,7 +48,7 @@ public sealed class Author : IEquatable<Author>
|
|||||||
/// Gets the ID of the author.
|
/// Gets the ID of the author.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <value>The ID.</value>
|
/// <value>The ID.</value>
|
||||||
public int Id { get; private set; }
|
public Guid Id { get; private set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the name of the author.
|
/// Gets or sets the name of the author.
|
||||||
@ -70,6 +70,6 @@ public sealed class Author : IEquatable<Author>
|
|||||||
|
|
||||||
public override int GetHashCode()
|
public override int GetHashCode()
|
||||||
{
|
{
|
||||||
return Id;
|
return Id.GetHashCode();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,7 +11,7 @@ public sealed class BlogPost : IEquatable<BlogPost>
|
|||||||
/// Gets the ID of the author.
|
/// Gets the ID of the author.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <value>The author ID.</value>
|
/// <value>The author ID.</value>
|
||||||
public int AuthorId { get; private set; }
|
public Guid AuthorId { get; private set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the body of the blog post.
|
/// Gets or sets the body of the blog post.
|
||||||
@ -47,7 +47,7 @@ public sealed class BlogPost : IEquatable<BlogPost>
|
|||||||
/// Gets the ID of the blog post.
|
/// Gets the ID of the blog post.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <value>The ID.</value>
|
/// <value>The ID.</value>
|
||||||
public int Id { get; private set; }
|
public Guid Id { get; private set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets a value indicating whether the blog post is a redirect.
|
/// Gets or sets a value indicating whether the blog post is a redirect.
|
||||||
@ -181,6 +181,6 @@ public sealed class BlogPost : IEquatable<BlogPost>
|
|||||||
public override int GetHashCode()
|
public override int GetHashCode()
|
||||||
{
|
{
|
||||||
// ReSharper disable once NonReadonlyMemberInGetHashCode
|
// ReSharper disable once NonReadonlyMemberInGetHashCode
|
||||||
return Id;
|
return Id.GetHashCode();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,7 +14,7 @@ internal sealed class AuthorConfiguration : IEntityTypeConfiguration<Author>
|
|||||||
builder.ToTable("Author");
|
builder.ToTable("Author");
|
||||||
builder.HasKey(e => e.Id);
|
builder.HasKey(e => e.Id);
|
||||||
|
|
||||||
builder.Property(e => e.Id).ValueGeneratedOnAdd();
|
builder.Property(e => e.Id);
|
||||||
builder.Property(e => e.Name).HasMaxLength(100).IsRequired();
|
builder.Property(e => e.Name).HasMaxLength(100).IsRequired();
|
||||||
builder.Property(e => e.EmailAddress).HasMaxLength(255).IsRequired(false);
|
builder.Property(e => e.EmailAddress).HasMaxLength(255).IsRequired(false);
|
||||||
}
|
}
|
||||||
|
@ -14,7 +14,7 @@ internal sealed class BlogPostConfiguration : IEntityTypeConfiguration<BlogPost>
|
|||||||
builder.ToTable("BlogPost");
|
builder.ToTable("BlogPost");
|
||||||
builder.HasKey(e => e.Id);
|
builder.HasKey(e => e.Id);
|
||||||
|
|
||||||
builder.Property(e => e.Id).ValueGeneratedOnAdd();
|
builder.Property(e => e.Id);
|
||||||
builder.Property(e => e.WordPressId).IsRequired(false);
|
builder.Property(e => e.WordPressId).IsRequired(false);
|
||||||
builder.Property(e => e.Slug).HasMaxLength(100).IsRequired();
|
builder.Property(e => e.Slug).HasMaxLength(100).IsRequired();
|
||||||
builder.Property(e => e.AuthorId).IsRequired();
|
builder.Property(e => e.AuthorId).IsRequired();
|
||||||
|
@ -15,8 +15,8 @@
|
|||||||
<link rel="shortcut icon" href="~/img/favicon.png">
|
<link rel="shortcut icon" href="~/img/favicon.png">
|
||||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.3.1/css/bootstrap.min.css" integrity="sha512-Z/def5z5u2aR89OuzYcxmDJ0Bnd5V1cKqBEbvLOiUNWdg9PQeXVvXLI90SE4QOHGlfLqUnDNVAYyZi8UwUTmWQ==" crossorigin="anonymous" referrerpolicy="no-referrer">
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.3.1/css/bootstrap.min.css" integrity="sha512-Z/def5z5u2aR89OuzYcxmDJ0Bnd5V1cKqBEbvLOiUNWdg9PQeXVvXLI90SE4QOHGlfLqUnDNVAYyZi8UwUTmWQ==" crossorigin="anonymous" referrerpolicy="no-referrer">
|
||||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/KaTeX/0.16.8/katex.min.css" integrity="sha512-7nTa5CnxbzfQgjQrNmHXB7bxGTUVO/DcYX6rpgt06MkzM0rVXP3EYCv/Ojxg5H0dKbY7llbbYaqgfZjnGOAWGA==" crossorigin="anonymous" referrerpolicy="no-referrer">
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/KaTeX/0.16.8/katex.min.css" integrity="sha512-7nTa5CnxbzfQgjQrNmHXB7bxGTUVO/DcYX6rpgt06MkzM0rVXP3EYCv/Ojxg5H0dKbY7llbbYaqgfZjnGOAWGA==" crossorigin="anonymous" referrerpolicy="no-referrer">
|
||||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.2/css/all.min.css" integrity="sha512-z3gLpd7yknf1YoNbCzqRKc4qyor8gaKU1qmn+CShxbuBusANI9QpRohGBreCFkKxLhei6S9CQXFEbbKuqLg0DA==" crossorigin="anonymous" referrerpolicy="no-referrer" />
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.2/css/all.min.css" integrity="sha512-z3gLpd7yknf1YoNbCzqRKc4qyor8gaKU1qmn+CShxbuBusANI9QpRohGBreCFkKxLhei6S9CQXFEbbKuqLg0DA==" crossorigin="anonymous" referrerpolicy="no-referrer">
|
||||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.1/animate.min.css" integrity="sha512-c42qTSw/wPZ3/5LBzD+Bw5f7bSF2oxou6wEb+I/lqeaKV5FDIfMvvRp772y4jcJLKuGUOpbJMdg/BTl50fJYAw==" crossorigin="anonymous" referrerpolicy="no-referrer" />
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.1/animate.min.css" integrity="sha512-c42qTSw/wPZ3/5LBzD+Bw5f7bSF2oxou6wEb+I/lqeaKV5FDIfMvvRp772y4jcJLKuGUOpbJMdg/BTl50fJYAw==" crossorigin="anonymous" referrerpolicy="no-referrer">
|
||||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||||
<link href="https://fonts.googleapis.com/css2?family=Roboto+Mono:wght@100;400;700&display=swap" rel="stylesheet">
|
<link href="https://fonts.googleapis.com/css2?family=Roboto+Mono:wght@100;400;700&display=swap" rel="stylesheet">
|
||||||
@ -36,22 +36,22 @@
|
|||||||
<nav>
|
<nav>
|
||||||
<ul class="site-nav">
|
<ul class="site-nav">
|
||||||
<li>
|
<li>
|
||||||
<a asp-page="/index">About</a>
|
<a asp-area="" asp-page="/index">About</a>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<a asp-page="/blog/index">Blog</a>
|
<a asp-area="blog" asp-page="/index">Blog</a>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<a asp-page="/tutorials/index">Tutorials</a>
|
<a asp-area="" asp-page="/tutorials/index">Tutorials</a>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<a asp-page="/projects/index">Projects</a>
|
<a asp-area="" asp-page="/projects/index">Projects</a>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<a asp-page="/contact/index">Contact</a>
|
<a asp-area="" asp-page="/contact/index">Contact</a>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<a asp-page="/donate">Donate</a>
|
<a asp-area="" asp-page="/donate">Donate</a>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</nav>
|
</nav>
|
||||||
@ -71,6 +71,8 @@
|
|||||||
<a asp-area="" asp-page="/privacy/index">Privacy</a>
|
<a asp-area="" asp-page="/privacy/index">Privacy</a>
|
||||||
•
|
•
|
||||||
<a href="https://mastodon.olivr.me/@@oliver" rel="me">Mastodon</a>
|
<a href="https://mastodon.olivr.me/@@oliver" rel="me">Mastodon</a>
|
||||||
|
•
|
||||||
|
<a href="/blog/feed"><i class="fa-solid fa-rss text-orange"></i></a>
|
||||||
</div>
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
|
|
||||||
|
@ -80,6 +80,7 @@ app.UseRouting();
|
|||||||
app.UseAuthorization();
|
app.UseAuthorization();
|
||||||
|
|
||||||
app.MapControllers();
|
app.MapControllers();
|
||||||
|
app.MapControllerRoute("scoped", "{area:exists}/{controller=Home}/{action=Index}/{id?}");
|
||||||
app.MapControllerRoute("default", "{controller=Home}/{action=Index}/{id?}");
|
app.MapControllerRoute("default", "{controller=Home}/{action=Index}/{id?}");
|
||||||
app.MapRazorPages();
|
app.MapRazorPages();
|
||||||
app.MapRssFeed("/blog/feed");
|
app.MapRssFeed("/blog/feed");
|
||||||
|
@ -74,7 +74,7 @@ public sealed class BlogService
|
|||||||
/// </param>
|
/// </param>
|
||||||
/// <returns><see langword="true" /> if the author is found; otherwise, <see langword="false" />.</returns>
|
/// <returns><see langword="true" /> if the author is found; otherwise, <see langword="false" />.</returns>
|
||||||
/// <exception cref="ArgumentNullException"><paramref name="post" /> is <see langword="null" />.</exception>
|
/// <exception cref="ArgumentNullException"><paramref name="post" /> is <see langword="null" />.</exception>
|
||||||
public bool TryGetAuthor(int id, [NotNullWhen(true)] out Author? author)
|
public bool TryGetAuthor(Guid id, [NotNullWhen(true)] out Author? author)
|
||||||
{
|
{
|
||||||
using BlogContext context = _dbContextFactory.CreateDbContext();
|
using BlogContext context = _dbContextFactory.CreateDbContext();
|
||||||
author = context.Authors.FirstOrDefault(a => a.Id == id);
|
author = context.Authors.FirstOrDefault(a => a.Id == id);
|
||||||
@ -136,7 +136,7 @@ public sealed class BlogService
|
|||||||
/// otherwise, <see langword="null" />.
|
/// otherwise, <see langword="null" />.
|
||||||
/// </param>
|
/// </param>
|
||||||
/// <returns><see langword="true" /> if the post is found; otherwise, <see langword="false" />.</returns>
|
/// <returns><see langword="true" /> if the post is found; otherwise, <see langword="false" />.</returns>
|
||||||
public bool TryGetBlogPost(int postId, [NotNullWhen(true)] out BlogPost? post)
|
public bool TryGetBlogPost(Guid postId, [NotNullWhen(true)] out BlogPost? post)
|
||||||
{
|
{
|
||||||
using BlogContext context = _dbContextFactory.CreateDbContext();
|
using BlogContext context = _dbContextFactory.CreateDbContext();
|
||||||
post = context.BlogPosts.FirstOrDefault(p => p.Id == postId);
|
post = context.BlogPosts.FirstOrDefault(p => p.Id == postId);
|
||||||
|
@ -233,4 +233,8 @@ a.bmc-btn {
|
|||||||
&:hover {
|
&:hover {
|
||||||
color: #fff;
|
color: #fff;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-orange {
|
||||||
|
color: #f60 !important;
|
||||||
}
|
}
|
Loading…
Reference in New Issue
Block a user