Added first cut of RSS feed; added links to head of default theme's
layout template
This commit is contained in:
Daniel J. Summers 2016-07-25 22:39:38 -05:00
parent d3712dc562
commit 7538b9ef26
4 changed files with 58 additions and 1 deletions

View File

@ -138,6 +138,24 @@ let tryFindPostByPriorPermalink conn (webLogId : string) (permalink : string) =
|> await |> await
|> Seq.tryHead |> Seq.tryHead
/// Get a set of posts for RSS
let findFeedPosts conn webLogId nbr : (Post * User option) list =
findPageOfPublishedPosts conn webLogId 1 nbr
|> List.map (fun post -> { post with categories = r.Table(Table.Category)
.GetAll(post.categoryIds |> List.toArray)
.OrderBy("name")
.Pluck("id", "name")
.RunListAsync<Category>(conn)
|> await
|> Seq.toList },
(match r.Table(Table.User)
.Get(post.authorId)
.RunAtomAsync<User>(conn)
|> await
|> box with
| null -> None
| user -> Some <| unbox user))
/// Save a post /// Save a post
let savePost conn post = let savePost conn post =
match post.id with match post.id with

View File

@ -11,8 +11,10 @@ open Nancy.Security
open Nancy.Session.Persistable open Nancy.Session.Persistable
open NodaTime open NodaTime
open RethinkDb.Driver.Net open RethinkDb.Driver.Net
open System
open System.ServiceModel.Syndication
/// Routes dealing with posts (including the home page, /tag, /category, and catch-all routes) /// Routes dealing with posts (including the home page, /tag, /category, RSS, and catch-all routes)
type PostModule(conn : IConnection, clock : IClock) as this = type PostModule(conn : IConnection, clock : IClock) as this =
inherit NancyModule() inherit NancyModule()
@ -31,6 +33,7 @@ type PostModule(conn : IConnection, clock : IClock) as this =
this.Get .["/category/{slug}/page/{page:int}"] <- 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}" ] <- fun parms -> this.TaggedPosts (downcast parms)
this.Get .["/tag/{tag}/page/{page:int}" ] <- fun parms -> this.TaggedPosts (downcast parms) this.Get .["/tag/{tag}/page/{page:int}" ] <- fun parms -> this.TaggedPosts (downcast parms)
this.Get .["/feed" ] <- fun parms -> this.GenerateFeed (downcast parms)
this.Get .["/posts/list" ] <- fun _ -> this.PostList 1 this.Get .["/posts/list" ] <- fun _ -> this.PostList 1
this.Get .["/posts/list/page/{page:int}" ] <- fun parms -> this.PostList (getPage <| downcast parms) 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.Get .["/post/{postId}/edit" ] <- fun parms -> this.EditPost (downcast parms)
@ -133,6 +136,39 @@ type PostModule(conn : IConnection, clock : IClock) as this =
model.subtitle <- Some <| sprintf "Posts tagged \"%s\"" tag model.subtitle <- Some <| sprintf "Posts tagged \"%s\"" tag
this.ThemedView "index" model this.ThemedView "index" model
/// Generate an RSS feed
member this.GenerateFeed (parameters : DynamicDictionary) =
let format = match parameters.ContainsKey "format" with // FIXME: format not coming through on query string
| true -> parameters.["format"].ToString ()
| _ -> "rss"
let posts = findFeedPosts conn this.WebLog.id 10
let feed =
SyndicationFeed(
this.WebLog.name, defaultArg this.WebLog.subtitle null,
Uri(sprintf "%s://%s" this.Request.Url.Scheme this.WebLog.urlBase), null,
(match posts |> List.tryHead with
| Some (post, _) -> Instant(post.updatedOn).ToDateTimeOffset ()
| _ -> System.DateTimeOffset(System.DateTime.MinValue)),
posts
|> List.map (fun (post, user) ->
let item =
SyndicationItem(
BaseUri = Uri(sprintf "%s://%s/%s" this.Request.Url.Scheme this.WebLog.urlBase post.permalink),
PublishDate = Instant(post.publishedOn).ToDateTimeOffset (),
LastUpdatedTime = Instant(post.updatedOn).ToDateTimeOffset (),
Title = TextSyndicationContent(post.title),
Content = TextSyndicationContent(post.text, TextSyndicationContentKind.Html))
user
|> Option.iter (fun u -> item.Authors.Add
(SyndicationPerson(u.userName, u.preferredName, defaultArg u.url null)))
post.categories
|> List.iter (fun c -> item.Categories.Add(SyndicationCategory(c.name)))
item))
let stream = new IO.MemoryStream()
Xml.XmlWriter.Create(stream)
|> match format with | "atom" -> feed.SaveAsAtom10 | _ -> feed.SaveAsRss20
stream.Position <- int64 0
upcast this.Response.FromStream(stream, sprintf "application/%s+xml" format)
// ---- Administer posts ---- // ---- Administer posts ----

View File

@ -148,6 +148,7 @@
<Reference Include="System.ComponentModel.DataAnnotations" /> <Reference Include="System.ComponentModel.DataAnnotations" />
<Reference Include="System.Core" /> <Reference Include="System.Core" />
<Reference Include="System.Numerics" /> <Reference Include="System.Numerics" />
<Reference Include="System.ServiceModel" />
<Reference Include="System.Web.Razor"> <Reference Include="System.Web.Razor">
<HintPath>..\packages\FSharp.Formatting.2.14.4\lib\net40\System.Web.Razor.dll</HintPath> <HintPath>..\packages\FSharp.Formatting.2.14.4\lib\net40\System.Web.Razor.dll</HintPath>
<Private>True</Private> <Private>True</Private>

View File

@ -8,6 +8,8 @@
<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="//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="/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" /> <link rel="stylesheet" type="text/css" href="//maxcdn.bootstrapcdn.com/font-awesome/4.3.0/css/font-awesome.min.css" />
<link rel="alternate" type="application/atom+xml" href="//@Model.webLog.urlBase/feed?format=atom" />
<link rel="alternate" type="application/rss+xml" href="//@Model.webLog.urlBase/feed" />
@Section['Head']; @Section['Head'];
</head> </head>
<body> <body>