From 7538b9ef26d08ff9d1ab27b165f81e1e21650ec3 Mon Sep 17 00:00:00 2001 From: "Daniel J. Summers" Date: Mon, 25 Jul 2016 22:39:38 -0500 Subject: [PATCH] RSS Added first cut of RSS feed; added links to head of default theme's layout template --- src/myWebLog.Data/Post.fs | 18 +++++++++ src/myWebLog.Web/PostModule.fs | 38 ++++++++++++++++++- src/myWebLog.Web/myWebLog.Web.fsproj | 1 + src/myWebLog/views/themes/default/layout.html | 2 + 4 files changed, 58 insertions(+), 1 deletion(-) diff --git a/src/myWebLog.Data/Post.fs b/src/myWebLog.Data/Post.fs index 34e5d04..8cf7a34 100644 --- a/src/myWebLog.Data/Post.fs +++ b/src/myWebLog.Data/Post.fs @@ -138,6 +138,24 @@ let tryFindPostByPriorPermalink conn (webLogId : string) (permalink : string) = |> await |> 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(conn) + |> await + |> Seq.toList }, + (match r.Table(Table.User) + .Get(post.authorId) + .RunAtomAsync(conn) + |> await + |> box with + | null -> None + | user -> Some <| unbox user)) + /// Save a post let savePost conn post = match post.id with diff --git a/src/myWebLog.Web/PostModule.fs b/src/myWebLog.Web/PostModule.fs index 669027a..5d24dfb 100644 --- a/src/myWebLog.Web/PostModule.fs +++ b/src/myWebLog.Web/PostModule.fs @@ -11,8 +11,10 @@ open Nancy.Security open Nancy.Session.Persistable open NodaTime 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 = 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 .["/tag/{tag}" ] <- 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/page/{page:int}" ] <- fun parms -> this.PostList (getPage <| 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 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 ---- diff --git a/src/myWebLog.Web/myWebLog.Web.fsproj b/src/myWebLog.Web/myWebLog.Web.fsproj index e20a015..79d71a0 100644 --- a/src/myWebLog.Web/myWebLog.Web.fsproj +++ b/src/myWebLog.Web/myWebLog.Web.fsproj @@ -148,6 +148,7 @@ + ..\packages\FSharp.Formatting.2.14.4\lib\net40\System.Web.Razor.dll True diff --git a/src/myWebLog/views/themes/default/layout.html b/src/myWebLog/views/themes/default/layout.html index 58eddbd..17ea0f5 100644 --- a/src/myWebLog/views/themes/default/layout.html +++ b/src/myWebLog/views/themes/default/layout.html @@ -8,6 +8,8 @@ + + @Section['Head'];