Updated views, added static to gitignore properly
This commit is contained in:
parent
8c2dccde3e
commit
a3d818a80a
|
|
@ -2,8 +2,7 @@
|
||||||
*.db
|
*.db
|
||||||
|
|
||||||
# STATIC
|
# STATIC
|
||||||
wwwroot/static/
|
src/FastBlog.Web/wwwroot/static/
|
||||||
|
|
||||||
# BLOGS
|
# BLOGS
|
||||||
md/
|
md/
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ public interface IBlogMetaRepository
|
||||||
{
|
{
|
||||||
Task<BlogMeta?> Get(string? slug);
|
Task<BlogMeta?> Get(string? slug);
|
||||||
Task<BlogMeta?> GetForEdit(int id);
|
Task<BlogMeta?> GetForEdit(int id);
|
||||||
|
Task<bool> Delete(int id);
|
||||||
Task<Result<BlogMeta, BusinessError>> Add(BlogMeta meta);
|
Task<Result<BlogMeta, BusinessError>> Add(BlogMeta meta);
|
||||||
Task<Result<BlogMeta, BusinessError>> Update(BlogMeta meta);
|
Task<Result<BlogMeta, BusinessError>> Update(BlogMeta meta);
|
||||||
}
|
}
|
||||||
|
|
@ -58,7 +58,7 @@ public static class DependencyInjection
|
||||||
if (mainPage is not null)
|
if (mainPage is not null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
await blogService.UpdateBlog(new Blog
|
await blogService.Update(new Blog
|
||||||
{
|
{
|
||||||
Text = BlogDefaultBody,
|
Text = BlogDefaultBody,
|
||||||
Metadata = new BlogMeta
|
Metadata = new BlogMeta
|
||||||
|
|
|
||||||
|
|
@ -44,6 +44,19 @@ public sealed class BlogMetaRepository(SqliteConnectionFactory connectionFactory
|
||||||
return await connection.QueryFirstOrDefaultAsync<BlogMeta>(sql, new { id });
|
return await connection.QueryFirstOrDefaultAsync<BlogMeta>(sql, new { id });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<bool> Delete(int id)
|
||||||
|
{
|
||||||
|
const string sql = """
|
||||||
|
update Blogs
|
||||||
|
set Deleted = 1
|
||||||
|
where id = @id
|
||||||
|
""";
|
||||||
|
|
||||||
|
using var connection = connectionFactory.Create();
|
||||||
|
|
||||||
|
return await connection.ExecuteAsync(sql, new { id }) > 0;
|
||||||
|
}
|
||||||
|
|
||||||
public async Task<Result<BlogMeta, BusinessError>> Add(BlogMeta meta)
|
public async Task<Result<BlogMeta, BusinessError>> Add(BlogMeta meta)
|
||||||
{
|
{
|
||||||
using var connection = connectionFactory.Create();
|
using var connection = connectionFactory.Create();
|
||||||
|
|
|
||||||
|
|
@ -47,12 +47,27 @@ public sealed class BlogService(IBlogMetaRepository metaRepository, IBlogFileRep
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public Task<Result<Blog, BusinessError>> UpdateBlog(Blog blog)
|
public Task<Result<Blog, BusinessError>> Update(Blog blog)
|
||||||
{
|
{
|
||||||
return blog.Metadata.Id is null ? CreateBlog(blog) : UpdateBlogInternal(blog);
|
return blog.Metadata.Id is null ? Create(blog) : UpdateInternal(blog);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<Result<Blog, BusinessError>> CreateBlog(Blog newBlog)
|
public async Task<bool> Delete(int id)
|
||||||
|
{
|
||||||
|
var meta = await metaRepository.GetForEdit(id);
|
||||||
|
if (meta is null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
var toDelete = meta.SourceLocation;
|
||||||
|
|
||||||
|
var result = await metaRepository.Delete(id);
|
||||||
|
if (!result)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return await blogFileRepository.Delete(toDelete);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<Result<Blog, BusinessError>> Create(Blog newBlog)
|
||||||
{
|
{
|
||||||
var metaResult = await metaRepository.Add(newBlog.Metadata);
|
var metaResult = await metaRepository.Add(newBlog.Metadata);
|
||||||
if (metaResult.IsError)
|
if (metaResult.IsError)
|
||||||
|
|
@ -71,7 +86,7 @@ public sealed class BlogService(IBlogMetaRepository metaRepository, IBlogFileRep
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private async Task<Result<Blog, BusinessError>> UpdateBlogInternal(Blog blog)
|
private async Task<Result<Blog, BusinessError>> UpdateInternal(Blog blog)
|
||||||
{
|
{
|
||||||
var oldMeta = await metaRepository.GetForEdit(blog.Metadata.Id!.Value);
|
var oldMeta = await metaRepository.GetForEdit(blog.Metadata.Id!.Value);
|
||||||
if (oldMeta is null)
|
if (oldMeta is null)
|
||||||
|
|
|
||||||
|
|
@ -44,12 +44,12 @@ public class BlogsController(BlogService service) : Controller
|
||||||
{
|
{
|
||||||
if (!id.HasValue)
|
if (!id.HasValue)
|
||||||
{
|
{
|
||||||
var date = DateTime.Now;
|
var date = DateTime.UtcNow;
|
||||||
return View(
|
return View(
|
||||||
new EditBlog
|
new EditBlog
|
||||||
{
|
{
|
||||||
Text = "# My new blog",
|
Text = "# My new blog",
|
||||||
Title = "Blog from " + DateTime.Now.ToString("g"),
|
Title = "Blog from " + DateTime.UtcNow.ToString("g"),
|
||||||
SourceLocation = $"{date:yyyy-MM-dd-HH-mm}_blog.md",
|
SourceLocation = $"{date:yyyy-MM-dd-HH-mm}_blog.md",
|
||||||
CreatedAt = date,
|
CreatedAt = date,
|
||||||
ModifiedAt = date,
|
ModifiedAt = date,
|
||||||
|
|
@ -87,7 +87,7 @@ public class BlogsController(BlogService service) : Controller
|
||||||
[Route("edit")]
|
[Route("edit")]
|
||||||
public async Task<IActionResult> Edit([FromForm] EditBlog editBlog)
|
public async Task<IActionResult> Edit([FromForm] EditBlog editBlog)
|
||||||
{
|
{
|
||||||
var result = await service.UpdateBlog(new()
|
var result = await service.Update(new()
|
||||||
{
|
{
|
||||||
Text = editBlog.Text,
|
Text = editBlog.Text,
|
||||||
Metadata = new()
|
Metadata = new()
|
||||||
|
|
@ -141,4 +141,17 @@ public class BlogsController(BlogService service) : Controller
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[HttpDelete]
|
||||||
|
[Route("{id:int}")]
|
||||||
|
public async Task<IActionResult> Delete(int id)
|
||||||
|
{
|
||||||
|
var result = await service.Delete(id);
|
||||||
|
|
||||||
|
if (!result)
|
||||||
|
return NotFound();
|
||||||
|
|
||||||
|
HttpContext.Response.Headers.Append("Hx-Redirect", "/");
|
||||||
|
return Ok();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -50,7 +50,7 @@ public class FilesController(FileService fileService) : Controller
|
||||||
var meta = new FileMeta
|
var meta = new FileMeta
|
||||||
{
|
{
|
||||||
SourceLocation = Path.Combine(sourcePath ?? "", file.FileName),
|
SourceLocation = Path.Combine(sourcePath ?? "", file.FileName),
|
||||||
CreatedAt = DateTime.Now,
|
CreatedAt = DateTime.UtcNow,
|
||||||
MimeType = file.ContentType
|
MimeType = file.ContentType
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -73,7 +73,7 @@ else
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<button class="preview-btn secondary" hx-post="/blog/preview" hx-target="#preview" hx-swap="OuterHTML">Preview</button>
|
<button class="preview-btn secondary" hx-post="/blogs/preview" hx-target="#preview" hx-swap="OuterHTML">Preview</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,12 @@
|
||||||
|
|
||||||
@{
|
@{
|
||||||
ViewBag.Title = Model.Metadata.Title;
|
ViewBag.Title = Model.Metadata.Title;
|
||||||
var pipelineBuilder = new MarkdownPipelineBuilder().UseAdvancedExtensions().UseHighlightJs();
|
var pipelineBuilder = new MarkdownPipelineBuilder()
|
||||||
|
.UseSmartyPants()
|
||||||
|
.UseEmojiAndSmiley()
|
||||||
|
.UseAlertBlocks()
|
||||||
|
.UseAdvancedExtensions()
|
||||||
|
.UseHighlightJs();
|
||||||
var sanitizer = new HtmlSanitizer();
|
var sanitizer = new HtmlSanitizer();
|
||||||
sanitizer.AllowedAttributes.Add("class");
|
sanitizer.AllowedAttributes.Add("class");
|
||||||
var pipeline = pipelineBuilder.Build();
|
var pipeline = pipelineBuilder.Build();
|
||||||
|
|
@ -37,4 +42,31 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
@Html.Raw(sanitizer.Sanitize(Markdown.ToHtml(Model.Text, pipeline)))
|
@Html.Raw(sanitizer.Sanitize(Markdown.ToHtml(Model.Text, pipeline)))
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<br>
|
||||||
|
|
||||||
|
<div class="container">
|
||||||
|
<hr/>
|
||||||
|
<article>
|
||||||
|
<header>
|
||||||
|
<h3>Editor's controls</h3>
|
||||||
|
</header>
|
||||||
|
<body>
|
||||||
|
<div class="grid">
|
||||||
|
<div>
|
||||||
|
<a href="/edit/@Model.Metadata.Id">
|
||||||
|
<button class="btn-fw">
|
||||||
|
Edit
|
||||||
|
</button>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<button class="btn-fw secondary" hx-delete="/blogs/@Model.Metadata.Id">
|
||||||
|
Delete
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</article>
|
||||||
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,4 @@
|
||||||
@using Ganss.Xss
|
@model FastBlog.Core.Models.Blogs.Blog
|
||||||
@using Markdig;
|
|
||||||
@using Pek.Markdig.HighlightJs
|
|
||||||
@model FastBlog.Core.Models.Blogs.Blog
|
|
||||||
|
|
||||||
@await Html.PartialAsync("Index", Model);
|
@await Html.PartialAsync("Index", Model);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,7 @@ return;
|
||||||
<ul>
|
<ul>
|
||||||
<li><a href="~/"><h1>@options.Value.Title</h1></a></li>
|
<li><a href="~/"><h1>@options.Value.Title</h1></a></li>
|
||||||
<li><a href="~/blog/list">Blog</a></li>
|
<li><a href="~/blog/list">Blog</a></li>
|
||||||
|
<li><a href="~/edit">New</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
<ul>
|
<ul>
|
||||||
@foreach (var link in options.Value.Links)
|
@foreach (var link in options.Value.Links)
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,9 @@
|
||||||
--pico-background-color: #0f1a27;
|
--pico-background-color: #0f1a27;
|
||||||
--pico-card-background-color: #132232;
|
--pico-card-background-color: #132232;
|
||||||
--pico-card-sectioning-background-color: #17273a;
|
--pico-card-sectioning-background-color: #17273a;
|
||||||
|
|
||||||
|
--pico-form-element-background-color: #ffffff08;
|
||||||
|
--pico-form-element-active-background-color: #ffffff18;
|
||||||
}
|
}
|
||||||
|
|
||||||
h1, h2, h3, h4, h5, h6 {
|
h1, h2, h3, h4, h5, h6 {
|
||||||
|
|
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 19 KiB |
Loading…
Reference in New Issue