diff --git a/src/MyWebLog.Data/Extensions/PageExtensions.cs b/src/MyWebLog.Data/Extensions/PageExtensions.cs index 4d8c26f..ea64a82 100644 --- a/src/MyWebLog.Data/Extensions/PageExtensions.cs +++ b/src/MyWebLog.Data/Extensions/PageExtensions.cs @@ -55,7 +55,7 @@ public static class PageExtensions /// The page number to retrieve /// The pages public static async Task> FindPageOfPages(this DbSet 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); /// /// Retrieve a page by its ID (tracked) diff --git a/src/MyWebLog.Data/Migrations/20220227160816_Initial.Designer.cs b/src/MyWebLog.Data/Migrations/20220307034307_Initial.Designer.cs similarity index 99% rename from src/MyWebLog.Data/Migrations/20220227160816_Initial.Designer.cs rename to src/MyWebLog.Data/Migrations/20220307034307_Initial.Designer.cs index f9df601..37c93a6 100644 --- a/src/MyWebLog.Data/Migrations/20220227160816_Initial.Designer.cs +++ b/src/MyWebLog.Data/Migrations/20220307034307_Initial.Designer.cs @@ -11,7 +11,7 @@ using MyWebLog.Data; namespace MyWebLog.Data.Migrations { [DbContext(typeof(WebLogDbContext))] - [Migration("20220227160816_Initial")] + [Migration("20220307034307_Initial")] partial class Initial { protected override void BuildTargetModel(ModelBuilder modelBuilder) @@ -123,6 +123,9 @@ namespace MyWebLog.Data.Migrations b.Property("ShowInPageList") .HasColumnType("INTEGER"); + b.Property("Template") + .HasColumnType("TEXT"); + b.Property("Text") .IsRequired() .HasColumnType("TEXT"); diff --git a/src/MyWebLog.Data/Migrations/20220227160816_Initial.cs b/src/MyWebLog.Data/Migrations/20220307034307_Initial.cs similarity index 99% rename from src/MyWebLog.Data/Migrations/20220227160816_Initial.cs rename to src/MyWebLog.Data/Migrations/20220307034307_Initial.cs index cf155f5..6381fc9 100644 --- a/src/MyWebLog.Data/Migrations/20220227160816_Initial.cs +++ b/src/MyWebLog.Data/Migrations/20220307034307_Initial.cs @@ -87,6 +87,7 @@ namespace MyWebLog.Data.Migrations PublishedOn = table.Column(type: "TEXT", nullable: false), UpdatedOn = table.Column(type: "TEXT", nullable: false), ShowInPageList = table.Column(type: "INTEGER", nullable: false), + Template = table.Column(type: "TEXT", nullable: true), Text = table.Column(type: "TEXT", nullable: false) }, constraints: table => diff --git a/src/MyWebLog.Data/Migrations/WebLogDbContextModelSnapshot.cs b/src/MyWebLog.Data/Migrations/WebLogDbContextModelSnapshot.cs index 8b1df7b..bd813ee 100644 --- a/src/MyWebLog.Data/Migrations/WebLogDbContextModelSnapshot.cs +++ b/src/MyWebLog.Data/Migrations/WebLogDbContextModelSnapshot.cs @@ -121,6 +121,9 @@ namespace MyWebLog.Data.Migrations b.Property("ShowInPageList") .HasColumnType("INTEGER"); + b.Property("Template") + .HasColumnType("TEXT"); + b.Property("Text") .IsRequired() .HasColumnType("TEXT"); diff --git a/src/MyWebLog.Data/Page.cs b/src/MyWebLog.Data/Page.cs index e1b3dd7..7233fdb 100644 --- a/src/MyWebLog.Data/Page.cs +++ b/src/MyWebLog.Data/Page.cs @@ -45,6 +45,11 @@ public class Page /// public bool ShowInPageList { get; set; } = false; + /// + /// The template to use when rendering this page + /// + public string? Template { get; set; } = null; + /// /// The current text of the page /// diff --git a/src/MyWebLog/Features/Pages/SinglePageModel.cs b/src/MyWebLog/Features/Pages/SinglePageModel.cs index 6138cac..e9913c4 100644 --- a/src/MyWebLog/Features/Pages/SinglePageModel.cs +++ b/src/MyWebLog/Features/Pages/SinglePageModel.cs @@ -10,6 +10,11 @@ public class SinglePageModel : MyWebLogModel /// public Page Page { get; init; } + /// + /// Is this the home page? + /// + public bool IsHome => Page.Id == WebLog.DefaultPage; + /// /// Constructor /// diff --git a/src/MyWebLog/Features/Posts/PostController.cs b/src/MyWebLog/Features/Posts/PostController.cs index 90054f5..91b34d7 100644 --- a/src/MyWebLog/Features/Posts/PostController.cs +++ b/src/MyWebLog/Features/Posts/PostController.cs @@ -21,7 +21,7 @@ public class PostController : MyWebLogController if (WebLog.DefaultPage == "posts") return await PageOfPosts(1); 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}")] @@ -42,7 +42,7 @@ public class PostController : MyWebLogController var page = await Db.Pages.FindByPermalink(permalink); 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 diff --git a/src/MyWebLog/Features/Shared/MyWebLogController.cs b/src/MyWebLog/Features/Shared/MyWebLogController.cs index f26b38b..b743564 100644 --- a/src/MyWebLog/Features/Shared/MyWebLogController.cs +++ b/src/MyWebLog/Features/Shared/MyWebLogController.cs @@ -34,6 +34,8 @@ public abstract class MyWebLogController : Controller protected ViewResult ThemedView(string template, object model) { + // TODO: get actual version + ViewBag.Version = "2"; return View(template, model); } } diff --git a/src/MyWebLog/Features/Shared/TagHelpers/ImageTagHelper.cs b/src/MyWebLog/Features/Shared/TagHelpers/ImageTagHelper.cs new file mode 100644 index 0000000..db07dae --- /dev/null +++ b/src/MyWebLog/Features/Shared/TagHelpers/ImageTagHelper.cs @@ -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; + +/// +/// Image tag helper to load a theme's image +/// +[HtmlTargetElement("img", Attributes = "asp-theme")] +public class ImageTagHelper : Microsoft.AspNetCore.Mvc.TagHelpers.ImageTagHelper +{ + /// + /// The theme for which the image should be loaded + /// + [HtmlAttributeName("asp-theme")] + public string Theme { get; set; } = ""; + + /// + public ImageTagHelper(IFileVersionProvider fileVersionProvider, HtmlEncoder htmlEncoder, + IUrlHelperFactory urlHelperFactory) + : base(fileVersionProvider, htmlEncoder, urlHelperFactory) { } + + /// + 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); + } +} diff --git a/src/MyWebLog/Features/Shared/TagHelpers/LinkTagHelper.cs b/src/MyWebLog/Features/Shared/TagHelpers/LinkTagHelper.cs new file mode 100644 index 0000000..dc25fa2 --- /dev/null +++ b/src/MyWebLog/Features/Shared/TagHelpers/LinkTagHelper.cs @@ -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; + +/// +/// Tag helper to link stylesheets for a theme +/// +[HtmlTargetElement("link", Attributes = "asp-theme")] +public class LinkTagHelper : Microsoft.AspNetCore.Mvc.TagHelpers.LinkTagHelper +{ + /// + /// The theme for which a style sheet should be loaded + /// + [HtmlAttributeName("asp-theme")] + public string Theme { get; set; } = ""; + + /// + /// The style sheet to be loaded (defaults to "style") + /// + [HtmlAttributeName("asp-style")] + public string Style { get; set; } = "style"; + + /// + public LinkTagHelper(IWebHostEnvironment hostingEnvironment, TagHelperMemoryCacheProvider cacheProvider, + IFileVersionProvider fileVersionProvider, HtmlEncoder htmlEncoder, JavaScriptEncoder javaScriptEncoder, + IUrlHelperFactory urlHelperFactory) + : base(hostingEnvironment, cacheProvider, fileVersionProvider, htmlEncoder, javaScriptEncoder, urlHelperFactory) + { } + + /// + 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); + } +} diff --git a/src/MyWebLog/MyWebLog.csproj b/src/MyWebLog/MyWebLog.csproj index 325e63e..31a7042 100644 --- a/src/MyWebLog/MyWebLog.csproj +++ b/src/MyWebLog/MyWebLog.csproj @@ -6,6 +6,14 @@ enable + + + + + + + + diff --git a/src/MyWebLog/Program.cs b/src/MyWebLog/Program.cs index 2a16434..c27e550 100644 --- a/src/MyWebLog/Program.cs +++ b/src/MyWebLog/Program.cs @@ -43,7 +43,7 @@ builder.Services.AddDbContext(o => { // TODO: can get from DI? var db = WebLogCache.HostToDb(new HttpContextAccessor().HttpContext!); - // "Data Source=Db/empty.db" + // "empty"; o.UseSqlite($"Data Source=Db/{db}.db"); }); diff --git a/src/MyWebLog/Themes/BitBadger/Shared/_AppSidebar.cshtml b/src/MyWebLog/Themes/BitBadger/Shared/_AppSidebar.cshtml new file mode 100644 index 0000000..e9a27bf --- /dev/null +++ b/src/MyWebLog/Themes/BitBadger/Shared/_AppSidebar.cshtml @@ -0,0 +1,29 @@ +@{ + var data = await SolutionInfo.GetAll(); + string[] cats = new[] { "Web Sites and Applications", "WordPress", "Static Sites", "Personal" }; + IEnumerable 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"; +} + diff --git a/src/MyWebLog/Themes/BitBadger/Shared/_Layout.cshtml b/src/MyWebLog/Themes/BitBadger/Shared/_Layout.cshtml new file mode 100644 index 0000000..a167b5a --- /dev/null +++ b/src/MyWebLog/Themes/BitBadger/Shared/_Layout.cshtml @@ -0,0 +1,56 @@ +@model MyWebLogModel + + + + + + + @ViewBag.Title « @Model.WebLog.Name + + + + + + +
+ @RenderBody() +
+ + + diff --git a/src/MyWebLog/Themes/BitBadger/SinglePage.cshtml b/src/MyWebLog/Themes/BitBadger/SinglePage.cshtml new file mode 100644 index 0000000..0c4c9f2 --- /dev/null +++ b/src/MyWebLog/Themes/BitBadger/SinglePage.cshtml @@ -0,0 +1,13 @@ +@using MyWebLog.Features.Pages +@model SinglePageModel +@{ + Layout = "_Layout"; + ViewBag.Title = Model.Page.Title; +} +
+
+ @if (!Model.IsHome) {

@Model.Page.Title

} + @Html.Raw(Model.Page.Text) +
+ @if (Model.IsHome) { @await Html.PartialAsync("_AppSidebar") } +
diff --git a/src/MyWebLog/Themes/BitBadger/SolutionInfo.cs b/src/MyWebLog/Themes/BitBadger/SolutionInfo.cs new file mode 100644 index 0000000..631f4ee --- /dev/null +++ b/src/MyWebLog/Themes/BitBadger/SolutionInfo.cs @@ -0,0 +1,147 @@ +using System.Reflection; +using System.Text.Json; + +namespace MyWebLog.Themes.BitBadger; + +/// +/// A technology used in a solution +/// +public class Technology +{ + /// + /// The name of the technology + /// + public string Name { get; set; } = ""; + + /// + /// Why this technology was used in this project + /// + public string Purpose { get; set; } = ""; + + /// + /// Whether this project currently uses this technology + /// + public bool? IsCurrent { get; set; } = null; +} + +/// +/// Information about the solutions displayed on the front page +/// +public class FrontPageInfo +{ + /// + /// Whether the solution should be on the front page sidebar + /// + public bool Display { get; set; } = false; + + /// + /// The order in which this solution should be displayed + /// + public byte? Order { get; set; } = null; + + /// + /// The description text for the front page sidebar + /// + public string? Text { get; set; } = null; +} + +/// +/// Information about a solution +/// +public class SolutionInfo +{ + /// + /// The name of the solution + /// + public string Name { get; set; } = ""; + + /// + /// The URL slug for the page for this solution + /// + public string Slug { get; set; } = ""; + + /// + /// The URL for the solution (not the page describing it) + /// + public string Url { get; set; } = ""; + + /// + /// The category into which this solution falls + /// + public string Category { get; set; } = ""; + + /// + /// A short summary of the solution + /// + public string? Summary { get; set; } = null; + + /// + /// Whether this solution is inactive + /// + public bool? IsInactive { get; set; } = null; + + /// + /// Whether this solution is active + /// + public bool IsActive => !(IsInactive ?? false); + + /// + /// Whether a link should not be generated to the URL for this solution + /// + public bool? DoNotLink { get; set; } = null; + + /// + /// Whether a link should be generated to this solution + /// + public bool LinkToSite => !(DoNotLink ?? false); + + /// + /// Whether an "About" link should be generated for this solution + /// + public bool? SkipAboutLink { get; set; } = null; + + /// + /// Whether an "About" link should be generated for this solution + /// + public bool LinkToAboutPage => !(SkipAboutLink ?? false); + + /// + /// Whether to generate a link to an archive site + /// + public bool? LinkToArchive { get; set; } = null; + + /// + /// The URL of the archive site for this solution + /// + public string? ArchiveUrl { get; set; } = null; + + /// + /// Home page sidebar display information + /// + public FrontPageInfo FrontPage { get; set; } = default!; + + /// + /// Technologies used for this solution + /// + public ICollection Technologies { get; set; } = new List(); + + /// + /// Cache for reading solution info + /// + private static readonly Lazy?>> _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>(stream); + }); + + /// + /// Get all known solutions + /// + /// + /// if any required object is null + public static async Task> GetAll() => + await _slnInfo.Value ?? throw new ArgumentNullException("Could not deserialize solution data"); +} diff --git a/src/MyWebLog/Themes/BitBadger/Solutions.cshtml b/src/MyWebLog/Themes/BitBadger/Solutions.cshtml new file mode 100644 index 0000000..7b9ac84 --- /dev/null +++ b/src/MyWebLog/Themes/BitBadger/Solutions.cshtml @@ -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); +} +
+

