From d3712dc562f51eb2181d308e718d15776103b07b Mon Sep 17 00:00:00 2001 From: "Daniel J. Summers" Date: Mon, 25 Jul 2016 19:54:45 -0500 Subject: [PATCH] Category / tag lists, logo Category and tag list pages now work; added footer logo to default theme with version and page load time tooltip --- src/myWebLog.Data/Category.fs | 2 +- src/myWebLog.Data/Page.fs | 2 +- src/myWebLog.Data/Post.fs | 43 ++++++++++-------- src/myWebLog.Data/myWebLog.Data.fsproj | 2 +- src/myWebLog.Resources/Resources.Designer.cs | 27 +++++++++++ src/myWebLog.Resources/Resources.resx | 9 ++++ src/myWebLog.Web/AdminModule.fs | 4 +- src/myWebLog.Web/CategoryModule.fs | 12 ++--- src/myWebLog.Web/ModuleExtensions.fs | 9 ++-- src/myWebLog.Web/PageModule.fs | 22 ++++----- src/myWebLog.Web/PostModule.fs | 35 +++++++------- src/myWebLog.Web/UserModule.fs | 19 ++++---- src/myWebLog.Web/ViewModels.fs | 38 +++++++++++----- src/myWebLog.Web/myWebLog.Web.fsproj | 2 +- src/myWebLog/myWebLog.csproj | 6 +++ src/myWebLog/views/admin/page/list.html | 2 +- .../themes/default/content/footer-logo.png | Bin 0 -> 2257 bytes src/myWebLog/views/themes/default/footer.html | 10 ++++ src/myWebLog/views/themes/default/index.html | 6 ++- src/myWebLog/views/themes/default/layout.html | 2 +- src/myWebLog/views/themes/default/page.html | 6 ++- .../views/themes/default/single-content.html | 20 ++++---- src/myWebLog/views/themes/default/single.html | 6 ++- 23 files changed, 186 insertions(+), 98 deletions(-) create mode 100644 src/myWebLog/views/themes/default/content/footer-logo.png create mode 100644 src/myWebLog/views/themes/default/footer.html diff --git a/src/myWebLog.Data/Category.fs b/src/myWebLog.Data/Category.fs index 84b12e3..d670029 100644 --- a/src/myWebLog.Data/Category.fs +++ b/src/myWebLog.Data/Category.fs @@ -120,7 +120,7 @@ let deleteCategory conn cat = /// Get a category by its slug let tryFindCategoryBySlug conn (webLogId : string) (slug : string) = r.Table(Table.Category) - .GetAll(webLogId, slug).OptArg("index", "slug") + .GetAll(r.Array(webLogId, slug)).OptArg("index", "slug") .RunCursorAsync(conn) |> await |> Seq.tryHead diff --git a/src/myWebLog.Data/Page.fs b/src/myWebLog.Data/Page.fs index 7b09fe1..904b0ff 100644 --- a/src/myWebLog.Data/Page.fs +++ b/src/myWebLog.Data/Page.fs @@ -31,7 +31,7 @@ let tryFindPageWithoutRevisions conn webLogId pageId : Page option = /// Find a page by its permalink let tryFindPageByPermalink conn (webLogId : string) (permalink : string) = r.Table(Table.Page) - .GetAll(webLogId, permalink).OptArg("index", "permalink") + .GetAll(r.Array(webLogId, permalink)).OptArg("index", "permalink") .Without("revisions") .RunCursorAsync(conn) |> await diff --git a/src/myWebLog.Data/Post.fs b/src/myWebLog.Data/Post.fs index bb523da..34e5d04 100644 --- a/src/myWebLog.Data/Post.fs +++ b/src/myWebLog.Data/Post.fs @@ -48,13 +48,13 @@ let findPageOfPublishedPosts conn webLogId pageNbr nbrPerPage = /// Get a page of published posts assigned to a given category let findPageOfCategorizedPosts conn webLogId (categoryId : string) pageNbr nbrPerPage = (publishedPosts webLogId) - .Filter(fun p -> p.["categoryIds"].Contains(categoryId)) + .Filter(ReqlFunction1(fun p -> upcast p.["categoryIds"].Contains(categoryId))) |> toPostList conn pageNbr nbrPerPage /// Get a page of published posts tagged with a given tag let findPageOfTaggedPosts conn webLogId (tag : string) pageNbr nbrPerPage = (publishedPosts webLogId) - .Filter(fun p -> p.["tags"].Contains(tag)) + .Filter(ReqlFunction1(fun p -> upcast p.["tags"].Contains(tag))) |> toPostList conn pageNbr nbrPerPage /// Try to get the next newest post from the given post @@ -103,23 +103,30 @@ let tryFindPost conn webLogId postId : Post option = | post -> Some <| unbox post /// Try to find a post by its permalink +// TODO: see if we can make .Merge work for page list even though the attribute is ignored +// (needs to be ignored for serialization, but included for deserialization) let tryFindPostByPermalink conn webLogId permalink = - r.Table(Table.Post) - .GetAll(r.Array(webLogId, permalink)).OptArg("index", "permalink") - .Filter(fun p -> p.["status"].Eq(PostStatus.Published)) - .Without("revisions") - .Merge(fun post -> r.HashMap("categories", - post.["categoryIds"] - .Map(ReqlFunction1(fun cat -> upcast r.Table(Table.Category).Get(cat).Without("children"))) - .CoerceTo("array"))) - .Merge(fun post -> r.HashMap("comments", - r.Table(Table.Comment) - .GetAll(post.["id"]).OptArg("index", "postId") - .OrderBy("postedOn") - .CoerceTo("array"))) - .RunCursorAsync(conn) - |> await - |> Seq.tryHead + match r.Table(Table.Post) + .GetAll(r.Array(webLogId, permalink)).OptArg("index", "permalink") + .Filter(fun p -> p.["status"].Eq(PostStatus.Published)) + .Without("revisions") + .RunCursorAsync(conn) + |> await + |> Seq.tryHead with + | Some p -> Some { p with categories = r.Table(Table.Category) + .GetAll(p.categoryIds |> List.toArray) + .Without("children") + .OrderBy("name") + .RunListAsync(conn) + |> await + |> Seq.toList + comments = r.Table(Table.Comment) + .GetAll(p.id).OptArg("index", "postId") + .OrderBy("postedOn") + .RunListAsync(conn) + |> await + |> Seq.toList } + | None -> None /// Try to find a post by its prior permalink let tryFindPostByPriorPermalink conn (webLogId : string) (permalink : string) = diff --git a/src/myWebLog.Data/myWebLog.Data.fsproj b/src/myWebLog.Data/myWebLog.Data.fsproj index 5e64bd3..8593331 100644 --- a/src/myWebLog.Data/myWebLog.Data.fsproj +++ b/src/myWebLog.Data/myWebLog.Data.fsproj @@ -22,7 +22,7 @@ bin\Debug\ DEBUG;TRACE 3 - bin\Debug\myWebLog.Data.XML + bin\Debug\myWebLog.Data.xml pdbonly diff --git a/src/myWebLog.Resources/Resources.Designer.cs b/src/myWebLog.Resources/Resources.Designer.cs index 597499f..aef5363 100644 --- a/src/myWebLog.Resources/Resources.Designer.cs +++ b/src/myWebLog.Resources/Resources.Designer.cs @@ -303,6 +303,15 @@ namespace myWebLog { } } + /// + /// Looks up a localized string similar to Loaded in. + /// + public static string LoadedIn { + get { + return ResourceManager.GetString("LoadedIn", resourceCulture); + } + } + /// /// Looks up a localized string similar to Log Off. /// @@ -537,6 +546,15 @@ namespace myWebLog { } } + /// + /// Looks up a localized string similar to Powered by. + /// + public static string PoweredBy { + get { + return ResourceManager.GetString("PoweredBy", resourceCulture); + } + } + /// /// Looks up a localized string similar to Previous Post. /// @@ -573,6 +591,15 @@ namespace myWebLog { } } + /// + /// Looks up a localized string similar to Seconds. + /// + public static string Seconds { + get { + return ResourceManager.GetString("Seconds", resourceCulture); + } + } + /// /// Looks up a localized string similar to Show in Page List. /// diff --git a/src/myWebLog.Resources/Resources.resx b/src/myWebLog.Resources/Resources.resx index 29a636c..054947a 100644 --- a/src/myWebLog.Resources/Resources.resx +++ b/src/myWebLog.Resources/Resources.resx @@ -318,4 +318,13 @@ Warning + + Loaded in + + + Powered by + + + Seconds + \ No newline at end of file diff --git a/src/myWebLog.Web/AdminModule.fs b/src/myWebLog.Web/AdminModule.fs index f7ce295..2fd3695 100644 --- a/src/myWebLog.Web/AdminModule.fs +++ b/src/myWebLog.Web/AdminModule.fs @@ -10,11 +10,11 @@ type AdminModule(conn : IConnection) as this = inherit NancyModule("/admin") do - this.Get.["/"] <- fun _ -> upcast this.Dashboard () + this.Get.["/"] <- fun _ -> this.Dashboard () /// Admin dashboard member this.Dashboard () = this.RequiresAccessLevel AuthorizationLevel.Administrator let model = DashboardModel(this.Context, this.WebLog, findDashboardCounts conn this.WebLog.id) model.pageTitle <- Resources.Dashboard - this.View.["admin/dashboard", model] + upcast this.View.["admin/dashboard", model] diff --git a/src/myWebLog.Web/CategoryModule.fs b/src/myWebLog.Web/CategoryModule.fs index 4b77c88..d72dfa8 100644 --- a/src/myWebLog.Web/CategoryModule.fs +++ b/src/myWebLog.Web/CategoryModule.fs @@ -12,10 +12,10 @@ type CategoryModule(conn : IConnection) as this = inherit NancyModule() do - this.Get .["/categories" ] <- fun _ -> upcast this.CategoryList () - this.Get .["/category/{id}/edit" ] <- fun parms -> upcast this.EditCategory (downcast parms) - this.Post .["/category/{id}/edit" ] <- fun parms -> upcast this.SaveCategory (downcast parms) - this.Delete.["/category/{id}/delete"] <- fun parms -> upcast this.DeleteCategory (downcast parms) + 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) /// Display a list of categories member this.CategoryList () = @@ -23,7 +23,7 @@ type CategoryModule(conn : IConnection) as this = let model = CategoryListModel(this.Context, this.WebLog, (getAllCategories conn this.WebLog.id |> List.map (fun cat -> IndentedCategory.create cat (fun _ -> false)))) - this.View.["/admin/category/list", model] + upcast this.View.["/admin/category/list", model] /// Edit a category member this.EditCategory (parameters : DynamicDictionary) = @@ -39,7 +39,7 @@ type CategoryModule(conn : IConnection) as this = model.categories <- getAllCategories conn this.WebLog.id |> List.map (fun cat -> IndentedCategory.create cat (fun c -> c = defaultArg (fst cat).parentId "")) - this.View.["admin/category/edit", model] + upcast this.View.["admin/category/edit", model] | None -> this.NotFound () /// Save a category diff --git a/src/myWebLog.Web/ModuleExtensions.fs b/src/myWebLog.Web/ModuleExtensions.fs index c7e34ce..e5deb9c 100644 --- a/src/myWebLog.Web/ModuleExtensions.fs +++ b/src/myWebLog.Web/ModuleExtensions.fs @@ -13,17 +13,18 @@ type NancyModule with member this.WebLog = this.Context.Items.[Keys.WebLog] :?> WebLog /// Display a view using the theme specified for the web log - member this.ThemedView view model = this.View.[(sprintf "themes/%s/%s" this.WebLog.themePath view), model] + member this.ThemedView view (model : MyWebLogModel) : obj = + upcast this.View.[(sprintf "themes/%s/%s" this.WebLog.themePath view), model] /// Return a 404 - member this.NotFound () = this.Negotiate.WithStatusCode 404 + member this.NotFound () : obj = upcast HttpStatusCode.NotFound /// Redirect a request, storing messages in the session if they exist - member this.Redirect url (model : MyWebLogModel) = + member this.Redirect url (model : MyWebLogModel) : obj = match List.length model.messages with | 0 -> () | _ -> this.Session.[Keys.Messages] <- model.messages - this.Negotiate.WithHeader("Location", url).WithStatusCode(HttpStatusCode.TemporaryRedirect) + upcast this.Response.AsRedirect(url).WithStatusCode HttpStatusCode.TemporaryRedirect /// Require a specific level of access for the current web log member this.RequiresAccessLevel level = diff --git a/src/myWebLog.Web/PageModule.fs b/src/myWebLog.Web/PageModule.fs index 2917ed2..c12950d 100644 --- a/src/myWebLog.Web/PageModule.fs +++ b/src/myWebLog.Web/PageModule.fs @@ -14,22 +14,22 @@ type PageModule(conn : IConnection, clock : IClock) as this = inherit NancyModule() do - this.Get .["/pages" ] <- fun _ -> upcast this.PageList () - this.Get .["/page/{id}/edit" ] <- fun parms -> upcast this.EditPage (downcast parms) - this.Post .["/page/{id}/edit" ] <- fun parms -> upcast this.SavePage (downcast parms) - this.Delete.["/page/{id}/delete"] <- fun parms -> upcast this.DeletePage (downcast parms) + this.Get .["/pages" ] <- fun _ -> this.PageList () + this.Get .["/page/{id}/edit" ] <- fun parms -> this.EditPage (downcast parms) + this.Post .["/page/{id}/edit" ] <- fun parms -> this.SavePage (downcast parms) + this.Delete.["/page/{id}/delete"] <- fun parms -> this.DeletePage (downcast parms) /// List all pages member this.PageList () = this.RequiresAccessLevel AuthorizationLevel.Administrator let model = PagesModel(this.Context, this.WebLog, findAllPages conn this.WebLog.id) model.pageTitle <- Resources.Pages - this.View.["admin/page/list", model] + upcast this.View.["admin/page/list", model] /// Edit a page member this.EditPage (parameters : DynamicDictionary) = this.RequiresAccessLevel AuthorizationLevel.Administrator - let pageId : string = downcast parameters.["id"] + let pageId = parameters.["id"].ToString () match (match pageId with | "new" -> Some Page.empty | _ -> tryFindPage conn this.WebLog.id pageId) with @@ -42,16 +42,16 @@ type PageModule(conn : IConnection, clock : IClock) as this = model.pageTitle <- match pageId with | "new" -> Resources.AddNewPage | _ -> Resources.EditPage - this.View.["admin/page/edit"] + upcast this.View.["admin/page/edit"] | None -> this.NotFound () /// Save a page member this.SavePage (parameters : DynamicDictionary) = this.ValidateCsrfToken () this.RequiresAccessLevel AuthorizationLevel.Administrator - let pageId : string = downcast parameters.["id"] - let form = this.Bind () - let now = clock.Now.Ticks + let pageId = parameters.["id"].ToString () + let form = this.Bind () + let now = clock.Now.Ticks match (match pageId with | "new" -> Some Page.empty | _ -> tryFindPage conn this.WebLog.id pageId) with @@ -84,7 +84,7 @@ type PageModule(conn : IConnection, clock : IClock) as this = member this.DeletePage (parameters : DynamicDictionary) = this.ValidateCsrfToken () this.RequiresAccessLevel AuthorizationLevel.Administrator - let pageId : string = downcast parameters.["id"] + let pageId = parameters.["id"].ToString () match tryFindPageWithoutRevisions conn this.WebLog.id pageId with | Some page -> deletePage conn page.webLogId page.id let model = MyWebLogModel(this.Context, this.WebLog) diff --git a/src/myWebLog.Web/PostModule.fs b/src/myWebLog.Web/PostModule.fs index 8b5e2d4..669027a 100644 --- a/src/myWebLog.Web/PostModule.fs +++ b/src/myWebLog.Web/PostModule.fs @@ -24,17 +24,17 @@ type PostModule(conn : IConnection, clock : IClock) as this = let forDisplay posts = posts |> List.map (fun post -> PostForDisplay(this.WebLog, post)) do - this.Get .["/" ] <- fun _ -> upcast this.HomePage () - this.Get .["/{permalink*}" ] <- fun parms -> upcast this.CatchAll (downcast parms) - this.Get .["/posts/page/{page:int}" ] <- fun parms -> upcast this.PublishedPostsPage (getPage <| downcast parms) - this.Get .["/category/{slug}" ] <- fun parms -> upcast this.CategorizedPosts (downcast parms) - this.Get .["/category/{slug}/page/{page:int}"] <- fun parms -> upcast this.CategorizedPosts (downcast parms) - this.Get .["/tag/{tag}" ] <- fun parms -> upcast this.TaggedPosts (downcast parms) - this.Get .["/tag/{tag}/page/{page:int}" ] <- fun parms -> upcast this.TaggedPosts (downcast parms) - this.Get .["/posts/list" ] <- fun _ -> upcast this.PostList 1 - this.Get .["/posts/list/page/{page:int}" ] <- fun parms -> upcast this.PostList (getPage <| downcast parms) - this.Get .["/post/{postId}/edit" ] <- fun parms -> upcast this.EditPost (downcast parms) - this.Post.["/post/{postId}/edit" ] <- fun parms -> upcast this.SavePost (downcast parms) + this.Get .["/" ] <- fun _ -> this.HomePage () + this.Get .["/{permalink*}" ] <- fun parms -> this.CatchAll (downcast parms) + this.Get .["/posts/page/{page:int}" ] <- fun parms -> this.PublishedPostsPage (getPage <| downcast parms) + this.Get .["/category/{slug}" ] <- fun parms -> this.CategorizedPosts (downcast parms) + this.Get .["/category/{slug}/page/{page:int}"] <- fun parms -> this.CategorizedPosts (downcast parms) + this.Get .["/tag/{tag}" ] <- fun parms -> this.TaggedPosts (downcast parms) + this.Get .["/tag/{tag}/page/{page:int}" ] <- fun parms -> this.TaggedPosts (downcast parms) + this.Get .["/posts/list" ] <- fun _ -> this.PostList 1 + this.Get .["/posts/list/page/{page:int}" ] <- fun parms -> this.PostList (getPage <| downcast parms) + this.Get .["/post/{postId}/edit" ] <- fun parms -> this.EditPost (downcast parms) + this.Post.["/post/{postId}/edit" ] <- fun parms -> this.SavePost (downcast parms) // ---- Display posts to users ---- @@ -86,9 +86,8 @@ type PostModule(conn : IConnection, clock : IClock) as this = | None -> // Maybe it's an old permalink for a post match tryFindPostByPriorPermalink conn this.WebLog.id url with | Some post -> // Redirect them to the proper permalink - this.Negotiate - .WithHeader("Location", sprintf "/%s" post.permalink) - .WithStatusCode HttpStatusCode.MovedPermanently + upcast this.Response.AsRedirect(sprintf "/%s" post.permalink) + .WithStatusCode HttpStatusCode.MovedPermanently | None -> this.NotFound () /// Display categorized posts @@ -102,7 +101,7 @@ type PostModule(conn : IConnection, clock : IClock) as this = model.hasNewer <- match List.isEmpty model.posts with | true -> false | _ -> Option.isSome <| tryFindNewerCategorizedPost conn cat.id - (List.last model.posts).post + (List.head model.posts).post model.hasOlder <- match List.isEmpty model.posts with | true -> false | _ -> Option.isSome <| tryFindOlderCategorizedPost conn cat.id @@ -125,7 +124,7 @@ type PostModule(conn : IConnection, clock : IClock) as this = model.posts <- findPageOfTaggedPosts conn this.WebLog.id tag pageNbr 10 |> forDisplay model.hasNewer <- match List.isEmpty model.posts with | true -> false - | _ -> Option.isSome <| tryFindNewerTaggedPost conn tag (List.last model.posts).post + | _ -> Option.isSome <| tryFindNewerTaggedPost conn tag (List.head model.posts).post model.hasOlder <- match List.isEmpty model.posts with | true -> false | _ -> Option.isSome <| tryFindOlderTaggedPost conn tag (List.last model.posts).post @@ -147,7 +146,7 @@ type PostModule(conn : IConnection, clock : IClock) as this = model.hasOlder <- List.length model.posts > 24 model.urlPrefix <- "/posts/list" model.pageTitle <- Resources.Posts - this.View.["admin/post/list", model] + upcast this.View.["admin/post/list", model] /// Edit a post member this.EditPost (parameters : DynamicDictionary) = @@ -170,7 +169,7 @@ type PostModule(conn : IConnection, clock : IClock) as this = model.pageTitle <- match post.id with | "new" -> Resources.AddNewPost | _ -> Resources.EditPost - this.View.["admin/post/edit"] + upcast this.View.["admin/post/edit"] | None -> this.NotFound () /// Save a post diff --git a/src/myWebLog.Web/UserModule.fs b/src/myWebLog.Web/UserModule.fs index e2986b2..5ef73ed 100644 --- a/src/myWebLog.Web/UserModule.fs +++ b/src/myWebLog.Web/UserModule.fs @@ -21,9 +21,9 @@ type UserModule(conn : IConnection) as this = |> Seq.fold (fun acc byt -> sprintf "%s%s" acc (byt.ToString "x2")) "" do - this.Get .["/logon" ] <- fun parms -> upcast this.ShowLogOn (downcast parms) - this.Post.["/logon" ] <- fun parms -> upcast this.DoLogOn (downcast parms) - this.Get .["/logoff"] <- fun parms -> upcast this.LogOff () + this.Get .["/logon" ] <- fun parms -> this.ShowLogOn (downcast parms) + this.Post.["/logon" ] <- fun parms -> this.DoLogOn (downcast parms) + this.Get .["/logoff"] <- fun parms -> this.LogOff () /// Show the log on page member this.ShowLogOn (parameters : DynamicDictionary) = @@ -31,7 +31,7 @@ type UserModule(conn : IConnection) as this = model.form.returnUrl <- match parameters.ContainsKey "returnUrl" with | true -> parameters.["returnUrl"].ToString () | _ -> "" - this.View.["admin/user/logon", model] + upcast this.View.["admin/user/logon", model] /// Process a user log on member this.DoLogOn (parameters : DynamicDictionary) = @@ -46,16 +46,13 @@ type UserModule(conn : IConnection) as this = |> model.addMessage this.Redirect "" model |> ignore // Save the messages in the session before the Nancy redirect // TODO: investigate if addMessage should update the session when it's called - this.LoginAndRedirect - (System.Guid.Parse user.id, fallbackRedirectUrl = defaultArg (Option.ofObj(form.returnUrl)) "/") + upcast this.LoginAndRedirect (System.Guid.Parse user.id, + fallbackRedirectUrl = defaultArg (Option.ofObj(form.returnUrl)) "/") | None -> { level = Level.Error message = Resources.ErrBadLogOnAttempt details = None } |> model.addMessage - this.Redirect "" model |> ignore // Save the messages in the session before the Nancy redirect - // Can't redirect with a negotiator when the other leg uses a straight response... :/ - this.Response.AsRedirect((sprintf "/user/logon?returnUrl=%s" form.returnUrl), - Responses.RedirectResponse.RedirectType.SeeOther) + this.Redirect (sprintf "/user/logon?returnUrl=%s" form.returnUrl) model /// Log a user off member this.LogOff () = @@ -67,4 +64,4 @@ type UserModule(conn : IConnection) as this = details = None } |> model.addMessage this.Redirect "" model |> ignore - this.LogoutAndRedirect "/" + upcast this.LogoutAndRedirect "/" diff --git a/src/myWebLog.Web/ViewModels.fs b/src/myWebLog.Web/ViewModels.fs index d25bbcf..4e3c772 100644 --- a/src/myWebLog.Web/ViewModels.fs +++ b/src/myWebLog.Web/ViewModels.fs @@ -7,18 +7,16 @@ open Nancy.Session.Persistable open Newtonsoft.Json open NodaTime open NodaTime.Text +open System /// Levels for a user message module Level = /// An informational message - [] let Info = "Info" /// A message regarding a non-fatal but non-optimal condition - [] let Warning = "WARNING" /// A message regarding a failure of the expected result - [] let Error = "ERROR" @@ -127,7 +125,28 @@ type MyWebLogModel(ctx : NancyContext, webLog : WebLog) = member this.displayShortDate ticks = FormatDateTime.shortDate this.webLog.timeZone ticks /// Display the time member this.displayTime ticks = FormatDateTime.time this.webLog.timeZone ticks + /// The page title with the web log name appended + member this.displayPageTitle = + match this.pageTitle with + | "" -> match this.webLog.subtitle with + | Some st -> sprintf "%s | %s" this.webLog.name st + | None -> this.webLog.name + | pt -> sprintf "%s | %s" pt this.webLog.name + /// An image with the version and load time in the tool tip + member this.footerLogo = + seq { + yield "\"myWebLog\"" + } + |> Seq.reduce (fun acc x -> acc + x) + // ---- Admin models ---- @@ -266,7 +285,7 @@ type EditPageModel(ctx, webLog, page, revision) = // ---- Post models ---- -/// Model for post display +/// Model for single post display type PostModel(ctx, webLog, post) = inherit MyWebLogModel(ctx, webLog) /// The post being displayed @@ -285,7 +304,10 @@ type PostModel(ctx, webLog, post) = member this.tags = post.tags |> List.sort |> List.map (fun tag -> tag, tag.Replace(' ', '+')) - + /// Does this post have a newer post? + member this.hasNewer = this.newerPost.IsSome + /// Does this post have an older post? + member this.hasOlder = this.olderPost.IsSome /// Wrapper for a post with additional properties type PostForDisplay(webLog : WebLog, post : Post) = @@ -311,22 +333,16 @@ type PostForDisplay(webLog : WebLog, post : Post) = /// Model for all page-of-posts pages type PostsModel(ctx, webLog) = inherit MyWebLogModel(ctx, webLog) - /// The subtitle for the page member val subtitle = Option.None with get, set - /// The posts to display member val posts = List.empty with get, set - /// The page number of the post list member val pageNbr = 0 with get, set - /// Whether there is a newer page of posts for the list member val hasNewer = false with get, set - /// Whether there is an older page of posts for the list member val hasOlder = true with get, set - /// The prefix for the next/prior links member val urlPrefix = "" with get, set diff --git a/src/myWebLog.Web/myWebLog.Web.fsproj b/src/myWebLog.Web/myWebLog.Web.fsproj index 6204272..e20a015 100644 --- a/src/myWebLog.Web/myWebLog.Web.fsproj +++ b/src/myWebLog.Web/myWebLog.Web.fsproj @@ -22,7 +22,7 @@ bin\Debug\ DEBUG;TRACE 3 - bin\Debug\myWebLog.Web.XML + bin\Debug\myWebLog.Web.xml pdbonly diff --git a/src/myWebLog/myWebLog.csproj b/src/myWebLog/myWebLog.csproj index 6807cef..cb19a3f 100644 --- a/src/myWebLog/myWebLog.csproj +++ b/src/myWebLog/myWebLog.csproj @@ -108,6 +108,12 @@ Always + + Always + + + Always + Always diff --git a/src/myWebLog/views/admin/page/list.html b/src/myWebLog/views/admin/page/list.html index 26aff35..fd3e71d 100644 --- a/src/myWebLog/views/admin/page/list.html +++ b/src/myWebLog/views/admin/page/list.html @@ -2,7 +2,7 @@ @Section['Content']
diff --git a/src/myWebLog/views/themes/default/content/footer-logo.png b/src/myWebLog/views/themes/default/content/footer-logo.png new file mode 100644 index 0000000000000000000000000000000000000000..40fd00f0c3fa6180150ed96e8bfad748c611f195 GIT binary patch literal 2257 zcmV;?2rl=DP)X1^@s6&b2Es00006VoOIv0RI60 z0RN!9r;`8x010qNS#tmY3ljhU3ljkVnw%H_000McNliru;0G5MF%)2sWG(;z03B&m zSad^gZEa<4bN~PV002XBWnpw>WFU8GbZ8()Nlj2>E@cM*00=cnL_t(&-tAdwOjKDG z{$3Rz6e2W@H3;sOvbhvQ5oIw3cL62X#8}-TL?txJ)C7rkYc-i*I)I|a1$7jch|}8Y zG+iP{GhozlK_s}0X}2*dh{_Itm@0k${+{=l9Q9I>f74d zpin5_B!diLyTaZB#qyN4@Ru3+-y z$*{At0|1!KX5{DRBQP)!PEJm6b92M=>Cn~R}Chr-d(5gr~Mm^W`8nwpxd z*BpK%==FL6;L4RNCGwFYM>04#m?W8ykU%>-J3fB=SkxC36c7OS?Ab$-G@H$gkB=t+ zjvqgsTefVWPN$K;rc6XW9I2!Q_n{>;wK=7tR$7#SH!092_|Y-(zf*iV@doA~&6%H?u#W81cE zBLGgEIFV+vS+pxGEEGxkZ9t==qb0T(85sn?Y15{?7XkVC`2@h>!-w)Gl?&)R{LfGuHC=Jh@1$f7N+v+Ltj%iU&e2XzkO?< zb4qf!VAleYWV^AQx*v2@YL&cHcZspFv6RVVeDvs%WPukhTp&rdw6xIP-kx*k&J|CP zySqEX!oo!V!NI`+O@J3F?ww@U(AUS2M-y?XU30kC`b?(apws8OQ` zfQ5yH7P>QM&JY0I-Q7j9LqkIefZMlkx3I6TuYWTE%m1;Q;o0GINOs_Wr30Au_cUt$ zrlHAX;`)ECXYVDwsrx}EaeDClLCP1%dHTj_p1N_0ek=UwknBLr2d~|~#$CI15din@ z-Aj^;ii%?2zI`P=A|oTIR4QpUn^{#=MF7ms&KC6wg+g5T>z@1f?@I!D>((vF90r3y z)O8+)cOk&p*_i-%@!~}b-Q&lPEqwOq(Srb3Q&VHrpSAhyI%`3sZzQryvoXB)a8#yO z;`Dz`V{Q3bOkO(~)wiqh*~rgu{`SkFS8}NYx*{DE4hn<}48gXO+i>*XN1>gdMNq#W zqIexNIP!*GIG6`0_gMKewNl4=S}v*4(b)k&8#@x5AHco8s6fqYqDBKgrCZOnRkz zap{4jY-w$Ind`pOGJSu#$Z$kN1U)=FSX5MGHI>}Fd6NK`nVHGZ&`=H-FhHXB_4OqH z>h=1!ro+w?R$W~!ksmvDi~#8A>G@tMXz0+P7B#99XV0D`0D5_OiL!qE`VjyR9Xe!T zUt3%I<`gt~->5B!F7^kQ%e*5b^e^RW)gpS{)^QGo6Tl={Mmzb zkA9sXFE39lfX>d&Y-?+?80YKj%W>nz(ca#kix)4J=$0;BN&pNC3u8w|$2$>_l9D2k zcXl$Vsj2TpKu%5$0Z^@0v$eHVvd>wwW)T3FFJCUo7cE*u01OTemh97HGBGA5=Hquk z-=F)QJ+(dgP0=?@C`e#_WxjaAWilCKV`Ht3Te4&cWilB7u&k`iqSy`^G>8Bg6cohO zt5>t2pny7^j>Coxvl>vfTFtq0=W@r69n@$v1i)Usdhx-72UhJfGBTKzl_fr%FdB_w z%NY<5z}(ziZrr$$6DCX`07gVa&|ol#_75LEWS>5L2!Mfsfn2j@4cDz($1!8ZFexeN z;}Wp@)NX#V_!I6wxBunhJB!2e<>gVW zR@2GJiB3*VRIAlowQ7~swcfjTkBNziR4SEpaB!fHj}KR`Ud{IQc8jKgB!v(CN6gq^ zMB2VI+PfWojMhIy +
+
+
+
+ @Model.footerLogo +
+
+
+ \ No newline at end of file diff --git a/src/myWebLog/views/themes/default/index.html b/src/myWebLog/views/themes/default/index.html index dbf4978..6ef4c3a 100644 --- a/src/myWebLog/views/themes/default/index.html +++ b/src/myWebLog/views/themes/default/index.html @@ -2,4 +2,8 @@ @Section['Content'] @Partial['themes/default/index-content', Model] -@EndSection \ No newline at end of file +@EndSection + +@Section['Footer'] + @Partial['themes/default/footer', Model] +@EndSection diff --git a/src/myWebLog/views/themes/default/layout.html b/src/myWebLog/views/themes/default/layout.html index 8ec9c05..58eddbd 100644 --- a/src/myWebLog/views/themes/default/layout.html +++ b/src/myWebLog/views/themes/default/layout.html @@ -4,7 +4,7 @@ - @Model.pageTitle | @Model.webLog.name + @Model.displayPageTitle diff --git a/src/myWebLog/views/themes/default/page.html b/src/myWebLog/views/themes/default/page.html index 29da8d0..d3d60c1 100644 --- a/src/myWebLog/views/themes/default/page.html +++ b/src/myWebLog/views/themes/default/page.html @@ -2,4 +2,8 @@ @Section['Content'] @Partial['themes/default/page-content', Model] -@EndSection \ No newline at end of file +@EndSection + +@Section['Footer'] + @Partial['themes/default/footer', Model] +@EndSection diff --git a/src/myWebLog/views/themes/default/single-content.html b/src/myWebLog/views/themes/default/single-content.html index 9cec7f1..6b813f3 100644 --- a/src/myWebLog/views/themes/default/single-content.html +++ b/src/myWebLog/views/themes/default/single-content.html @@ -8,9 +8,11 @@ @Model.publishedDate   @Model.publishedTime       @Each.post.categories -   - - @Current.name     + + + @Current.name +     + @EndEach @@ -22,9 +24,11 @@
@Each.tags - - @Current.Item1 -     + + + @Current.Item1 +     + @EndEach
@@ -52,14 +56,14 @@