From 739fe3ff9c80615c1af07b3b52f526b01f8d2160 Mon Sep 17 00:00:00 2001 From: "Daniel J. Summers" Date: Fri, 11 Nov 2016 22:44:23 -0600 Subject: [PATCH] So close... - Added comment display and counts - Fixed problem with set-up-from-scratch bombing on index creation - RSS feed is returning errors; need to determine why Nginx is intercepting theme content requests --- src/MyWebLog.App/App.fs | 3 +- src/MyWebLog.App/CategoryModule.fs | 47 ++++++++++--------- src/MyWebLog.App/UserModule.fs | 13 +++-- src/MyWebLog.App/ViewModels.fs | 32 ++++++++++++- src/MyWebLog.Data.RethinkDB/Post.fs | 11 ++++- src/MyWebLog.Data.RethinkDB/SetUp.fs | 1 - src/MyWebLog.Resources/en-US.json | 8 +++- .../views/admin/user/log-on.html} | 2 +- .../views/themes/default/comment.html | 4 ++ src/myWebLog/views/admin/admin-layout.html | 4 +- src/myWebLog/views/admin/category/list.html | 2 +- .../views/themes/default/index-content.html | 3 +- src/myWebLog/views/themes/default/layout.html | 6 +-- .../views/themes/default/single-content.html | 24 ++++------ 14 files changed, 102 insertions(+), 58 deletions(-) rename src/{myWebLog/views/admin/user/logon.html => MyWebLog/views/admin/user/log-on.html} (96%) create mode 100644 src/MyWebLog/views/themes/default/comment.html diff --git a/src/MyWebLog.App/App.fs b/src/MyWebLog.App/App.fs index 5eba1cc..4a51d6f 100644 --- a/src/MyWebLog.App/App.fs +++ b/src/MyWebLog.App/App.fs @@ -112,7 +112,7 @@ type MyWebLogBootstrapper() = CryptographyConfiguration ( AesEncryptionProvider (PassphraseKeyGenerator (cfg.AuthEncryptionPassphrase, cfg.AuthSalt)), DefaultHmacProvider (PassphraseKeyGenerator (cfg.AuthHmacPassphrase, cfg.AuthSalt))), - RedirectUrl = "~/user/logon", + RedirectUrl = "~/user/log-on", UserMapper = container.Resolve ()) FormsAuthentication.Enable (pipelines, auth) // CSRF @@ -163,6 +163,7 @@ let Run () = use host = WebHostBuilder() .UseContentRoot(System.IO.Directory.GetCurrentDirectory ()) + .UseUrls("http://localhost:5001") .UseKestrel() .UseStartup() .Build () diff --git a/src/MyWebLog.App/CategoryModule.fs b/src/MyWebLog.App/CategoryModule.fs index 05197fb..1d752eb 100644 --- a/src/MyWebLog.App/CategoryModule.fs +++ b/src/MyWebLog.App/CategoryModule.fs @@ -14,10 +14,10 @@ type CategoryModule (data : IMyWebLogData) as this = inherit NancyModule () do - this.Get ("/categories", fun _ -> this.CategoryList ()) - this.Get ("/category/{id}/edit", fun parms -> this.EditCategory (downcast parms)) - this.Post ("/category/{id}/edit", fun parms -> this.SaveCategory (downcast parms)) - this.Delete ("/category/{id}/delete", fun parms -> this.DeleteCategory (downcast parms)) + this.Get ("/categories", fun _ -> this.CategoryList ()) + this.Get ("/category/{id}/edit", fun p -> this.EditCategory (downcast p)) + this.Post ("/category/{id}/edit", fun p -> this.SaveCategory (downcast p)) + this.Post ("/category/{id}/delete", fun p -> this.DeleteCategory (downcast p)) /// Display a list of categories member this.CategoryList () : obj = @@ -26,6 +26,7 @@ type CategoryModule (data : IMyWebLogData) as this = CategoryListModel ( this.Context, this.WebLog, findAllCategories data this.WebLog.Id |> List.map (fun cat -> IndentedCategory.Create cat (fun _ -> false))) + model.PageTitle <- Strings.get "Categories" upcast this.View.["admin/category/list", model] /// Edit a category @@ -35,10 +36,11 @@ type CategoryModule (data : IMyWebLogData) as this = match catId with "new" -> Some Category.Empty | _ -> tryFindCategory data this.WebLog.Id catId |> function | Some cat -> - let model = CategoryEditModel(this.Context, this.WebLog, cat) + let model = CategoryEditModel (this.Context, this.WebLog, cat) model.Categories <- findAllCategories data this.WebLog.Id - |> List.map (fun cat -> IndentedCategory.Create cat - (fun c -> c = defaultArg (fst cat).ParentId "")) + |> List.map (fun c -> + IndentedCategory.Create c (fun catId -> catId = defaultArg cat.ParentId "")) + model.PageTitle <- Strings.get <| match catId with "new" -> "AddNewCategory" | _ -> "EditCategory" upcast this.View.["admin/category/edit", model] | _ -> this.NotFound () @@ -53,10 +55,13 @@ type CategoryModule (data : IMyWebLogData) as this = | _ -> tryFindCategory data this.WebLog.Id catId |> function | Some old -> - let cat = { old with Name = form.Name - Slug = form.Slug - Description = match form.Description with "" -> None | d -> Some d - ParentId = match form.ParentId with "" -> None | p -> Some p } + let cat = + { old with + Name = form.Name + Slug = form.Slug + Description = match form.Description with "" -> None | d -> Some d + ParentId = match form.ParentId with "" -> None | p -> Some p + } let newCatId = saveCategory data cat match old.ParentId = cat.ParentId with | true -> () @@ -68,12 +73,12 @@ type CategoryModule (data : IMyWebLogData) as this = | Some parentId -> addCategoryToParent data this.WebLog.Id parentId newCatId | _ -> () let model = MyWebLogModel (this.Context, this.WebLog) - { UserMessage.Empty with - Level = Level.Info - Message = System.String.Format - (Strings.get "MsgCategoryEditSuccess", - Strings.get (match catId with "new" -> "Added" | _ -> "Updated")) } - |> model.AddMessage + model.AddMessage + { UserMessage.Empty with + Message = System.String.Format + (Strings.get "MsgCategoryEditSuccess", + Strings.get (match catId with "new" -> "Added" | _ -> "Updated")) + } this.Redirect (sprintf "/category/%s/edit" newCatId) model | _ -> this.NotFound () @@ -85,10 +90,8 @@ type CategoryModule (data : IMyWebLogData) as this = match tryFindCategory data this.WebLog.Id catId with | Some cat -> deleteCategory data cat - let model = MyWebLogModel(this.Context, this.WebLog) - { UserMessage.Empty with - Level = Level.Info - Message = System.String.Format(Strings.get "MsgCategoryDeleted", cat.Name) } - |> model.AddMessage + let model = MyWebLogModel (this.Context, this.WebLog) + model.AddMessage + { UserMessage.Empty with Message = System.String.Format(Strings.get "MsgCategoryDeleted", cat.Name) } this.Redirect "/categories" model | _ -> this.NotFound () diff --git a/src/MyWebLog.App/UserModule.fs b/src/MyWebLog.App/UserModule.fs index 2ad8ea3..eca46af 100644 --- a/src/MyWebLog.App/UserModule.fs +++ b/src/MyWebLog.App/UserModule.fs @@ -23,16 +23,17 @@ type UserModule (data : IMyWebLogData, cfg : AppConfig) as this = |> Seq.fold (fun acc byt -> sprintf "%s%s" acc (byt.ToString "x2")) "" do - this.Get ("/logon", fun _ -> this.ShowLogOn ()) - this.Post ("/logon", fun p -> this.DoLogOn (downcast p)) - this.Get ("/logoff", fun _ -> this.LogOff ()) + this.Get ("/log-on", fun _ -> this.ShowLogOn ()) + this.Post ("/log-on", fun p -> this.DoLogOn (downcast p)) + this.Get ("/log-off", fun _ -> this.LogOff ()) /// Show the log on page member this.ShowLogOn () : obj = let model = LogOnModel (this.Context, this.WebLog) let query = this.Request.Query :?> DynamicDictionary model.Form.ReturnUrl <- match query.ContainsKey "returnUrl" with true -> query.["returnUrl"].ToString () | _ -> "" - upcast this.View.["admin/user/logon", model] + model.PageTitle <- Strings.get "LogOn" + upcast this.View.["admin/user/log-on", model] /// Process a user log on member this.DoLogOn (parameters : DynamicDictionary) : obj = @@ -52,12 +53,10 @@ type UserModule (data : IMyWebLogData, cfg : AppConfig) as this = Level = Level.Error Message = Strings.get "ErrBadLogOnAttempt" } |> model.AddMessage - this.Redirect (sprintf "/user/logon?returnUrl=%s" form.ReturnUrl) model + this.Redirect (sprintf "/user/log-on?returnUrl=%s" form.ReturnUrl) model /// Log a user off member this.LogOff () : obj = - // FIXME: why are we getting the user here if we don't do anything with it? - let user = this.Request.PersistableSession.GetOrDefault (Keys.User, User.Empty) this.Session.DeleteAll () let model = MyWebLogModel (this.Context, this.WebLog) model.AddMessage { UserMessage.Empty with Message = Strings.get "MsgLogOffSuccess" } diff --git a/src/MyWebLog.App/ViewModels.fs b/src/MyWebLog.App/ViewModels.fs index 137216f..7a0babe 100644 --- a/src/MyWebLog.App/ViewModels.fs +++ b/src/MyWebLog.App/ViewModels.fs @@ -176,7 +176,7 @@ type IndentedCategory = Selected : bool } with /// Create an indented category - static member Create (cat : Category * int) (isSelected : string -> bool) = + static member Create cat isSelected = { Category = fst cat Indent = snd cat Selected = isSelected (fst cat).Id } @@ -304,6 +304,17 @@ type EditPageModel (ctx, webLog, page, revision) = // ---- Post models ---- +/// Formatter for comment information +type CommentForDisplay (comment : Comment, tz) = + /// The comment on which this model is based + member this.Comment = comment + /// The commentor (linked with a URL if there is one) + member this.Commentor = + match comment.Url with Some url -> sprintf "%s" url comment.Name | _ -> comment.Name + /// The date/time this comment was posted + member this.CommentedOn = + sprintf "%s / %s" (FormatDateTime.longDate tz comment.PostedOn) (FormatDateTime.time tz comment.PostedOn) + /// Model for single post display type PostModel (ctx, webLog, post) = inherit MyWebLogModel (ctx, webLog) @@ -317,8 +328,19 @@ type PostModel (ctx, webLog, post) = member this.PublishedDate = this.DisplayLongDate this.Post.PublishedOn /// The time the post was published member this.PublishedTime = this.DisplayTime this.Post.PublishedOn + /// The number of comments + member this.CommentCount = + match post.Comments |> List.length with + | 0 -> Strings.get "NoComments" + | 1 -> Strings.get "OneComment" + | x -> String.Format (Strings.get "XComments", x) + /// The comments for display + member this.Comments = post.Comments + |> List.filter (fun c -> c.Status = CommentStatus.Approved) + |> List.map (fun c -> CommentForDisplay (c, webLog.TimeZone)) + /// Does the post have tags? - member this.HasTags = not (List.isEmpty post.Tags) + member this.HasTags = not <| List.isEmpty post.Tags /// Get the tags sorted member this.Tags = post.Tags |> List.sort @@ -347,6 +369,12 @@ type PostForDisplay (webLog : WebLog, post : Post) = match this.Post.Status with | PostStatus.Published -> FormatDateTime.time this.TimeZone this.Post.PublishedOn | _ -> FormatDateTime.time this.TimeZone this.Post.UpdatedOn + /// The number of comments + member this.CommentCount = + match post.Comments |> List.length with + | 0 -> Strings.get "NoComments" + | 1 -> Strings.get "OneComment" + | x -> String.Format (Strings.get "XComments", x) /// Tags member this.Tags = match List.length this.Post.Tags with diff --git a/src/MyWebLog.Data.RethinkDB/Post.fs b/src/MyWebLog.Data.RethinkDB/Post.fs index b946ed9..4d4bb14 100644 --- a/src/MyWebLog.Data.RethinkDB/Post.fs +++ b/src/MyWebLog.Data.RethinkDB/Post.fs @@ -6,9 +6,18 @@ open RethinkDb.Driver.Ast let private r = RethinkDb.Driver.RethinkDB.R /// Shorthand to select all published posts for a web log -let private publishedPosts (webLogId : string)= +let private publishedPosts (webLogId : string) = r.Table(Table.Post) .GetAll(r.Array (webLogId, PostStatus.Published)).OptArg("index", "WebLogAndStatus") + .Without("Revisions") + // This allows us to count comments without retrieving them all + .Merge(ReqlFunction1 (fun p -> + upcast r.HashMap( + "Comments", r.Table(Table.Comment) + .GetAll(p.["id"]).OptArg("index", "PostId") + .Pluck("id") + .CoerceTo("array")))) + /// Shorthand to sort posts by published date, slice for the given page, and return a list let private toPostList conn pageNbr nbrPerPage (filter : ReqlExpr) = diff --git a/src/MyWebLog.Data.RethinkDB/SetUp.fs b/src/MyWebLog.Data.RethinkDB/SetUp.fs index 347a488..bd0341a 100644 --- a/src/MyWebLog.Data.RethinkDB/SetUp.fs +++ b/src/MyWebLog.Data.RethinkDB/SetUp.fs @@ -45,7 +45,6 @@ let private createIndex cfg table (index : string * (ReqlExpr -> obj) option) = | Some f -> (tbl cfg table).IndexCreate(idxName, f) | None -> (tbl cfg table).IndexCreate(idxName)) .RunResultAsync cfg.Conn - do! (tbl cfg table).IndexWait(idxName).RunResultAsync cfg.Conn logStepDone () } diff --git a/src/MyWebLog.Resources/en-US.json b/src/MyWebLog.Resources/en-US.json index d5b98d1..be2715a 100644 --- a/src/MyWebLog.Resources/en-US.json +++ b/src/MyWebLog.Resources/en-US.json @@ -2,6 +2,7 @@ "Action": "Action", "Added": "Added", "AddNew": "Add New", + "AddNewCategory": "Add New Category", "AddNewPage": "Add New Page", "AddNewPost": "Add New Post", "Admin": "Admin", @@ -15,11 +16,13 @@ "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", @@ -44,8 +47,10 @@ "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 #", @@ -73,5 +78,6 @@ "Title": "Title", "Updated": "Updated", "View": "View", - "Warning": "Warning" + "Warning": "Warning", + "XComments": "{0} Comments" } diff --git a/src/myWebLog/views/admin/user/logon.html b/src/MyWebLog/views/admin/user/log-on.html similarity index 96% rename from src/myWebLog/views/admin/user/logon.html rename to src/MyWebLog/views/admin/user/log-on.html index 70432d4..2115f3c 100644 --- a/src/myWebLog/views/admin/user/logon.html +++ b/src/MyWebLog/views/admin/user/log-on.html @@ -1,7 +1,7 @@ @Master['admin/admin-layout'] @Section['Content'] -
+ @AntiForgeryToken
diff --git a/src/MyWebLog/views/themes/default/comment.html b/src/MyWebLog/views/themes/default/comment.html new file mode 100644 index 0000000..2c7b34b --- /dev/null +++ b/src/MyWebLog/views/themes/default/comment.html @@ -0,0 +1,4 @@ +

