V2 #1
|
@ -55,7 +55,7 @@ public static class PageExtensions
|
||||||
/// <param name="pageNbr">The page number to retrieve</param>
|
/// <param name="pageNbr">The page number to retrieve</param>
|
||||||
/// <returns>The pages</returns>
|
/// <returns>The pages</returns>
|
||||||
public static async Task<List<Page>> FindPageOfPages(this DbSet<Page> db, int pageNbr) =>
|
public static async Task<List<Page>> FindPageOfPages(this DbSet<Page> db, int pageNbr) =>
|
||||||
await db.Skip((pageNbr - 1) * 50).Take(25).ToListAsync().ConfigureAwait(false);
|
await db.OrderBy(p => p.Title).Skip((pageNbr - 1) * 25).Take(25).ToListAsync().ConfigureAwait(false);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Retrieve a page by its ID (tracked)
|
/// Retrieve a page by its ID (tracked)
|
||||||
|
|
|
@ -11,7 +11,7 @@ using MyWebLog.Data;
|
||||||
namespace MyWebLog.Data.Migrations
|
namespace MyWebLog.Data.Migrations
|
||||||
{
|
{
|
||||||
[DbContext(typeof(WebLogDbContext))]
|
[DbContext(typeof(WebLogDbContext))]
|
||||||
[Migration("20220227160816_Initial")]
|
[Migration("20220307034307_Initial")]
|
||||||
partial class Initial
|
partial class Initial
|
||||||
{
|
{
|
||||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||||
|
@ -123,6 +123,9 @@ namespace MyWebLog.Data.Migrations
|
||||||
b.Property<bool>("ShowInPageList")
|
b.Property<bool>("ShowInPageList")
|
||||||
.HasColumnType("INTEGER");
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<string>("Template")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
b.Property<string>("Text")
|
b.Property<string>("Text")
|
||||||
.IsRequired()
|
.IsRequired()
|
||||||
.HasColumnType("TEXT");
|
.HasColumnType("TEXT");
|
|
@ -87,6 +87,7 @@ namespace MyWebLog.Data.Migrations
|
||||||
PublishedOn = table.Column<DateTime>(type: "TEXT", nullable: false),
|
PublishedOn = table.Column<DateTime>(type: "TEXT", nullable: false),
|
||||||
UpdatedOn = table.Column<DateTime>(type: "TEXT", nullable: false),
|
UpdatedOn = table.Column<DateTime>(type: "TEXT", nullable: false),
|
||||||
ShowInPageList = table.Column<bool>(type: "INTEGER", nullable: false),
|
ShowInPageList = table.Column<bool>(type: "INTEGER", nullable: false),
|
||||||
|
Template = table.Column<string>(type: "TEXT", nullable: true),
|
||||||
Text = table.Column<string>(type: "TEXT", nullable: false)
|
Text = table.Column<string>(type: "TEXT", nullable: false)
|
||||||
},
|
},
|
||||||
constraints: table =>
|
constraints: table =>
|
|
@ -121,6 +121,9 @@ namespace MyWebLog.Data.Migrations
|
||||||
b.Property<bool>("ShowInPageList")
|
b.Property<bool>("ShowInPageList")
|
||||||
.HasColumnType("INTEGER");
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<string>("Template")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
b.Property<string>("Text")
|
b.Property<string>("Text")
|
||||||
.IsRequired()
|
.IsRequired()
|
||||||
.HasColumnType("TEXT");
|
.HasColumnType("TEXT");
|
||||||
|
|
|
@ -45,6 +45,11 @@ public class Page
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool ShowInPageList { get; set; } = false;
|
public bool ShowInPageList { get; set; } = false;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The template to use when rendering this page
|
||||||
|
/// </summary>
|
||||||
|
public string? Template { get; set; } = null;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The current text of the page
|
/// The current text of the page
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
@ -10,6 +10,11 @@ public class SinglePageModel : MyWebLogModel
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public Page Page { get; init; }
|
public Page Page { get; init; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Is this the home page?
|
||||||
|
/// </summary>
|
||||||
|
public bool IsHome => Page.Id == WebLog.DefaultPage;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Constructor
|
/// Constructor
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
@ -21,7 +21,7 @@ public class PostController : MyWebLogController
|
||||||
if (WebLog.DefaultPage == "posts") return await PageOfPosts(1);
|
if (WebLog.DefaultPage == "posts") return await PageOfPosts(1);
|
||||||
|
|
||||||
var page = await Db.Pages.FindById(WebLog.DefaultPage);
|
var page = await Db.Pages.FindById(WebLog.DefaultPage);
|
||||||
return page is null ? NotFound() : ThemedView("SinglePage", new SinglePageModel(page, WebLog));
|
return page is null ? NotFound() : ThemedView(page.Template ?? "SinglePage", new SinglePageModel(page, WebLog));
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet("~/page/{pageNbr:int}")]
|
[HttpGet("~/page/{pageNbr:int}")]
|
||||||
|
@ -42,7 +42,7 @@ public class PostController : MyWebLogController
|
||||||
var page = await Db.Pages.FindByPermalink(permalink);
|
var page = await Db.Pages.FindByPermalink(permalink);
|
||||||
if (page != null)
|
if (page != null)
|
||||||
{
|
{
|
||||||
return ThemedView("SinglePage", new SinglePageModel(page, WebLog));
|
return ThemedView(page.Template ?? "SinglePage", new SinglePageModel(page, WebLog));
|
||||||
}
|
}
|
||||||
|
|
||||||
// TOOD: search prior permalinks for posts and pages
|
// TOOD: search prior permalinks for posts and pages
|
||||||
|
|
|
@ -34,6 +34,8 @@ public abstract class MyWebLogController : Controller
|
||||||
|
|
||||||
protected ViewResult ThemedView(string template, object model)
|
protected ViewResult ThemedView(string template, object model)
|
||||||
{
|
{
|
||||||
|
// TODO: get actual version
|
||||||
|
ViewBag.Version = "2";
|
||||||
return View(template, model);
|
return View(template, model);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
37
src/MyWebLog/Features/Shared/TagHelpers/ImageTagHelper.cs
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
using Microsoft.AspNetCore.Mvc.Routing;
|
||||||
|
using Microsoft.AspNetCore.Mvc.ViewFeatures;
|
||||||
|
using Microsoft.AspNetCore.Razor.TagHelpers;
|
||||||
|
using System.Text.Encodings.Web;
|
||||||
|
|
||||||
|
namespace MyWebLog.Features.Shared.TagHelpers;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Image tag helper to load a theme's image
|
||||||
|
/// </summary>
|
||||||
|
[HtmlTargetElement("img", Attributes = "asp-theme")]
|
||||||
|
public class ImageTagHelper : Microsoft.AspNetCore.Mvc.TagHelpers.ImageTagHelper
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The theme for which the image should be loaded
|
||||||
|
/// </summary>
|
||||||
|
[HtmlAttributeName("asp-theme")]
|
||||||
|
public string Theme { get; set; } = "";
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public ImageTagHelper(IFileVersionProvider fileVersionProvider, HtmlEncoder htmlEncoder,
|
||||||
|
IUrlHelperFactory urlHelperFactory)
|
||||||
|
: base(fileVersionProvider, htmlEncoder, urlHelperFactory) { }
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override void Process(TagHelperContext context, TagHelperOutput output)
|
||||||
|
{
|
||||||
|
if (Theme == "")
|
||||||
|
{
|
||||||
|
base.Process(context, output);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
output.Attributes.SetAttribute("src", $"~/img/{Theme}/{context.AllAttributes["src"]?.Value}");
|
||||||
|
ProcessUrlAttribute("src", output);
|
||||||
|
}
|
||||||
|
}
|
55
src/MyWebLog/Features/Shared/TagHelpers/LinkTagHelper.cs
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
using Microsoft.AspNetCore.Mvc.Razor.Infrastructure;
|
||||||
|
using Microsoft.AspNetCore.Mvc.Routing;
|
||||||
|
using Microsoft.AspNetCore.Mvc.ViewFeatures;
|
||||||
|
using Microsoft.AspNetCore.Razor.TagHelpers;
|
||||||
|
using System.Text.Encodings.Web;
|
||||||
|
|
||||||
|
namespace MyWebLog.Features.Shared.TagHelpers;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Tag helper to link stylesheets for a theme
|
||||||
|
/// </summary>
|
||||||
|
[HtmlTargetElement("link", Attributes = "asp-theme")]
|
||||||
|
public class LinkTagHelper : Microsoft.AspNetCore.Mvc.TagHelpers.LinkTagHelper
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The theme for which a style sheet should be loaded
|
||||||
|
/// </summary>
|
||||||
|
[HtmlAttributeName("asp-theme")]
|
||||||
|
public string Theme { get; set; } = "";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The style sheet to be loaded (defaults to "style")
|
||||||
|
/// </summary>
|
||||||
|
[HtmlAttributeName("asp-style")]
|
||||||
|
public string Style { get; set; } = "style";
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public LinkTagHelper(IWebHostEnvironment hostingEnvironment, TagHelperMemoryCacheProvider cacheProvider,
|
||||||
|
IFileVersionProvider fileVersionProvider, HtmlEncoder htmlEncoder, JavaScriptEncoder javaScriptEncoder,
|
||||||
|
IUrlHelperFactory urlHelperFactory)
|
||||||
|
: base(hostingEnvironment, cacheProvider, fileVersionProvider, htmlEncoder, javaScriptEncoder, urlHelperFactory)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override void Process(TagHelperContext context, TagHelperOutput output)
|
||||||
|
{
|
||||||
|
if (Theme == "")
|
||||||
|
{
|
||||||
|
base.Process(context, output);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (context.AllAttributes["rel"]?.Value.ToString())
|
||||||
|
{
|
||||||
|
case "stylesheet":
|
||||||
|
output.Attributes.SetAttribute("href", $"~/css/{Theme}/{Style}.css");
|
||||||
|
break;
|
||||||
|
case "icon":
|
||||||
|
output.Attributes.SetAttribute("type", "image/x-icon");
|
||||||
|
output.Attributes.SetAttribute("href", $"~/img/{Theme}/favicon.ico");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
ProcessUrlAttribute("href", output);
|
||||||
|
}
|
||||||
|
}
|
|
@ -6,6 +6,14 @@
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Content Remove="Themes\BitBadger\solutions.json" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<EmbeddedResource Include="Themes\BitBadger\solutions.json" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Folder Include="wwwroot\img\" />
|
<Folder Include="wwwroot\img\" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
|
@ -43,7 +43,7 @@ builder.Services.AddDbContext<WebLogDbContext>(o =>
|
||||||
{
|
{
|
||||||
// TODO: can get from DI?
|
// TODO: can get from DI?
|
||||||
var db = WebLogCache.HostToDb(new HttpContextAccessor().HttpContext!);
|
var db = WebLogCache.HostToDb(new HttpContextAccessor().HttpContext!);
|
||||||
// "Data Source=Db/empty.db"
|
// "empty";
|
||||||
o.UseSqlite($"Data Source=Db/{db}.db");
|
o.UseSqlite($"Data Source=Db/{db}.db");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
29
src/MyWebLog/Themes/BitBadger/Shared/_AppSidebar.cshtml
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
@{
|
||||||
|
var data = await SolutionInfo.GetAll();
|
||||||
|
string[] cats = new[] { "Web Sites and Applications", "WordPress", "Static Sites", "Personal" };
|
||||||
|
IEnumerable<SolutionInfo> solutionsForCat(string cat) =>
|
||||||
|
data.Where(it => it.Category == cat && it.FrontPage.Display).OrderBy(it => it.FrontPage.Order ?? 99);
|
||||||
|
string aboutTitle(string title) => $"{title} | Bit Badger Solutions";
|
||||||
|
}
|
||||||
|
<aside class="app-sidebar">
|
||||||
|
@foreach (var cat in cats)
|
||||||
|
{
|
||||||
|
<div>
|
||||||
|
<div class="app-sidebar-head">@cat</div>
|
||||||
|
@foreach (var sln in solutionsForCat(cat))
|
||||||
|
{
|
||||||
|
<div>
|
||||||
|
<p class="app-sidebar-name">
|
||||||
|
<strong>@sln.Name</strong><br>
|
||||||
|
@if (sln.LinkToAboutPage)
|
||||||
|
{
|
||||||
|
<a href="~/solutions/@(sln.Slug).html" title="@aboutTitle(sln.Name)">About</a><text> • </text>
|
||||||
|
}
|
||||||
|
@if (sln.LinkToSite) { <a href="@sln.Url" title="@sln.Name" target="_blank">Visit</a> }
|
||||||
|
</p>
|
||||||
|
<p class="app-sidebar-description">@Html.Raw(sln.FrontPage.Text ?? sln.Summary ?? "")</p>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</aside>
|
56
src/MyWebLog/Themes/BitBadger/Shared/_Layout.cshtml
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
@model MyWebLogModel
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width">
|
||||||
|
<meta name="generator" content="myWebLog @ViewBag.Version">
|
||||||
|
<title>@ViewBag.Title « @Model.WebLog.Name</title>
|
||||||
|
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Oswald|Raleway">
|
||||||
|
<link rel="stylesheet" asp-theme="@Model.WebLog.ThemePath" />
|
||||||
|
<link rel="icon" asp-theme="@Model.WebLog.ThemePath" />
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<header class="site-header">
|
||||||
|
<div class="header-logo">
|
||||||
|
<a href="/">
|
||||||
|
<img asp-theme="@Model.WebLog.ThemePath" src="bitbadger.png"
|
||||||
|
alt="A cartoon badger looking at a computer screen, with his paw on a mouse"
|
||||||
|
title="Bit Badger Solutions" />
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div class="header-title">
|
||||||
|
<a href="/">Bit Badger Solutions</a>
|
||||||
|
</div>
|
||||||
|
<div class="header-spacer">
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div class="header-social">
|
||||||
|
<a href="https://twitter.com/Bit_Badger" title="Bit_Badger on Twitter" target="_blank">
|
||||||
|
<img asp-theme="@Model.WebLog.ThemePath" src="twitter.png" alt="Twitter" />
|
||||||
|
</a> <a href="https://www.facebook.com/bitbadger.solutions" title="Bit Badger Solutions on Facebook" _target="_blank">
|
||||||
|
<img asp-theme="@Model.WebLog.ThemePath" src="facebook.png" alt="Facebook" />
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
<main>
|
||||||
|
@RenderBody()
|
||||||
|
</main>
|
||||||
|
<footer class="site-footer">
|
||||||
|
<p>
|
||||||
|
Powered by <strong><a href="#">myWebLog</a></strong> •
|
||||||
|
@if (User is not null && (User.Identity?.IsAuthenticated ?? false))
|
||||||
|
{
|
||||||
|
<a href="/admin">@Resources.Dashboard</a>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<a href="/user/log-on">@Resources.LogOn</a>
|
||||||
|
}
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
A <strong><a href="/">Bit Badger Solutions</a></strong> original design
|
||||||
|
</p>
|
||||||
|
</footer>
|
||||||
|
</body>
|
||||||
|
</html>
|
13
src/MyWebLog/Themes/BitBadger/SinglePage.cshtml
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
@using MyWebLog.Features.Pages
|
||||||
|
@model SinglePageModel
|
||||||
|
@{
|
||||||
|
Layout = "_Layout";
|
||||||
|
ViewBag.Title = Model.Page.Title;
|
||||||
|
}
|
||||||
|
<div class="@(Model.IsHome ? "home" : null)">
|
||||||
|
<article class="content auto">
|
||||||
|
@if (!Model.IsHome) { <h2>@Model.Page.Title</h2> }
|
||||||
|
@Html.Raw(Model.Page.Text)
|
||||||
|
</article>
|
||||||
|
@if (Model.IsHome) { @await Html.PartialAsync("_AppSidebar") }
|
||||||
|
</div>
|
147
src/MyWebLog/Themes/BitBadger/SolutionInfo.cs
Normal file
|
@ -0,0 +1,147 @@
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Text.Json;
|
||||||
|
|
||||||
|
namespace MyWebLog.Themes.BitBadger;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A technology used in a solution
|
||||||
|
/// </summary>
|
||||||
|
public class Technology
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The name of the technology
|
||||||
|
/// </summary>
|
||||||
|
public string Name { get; set; } = "";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Why this technology was used in this project
|
||||||
|
/// </summary>
|
||||||
|
public string Purpose { get; set; } = "";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Whether this project currently uses this technology
|
||||||
|
/// </summary>
|
||||||
|
public bool? IsCurrent { get; set; } = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Information about the solutions displayed on the front page
|
||||||
|
/// </summary>
|
||||||
|
public class FrontPageInfo
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Whether the solution should be on the front page sidebar
|
||||||
|
/// </summary>
|
||||||
|
public bool Display { get; set; } = false;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The order in which this solution should be displayed
|
||||||
|
/// </summary>
|
||||||
|
public byte? Order { get; set; } = null;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The description text for the front page sidebar
|
||||||
|
/// </summary>
|
||||||
|
public string? Text { get; set; } = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Information about a solution
|
||||||
|
/// </summary>
|
||||||
|
public class SolutionInfo
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The name of the solution
|
||||||
|
/// </summary>
|
||||||
|
public string Name { get; set; } = "";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The URL slug for the page for this solution
|
||||||
|
/// </summary>
|
||||||
|
public string Slug { get; set; } = "";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The URL for the solution (not the page describing it)
|
||||||
|
/// </summary>
|
||||||
|
public string Url { get; set; } = "";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The category into which this solution falls
|
||||||
|
/// </summary>
|
||||||
|
public string Category { get; set; } = "";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A short summary of the solution
|
||||||
|
/// </summary>
|
||||||
|
public string? Summary { get; set; } = null;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Whether this solution is inactive
|
||||||
|
/// </summary>
|
||||||
|
public bool? IsInactive { get; set; } = null;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Whether this solution is active
|
||||||
|
/// </summary>
|
||||||
|
public bool IsActive => !(IsInactive ?? false);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Whether a link should not be generated to the URL for this solution
|
||||||
|
/// </summary>
|
||||||
|
public bool? DoNotLink { get; set; } = null;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Whether a link should be generated to this solution
|
||||||
|
/// </summary>
|
||||||
|
public bool LinkToSite => !(DoNotLink ?? false);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Whether an "About" link should be generated for this solution
|
||||||
|
/// </summary>
|
||||||
|
public bool? SkipAboutLink { get; set; } = null;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Whether an "About" link should be generated for this solution
|
||||||
|
/// </summary>
|
||||||
|
public bool LinkToAboutPage => !(SkipAboutLink ?? false);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Whether to generate a link to an archive site
|
||||||
|
/// </summary>
|
||||||
|
public bool? LinkToArchive { get; set; } = null;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The URL of the archive site for this solution
|
||||||
|
/// </summary>
|
||||||
|
public string? ArchiveUrl { get; set; } = null;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Home page sidebar display information
|
||||||
|
/// </summary>
|
||||||
|
public FrontPageInfo FrontPage { get; set; } = default!;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Technologies used for this solution
|
||||||
|
/// </summary>
|
||||||
|
public ICollection<Technology> Technologies { get; set; } = new List<Technology>();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Cache for reading solution info
|
||||||
|
/// </summary>
|
||||||
|
private static readonly Lazy<ValueTask<List<SolutionInfo>?>> _slnInfo = new(() =>
|
||||||
|
{
|
||||||
|
var asm = Assembly.GetAssembly(typeof(SolutionInfo))
|
||||||
|
?? throw new ArgumentNullException("Could not load the containing assembly");
|
||||||
|
using var stream = asm.GetManifestResourceStream("MyWebLog.Themes.BitBadger.solutions.json")
|
||||||
|
?? throw new ArgumentNullException("Could not load the solution data");
|
||||||
|
return JsonSerializer.DeserializeAsync<List<SolutionInfo>>(stream);
|
||||||
|
});
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get all known solutions
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
/// <exception cref="ArgumentNullException">if any required object is null</exception>
|
||||||
|
public static async Task<ICollection<SolutionInfo>> GetAll() =>
|
||||||
|
await _slnInfo.Value ?? throw new ArgumentNullException("Could not deserialize solution data");
|
||||||
|
}
|
43
src/MyWebLog/Themes/BitBadger/Solutions.cshtml
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
@{
|
||||||
|
Layout = "_Layout";
|
||||||
|
ViewBag.Title = "All Solutions";
|
||||||
|
|
||||||
|
var data = await SolutionInfo.GetAll();
|
||||||
|
var active = data.Where(it => it.IsActive && it.LinkToAboutPage).OrderBy(it => it.Slug);
|
||||||
|
var inactive = data.Where(it => !it.IsActive && it.LinkToAboutPage).OrderBy(it => it.Slug);
|
||||||
|
}
|
||||||
|
<article class="content auto">
|
||||||
|
<h1>All Solutions</h1>
|
||||||
|
<h2>Active Solutions</h2>
|
||||||
|
@foreach (var sln in active)
|
||||||
|
{
|
||||||
|
<p>
|
||||||
|
<span class="app-name">@sln.Name</span> ~ <a href="~/solutions/@(sln.Slug).html">About</a>
|
||||||
|
@if (sln.IsActive)
|
||||||
|
{
|
||||||
|
<text>~ </text><a href="@sln.Url" target="_blank">Visit</a>
|
||||||
|
}
|
||||||
|
else if (sln.LinkToArchive ?? false)
|
||||||
|
{
|
||||||
|
<text>~ </text><a href="@sln.ArchiveUrl" target="_blank">Visit</a> <em>(archive)</em>
|
||||||
|
}
|
||||||
|
<br>@Html.Raw(sln.Summary)
|
||||||
|
</p>
|
||||||
|
}
|
||||||
|
<h2>Past Solutions</h2>
|
||||||
|
@foreach (var sln in inactive)
|
||||||
|
{
|
||||||
|
<p>
|
||||||
|
<span class="app-name">@sln.Name</span> ~ <a href="~/solutions/@(sln.Slug).html">About</a>
|
||||||
|
@if (sln.IsActive)
|
||||||
|
{
|
||||||
|
<text>~ </text><a href="@sln.Url" target="_blank">Visit</a>
|
||||||
|
}
|
||||||
|
else if (sln.LinkToArchive ?? false)
|
||||||
|
{
|
||||||
|
<text>~ </text><a href="@sln.ArchiveUrl" target="_blank">Visit</a> <em>(archive)</em>
|
||||||
|
}
|
||||||
|
<br>@Html.Raw(sln.Summary)
|
||||||
|
</p>
|
||||||
|
}
|
||||||
|
</article>
|
683
src/MyWebLog/Themes/BitBadger/solutions.json
Normal file
|
@ -0,0 +1,683 @@
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"Name": "A Word from the Word",
|
||||||
|
"Slug": "a-word-from-the-word",
|
||||||
|
"Url": "https://devotions.summershome.org",
|
||||||
|
"Category": "Personal",
|
||||||
|
"SkipAboutLink": true,
|
||||||
|
"FrontPage": {
|
||||||
|
"Display": true,
|
||||||
|
"Order": 2,
|
||||||
|
"Text": "Devotions by Daniel"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "Bay Vista Baptist Church",
|
||||||
|
"Slug": "bay-vista",
|
||||||
|
"Url": "https://bayvista.org",
|
||||||
|
"Category": "Static Sites",
|
||||||
|
"Summary": "Southern Baptist church in Biloxi, Mississippi",
|
||||||
|
"FrontPage": {
|
||||||
|
"Display": true,
|
||||||
|
"Order": 1,
|
||||||
|
"Text": "Biloxi, Mississippi"
|
||||||
|
},
|
||||||
|
"Technologies": [
|
||||||
|
{
|
||||||
|
"Name": "Hugo",
|
||||||
|
"Purpose": "static site generation",
|
||||||
|
"IsCurrent": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "Azure",
|
||||||
|
"Purpose": "podcast file storage, automated builds, and static site hosting",
|
||||||
|
"IsCurrent": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "GitHub",
|
||||||
|
"Purpose": "source code control",
|
||||||
|
"IsCurrent": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "Hexo",
|
||||||
|
"Purpose": "static site generation"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "Jekyll",
|
||||||
|
"Purpose": "static site generation"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "WordPress",
|
||||||
|
"Purpose": "content management"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "MySQL",
|
||||||
|
"Purpose": "data storage"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "Cassy Fiano",
|
||||||
|
"Slug": "cassy-fiano",
|
||||||
|
"Url": "http://www.cassyfiano.com",
|
||||||
|
"Category": "WordPress",
|
||||||
|
"Summary": "A “rising star” conservative blogger",
|
||||||
|
"IsInactive": true,
|
||||||
|
"DoNotLink": true,
|
||||||
|
"FrontPage": {
|
||||||
|
"Display": false
|
||||||
|
},
|
||||||
|
"Technologies": [
|
||||||
|
{
|
||||||
|
"Name": "WordPress",
|
||||||
|
"Purpose": "blogging (with a custom theme)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "MySQL",
|
||||||
|
"Purpose": "data storage"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "Rackspace Cloud",
|
||||||
|
"Purpose": "backup and recovery"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "Azure",
|
||||||
|
"Purpose": "backup and recovery"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "Daniel J. Summers",
|
||||||
|
"Slug": "daniel-j-summers",
|
||||||
|
"Url": "https://daniel.summershome.org",
|
||||||
|
"Category": "Personal",
|
||||||
|
"SkipAboutLink": true,
|
||||||
|
"FrontPage": {
|
||||||
|
"Display": true,
|
||||||
|
"Order": 1,
|
||||||
|
"Text": "Daniel’s personal blog"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "Dr. Melissa Clouthier",
|
||||||
|
"Slug": "dr-melissa-clouthier",
|
||||||
|
"Url": "http://melissablogs.com",
|
||||||
|
"Category": "WordPress",
|
||||||
|
"Summary": "Politics, health, podcasts and more",
|
||||||
|
"IsInactive": true,
|
||||||
|
"DoNotLink": true,
|
||||||
|
"FrontPage": {
|
||||||
|
"Display": false
|
||||||
|
},
|
||||||
|
"Technologies": [
|
||||||
|
{
|
||||||
|
"Name": "WordPress",
|
||||||
|
"Purpose": "blogging (with a custom theme)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "MySQL",
|
||||||
|
"Purpose": "data storage"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "Rackspace Cloud",
|
||||||
|
"Purpose": "backup and recovery"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "Azure",
|
||||||
|
"Purpose": "backup and recovery"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "Emerald Mountain Christian School",
|
||||||
|
"Slug": "emerald-mountain-christian-school",
|
||||||
|
"Url": "http://www.emeraldmountainchristianschool.org",
|
||||||
|
"Category": "Web Sites and Applications",
|
||||||
|
"Summary": "Classical, Christ-centered education near Wetumpka, Alabama",
|
||||||
|
"IsInactive": true,
|
||||||
|
"FrontPage": {
|
||||||
|
"Display": false
|
||||||
|
},
|
||||||
|
"Technologies": [
|
||||||
|
{
|
||||||
|
"Name": "PHP",
|
||||||
|
"Purpose": "page generation and interactivity"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "ASP.NET MVC",
|
||||||
|
"Purpose": "page generation and interactivity"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "PostgreSQL",
|
||||||
|
"Purpose": "data storage"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "Rackspace Cloud",
|
||||||
|
"Purpose": "hosting"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "Azure",
|
||||||
|
"Purpose": "hosting"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "Futility Closet",
|
||||||
|
"Slug": "futility-closet",
|
||||||
|
"Url": "https://www.futilitycloset.com",
|
||||||
|
"Category": "WordPress",
|
||||||
|
"Summary": "An idler’s miscellany of compendious amusements",
|
||||||
|
"FrontPage": {
|
||||||
|
"Display": true,
|
||||||
|
"Order": 1
|
||||||
|
},
|
||||||
|
"Technologies": [
|
||||||
|
{
|
||||||
|
"Name": "WordPress",
|
||||||
|
"Purpose": "blogging",
|
||||||
|
"IsCurrent": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "nginx",
|
||||||
|
"Purpose": "the web server",
|
||||||
|
"IsCurrent": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "MySQL",
|
||||||
|
"Purpose": "data storage",
|
||||||
|
"IsCurrent": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "Digital Ocean",
|
||||||
|
"Purpose": "web site hosting",
|
||||||
|
"IsCurrent": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "Azure",
|
||||||
|
"Purpose": "backup and recovery",
|
||||||
|
"IsCurrent": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "Rackspace Cloud",
|
||||||
|
"Purpose": "web site hosting"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "Hard Corps Wife",
|
||||||
|
"Slug": "hard-corps-wife",
|
||||||
|
"Url": "http://www.hardcorpswife.com",
|
||||||
|
"Category": "WordPress",
|
||||||
|
"Summary": "Cassy’s life as a Marine wife",
|
||||||
|
"IsInactive": true,
|
||||||
|
"DoNotLink": true,
|
||||||
|
"FrontPage": {
|
||||||
|
"Display": false
|
||||||
|
},
|
||||||
|
"Technologies": [
|
||||||
|
{
|
||||||
|
"Name": "WordPress",
|
||||||
|
"Purpose": "blogging"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "MySQL",
|
||||||
|
"Purpose": "data storage"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "Rackspace Cloud",
|
||||||
|
"Purpose": "backup and recovery"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "Liberty Pundits",
|
||||||
|
"Slug": "liberty-pundits",
|
||||||
|
"Url": "http://libertypundits.net",
|
||||||
|
"Category": "WordPress",
|
||||||
|
"Summary": "The home for conservatives",
|
||||||
|
"IsInactive": true,
|
||||||
|
"DoNotLink": true,
|
||||||
|
"FrontPage": {
|
||||||
|
"Display": false
|
||||||
|
},
|
||||||
|
"Technologies": [
|
||||||
|
{
|
||||||
|
"Name": "WordPress",
|
||||||
|
"Purpose": "blogging"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "PHP",
|
||||||
|
"Purpose": "custom data migration software"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "MySQL",
|
||||||
|
"Purpose": "data storage"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "Linux Resources",
|
||||||
|
"Slug": "linux-resources",
|
||||||
|
"Url": "https://blog.bitbadger.solutions/linux/",
|
||||||
|
"Category": "Web Sites and Applications",
|
||||||
|
"SkipAboutLink": true,
|
||||||
|
"FrontPage": {
|
||||||
|
"Display": true,
|
||||||
|
"Order": 99,
|
||||||
|
"Text": "Handy information for Linux folks"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "Mindy Mackenzie",
|
||||||
|
"Slug": "mindy-mackenzie",
|
||||||
|
"Url": "https://mindymackenzie.com",
|
||||||
|
"Category": "WordPress",
|
||||||
|
"Summary": "<em>Wall Street Journal</em> best-selling author and C-suite advisor",
|
||||||
|
"FrontPage": {
|
||||||
|
"Display": true,
|
||||||
|
"Order": 2,
|
||||||
|
"Text": "WSJ-best-selling author of The Courage Solution"
|
||||||
|
},
|
||||||
|
"Technologies": [
|
||||||
|
{
|
||||||
|
"Name": "WordPress",
|
||||||
|
"Purpose": "blogging and content management",
|
||||||
|
"IsCurrent": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "nginx",
|
||||||
|
"Purpose": "the web server",
|
||||||
|
"IsCurrent": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "MySQL",
|
||||||
|
"Purpose": "data storage",
|
||||||
|
"IsCurrent": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "Digital Ocean",
|
||||||
|
"Purpose": "web site hosting",
|
||||||
|
"IsCurrent": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "Azure",
|
||||||
|
"Purpose": "backup and recovery",
|
||||||
|
"IsCurrent": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "myPrayerJournal",
|
||||||
|
"Slug": "my-prayer-journal",
|
||||||
|
"Url": "https://prayerjournal.me",
|
||||||
|
"Category": "Web Sites and Applications",
|
||||||
|
"Summary": "Minimalist personal prayer journal",
|
||||||
|
"FrontPage": {
|
||||||
|
"Display": true,
|
||||||
|
"Order": 2
|
||||||
|
},
|
||||||
|
"Technologies": [
|
||||||
|
{
|
||||||
|
"Name": "htmx",
|
||||||
|
"Purpose": "front end interactivity",
|
||||||
|
"IsCurrent": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "Giraffe",
|
||||||
|
"Purpose": "the back end",
|
||||||
|
"IsCurrent": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "LiteDB",
|
||||||
|
"Purpose": "data storage",
|
||||||
|
"IsCurrent": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "GitHub",
|
||||||
|
"Purpose": "source code control",
|
||||||
|
"IsCurrent": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "GitHub Pages",
|
||||||
|
"Purpose": "documentation",
|
||||||
|
"IsCurrent": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "Vue.js",
|
||||||
|
"Purpose": "the front end"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "RavenDB",
|
||||||
|
"Purpose": "data storage"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "PostgreSQL",
|
||||||
|
"Purpose": "data storage"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "Not So Extreme Makeover: Community Edition",
|
||||||
|
"Slug": "nsx",
|
||||||
|
"Url": "http://notsoextreme.org",
|
||||||
|
"Category": "Web Sites and Applications",
|
||||||
|
"Summary": "Public site for the makeover; provides event-driven management of volunteers, donations, and families needing help",
|
||||||
|
"IsInactive": true,
|
||||||
|
"DoNotLink": true,
|
||||||
|
"LinkToArchive": true,
|
||||||
|
"ArchiveUrl": "https://nsx.archive.bitbadger.solutions",
|
||||||
|
"FrontPage": {
|
||||||
|
"Display": false
|
||||||
|
},
|
||||||
|
"Technologies": [
|
||||||
|
{
|
||||||
|
"Name": "WordPress",
|
||||||
|
"Purpose": "content management"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "PHP",
|
||||||
|
"Purpose": "for NSXapp"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "MySQL",
|
||||||
|
"Purpose": "WordPress data storage"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "PostgreSQL",
|
||||||
|
"Purpose": "NSXapp data storage"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "Olivet Baptist Church",
|
||||||
|
"Slug": "olivet-baptist",
|
||||||
|
"Url": "https://olivet-baptist.org",
|
||||||
|
"Category": "Static Sites",
|
||||||
|
"Summary": "Southern Baptist church in Gulfport, Mississippi",
|
||||||
|
"IsInactive": true,
|
||||||
|
"DoNotLink": true,
|
||||||
|
"LinkToArchive": true,
|
||||||
|
"ArchiveUrl": "https://olivet.archive.bitbadger.solutions",
|
||||||
|
"FrontPage": {
|
||||||
|
"Display": false
|
||||||
|
},
|
||||||
|
"Technologies": [
|
||||||
|
{
|
||||||
|
"Name": "Azure",
|
||||||
|
"Purpose": "podcast file storage and archive site hosting",
|
||||||
|
"IsCurrent": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "Vue.js",
|
||||||
|
"Purpose": "the user interface for the PWA"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "Hexo",
|
||||||
|
"Purpose": "for generating the site pages"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "WordPress",
|
||||||
|
"Purpose": "content management"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "MySQL",
|
||||||
|
"Purpose": "data storage"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "Photography by Michelle",
|
||||||
|
"Slug": "photography-by-michelle",
|
||||||
|
"Url": "https://www.summershome.org",
|
||||||
|
"Category": "Web Sites and Applications",
|
||||||
|
"Summary": "Photography services in Albuquerque, New Mexico",
|
||||||
|
"IsInactive": true,
|
||||||
|
"DoNotLink": true,
|
||||||
|
"FrontPage": {
|
||||||
|
"Display": false
|
||||||
|
},
|
||||||
|
"Technologies": [
|
||||||
|
{
|
||||||
|
"Name": "ASP.NET MVC",
|
||||||
|
"Purpose": "content management / gallery creation API"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "PostgreSQL",
|
||||||
|
"Purpose": "data storage"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "C# / Windows Forms",
|
||||||
|
"Purpose": "desktop gallery application"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "WordPress",
|
||||||
|
"Purpose": "content management"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "MySQL",
|
||||||
|
"Purpose": "data storage"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "PrayerTracker",
|
||||||
|
"Slug": "prayer-tracker",
|
||||||
|
"Url": "https://prayer.bitbadger.solutions",
|
||||||
|
"Category": "Web Sites and Applications",
|
||||||
|
"Summary": "Provides an ongoing, centralized prayer list for Sunday School classes and other groups",
|
||||||
|
"FrontPage": {
|
||||||
|
"Display": true,
|
||||||
|
"Order": 1,
|
||||||
|
"Text": "A prayer request tracking website (Free for any church or Sunday School class!)"
|
||||||
|
},
|
||||||
|
"Technologies": [
|
||||||
|
{
|
||||||
|
"Name": "Giraffe",
|
||||||
|
"Purpose": "server-side logic and dynamic page generation",
|
||||||
|
"IsCurrent": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "PostgreSQL",
|
||||||
|
"Purpose": "data storage",
|
||||||
|
"IsCurrent": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "GitHub",
|
||||||
|
"Purpose": "source code control",
|
||||||
|
"IsCurrent": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "GitHub Pages",
|
||||||
|
"Purpose": "documentation hosting",
|
||||||
|
"IsCurrent": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "MongoDB",
|
||||||
|
"Purpose": "data storage"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "ASP.NET MVC",
|
||||||
|
"Purpose": "dynamic content generation"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "Database Abstraction",
|
||||||
|
"Purpose": "data access"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "MySQL",
|
||||||
|
"Purpose": "data storage"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "PHP",
|
||||||
|
"Purpose": "dynamic content generation"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "Riehl World News",
|
||||||
|
"Slug": "riehl-world-news",
|
||||||
|
"Url": "http://riehlworldview.com",
|
||||||
|
"Category": "WordPress",
|
||||||
|
"Summary": "Riehl news for real people",
|
||||||
|
"FrontPage": {
|
||||||
|
"Display": true,
|
||||||
|
"Order": 3
|
||||||
|
},
|
||||||
|
"Technologies": [
|
||||||
|
{
|
||||||
|
"Name": "WordPress",
|
||||||
|
"Purpose": "blogging",
|
||||||
|
"IsCurrent": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "MySQL",
|
||||||
|
"Purpose": "data storage",
|
||||||
|
"IsCurrent": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "Azure",
|
||||||
|
"Purpose": "backup and recovery",
|
||||||
|
"IsCurrent": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "F#",
|
||||||
|
"Purpose": "custom archive static page generation"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "The Clearinghouse Management System",
|
||||||
|
"Slug": "tcms",
|
||||||
|
"Url": "http://tcms.us",
|
||||||
|
"Category": "Web Sites and Applications",
|
||||||
|
"Summary": "Assists a needs clearinghouse in connecting people with needs to people that can help meet those needs",
|
||||||
|
"IsInactive": true,
|
||||||
|
"DoNotLink": true,
|
||||||
|
"FrontPage": {
|
||||||
|
"Display": false
|
||||||
|
},
|
||||||
|
"Technologies": [
|
||||||
|
{
|
||||||
|
"Name": "PHP",
|
||||||
|
"Purpose": "the TCMS application logic"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "WordPress",
|
||||||
|
"Purpose": "publicly-facing pages and authentication"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "PostgreSQL",
|
||||||
|
"Purpose": "application data storage"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "MySQL",
|
||||||
|
"Purpose": "WordPress data storage"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "The Bit Badger Blog",
|
||||||
|
"Slug": "tech-blog",
|
||||||
|
"Url": "https://blog.bitbadger.solutions",
|
||||||
|
"Category": "Static Sites",
|
||||||
|
"Summary": "Geek stuff from Bit Badger Solutions",
|
||||||
|
"FrontPage": {
|
||||||
|
"Display": true,
|
||||||
|
"Order": 3,
|
||||||
|
"Text": "Technical information (“geek stuff”) from Bit Badger Solutions"
|
||||||
|
},
|
||||||
|
"Technologies": [
|
||||||
|
{
|
||||||
|
"Name": "Hexo",
|
||||||
|
"Purpose": "static site generation",
|
||||||
|
"IsCurrent": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "Azure",
|
||||||
|
"Purpose": "static site hosting",
|
||||||
|
"IsCurrent": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "GitHub",
|
||||||
|
"Purpose": "source code control",
|
||||||
|
"IsCurrent": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "Custom Software",
|
||||||
|
"Purpose": "content management"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "WordPress",
|
||||||
|
"Purpose": "content management"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "BlogEngine.NET",
|
||||||
|
"Purpose": "content management"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "Orchard",
|
||||||
|
"Purpose": "content management"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "myWebLog",
|
||||||
|
"Purpose": "content management"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "Jekyll",
|
||||||
|
"Purpose": "static site generation"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "MySQL",
|
||||||
|
"Purpose": "data storage"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "SQL Sever",
|
||||||
|
"Purpose": "data storage"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "RethinkDB",
|
||||||
|
"Purpose": "data storage"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "The Shark Tank",
|
||||||
|
"Slug": "the-shark-tank",
|
||||||
|
"Url": "http://shark-tank.net",
|
||||||
|
"Category": "WordPress",
|
||||||
|
"Summary": "Florida’s political feeding frenzy",
|
||||||
|
"IsInactive": true,
|
||||||
|
"DoNotLink": true,
|
||||||
|
"FrontPage": {
|
||||||
|
"Display": false
|
||||||
|
},
|
||||||
|
"Technologies": [
|
||||||
|
{
|
||||||
|
"Name": "WordPress",
|
||||||
|
"Purpose": "blogging"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "Virtual Prayer Room",
|
||||||
|
"Slug": "virtual-prayer-room",
|
||||||
|
"Url": "https://virtualprayerroom.us",
|
||||||
|
"Category": "Web Sites and Applications",
|
||||||
|
"Summary": "Gives prayer warriors access to requests from wherever they may be, and sends them daily updates",
|
||||||
|
"IsInactive": true,
|
||||||
|
"DoNotLink": true,
|
||||||
|
"FrontPage": {
|
||||||
|
"Display": false
|
||||||
|
},
|
||||||
|
"Technologies": [
|
||||||
|
{
|
||||||
|
"Name": "PHP",
|
||||||
|
"Purpose": "the application logic"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "PostgreSQL",
|
||||||
|
"Purpose": "data storage"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
|
@ -1,6 +1,6 @@
|
||||||
<footer>
|
<footer>
|
||||||
<hr>
|
<hr>
|
||||||
<div class="container-fluid text-end">
|
<div class="container-fluid text-end">
|
||||||
<img src="img/logo-dark.png" alt="myWebLog">
|
<img src="~/img/logo-dark.png" alt="myWebLog">
|
||||||
</div>
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
|
|
|
@ -2,11 +2,12 @@
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
<meta name="viewport" content="width=device-width">
|
<meta name="viewport" content="width=device-width">
|
||||||
<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/@Model.WebLog.ThemePath/style.css">
|
<link asp-theme="@Model.WebLog.ThemePath" />
|
||||||
@await RenderSectionAsync("Style", false)
|
@await RenderSectionAsync("Style", false)
|
||||||
<title>@ViewBag.Title « @Model.WebLog.Name</title>
|
<title>@ViewBag.Title « @Model.WebLog.Name</title>
|
||||||
</head>
|
</head>
|
||||||
|
|
|
@ -1 +1,3 @@
|
||||||
@namespace MyWebLog.Themes
|
@namespace MyWebLog.Themes
|
||||||
|
|
||||||
|
@addTagHelper *, MyWebLog
|
||||||
|
|
262
src/MyWebLog/wwwroot/css/BitBadger/style.css
Normal file
|
@ -0,0 +1,262 @@
|
||||||
|
html {
|
||||||
|
background-color: lightgray;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
margin: 0px;
|
||||||
|
font-family: "Raleway", "Segoe UI", Ubuntu, Tahoma, "DejaVu Sans", "Liberation Sans", Arial, sans-serif;
|
||||||
|
background-color: #FFFAFA;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: navy;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
a:hover {
|
||||||
|
border-bottom: dotted 1px navy;
|
||||||
|
}
|
||||||
|
|
||||||
|
a img {
|
||||||
|
border: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
acronym {
|
||||||
|
border-bottom: dotted 1px black;
|
||||||
|
}
|
||||||
|
|
||||||
|
header, h1, h2, h3, footer a {
|
||||||
|
font-family: "Oswald", "Segoe UI", Ubuntu, "DejaVu Sans", "Liberation Sans", Arial, sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
text-align: center;
|
||||||
|
margin: 1.4rem 0;
|
||||||
|
font-size: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
margin: 1.2rem 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
margin: 1rem 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
h2, h3 {
|
||||||
|
border-bottom: solid 2px navy;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media all and (min-width:40rem) {
|
||||||
|
h2, h3 {
|
||||||
|
width: 80%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
margin: 1rem 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#content {
|
||||||
|
margin: 0 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content {
|
||||||
|
font-size: 1.1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.auto {
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media all and (min-width: 68rem) {
|
||||||
|
.content {
|
||||||
|
width: 66rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.hdr {
|
||||||
|
font-size: 14pt;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.strike {
|
||||||
|
text-decoration: line-through;
|
||||||
|
}
|
||||||
|
|
||||||
|
.alignleft {
|
||||||
|
float: left;
|
||||||
|
padding-right: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul {
|
||||||
|
padding-left: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
li {
|
||||||
|
list-style-type: disc;
|
||||||
|
}
|
||||||
|
|
||||||
|
.app-info {
|
||||||
|
display: flex;
|
||||||
|
flex-flow: row-reverse wrap;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
abbr[title] {
|
||||||
|
text-decoration: none;
|
||||||
|
border-bottom: dotted 1px rgba(0, 0, 0, .5)
|
||||||
|
}
|
||||||
|
/* Header Style */
|
||||||
|
.site-header {
|
||||||
|
height: 100px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: space-between;
|
||||||
|
background-image: linear-gradient(to bottom, lightgray, #FFFAFA);
|
||||||
|
}
|
||||||
|
|
||||||
|
.site-header a, .site-header a:visited {
|
||||||
|
color: black;
|
||||||
|
}
|
||||||
|
|
||||||
|
.site-header a:hover {
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.site-header .header-title {
|
||||||
|
font-size: 3rem;
|
||||||
|
font-weight: bold;
|
||||||
|
line-height: 100px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.site-header .header-spacer {
|
||||||
|
flex-grow: 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
.site-header .header-social {
|
||||||
|
padding: 25px .8rem 0 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.site-header .header-social img {
|
||||||
|
width: 50px;
|
||||||
|
height: 50px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media all and (max-width:40rem) {
|
||||||
|
.site-header {
|
||||||
|
height: auto;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.site-header .header-title {
|
||||||
|
line-height: 3rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.site-header .header-spacer {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* Home Page Styles */
|
||||||
|
@media all and (min-width: 80rem) {
|
||||||
|
.home {
|
||||||
|
display: flex;
|
||||||
|
flex-flow: row;
|
||||||
|
align-items: flex-start;
|
||||||
|
justify-content: space-around;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.home-lead {
|
||||||
|
font-size: 1.3rem;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Home Page Sidebar Styles */
|
||||||
|
.app-sidebar {
|
||||||
|
text-align: center;
|
||||||
|
border-top: dotted 1px lightgray;
|
||||||
|
padding-top: 1rem;
|
||||||
|
font-size: .9rem;
|
||||||
|
display: flex;
|
||||||
|
flex-flow: row wrap;
|
||||||
|
justify-content: space-around;
|
||||||
|
}
|
||||||
|
|
||||||
|
.app-sidebar > div {
|
||||||
|
width: 20rem;
|
||||||
|
padding-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media all and (min-width: 68rem) {
|
||||||
|
.app-sidebar {
|
||||||
|
width: 66rem;
|
||||||
|
margin: auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media all and (min-width: 80rem) {
|
||||||
|
.app-sidebar {
|
||||||
|
width: 12rem;
|
||||||
|
border-top: none;
|
||||||
|
border-left: dotted 1px lightgray;
|
||||||
|
padding-top: 0;
|
||||||
|
padding-left: 2rem;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.app-sidebar > div {
|
||||||
|
width: auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.app-sidebar a {
|
||||||
|
font-size: 10pt;
|
||||||
|
font-family: sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
.app-sidebar-head {
|
||||||
|
font-family: "Oswald", "Segoe UI", Ubuntu, "DejaVu Sans", "Liberation Sans", Arial, sans-serif;
|
||||||
|
font-weight: bold;
|
||||||
|
color: maroon;
|
||||||
|
margin-bottom: .8rem;
|
||||||
|
padding: 3px 12px;
|
||||||
|
border-bottom: solid 2px lightgray;
|
||||||
|
font-size: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.app-sidebar-name, .app-sidebar-description {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.app-sidebar-description {
|
||||||
|
font-style: italic;
|
||||||
|
color: #555555;
|
||||||
|
padding-bottom: .6rem;
|
||||||
|
}
|
||||||
|
/* Solutions Page Styles */
|
||||||
|
.app-name {
|
||||||
|
font-family: "Oswald", "Segoe UI", Ubuntu, "DejaVu Sans", "Liberation Sans", Arial, sans-serif;
|
||||||
|
font-size: 1.3rem;
|
||||||
|
font-weight: bold;
|
||||||
|
color: maroon;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Footer Styles */
|
||||||
|
footer.site-footer {
|
||||||
|
display: flex;
|
||||||
|
flex-flow: row wrap;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 20px 15px 10px 15px;
|
||||||
|
font-size: 1rem;
|
||||||
|
color: black;
|
||||||
|
clear: both;
|
||||||
|
background-image: linear-gradient(to bottom, #FFFAFA, lightgray);
|
||||||
|
}
|
||||||
|
|
||||||
|
footer.site-footer a:link, footer.site-footer a:visited {
|
||||||
|
color: black;
|
||||||
|
}
|
BIN
src/MyWebLog/wwwroot/img/BitBadger/bit-badger-auth.png
Normal file
After Width: | Height: | Size: 23 KiB |
BIN
src/MyWebLog/wwwroot/img/BitBadger/bitbadger.png
Normal file
After Width: | Height: | Size: 17 KiB |
BIN
src/MyWebLog/wwwroot/img/BitBadger/facebook.png
Normal file
After Width: | Height: | Size: 6.4 KiB |
BIN
src/MyWebLog/wwwroot/img/BitBadger/favicon.ico
Normal file
After Width: | Height: | Size: 9.3 KiB |
BIN
src/MyWebLog/wwwroot/img/BitBadger/screenshots/bay-vista.png
Normal file
After Width: | Height: | Size: 54 KiB |
BIN
src/MyWebLog/wwwroot/img/BitBadger/screenshots/cassy-fiano.png
Normal file
After Width: | Height: | Size: 57 KiB |
After Width: | Height: | Size: 69 KiB |
After Width: | Height: | Size: 70 KiB |
After Width: | Height: | Size: 90 KiB |
After Width: | Height: | Size: 70 KiB |
After Width: | Height: | Size: 68 KiB |
After Width: | Height: | Size: 38 KiB |
After Width: | Height: | Size: 23 KiB |
BIN
src/MyWebLog/wwwroot/img/BitBadger/screenshots/nsx.png
Normal file
After Width: | Height: | Size: 47 KiB |
After Width: | Height: | Size: 15 KiB |
After Width: | Height: | Size: 90 KiB |
After Width: | Height: | Size: 42 KiB |
After Width: | Height: | Size: 71 KiB |
BIN
src/MyWebLog/wwwroot/img/BitBadger/screenshots/tcms.png
Normal file
After Width: | Height: | Size: 34 KiB |
BIN
src/MyWebLog/wwwroot/img/BitBadger/screenshots/tech-blog.png
Normal file
After Width: | Height: | Size: 60 KiB |
After Width: | Height: | Size: 95 KiB |
After Width: | Height: | Size: 44 KiB |
BIN
src/MyWebLog/wwwroot/img/BitBadger/twitter.png
Normal file
After Width: | Height: | Size: 10 KiB |