All Solutions

+

Active Solutions

+ @foreach (var sln in active) + { +

+ @sln.Name ~ About + @if (sln.IsActive) + { + ~ Visit + } + else if (sln.LinkToArchive ?? false) + { + ~ Visit (archive) + } +
@Html.Raw(sln.Summary) +

+ } +

Past Solutions

+ @foreach (var sln in inactive) + { +

+ @sln.Name ~ About + @if (sln.IsActive) + { + ~ Visit + } + else if (sln.LinkToArchive ?? false) + { + ~ Visit (archive) + } +
@Html.Raw(sln.Summary) +

+ } +
diff --git a/src/MyWebLog/Themes/BitBadger/solutions.json b/src/MyWebLog/Themes/BitBadger/solutions.json new file mode 100644 index 0000000..36a63a0 --- /dev/null +++ b/src/MyWebLog/Themes/BitBadger/solutions.json @@ -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": "Wall Street Journal 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" + } + ] + } +] diff --git a/src/MyWebLog/Themes/Default/Shared/_DefaultFooter.cshtml b/src/MyWebLog/Themes/Default/Shared/_DefaultFooter.cshtml index b9f86bb..d32c8fa 100644 --- a/src/MyWebLog/Themes/Default/Shared/_DefaultFooter.cshtml +++ b/src/MyWebLog/Themes/Default/Shared/_DefaultFooter.cshtml @@ -1,6 +1,6 @@ 

