diff --git a/src/MyWebLog.Data/Data.fs b/src/MyWebLog.Data/Data.fs index e335d33..0dbcbf3 100644 --- a/src/MyWebLog.Data/Data.fs +++ b/src/MyWebLog.Data/Data.fs @@ -326,7 +326,7 @@ module Page = withTable Table.Page getAll [ webLogId ] (nameof webLogId) without [ "priorPermalinks"; "revisions" ] - orderBy "title" + orderByFunc (fun row -> row.G("title").Downcase ()) skip ((pageNbr - 1) * 25) limit 25 result; withRetryDefault diff --git a/src/MyWebLog.Domain/ViewModels.fs b/src/MyWebLog.Domain/ViewModels.fs index b9b73e3..c0261de 100644 --- a/src/MyWebLog.Domain/ViewModels.fs +++ b/src/MyWebLog.Domain/ViewModels.fs @@ -46,7 +46,26 @@ type DisplayPage = /// Is this the default page? isDefault : bool + + /// The text of the page + text : string + + /// The metadata for the page + metadata : MetaItem list } + /// Create a minimal display page (no text or metadata) from a database page + static member fromPageMinimal webLog (page : Page) = + let pageId = PageId.toString page.id + { id = pageId + title = page.title + permalink = Permalink.toString page.permalink + publishedOn = page.publishedOn + updatedOn = page.updatedOn + showInPageList = page.showInPageList + isDefault = pageId = webLog.defaultPage + text = "" + metadata = [] + } /// Create a display page from a database page static member fromPage webLog (page : Page) = let pageId = PageId.toString page.id @@ -57,6 +76,8 @@ type DisplayPage = updatedOn = page.updatedOn showInPageList = page.showInPageList isDefault = pageId = webLog.defaultPage + text = page.text + metadata = page.metadata } diff --git a/src/MyWebLog.Themes.BitBadger/MyWebLog.Themes.BitBadger.csproj b/src/MyWebLog.Themes.BitBadger/MyWebLog.Themes.BitBadger.csproj deleted file mode 100644 index 0fc96fe..0000000 --- a/src/MyWebLog.Themes.BitBadger/MyWebLog.Themes.BitBadger.csproj +++ /dev/null @@ -1,25 +0,0 @@ - - - - net6.0 - enable - enable - true - true - - - - - - - - - - - - - - - - - diff --git a/src/MyWebLog.Themes.BitBadger/Themes/BitBadger/Shared/_AppSidebar.cshtml b/src/MyWebLog.Themes.BitBadger/Themes/BitBadger/Shared/_AppSidebar.cshtml deleted file mode 100644 index e9a27bf..0000000 --- a/src/MyWebLog.Themes.BitBadger/Themes/BitBadger/Shared/_AppSidebar.cshtml +++ /dev/null @@ -1,29 +0,0 @@ -@{ - 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/Themes/BitBadger/Shared/_Layout.cshtml b/src/MyWebLog.Themes.BitBadger/Themes/BitBadger/Shared/_Layout.cshtml deleted file mode 100644 index a167b5a..0000000 --- a/src/MyWebLog.Themes.BitBadger/Themes/BitBadger/Shared/_Layout.cshtml +++ /dev/null @@ -1,56 +0,0 @@ -@model MyWebLogModel - - - - - - - @ViewBag.Title « @Model.WebLog.Name - - - - - - -
- @RenderBody() -
- - - diff --git a/src/MyWebLog.Themes.BitBadger/Themes/BitBadger/SinglePage.cshtml b/src/MyWebLog.Themes.BitBadger/Themes/BitBadger/SinglePage.cshtml deleted file mode 100644 index 0c4c9f2..0000000 --- a/src/MyWebLog.Themes.BitBadger/Themes/BitBadger/SinglePage.cshtml +++ /dev/null @@ -1,13 +0,0 @@ -@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/Themes/BitBadger/SolutionInfo.cs b/src/MyWebLog.Themes.BitBadger/Themes/BitBadger/SolutionInfo.cs deleted file mode 100644 index 631f4ee..0000000 --- a/src/MyWebLog.Themes.BitBadger/Themes/BitBadger/SolutionInfo.cs +++ /dev/null @@ -1,147 +0,0 @@ -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/Themes/BitBadger/Solutions.cshtml b/src/MyWebLog.Themes.BitBadger/Themes/BitBadger/Solutions.cshtml deleted file mode 100644 index 7b9ac84..0000000 --- a/src/MyWebLog.Themes.BitBadger/Themes/BitBadger/Solutions.cshtml +++ /dev/null @@ -1,43 +0,0 @@ -@{ - 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/Themes/BitBadger/solutions.json b/src/MyWebLog.Themes.BitBadger/Themes/BitBadger/solutions.json deleted file mode 100644 index 36a63a0..0000000 --- a/src/MyWebLog.Themes.BitBadger/Themes/BitBadger/solutions.json +++ /dev/null @@ -1,683 +0,0 @@ -[ - { - "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.BitBadger/Themes/_ViewImports.cshtml b/src/MyWebLog.Themes.BitBadger/Themes/_ViewImports.cshtml deleted file mode 100644 index 6c82c1f..0000000 --- a/src/MyWebLog.Themes.BitBadger/Themes/_ViewImports.cshtml +++ /dev/null @@ -1,6 +0,0 @@ -@namespace MyWebLog.Themes - -@using MyWebLog.Features.Shared -@using MyWebLog.Properties - -@addTagHelper *, MyWebLog diff --git a/src/MyWebLog.Themes.BitBadger/wwwroot/BitBadger/css/style.css b/src/MyWebLog.Themes.BitBadger/wwwroot/BitBadger/css/style.css deleted file mode 100644 index b52743a..0000000 --- a/src/MyWebLog.Themes.BitBadger/wwwroot/BitBadger/css/style.css +++ /dev/null @@ -1,262 +0,0 @@ -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.Themes.BitBadger/wwwroot/BitBadger/img/bit-badger-auth.png b/src/MyWebLog.Themes.BitBadger/wwwroot/BitBadger/img/bit-badger-auth.png deleted file mode 100644 index 42c407c..0000000 Binary files a/src/MyWebLog.Themes.BitBadger/wwwroot/BitBadger/img/bit-badger-auth.png and /dev/null differ diff --git a/src/MyWebLog.Themes.BitBadger/wwwroot/BitBadger/img/bitbadger.png b/src/MyWebLog.Themes.BitBadger/wwwroot/BitBadger/img/bitbadger.png deleted file mode 100644 index 62f8d76..0000000 Binary files a/src/MyWebLog.Themes.BitBadger/wwwroot/BitBadger/img/bitbadger.png and /dev/null differ diff --git a/src/MyWebLog.Themes.BitBadger/wwwroot/BitBadger/img/facebook.png b/src/MyWebLog.Themes.BitBadger/wwwroot/BitBadger/img/facebook.png deleted file mode 100644 index 3cdde01..0000000 Binary files a/src/MyWebLog.Themes.BitBadger/wwwroot/BitBadger/img/facebook.png and /dev/null differ diff --git a/src/MyWebLog.Themes.BitBadger/wwwroot/BitBadger/img/favicon.ico b/src/MyWebLog.Themes.BitBadger/wwwroot/BitBadger/img/favicon.ico deleted file mode 100644 index 22ca446..0000000 Binary files a/src/MyWebLog.Themes.BitBadger/wwwroot/BitBadger/img/favicon.ico and /dev/null differ diff --git a/src/MyWebLog.Themes.BitBadger/wwwroot/BitBadger/img/screenshots/bay-vista.png b/src/MyWebLog.Themes.BitBadger/wwwroot/BitBadger/img/screenshots/bay-vista.png deleted file mode 100644 index 0af52cf..0000000 Binary files a/src/MyWebLog.Themes.BitBadger/wwwroot/BitBadger/img/screenshots/bay-vista.png and /dev/null differ diff --git a/src/MyWebLog.Themes.BitBadger/wwwroot/BitBadger/img/screenshots/cassy-fiano.png b/src/MyWebLog.Themes.BitBadger/wwwroot/BitBadger/img/screenshots/cassy-fiano.png deleted file mode 100644 index 5c96982..0000000 Binary files a/src/MyWebLog.Themes.BitBadger/wwwroot/BitBadger/img/screenshots/cassy-fiano.png and /dev/null differ diff --git a/src/MyWebLog.Themes.BitBadger/wwwroot/BitBadger/img/screenshots/dr-melissa-clouthier.png b/src/MyWebLog.Themes.BitBadger/wwwroot/BitBadger/img/screenshots/dr-melissa-clouthier.png deleted file mode 100644 index 1784647..0000000 Binary files a/src/MyWebLog.Themes.BitBadger/wwwroot/BitBadger/img/screenshots/dr-melissa-clouthier.png and /dev/null differ diff --git a/src/MyWebLog.Themes.BitBadger/wwwroot/BitBadger/img/screenshots/emerald-mountain-christian-school.png b/src/MyWebLog.Themes.BitBadger/wwwroot/BitBadger/img/screenshots/emerald-mountain-christian-school.png deleted file mode 100644 index 9275710..0000000 Binary files a/src/MyWebLog.Themes.BitBadger/wwwroot/BitBadger/img/screenshots/emerald-mountain-christian-school.png and /dev/null differ diff --git a/src/MyWebLog.Themes.BitBadger/wwwroot/BitBadger/img/screenshots/futility-closet.png b/src/MyWebLog.Themes.BitBadger/wwwroot/BitBadger/img/screenshots/futility-closet.png deleted file mode 100644 index e9189fa..0000000 Binary files a/src/MyWebLog.Themes.BitBadger/wwwroot/BitBadger/img/screenshots/futility-closet.png and /dev/null differ diff --git a/src/MyWebLog.Themes.BitBadger/wwwroot/BitBadger/img/screenshots/hard-corps-wife.png b/src/MyWebLog.Themes.BitBadger/wwwroot/BitBadger/img/screenshots/hard-corps-wife.png deleted file mode 100644 index 31e9d11..0000000 Binary files a/src/MyWebLog.Themes.BitBadger/wwwroot/BitBadger/img/screenshots/hard-corps-wife.png and /dev/null differ diff --git a/src/MyWebLog.Themes.BitBadger/wwwroot/BitBadger/img/screenshots/liberty-pundits.png b/src/MyWebLog.Themes.BitBadger/wwwroot/BitBadger/img/screenshots/liberty-pundits.png deleted file mode 100644 index 1382c80..0000000 Binary files a/src/MyWebLog.Themes.BitBadger/wwwroot/BitBadger/img/screenshots/liberty-pundits.png and /dev/null differ diff --git a/src/MyWebLog.Themes.BitBadger/wwwroot/BitBadger/img/screenshots/mindy-mackenzie.png b/src/MyWebLog.Themes.BitBadger/wwwroot/BitBadger/img/screenshots/mindy-mackenzie.png deleted file mode 100644 index 39fef9c..0000000 Binary files a/src/MyWebLog.Themes.BitBadger/wwwroot/BitBadger/img/screenshots/mindy-mackenzie.png and /dev/null differ diff --git a/src/MyWebLog.Themes.BitBadger/wwwroot/BitBadger/img/screenshots/my-prayer-journal.png b/src/MyWebLog.Themes.BitBadger/wwwroot/BitBadger/img/screenshots/my-prayer-journal.png deleted file mode 100644 index 6192350..0000000 Binary files a/src/MyWebLog.Themes.BitBadger/wwwroot/BitBadger/img/screenshots/my-prayer-journal.png and /dev/null differ diff --git a/src/MyWebLog.Themes.BitBadger/wwwroot/BitBadger/img/screenshots/nsx.png b/src/MyWebLog.Themes.BitBadger/wwwroot/BitBadger/img/screenshots/nsx.png deleted file mode 100644 index 4083b97..0000000 Binary files a/src/MyWebLog.Themes.BitBadger/wwwroot/BitBadger/img/screenshots/nsx.png and /dev/null differ diff --git a/src/MyWebLog.Themes.BitBadger/wwwroot/BitBadger/img/screenshots/olivet-baptist.png b/src/MyWebLog.Themes.BitBadger/wwwroot/BitBadger/img/screenshots/olivet-baptist.png deleted file mode 100644 index 30edc33..0000000 Binary files a/src/MyWebLog.Themes.BitBadger/wwwroot/BitBadger/img/screenshots/olivet-baptist.png and /dev/null differ diff --git a/src/MyWebLog.Themes.BitBadger/wwwroot/BitBadger/img/screenshots/photography-by-michelle.png b/src/MyWebLog.Themes.BitBadger/wwwroot/BitBadger/img/screenshots/photography-by-michelle.png deleted file mode 100644 index 31c662b..0000000 Binary files a/src/MyWebLog.Themes.BitBadger/wwwroot/BitBadger/img/screenshots/photography-by-michelle.png and /dev/null differ diff --git a/src/MyWebLog.Themes.BitBadger/wwwroot/BitBadger/img/screenshots/prayer-tracker.png b/src/MyWebLog.Themes.BitBadger/wwwroot/BitBadger/img/screenshots/prayer-tracker.png deleted file mode 100644 index 0c81b77..0000000 Binary files a/src/MyWebLog.Themes.BitBadger/wwwroot/BitBadger/img/screenshots/prayer-tracker.png and /dev/null differ diff --git a/src/MyWebLog.Themes.BitBadger/wwwroot/BitBadger/img/screenshots/riehl-world-news.png b/src/MyWebLog.Themes.BitBadger/wwwroot/BitBadger/img/screenshots/riehl-world-news.png deleted file mode 100644 index a6f6d57..0000000 Binary files a/src/MyWebLog.Themes.BitBadger/wwwroot/BitBadger/img/screenshots/riehl-world-news.png and /dev/null differ diff --git a/src/MyWebLog.Themes.BitBadger/wwwroot/BitBadger/img/screenshots/tcms.png b/src/MyWebLog.Themes.BitBadger/wwwroot/BitBadger/img/screenshots/tcms.png deleted file mode 100644 index 01c4792..0000000 Binary files a/src/MyWebLog.Themes.BitBadger/wwwroot/BitBadger/img/screenshots/tcms.png and /dev/null differ diff --git a/src/MyWebLog.Themes.BitBadger/wwwroot/BitBadger/img/screenshots/tech-blog.png b/src/MyWebLog.Themes.BitBadger/wwwroot/BitBadger/img/screenshots/tech-blog.png deleted file mode 100644 index e1a479f..0000000 Binary files a/src/MyWebLog.Themes.BitBadger/wwwroot/BitBadger/img/screenshots/tech-blog.png and /dev/null differ diff --git a/src/MyWebLog.Themes.BitBadger/wwwroot/BitBadger/img/screenshots/the-shark-tank.png b/src/MyWebLog.Themes.BitBadger/wwwroot/BitBadger/img/screenshots/the-shark-tank.png deleted file mode 100644 index 35783d9..0000000 Binary files a/src/MyWebLog.Themes.BitBadger/wwwroot/BitBadger/img/screenshots/the-shark-tank.png and /dev/null differ diff --git a/src/MyWebLog.Themes.BitBadger/wwwroot/BitBadger/img/screenshots/virtual-prayer-room.png b/src/MyWebLog.Themes.BitBadger/wwwroot/BitBadger/img/screenshots/virtual-prayer-room.png deleted file mode 100644 index d9e1bed..0000000 Binary files a/src/MyWebLog.Themes.BitBadger/wwwroot/BitBadger/img/screenshots/virtual-prayer-room.png and /dev/null differ diff --git a/src/MyWebLog.Themes.BitBadger/wwwroot/BitBadger/img/twitter.png b/src/MyWebLog.Themes.BitBadger/wwwroot/BitBadger/img/twitter.png deleted file mode 100644 index 8b5ae7c..0000000 Binary files a/src/MyWebLog.Themes.BitBadger/wwwroot/BitBadger/img/twitter.png and /dev/null differ diff --git a/src/MyWebLog.sln b/src/MyWebLog.sln index 840f5d6..a594b6e 100644 --- a/src/MyWebLog.sln +++ b/src/MyWebLog.sln @@ -3,8 +3,6 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 VisualStudioVersion = 17.1.32210.238 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MyWebLog.Themes.BitBadger", "MyWebLog.Themes.BitBadger\MyWebLog.Themes.BitBadger.csproj", "{729F7AB3-2300-4390-B972-71D32FBBBF50}" -EndProject Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "MyWebLog.Domain", "MyWebLog.Domain\MyWebLog.Domain.fsproj", "{8CA99122-888A-4524-8C1B-685F0A4B7B4B}" EndProject Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "MyWebLog.Data", "MyWebLog.Data\MyWebLog.Data.fsproj", "{D284584D-2CB2-40C8-B605-6D0FD84D9D3D}" @@ -17,10 +15,6 @@ Global Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {729F7AB3-2300-4390-B972-71D32FBBBF50}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {729F7AB3-2300-4390-B972-71D32FBBBF50}.Debug|Any CPU.Build.0 = Debug|Any CPU - {729F7AB3-2300-4390-B972-71D32FBBBF50}.Release|Any CPU.ActiveCfg = Release|Any CPU - {729F7AB3-2300-4390-B972-71D32FBBBF50}.Release|Any CPU.Build.0 = Release|Any CPU {8CA99122-888A-4524-8C1B-685F0A4B7B4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {8CA99122-888A-4524-8C1B-685F0A4B7B4B}.Debug|Any CPU.Build.0 = Debug|Any CPU {8CA99122-888A-4524-8C1B-685F0A4B7B4B}.Release|Any CPU.ActiveCfg = Release|Any CPU diff --git a/src/MyWebLog.v2/Data.fs b/src/MyWebLog.v2/Data.fs deleted file mode 100644 index e69de29..0000000 diff --git a/src/MyWebLog.v2/Domain.fs b/src/MyWebLog.v2/Domain.fs deleted file mode 100644 index b12b397..0000000 --- a/src/MyWebLog.v2/Domain.fs +++ /dev/null @@ -1,490 +0,0 @@ -namespace MyWebLog.Domain - -// -- Supporting Types -- - -/// Types of markup text supported -type MarkupText = - /// Text in Markdown format - | Markdown of string - /// Text in HTML format - | Html of string - -/// Functions to support maniuplating markup text -module MarkupText = - /// Get the string representation of this markup text - let toString it = - match it with - | Markdown x -> "Markdown", x - | Html x -> "HTML", x - ||> sprintf "%s: %s" - /// Get the HTML value of the text - let toHtml = function - | Markdown it -> sprintf "TODO: convert to HTML - %s" it - | Html it -> it - /// Parse a string representation to markup text - let ofString (it : string) = - match true with - | _ when it.StartsWith "Markdown: " -> it.Substring 10 |> Markdown - | _ when it.StartsWith "HTML: " -> it.Substring 6 |> Html - | _ -> sprintf "Cannot determine text type - %s" it |> invalidOp - - -/// Authorization levels -type AuthorizationLevel = - /// Authorization to administer a weblog - | Administrator - /// Authorization to comment on a weblog - | User - -/// Functions to support authorization levels -module AuthorizationLevel = - /// Get the string reprsentation of an authorization level - let toString = function Administrator -> "Administrator" | User -> "User" - /// Create an authorization level from a string - let ofString it = - match it with - | "Administrator" -> Administrator - | "User" -> User - | _ -> sprintf "%s is not an authorization level" it |> invalidOp - - -/// Post statuses -type PostStatus = - /// Post has not been released for public consumption - | Draft - /// Post is released - | Published - -/// Functions to support post statuses -module PostStatus = - /// Get the string representation of a post status - let toString = function Draft -> "Draft" | Published -> "Published" - /// Create a post status from a string - let ofString it = - match it with - | "Draft" -> Draft - | "Published" -> Published - | _ -> sprintf "%s is not a post status" it |> invalidOp - - -/// Comment statuses -type CommentStatus = - /// Comment is approved - | Approved - /// Comment has yet to be approved - | Pending - /// Comment was flagged as spam - | Spam - -/// Functions to support comment statuses -module CommentStatus = - /// Get the string representation of a comment status - let toString = function Approved -> "Approved" | Pending -> "Pending" | Spam -> "Spam" - /// Create a comment status from a string - let ofString it = - match it with - | "Approved" -> Approved - | "Pending" -> Pending - | "Spam" -> Spam - | _ -> sprintf "%s is not a comment status" it |> invalidOp - - -/// Seconds since the Unix epoch -type UnixSeconds = UnixSeconds of int64 - -/// Functions to support Unix seconds -module UnixSeconds = - /// Get the long (int64) representation of Unix seconds - let toLong = function UnixSeconds it -> it - /// Zero seconds past the epoch - let none = UnixSeconds 0L - - -// -- IDs -- - -open System - -// See https://www.madskristensen.net/blog/A-shorter-and-URL-friendly-GUID for info on "short GUIDs" - -/// A short GUID -type ShortGuid = ShortGuid of Guid - -/// Functions to support short GUIDs -module ShortGuid = - /// Encode a GUID into a short GUID - let toString = function - | ShortGuid guid -> - Convert.ToBase64String(guid.ToByteArray ()) - .Replace("/", "_") - .Replace("+", "-") - .Substring (0, 22) - /// Decode a short GUID into a GUID - let ofString (it : string) = - it.Replace("_", "/").Replace ("-", "+") - |> (sprintf "%s==" >> Convert.FromBase64String >> Guid >> ShortGuid) - /// Create a new short GUID - let create () = (Guid.NewGuid >> ShortGuid) () - /// The empty short GUID - let empty = ShortGuid Guid.Empty - - -/// The ID of a category -type CategoryId = CategoryId of ShortGuid - -/// Functions to support category IDs -module CategoryId = - /// Get the string representation of a page ID - let toString = function CategoryId it -> ShortGuid.toString it - /// Create a category ID from its string representation - let ofString = ShortGuid.ofString >> CategoryId - /// An empty category ID - let empty = CategoryId ShortGuid.empty - - -/// The ID of a comment -type CommentId = CommentId of ShortGuid - -/// Functions to support comment IDs -module CommentId = - /// Get the string representation of a comment ID - let toString = function CommentId it -> ShortGuid.toString it - /// Create a comment ID from its string representation - let ofString = ShortGuid.ofString >> CommentId - /// An empty comment ID - let empty = CommentId ShortGuid.empty - - -/// The ID of a page -type PageId = PageId of ShortGuid - -/// Functions to support page IDs -module PageId = - /// Get the string representation of a page ID - let toString = function PageId it -> ShortGuid.toString it - /// Create a page ID from its string representation - let ofString = ShortGuid.ofString >> PageId - /// An empty page ID - let empty = PageId ShortGuid.empty - - -/// The ID of a post -type PostId = PostId of ShortGuid - -/// Functions to support post IDs -module PostId = - /// Get the string representation of a post ID - let toString = function PostId it -> ShortGuid.toString it - /// Create a post ID from its string representation - let ofString = ShortGuid.ofString >> PostId - /// An empty post ID - let empty = PostId ShortGuid.empty - - -/// The ID of a user -type UserId = UserId of ShortGuid - -/// Functions to support user IDs -module UserId = - /// Get the string representation of a user ID - let toString = function UserId it -> ShortGuid.toString it - /// Create a user ID from its string representation - let ofString = ShortGuid.ofString >> UserId - /// An empty user ID - let empty = UserId ShortGuid.empty - - -/// The ID of a web log -type WebLogId = WebLogId of ShortGuid - -/// Functions to support web log IDs -module WebLogId = - /// Get the string representation of a web log ID - let toString = function WebLogId it -> ShortGuid.toString it - /// Create a web log ID from its string representation - let ofString = ShortGuid.ofString >> WebLogId - /// An empty web log ID - let empty = WebLogId ShortGuid.empty - - -// -- Domain Entities -- -// fsharplint:disable RecordFieldNames - -/// A revision of a post or page -type Revision = { - /// The instant which this revision was saved - asOf : UnixSeconds - /// The text - text : MarkupText - } -with - /// An empty revision - static member empty = - { asOf = UnixSeconds.none - text = Markdown "" - } - - -/// A page with static content -[] -type Page = { - /// The Id - id : PageId - /// The Id of the web log to which this page belongs - webLogId : WebLogId - /// The Id of the author of this page - authorId : UserId - /// The title of the page - title : string - /// The link at which this page is displayed - permalink : string - /// The instant this page was published - publishedOn : UnixSeconds - /// The instant this page was last updated - updatedOn : UnixSeconds - /// Whether this page shows as part of the web log's navigation - showInPageList : bool - /// The current text of the page - text : MarkupText - /// Revisions of this page - revisions : Revision list - } -with - static member empty = - { id = PageId.empty - webLogId = WebLogId.empty - authorId = UserId.empty - title = "" - permalink = "" - publishedOn = UnixSeconds.none - updatedOn = UnixSeconds.none - showInPageList = false - text = Markdown "" - revisions = [] - } - - -/// An entry in the list of pages displayed as part of the web log (derived via query) -type PageListEntry = { - /// The permanent link for the page - permalink : string - /// The title of the page - title : string - } - - -/// A web log -[] -type WebLog = { - /// The Id - id : WebLogId - /// The name - name : string - /// The subtitle - subtitle : string option - /// The default page ("posts" or a page Id) - defaultPage : string - /// The path of the theme (within /views/themes) - themePath : string - /// The URL base - urlBase : string - /// The time zone in which dates/times should be displayed - timeZone : string - /// A list of pages to be rendered as part of the site navigation (not stored) - pageList : PageListEntry list - } -with - /// An empty web log - static member empty = - { id = WebLogId.empty - name = "" - subtitle = None - defaultPage = "" - themePath = "default" - urlBase = "" - timeZone = "America/New_York" - pageList = [] - } - - -/// An authorization between a user and a web log -type Authorization = { - /// The Id of the web log to which this authorization grants access - webLogId : WebLogId - /// The level of access granted by this authorization - level : AuthorizationLevel -} - - -/// A user of myWebLog -[] -type User = { - /// The Id - id : UserId - /// The user name (e-mail address) - userName : string - /// The first name - firstName : string - /// The last name - lastName : string - /// The user's preferred name - preferredName : string - /// The hash of the user's password - passwordHash : string - /// The URL of the user's personal site - url : string option - /// The user's authorizations - authorizations : Authorization list - } -with - /// An empty user - static member empty = - { id = UserId.empty - userName = "" - firstName = "" - lastName = "" - preferredName = "" - passwordHash = "" - url = None - authorizations = [] - } - -/// Functions supporting users -module User = - /// Claims for this user - let claims user = - user.authorizations - |> List.map (fun a -> sprintf "%s|%s" (WebLogId.toString a.webLogId) (AuthorizationLevel.toString a.level)) - - -/// A category to which posts may be assigned -[] -type Category = { - /// The Id - id : CategoryId - /// The Id of the web log to which this category belongs - webLogId : WebLogId - /// The displayed name - name : string - /// The slug (used in category URLs) - slug : string - /// A longer description of the category - description : string option - /// The parent Id of this category (if a subcategory) - parentId : CategoryId option - /// The categories for which this category is the parent - children : CategoryId list - } -with - /// An empty category - static member empty = - { id = CategoryId.empty - webLogId = WebLogId.empty - name = "" - slug = "" - description = None - parentId = None - children = [] - } - - -/// A comment (applies to a post) -[] -type Comment = { - /// The Id - id : CommentId - /// The Id of the post to which this comment applies - postId : PostId - /// The Id of the comment to which this comment is a reply - inReplyToId : CommentId option - /// The name of the commentor - name : string - /// The e-mail address of the commentor - email : string - /// The URL of the commentor's personal website - url : string option - /// The status of the comment - status : CommentStatus - /// The instant the comment was posted - postedOn : UnixSeconds - /// The text of the comment - text : string - } -with - static member empty = - { id = CommentId.empty - postId = PostId.empty - inReplyToId = None - name = "" - email = "" - url = None - status = Pending - postedOn = UnixSeconds.none - text = "" - } - - -/// A post -[] -type Post = { - /// The Id - id : PostId - /// The Id of the web log to which this post belongs - webLogId : WebLogId - /// The Id of the author of this post - authorId : UserId - /// The status - status : PostStatus - /// The title - title : string - /// The link at which the post resides - permalink : string - /// The instant on which the post was originally published - publishedOn : UnixSeconds - /// The instant on which the post was last updated - updatedOn : UnixSeconds - /// The text of the post - text : MarkupText - /// The Ids of the categories to which this is assigned - categoryIds : CategoryId list - /// The tags for the post - tags : string list - /// The permalinks at which this post may have once resided - priorPermalinks : string list - /// Revisions of this post - revisions : Revision list - /// The categories to which this is assigned (not stored in database) - categories : Category list - /// The comments (not stored in database) - comments : Comment list - } -with - static member empty = - { id = PostId.empty - webLogId = WebLogId.empty - authorId = UserId.empty - status = Draft - title = "" - permalink = "" - publishedOn = UnixSeconds.none - updatedOn = UnixSeconds.none - text = Markdown "" - categoryIds = [] - tags = [] - priorPermalinks = [] - revisions = [] - categories = [] - comments = [] - } - - -// --- UI Support --- - -/// Counts of items displayed on the admin dashboard -type DashboardCounts = { - /// The number of pages for the web log - pages : int - /// The number of pages for the web log - posts : int - /// The number of categories for the web log - categories : int - } diff --git a/src/MyWebLog.v2/MyWebLog.fsproj b/src/MyWebLog.v2/MyWebLog.fsproj deleted file mode 100644 index e11ac96..0000000 --- a/src/MyWebLog.v2/MyWebLog.fsproj +++ /dev/null @@ -1,28 +0,0 @@ - - - - Exe - net6.0 - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/MyWebLog.v2/Program.fs b/src/MyWebLog.v2/Program.fs deleted file mode 100644 index 139a10e..0000000 --- a/src/MyWebLog.v2/Program.fs +++ /dev/null @@ -1,4 +0,0 @@ -open MyWebLog -open Suave - -startWebServer defaultConfig (Successful.OK (Strings.get "LastUpdated")) diff --git a/src/MyWebLog.v2/Resources/en-US.json b/src/MyWebLog.v2/Resources/en-US.json deleted file mode 100644 index be2715a..0000000 --- a/src/MyWebLog.v2/Resources/en-US.json +++ /dev/null @@ -1,83 +0,0 @@ -{ - "Action": "Action", - "Added": "Added", - "AddNew": "Add New", - "AddNewCategory": "Add New Category", - "AddNewPage": "Add New Page", - "AddNewPost": "Add New Post", - "Admin": "Admin", - "AndPublished": " and Published", - "andXMore": "and {0} more...", - "at": "at", - "BackToCategoryList": "Back to Category List", - "BackToPageList": "Back to Page List", - "BackToPostList": "Back to Post List", - "Categories": "Categories", - "Category": "Category", - "CategoryDeleteWarning": "Are you sure you wish to delete the category", - "Close": "Close", - "Comments": "Comments", - "Dashboard": "Dashboard", - "Date": "Date", - "Delete": "Delete", - "Description": "Description", - "Edit": "Edit", - "EditCategory": "Edit Category", - "EditPage": "Edit Page", - "EditPost": "Edit Post", - "EmailAddress": "E-mail Address", - "ErrBadAppConfig": "Could not convert config.json to myWebLog configuration", - "ErrBadLogOnAttempt": "Invalid e-mail address or password", - "ErrDataConfig": "Could not convert data-config.json to RethinkDB connection", - "ErrNotConfigured": "is not properly configured for myWebLog", - "Error": "Error", - "LastUpdated": "Last Updated", - "LastUpdatedDate": "Last Updated Date", - "ListAll": "List All", - "LoadedIn": "Loaded in", - "LogOff": "Log Off", - "LogOn": "Log On", - "MsgCategoryDeleted": "Deleted category {0} successfully", - "MsgCategoryEditSuccess": "{0} category successfully", - "MsgLogOffSuccess": "Log off successful | Have a nice day!", - "MsgLogOnSuccess": "Log on successful | Welcome to myWebLog!", - "MsgPageDeleted": "Deleted page successfully", - "MsgPageEditSuccess": "{0} page successfully", - "MsgPostEditSuccess": "{0}{1} post successfully", - "Name": "Name", - "NewerPosts": "Newer Posts", - "NextPost": "Next Post", - "NoComments": "No Comments", - "NoParent": "No Parent", - "OlderPosts": "Older Posts", - "OneComment": "1 Comment", - "PageDeleteWarning": "Are you sure you wish to delete the page", - "PageDetails": "Page Details", - "PageHash": "Page #", - "Pages": "Pages", - "ParentCategory": "Parent Category", - "Password": "Password", - "Permalink": "Permalink", - "PermanentLinkTo": "Permanent Link to", - "PostDetails": "Post Details", - "Posts": "Posts", - "PostsTagged": "Posts Tagged", - "PostStatus": "Post Status", - "PoweredBy": "Powered by", - "PreviousPost": "Previous Post", - "PublishedDate": "Published Date", - "PublishThisPost": "Publish This Post", - "Save": "Save", - "Seconds": "Seconds", - "ShowInPageList": "Show in Page List", - "Slug": "Slug", - "startingWith": "starting with", - "Status": "Status", - "Tags": "Tags", - "Time": "Time", - "Title": "Title", - "Updated": "Updated", - "View": "View", - "Warning": "Warning", - "XComments": "{0} Comments" -} diff --git a/src/MyWebLog.v2/Strings.fs b/src/MyWebLog.v2/Strings.fs deleted file mode 100644 index 55a725b..0000000 --- a/src/MyWebLog.v2/Strings.fs +++ /dev/null @@ -1,40 +0,0 @@ -module MyWebLog.Strings - -open System.Collections.Generic -open System.Globalization -open System.IO -open System.Reflection -open System.Text.Json - -/// The locales we'll try to load -let private supportedLocales = [ "en-US" ] - -/// The fallback locale, if a key is not found in a non-default locale -let private fallbackLocale = "en-US" - -/// Get an embedded JSON file as a string -let private getEmbedded locale = - let str = sprintf "MyWebLog.Resources.%s.json" locale |> Assembly.GetExecutingAssembly().GetManifestResourceStream - use rdr = new StreamReader (str) - rdr.ReadToEnd() - -/// The dictionary of localized strings -let private strings = - supportedLocales - |> List.map (fun loc -> loc, getEmbedded loc |> JsonSerializer.Deserialize>) - |> dict - -/// Get a key from the resources file for the given locale -let getForLocale locale key = - let getString thisLocale = - match strings.ContainsKey thisLocale && strings.[thisLocale].ContainsKey key with - | true -> Some strings.[thisLocale].[key] - | false -> None - match getString locale with - | Some xlat -> Some xlat - | None when locale <> fallbackLocale -> getString fallbackLocale - | None -> None - |> function Some xlat -> xlat | None -> sprintf "%s.%s" locale key - -/// Translate the key for the current locale -let get key = getForLocale CultureInfo.CurrentCulture.Name key diff --git a/src/MyWebLog/Handlers.fs b/src/MyWebLog/Handlers.fs index 86bc180..ecf802e 100644 --- a/src/MyWebLog/Handlers.fs +++ b/src/MyWebLog/Handlers.fs @@ -58,6 +58,7 @@ open System.Collections.Generic module private Helpers = open Microsoft.AspNetCore.Antiforgery + open Microsoft.Extensions.Configuration open Microsoft.Extensions.DependencyInjection open System.Security.Claims open System.IO @@ -93,7 +94,17 @@ module private Helpers = return msg |> (List.rev >> Array.ofList) | None -> return [||] } - + + /// Hold variable for the configured generator string + let mutable private generatorString : string option = None + + /// Get the generator string + let private generator (ctx : HttpContext) = + if Option.isNone generatorString then + let cfg = ctx.RequestServices.GetRequiredService () + generatorString <- Option.ofObj cfg["Generator"] + match generatorString with Some gen -> gen | None -> "generator not configured" + /// Either get the web log from the hash, or get it from the cache and add it to the hash let private deriveWebLogFromHash (hash : Hash) ctx = match hash.ContainsKey "web_log" with @@ -112,6 +123,7 @@ module private Helpers = hash.Add ("page_list", PageListCache.get ctx) hash.Add ("current_page", ctx.Request.Path.Value.Substring 1) hash.Add ("messages", messages) + hash.Add ("generator", generator ctx) do! commitSession ctx @@ -343,7 +355,7 @@ module Page = let! pages = Data.Page.findPageOfPages webLog.id pageNbr (conn ctx) return! Hash.FromAnonymousObject - {| pages = pages |> List.map (DisplayPage.fromPage webLog) + {| pages = pages |> List.map (DisplayPage.fromPageMinimal webLog) page_title = "Pages" |} |> viewForTheme "admin" "page-list" next ctx @@ -414,6 +426,7 @@ module Page = metadata = Seq.zip model.metaNames model.metaValues |> Seq.filter (fun it -> fst it > "") |> Seq.map (fun it -> { name = fst it; value = snd it }) + |> Seq.sortBy (fun it -> $"{it.name.ToLower ()} {it.value.ToLower ()}") |> List.ofSeq revisions = revision :: page.revisions } @@ -482,7 +495,7 @@ module Post = match! Data.Page.findById (PageId pageId) webLog.id (conn ctx) with | Some page -> return! - Hash.FromAnonymousObject {| page = page; page_title = page.title |} + Hash.FromAnonymousObject {| page = DisplayPage.fromPage webLog page; page_title = page.title |} |> themedView (defaultArg page.template "single-page") next ctx | None -> return! Error.notFound next ctx } @@ -501,7 +514,7 @@ module Post = match! Data.Page.findByPermalink permalink webLog.id conn with | Some page -> return! - Hash.FromAnonymousObject {| page = page; page_title = page.title |} + Hash.FromAnonymousObject {| page = DisplayPage.fromPage webLog page; page_title = page.title |} |> themedView (defaultArg page.template "single-page") next ctx | None -> // Prior post diff --git a/src/MyWebLog/appsettings.json b/src/MyWebLog/appsettings.json index a636102..a62aa93 100644 --- a/src/MyWebLog/appsettings.json +++ b/src/MyWebLog/appsettings.json @@ -8,5 +8,6 @@ "RethinkDB.DistributedCache": "Debug", "RethinkDb.Driver": "Debug" } - } + }, + "Generator": "myWebLog 2.0-alpha01" } diff --git a/src/MyWebLog/themes/admin/layout.liquid b/src/MyWebLog/themes/admin/layout.liquid index 2e043d9..736ad76 100644 --- a/src/MyWebLog/themes/admin/layout.liquid +++ b/src/MyWebLog/themes/admin/layout.liquid @@ -1,7 +1,8 @@ - + + {{ page_title | escape }} « Admin « {{ web_log.name | escape }} diff --git a/src/MyWebLog/themes/admin/page-edit.liquid b/src/MyWebLog/themes/admin/page-edit.liquid index c94e7e3..3cc3cef 100644 --- a/src/MyWebLog/themes/admin/page-edit.liquid +++ b/src/MyWebLog/themes/admin/page-edit.liquid @@ -21,7 +21,7 @@