V2 #1
|
@ -3,7 +3,7 @@ module MyWebLog.Data
|
||||||
|
|
||||||
/// Table names
|
/// Table names
|
||||||
[<RequireQualifiedAccess>]
|
[<RequireQualifiedAccess>]
|
||||||
module private Table =
|
module Table =
|
||||||
|
|
||||||
/// The category table
|
/// The category table
|
||||||
let Category = "Category"
|
let Category = "Category"
|
||||||
|
|
|
@ -264,6 +264,11 @@ module WebLog =
|
||||||
|
|
||||||
/// Convert a permalink to an absolute URL
|
/// Convert a permalink to an absolute URL
|
||||||
let absoluteUrl webLog = function Permalink link -> $"{webLog.urlBase}{link}"
|
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
|
/// A user of the web log
|
||||||
|
|
|
@ -232,7 +232,7 @@ type EditPostModel =
|
||||||
setUpdated : bool
|
setUpdated : bool
|
||||||
}
|
}
|
||||||
/// Create an edit model from an existing past
|
/// Create an edit model from an existing past
|
||||||
static member fromPost (post : Post) =
|
static member fromPost webLog (post : Post) =
|
||||||
let latest =
|
let latest =
|
||||||
match post.revisions |> List.sortByDescending (fun r -> r.asOf) |> List.tryHead with
|
match post.revisions |> List.sortByDescending (fun r -> r.asOf) |> List.tryHead with
|
||||||
| Some rev -> rev
|
| Some rev -> rev
|
||||||
|
@ -250,7 +250,7 @@ type EditPostModel =
|
||||||
metaNames = post.metadata |> List.map (fun m -> m.name) |> Array.ofList
|
metaNames = post.metadata |> List.map (fun m -> m.name) |> Array.ofList
|
||||||
metaValues = post.metadata |> List.map (fun m -> m.value) |> Array.ofList
|
metaValues = post.metadata |> List.map (fun m -> m.value) |> Array.ofList
|
||||||
setPublished = false
|
setPublished = false
|
||||||
pubOverride = Nullable<DateTime> ()
|
pubOverride = post.publishedOn |> Option.map (WebLog.localTime webLog) |> Option.toNullable
|
||||||
setUpdated = false
|
setUpdated = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -338,8 +338,7 @@ type PostListItem =
|
||||||
|
|
||||||
/// Create a post list item from a post
|
/// Create a post list item from a post
|
||||||
static member fromPost (webLog : WebLog) (post : Post) =
|
static member fromPost (webLog : WebLog) (post : Post) =
|
||||||
let tz = TimeZoneInfo.FindSystemTimeZoneById webLog.timeZone
|
let inTZ = WebLog.localTime webLog
|
||||||
let inTZ (it : DateTime) = TimeZoneInfo.ConvertTimeFromUtc (DateTime (it.Ticks, DateTimeKind.Utc), tz)
|
|
||||||
{ id = PostId.toString post.id
|
{ id = PostId.toString post.id
|
||||||
authorId = WebLogUserId.toString post.authorId
|
authorId = WebLogUserId.toString post.authorId
|
||||||
status = PostStatus.toString post.status
|
status = PostStatus.toString post.status
|
||||||
|
|
|
@ -716,23 +716,23 @@ module Post =
|
||||||
|
|
||||||
// GET /post/{id}/edit
|
// GET /post/{id}/edit
|
||||||
let edit postId : HttpHandler = requireUser >=> fun next ctx -> task {
|
let edit postId : HttpHandler = requireUser >=> fun next ctx -> task {
|
||||||
let webLogId = webLogId ctx
|
let webLog = WebLogCache.get ctx
|
||||||
let conn = conn ctx
|
let conn = conn ctx
|
||||||
let! result = task {
|
let! result = task {
|
||||||
match postId with
|
match postId with
|
||||||
| "new" -> return Some ("Write a New Post", { Post.empty with id = PostId "new" })
|
| "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)
|
| Some post -> return Some ("Edit Post", post)
|
||||||
| None -> return None
|
| None -> return None
|
||||||
}
|
}
|
||||||
match result with
|
match result with
|
||||||
| Some (title, post) ->
|
| Some (title, post) ->
|
||||||
let! cats = Data.Category.findAllForView webLogId conn
|
let! cats = Data.Category.findAllForView webLog.id conn
|
||||||
return!
|
return!
|
||||||
Hash.FromAnonymousObject {|
|
Hash.FromAnonymousObject {|
|
||||||
csrf = csrfToken ctx
|
csrf = csrfToken ctx
|
||||||
model = EditPostModel.fromPost post
|
model = EditPostModel.fromPost webLog post
|
||||||
page_title = title
|
page_title = title
|
||||||
categories = cats
|
categories = cats
|
||||||
|}
|
|}
|
||||||
|
|
|
@ -72,6 +72,9 @@ module DotLiquidBespoke =
|
||||||
/// Create the default information for a new web log
|
/// Create the default information for a new web log
|
||||||
module NewWebLog =
|
module NewWebLog =
|
||||||
|
|
||||||
|
open System.IO
|
||||||
|
open RethinkDb.Driver.FSharp
|
||||||
|
|
||||||
/// Create the web log information
|
/// Create the web log information
|
||||||
let private createWebLog (args : string[]) (sp : IServiceProvider) = task {
|
let private createWebLog (args : string[]) (sp : IServiceProvider) = task {
|
||||||
|
|
||||||
|
@ -134,7 +137,7 @@ module NewWebLog =
|
||||||
]
|
]
|
||||||
} conn
|
} 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
|
/// Create a new web log
|
||||||
|
@ -142,7 +145,50 @@ module NewWebLog =
|
||||||
match args |> Array.length with
|
match args |> Array.length with
|
||||||
| 5 -> return! createWebLog args sp
|
| 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<IConnection> ()
|
||||||
|
|
||||||
|
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<Post> {
|
||||||
|
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
|
return! System.Threading.Tasks.Task.CompletedTask
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -219,7 +265,10 @@ let main args =
|
||||||
let app = builder.Build ()
|
let app = builder.Build ()
|
||||||
|
|
||||||
match args |> Array.tryHead with
|
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.UseCookiePolicy (CookiePolicyOptions (MinimumSameSitePolicy = SameSiteMode.Strict))
|
||||||
let _ = app.UseMiddleware<WebLogMiddleware> ()
|
let _ = app.UseMiddleware<WebLogMiddleware> ()
|
||||||
|
|
|
@ -97,7 +97,10 @@
|
||||||
<div class="col-4">
|
<div class="col-4">
|
||||||
<div class="form-floating">
|
<div class="form-floating">
|
||||||
<input type="datetime-local" name="pubOverride" id="pubOverride" class="form-control"
|
<input type="datetime-local" name="pubOverride" id="pubOverride" class="form-control"
|
||||||
placeholder="Override Date">
|
placeholder="Override Date"
|
||||||
|
{%- if model.pub_override -%}
|
||||||
|
value="{{ model.pub_override | date: "yyyy-MM-dd\THH:mm" }}"
|
||||||
|
{%- endif %}>
|
||||||
<label for="pubOverride" class="form-label">Published On</label>
|
<label for="pubOverride" class="form-label">Published On</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -5,7 +5,13 @@
|
||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
<meta name="generator" content="{{ generator }}">
|
<meta name="generator" content="{{ generator }}">
|
||||||
<title>{{ page_title | strip_html }}{% if page_title and page_title != "" %} » {% endif %}{{ web_log.name }}</title>
|
<title>
|
||||||
|
{%- 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 %}
|
||||||
|
</title>
|
||||||
<link rel="preload" href="https://fonts.googleapis.com/css?family=Quicksand|Oswald" as="style">
|
<link rel="preload" href="https://fonts.googleapis.com/css?family=Quicksand|Oswald" as="style">
|
||||||
<link rel="preload" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.3.0/css/font-awesome.min.css" as="style">
|
<link rel="preload" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.3.0/css/font-awesome.min.css" as="style">
|
||||||
<link rel="preload" href="/themes/{{ web_log.theme_path }}/style.css" as="style">
|
<link rel="preload" href="/themes/{{ web_log.theme_path }}/style.css" as="style">
|
||||||
|
@ -79,9 +85,9 @@
|
||||||
<td class="text-center" style="line-height:1.3em;">
|
<td class="text-center" style="line-height:1.3em;">
|
||||||
2021 Season — <strong>NR</strong><br>
|
2021 Season — <strong>NR</strong><br>
|
||||||
<small>
|
<small>
|
||||||
(5-5 • 3-4 SEC/3<sup>rd</sup> East)<br><br>
|
(7-6 • 4-4 SEC/3<sup>rd</sup> East)<br><br>
|
||||||
Last — L (17-41) vs. <sub>1</sub> Georgia<br>
|
Last — L* (45-48) vs. Purdue<br>
|
||||||
Next — 11/20 vs. South Alabama
|
<em>Music City Bowl</em>
|
||||||
</small>
|
</small>
|
||||||
</td>
|
</td>
|
||||||
</tr></tbody></table>
|
</tr></tbody></table>
|
||||||
|
@ -100,9 +106,8 @@
|
||||||
<td class="text-center" style="line-height:1.3em;">
|
<td class="text-center" style="line-height:1.3em;">
|
||||||
2021 Season — <strong>NR</strong><br>
|
2021 Season — <strong>NR</strong><br>
|
||||||
<small>
|
<small>
|
||||||
(3-6 • 2-3 MWC/4<sup>th</sup> Mountain)<br><br>
|
(3-9 • 2-6 MWC/5<sup>th</sup> Mountain)<br><br>
|
||||||
Last — L (17-31) at Wyoming<br>
|
Last — L (10-52) at Nevada
|
||||||
Next — 11/13 vs. Air Force
|
|
||||||
</small>
|
</small>
|
||||||
</td>
|
</td>
|
||||||
</tr></tbody></table>
|
</tr></tbody></table>
|
||||||
|
|
Loading…
Reference in New Issue
Block a user