Compare commits
3 Commits
b9e5f4d108
...
c2a5dd4ee6
Author | SHA1 | Date | |
---|---|---|---|
c2a5dd4ee6 | |||
1f6825c9df | |||
25822c6577 |
47
OliverBooth/Controllers/FormattedBlacklist.cs
Normal file
47
OliverBooth/Controllers/FormattedBlacklist.cs
Normal file
@ -0,0 +1,47 @@
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using OliverBooth.Data.Web;
|
||||
using OliverBooth.Services;
|
||||
|
||||
namespace OliverBooth.Controllers;
|
||||
|
||||
[Controller]
|
||||
[Route("contact/blacklist/formatted")]
|
||||
public class FormattedBlacklistController : Controller
|
||||
{
|
||||
private readonly IContactService _contactService;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="FormattedBlacklistController" /> class.
|
||||
/// </summary>
|
||||
/// <param name="contactService">The <see cref="IContactService" />.</param>
|
||||
public FormattedBlacklistController(IContactService contactService)
|
||||
{
|
||||
_contactService = contactService;
|
||||
}
|
||||
|
||||
[HttpGet("{format}")]
|
||||
public IActionResult OnGet([FromRoute] string format)
|
||||
{
|
||||
IReadOnlyCollection<IBlacklistEntry> blacklist = _contactService.GetBlacklist();
|
||||
|
||||
switch (format)
|
||||
{
|
||||
case "json":
|
||||
return Json(blacklist);
|
||||
|
||||
case "csv":
|
||||
var builder = new StringBuilder();
|
||||
builder.AppendLine("EmailAddress,Name,Reason");
|
||||
foreach (IBlacklistEntry entry in blacklist)
|
||||
{
|
||||
builder.AppendLine($"{entry.EmailAddress},{entry.Name},{entry.Reason}");
|
||||
}
|
||||
|
||||
return Content(builder.ToString(), "text/csv", Encoding.UTF8);
|
||||
}
|
||||
|
||||
return NotFound();
|
||||
}
|
||||
}
|
75
OliverBooth/Data/Web/BlacklistEntry.cs
Normal file
75
OliverBooth/Data/Web/BlacklistEntry.cs
Normal file
@ -0,0 +1,75 @@
|
||||
namespace OliverBooth.Data.Web;
|
||||
|
||||
/// <inheritdoc cref="IBlacklistEntry"/>
|
||||
internal sealed class BlacklistEntry : IEquatable<BlacklistEntry>, IBlacklistEntry
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public string EmailAddress { get; internal set; } = string.Empty;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string Name { get; internal set; } = string.Empty;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string Reason { get; internal set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Returns a value indicating whether two instances of <see cref="BlacklistEntry" /> are equal.
|
||||
/// </summary>
|
||||
/// <param name="left">The first instance of <see cref="BlacklistEntry" /> to compare.</param>
|
||||
/// <param name="right">The second instance of <see cref="BlacklistEntry" /> to compare.</param>
|
||||
/// <returns>
|
||||
/// <see langword="true" /> if <paramref name="left" /> and <paramref name="right" /> are equal; otherwise,
|
||||
/// <see langword="false" />.
|
||||
/// </returns>
|
||||
public static bool operator ==(BlacklistEntry? left, BlacklistEntry? right) => Equals(left, right);
|
||||
|
||||
/// <summary>
|
||||
/// Returns a value indicating whether two instances of <see cref="BlacklistEntry" /> are not equal.
|
||||
/// </summary>
|
||||
/// <param name="left">The first instance of <see cref="BlacklistEntry" /> to compare.</param>
|
||||
/// <param name="right">The second instance of <see cref="BlacklistEntry" /> to compare.</param>
|
||||
/// <returns>
|
||||
/// <see langword="true" /> if <paramref name="left" /> and <paramref name="right" /> are not equal; otherwise,
|
||||
/// <see langword="false" />.
|
||||
/// </returns>
|
||||
public static bool operator !=(BlacklistEntry? left, BlacklistEntry? right) => !(left == right);
|
||||
|
||||
/// <summary>
|
||||
/// Returns a value indicating whether this instance of <see cref="BlacklistEntry" /> is equal to another
|
||||
/// instance.
|
||||
/// </summary>
|
||||
/// <param name="other">An instance to compare with this instance.</param>
|
||||
/// <returns>
|
||||
/// <see langword="true" /> if <paramref name="other" /> is equal to this instance; otherwise,
|
||||
/// <see langword="false" />.
|
||||
/// </returns>
|
||||
public bool Equals(BlacklistEntry? other)
|
||||
{
|
||||
if (ReferenceEquals(null, other)) return false;
|
||||
if (ReferenceEquals(this, other)) return true;
|
||||
return EmailAddress.Equals(other.EmailAddress);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a value indicating whether this instance is equal to a specified object.
|
||||
/// </summary>
|
||||
/// <param name="obj">An object to compare with this instance.</param>
|
||||
/// <returns>
|
||||
/// <see langword="true" /> if <paramref name="obj" /> is an instance of <see cref="BlacklistEntry" /> and
|
||||
/// equals the value of this instance; otherwise, <see langword="false" />.
|
||||
/// </returns>
|
||||
public override bool Equals(object? obj)
|
||||
{
|
||||
return ReferenceEquals(this, obj) || obj is BlacklistEntry other && Equals(other);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the hash code for this instance.
|
||||
/// </summary>
|
||||
/// <returns>The hash code.</returns>
|
||||
public override int GetHashCode()
|
||||
{
|
||||
// ReSharper disable once NonReadonlyMemberInGetHashCode
|
||||
return EmailAddress.GetHashCode();
|
||||
}
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Metadata.Builders;
|
||||
|
||||
namespace OliverBooth.Data.Web.Configuration;
|
||||
|
||||
/// <summary>
|
||||
/// Represents the configuration for the <see cref="BlacklistEntry" /> entity.
|
||||
/// </summary>
|
||||
internal sealed class BlacklistEntryConfiguration : IEntityTypeConfiguration<BlacklistEntry>
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public void Configure(EntityTypeBuilder<BlacklistEntry> builder)
|
||||
{
|
||||
builder.ToTable("ContactBlacklist");
|
||||
builder.HasKey(entry => entry.EmailAddress);
|
||||
|
||||
builder.Property(entry => entry.EmailAddress).IsRequired();
|
||||
builder.Property(entry => entry.Name).IsRequired();
|
||||
builder.Property(entry => entry.Reason).IsRequired();
|
||||
}
|
||||
}
|
25
OliverBooth/Data/Web/IBlacklistEntry.cs
Normal file
25
OliverBooth/Data/Web/IBlacklistEntry.cs
Normal file
@ -0,0 +1,25 @@
|
||||
namespace OliverBooth.Data.Web;
|
||||
|
||||
/// <summary>
|
||||
/// Represents an entry in the blacklist.
|
||||
/// </summary>
|
||||
public interface IBlacklistEntry
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the email address of the entry.
|
||||
/// </summary>
|
||||
/// <value>The email address of the entry.</value>
|
||||
string EmailAddress { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name of the entry.
|
||||
/// </summary>
|
||||
/// <value>The name of the entry.</value>
|
||||
string Name { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the reason for the entry.
|
||||
/// </summary>
|
||||
/// <value>The reason for the entry.</value>
|
||||
string Reason { get; }
|
||||
}
|
@ -25,6 +25,12 @@ internal sealed class WebContext : DbContext
|
||||
/// <value>The collection of books.</value>
|
||||
public DbSet<Book> Books { get; private set; } = null!;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the collection of blacklist entries in the database.
|
||||
/// </summary>
|
||||
/// <value>The collection of blacklist entries.</value>
|
||||
public DbSet<BlacklistEntry> ContactBlacklist { get; private set; } = null!;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the collection of projects in the database.
|
||||
/// </summary>
|
||||
@ -53,6 +59,7 @@ internal sealed class WebContext : DbContext
|
||||
|
||||
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
||||
{
|
||||
modelBuilder.ApplyConfiguration(new BlacklistEntryConfiguration());
|
||||
modelBuilder.ApplyConfiguration(new BookConfiguration());
|
||||
modelBuilder.ApplyConfiguration(new ProjectConfiguration());
|
||||
modelBuilder.ApplyConfiguration(new TemplateConfiguration());
|
||||
|
@ -10,17 +10,18 @@
|
||||
ViewData["Title"] = "Reading List";
|
||||
}
|
||||
|
||||
<h1 class="display-4">Reading List</h1>
|
||||
<p>
|
||||
This is a list of the books I've read, I'm currently reading, or that I plan to read. Not every book is listed here,
|
||||
but I will update this list as I try to remember what it is I've read in the past.
|
||||
</p>
|
||||
<p>
|
||||
<main class="container">
|
||||
<h1 class="display-4">Reading List</h1>
|
||||
<p>
|
||||
This is a list of the books I've read, I'm currently reading, or that I plan to read. Not every book is listed
|
||||
here, but I will update this list as I try to remember what it is I've read in the past.
|
||||
</p>
|
||||
<p>
|
||||
This list is also available on <a href="https://www.goodreads.com/review/list/145592619">Goodreads</a>.
|
||||
</p>
|
||||
</p>
|
||||
|
||||
<p class="lead">Currently Reading</p>
|
||||
<table class="table reading-list">
|
||||
<p class="lead">Currently Reading</p>
|
||||
<table class="table reading-list">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width: 50%">Title</th>
|
||||
@ -38,14 +39,16 @@
|
||||
@book.Title.Trim()
|
||||
</td>
|
||||
<td>@book.Author.Trim()</td>
|
||||
<td style="font-family: monospace">@book.Isbn.Trim()<br><img src="@book.GetBarcode()" alt="@book.Isbn"></td>
|
||||
<td style="font-family: monospace">
|
||||
@book.Isbn.Trim()<br><img src="@book.GetBarcode()" alt="@book.Isbn">
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
</table>
|
||||
|
||||
<p class="lead">Plan to Read</p>
|
||||
<table class="table reading-list">
|
||||
<p class="lead">Plan to Read</p>
|
||||
<table class="table reading-list">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width: 50%">Title</th>
|
||||
@ -63,14 +66,16 @@
|
||||
@book.Title.Trim()
|
||||
</td>
|
||||
<td>@book.Author.Trim()</td>
|
||||
<td style="font-family: monospace">@book.Isbn.Trim()<br><img src="@book.GetBarcode()" alt="@book.Isbn"></td>
|
||||
<td style="font-family: monospace">
|
||||
@book.Isbn.Trim()<br><img src="@book.GetBarcode()" alt="@book.Isbn">
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
</table>
|
||||
|
||||
<p class="lead">Read</p>
|
||||
<table class="table reading-list">
|
||||
<p class="lead">Read</p>
|
||||
<table class="table reading-list">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width: 50%">Title</th>
|
||||
@ -88,8 +93,11 @@
|
||||
@book.Title.Trim()
|
||||
</td>
|
||||
<td>@book.Author.Trim()</td>
|
||||
<td style="font-family: monospace">@book.Isbn.Trim()<br><img src="@book.GetBarcode()" alt="@book.Isbn"></td>
|
||||
<td style="font-family: monospace">
|
||||
@book.Isbn.Trim()<br><img src="@book.GetBarcode()" alt="@book.Isbn">
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
</table>
|
||||
</main>
|
38
OliverBooth/Pages/Contact/Blacklist.cshtml
Normal file
38
OliverBooth/Pages/Contact/Blacklist.cshtml
Normal file
@ -0,0 +1,38 @@
|
||||
@page
|
||||
@using OliverBooth.Data.Web
|
||||
@using OliverBooth.Services
|
||||
@inject IContactService ContactService
|
||||
@{
|
||||
ViewData["Title"] = "Blacklist";
|
||||
}
|
||||
|
||||
<main class="container">
|
||||
<h1 class="display-4">Contact Blacklist</h1>
|
||||
<p>
|
||||
Below is a list of email addresses that have been blocked from contacting me. This list is public so that others
|
||||
may also block these addresses if they wish. Any email address that contains an asterisk (*) is a wildcard,
|
||||
meaning that any email address that matches the pattern will be blocked.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
You can view this list in JSON format
|
||||
<a asp-controller="FormattedBlacklist" asp-action="OnGet" asp-route-format="json">here</a>,
|
||||
or in CSV format
|
||||
<a asp-controller="FormattedBlacklist" asp-action="OnGet" asp-route-format="csv">here</a>.
|
||||
</p>
|
||||
|
||||
<table class="table">
|
||||
<tr>
|
||||
<th>Name / Email</th>
|
||||
<th>Reason</th>
|
||||
</tr>
|
||||
|
||||
@foreach (IBlacklistEntry entry in ContactService.GetBlacklist())
|
||||
{
|
||||
<tr>
|
||||
<td>@entry.Name <@entry.EmailAddress></td>
|
||||
<td>@entry.Reason</td>
|
||||
</tr>
|
||||
}
|
||||
</table>
|
||||
</main>
|
@ -3,52 +3,39 @@
|
||||
ViewData["Title"] = "Contact";
|
||||
}
|
||||
|
||||
<h1 class="display-4">Contact</h1>
|
||||
<p>
|
||||
Thanks for getting in touch! While I do my best to read to all inquiries, I cannot guarantee that I will be able to
|
||||
respond to your message. Nevertheless, I appreciate you taking the time to reach out to me and I will respond if I
|
||||
can.
|
||||
</p>
|
||||
<main class="container">
|
||||
<h1 class="display-4">Contact</h1>
|
||||
<p>
|
||||
Thanks for getting in touch! While I do my best to read to all inquiries, I cannot guarantee that I will be able
|
||||
to respond to your message. Nevertheless, I appreciate you taking the time to reach out to me and I will respond
|
||||
if I can.
|
||||
</p>
|
||||
|
||||
<div class="alert alert-warning">
|
||||
<div class="alert alert-warning">
|
||||
<p class="lead"><i class="fa-solid fa-triangle-exclamation"></i> Spam warning</p>
|
||||
<p>
|
||||
In the short time that this site has been live, I have received a considerable amount of spam that I suspect is
|
||||
automated. As such, I added background reCaptcha validation to this form to minimise the amount of
|
||||
bot-written junk I receive. However, some spam still gets through - that means there are humans reading this.
|
||||
</p>
|
||||
<p>
|
||||
I am politely asking that you respect my inbox and keep your unsolicited advertising away. This is a simple
|
||||
request.
|
||||
request. If you send me any kind of spam after this, you have demonstrated that you do not have the basic
|
||||
human decency to respect my wishes or my privacy, and you have lost the privilege for me to respect yours. I
|
||||
<strong>will</strong> block your email address, and I will add your name to my public blacklist of spammers,
|
||||
which you can find <a asp-page="/Contact/Blacklist">here</a>.
|
||||
</p>
|
||||
<p>
|
||||
If you send me any kind of spam after this, you have demonstrated that you do not have the basic human decency
|
||||
to respect my wishes or my privacy, and you have lost the privilege for me to respect yours. I
|
||||
<strong>will</strong> block your email address and report you to your email provider. I will also begin to
|
||||
maintain a public blacklist of spammers, so that others may do the same. You will lose all credibility, and you
|
||||
<strong>will</strong> be publicly shamed for it.
|
||||
</p>
|
||||
<p>
|
||||
This <strong>does not apply</strong> to legitimate businesses who are offering something genuine
|
||||
<span style="text-decoration: underline;">to me specifically</span>, nor to individuals who are interested in my
|
||||
services.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="alert alert-info">
|
||||
<div class="alert alert-info">
|
||||
<p class="lead"><i class="fa-solid fa-circle-info"></i> Dear SEO marketing teams</p>
|
||||
<p>
|
||||
While I don't necessarily consider receiving legitimate offers for SEO services to be spam, I am not interested
|
||||
in your services at this time. I understand that you are just doing your job, but hopefully you can do it more
|
||||
efficiently by going to others who are more receptive to your services.
|
||||
While I don't necessarily consider receiving legitimate offers for SEO services to be spam, I am not
|
||||
interested in your services at this time. I understand that you are just doing your job, but hopefully you
|
||||
can do it more efficiently by going to others who are more receptive to your services.
|
||||
</p>
|
||||
<p>
|
||||
Do not contact me about SEO services. If you do, while I will not publicly blacklist your address, you will -
|
||||
however - be blocked.
|
||||
Do not contact me about SEO services. If you do, while I will not publicly blacklist your address, you
|
||||
will - however - be blocked.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<form method="post" asp-controller="Contact" asp-action="HandleForm">
|
||||
<form method="post" asp-controller="Contact" asp-action="HandleForm">
|
||||
<input type="hidden" name="contact-type" value="other">
|
||||
|
||||
<div class="form-group" style="margin-top: 10px;">
|
||||
@ -74,4 +61,5 @@
|
||||
<recaptcha/>
|
||||
|
||||
<button class="btn btn-primary" style="margin-top: 10px;">Submit</button>
|
||||
</form>
|
||||
</form>
|
||||
</main>
|
@ -3,35 +3,37 @@
|
||||
ViewData["Title"] = "Donate";
|
||||
}
|
||||
|
||||
<h1 class="display-4">Donate</h1>
|
||||
<main class="container">
|
||||
<h1 class="display-4">Donate</h1>
|
||||
|
||||
<p>
|
||||
I believe in free and open exchange of information, and I want to keep my educational content free for everyone to
|
||||
access. I will never put ads on my site, and I don't want to put behind a paywall resources that should be available
|
||||
to everybody, regardless of their financial situation.
|
||||
</p>
|
||||
<p>
|
||||
<p>
|
||||
I believe in free and open exchange of information, and I want to keep my educational content free for everyone
|
||||
to access. I will never put ads on my site, and I don't want to put behind a paywall resources that should be
|
||||
available to everybody, regardless of their financial situation.
|
||||
</p>
|
||||
<p>
|
||||
However, writing tutorials takes time, and I do have to pay for hosting. While I will never ask you for money, I
|
||||
will always appreciate it if you do decide to donate. It also helps me to know that people are finding my content
|
||||
useful, and that I should continue to make more.
|
||||
</p>
|
||||
<p>
|
||||
If you like what I do and are both willing and able to donate money to me and fund all of this, you can do so using
|
||||
the links below. Thank you for your support!
|
||||
</p>
|
||||
will always appreciate it if you do decide to donate. It also helps me to know that people are finding my
|
||||
content useful, and that I should continue to make more.
|
||||
</p>
|
||||
<p>
|
||||
If you like what I do and are both willing and able to donate money to me and fund all of this, you can do so
|
||||
using the links below. Thank you for your support!
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<p>
|
||||
<script type="text/javascript" src="https://storage.ko-fi.com/cdn/widget/Widget_2.js"></script>
|
||||
<script type="text/javascript">kofiwidget2.init('Support Me on Ko-fi', '#29abe0', 'C0C4DVDZX');kofiwidget2.draw();</script>
|
||||
</p>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<p>
|
||||
<script type="text/javascript" src="https://cdnjs.buymeacoffee.com/1.0.0/button.prod.min.js" data-name="bmc-button" data-slug="oliverbooth" data-color="#FFDD00" data-emoji="" data-font="Cookie" data-text="Buy me a coffee" data-outline-color="#000000" data-font-color="#000000" data-coffee-color="#ffffff"></script>
|
||||
</p>
|
||||
</p>
|
||||
|
||||
<p>I also accept cryptocurrency donations.</p>
|
||||
<p>I also accept cryptocurrency donations.</p>
|
||||
|
||||
<ul>
|
||||
<ul>
|
||||
<li>BTC: 1LmXvavJr1omscfkXjp7A4VyNf3XhKP9JK</li>
|
||||
<li>ETH: 0x972C6641e36e2736823A6B1e6BA4D2A814b69fD2</li>
|
||||
</ul>
|
||||
</ul>
|
||||
</main>
|
@ -1,30 +1,33 @@
|
||||
@page
|
||||
|
||||
<h1 class="display-4">Hi, I'm Oliver.</h1>
|
||||
<p class="lead">I'm a tech enthusiast, coffee drinker, and software developer.</p>
|
||||
<main class="container">
|
||||
<h1 class="display-4">Hi, I'm Oliver.</h1>
|
||||
<p class="lead">I'm a tech enthusiast, coffee drinker, and software developer.</p>
|
||||
|
||||
<p class="text-center">
|
||||
<p class="text-center">
|
||||
<img src="~/img/headshot_512x512_2023.jpg" style="width: 512px; max-width: 100%;">
|
||||
</p>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
My primary focus is C#, though I have dabbled in several other languages such as Java, Kotlin, VB, C/C++, Python,
|
||||
and others. Over the years I've built up a collection of projects. Some of which I'm extremely proud of, and others
|
||||
I've quietly abandoned and buried. I'm currently working on a few projects that I hope to release in the near
|
||||
future, but in the meantime, feel free to check out some of my <a asp-page="/Projects/Index">previous work</a>.
|
||||
</p>
|
||||
<p>
|
||||
My primary focus is C#, though I have dabbled in several other languages such as Java, Kotlin, VB, C/C++,
|
||||
Python, and others. Over the years I've built up a collection of projects. Some of which I'm extremely proud of,
|
||||
and others I've quietly abandoned and buried. I'm currently working on a few projects that I hope to release in
|
||||
the near future, but in the meantime, feel free to check out some of my
|
||||
<a asp-page="/Projects/Index">previous work</a>.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<p>
|
||||
I've also written a few <a asp-page="/Tutorials/Index">tutorials</a> on various topics, usually involving
|
||||
information not readily available elsewhere. I hope you find them useful. On occasion, I also write about other
|
||||
topics that I find interesting, such as
|
||||
<a asp-page="/Blog/Index">my thoughts on the state of the world or the tech industry</a>.
|
||||
</p>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
If you want a general overview of stuff I've made, check out my <a href="https://github.com/oliverbooth">GitHub</a>,
|
||||
<a href="https://oliverbooth.itch.io">itch.io</a>, and
|
||||
<p>
|
||||
If you want a general overview of stuff I've made, check out my
|
||||
<a href="https://github.com/oliverbooth">GitHub</a>, <a href="https://oliverbooth.itch.io">itch.io</a>, and
|
||||
<a href="https://play.google.com/store/apps/dev?id=9010459220239503006">Google Play</a>.
|
||||
</p>
|
||||
</p>
|
||||
|
||||
<p>If you'd like to get in touch, you can do so by <a asp-page="/Contact/Index">clicking here</a>.</p>
|
||||
<p>If you'd like to get in touch, you can do so by <a asp-page="/Contact/Index">clicking here</a>.</p>
|
||||
</main>
|
@ -3,64 +3,66 @@
|
||||
ViewData["Title"] = "It's 5 O'Clock Somewhere Privacy Policy";
|
||||
}
|
||||
|
||||
<h1 class="display-4">@ViewData["Title"]</h1>
|
||||
<p class="lead">Last Updated: 24 September 2023</p>
|
||||
<div class="alert alert-primary">
|
||||
<main class="container">
|
||||
<h1 class="display-4">@ViewData["Title"]</h1>
|
||||
<p class="lead">Last Updated: 24 September 2023</p>
|
||||
<div class="alert alert-primary">
|
||||
This Privacy Policy differs from the policy that applies to other applications that I published on Google Play. For
|
||||
the generalised privacy policy, please <a asp-page="/Privacy/GooglePlay">click here</a>.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p>
|
||||
<p>
|
||||
This Privacy Policy describes how your personal information is collected, used, and shared when you use or interact
|
||||
with the application <em>It's 5 O'Clock Somewhere</em> (the "Application").
|
||||
</p>
|
||||
</p>
|
||||
|
||||
<h2>Introduction</h2>
|
||||
<p>
|
||||
I am committed to protecting your privacy and ensuring the security of any information you provide to me when using
|
||||
my applications. This Privacy Policy outlines my practices regarding the collection, use, and disclosure of
|
||||
information I may gather from users of the Application.
|
||||
</p>
|
||||
<h2>Introduction</h2>
|
||||
<p>
|
||||
I am committed to protecting your privacy and ensuring the security of any information you provide to me when
|
||||
using my applications. This Privacy Policy outlines my practices regarding the collection, use, and disclosure
|
||||
of information I may gather from users of the Application.
|
||||
</p>
|
||||
|
||||
<h2>Information I Collect</h2>
|
||||
<p>
|
||||
The Application will temporarily read your device's clock to determine the current time. This information is sent to
|
||||
the Application's server to determine an accurate and time-zone aware response to you, the user. This information is
|
||||
not stored or retained on the server. I do not use cookies, tracking technologies, or any other means to collect or
|
||||
track your usage behavior within the Application. I do not have access to any personal data, including your name,
|
||||
email address, or any other personally identifiable information.
|
||||
</p>
|
||||
<h2>Information I Collect</h2>
|
||||
<p>
|
||||
The Application will temporarily read your device's clock to determine the current time. This information is
|
||||
sent to the Application's server to determine an accurate and time-zone aware response to you, the user. This
|
||||
information is not stored or retained on the server. I do not use cookies, tracking technologies, or any other
|
||||
means to collect or track your usage behavior within the Application. I do not have access to any personal data,
|
||||
including your name, email address, or any other personally identifiable information.
|
||||
</p>
|
||||
|
||||
<h2>Use of Information</h2>
|
||||
<p>
|
||||
The Application uses the little information it collects from you to provide the Application's functionality to you.
|
||||
This information is not used for any other purpose.
|
||||
</p>
|
||||
<h2>Use of Information</h2>
|
||||
<p>
|
||||
The Application uses the little information it collects from you to provide the Application's functionality to
|
||||
you. This information is not used for any other purpose.
|
||||
</p>
|
||||
|
||||
|
||||
<h2>Disclosure of Information</h2>
|
||||
<p>
|
||||
<h2>Disclosure of Information</h2>
|
||||
<p>
|
||||
I do not disclose your personal information to any third parties unless required by law or with your explicit
|
||||
consent. I maintain strict confidentiality and take reasonable precautions to protect your personal information from
|
||||
unauthorized access, use, or disclosure.
|
||||
</p>
|
||||
consent. I maintain strict confidentiality and take reasonable precautions to protect your personal information
|
||||
from unauthorized access, use, or disclosure.
|
||||
</p>
|
||||
|
||||
<h2>Security</h2>
|
||||
<p>
|
||||
<h2>Security</h2>
|
||||
<p>
|
||||
I prioritize the security of your personal information and take reasonable precautions to protect it. However,
|
||||
please be aware that no method of transmission over the internet or electronic storage is 100% secure, and I cannot
|
||||
guarantee absolute security.
|
||||
</p>
|
||||
please be aware that no method of transmission over the internet or electronic storage is 100% secure, and I
|
||||
cannot guarantee absolute security.
|
||||
</p>
|
||||
|
||||
<h2>Changes to this Privacy Policy</h2>
|
||||
<p>
|
||||
<h2>Changes to this Privacy Policy</h2>
|
||||
<p>
|
||||
I may update this Privacy Policy from time to time to reflect changes in my practices or for other operational,
|
||||
legal, or regulatory reasons. I encourage you to review this Privacy Policy periodically for any updates. The
|
||||
revised policy will be effective immediately upon posting.
|
||||
</p>
|
||||
</p>
|
||||
|
||||
<h2>Contact Me</h2>
|
||||
<p>
|
||||
<h2>Contact Me</h2>
|
||||
<p>
|
||||
If you have any questions or concerns about this Privacy Policy or my privacy practices, please
|
||||
<a asp-page="/Contact/Index">get in touch</a>.
|
||||
</p>
|
||||
</p>
|
||||
</main>
|
@ -3,68 +3,70 @@
|
||||
ViewData["Title"] = "Google Play Privacy Policy";
|
||||
}
|
||||
|
||||
<h1 class="display-4">@ViewData["Title"]</h1>
|
||||
<p class="lead">Last Updated: 24 September 2023</p>
|
||||
<div class="alert alert-primary">
|
||||
This Privacy Policy differs from the policy that applies to this website. For this website's privacy policy, please
|
||||
<a asp-page="/Privacy/Index">click here</a>.
|
||||
</div>
|
||||
<div class="alert alert-warning">
|
||||
This Privacy Policy does not apply to the application <em>It's 5 O'Clock Somewhere</em>. For the privacy policy that
|
||||
applies to that application, please <a asp-page="/Privacy/FiveOClockSomewhere">click here</a>.
|
||||
</div>
|
||||
<main class="container">
|
||||
<h1 class="display-4">@ViewData["Title"]</h1>
|
||||
<p class="lead">Last Updated: 24 September 2023</p>
|
||||
<div class="alert alert-primary">
|
||||
This Privacy Policy differs from the policy that applies to this website. For this website's privacy policy,
|
||||
please <a asp-page="/Privacy/Index">click here</a>.
|
||||
</div>
|
||||
<div class="alert alert-warning">
|
||||
This Privacy Policy does not apply to the application <em>It's 5 O'Clock Somewhere</em>. For the privacy policy
|
||||
that applies to that application, please <a asp-page="/Privacy/FiveOClockSomewhere">click here</a>.
|
||||
</div>
|
||||
|
||||
<p>
|
||||
This Privacy Policy describes how your personal information is collected, used, and shared when you use or interact
|
||||
with applications that I publish on the <a href="https://play.google.com/store/">Google Play Store</a>.
|
||||
</p>
|
||||
<p>
|
||||
This Privacy Policy describes how your personal information is collected, used, and shared when you use or
|
||||
interact with applications that I publish on the <a href="https://play.google.com/store/">Google Play Store</a>.
|
||||
</p>
|
||||
|
||||
<h2>Introduction</h2>
|
||||
<p>
|
||||
I am committed to protecting your privacy and ensuring the security of any information you provide to me when using
|
||||
my applications. This Privacy Policy outlines my practices regarding the collection, use, and disclosure of
|
||||
information I may gather from users of my applications.
|
||||
</p>
|
||||
<h2>Introduction</h2>
|
||||
<p>
|
||||
I am committed to protecting your privacy and ensuring the security of any information you provide to me when
|
||||
using my applications. This Privacy Policy outlines my practices regarding the collection, use, and disclosure
|
||||
of information I may gather from users of my applications.
|
||||
</p>
|
||||
|
||||
<h2>Information I Collect</h2>
|
||||
<p>
|
||||
<h2>Information I Collect</h2>
|
||||
<p>
|
||||
I do not collect any personally identifiable information about you when you use my applications. I do not use any
|
||||
cookies or similar tracking technologies that can identify individual users or track your usage behavior within the
|
||||
application.
|
||||
</p>
|
||||
<p>
|
||||
However, please note that my applications may make use of third party integrations such as Google Play Services or
|
||||
others. I am not responsible for the privacy practices or content of these third-party services. I encourage you to
|
||||
review the privacy policies of those third-party services before providing any personal information.
|
||||
</p>
|
||||
cookies or similar tracking technologies that can identify individual users or track your usage behavior within
|
||||
the application.
|
||||
</p>
|
||||
<p>
|
||||
However, please note that my applications may make use of third party integrations such as Google Play Services
|
||||
or others. I am not responsible for the privacy practices or content of these third-party services. I encourage
|
||||
you to review the privacy policies of those third-party services before providing any personal information.
|
||||
</p>
|
||||
|
||||
<h2>Use of Information</h2>
|
||||
<p>Since I do not collect any personal information about you, I do not use it for any purpose.</p>
|
||||
<h2>Use of Information</h2>
|
||||
<p>Since I do not collect any personal information about you, I do not use it for any purpose.</p>
|
||||
|
||||
<h2>Disclosure of Information</h2>
|
||||
<p>I do not share any personal information about you because I do not collect any such information.</p>
|
||||
<p>
|
||||
<h2>Disclosure of Information</h2>
|
||||
<p>I do not share any personal information about you because I do not collect any such information.</p>
|
||||
<p>
|
||||
However, please be aware that my applications contains links to third-party websites and services. I am not
|
||||
responsible for the privacy practices or content of these third-party sites. I encourage you to review the privacy
|
||||
policies of those third-party sites before providing any personal information.
|
||||
</p>
|
||||
responsible for the privacy practices or content of these third-party sites. I encourage you to review the
|
||||
privacy policies of those third-party sites before providing any personal information.
|
||||
</p>
|
||||
|
||||
<h2>Security</h2>
|
||||
<p>
|
||||
<h2>Security</h2>
|
||||
<p>
|
||||
I prioritize the security of your personal information and take reasonable precautions to protect it. However,
|
||||
please be aware that no method of transmission over the internet or electronic storage is 100% secure, and I cannot
|
||||
guarantee absolute security.
|
||||
</p>
|
||||
please be aware that no method of transmission over the internet or electronic storage is 100% secure, and I
|
||||
cannot guarantee absolute security.
|
||||
</p>
|
||||
|
||||
<h2>Changes to this Privacy Policy</h2>
|
||||
<p>
|
||||
<h2>Changes to this Privacy Policy</h2>
|
||||
<p>
|
||||
I may update this Privacy Policy from time to time to reflect changes in my practices or for other operational,
|
||||
legal, or regulatory reasons. I encourage you to review this Privacy Policy periodically for any updates. The
|
||||
revised policy will be effective immediately upon posting.
|
||||
</p>
|
||||
</p>
|
||||
|
||||
<h2>Contact Me</h2>
|
||||
<p>
|
||||
<h2>Contact Me</h2>
|
||||
<p>
|
||||
If you have any questions or concerns about this Privacy Policy or my privacy practices, please
|
||||
<a asp-page="/Contact/Index">get in touch</a>.
|
||||
</p>
|
||||
</p>
|
||||
</main>
|
@ -3,84 +3,86 @@
|
||||
ViewData["Title"] = "Privacy Policy";
|
||||
}
|
||||
|
||||
<h1 class="display-4">@ViewData["Title"]</h1>
|
||||
<p class="lead">Last Updated: 26 May 2023</p>
|
||||
<div class="alert alert-primary">
|
||||
<main class="container">
|
||||
<h1 class="display-4">@ViewData["Title"]</h1>
|
||||
<p class="lead">Last Updated: 26 May 2023</p>
|
||||
<div class="alert alert-primary">
|
||||
This Privacy Policy differs from the policy that applies to my applications published to Google Play. For my
|
||||
applications' privacy policy, please <a asp-page="/Privacy/GooglePlay">click here</a>.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p>
|
||||
<p>
|
||||
This Privacy Policy describes how your personal information is collected, used, and shared when you visit or
|
||||
interact with my website <a href="https://oliverbooth.dev/">oliverbooth.dev</a>.
|
||||
</p>
|
||||
</p>
|
||||
|
||||
<h2>Introduction</h2>
|
||||
<p>
|
||||
I am committed to protecting your privacy and ensuring the security of any information you provide to me when using
|
||||
my website. This Privacy Policy outlines my practices regarding the collection, use, and disclosure of information I
|
||||
may gather from users of my website.
|
||||
</p>
|
||||
<h2>Introduction</h2>
|
||||
<p>
|
||||
I am committed to protecting your privacy and ensuring the security of any information you provide to me when
|
||||
using my website. This Privacy Policy outlines my practices regarding the collection, use, and disclosure of
|
||||
information I may gather from users of my website.
|
||||
</p>
|
||||
|
||||
<h2>Information I Collect</h2>
|
||||
<p>
|
||||
<h2>Information I Collect</h2>
|
||||
<p>
|
||||
When you choose to contact me via the contact form on my website, I collect your name and email address. This
|
||||
information is provided voluntarily by you and is necessary for me to respond to your inquiries and engage in a
|
||||
conversation. I do not collect any additional personally identifiable information about you through the contact
|
||||
form.
|
||||
</p>
|
||||
<p>I do not use any cookies or similar tracking technologies that can identify individual users.</p>
|
||||
<p>
|
||||
</p>
|
||||
<p>I do not use any cookies or similar tracking technologies that can identify individual users.</p>
|
||||
<p>
|
||||
Please note that my website includes a Disqus integration for commenting on blog posts. Disqus is a third-party
|
||||
service, and their use of cookies and collection of personal information are governed by their own privacy policies.
|
||||
I have no control over the information collected by Disqus, and I encourage you to review their privacy policy to
|
||||
understand how your information may be used by them.
|
||||
</p>
|
||||
service, and their use of cookies and collection of personal information are governed by their own privacy
|
||||
policies. I have no control over the information collected by Disqus, and I encourage you to review their
|
||||
privacy policy to understand how your information may be used by them.
|
||||
</p>
|
||||
|
||||
<h2>Use of Information</h2>
|
||||
<p>
|
||||
<h2>Use of Information</h2>
|
||||
<p>
|
||||
The name and email address you provide through the contact form are used solely for the purpose of responding to
|
||||
your inquiries and engaging in relevant conversation. I keep this information confidential and do not disclose,
|
||||
sell, or share it with any third parties unless required by law or with your explicit consent.
|
||||
</p>
|
||||
<p>
|
||||
I do not use your personal information for marketing purposes or send you any unsolicited communications. Once our
|
||||
conversation is complete and no longer necessary, I will securely delete your personal information unless otherwise
|
||||
required to retain it by applicable laws or regulations.
|
||||
</p>
|
||||
</p>
|
||||
<p>
|
||||
I do not use your personal information for marketing purposes or send you any unsolicited communications. Once
|
||||
our conversation is complete and no longer necessary, I will securely delete your personal information unless
|
||||
otherwise required to retain it by applicable laws or regulations.
|
||||
</p>
|
||||
|
||||
<h2>Disclosure of Information</h2>
|
||||
<p>
|
||||
<h2>Disclosure of Information</h2>
|
||||
<p>
|
||||
I do not disclose your personal information to any third parties unless required by law or with your explicit
|
||||
consent. I maintain strict confidentiality and take reasonable precautions to protect your personal information from
|
||||
unauthorized access, use, or disclosure.
|
||||
</p>
|
||||
<p>
|
||||
However, please be aware that my website contains links to third-party websites and services. I am not responsible
|
||||
for the privacy practices or content of these third-party sites. I encourage you to review the privacy policies of
|
||||
those third-party sites before providing any personal information.
|
||||
</p>
|
||||
consent. I maintain strict confidentiality and take reasonable precautions to protect your personal information
|
||||
from unauthorized access, use, or disclosure.
|
||||
</p>
|
||||
<p>
|
||||
However, please be aware that my website contains links to third-party websites and services. I am not
|
||||
responsible for the privacy practices or content of these third-party sites. I encourage you to review the
|
||||
privacy policies of those third-party sites before providing any personal information.
|
||||
</p>
|
||||
|
||||
<h2>Security</h2>
|
||||
<p>
|
||||
<h2>Security</h2>
|
||||
<p>
|
||||
I prioritize the security of your personal information and take reasonable precautions to protect it. However,
|
||||
please be aware that no method of transmission over the internet or electronic storage is 100% secure, and I cannot
|
||||
guarantee absolute security.
|
||||
</p>
|
||||
please be aware that no method of transmission over the internet or electronic storage is 100% secure, and I
|
||||
cannot guarantee absolute security.
|
||||
</p>
|
||||
|
||||
<h2>Changes to this Privacy Policy</h2>
|
||||
<p>
|
||||
<h2>Changes to this Privacy Policy</h2>
|
||||
<p>
|
||||
I may update this Privacy Policy from time to time to reflect changes in my practices or for other operational,
|
||||
legal, or regulatory reasons. I encourage you to review this Privacy Policy periodically for any updates. The
|
||||
revised policy will be effective immediately upon posting.
|
||||
</p>
|
||||
</p>
|
||||
|
||||
<h2>Contact Me</h2>
|
||||
<p>
|
||||
<h2>Contact Me</h2>
|
||||
<p>
|
||||
If you have any questions or concerns about this Privacy Policy or my privacy practices, please
|
||||
<a asp-page="/Contact/Index">get in touch</a>.
|
||||
</p>
|
||||
</p>
|
||||
|
||||
<hr/>
|
||||
<hr/>
|
||||
|
||||
<p>By using my website, you signify your acceptance of this Privacy Policy.</p>
|
||||
<p>By using my website, you signify your acceptance of this Privacy Policy.</p>
|
||||
</main>
|
@ -6,10 +6,11 @@
|
||||
ViewData["Title"] = "Projects";
|
||||
}
|
||||
|
||||
<h1 class="display-4">Projects</h1>
|
||||
<main class="container">
|
||||
<h1 class="display-4">Projects</h1>
|
||||
|
||||
@foreach (IProject[] chunk in ProjectService.GetProjects(ProjectStatus.Ongoing).OrderBy(p => p.Rank).Chunk(2))
|
||||
{
|
||||
@foreach (IProject[] chunk in ProjectService.GetProjects(ProjectStatus.Ongoing).OrderBy(p => p.Rank).Chunk(2))
|
||||
{
|
||||
<div class="card-group row" style="margin-top: 20px;">
|
||||
@foreach (IProject project in chunk)
|
||||
{
|
||||
@ -38,10 +39,10 @@
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
}
|
||||
|
||||
@foreach (IProject[] chunk in ProjectService.GetProjects(ProjectStatus.Past).Chunk(2))
|
||||
{
|
||||
@foreach (IProject[] chunk in ProjectService.GetProjects(ProjectStatus.Past).Chunk(2))
|
||||
{
|
||||
<div class="card-group row" style="margin-top: 20px;">
|
||||
@foreach (IProject project in chunk)
|
||||
{
|
||||
@ -70,10 +71,10 @@
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
}
|
||||
|
||||
@foreach (IProject[] chunk in ProjectService.GetProjects(ProjectStatus.Hiatus).Chunk(2))
|
||||
{
|
||||
@foreach (IProject[] chunk in ProjectService.GetProjects(ProjectStatus.Hiatus).Chunk(2))
|
||||
{
|
||||
<div class="card-group row" style="margin-top: 20px;">
|
||||
@foreach (IProject project in chunk)
|
||||
{
|
||||
@ -102,4 +103,5 @@
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
}
|
||||
</main>
|
@ -3,19 +3,21 @@
|
||||
ViewData["Title"] = "Tutorials";
|
||||
}
|
||||
|
||||
<h1 class="display-4">Tutorials</h1>
|
||||
<p class="lead">Coming Soon</p>
|
||||
<p>
|
||||
<main class="container">
|
||||
<h1 class="display-4">Tutorials</h1>
|
||||
<p class="lead">Coming Soon</p>
|
||||
<p>
|
||||
Due to Unity's poor corporate decision-making, I'm left in a position where I find it infeasible to write Unity
|
||||
tutorials. I plan to write tutorials for things like Unreal and MonoGame as I learn them, and C# tutorials are
|
||||
still on the table for sure. But tutorials take a lot of time and effort, so unfortunately it may be a while before
|
||||
I can get around to publishing them.
|
||||
</p>
|
||||
<p>
|
||||
still on the table for sure. But tutorials take a lot of time and effort, so unfortunately it may be a while
|
||||
before I can get around to publishing them.
|
||||
</p>
|
||||
<p>
|
||||
However, in the meantime, I do have various blog posts that contain some tutorials and guides. You can find them
|
||||
<a asp-page="/Blog/Index">here</a>!
|
||||
</p>
|
||||
<p>
|
||||
</p>
|
||||
<p>
|
||||
I'm sorry for the inconvenience, but I hope you understand my position. Watch this space! New tutorials will be
|
||||
coming. If you have any questions or requests, please feel free to <a asp-page="/Contact/Index">contact me</a>.
|
||||
</p>
|
||||
</p>
|
||||
</main>
|
@ -32,6 +32,7 @@ builder.Services.AddSingleton(provider => new MarkdownPipelineBuilder()
|
||||
|
||||
builder.Services.AddDbContextFactory<BlogContext>();
|
||||
builder.Services.AddDbContextFactory<WebContext>();
|
||||
builder.Services.AddSingleton<IContactService, ContactService>();
|
||||
builder.Services.AddSingleton<ITemplateService, TemplateService>();
|
||||
builder.Services.AddSingleton<IBlogPostService, BlogPostService>();
|
||||
builder.Services.AddSingleton<IBlogUserService, BlogUserService>();
|
||||
|
26
OliverBooth/Services/ContactService.cs
Normal file
26
OliverBooth/Services/ContactService.cs
Normal file
@ -0,0 +1,26 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using OliverBooth.Data.Web;
|
||||
|
||||
namespace OliverBooth.Services;
|
||||
|
||||
/// <inheritdoc cref="IContactService" />
|
||||
internal sealed class ContactService : IContactService
|
||||
{
|
||||
private readonly IDbContextFactory<WebContext> _dbContextFactory;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ContactService" /> class.
|
||||
/// </summary>
|
||||
/// <param name="dbContextFactory">The <see cref="IDbContextFactory{TContext}" />.</param>
|
||||
public ContactService(IDbContextFactory<WebContext> dbContextFactory)
|
||||
{
|
||||
_dbContextFactory = dbContextFactory;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IReadOnlyCollection<IBlacklistEntry> GetBlacklist()
|
||||
{
|
||||
using WebContext context = _dbContextFactory.CreateDbContext();
|
||||
return context.ContactBlacklist.OrderBy(b => b.EmailAddress).ToArray();
|
||||
}
|
||||
}
|
14
OliverBooth/Services/IContactService.cs
Normal file
14
OliverBooth/Services/IContactService.cs
Normal file
@ -0,0 +1,14 @@
|
||||
using OliverBooth.Data.Web;
|
||||
|
||||
namespace OliverBooth.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a service for managing contact information.
|
||||
/// </summary>
|
||||
public interface IContactService
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the blacklist.
|
||||
/// </summary>
|
||||
IReadOnlyCollection<IBlacklistEntry> GetBlacklist();
|
||||
}
|
@ -1,7 +1,6 @@
|
||||
html, body {
|
||||
background: #121212;
|
||||
color: #f5f5f5;
|
||||
font-family: 'Gabarito', sans-serif;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
@ -24,6 +23,11 @@ body {
|
||||
margin-bottom: 60px;
|
||||
}
|
||||
|
||||
main.container {
|
||||
background: #333;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
a {
|
||||
&:link, &:visited, &:hover, &:active {
|
||||
text-decoration: none;
|
||||
|
Loading…
Reference in New Issue
Block a user