Compare commits

...

11 Commits

16 changed files with 219 additions and 63 deletions

View File

@ -77,6 +77,7 @@ public sealed class BlogApiController : ControllerBase
excerpt = _blogPostService.RenderExcerpt(post, out bool trimmed),
content = includeContent ? _blogPostService.RenderPost(post) : null,
trimmed,
tags = post.Tags.Select(t => t.Replace(' ', '-')),
url = new
{
year = post.Published.ToString("yyyy"),

View File

@ -0,0 +1,71 @@
using MailKitSimplified.Sender.Services;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Primitives;
namespace OliverBooth.Controllers;
[Controller]
[Route("contact/submit")]
public class ContactController : Controller
{
private readonly ILogger<ContactController> _logger;
private readonly IConfiguration _configuration;
private readonly IConfigurationSection _destination;
/// <summary>
/// Initializes a new instance of the <see cref="ContactController" /> class.
/// </summary>
/// <param name="logger">The logger.</param>
/// <param name="configuration">The configuration.</param>
public ContactController(ILogger<ContactController> logger, IConfiguration configuration)
{
_logger = logger;
_configuration = configuration;
_destination = configuration.GetSection("Mail").GetSection("Destination");
}
[HttpGet("{_?}")]
public IActionResult OnGet(string _)
{
_logger.LogWarning("Method GET for endpoint {Path} is not supported!", Request.Path);
return RedirectToPage("/Contact/Index");
}
[HttpPost("other")]
public async Task<IActionResult> HandleMiscellaneous()
{
if (!Request.HasFormContentType)
{
return RedirectToPage("/Contact/Other");
}
IFormCollection form = Request.Form;
StringValues name = form["name"];
StringValues email = form["email"];
StringValues subject = form["subject"];
StringValues message = form["message"];
await using SmtpSender sender = CreateSender();
await sender.WriteEmail
.To("Oliver Booth", _destination.GetValue<string>("Other"))
.From(name, email)
.Subject(subject)
.BodyHtml(message)
.SendAsync();
TempData["Success"] = true;
return RedirectToPage("/Contact/Result");
}
private SmtpSender CreateSender()
{
IConfigurationSection mailSection = _configuration.GetSection("Mail");
string? mailServer = mailSection.GetSection("Server").Value;
string? mailUsername = mailSection.GetSection("Username").Value;
string? mailPassword = mailSection.GetSection("Password").Value;
var sender = SmtpSender.Create(mailServer);
sender.SetCredential(mailUsername, mailPassword);
return sender;
}
}

View File

@ -34,6 +34,9 @@ internal sealed class BlogPost : IBlogPost
/// <inheritdoc />
public string Slug { get; internal set; } = string.Empty;
/// <inheritdoc />
public IReadOnlyList<string> Tags { get; internal set; } = ArraySegment<string>.Empty;
/// <inheritdoc />
public string Title { get; internal set; } = string.Empty;

View File

@ -28,5 +28,10 @@ internal sealed class BlogPostConfiguration : IEntityTypeConfiguration<BlogPost>
builder.Property(e => e.DisqusPath).IsRequired(false);
builder.Property(e => e.Visibility).HasConversion(new EnumToStringConverter<BlogPostVisibility>()).IsRequired();
builder.Property(e => e.Password).HasMaxLength(255).IsRequired(false);
builder.Property(e => e.Tags).IsRequired()
.HasConversion(
tags => string.Join(' ', tags.Select(t => t.Replace(' ', '-'))),
tags => tags.Split(' ', StringSplitOptions.RemoveEmptyEntries)
.Select(t => t.Replace('-', ' ')).ToArray());
}
}

View File

@ -63,6 +63,12 @@ public interface IBlogPost
/// <value>The slug of the post.</value>
string Slug { get; }
/// <summary>
/// Gets the tags of the post.
/// </summary>
/// <value>The tags of the post.</value>
IReadOnlyList<string> Tags { get; }
/// <summary>
/// Gets the title of the post.
/// </summary>

View File

