First cut of admin dashboard
Add web log details to common model
This commit is contained in:
parent
39e0d5ec8b
commit
8f94d7ddfe
13
src/MyWebLog.Data/Extensions/CategoryExtensions.cs
Normal file
13
src/MyWebLog.Data/Extensions/CategoryExtensions.cs
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
|
namespace MyWebLog.Data;
|
||||||
|
|
||||||
|
public static class CategoryEtensions
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Count all categories
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>A count of all categories</returns>
|
||||||
|
public static async Task<int> CountAll(this DbSet<Category> db) =>
|
||||||
|
await db.CountAsync().ConfigureAwait(false);
|
||||||
|
}
|
|
@ -4,11 +4,26 @@ namespace MyWebLog.Data;
|
||||||
|
|
||||||
public static class PageExtensions
|
public static class PageExtensions
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Count the number of pages
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>The number of pages</returns>
|
||||||
|
public static async Task<int> CountAll(this DbSet<Page> db) =>
|
||||||
|
await db.CountAsync().ConfigureAwait(false);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Retrieve a page by its ID (non-tracked)
|
/// Retrieve a page by its ID (non-tracked)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="id">The ID of the page to retrieve</param>
|
/// <param name="id">The ID of the page to retrieve</param>
|
||||||
/// <returns>The requested page (or null if it is not found)</returns>
|
/// <returns>The requested page (or null if it is not found)</returns>
|
||||||
public static async Task<Page?> FindById(this DbSet<Page> db, string id) =>
|
public static async Task<Page?> FindById(this DbSet<Page> db, string id) =>
|
||||||
await db.FirstOrDefaultAsync(p => p.Id == id).ConfigureAwait(false);
|
await db.SingleOrDefaultAsync(p => p.Id == id).ConfigureAwait(false);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Retrieve a page by its permalink (non-tracked)
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="permalink">The permalink</param>
|
||||||
|
/// <returns>The requested page (or null if it is not found)</returns>
|
||||||
|
public static async Task<Page?> FindByPermalink(this DbSet<Page> db, string permalink) =>
|
||||||
|
await db.SingleOrDefaultAsync(p => p.Permalink == permalink).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,22 @@ namespace MyWebLog.Data;
|
||||||
|
|
||||||
public static class PostExtensions
|
public static class PostExtensions
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Count the posts in the given status
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="status">The status for which posts should be counted</param>
|
||||||
|
/// <returns>A count of the posts in the given status</returns>
|
||||||
|
public static async Task<int> CountByStatus(this DbSet<Post> db, PostStatus status) =>
|
||||||
|
await db.CountAsync(p => p.Status == status).ConfigureAwait(false);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Retrieve a post by its permalink (non-tracked)
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="permalink">The possible post permalink</param>
|
||||||
|
/// <returns>The post matching the permalink, or null if none is found</returns>
|
||||||
|
public static async Task<Post?> FindByPermalink(this DbSet<Post> db, string permalink) =>
|
||||||
|
await db.SingleOrDefaultAsync(p => p.Id == permalink).ConfigureAwait(false);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Retrieve a page of published posts (non-tracked)
|
/// Retrieve a page of published posts (non-tracked)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
@ -12,8 +12,12 @@ public class AdminController : MyWebLogController
|
||||||
public AdminController(WebLogDbContext db) : base(db) { }
|
public AdminController(WebLogDbContext db) : base(db) { }
|
||||||
|
|
||||||
[HttpGet("")]
|
[HttpGet("")]
|
||||||
public IActionResult Index()
|
public async Task<IActionResult> Index() =>
|
||||||
|
View(new DashboardModel(WebLog)
|
||||||
{
|
{
|
||||||
return View();
|
Posts = await Db.Posts.CountByStatus(PostStatus.Published),
|
||||||
}
|
Drafts = await Db.Posts.CountByStatus(PostStatus.Draft),
|
||||||
|
Pages = await Db.Pages.CountAll(),
|
||||||
|
Categories = await Db.Categories.CountAll()
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
30
src/MyWebLog/Features/Admin/DashboardModel.cs
Normal file
30
src/MyWebLog/Features/Admin/DashboardModel.cs
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
namespace MyWebLog.Features.Admin;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The model used to display the dashboard
|
||||||
|
/// </summary>
|
||||||
|
public class DashboardModel : MyWebLogModel
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The number of published posts
|
||||||
|
/// </summary>
|
||||||
|
public int Posts { get; set; } = 0;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The number of post drafts
|
||||||
|
/// </summary>
|
||||||
|
public int Drafts { get; set; } = 0;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The number of pages
|
||||||
|
/// </summary>
|
||||||
|
public int Pages { get; set; } = 0;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The number of categories
|
||||||
|
/// </summary>
|
||||||
|
public int Categories { get; set; } = 0;
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public DashboardModel(WebLogDetails webLog) : base(webLog) { }
|
||||||
|
}
|
|
@ -1,5 +1,35 @@
|
||||||
@{
|
@model DashboardModel
|
||||||
|
@{
|
||||||
Layout = "_AdminLayout";
|
Layout = "_AdminLayout";
|
||||||
ViewBag.Title = Resources.Dashboard;
|
ViewBag.Title = Resources.Dashboard;
|
||||||
}
|
}
|
||||||
<p>You're logged on!</p>
|
<article class="container">
|
||||||
|
<div class="row">
|
||||||
|
<section class="col col-sm-4">
|
||||||
|
<h3>@Resources.Posts</h3>
|
||||||
|
<p>@string.Format(Resources.ThereAreXPublishedPostsAndYDrafts, Model.Posts, Model.Drafts)</p>
|
||||||
|
<p>
|
||||||
|
<a asp-action="All" asp-controller="Post" class="btn btn-secondary me-2">View All</a>
|
||||||
|
<a asp-action="Edit" asp-controller="Post" asp-route-id="new" class="btn btn-primary">Write a New Post</a>
|
||||||
|
</p>
|
||||||
|
</section>
|
||||||
|
<section class="col col-sm-4">
|
||||||
|
<h3>@Resources.Pages</h3>
|
||||||
|
<p>@string.Format(Resources.ThereAreXPages, Model.Pages)</p>
|
||||||
|
<p>
|
||||||
|
<a asp-action="All" asp-controller="Page" class="btn btn-secondary me-2">View All</a>
|
||||||
|
<a asp-action="Edit" asp-controller="Page" asp-route-id="new" class="btn btn-primary">Create a New Page</a>
|
||||||
|
</p>
|
||||||
|
</section>
|
||||||
|
<section class="col col-sm-4">
|
||||||
|
<h3>@Resources.Categories</h3>
|
||||||
|
<p>@string.Format(Resources.ThereAreXCategories, Model.Categories)</p>
|
||||||
|
<p>
|
||||||
|
<a asp-action="All" asp-controller="Category" class="btn btn-secondary me-2">View All</a>
|
||||||
|
<a asp-action="Edit" asp-controller="Category" asp-route-id="new" class="btn btn-secondary">
|
||||||
|
Add a New Category
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
</article>
|
||||||
|
|
29
src/MyWebLog/Features/Categories/CategoryController.cs
Normal file
29
src/MyWebLog/Features/Categories/CategoryController.cs
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
using Microsoft.AspNetCore.Authorization;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
|
||||||
|
namespace MyWebLog.Features.Categories;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Handle routes for categories
|
||||||
|
/// </summary>
|
||||||
|
[Route("/category")]
|
||||||
|
[Authorize]
|
||||||
|
public class CategoryController : MyWebLogController
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public CategoryController(WebLogDbContext db) : base(db) { }
|
||||||
|
|
||||||
|
[HttpGet("all")]
|
||||||
|
public async Task<IActionResult> All()
|
||||||
|
{
|
||||||
|
await Task.CompletedTask;
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet("{id}/edit")]
|
||||||
|
public async Task<IActionResult> Edit(string id)
|
||||||
|
{
|
||||||
|
await Task.CompletedTask;
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
}
|
29
src/MyWebLog/Features/Pages/PageController.cs
Normal file
29
src/MyWebLog/Features/Pages/PageController.cs
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
using Microsoft.AspNetCore.Authorization;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
|
||||||
|
namespace MyWebLog.Features.Pages;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Handle routes for pages
|
||||||
|
/// </summary>
|
||||||
|
[Route("/page")]
|
||||||
|
[Authorize]
|
||||||
|
public class PageController : MyWebLogController
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public PageController(WebLogDbContext db) : base(db) { }
|
||||||
|
|
||||||
|
[HttpGet("all")]
|
||||||
|
public async Task<IActionResult> All()
|
||||||
|
{
|
||||||
|
await Task.CompletedTask;
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet("{id}/edit")]
|
||||||
|
public async Task<IActionResult> Edit(string id)
|
||||||
|
{
|
||||||
|
await Task.CompletedTask;
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
}
|
22
src/MyWebLog/Features/Pages/SinglePageModel.cs
Normal file
22
src/MyWebLog/Features/Pages/SinglePageModel.cs
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
namespace MyWebLog.Features.Pages;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The model used to render a single page
|
||||||
|
/// </summary>
|
||||||
|
public class SinglePageModel : MyWebLogModel
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The page to be rendered
|
||||||
|
/// </summary>
|
||||||
|
public Page Page { get; init; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructor
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="page">The page to be rendered</param>
|
||||||
|
/// <param name="webLog">The details for the web log</param>
|
||||||
|
public SinglePageModel(Page page, WebLogDetails webLog) : base(webLog)
|
||||||
|
{
|
||||||
|
Page = page;
|
||||||
|
}
|
||||||
|
}
|
22
src/MyWebLog/Features/Posts/MultiplePostModel.cs
Normal file
22
src/MyWebLog/Features/Posts/MultiplePostModel.cs
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
namespace MyWebLog.Features.Posts;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The model used to render multiple posts
|
||||||
|
/// </summary>
|
||||||
|
public class MultiplePostModel : MyWebLogModel
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The posts to be rendered
|
||||||
|
/// </summary>
|
||||||
|
public IEnumerable<Post> Posts { get; init; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructor
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="posts">The posts to be rendered</param>
|
||||||
|
/// <param name="webLog">The details for the web log</param>
|
||||||
|
public MultiplePostModel(IEnumerable<Post> posts, WebLogDetails webLog) : base(webLog)
|
||||||
|
{
|
||||||
|
Posts = posts;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,25 +1,68 @@
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using MyWebLog.Features.Pages;
|
||||||
|
|
||||||
namespace MyWebLog.Features.Posts;
|
namespace MyWebLog.Features.Posts;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Handle post-related requests
|
/// Handle post-related requests
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[Route("/post")]
|
||||||
|
[Authorize]
|
||||||
public class PostController : MyWebLogController
|
public class PostController : MyWebLogController
|
||||||
{
|
{
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public PostController(WebLogDbContext db) : base(db) { }
|
public PostController(WebLogDbContext db) : base(db) { }
|
||||||
|
|
||||||
[HttpGet("~/")]
|
[HttpGet("~/")]
|
||||||
|
[AllowAnonymous]
|
||||||
public async Task<IActionResult> Index()
|
public async Task<IActionResult> Index()
|
||||||
{
|
{
|
||||||
var webLog = WebLogCache.Get(HttpContext);
|
if (WebLog.DefaultPage == "posts") return await PageOfPosts(1);
|
||||||
if (webLog.DefaultPage == "posts")
|
|
||||||
|
var page = await Db.Pages.FindById(WebLog.DefaultPage);
|
||||||
|
return page is null ? NotFound() : ThemedView("SinglePage", new SinglePageModel(page, WebLog));
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet("~/page/{pageNbr:int}")]
|
||||||
|
[AllowAnonymous]
|
||||||
|
public async Task<IActionResult> PageOfPosts(int pageNbr) =>
|
||||||
|
ThemedView("Index",
|
||||||
|
new MultiplePostModel(await Db.Posts.FindPageOfPublishedPosts(pageNbr, WebLog.PostsPerPage), WebLog));
|
||||||
|
|
||||||
|
[HttpGet("~/{*permalink}")]
|
||||||
|
public async Task<IActionResult> CatchAll(string permalink)
|
||||||
{
|
{
|
||||||
var posts = await Db.Posts.FindPageOfPublishedPosts(1, webLog.PostsPerPage);
|
Console.Write($"Got permalink |{permalink}|");
|
||||||
return ThemedView("Index", posts);
|
var post = await Db.Posts.FindByPermalink(permalink);
|
||||||
|
if (post != null)
|
||||||
|
{
|
||||||
|
// TODO: return via single-post action
|
||||||
}
|
}
|
||||||
var page = await Db.Pages.FindById(webLog.DefaultPage);
|
|
||||||
return page is null ? NotFound() : ThemedView("SinglePage", page);
|
var page = await Db.Pages.FindByPermalink(permalink);
|
||||||
|
if (page != null)
|
||||||
|
{
|
||||||
|
return ThemedView("SinglePage", new SinglePageModel(page, WebLog));
|
||||||
|
}
|
||||||
|
|
||||||
|
// TOOD: search prior permalinks for posts and pages
|
||||||
|
|
||||||
|
await Task.CompletedTask;
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet("all")]
|
||||||
|
public async Task<IActionResult> All()
|
||||||
|
{
|
||||||
|
await Task.CompletedTask;
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet("{id}/edit")]
|
||||||
|
public async Task<IActionResult> Edit(string id)
|
||||||
|
{
|
||||||
|
await Task.CompletedTask;
|
||||||
|
throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,11 +9,16 @@ public abstract class MyWebLogController : Controller
|
||||||
/// </summary>
|
/// </summary>
|
||||||
protected WebLogDbContext Db { get; init; }
|
protected WebLogDbContext Db { get; init; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The details for the current web log
|
||||||
|
/// </summary>
|
||||||
|
protected WebLogDetails WebLog => WebLogCache.Get(HttpContext);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Constructor
|
/// Constructor
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="db">The data context to use to fulfil this request</param>
|
/// <param name="db">The data context to use to fulfil this request</param>
|
||||||
protected MyWebLogController(WebLogDbContext db)
|
protected MyWebLogController(WebLogDbContext db) : base()
|
||||||
{
|
{
|
||||||
Db = db;
|
Db = db;
|
||||||
}
|
}
|
||||||
|
|
21
src/MyWebLog/Features/Shared/MyWebLogModel.cs
Normal file
21
src/MyWebLog/Features/Shared/MyWebLogModel.cs
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
namespace MyWebLog.Features.Shared;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Base model class for myWebLog views
|
||||||
|
/// </summary>
|
||||||
|
public class MyWebLogModel
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The details for the web log
|
||||||
|
/// </summary>
|
||||||
|
public WebLogDetails WebLog { get; init; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructor
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="webLog">The details for the web log</param>
|
||||||
|
protected MyWebLogModel(WebLogDetails webLog)
|
||||||
|
{
|
||||||
|
WebLog = webLog;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,12 +1,9 @@
|
||||||
@inject IHttpContextAccessor ctxAcc
|
@model MyWebLogModel
|
||||||
@{
|
|
||||||
var details = WebLogCache.Get(ctxAcc.HttpContext!);
|
|
||||||
}
|
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta name="viewport" content="width=device-width" />
|
<meta name="viewport" content="width=device-width" />
|
||||||
<title>@ViewBag.Title « @Resources.Admin « @details.Name</title>
|
<title>@ViewBag.Title « @Resources.Admin « @Model.WebLog.Name</title>
|
||||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css"
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css"
|
||||||
integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
|
integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
|
||||||
<link rel="stylesheet" href="~/css/admin.css">
|
<link rel="stylesheet" href="~/css/admin.css">
|
||||||
|
@ -15,7 +12,7 @@
|
||||||
<header>
|
<header>
|
||||||
<nav class="navbar navbar-dark bg-dark navbar-expand-md justify-content-start px-2">
|
<nav class="navbar navbar-dark bg-dark navbar-expand-md justify-content-start px-2">
|
||||||
<div class="container-fluid">
|
<div class="container-fluid">
|
||||||
<a class="navbar-brand" href="~/">@details.Name</a>
|
<a class="navbar-brand" href="~/">@Model.WebLog.Name</a>
|
||||||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarText"
|
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarText"
|
||||||
aria-controls="navbarText" aria-expanded="false" aria-label="Toggle navigation">
|
aria-controls="navbarText" aria-expanded="false" aria-label="Toggle navigation">
|
||||||
<span class="navbar-toggler-icon"></span>
|
<span class="navbar-toggler-icon"></span>
|
||||||
|
|
|
@ -5,7 +5,7 @@ namespace MyWebLog.Features.Users;
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The model to use to allow a user to log on
|
/// The model to use to allow a user to log on
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class LogOnModel
|
public class LogOnModel : MyWebLogModel
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The user's e-mail address
|
/// The user's e-mail address
|
||||||
|
@ -21,4 +21,10 @@ public class LogOnModel
|
||||||
[Required(AllowEmptyStrings = false)]
|
[Required(AllowEmptyStrings = false)]
|
||||||
[Display(ResourceType = typeof(Resources), Name = "Password")]
|
[Display(ResourceType = typeof(Resources), Name = "Password")]
|
||||||
public string Password { get; set; } = "";
|
public string Password { get; set; } = "";
|
||||||
|
|
||||||
|
[Obsolete("Only used for model binding; use the WebLogDetails constructor")]
|
||||||
|
public LogOnModel() : base(new()) { }
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public LogOnModel(WebLogDetails webLog) : base(webLog) { }
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,7 +33,7 @@ public class UserController : MyWebLogController
|
||||||
|
|
||||||
[HttpGet("log-on")]
|
[HttpGet("log-on")]
|
||||||
public IActionResult LogOn() =>
|
public IActionResult LogOn() =>
|
||||||
View(new LogOnModel());
|
View(new LogOnModel(WebLog));
|
||||||
|
|
||||||
[HttpPost("log-on")]
|
[HttpPost("log-on")]
|
||||||
public async Task<IActionResult> DoLogOn(LogOnModel model)
|
public async Task<IActionResult> DoLogOn(LogOnModel model)
|
||||||
|
|
54
src/MyWebLog/Properties/Resources.Designer.cs
generated
54
src/MyWebLog/Properties/Resources.Designer.cs
generated
|
@ -69,6 +69,15 @@ namespace MyWebLog.Properties {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to Categories.
|
||||||
|
/// </summary>
|
||||||
|
public static string Categories {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("Categories", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Looks up a localized string similar to Dashboard.
|
/// Looks up a localized string similar to Dashboard.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -105,6 +114,15 @@ namespace MyWebLog.Properties {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to Pages.
|
||||||
|
/// </summary>
|
||||||
|
public static string Pages {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("Pages", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Looks up a localized string similar to Password.
|
/// Looks up a localized string similar to Password.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -113,5 +131,41 @@ namespace MyWebLog.Properties {
|
||||||
return ResourceManager.GetString("Password", resourceCulture);
|
return ResourceManager.GetString("Password", resourceCulture);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to Posts.
|
||||||
|
/// </summary>
|
||||||
|
public static string Posts {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("Posts", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to There are {0} categories.
|
||||||
|
/// </summary>
|
||||||
|
public static string ThereAreXCategories {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("ThereAreXCategories", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to There are {0} pages.
|
||||||
|
/// </summary>
|
||||||
|
public static string ThereAreXPages {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("ThereAreXPages", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to There are {0} published posts and {1} drafts.
|
||||||
|
/// </summary>
|
||||||
|
public static string ThereAreXPublishedPostsAndYDrafts {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("ThereAreXPublishedPostsAndYDrafts", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -120,6 +120,9 @@
|
||||||
<data name="Admin" xml:space="preserve">
|
<data name="Admin" xml:space="preserve">
|
||||||
<value>Admin</value>
|
<value>Admin</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="Categories" xml:space="preserve">
|
||||||
|
<value>Categories</value>
|
||||||
|
</data>
|
||||||
<data name="Dashboard" xml:space="preserve">
|
<data name="Dashboard" xml:space="preserve">
|
||||||
<value>Dashboard</value>
|
<value>Dashboard</value>
|
||||||
</data>
|
</data>
|
||||||
|
@ -132,7 +135,22 @@
|
||||||
<data name="LogOn" xml:space="preserve">
|
<data name="LogOn" xml:space="preserve">
|
||||||
<value>Log On</value>
|
<value>Log On</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="Pages" xml:space="preserve">
|
||||||
|
<value>Pages</value>
|
||||||
|
</data>
|
||||||
<data name="Password" xml:space="preserve">
|
<data name="Password" xml:space="preserve">
|
||||||
<value>Password</value>
|
<value>Password</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="Posts" xml:space="preserve">
|
||||||
|
<value>Posts</value>
|
||||||
|
</data>
|
||||||
|
<data name="ThereAreXCategories" xml:space="preserve">
|
||||||
|
<value>There are {0} categories</value>
|
||||||
|
</data>
|
||||||
|
<data name="ThereAreXPages" xml:space="preserve">
|
||||||
|
<value>There are {0} pages</value>
|
||||||
|
</data>
|
||||||
|
<data name="ThereAreXPublishedPostsAndYDrafts" xml:space="preserve">
|
||||||
|
<value>There are {0} published posts and {1} drafts</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
|
@ -1,19 +1,16 @@
|
||||||
@inject IHttpContextAccessor ctxAcc
|
@model MyWebLogModel
|
||||||
@{
|
|
||||||
var details = WebLogCache.Get(ctxAcc.HttpContext!);
|
|
||||||
}
|
|
||||||
<header>
|
<header>
|
||||||
<nav class="navbar navbar-light bg-light navbar-expand-md justify-content-start px-2">
|
<nav class="navbar navbar-light bg-light navbar-expand-md justify-content-start px-2">
|
||||||
<div class="container-fluid">
|
<div class="container-fluid">
|
||||||
<a class="navbar-brand" href="~/">@details.Name</a>
|
<a class="navbar-brand" href="~/">@Model.WebLog.Name</a>
|
||||||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarText"
|
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarText"
|
||||||
aria-controls="navbarText" aria-expanded="false" aria-label="Toggle navigation">
|
aria-controls="navbarText" aria-expanded="false" aria-label="Toggle navigation">
|
||||||
<span class="navbar-toggler-icon"></span>
|
<span class="navbar-toggler-icon"></span>
|
||||||
</button>
|
</button>
|
||||||
<div class="collapse navbar-collapse" id="navbarText">
|
<div class="collapse navbar-collapse" id="navbarText">
|
||||||
@if (details.Subtitle is not null)
|
@if (Model.WebLog.Subtitle is not null)
|
||||||
{
|
{
|
||||||
<span class="navbar-text">@details.Subtitle</span>
|
<span class="navbar-text">@Model.WebLog.Subtitle</span>
|
||||||
}
|
}
|
||||||
@* TODO: list pages for current web log *@
|
@* TODO: list pages for current web log *@
|
||||||
@await Html.PartialAsync("_LogOnOffPartial")
|
@await Html.PartialAsync("_LogOnOffPartial")
|
||||||
|
|
|
@ -1,7 +1,4 @@
|
||||||
@inject IHttpContextAccessor ctxAcc
|
@model MyWebLogModel
|
||||||
@{
|
|
||||||
var details = WebLogCache.Get(ctxAcc.HttpContext!);
|
|
||||||
}
|
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
|
@ -9,9 +6,9 @@
|
||||||
<meta name="generator" content="myWebLog 2">
|
<meta name="generator" content="myWebLog 2">
|
||||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css"
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css"
|
||||||
integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
|
integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
|
||||||
<link rel="stylesheet" href="~/css/@details.ThemePath/style.css">
|
<link rel="stylesheet" href="~/css/@Model.WebLog.ThemePath/style.css">
|
||||||
@await RenderSectionAsync("Style", false)
|
@await RenderSectionAsync("Style", false)
|
||||||
<title>@ViewBag.Title « @details.Name</title>
|
<title>@ViewBag.Title « @Model.WebLog.Name</title>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
@if (IsSectionDefined("Header"))
|
@if (IsSectionDefined("Header"))
|
||||||
|
@ -20,7 +17,7 @@
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@await Html.PartialAsync("_DefaultHeader")
|
@await Html.PartialAsync("_DefaultHeader", Model)
|
||||||
}
|
}
|
||||||
<main>
|
<main>
|
||||||
@RenderBody()
|
@RenderBody()
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
@model Page
|
@using MyWebLog.Features.Pages
|
||||||
|
@model SinglePageModel
|
||||||
@{
|
@{
|
||||||
Layout = "_Layout";
|
Layout = "_Layout";
|
||||||
ViewBag.Title = Model.Title;
|
ViewBag.Title = Model.Page.Title;
|
||||||
}
|
}
|
||||||
<h2>@Model.Title</h2>
|
<h2>@Model.Page.Title</h2>
|
||||||
<article>
|
<article>
|
||||||
@Html.Raw(Model.Text)
|
@Html.Raw(Model.Page.Text)
|
||||||
</article>
|
</article>
|
||||||
|
|
Loading…
Reference in New Issue
Block a user