diff --git a/src/MyWebLog.Data/Data.fs b/src/MyWebLog.Data/Data.fs index 47eab12..b2f2637 100644 --- a/src/MyWebLog.Data/Data.fs +++ b/src/MyWebLog.Data/Data.fs @@ -3,7 +3,7 @@ module MyWebLog.Data /// Table names [] -module private Table = +module Table = /// The category table let Category = "Category" diff --git a/src/MyWebLog.Domain/DataTypes.fs b/src/MyWebLog.Domain/DataTypes.fs index 89147f2..2f0b6c1 100644 --- a/src/MyWebLog.Domain/DataTypes.fs +++ b/src/MyWebLog.Domain/DataTypes.fs @@ -264,6 +264,11 @@ module WebLog = /// Convert a permalink to an absolute URL let absoluteUrl webLog = function Permalink link -> $"{webLog.urlBase}{link}" + + /// Convert a date/time to the web log's local date/time + let localTime webLog (date : DateTime) = + let tz = TimeZoneInfo.FindSystemTimeZoneById webLog.timeZone + TimeZoneInfo.ConvertTimeFromUtc (DateTime (date.Ticks, DateTimeKind.Utc), tz) /// A user of the web log diff --git a/src/MyWebLog.Domain/ViewModels.fs b/src/MyWebLog.Domain/ViewModels.fs index ab2c521..18b7cfe 100644 --- a/src/MyWebLog.Domain/ViewModels.fs +++ b/src/MyWebLog.Domain/ViewModels.fs @@ -232,7 +232,7 @@ type EditPostModel = setUpdated : bool } /// Create an edit model from an existing past - static member fromPost (post : Post) = + static member fromPost webLog (post : Post) = let latest = match post.revisions |> List.sortByDescending (fun r -> r.asOf) |> List.tryHead with | Some rev -> rev @@ -250,7 +250,7 @@ type EditPostModel = metaNames = post.metadata |> List.map (fun m -> m.name) |> Array.ofList metaValues = post.metadata |> List.map (fun m -> m.value) |> Array.ofList setPublished = false - pubOverride = Nullable () + pubOverride = post.publishedOn |> Option.map (WebLog.localTime webLog) |> Option.toNullable setUpdated = false } @@ -338,8 +338,7 @@ type PostListItem = /// Create a post list item from a post static member fromPost (webLog : WebLog) (post : Post) = - let tz = TimeZoneInfo.FindSystemTimeZoneById webLog.timeZone - let inTZ (it : DateTime) = TimeZoneInfo.ConvertTimeFromUtc (DateTime (it.Ticks, DateTimeKind.Utc), tz) + let inTZ = WebLog.localTime webLog { id = PostId.toString post.id authorId = WebLogUserId.toString post.authorId status = PostStatus.toString post.status diff --git a/src/MyWebLog/Handlers.fs b/src/MyWebLog/Handlers.fs index 08eb4b2..0039de6 100644 --- a/src/MyWebLog/Handlers.fs +++ b/src/MyWebLog/Handlers.fs @@ -716,23 +716,23 @@ module Post = // GET /post/{id}/edit let edit postId : HttpHandler = requireUser >=> fun next ctx -> task { - let webLogId = webLogId ctx - let conn = conn ctx - let! result = task { + let webLog = WebLogCache.get ctx + let conn = conn ctx + let! result = task { match postId with | "new" -> return Some ("Write a New Post", { Post.empty with id = PostId "new" }) | _ -> - match! Data.Post.findByFullId (PostId postId) webLogId conn with + match! Data.Post.findByFullId (PostId postId) webLog.id conn with | Some post -> return Some ("Edit Post", post) | None -> return None } match result with | Some (title, post) -> - let! cats = Data.Category.findAllForView webLogId conn + let! cats = Data.Category.findAllForView webLog.id conn return! Hash.FromAnonymousObject {| csrf = csrfToken ctx - model = EditPostModel.fromPost post + model = EditPostModel.fromPost webLog post page_title = title categories = cats |} diff --git a/src/MyWebLog/Program.fs b/src/MyWebLog/Program.fs index ca515e6..904b5ea 100644 --- a/src/MyWebLog/Program.fs +++ b/src/MyWebLog/Program.fs @@ -72,6 +72,9 @@ module DotLiquidBespoke = /// Create the default information for a new web log module NewWebLog = + open System.IO + open RethinkDb.Driver.FSharp + /// Create the web log information let private createWebLog (args : string[]) (sp : IServiceProvider) = task { @@ -134,7 +137,7 @@ module NewWebLog = ] } conn - Console.WriteLine($"Successfully initialized database for {args[2]} with URL base {args[1]}"); + printfn $"Successfully initialized database for {args[2]} with URL base {args[1]}" } /// Create a new web log @@ -142,7 +145,50 @@ module NewWebLog = match args |> Array.length with | 5 -> return! createWebLog args sp | _ -> - Console.WriteLine "Usage: MyWebLog init [url] [name] [admin-email] [admin-pw]" + printfn "Usage: MyWebLog init [url] [name] [admin-email] [admin-pw]" + return! System.Threading.Tasks.Task.CompletedTask + } + + /// Import prior permalinks from a text files with lines in the format "[old] [new]" + let importPriorPermalinks urlBase file (sp : IServiceProvider) = task { + let conn = sp.GetRequiredService () + + match! Data.WebLog.findByHost urlBase conn with + | Some webLog -> + + let mapping = + File.ReadAllLines file + |> Seq.ofArray + |> Seq.map (fun it -> + let parts = it.Split " " + Permalink parts[0], Permalink parts[1]) + + for old, current in mapping do + match! Data.Post.findByPermalink current webLog.id conn with + | Some post -> + let! withLinks = rethink { + withTable Data.Table.Post + get post.id + result conn + } + do! rethink { + withTable Data.Table.Post + get post.id + update [ "priorPermalinks", old :: withLinks.priorPermalinks :> obj] + write; ignoreResult conn + } + printfn $"{Permalink.toString old} -> {Permalink.toString current}" + | None -> printfn $"Cannot find current post for {Permalink.toString current}" + printfn "Done!" + | None -> printfn $"No web log found at {urlBase}" + } + + /// Import permalinks if all is well + let importPermalinks args sp = task { + match args |> Array.length with + | 3 -> return! importPriorPermalinks args[1] args[2] sp + | _ -> + printfn "Usage: MyWebLog import-permalinks [url] [file-name]" return! System.Threading.Tasks.Task.CompletedTask } @@ -219,7 +265,10 @@ let main args = let app = builder.Build () match args |> Array.tryHead with - | Some it when it = "init" -> NewWebLog.create args app.Services |> Async.AwaitTask |> Async.RunSynchronously + | Some it when it = "init" -> + NewWebLog.create args app.Services |> Async.AwaitTask |> Async.RunSynchronously + | Some it when it = "import-permalinks" -> + NewWebLog.importPermalinks args app.Services |> Async.AwaitTask |> Async.RunSynchronously | _ -> let _ = app.UseCookiePolicy (CookiePolicyOptions (MinimumSameSitePolicy = SameSiteMode.Strict)) let _ = app.UseMiddleware () diff --git a/src/MyWebLog/themes/admin/post-edit.liquid b/src/MyWebLog/themes/admin/post-edit.liquid index bfb1097..ea7eac5 100644 --- a/src/MyWebLog/themes/admin/post-edit.liquid +++ b/src/MyWebLog/themes/admin/post-edit.liquid @@ -97,7 +97,10 @@
+ placeholder="Override Date" + {%- if model.pub_override -%} + value="{{ model.pub_override | date: "yyyy-MM-dd\THH:mm" }}" + {%- endif %}>
diff --git a/src/MyWebLog/themes/daniel-j-summers/layout.liquid b/src/MyWebLog/themes/daniel-j-summers/layout.liquid index e6a6b43..0af2e21 100644 --- a/src/MyWebLog/themes/daniel-j-summers/layout.liquid +++ b/src/MyWebLog/themes/daniel-j-summers/layout.liquid @@ -5,7 +5,13 @@ - {{ page_title | strip_html }}{% if page_title and page_title != "" %} » {% endif %}{{ web_log.name }} + + {%- if is_home %} + {{ web_log.name }}{% if web_log.subtitle %} | {{ web_log.subtitle.value }}{% endif %} + {%- else %} + {{ page_title | strip_html }}{% if page_title and page_title != "" %} » {% endif %}{{ web_log.name }} + {%- endif %} + @@ -79,9 +85,9 @@ 2021 Season — NR
- (5-5 • 3-4 SEC/3rd East)

- Last — L (17-41) vs. 1 Georgia
- Next — 11/20 vs. South Alabama + (7-6 • 4-4 SEC/3rd East)

+ Last — L* (45-48) vs. Purdue
+ Music City Bowl
@@ -100,9 +106,8 @@ 2021 Season — NR
- (3-6 • 2-3 MWC/4th Mountain)

- Last — L (17-31) at Wyoming
- Next — 11/13 vs. Air Force + (3-9 • 2-6 MWC/5th Mountain)

+ Last — L (10-52) at Nevada