RSS take 2 / page edit
RSS now generates for /feed, and ?format[atom|rss|rss2]; page list shows updated date/time, page edit page loads
This commit is contained in:
parent
7538b9ef26
commit
2574501ccd
|
@ -3,6 +3,7 @@
|
|||
open FSharp.Interop.Dynamic
|
||||
open myWebLog.Entities
|
||||
open Rethink
|
||||
open RethinkDb.Driver.Ast
|
||||
open System.Dynamic
|
||||
|
||||
let private r = RethinkDb.Driver.RethinkDB.R
|
||||
|
@ -11,14 +12,18 @@ let private r = RethinkDb.Driver.RethinkDB.R
|
|||
let private page (webLogId : string) (pageId : string) =
|
||||
r.Table(Table.Page)
|
||||
.Get(pageId)
|
||||
.Filter(fun p -> p.["webLogId"].Eq(webLogId))
|
||||
.Filter(ReqlFunction1(fun p -> upcast p.["webLogId"].Eq(webLogId)))
|
||||
|
||||
/// Get a page by its Id
|
||||
let tryFindPage conn webLogId pageId : Page option =
|
||||
match (page webLogId pageId)
|
||||
let tryFindPage conn webLogId pageId =
|
||||
match r.Table(Table.Page)
|
||||
.Get(pageId)
|
||||
.RunAtomAsync<Page>(conn) |> await |> box with
|
||||
| null -> None
|
||||
| page -> Some <| unbox page
|
||||
| page -> let pg : Page = unbox page
|
||||
match pg.webLogId = webLogId with
|
||||
| true -> Some pg
|
||||
| _ -> None
|
||||
|
||||
/// Get a page by its Id (excluding revisions)
|
||||
let tryFindPageWithoutRevisions conn webLogId pageId : Page option =
|
||||
|
|
|
@ -91,7 +91,7 @@
|
|||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="RethinkDb.Driver">
|
||||
<HintPath>..\packages\RethinkDb.Driver.2.3.8\lib\net45\RethinkDb.Driver.dll</HintPath>
|
||||
<HintPath>..\packages\RethinkDb.Driver.2.3.9\lib\net45\RethinkDb.Driver.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
|
|
|
@ -6,5 +6,5 @@
|
|||
<package id="FSharp.Core" version="4.0.0.1" targetFramework="net452" />
|
||||
<package id="FSharp.Interop.Dynamic" version="3.0.0.0" targetFramework="net452" />
|
||||
<package id="Newtonsoft.Json" version="9.0.1" targetFramework="net452" />
|
||||
<package id="RethinkDb.Driver" version="2.3.8" targetFramework="net452" />
|
||||
<package id="RethinkDb.Driver" version="2.3.9" targetFramework="net452" />
|
||||
</packages>
|
|
@ -22,7 +22,8 @@ type PageModule(conn : IConnection, clock : IClock) as this =
|
|||
/// List all pages
|
||||
member this.PageList () =
|
||||
this.RequiresAccessLevel AuthorizationLevel.Administrator
|
||||
let model = PagesModel(this.Context, this.WebLog, findAllPages conn this.WebLog.id)
|
||||
let model = PagesModel(this.Context, this.WebLog, (findAllPages conn this.WebLog.id
|
||||
|> List.map (fun p -> PageForDisplay(this.WebLog, p))))
|
||||
model.pageTitle <- Resources.Pages
|
||||
upcast this.View.["admin/page/list", model]
|
||||
|
||||
|
@ -42,7 +43,7 @@ type PageModule(conn : IConnection, clock : IClock) as this =
|
|||
model.pageTitle <- match pageId with
|
||||
| "new" -> Resources.AddNewPage
|
||||
| _ -> Resources.EditPage
|
||||
upcast this.View.["admin/page/edit"]
|
||||
upcast this.View.["admin/page/edit", model]
|
||||
| None -> this.NotFound ()
|
||||
|
||||
/// Save a page
|
||||
|
|
|
@ -25,6 +25,37 @@ type PostModule(conn : IConnection, clock : IClock) as this =
|
|||
/// Convert a list of posts to a list of posts for display
|
||||
let forDisplay posts = posts |> List.map (fun post -> PostForDisplay(this.WebLog, post))
|
||||
|
||||
/// Generate an RSS/Atom feed of the latest posts
|
||||
let generateFeed format : obj =
|
||||
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)
|
||||
|
||||
do
|
||||
this.Get .["/" ] <- fun _ -> this.HomePage ()
|
||||
this.Get .["/{permalink*}" ] <- fun parms -> this.CatchAll (downcast parms)
|
||||
|
@ -33,7 +64,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 .["/feed" ] <- fun _ -> this.Feed ()
|
||||
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)
|
||||
|
@ -137,38 +168,14 @@ type PostModule(conn : IConnection, clock : IClock) as this =
|
|||
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)
|
||||
member this.Feed () =
|
||||
let query = this.Request.Query :?> DynamicDictionary
|
||||
match query.ContainsKey "format" with
|
||||
| true -> match query.["format"].ToString () with
|
||||
| x when x = "atom" || x = "rss" -> generateFeed x
|
||||
| x when x = "rss2" -> generateFeed "rss"
|
||||
| _ -> this.Redirect "/feed" (MyWebLogModel(this.Context, this.WebLog))
|
||||
| _ -> generateFeed "rss"
|
||||
|
||||
// ---- Administer posts ----
|
||||
|
||||
|
|
|
@ -228,11 +228,23 @@ type PageModel(ctx, webLog, page) =
|
|||
member this.page : Page = page
|
||||
|
||||
|
||||
/// Wrapper for a page with additional properties
|
||||
type PageForDisplay(webLog, page) =
|
||||
/// The page
|
||||
member this.page : Page = page
|
||||
/// The time zone of the web log
|
||||
member this.timeZone = webLog.timeZone
|
||||
/// The date the page was last updated
|
||||
member this.updatedDate = FormatDateTime.longDate this.timeZone page.updatedOn
|
||||
/// The time the page was last updated
|
||||
member this.updatedTime = FormatDateTime.time this.timeZone page.updatedOn
|
||||
|
||||
|
||||
/// Model for page list display
|
||||
type PagesModel(ctx, webLog, pages) =
|
||||
inherit MyWebLogModel(ctx, webLog)
|
||||
/// The pages
|
||||
member this.pages : Page list = pages
|
||||
member this.pages : PageForDisplay list = pages
|
||||
|
||||
|
||||
/// Form used to edit a page
|
||||
|
|
|
@ -75,6 +75,10 @@
|
|||
<HintPath>..\packages\FSharp.Formatting.2.14.4\lib\net40\CSharpFormat.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="Dynamitey">
|
||||
<HintPath>..\packages\Dynamitey.1.0.2.0\lib\net40\Dynamitey.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="FSharp.CodeFormat">
|
||||
<HintPath>..\packages\FSharp.Formatting.2.14.4\lib\net40\FSharp.CodeFormat.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
|
@ -91,6 +95,10 @@
|
|||
<HintPath>..\packages\FSharp.Formatting.2.14.4\lib\net40\FSharp.Formatting.Common.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="FSharp.Interop.Dynamic">
|
||||
<HintPath>..\packages\FSharp.Interop.Dynamic.3.0.0.0\lib\portable-net45+sl50+win\FSharp.Interop.Dynamic.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="FSharp.Literate">
|
||||
<HintPath>..\packages\FSharp.Formatting.2.14.4\lib\net40\FSharp.Literate.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
|
@ -137,7 +145,7 @@
|
|||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="RethinkDb.Driver">
|
||||
<HintPath>..\packages\RethinkDb.Driver.2.3.8\lib\net45\RethinkDb.Driver.dll</HintPath>
|
||||
<HintPath>..\packages\RethinkDb.Driver.2.3.9\lib\net45\RethinkDb.Driver.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="Suave">
|
||||
|
|
|
@ -2,9 +2,11 @@
|
|||
<packages>
|
||||
<package id="Common.Logging" version="3.3.1" targetFramework="net452" />
|
||||
<package id="Common.Logging.Core" version="3.3.1" targetFramework="net452" />
|
||||
<package id="Dynamitey" version="1.0.2.0" targetFramework="net452" />
|
||||
<package id="FSharp.Compiler.Service" version="2.0.0.6" targetFramework="net452" />
|
||||
<package id="FSharp.Core" version="4.0.0.1" targetFramework="net452" />
|
||||
<package id="FSharp.Formatting" version="2.14.4" targetFramework="net452" />
|
||||
<package id="FSharp.Interop.Dynamic" version="3.0.0.0" targetFramework="net452" />
|
||||
<package id="FSharpVSPowerTools.Core" version="2.3.0" targetFramework="net452" />
|
||||
<package id="Nancy" version="1.4.3" targetFramework="net452" />
|
||||
<package id="Nancy.Authentication.Forms" version="1.4.1" targetFramework="net452" />
|
||||
|
@ -12,6 +14,6 @@
|
|||
<package id="Nancy.Session.RethinkDB" version="0.8.6" targetFramework="net452" />
|
||||
<package id="Newtonsoft.Json" version="9.0.1" targetFramework="net452" />
|
||||
<package id="NodaTime" version="1.3.2" targetFramework="net452" />
|
||||
<package id="RethinkDb.Driver" version="2.3.8" targetFramework="net452" />
|
||||
<package id="RethinkDb.Driver" version="2.3.9" targetFramework="net452" />
|
||||
<package id="Suave" version="1.1.3" targetFramework="net452" />
|
||||
</packages>
|
|
@ -13,17 +13,12 @@
|
|||
@Each.pages
|
||||
<tr>
|
||||
<td>
|
||||
@Current.title<br />
|
||||
<a href="/@Current.permalink">@Translate.View</a>
|
||||
<a href="/page/@Current.id/edit">@Translate.Edit</a>
|
||||
<a href="javascript:void(0)" onclick="deletePage('@Current.id', '@Current.title')">@Translate.Delete</a>
|
||||
</td>
|
||||
<td>
|
||||
<!-- // TODO: make the formatting stuff work in a loop
|
||||
=theDate.format('MMM D, YYYY')
|
||||
br
|
||||
#{__("at")} #{theDate.format('h:mma')} -->
|
||||
@Current.page.title<br />
|
||||
<a href="/@Current.page.permalink">@Translate.View</a>
|
||||
<a href="/page/@Current.page.id/edit">@Translate.Edit</a>
|
||||
<a href="javascript:void(0)" onclick="deletePage('@Current.page.id', '@Current.title')">@Translate.Delete</a>
|
||||
</td>
|
||||
<td>@Current.updatedDate<br />@Translate.at @Current.updatedTime</td>
|
||||
</tr>
|
||||
@EndEach
|
||||
</table>
|
||||
|
|
Loading…
Reference in New Issue
Block a user