Add permalink import
- Fill publish date on post edit page
This commit is contained in:
		
							parent
							
								
									afca5edfdd
								
							
						
					
					
						commit
						c07f1b11c9
					
				| @ -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" | ||||||
|  | |||||||
| @ -265,6 +265,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 | ||||||
| [<CLIMutable; NoComparison; NoEquality>] | [<CLIMutable; NoComparison; NoEquality>] | ||||||
|  | |||||||
| @ -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…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user