Category / tag lists, logo
Category and tag list pages now work; added footer logo to default theme with version and page load time tooltip
This commit is contained in:
parent
7c99da8cb5
commit
d3712dc562
@ -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<Category>(conn)
|
||||
|> await
|
||||
|> Seq.tryHead
|
||||
|
@ -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<Page>(conn)
|
||||
|> await
|
||||
|
@ -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<Post>(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<Post>(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<Category>(conn)
|
||||
|> await
|
||||
|> Seq.toList
|
||||
comments = r.Table(Table.Comment)
|
||||
.GetAll(p.id).OptArg("index", "postId")
|
||||
.OrderBy("postedOn")
|
||||
.RunListAsync<Comment>(conn)
|
||||
|> await
|
||||
|> Seq.toList }
|
||||
| None -> None
|
||||
|
||||
/// Try to find a post by its prior permalink
|
||||
let tryFindPostByPriorPermalink conn (webLogId : string) (permalink : string) =
|
||||
|
@ -22,7 +22,7 @@
|
||||
<OutputPath>bin\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<WarningLevel>3</WarningLevel>
|
||||
<DocumentationFile>bin\Debug\myWebLog.Data.XML</DocumentationFile>
|
||||
<DocumentationFile>bin\Debug\myWebLog.Data.xml</DocumentationFile>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<DebugType>pdbonly</DebugType>
|
||||
|
27
src/myWebLog.Resources/Resources.Designer.cs
generated
27
src/myWebLog.Resources/Resources.Designer.cs
generated
@ -303,6 +303,15 @@ namespace myWebLog {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Loaded in.
|
||||
/// </summary>
|
||||
public static string LoadedIn {
|
||||
get {
|
||||
return ResourceManager.GetString("LoadedIn", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Log Off.
|
||||
/// </summary>
|
||||
@ -537,6 +546,15 @@ namespace myWebLog {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Powered by.
|
||||
/// </summary>
|
||||
public static string PoweredBy {
|
||||
get {
|
||||
return ResourceManager.GetString("PoweredBy", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Previous Post.
|
||||
/// </summary>
|
||||
@ -573,6 +591,15 @@ namespace myWebLog {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Seconds.
|
||||
/// </summary>
|
||||
public static string Seconds {
|
||||
get {
|
||||
return ResourceManager.GetString("Seconds", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Show in Page List.
|
||||
/// </summary>
|
||||
|
@ -318,4 +318,13 @@
|
||||
<data name="Warning" xml:space="preserve">
|
||||
<value>Warning</value>
|
||||
</data>
|
||||
<data name="LoadedIn" xml:space="preserve">
|
||||
<value>Loaded in</value>
|
||||
</data>
|
||||
<data name="PoweredBy" xml:space="preserve">
|
||||
<value>Powered by</value>
|
||||
</data>
|
||||
<data name="Seconds" xml:space="preserve">
|
||||
<value>Seconds</value>
|
||||
</data>
|
||||
</root>
|
@ -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]
|
||||
|
@ -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
|
||||
|
@ -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 =
|
||||
|
@ -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<EditPageForm> ()
|
||||
let now = clock.Now.Ticks
|
||||
let pageId = parameters.["id"].ToString ()
|
||||
let form = this.Bind<EditPageForm> ()
|
||||
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)
|
||||
|
@ -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
|
||||
|
@ -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 "/"
|
||||
|
@ -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
|
||||
[<Literal>]
|
||||
let Info = "Info"
|
||||
/// A message regarding a non-fatal but non-optimal condition
|
||||
[<Literal>]
|
||||
let Warning = "WARNING"
|
||||
/// A message regarding a failure of the expected result
|
||||
[<Literal>]
|
||||
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 "<img src=\"/default/footer-logo.png\" alt=\"myWebLog\" title=\""
|
||||
yield sprintf "%s %s • " Resources.PoweredBy this.generator
|
||||
yield Resources.LoadedIn
|
||||
yield " "
|
||||
yield TimeSpan(System.DateTime.Now.Ticks - this.requestStart).TotalSeconds.ToString "f3"
|
||||
yield " "
|
||||
yield Resources.Seconds.ToLower ()
|
||||
yield "\" />"
|
||||
}
|
||||
|> 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<string>.None with get, set
|
||||
|
||||
/// The posts to display
|
||||
member val posts = List.empty<PostForDisplay> 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
|
||||
|
||||
|
@ -22,7 +22,7 @@
|
||||
<OutputPath>bin\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<WarningLevel>3</WarningLevel>
|
||||
<DocumentationFile>bin\Debug\myWebLog.Web.XML</DocumentationFile>
|
||||
<DocumentationFile>bin\Debug\myWebLog.Web.xml</DocumentationFile>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<DebugType>pdbonly</DebugType>
|
||||
|
@ -108,6 +108,12 @@
|
||||
<Content Include="views\themes\default\content\bootstrap-theme.min.css">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="views\themes\default\content\footer-logo.png">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="views\themes\default\footer.html">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="views\themes\default\index-content.html">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
@Section['Content']
|
||||
<div class="row">
|
||||
<p><a class="btn btn-primary" href="/page/new/edit"><i class="fa fa-plus"></i> @Translate.AddNew</a></p>
|
||||
<p><a class="btn btn-primary" href="/page/new/edit"><i class="fa fa-plus"></i> @Translate.AddNew</a></p>
|
||||
</div>
|
||||
<div class="row">
|
||||
<table class="table table-hover">
|
||||
|
BIN
src/myWebLog/views/themes/default/content/footer-logo.png
Normal file
BIN
src/myWebLog/views/themes/default/content/footer-logo.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.2 KiB |
10
src/myWebLog/views/themes/default/footer.html
Normal file
10
src/myWebLog/views/themes/default/footer.html
Normal file
@ -0,0 +1,10 @@
|
||||
<footer>
|
||||
<hr />
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-xs-12 text-right">
|
||||
@Model.footerLogo
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
@ -2,4 +2,8 @@
|
||||
|
||||
@Section['Content']
|
||||
@Partial['themes/default/index-content', Model]
|
||||
@EndSection
|
||||
@EndSection
|
||||
|
||||
@Section['Footer']
|
||||
@Partial['themes/default/footer', Model]
|
||||
@EndSection
|
||||
|
@ -4,7 +4,7 @@
|
||||
<meta charset="utf-8"/>
|
||||
<meta name="viewport" content="width=device-width" />
|
||||
<meta name="generator" content="@Model.generator" />
|
||||
<title>@Model.pageTitle | @Model.webLog.name</title>
|
||||
<title>@Model.displayPageTitle</title>
|
||||
<link rel="stylesheet" type="text/css" href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.4/css/bootstrap.min.css" />
|
||||
<link rel="stylesheet" type="text/css" href="/default/bootstrap-theme.min.css" />
|
||||
<link rel="stylesheet" type="text/css" href="//maxcdn.bootstrapcdn.com/font-awesome/4.3.0/css/font-awesome.min.css" />
|
||||
|
@ -2,4 +2,8 @@
|
||||
|
||||
@Section['Content']
|
||||
@Partial['themes/default/page-content', Model]
|
||||
@EndSection
|
||||
@EndSection
|
||||
|
||||
@Section['Footer']
|
||||
@Partial['themes/default/footer', Model]
|
||||
@EndSection
|
||||
|
@ -8,9 +8,11 @@
|
||||
<i class="fa fa-calendar" title="@Translate.Date"></i> @Model.publishedDate
|
||||
<i class="fa fa-clock-o" title="@Translate.Time"></i> @Model.publishedTime
|
||||
@Each.post.categories
|
||||
<i class="fa fa-folder-open-o" title="@Translate.Category"></i>
|
||||
<!-- <a href="/category/@Current.slug" title=__("Categorized under %s", category.name)) -->
|
||||
@Current.name
|
||||
<span style="white-space:nowrap;">
|
||||
<i class="fa fa-folder-open-o" title="@Translate.Category"></i>
|
||||
<a href="/category/@Current.slug" title="@Translate.CategorizedUnder @Current.name">@Current.name</a>
|
||||
|
||||
</span>
|
||||
@EndEach
|
||||
</h4>
|
||||
</div>
|
||||
@ -22,9 +24,11 @@
|
||||
<div class="row">
|
||||
<div class="col-xs-12">
|
||||
@Each.tags
|
||||
<a href="/tag/@Current.Item2" title="@Translate.PostsTagged "@Current.Item1"">
|
||||
<i class="fa fa-tag"></i> @Current.Item1
|
||||
</a>
|
||||
<span style="white-space:nowrap;">
|
||||
<a href="/tag/@Current.Item2" title="@Translate.PostsTagged "@Current.Item1"">
|
||||
<i class="fa fa-tag"></i> @Current.Item1
|
||||
</a>
|
||||
</span>
|
||||
@EndEach
|
||||
</div>
|
||||
</div>
|
||||
@ -52,14 +56,14 @@
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-xs-6">
|
||||
@If.newerPost.IsSome
|
||||
@If.hasNewer
|
||||
<a href="/@Model.newerPost.Value.permalink" title="@Translate.NextPost - "@Model.newerPost.Value.title"">
|
||||
« @Model.newerPost.Value.title
|
||||
</a>
|
||||
@EndIf
|
||||
</div>
|
||||
<div class="col-xs-6 text-right">
|
||||
@If.olderPost.IsSome
|
||||
@If.hasOlder
|
||||
<a href="/@Model.olderPost.Value.permalink"
|
||||
title="@Translate.PreviousPost - "@Model.olderPost.Value.title"">
|
||||
@Model.olderPost.Value.title »
|
||||
|
@ -2,4 +2,8 @@
|
||||
|
||||
@Section['Content']
|
||||
@Partial['themes/default/single-content', Model]
|
||||
@EndSection
|
||||
@EndSection
|
||||
|
||||
@Section['Footer']
|
||||
@Partial['themes/default/footer', Model]
|
||||
@EndSection
|
||||
|
Loading…
Reference in New Issue
Block a user