From 4b2223634e0058d792c9b10de24e0fbba7a47f3e Mon Sep 17 00:00:00 2001 From: Oliver Booth Date: Mon, 26 Feb 2024 02:50:48 +0000 Subject: [PATCH] feat: add blog post editing --- OliverBooth.sln | 1 + .../Controllers/Api/v1/BlogPostController.cs | 53 ++++++++++++ OliverBooth/Data/Blog/BlogPost.cs | 2 +- OliverBooth/Data/Blog/IBlogPost.cs | 4 +- OliverBooth/Pages/Admin/BlogPosts.cshtml | 86 +++++++++++++++++++ OliverBooth/Pages/Admin/BlogPosts.cshtml.cs | 29 +++++++ OliverBooth/Pages/Admin/EditBlogPost.cshtml | 27 ++++++ .../Pages/Admin/EditBlogPost.cshtml.cs | 39 +++++++++ OliverBooth/Pages/Shared/_AdminLayout.cshtml | 1 + OliverBooth/Services/BlogPostService.cs | 13 +++ OliverBooth/Services/IBlogPostService.cs | 7 ++ src/scss/admin.scss | 45 +++++++++- src/ts/admin/EditBlogPost.ts | 58 +++++++++++++ src/ts/admin/admin.ts | 1 + src/ts/app/API.ts | 38 ++++++-- 15 files changed, 389 insertions(+), 15 deletions(-) create mode 100644 OliverBooth/Controllers/Api/v1/BlogPostController.cs create mode 100644 OliverBooth/Pages/Admin/BlogPosts.cshtml create mode 100644 OliverBooth/Pages/Admin/BlogPosts.cshtml.cs create mode 100644 OliverBooth/Pages/Admin/EditBlogPost.cshtml create mode 100644 OliverBooth/Pages/Admin/EditBlogPost.cshtml.cs create mode 100644 src/ts/admin/EditBlogPost.ts diff --git a/OliverBooth.sln b/OliverBooth.sln index a2b5dc8..b3d65bb 100644 --- a/OliverBooth.sln +++ b/OliverBooth.sln @@ -40,6 +40,7 @@ EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "admin", "admin", "{183CDB1F-371D-4A24-8F96-1DF0967995E4}" ProjectSection(SolutionItems) = preProject src\ts\admin\admin.ts = src\ts\admin\admin.ts + src\ts\admin\EditBlogPost.ts = src\ts\admin\EditBlogPost.ts EndProjectSection EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "app", "app", "{A6590915-CB40-43EA-B0A3-EDEC63769780}" diff --git a/OliverBooth/Controllers/Api/v1/BlogPostController.cs b/OliverBooth/Controllers/Api/v1/BlogPostController.cs new file mode 100644 index 0000000..b30107e --- /dev/null +++ b/OliverBooth/Controllers/Api/v1/BlogPostController.cs @@ -0,0 +1,53 @@ +using System.Text; +using System.Text.Json.Serialization; +using Asp.Versioning; +using Microsoft.AspNetCore.Mvc; +using OliverBooth.Data.Blog; +using OliverBooth.Data.Web; +using OliverBooth.Services; + +namespace OliverBooth.Controllers.Api.v1; + +[ApiController] +[Route("api/v{version:apiVersion}/post")] +[ApiVersion(1)] +[Produces("application/json")] +public sealed class BlogPostController : ControllerBase +{ + private readonly ILogger _logger; + private readonly ISessionService _sessionService; + private readonly IBlogPostService _blogPostService; + + public BlogPostController(ILogger logger, + ISessionService sessionService, + IBlogPostService blogPostService) + { + _logger = logger; + _sessionService = sessionService; + _blogPostService = blogPostService; + } + + [HttpPatch("{id:guid}")] + public async Task OnPatch([FromRoute] Guid id) + { + if (!_sessionService.TryGetCurrentUser(Request, Response, out IUser? user)) + { + Response.StatusCode = 401; + return new JsonResult(new { status = 401, message = "Unauthorized" }); + } + + if (!_blogPostService.TryGetPost(id, out IBlogPost? post)) + { + Response.StatusCode = 404; + return new JsonResult(new { status = 404, message = "Not Found" }); + } + + using var reader = new StreamReader(Request.Body, Encoding.UTF8); + string content = await reader.ReadToEndAsync(); + + post.Body = content; + _blogPostService.UpdatePost(post); + + return new JsonResult(new { status = 200, message = "OK" }); + } +} diff --git a/OliverBooth/Data/Blog/BlogPost.cs b/OliverBooth/Data/Blog/BlogPost.cs index cfb1110..79161d1 100644 --- a/OliverBooth/Data/Blog/BlogPost.cs +++ b/OliverBooth/Data/Blog/BlogPost.cs @@ -11,7 +11,7 @@ internal sealed class BlogPost : IBlogPost public IBlogAuthor Author { get; internal set; } = null!; /// - public string Body { get; internal set; } = string.Empty; + public string Body { get; set; } = string.Empty; /// public bool EnableComments { get; internal set; } diff --git a/OliverBooth/Data/Blog/IBlogPost.cs b/OliverBooth/Data/Blog/IBlogPost.cs index a8b9818..05c252a 100644 --- a/OliverBooth/Data/Blog/IBlogPost.cs +++ b/OliverBooth/Data/Blog/IBlogPost.cs @@ -12,10 +12,10 @@ public interface IBlogPost IBlogAuthor Author { get; } /// - /// Gets the body of the post. + /// Gets or sets the body of the post. /// /// The body of the post. - string Body { get; } + string Body { get; set; } /// /// Gets a value indicating whether comments are enabled for the post. diff --git a/OliverBooth/Pages/Admin/BlogPosts.cshtml b/OliverBooth/Pages/Admin/BlogPosts.cshtml new file mode 100644 index 0000000..be5ef21 --- /dev/null +++ b/OliverBooth/Pages/Admin/BlogPosts.cshtml @@ -0,0 +1,86 @@ +@page "/admin/blog-posts" +@using System.Diagnostics +@using OliverBooth.Data +@using OliverBooth.Data.Blog +@using OliverBooth.Data.Web +@using OliverBooth.Services +@model OliverBooth.Pages.Admin.BlogPosts +@inject IBlogPostService BlogPostService +@inject IUserService UserService +@inject ISessionService SessionService + +@{ + ViewData["Title"] = "Blog Posts"; + Layout = "Shared/_AdminLayout"; + + HttpRequest request = HttpContext.Request; + SessionService.TryGetSession(request, out ISession? session); + IUser? user = null; + if (session is not null) + { + UserService.TryGetUser(session.UserId, out user); + } + Debug.Assert(user is not null); +} + +
+
+
+
+
+
+
+ + Total Blog Posts +
+
+ @BlogPostService.GetBlogPostCount() +
+
+
+
+
+
+
+ + + + + + + + + + + + + @foreach (IBlogPost post in BlogPostService.GetAllBlogPosts(visibility: (BlogPostVisibility)(-1))) + { + if (post.Visibility != BlogPostVisibility.Published && post.Author.Id != user.Id && !user.HasPermission(Permission.Administrator)) + { + continue; + } + + string icon = post.Visibility switch + { + BlogPostVisibility.Private => "key text-danger", + BlogPostVisibility.Unlisted => "unlock text-warning", + BlogPostVisibility.Published => "circle-check text-success" + }; + + + + + + + + } + +
TitlePostedAuthorOptions
@post.Title@post.Published @post.Author.DisplayName + + + + + + +
diff --git a/OliverBooth/Pages/Admin/BlogPosts.cshtml.cs b/OliverBooth/Pages/Admin/BlogPosts.cshtml.cs new file mode 100644 index 0000000..ff2e5de --- /dev/null +++ b/OliverBooth/Pages/Admin/BlogPosts.cshtml.cs @@ -0,0 +1,29 @@ +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.RazorPages; +using OliverBooth.Data.Web; +using OliverBooth.Services; + +namespace OliverBooth.Pages.Admin; + +public class BlogPosts : PageModel +{ + private readonly ISessionService _sessionService; + + public BlogPosts(ISessionService sessionService) + { + _sessionService = sessionService; + } + + public IUser CurrentUser { get; private set; } = null!; + + public IActionResult OnGet() + { + if (!_sessionService.TryGetCurrentUser(Request, Response, out IUser? user)) + { + return RedirectToPage("/admin/login"); + } + + CurrentUser = user; + return Page(); + } +} diff --git a/OliverBooth/Pages/Admin/EditBlogPost.cshtml b/OliverBooth/Pages/Admin/EditBlogPost.cshtml new file mode 100644 index 0000000..6f23017 --- /dev/null +++ b/OliverBooth/Pages/Admin/EditBlogPost.cshtml @@ -0,0 +1,27 @@ +@page "/admin/blog-posts/edit/{id}" +@using Markdig +@using OliverBooth.Data.Blog +@model OliverBooth.Pages.Admin.EditBlogPost +@inject MarkdownPipeline MarkdownPipeline + +@{ + Layout = "Shared/_AdminLayout"; + ViewData["Title"] = "Edit Post"; + IBlogPost post = Model.BlogPost; +} + + + + + Preview + +
+
+ +
+
+
+ @Html.Raw(Markdown.ToHtml(post.Body, MarkdownPipeline)) +
+
+
\ No newline at end of file diff --git a/OliverBooth/Pages/Admin/EditBlogPost.cshtml.cs b/OliverBooth/Pages/Admin/EditBlogPost.cshtml.cs new file mode 100644 index 0000000..e2e659a --- /dev/null +++ b/OliverBooth/Pages/Admin/EditBlogPost.cshtml.cs @@ -0,0 +1,39 @@ +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.RazorPages; +using OliverBooth.Data.Blog; +using OliverBooth.Data.Web; +using OliverBooth.Services; + +namespace OliverBooth.Pages.Admin; + +public class EditBlogPost : PageModel +{ + private readonly IBlogPostService _blogPostService; + private readonly ISessionService _sessionService; + + public EditBlogPost(IBlogPostService blogPostService, ISessionService sessionService) + { + _blogPostService = blogPostService; + _sessionService = sessionService; + } + + public IUser CurrentUser { get; private set; } = null!; + + public IBlogPost BlogPost { get; private set; } = null!; + + public IActionResult OnGet([FromRoute(Name = "id")] Guid postId) + { + if (!_sessionService.TryGetCurrentUser(Request, Response, out IUser? user)) + { + return RedirectToPage("/admin/login"); + } + + if (_blogPostService.TryGetPost(postId, out IBlogPost? post)) + { + BlogPost = post; + } + + CurrentUser = user; + return Page(); + } +} diff --git a/OliverBooth/Pages/Shared/_AdminLayout.cshtml b/OliverBooth/Pages/Shared/_AdminLayout.cshtml index a3f70cd..10fda4d 100644 --- a/OliverBooth/Pages/Shared/_AdminLayout.cshtml +++ b/OliverBooth/Pages/Shared/_AdminLayout.cshtml @@ -148,6 +148,7 @@ +