@ -64,6 +64,13 @@
<a href="#disqus_thread" data-disqus-identifier="@post.GetDisqusIdentifier()">0 Comments</a>
}
</p>
<div>
@foreach (string tag in post.Tags)
{
<a asp-page="Index" asp-route-tag="@tag" class="badge bg-secondary">@tag</a>
}
</div>
<hr>
<article data-blog-post="true" data-blog-id="@post.Id.ToString("D")">
<p class="text-center">Loading ...</p>

View File

@ -10,12 +10,8 @@
</div>
<script id="blog-post-template" type="text/x-handlebars-template">
<div class="card-body">
<h2>
<a href="{{post.url}}"> {{post.title}}</a>
</h2>
<p class="text-muted">
<div class="card-header">
<span class="text-muted">
<img class="blog-author-icon" src="{{author.avatar}}" alt="{{author.name}}">
<span>{{author.name}}<span>
<span> &bull; </span>
@ -26,7 +22,12 @@
Loading comment count &hellip;
</a>
{{/if}}
</p>
</span>
</div>
<div class="card-body">
<h2>
<a href="{{post.url}}"> {{post.title}}</a>
</h2>
<p>{{{post.excerpt}}}</p>
@ -38,4 +39,9 @@
</p>
{{/if}}
</div>
<div class="card-footer">
{{#each post.tags}}
<a href="?tag={{this}}" class="badge text-bg-dark">{{this}}</a>
{{/each}}
</div>
</script>

View File

@ -23,13 +23,13 @@
<input type="hidden" name="contact-type" value="job-opportunity">
<div class="form-group" style="margin-top: 10px;">
<label for="your-name">Your Name</label>
<input type="text" class="form-control" id="your-name" name="your-name" placeholder="Who are you?" required>
<label for="name">Your Name</label>
<input type="text" class="form-control" id="name" name="name" placeholder="Who are you?" required>
</div>
<div class="form-group" style="margin-top: 10px;">
<label for="your-email">Your Email Address</label>
<input type="email" class="form-control" id="your-email" name="your-email" placeholder="How can I reach you?" required>
<label for="email">Your Email Address</label>
<input type="email" class="form-control" id="email" name="email" placeholder="How can I reach you?" required>
</div>
<div class="form-group" style="margin-top: 10px;">
@ -49,7 +49,7 @@
<div class="form-group" style="margin-top: 10px;">
<label for="starting-salary">Starting Salary</label>
<input type="number" class="form-control" id="starting-salary" name="starting-salary" placeholder="e.g. £30,000" required>
<input type="text" class="form-control" id="starting-salary" name="starting-salary" placeholder="e.g. £30,000" required>
</div>
<button type="submit" class="btn btn-primary" style="margin-top: 10px;">Submit</button>

View File

@ -18,8 +18,8 @@
will review it as soon as I can!
</p>
<p>
Below is a <strong>non-exhaustive</strong> list of the repositories that are currently active. This list is subject
to change at any time, and may not be up to date. For a better gauge of what I am currently working on, please
Below is a <mark>non-exhaustive</mark> list of the repositories that are currently active. This list is subject
to change at any time, but may not be up-to-date. For a better gauge of what I am currently working on, please
visit my <a href="https://github.com/oliverbooth">GitHub profile</a>. If you would like to contribute to a project
that is not in active development, I would recommend that you fork the repository and continue development on your
own as unfortunately I have no timeline as to when I will start such projects back up again.

View File

@ -18,17 +18,17 @@
can.
</p>
<form>
<form method="post" asp-controller="Contact" asp-action="HandleMiscellaneous">
<input type="hidden" name="contact-type" value="other">
<div class="form-group" style="margin-top: 10px;">
<label for="your-name">Your Name</label>
<input type="text" class="form-control" id="your-name" name="your-name" placeholder="Who are you?" required>
<label for="name">Your Name</label>
<input type="text" class="form-control" id="name" name="name" placeholder="Who are you?" required>
</div>
<div class="form-group" style="margin-top: 10px;">
<label for="your-email">Your Email Address</label>
<input type="email" class="form-control" id="your-email" name="your-email" placeholder="How can I reach you?" required>
<label for="email">Your Email Address</label>
<input type="email" class="form-control" id="email" name="email" placeholder="How can I reach you?" required>
</div>
<div class="form-group" style="margin-top: 10px;">

View File

@ -40,13 +40,13 @@
<input type="hidden" name="contact-type" value="project-idea">
<div class="form-group" style="margin-top: 10px;">
<label for="project-title">Your Name</label>
<input type="text" class="form-control" id="name" name="project-title" placeholder="Who are you?">
<label for="name">Your Name</label>
<input type="text" class="form-control" id="name" name="name" placeholder="Who are you?">
</div>
<div class="form-group" style="margin-top: 10px;">
<label for="project-title">Your Email Address</label>
<input type="email" class="form-control" id="email" name="project-title" placeholder="How can I reach you, if I need to?">
<label for="email">Your Email Address</label>
<input type="email" class="form-control" id="email" name="email" placeholder="How can I reach you, if I need to?">
</div>
<div class="form-group" style="margin-top: 10px;">

View File

@ -0,0 +1,25 @@
@page
@model OliverBooth.Pages.Contact.Result
@{
ViewData["Title"] = "Contact";
}
@if (Model.WasSuccessful)
{
<h1 class="display-4 text-success"><i class="fa-solid fa-circle-check"></i> Sent successfully!</h1>
<p>Thank you for getting in touch. I will get back to you as soon as possible.</p>
<p>
In the meantime, why not check out my <a asp-page="/Blog/Index">blog</a> or
<a asp-page="/Projects/Index">portfolio</a>?
</p>
}
else
{
<h1 class="display-4 text-danger"><i class="fa-solid fa-circle-xmark"></i> A problem occured</h1>
<p>Sorry, something went wrong. This has been logged and if I'll get to it soon.</p>
<p>
You can <a asp-page="Index">try again</a>, or check out my <a asp-page="/Blog/Index">blog</a> or
<a asp-page="/Projects/Index">portfolio</a>!
</p>
}

View File

@ -0,0 +1,23 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
namespace OliverBooth.Pages.Contact;
public class Result : PageModel
{
public bool WasSuccessful { get; private set; }
public IActionResult OnGet()
{
if (!TempData.ContainsKey("Success"))
{
return RedirectToPage("/Contact/Index");
}
#pragma warning disable S1125
WasSuccessful = TempData["Success"] is true;
#pragma warning restore S1125
TempData.Remove("Success");
return Page();
}
}

View File

@ -4,11 +4,11 @@
}
<h1 class="display-4">Projects</h1>
<h2 class="lead" style="margin-top: 20px">Ongoing Development</h2>
<p>This is work that I am actively developing and/or maintaining.</p>
<div class="card-group row">
<div class="col-xs-1 col-md-6 col-lg-6 d-flex align-items-stretch">
<div class="card">
<div class="card border-success project-card">
<div class="card-header text-bg-success">In Active Development</div>
<img src="~/img/projects/hero/x10d-1280x640.png" class="card-img-top" alt="X10D">
<div class="card-body">
<h5 class="card-title">X10D</h5>
@ -18,11 +18,12 @@
</div>
</div>
<div class="col-xs-1 col-md-6 col-lg-6 d-flex align-items-stretch">
<div class="card">
<div class="card border-success project-card">
<div class="card-header text-bg-success">In Active Development</div>
<img src="~/img/projects/hero/brackeysbot-1280x640.png" class="card-img-top" alt="BrackeysBot">
<div class="card-body">
<h5 class="card-title">BrackeysBot</h5>
<p class="card-text">A collection of self-contained Discord bots that power the <a href="https://discord.gg/brackeys">Brackeys Community</a> Disccord server.</p>
<p class="card-text">A collection of self-contained Discord bots that power the <a href="https://discord.gg/brackeys">Brackeys Community</a> Discord server.</p>
<a href="https://github.com/BrackeysBot" class="btn btn-primary">View on GitHub</a>
</div>
</div>
@ -30,7 +31,8 @@
</div>
<div class="card-group row" style="margin-top: 20px;">
<div class="col-xs-1 col-md-6 col-lg-6 d-flex align-items-stretch">
<div class="card">
<div class="card border-success project-card">
<div class="card-header text-bg-success">In Active Development</div>
<img src="~/img/projects/hero/none-1280x640.png" class="card-img-top" alt="Project KW">
<div class="card-body">
<h5 class="card-title">Project KW</h5>
@ -41,14 +43,10 @@
</div>
</div>
<h2 class="lead" style="margin-top: 20px">Past Work</h2>
<p>
This is work that I have deemed either "ready for public consumption" or "no further work needs to be done". It
includes projects that are both retired and still in use.
</p>
<div class="card-group row" style="margin-top: 20px;">
<div class="col-xs-1 col-md-6 col-lg-6 d-flex align-items-stretch">
<div class="card">
<div class="card border-info project-card">
<div class="card-header text-bg-info">Past Work</div>
<img src="~/img/projects/hero/fiveoclock-1280x640.png" class="card-img-top" alt="It's 5 O'Clock Somewhere">
<div class="card-body">
<h5 class="card-title">It's 5 O'Clock Somewhere</h5>
@ -58,7 +56,8 @@
</div>
</div>
<div class="col-xs-1 col-md-6 col-lg-6 d-flex align-items-stretch">
<div class="card">
<div class="card border-info project-card">
<div class="card-header text-bg-info">Past Work</div>
<img src="~/img/projects/hero/birthday-1280x640.png" class="card-img-top" alt="Is It My Birthday?">
<div class="card-body">
<h5 class="card-title">Is It My Birthday?</h5>
@ -70,7 +69,8 @@
</div>
<div class="card-group row" style="margin-top: 20px;">
<div class="col-xs-1 col-md-6 col-lg-6 d-flex align-items-stretch">
<div class="card">
<div class="card border-info project-card">
<div class="card-header text-bg-info">Past Work</div>
<img src="~/img/projects/hero/candyjam-1280x640.png" class="card-img-top" alt="Scrolling Candy Apple Saga">
<div class="card-body">
<h5 class="card-title">Scrolling Candy Apple Saga</h5>
@ -80,8 +80,9 @@
</div>
</div>
<div class="col-xs-1 col-md-6 col-lg-6 d-flex align-items-stretch">
<div class="card">
<img src="~/img/projects/hero/none-1280x640.png" class="card-img-top" alt="SAMP.NET">
<div class="card border-light project-card">
<div class="card-header text-bg-light">Retired</div>
<img src="~/img/projects/hero/sampdotnet-1280x640.png" class="card-img-top" alt="SAMP.NET">
<div class="card-body">
<h5 class="card-title">SAMP.NET</h5>
<p class="card-text">A .NET wrapper for the Pawn SAMP API, made during my time in college.</p>
@ -92,7 +93,8 @@
</div>
<div class="card-group row" style="margin-top: 20px;">
<div class="col-xs-1 col-md-6 col-lg-6 d-flex align-items-stretch">
<div class="card">
<div class="card border-light project-card">
<div class="card-header text-bg-light">Retired</div>
<img src="~/img/projects/hero/none-1280x640.png" class="card-img-top" alt="SAMP.NET">
<div class="card-body">
<h5 class="card-title">HaloMCCForgePatcher</h5>
@ -101,16 +103,23 @@
</div>
</div>
</div>
<div class="col-xs-1 col-md-6 col-lg-6 d-flex align-items-stretch">
<div class="card border-light project-card">
<div class="card-header text-bg-light">Retired</div>
<img src="~/img/projects/hero/unitydocs-1280x640.png" class="card-img-top" alt="Unity API Docs">
<div class="card-body">
<h5 class="card-title">Unity API Docs</h5>
<p class="card-text">A complete rewrite and redesign of the Unity API docs, inspired by the C# and .NET documentation on Microsoft Learn.</p>
<p>This project was cancelled following Unity's pricing change announcement.</p>
</div>
</div>
</div>
</div>
<h2 class="lead" style="margin-top: 20px">On Hiatus</h2>
<p>
This is work that I have put on indefinite hold; projects that I would like to revisit one day, but I have no
timeline for when or if that will happen.
</p>
<div class="card-group row" style="margin-top: 20px">
<div class="col-xs-1 col-md-6 col-lg-6 d-flex align-items-stretch">
<div class="card">
<div class="card border-dark project-card">
<div class="card-header text-bg-dark">On Hiatus</div>
<img src="~/img/projects/hero/olive-1280x640.png" class="card-img-top" alt="Olive">
<div class="card-body">
<h5 class="card-title">Olive</h5>
@ -120,7 +129,8 @@
</div>
</div>
<div class="col-xs-1 col-md-6 col-lg-6 d-flex align-items-stretch">
<div class="card">
<div class="card border-dark project-card">
<div class="card-header text-bg-dark">On Hiatus</div>
<img src="~/img/projects/hero/none-1280x640.png" class="card-img-top" alt="TCP.NET">
<div class="card-body">
<h5 class="card-title">TCP.NET</h5>
@ -132,7 +142,8 @@
</div>
<div class="card-group row" style="margin-top: 20px;">
<div class="col-xs-1 col-md-6 col-lg-6 d-flex align-items-stretch">
<div class="card">
<div class="card border-dark project-card">
<div class="card-header text-bg-dark">On Hiatus</div>
<img src="~/img/projects/hero/none-1280x640.png" class="card-img-top" alt="MelonSharp">
<div class="card-body">
<h5 class="card-title">MelonSharp</h5>
@ -142,7 +153,8 @@
</div>
</div>
<div class="col-xs-1 col-md-6 col-lg-6 d-flex align-items-stretch">
<div class="card">
<div class="card border-dark project-card">
<div class="card-header text-bg-dark">On Hiatus</div>
<img src="~/img/projects/hero/syntaxgen-1280x640.png" class="card-img-top" alt="SyntaxGen.NET">
<div class="card-body">
<h5 class="card-title">SyntaxGen.NET</h5>
@ -154,17 +166,8 @@
</div>
<div class="card-group row" style="margin-top: 20px;">
<div class="col-xs-1 col-md-6 col-lg-6 d-flex align-items-stretch">
<div class="card">
<img src="~/img/projects/hero/unitydocs-1280x640.png" class="card-img-top" alt="Unity API Docs">
<div class="card-body">
<h5 class="card-title">Unity API Docs</h5>
<p class="card-text">A complete rewrite and redesign of the Unity API docs, inspired by the C# and .NET documentation on Microsoft Learn.</p>
<p>This project is currently closed source until a stable prototype is ready.</p>
</div>
</div>
</div>
<div class="col-xs-1 col-md-6 col-lg-6 d-flex align-items-stretch">
<div class="card">
<div class="card border-dark project-card">
<div class="card-header text-bg-dark">On Hiatus</div>
<img src="~/img/projects/hero/mutation-1280x640.png" class="card-img-top" alt="Mutation">
<div class="card-body">
<h5 class="card-title">Mutation</h5>
@ -173,10 +176,9 @@
</div>
</div>
</div>
</div>
<div class="card-group row" style="margin-top: 20px">
<div class="col-xs-1 col-md-6 col-lg-6 d-flex align-items-stretch">
<div class="card">
<div class="card border-dark project-card">
<div class="card-header text-bg-dark">On Hiatus</div>
<img src="~/img/projects/hero/none-1280x640.png" class="card-img-top" alt="Olive">
<div class="card-body">
<h5 class="card-title">Astraeos</h5>

View File

@ -14,6 +14,7 @@ class BlogPost {
private readonly _identifier: string;
private readonly _humanizedTimestamp: string;
private readonly _formattedDate: string;
private readonly _tags: string[];
constructor(json: any) {
this._id = json.id;
@ -29,6 +30,7 @@ class BlogPost {
this._identifier = json.identifier;
this._humanizedTimestamp = json.humanizedTimestamp;
this._formattedDate = json.formattedDate;
this._tags = json.tags;
}
get id(): string {
@ -66,6 +68,10 @@ class BlogPost {
get url(): BlogUrl {
return this._url;
}
get tags(): string[] {
return this._tags;
}
get trimmed(): boolean {
return this._trimmed;

View File

@ -55,6 +55,7 @@ class UI {
enable_comments: post.commentsEnabled,
disqus_identifier: post.identifier,
trimmed: post.trimmed,
tags: post.tags
},
author: {
name: author.name,