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:
Daniel J. Summers 2016-07-25 19:54:45 -05:00
parent 7c99da8cb5
commit d3712dc562
23 changed files with 186 additions and 98 deletions

View File

@ -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

View File

@ -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

View File

@ -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)
match 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
|> 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) =

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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]

View File

@ -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

View File

@ -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 =

View File

@ -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,14 +42,14 @@ 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 pageId = parameters.["id"].ToString ()
let form = this.Bind<EditPageForm> ()
let now = clock.Now.Ticks
match (match 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)

View File

@ -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,8 +86,7 @@ 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)
upcast this.Response.AsRedirect(sprintf "/%s" post.permalink)
.WithStatusCode HttpStatusCode.MovedPermanently
| None -> this.NotFound ()
@ -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

View File

@ -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 "/"

View File

@ -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,6 +125,27 @@ 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 &bull; " 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

View File

@ -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>

View File

@ -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>

View File

@ -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> &nbsp; @Translate.AddNew</a></p>
</div>
<div class="row">
<table class="table table-hover">

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

View 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>

View File

@ -3,3 +3,7 @@
@Section['Content']
@Partial['themes/default/index-content', Model]
@EndSection
@Section['Footer']
@Partial['themes/default/footer', Model]
@EndSection

View File

@ -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" />

View File

@ -3,3 +3,7 @@
@Section['Content']
@Partial['themes/default/page-content', Model]
@EndSection
@Section['Footer']
@Partial['themes/default/footer', Model]
@EndSection

View File

@ -8,9 +8,11 @@
<i class="fa fa-calendar" title="@Translate.Date"></i> @Model.publishedDate &nbsp;
<i class="fa fa-clock-o" title="@Translate.Time"></i> @Model.publishedTime &nbsp; &nbsp; &nbsp;
@Each.post.categories
<i class="fa fa-folder-open-o" title="@Translate.Category"></i> &nbsp;
<!-- <a href="/category/@Current.slug" title=__("Categorized under %s", category.name)) -->
@Current.name &nbsp; &nbsp;
<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>
&nbsp; &nbsp;
</span>
@EndEach
</h4>
</div>
@ -22,9 +24,11 @@
<div class="row">
<div class="col-xs-12">
@Each.tags
<span style="white-space:nowrap;">
<a href="/tag/@Current.Item2" title="@Translate.PostsTagged &quot;@Current.Item1&quot;">
<i class="fa fa-tag"></i> @Current.Item1
</a> &nbsp; &nbsp;
</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 - &quot;@Model.newerPost.Value.title&quot;">
&#xab;&nbsp; @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 - &quot;@Model.olderPost.Value.title&quot;">
@Model.olderPost.Value.title &nbsp;&#xbb;

View File

@ -3,3 +3,7 @@
@Section['Content']
@Partial['themes/default/single-content', Model]
@EndSection
@Section['Footer']
@Partial['themes/default/footer', Model]
@EndSection