+ @Model.Commentor    @Model.CommentedOn +

+@Model.Comment.Text \ No newline at end of file diff --git a/src/myWebLog/views/admin/admin-layout.html b/src/myWebLog/views/admin/admin-layout.html index 8b01c68..7974c7e 100644 --- a/src/myWebLog/views/admin/admin-layout.html +++ b/src/myWebLog/views/admin/admin-layout.html @@ -22,10 +22,10 @@
diff --git a/src/myWebLog/views/admin/category/list.html b/src/myWebLog/views/admin/category/list.html index d49849f..14d7faa 100644 --- a/src/myWebLog/views/admin/category/list.html +++ b/src/myWebLog/views/admin/category/list.html @@ -32,7 +32,7 @@ @EndEach - + @AntiForgeryToken
@EndSection diff --git a/src/myWebLog/views/themes/default/index-content.html b/src/myWebLog/views/themes/default/index-content.html index c6b9369..18a7f6b 100644 --- a/src/myWebLog/views/themes/default/index-content.html +++ b/src/myWebLog/views/themes/default/index-content.html @@ -16,7 +16,8 @@

@Current.PublishedDate   - @Current.PublishedTime + @Current.PublishedTime   + @Current.CommentCount

@Current.Post.Text diff --git a/src/myWebLog/views/themes/default/layout.html b/src/myWebLog/views/themes/default/layout.html index f2d274b..ed29d2c 100644 --- a/src/myWebLog/views/themes/default/layout.html +++ b/src/myWebLog/views/themes/default/layout.html @@ -27,11 +27,11 @@ diff --git a/src/myWebLog/views/themes/default/single-content.html b/src/myWebLog/views/themes/default/single-content.html index c8cf155..c2e3f94 100644 --- a/src/myWebLog/views/themes/default/single-content.html +++ b/src/myWebLog/views/themes/default/single-content.html @@ -6,7 +6,8 @@

@Model.PublishedDate   - @Model.PublishedTime       + @Model.PublishedTime   + @Model.CommentCount       @Each.Post.Categories @@ -37,20 +38,13 @@

- - +
+
+ @Each.Comments + @Partial['themes/default/comment', @Current] + @EndEach +
+