- myWebLog + myWebLog
diff --git a/src/MyWebLog/Themes/Default/Shared/_Layout.cshtml b/src/MyWebLog/Themes/Default/Shared/_Layout.cshtml index e70c2cc..37a8fbd 100644 --- a/src/MyWebLog/Themes/Default/Shared/_Layout.cshtml +++ b/src/MyWebLog/Themes/Default/Shared/_Layout.cshtml @@ -2,11 +2,12 @@ + - + @await RenderSectionAsync("Style", false) @ViewBag.Title « @Model.WebLog.Name diff --git a/src/MyWebLog/Themes/_ViewImports.cshtml b/src/MyWebLog/Themes/_ViewImports.cshtml index eaf3b3e..16800cf 100644 --- a/src/MyWebLog/Themes/_ViewImports.cshtml +++ b/src/MyWebLog/Themes/_ViewImports.cshtml @@ -1 +1,3 @@ @namespace MyWebLog.Themes + +@addTagHelper *, MyWebLog diff --git a/src/MyWebLog/wwwroot/css/BitBadger/style.css b/src/MyWebLog/wwwroot/css/BitBadger/style.css new file mode 100644 index 0000000..b52743a --- /dev/null +++ b/src/MyWebLog/wwwroot/css/BitBadger/style.css @@ -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; +} diff --git a/src/MyWebLog/wwwroot/img/BitBadger/bit-badger-auth.png b/src/MyWebLog/wwwroot/img/BitBadger/bit-badger-auth.png new file mode 100644 index 0000000..42c407c Binary files /dev/null and b/src/MyWebLog/wwwroot/img/BitBadger/bit-badger-auth.png differ diff --git a/src/MyWebLog/wwwroot/img/BitBadger/bitbadger.png b/src/MyWebLog/wwwroot/img/BitBadger/bitbadger.png new file mode 100644 index 0000000..62f8d76 Binary files /dev/null and b/src/MyWebLog/wwwroot/img/BitBadger/bitbadger.png differ diff --git a/src/MyWebLog/wwwroot/img/BitBadger/facebook.png b/src/MyWebLog/wwwroot/img/BitBadger/facebook.png new file mode 100644 index 0000000..3cdde01 Binary files /dev/null and b/src/MyWebLog/wwwroot/img/BitBadger/facebook.png differ diff --git a/src/MyWebLog/wwwroot/img/BitBadger/favicon.ico b/src/MyWebLog/wwwroot/img/BitBadger/favicon.ico new file mode 100644 index 0000000..22ca446 Binary files /dev/null and b/src/MyWebLog/wwwroot/img/BitBadger/favicon.ico differ diff --git a/src/MyWebLog/wwwroot/img/BitBadger/screenshots/bay-vista.png b/src/MyWebLog/wwwroot/img/BitBadger/screenshots/bay-vista.png new file mode 100644 index 0000000..0af52cf Binary files /dev/null and b/src/MyWebLog/wwwroot/img/BitBadger/screenshots/bay-vista.png differ diff --git a/src/MyWebLog/wwwroot/img/BitBadger/screenshots/cassy-fiano.png b/src/MyWebLog/wwwroot/img/BitBadger/screenshots/cassy-fiano.png new file mode 100644 index 0000000..5c96982 Binary files /dev/null and b/src/MyWebLog/wwwroot/img/BitBadger/screenshots/cassy-fiano.png differ diff --git a/src/MyWebLog/wwwroot/img/BitBadger/screenshots/dr-melissa-clouthier.png b/src/MyWebLog/wwwroot/img/BitBadger/screenshots/dr-melissa-clouthier.png new file mode 100644 index 0000000..1784647 Binary files /dev/null and b/src/MyWebLog/wwwroot/img/BitBadger/screenshots/dr-melissa-clouthier.png differ diff --git a/src/MyWebLog/wwwroot/img/BitBadger/screenshots/emerald-mountain-christian-school.png b/src/MyWebLog/wwwroot/img/BitBadger/screenshots/emerald-mountain-christian-school.png new file mode 100644 index 0000000..9275710 Binary files /dev/null and b/src/MyWebLog/wwwroot/img/BitBadger/screenshots/emerald-mountain-christian-school.png differ diff --git a/src/MyWebLog/wwwroot/img/BitBadger/screenshots/futility-closet.png b/src/MyWebLog/wwwroot/img/BitBadger/screenshots/futility-closet.png new file mode 100644 index 0000000..e9189fa Binary files /dev/null and b/src/MyWebLog/wwwroot/img/BitBadger/screenshots/futility-closet.png differ diff --git a/src/MyWebLog/wwwroot/img/BitBadger/screenshots/hard-corps-wife.png b/src/MyWebLog/wwwroot/img/BitBadger/screenshots/hard-corps-wife.png new file mode 100644 index 0000000..31e9d11 Binary files /dev/null and b/src/MyWebLog/wwwroot/img/BitBadger/screenshots/hard-corps-wife.png differ diff --git a/src/MyWebLog/wwwroot/img/BitBadger/screenshots/liberty-pundits.png b/src/MyWebLog/wwwroot/img/BitBadger/screenshots/liberty-pundits.png new file mode 100644 index 0000000..1382c80 Binary files /dev/null and b/src/MyWebLog/wwwroot/img/BitBadger/screenshots/liberty-pundits.png differ diff --git a/src/MyWebLog/wwwroot/img/BitBadger/screenshots/mindy-mackenzie.png b/src/MyWebLog/wwwroot/img/BitBadger/screenshots/mindy-mackenzie.png new file mode 100644 index 0000000..39fef9c Binary files /dev/null and b/src/MyWebLog/wwwroot/img/BitBadger/screenshots/mindy-mackenzie.png differ diff --git a/src/MyWebLog/wwwroot/img/BitBadger/screenshots/my-prayer-journal.png b/src/MyWebLog/wwwroot/img/BitBadger/screenshots/my-prayer-journal.png new file mode 100644 index 0000000..6192350 Binary files /dev/null and b/src/MyWebLog/wwwroot/img/BitBadger/screenshots/my-prayer-journal.png differ diff --git a/src/MyWebLog/wwwroot/img/BitBadger/screenshots/nsx.png b/src/MyWebLog/wwwroot/img/BitBadger/screenshots/nsx.png new file mode 100644 index 0000000..4083b97 Binary files /dev/null and b/src/MyWebLog/wwwroot/img/BitBadger/screenshots/nsx.png differ diff --git a/src/MyWebLog/wwwroot/img/BitBadger/screenshots/olivet-baptist.png b/src/MyWebLog/wwwroot/img/BitBadger/screenshots/olivet-baptist.png new file mode 100644 index 0000000..30edc33 Binary files /dev/null and b/src/MyWebLog/wwwroot/img/BitBadger/screenshots/olivet-baptist.png differ diff --git a/src/MyWebLog/wwwroot/img/BitBadger/screenshots/photography-by-michelle.png b/src/MyWebLog/wwwroot/img/BitBadger/screenshots/photography-by-michelle.png new file mode 100644 index 0000000..31c662b Binary files /dev/null and b/src/MyWebLog/wwwroot/img/BitBadger/screenshots/photography-by-michelle.png differ diff --git a/src/MyWebLog/wwwroot/img/BitBadger/screenshots/prayer-tracker.png b/src/MyWebLog/wwwroot/img/BitBadger/screenshots/prayer-tracker.png new file mode 100644 index 0000000..0c81b77 Binary files /dev/null and b/src/MyWebLog/wwwroot/img/BitBadger/screenshots/prayer-tracker.png differ diff --git a/src/MyWebLog/wwwroot/img/BitBadger/screenshots/riehl-world-news.png b/src/MyWebLog/wwwroot/img/BitBadger/screenshots/riehl-world-news.png new file mode 100644 index 0000000..a6f6d57 Binary files /dev/null and b/src/MyWebLog/wwwroot/img/BitBadger/screenshots/riehl-world-news.png differ diff --git a/src/MyWebLog/wwwroot/img/BitBadger/screenshots/tcms.png b/src/MyWebLog/wwwroot/img/BitBadger/screenshots/tcms.png new file mode 100644 index 0000000..01c4792 Binary files /dev/null and b/src/MyWebLog/wwwroot/img/BitBadger/screenshots/tcms.png differ diff --git a/src/MyWebLog/wwwroot/img/BitBadger/screenshots/tech-blog.png b/src/MyWebLog/wwwroot/img/BitBadger/screenshots/tech-blog.png new file mode 100644 index 0000000..e1a479f Binary files /dev/null and b/src/MyWebLog/wwwroot/img/BitBadger/screenshots/tech-blog.png differ diff --git a/src/MyWebLog/wwwroot/img/BitBadger/screenshots/the-shark-tank.png b/src/MyWebLog/wwwroot/img/BitBadger/screenshots/the-shark-tank.png new file mode 100644 index 0000000..35783d9 Binary files /dev/null and b/src/MyWebLog/wwwroot/img/BitBadger/screenshots/the-shark-tank.png differ diff --git a/src/MyWebLog/wwwroot/img/BitBadger/screenshots/virtual-prayer-room.png b/src/MyWebLog/wwwroot/img/BitBadger/screenshots/virtual-prayer-room.png new file mode 100644 index 0000000..d9e1bed Binary files /dev/null and b/src/MyWebLog/wwwroot/img/BitBadger/screenshots/virtual-prayer-room.png differ diff --git a/src/MyWebLog/wwwroot/img/BitBadger/twitter.png b/src/MyWebLog/wwwroot/img/BitBadger/twitter.png new file mode 100644 index 0000000..8b5ae7c Binary files /dev/null and b/src/MyWebLog/wwwroot/img/BitBadger/twitter.png differ