diff --git a/src/Settings.FSharpLint b/src/Settings.FSharpLint
new file mode 100644
index 0000000..d8e7530
--- /dev/null
+++ b/src/Settings.FSharpLint
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/src/myWebLog.Data/AssemblyInfo.fs b/src/myWebLog.Data/AssemblyInfo.fs
index 701d1ed..e5d523c 100644
--- a/src/myWebLog.Data/AssemblyInfo.fs
+++ b/src/myWebLog.Data/AssemblyInfo.fs
@@ -4,11 +4,11 @@ open System.Reflection
open System.Runtime.CompilerServices
open System.Runtime.InteropServices
-[]
+[]
[]
[]
[]
-[]
+[]
[]
[]
[]
diff --git a/src/myWebLog.Data/Category.fs b/src/myWebLog.Data/Category.fs
index d670029..f498fe8 100644
--- a/src/myWebLog.Data/Category.fs
+++ b/src/myWebLog.Data/Category.fs
@@ -1,8 +1,9 @@
-module myWebLog.Data.Category
+module MyWebLog.Data.Category
open FSharp.Interop.Dynamic
-open myWebLog.Entities
+open MyWebLog.Entities
open Rethink
+open RethinkDb.Driver.Ast
open System.Dynamic
let private r = RethinkDb.Driver.RethinkDB.R
@@ -11,18 +12,18 @@ let private r = RethinkDb.Driver.RethinkDB.R
let private category (webLogId : string) (catId : string) =
r.Table(Table.Category)
.Get(catId)
- .Filter(fun c -> c.["webLogId"].Eq(webLogId))
+ .Filter(fun c -> c.["WebLogId"].Eq(webLogId))
/// Sort categories by their name, with their children sorted below them, including an indent level
let sortCategories categories =
let rec getChildren (cat : Category) indent =
seq {
yield cat, indent
- for child in categories |> List.filter (fun c -> c.parentId = Some cat.id) do
+ for child in categories |> List.filter (fun c -> c.ParentId = Some cat.Id) do
yield! getChildren child (indent + 1)
}
categories
- |> List.filter (fun c -> c.parentId.IsNone)
+ |> List.filter (fun c -> c.ParentId.IsNone)
|> List.map (fun c -> getChildren c 0)
|> Seq.collect id
|> Seq.toList
@@ -30,8 +31,8 @@ let sortCategories categories =
/// Get all categories for a web log
let getAllCategories conn (webLogId : string) =
r.Table(Table.Category)
- .GetAll(webLogId).OptArg("index", "webLogId")
- .OrderBy("name")
+ .GetAll(webLogId).OptArg("index", "WebLogId")
+ .OrderBy("Name")
.RunListAsync(conn)
|> await
|> Seq.toList
@@ -46,28 +47,28 @@ let tryFindCategory conn webLogId catId : Category option =
/// Save a category
let saveCategory conn webLogId (cat : Category) =
- match cat.id with
- | "new" -> let newCat = { cat with id = string <| System.Guid.NewGuid()
- webLogId = webLogId }
+ match cat.Id with
+ | "new" -> let newCat = { cat with Id = string <| System.Guid.NewGuid()
+ WebLogId = webLogId }
r.Table(Table.Category)
.Insert(newCat)
.RunResultAsync(conn) |> await |> ignore
- newCat.id
+ newCat.Id
| _ -> let upd8 = ExpandoObject()
- upd8?name <- cat.name
- upd8?slug <- cat.slug
- upd8?description <- cat.description
- upd8?parentId <- cat.parentId
- (category webLogId cat.id)
+ upd8?Name <- cat.Name
+ upd8?Slug <- cat.Slug
+ upd8?Description <- cat.Description
+ upd8?ParentId <- cat.ParentId
+ (category webLogId cat.Id)
.Update(upd8)
.RunResultAsync(conn) |> await |> ignore
- cat.id
+ cat.Id
/// Remove a category from a given parent
let removeCategoryFromParent conn webLogId parentId catId =
match tryFindCategory conn webLogId parentId with
| Some parent -> let upd8 = ExpandoObject()
- upd8?children <- parent.children
+ upd8?Children <- parent.Children
|> List.filter (fun childId -> childId <> catId)
(category webLogId parentId)
.Update(upd8)
@@ -78,7 +79,7 @@ let removeCategoryFromParent conn webLogId parentId catId =
let addCategoryToParent conn webLogId parentId catId =
match tryFindCategory conn webLogId parentId with
| Some parent -> let upd8 = ExpandoObject()
- upd8?children <- catId :: parent.children
+ upd8?Children <- catId :: parent.Children
(category webLogId parentId)
.Update(upd8)
.RunResultAsync(conn) |> await |> ignore
@@ -87,40 +88,40 @@ let addCategoryToParent conn webLogId parentId catId =
/// Delete a category
let deleteCategory conn cat =
// Remove the category from its parent
- match cat.parentId with
- | Some parentId -> removeCategoryFromParent conn cat.webLogId parentId cat.id
+ match cat.ParentId with
+ | Some parentId -> removeCategoryFromParent conn cat.WebLogId parentId cat.Id
| None -> ()
// Move this category's children to its parent
let newParent = ExpandoObject()
- newParent?parentId <- cat.parentId
- cat.children
- |> List.iter (fun childId -> (category cat.webLogId childId)
+ newParent?ParentId <- cat.ParentId
+ cat.Children
+ |> List.iter (fun childId -> (category cat.WebLogId childId)
.Update(newParent)
.RunResultAsync(conn) |> await |> ignore)
// Remove the category from posts where it is assigned
r.Table(Table.Post)
- .GetAll(cat.webLogId).OptArg("index", "webLogId")
- .Filter(fun p -> p.["categoryIds"].Contains(cat.id))
+ .GetAll(cat.WebLogId).OptArg("index", "WebLogId")
+ .Filter(ReqlFunction1(fun p -> upcast p.["CategoryIds"].Contains(cat.Id)))
.RunCursorAsync(conn)
|> await
|> Seq.toList
|> List.iter (fun post -> let newCats = ExpandoObject()
- newCats?categoryIds <- post.categoryIds
- |> List.filter (fun c -> c <> cat.id)
+ newCats?CategoryIds <- post.CategoryIds
+ |> List.filter (fun c -> c <> cat.Id)
r.Table(Table.Post)
- .Get(post.id)
+ .Get(post.Id)
.Update(newCats)
.RunResultAsync(conn) |> await |> ignore)
// Now, delete the category
r.Table(Table.Category)
- .Get(cat.id)
+ .Get(cat.Id)
.Delete()
.RunResultAsync(conn) |> await |> ignore
/// Get a category by its slug
let tryFindCategoryBySlug conn (webLogId : string) (slug : string) =
r.Table(Table.Category)
- .GetAll(r.Array(webLogId, slug)).OptArg("index", "slug")
+ .GetAll(r.Array(webLogId, slug)).OptArg("index", "Slug")
.RunCursorAsync(conn)
|> await
|> Seq.tryHead
diff --git a/src/myWebLog.Data/DataConfig.fs b/src/myWebLog.Data/DataConfig.fs
index e4709ff..49325de 100644
--- a/src/myWebLog.Data/DataConfig.fs
+++ b/src/myWebLog.Data/DataConfig.fs
@@ -1,47 +1,59 @@
-namespace myWebLog.Data
+namespace MyWebLog.Data
open RethinkDb.Driver
open RethinkDb.Driver.Net
open Newtonsoft.Json
/// Data configuration
-type DataConfig = {
- /// The hostname for the RethinkDB server
- hostname : string
- /// The port for the RethinkDB server
- port : int
- /// The authorization key to use when connecting to the server
- authKey : string
- /// How long an attempt to connect to the server should wait before giving up
- timeout : int
- /// The name of the default database to use on the connection
- database : string
- /// A connection to the RethinkDB server using the configuration in this object
- conn : IConnection
- }
+type DataConfig =
+ { /// The hostname for the RethinkDB server
+ []
+ Hostname : string
+ /// The port for the RethinkDB server
+ []
+ Port : int
+ /// The authorization key to use when connecting to the server
+ []
+ AuthKey : string
+ /// How long an attempt to connect to the server should wait before giving up
+ []
+ Timeout : int
+ /// The name of the default database to use on the connection
+ []
+ Database : string
+ /// A connection to the RethinkDB server using the configuration in this object
+ []
+ Conn : IConnection }
with
/// Create a data configuration from JSON
- static member fromJson json =
- let mutable cfg = JsonConvert.DeserializeObject json
- cfg <- match cfg.hostname with
- | null -> { cfg with hostname = RethinkDBConstants.DefaultHostname }
- | _ -> cfg
- cfg <- match cfg.port with
- | 0 -> { cfg with port = RethinkDBConstants.DefaultPort }
- | _ -> cfg
- cfg <- match cfg.authKey with
- | null -> { cfg with authKey = RethinkDBConstants.DefaultAuthkey }
- | _ -> cfg
- cfg <- match cfg.timeout with
- | 0 -> { cfg with timeout = RethinkDBConstants.DefaultTimeout }
- | _ -> cfg
- cfg <- match cfg.database with
- | null -> { cfg with database = RethinkDBConstants.DefaultDbName }
- | _ -> cfg
- { cfg with conn = RethinkDB.R.Connection()
- .Hostname(cfg.hostname)
- .Port(cfg.port)
- .AuthKey(cfg.authKey)
- .Db(cfg.database)
- .Timeout(cfg.timeout)
- .Connect() }
+ static member FromJson json =
+ let ensureHostname cfg = match cfg.Hostname with
+ | null -> { cfg with Hostname = RethinkDBConstants.DefaultHostname }
+ | _ -> cfg
+ let ensurePort cfg = match cfg.Port with
+ | 0 -> { cfg with Port = RethinkDBConstants.DefaultPort }
+ | _ -> cfg
+ let ensureAuthKey cfg = match cfg.AuthKey with
+ | null -> { cfg with AuthKey = RethinkDBConstants.DefaultAuthkey }
+ | _ -> cfg
+ let ensureTimeout cfg = match cfg.Timeout with
+ | 0 -> { cfg with Timeout = RethinkDBConstants.DefaultTimeout }
+ | _ -> cfg
+ let ensureDatabase cfg = match cfg.Database with
+ | null -> { cfg with Database = RethinkDBConstants.DefaultDbName }
+ | _ -> cfg
+ let connect cfg = { cfg with Conn = RethinkDB.R.Connection()
+ .Hostname(cfg.Hostname)
+ .Port(cfg.Port)
+ .AuthKey(cfg.AuthKey)
+ .Db(cfg.Database)
+ .Timeout(cfg.Timeout)
+ .Connect() }
+ JsonConvert.DeserializeObject json
+ |> ensureHostname
+ |> ensurePort
+ |> ensureAuthKey
+ |> ensureTimeout
+ |> ensureDatabase
+ |> connect
+
diff --git a/src/myWebLog.Data/Entities.fs b/src/myWebLog.Data/Entities.fs
index bde0a9b..df767ad 100644
--- a/src/myWebLog.Data/Entities.fs
+++ b/src/myWebLog.Data/Entities.fs
@@ -1,4 +1,4 @@
-namespace myWebLog.Entities
+namespace MyWebLog.Entities
open Newtonsoft.Json
@@ -37,261 +37,244 @@ module CommentStatus =
// ---- Entities ----
/// A revision of a post or page
-type Revision = {
- /// The instant which this revision was saved
- asOf : int64
- /// The source language
- sourceType : string
- /// The text
- text : string
- }
+type Revision =
+ { /// The instant which this revision was saved
+ AsOf : int64
+ /// The source language
+ SourceType : string
+ /// The text
+ Text : string }
with
/// An empty revision
- static member empty =
- { asOf = int64 0
- sourceType = RevisionSource.HTML
- text = "" }
+ static member Empty =
+ { AsOf = int64 0
+ SourceType = RevisionSource.HTML
+ Text = "" }
/// A page with static content
-type Page = {
- /// The Id
- id : string
- /// The Id of the web log to which this page belongs
- webLogId : string
- /// The Id of the author of this page
- authorId : string
- /// The title of the page
- title : string
- /// The link at which this page is displayed
- permalink : string
- /// The instant this page was published
- publishedOn : int64
- /// The instant this page was last updated
- updatedOn : int64
- /// Whether this page shows as part of the web log's navigation
- showInPageList : bool
- /// The current text of the page
- text : string
- /// Revisions of this page
- revisions : Revision list
- }
+type Page =
+ { /// The Id
+ Id : string
+ /// The Id of the web log to which this page belongs
+ WebLogId : string
+ /// The Id of the author of this page
+ AuthorId : string
+ /// The title of the page
+ Title : string
+ /// The link at which this page is displayed
+ Permalink : string
+ /// The instant this page was published
+ PublishedOn : int64
+ /// The instant this page was last updated
+ UpdatedOn : int64
+ /// Whether this page shows as part of the web log's navigation
+ ShowInPageList : bool
+ /// The current text of the page
+ Text : string
+ /// Revisions of this page
+ Revisions : Revision list }
with
- static member empty =
- { id = ""
- webLogId = ""
- authorId = ""
- title = ""
- permalink = ""
- publishedOn = int64 0
- updatedOn = int64 0
- showInPageList = false
- text = ""
- revisions = List.empty
+ static member Empty =
+ { Id = ""
+ WebLogId = ""
+ AuthorId = ""
+ Title = ""
+ Permalink = ""
+ PublishedOn = int64 0
+ UpdatedOn = int64 0
+ ShowInPageList = false
+ Text = ""
+ Revisions = List.empty
}
/// An entry in the list of pages displayed as part of the web log (derived via query)
-type PageListEntry = {
- permalink : string
- title : string
- }
+type PageListEntry =
+ { Permalink : string
+ Title : string }
/// A web log
-type WebLog = {
- /// The Id
- id : string
- /// The name
- name : string
- /// The subtitle
- subtitle : string option
- /// The default page ("posts" or a page Id)
- defaultPage : string
- /// The path of the theme (within /views/themes)
- themePath : string
- /// The URL base
- urlBase : string
- /// The time zone in which dates/times should be displayed
- timeZone : string
- /// A list of pages to be rendered as part of the site navigation
- []
- pageList : PageListEntry list
- }
+type WebLog =
+ { /// The Id
+ Id : string
+ /// The name
+ Name : string
+ /// The subtitle
+ Subtitle : string option
+ /// The default page ("posts" or a page Id)
+ DefaultPage : string
+ /// The path of the theme (within /views/themes)
+ ThemePath : string
+ /// The URL base
+ UrlBase : string
+ /// The time zone in which dates/times should be displayed
+ TimeZone : string
+ /// A list of pages to be rendered as part of the site navigation (not stored)
+ PageList : PageListEntry list }
with
/// An empty web log
- static member empty =
- { id = ""
- name = ""
- subtitle = None
- defaultPage = ""
- themePath = "default"
- urlBase = ""
- timeZone = "America/New_York"
- pageList = List.empty
- }
+ static member Empty =
+ { Id = ""
+ Name = ""
+ Subtitle = None
+ DefaultPage = ""
+ ThemePath = "default"
+ UrlBase = ""
+ TimeZone = "America/New_York"
+ PageList = List.empty }
/// An authorization between a user and a web log
-type Authorization = {
- /// The Id of the web log to which this authorization grants access
- webLogId : string
- /// The level of access granted by this authorization
- level : string
- }
+type Authorization =
+ { /// The Id of the web log to which this authorization grants access
+ WebLogId : string
+ /// The level of access granted by this authorization
+ Level : string }
/// A user of myWebLog
-type User = {
- /// The Id
- id : string
- /// The user name (e-mail address)
- userName : string
- /// The first name
- firstName : string
- /// The last name
- lastName : string
- /// The user's preferred name
- preferredName : string
- /// The hash of the user's password
- passwordHash : string
- /// The URL of the user's personal site
- url : string option
- /// The user's authorizations
- authorizations : Authorization list
- }
+type User =
+ { /// The Id
+ Id : string
+ /// The user name (e-mail address)
+ UserName : string
+ /// The first name
+ FirstName : string
+ /// The last name
+ LastName : string
+ /// The user's preferred name
+ PreferredName : string
+ /// The hash of the user's password
+ PasswordHash : string
+ /// The URL of the user's personal site
+ Url : string option
+ /// The user's authorizations
+ Authorizations : Authorization list }
with
/// An empty user
- static member empty =
- { id = ""
- userName = ""
- firstName = ""
- lastName = ""
- preferredName = ""
- passwordHash = ""
- url = None
- authorizations = List.empty
- }
+ static member Empty =
+ { Id = ""
+ UserName = ""
+ FirstName = ""
+ LastName = ""
+ PreferredName = ""
+ PasswordHash = ""
+ Url = None
+ Authorizations = List.empty }
/// Claims for this user
[]
- member this.claims = this.authorizations
- |> List.map (fun auth -> sprintf "%s|%s" auth.webLogId auth.level)
+ member this.Claims = this.Authorizations
+ |> List.map (fun auth -> sprintf "%s|%s" auth.WebLogId auth.Level)
/// A category to which posts may be assigned
-type Category = {
- /// The Id
- id : string
- /// The Id of the web log to which this category belongs
- webLogId : string
- /// The displayed name
- name : string
- /// The slug (used in category URLs)
- slug : string
- /// A longer description of the category
- description : string option
- /// The parent Id of this category (if a subcategory)
- parentId : string option
- /// The categories for which this category is the parent
- children : string list
- }
+type Category =
+ { /// The Id
+ Id : string
+ /// The Id of the web log to which this category belongs
+ WebLogId : string
+ /// The displayed name
+ Name : string
+ /// The slug (used in category URLs)
+ Slug : string
+ /// A longer description of the category
+ Description : string option
+ /// The parent Id of this category (if a subcategory)
+ ParentId : string option
+ /// The categories for which this category is the parent
+ Children : string list }
with
/// An empty category
static member empty =
- { id = "new"
- webLogId = ""
- name = ""
- slug = ""
- description = None
- parentId = None
- children = List.empty
- }
+ { Id = "new"
+ WebLogId = ""
+ Name = ""
+ Slug = ""
+ Description = None
+ ParentId = None
+ Children = List.empty }
/// A comment (applies to a post)
-type Comment = {
- /// The Id
- id : string
- /// The Id of the post to which this comment applies
- postId : string
- /// The Id of the comment to which this comment is a reply
- inReplyToId : string option
- /// The name of the commentor
- name : string
- /// The e-mail address of the commentor
- email : string
- /// The URL of the commentor's personal website
- url : string option
- /// The status of the comment
- status : string
- /// The instant the comment was posted
- postedOn : int64
- /// The text of the comment
- text : string
- }
+type Comment =
+ { /// The Id
+ Id : string
+ /// The Id of the post to which this comment applies
+ PostId : string
+ /// The Id of the comment to which this comment is a reply
+ InReplyToId : string option
+ /// The name of the commentor
+ Name : string
+ /// The e-mail address of the commentor
+ Email : string
+ /// The URL of the commentor's personal website
+ Url : string option
+ /// The status of the comment
+ Status : string
+ /// The instant the comment was posted
+ PostedOn : int64
+ /// The text of the comment
+ Text : string }
with
- static member empty =
- { id = ""
- postId = ""
- inReplyToId = None
- name = ""
- email = ""
- url = None
- status = CommentStatus.Pending
- postedOn = int64 0
- text = ""
- }
+ static member Empty =
+ { Id = ""
+ PostId = ""
+ InReplyToId = None
+ Name = ""
+ Email = ""
+ Url = None
+ Status = CommentStatus.Pending
+ PostedOn = int64 0
+ Text = "" }
/// A post
-type Post = {
- /// The Id
- id : string
- /// The Id of the web log to which this post belongs
- webLogId : string
- /// The Id of the author of this post
- authorId : string
- /// The status
- status : string
- /// The title
- title : string
- /// The link at which the post resides
- permalink : string
- /// The instant on which the post was originally published
- publishedOn : int64
- /// The instant on which the post was last updated
- updatedOn : int64
- /// The text of the post
- text : string
- /// The Ids of the categories to which this is assigned
- categoryIds : string list
- /// The tags for the post
- tags : string list
- /// The permalinks at which this post may have once resided
- priorPermalinks : string list
- /// Revisions of this post
- revisions : Revision list
- /// The categories to which this is assigned
- []
- categories : Category list
- /// The comments
- []
- comments : Comment list
- }
+type Post =
+ { /// The Id
+ Id : string
+ /// The Id of the web log to which this post belongs
+ WebLogId : string
+ /// The Id of the author of this post
+ AuthorId : string
+ /// The status
+ Status : string
+ /// The title
+ Title : string
+ /// The link at which the post resides
+ Permalink : string
+ /// The instant on which the post was originally published
+ PublishedOn : int64
+ /// The instant on which the post was last updated
+ UpdatedOn : int64
+ /// The text of the post
+ Text : string
+ /// The Ids of the categories to which this is assigned
+ CategoryIds : string list
+ /// The tags for the post
+ Tags : string list
+ /// The permalinks at which this post may have once resided
+ PriorPermalinks : string list
+ /// Revisions of this post
+ Revisions : Revision list
+ /// The categories to which this is assigned (not stored in database)
+ Categories : Category list
+ /// The comments (not stored in database)
+ Comments : Comment list }
with
- static member empty =
- { id = "new"
- webLogId = ""
- authorId = ""
- status = PostStatus.Draft
- title = ""
- permalink = ""
- publishedOn = int64 0
- updatedOn = int64 0
- text = ""
- categoryIds = List.empty
- tags = List.empty
- priorPermalinks = List.empty
- revisions = List.empty
- categories = List.empty
- comments = List.empty
- }
+ static member Empty =
+ { Id = "new"
+ WebLogId = ""
+ AuthorId = ""
+ Status = PostStatus.Draft
+ Title = ""
+ Permalink = ""
+ PublishedOn = int64 0
+ UpdatedOn = int64 0
+ Text = ""
+ CategoryIds = List.empty
+ Tags = List.empty
+ PriorPermalinks = List.empty
+ Revisions = List.empty
+ Categories = List.empty
+ Comments = List.empty }
diff --git a/src/myWebLog.Data/Page.fs b/src/myWebLog.Data/Page.fs
index 6fdb455..9f2d299 100644
--- a/src/myWebLog.Data/Page.fs
+++ b/src/myWebLog.Data/Page.fs
@@ -1,7 +1,7 @@
-module myWebLog.Data.Page
+module MyWebLog.Data.Page
open FSharp.Interop.Dynamic
-open myWebLog.Entities
+open MyWebLog.Entities
open Rethink
open RethinkDb.Driver.Ast
open System.Dynamic
@@ -12,7 +12,7 @@ let private r = RethinkDb.Driver.RethinkDB.R
let private page (webLogId : string) (pageId : string) =
r.Table(Table.Page)
.Get(pageId)
- .Filter(ReqlFunction1(fun p -> upcast p.["webLogId"].Eq(webLogId)))
+ .Filter(ReqlFunction1(fun p -> upcast p.["WebLogId"].Eq(webLogId)))
/// Get a page by its Id
let tryFindPage conn webLogId pageId =
@@ -21,14 +21,14 @@ let tryFindPage conn webLogId pageId =
.RunAtomAsync(conn) |> await |> box with
| null -> None
| page -> let pg : Page = unbox page
- match pg.webLogId = webLogId with
+ match pg.WebLogId = webLogId with
| true -> Some pg
| _ -> None
/// Get a page by its Id (excluding revisions)
let tryFindPageWithoutRevisions conn webLogId pageId : Page option =
match (page webLogId pageId)
- .Without("revisions")
+ .Without("Revisions")
.RunAtomAsync(conn) |> await |> box with
| null -> None
| page -> Some <| unbox page
@@ -36,8 +36,8 @@ 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(r.Array(webLogId, permalink)).OptArg("index", "permalink")
- .Without("revisions")
+ .GetAll(r.Array(webLogId, permalink)).OptArg("index", "Permalink")
+ .Without("Revisions")
.RunCursorAsync(conn)
|> await
|> Seq.tryHead
@@ -45,32 +45,32 @@ let tryFindPageByPermalink conn (webLogId : string) (permalink : string) =
/// Get a list of all pages (excludes page text and revisions)
let findAllPages conn (webLogId : string) =
r.Table(Table.Page)
- .GetAll(webLogId).OptArg("index", "webLogId")
- .OrderBy("title")
- .Without("text", "revisions")
+ .GetAll(webLogId).OptArg("index", "WebLogId")
+ .OrderBy("Title")
+ .Without("Text", "Revisions")
.RunListAsync(conn)
|> await
|> Seq.toList
/// Save a page
let savePage conn (pg : Page) =
- match pg.id with
- | "new" -> let newPage = { pg with id = string <| System.Guid.NewGuid() }
+ match pg.Id with
+ | "new" -> let newPage = { pg with Id = string <| System.Guid.NewGuid() }
r.Table(Table.Page)
.Insert(page)
.RunResultAsync(conn) |> await |> ignore
- newPage.id
+ newPage.Id
| _ -> let upd8 = ExpandoObject()
- upd8?title <- pg.title
- upd8?permalink <- pg.permalink
- upd8?publishedOn <- pg.publishedOn
- upd8?updatedOn <- pg.updatedOn
- upd8?text <- pg.text
- upd8?revisions <- pg.revisions
- (page pg.webLogId pg.id)
+ upd8?Title <- pg.Title
+ upd8?Permalink <- pg.Permalink
+ upd8?PublishedOn <- pg.PublishedOn
+ upd8?UpdatedOn <- pg.UpdatedOn
+ upd8?Text <- pg.Text
+ upd8?Revisions <- pg.Revisions
+ (page pg.WebLogId pg.Id)
.Update(upd8)
.RunResultAsync(conn) |> await |> ignore
- pg.id
+ pg.Id
/// Delete a page
let deletePage conn webLogId pageId =
diff --git a/src/myWebLog.Data/Post.fs b/src/myWebLog.Data/Post.fs
index 8cf7a34..5e916ce 100644
--- a/src/myWebLog.Data/Post.fs
+++ b/src/myWebLog.Data/Post.fs
@@ -1,7 +1,7 @@
-module myWebLog.Data.Post
+module MyWebLog.Data.Post
open FSharp.Interop.Dynamic
-open myWebLog.Entities
+open MyWebLog.Entities
open Rethink
open RethinkDb.Driver.Ast
open System.Dynamic
@@ -11,22 +11,20 @@ let private r = RethinkDb.Driver.RethinkDB.R
/// Shorthand to select all published posts for a web log
let private publishedPosts (webLogId : string)=
r.Table(Table.Post)
- .GetAll(r.Array(webLogId, PostStatus.Published)).OptArg("index", "webLogAndStatus")
+ .GetAll(r.Array(webLogId, PostStatus.Published)).OptArg("index", "WebLogAndStatus")
/// Shorthand to sort posts by published date, slice for the given page, and return a list
let private toPostList conn pageNbr nbrPerPage (filter : ReqlExpr) =
filter
- .OrderBy(r.Desc("publishedOn"))
+ .OrderBy(r.Desc("PublishedOn"))
.Slice((pageNbr - 1) * nbrPerPage, pageNbr * nbrPerPage)
.RunListAsync(conn)
|> await
|> Seq.toList
/// Shorthand to get a newer or older post
-// TODO: older posts need to sort by published on DESC
-//let private adjacentPost conn post (theFilter : ReqlExpr -> ReqlExpr) (sort :ReqlExpr) : Post option =
-let private adjacentPost conn post (theFilter : ReqlExpr -> obj) (sort : obj) : Post option =
- (publishedPosts post.webLogId)
+let private adjacentPost conn post (theFilter : ReqlExpr -> obj) (sort : obj) =
+ (publishedPosts post.WebLogId)
.Filter(theFilter)
.OrderBy(sort)
.Limit(1)
@@ -48,45 +46,45 @@ 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(ReqlFunction1(fun p -> upcast 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(ReqlFunction1(fun p -> upcast 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
-let tryFindNewerPost conn post = newerPost conn post (fun p -> upcast p.["publishedOn"].Gt(post.publishedOn))
+let tryFindNewerPost conn post = newerPost conn post (fun p -> upcast p.["PublishedOn"].Gt(post.PublishedOn))
/// Try to get the next newest post assigned to the given category
let tryFindNewerCategorizedPost conn (categoryId : string) post =
- newerPost conn post (fun p -> upcast p.["publishedOn"].Gt(post.publishedOn)
- .And(p.["categoryIds"].Contains(categoryId)))
+ newerPost conn post (fun p -> upcast p.["PublishedOn"].Gt(post.PublishedOn)
+ .And(p.["CategoryIds"].Contains(categoryId)))
/// Try to get the next newest tagged post from the given tagged post
let tryFindNewerTaggedPost conn (tag : string) post =
- newerPost conn post (fun p -> upcast p.["publishedOn"].Gt(post.publishedOn).And(p.["tags"].Contains(tag)))
+ newerPost conn post (fun p -> upcast p.["PublishedOn"].Gt(post.PublishedOn).And(p.["Tags"].Contains(tag)))
/// Try to get the next oldest post from the given post
-let tryFindOlderPost conn post = olderPost conn post (fun p -> upcast p.["publishedOn"].Lt(post.publishedOn))
+let tryFindOlderPost conn post = olderPost conn post (fun p -> upcast p.["PublishedOn"].Lt(post.PublishedOn))
/// Try to get the next oldest post assigned to the given category
let tryFindOlderCategorizedPost conn (categoryId : string) post =
- olderPost conn post (fun p -> upcast p.["publishedOn"].Lt(post.publishedOn)
- .And(p.["categoryIds"].Contains(categoryId)))
+ olderPost conn post (fun p -> upcast p.["PublishedOn"].Lt(post.PublishedOn)
+ .And(p.["CategoryIds"].Contains(categoryId)))
/// Try to get the next oldest tagged post from the given tagged post
let tryFindOlderTaggedPost conn (tag : string) post =
- olderPost conn post (fun p -> upcast p.["publishedOn"].Lt(post.publishedOn).And(p.["tags"].Contains(tag)))
+ olderPost conn post (fun p -> upcast p.["PublishedOn"].Lt(post.PublishedOn).And(p.["Tags"].Contains(tag)))
/// Get a page of all posts in all statuses
let findPageOfAllPosts conn (webLogId : string) pageNbr nbrPerPage =
// FIXME: sort unpublished posts by their last updated date
r.Table(Table.Post)
- .GetAll(webLogId).OptArg("index", "webLogId")
- .OrderBy(r.Desc("publishedOn"))
+ .GetAll(webLogId).OptArg("index", "WebLogId")
+ .OrderBy(r.Desc("PublishedOn"))
.Slice((pageNbr - 1) * nbrPerPage, pageNbr * nbrPerPage)
.RunListAsync(conn)
|> await
@@ -96,7 +94,7 @@ let findPageOfAllPosts conn (webLogId : string) pageNbr nbrPerPage =
let tryFindPost conn webLogId postId : Post option =
match r.Table(Table.Post)
.Get(postId)
- .Filter(fun p -> p.["webLogId"].Eq(webLogId))
+ .Filter(ReqlFunction1(fun p -> upcast p.["WebLogId"].Eq(webLogId)))
.RunAtomAsync(conn)
|> box with
| null -> None
@@ -106,27 +104,22 @@ let tryFindPost conn webLogId postId : Post option =
// 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 =
- match r.Table(Table.Post)
- .GetAll(r.Array(webLogId, permalink)).OptArg("index", "permalink")
- .Filter(fun p -> p.["status"].Eq(PostStatus.Published))
- .Without("revisions")
- .RunCursorAsync(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(conn)
- |> await
- |> Seq.toList
- comments = r.Table(Table.Comment)
- .GetAll(p.id).OptArg("index", "postId")
- .OrderBy("postedOn")
- .RunListAsync(conn)
- |> await
- |> Seq.toList }
- | None -> None
+ r.Table(Table.Post)
+ .GetAll(r.Array(webLogId, permalink)).OptArg("index", "Permalink")
+ .Filter(fun p -> p.["Status"].Eq(PostStatus.Published))
+ .Without("Revisions")
+ .Merge(fun p -> r.HashMap("Categories", r.Table(Table.Category)
+ .GetAll(p.["CategoryIds"])
+ .Without("Children")
+ .OrderBy("Name")
+ .CoerceTo("array")))
+ .Merge(fun p -> r.HashMap("Comments", r.Table(Table.Comment)
+ .GetAll(p.["Id"]).OptArg("index", "PostId")
+ .OrderBy("PostedOn")
+ .CoerceTo("array")))
+ .RunCursorAsync(conn)
+ |> await
+ |> Seq.tryHead
/// Try to find a post by its prior permalink
let tryFindPostByPriorPermalink conn (webLogId : string) (permalink : string) =
@@ -140,34 +133,34 @@ let tryFindPostByPriorPermalink conn (webLogId : string) (permalink : string) =
/// 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))
+ (publishedPosts webLogId)
+ .Merge(fun post -> r.HashMap("Categories", r.Table(Table.Category)
+ .GetAll(post.["CategoryIds"])
+ .OrderBy("Name")
+ .Pluck("Id", "Name")
+ .CoerceTo("array")))
+ |> toPostList conn 1 nbr
+ |> List.map (fun post -> post, 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
- | "new" -> let newPost = { post with id = string <| System.Guid.NewGuid() }
+ match post.Id with
+ | "new" -> let newPost = { post with Id = string <| System.Guid.NewGuid() }
r.Table(Table.Post)
.Insert(newPost)
.RunResultAsync(conn)
|> ignore
- newPost.id
+ newPost.Id
| _ -> r.Table(Table.Post)
- .Get(post.id)
- .Replace(post)
+ .Get(post.Id)
+ .Replace( { post with Categories = List.empty
+ Comments = List.empty } )
.RunResultAsync(conn)
|> ignore
- post.id
+ post.Id
diff --git a/src/myWebLog.Data/Rethink.fs b/src/myWebLog.Data/Rethink.fs
index 1abb517..2d92d39 100644
--- a/src/myWebLog.Data/Rethink.fs
+++ b/src/myWebLog.Data/Rethink.fs
@@ -1,4 +1,4 @@
-module myWebLog.Data.Rethink
+module MyWebLog.Data.Rethink
open RethinkDb.Driver.Ast
open RethinkDb.Driver.Net
diff --git a/src/myWebLog.Data/SetUp.fs b/src/myWebLog.Data/SetUp.fs
index f48c8d1..041f9cd 100644
--- a/src/myWebLog.Data/SetUp.fs
+++ b/src/myWebLog.Data/SetUp.fs
@@ -1,4 +1,4 @@
-module myWebLog.Data.SetUp
+module MyWebLog.Data.SetUp
open Rethink
open RethinkDb.Driver.Ast
@@ -12,17 +12,17 @@ let private logStepDone () = Console.Out.WriteLine (" done.")
/// Ensure the myWebLog database exists
let checkDatabase (cfg : DataConfig) =
logStep "|> Checking database"
- let dbs = r.DbList().RunListAsync(cfg.conn) |> await
- match dbs.Contains cfg.database with
+ let dbs = r.DbList().RunListAsync(cfg.Conn) |> await
+ match dbs.Contains cfg.Database with
| true -> ()
- | _ -> logStepStart (sprintf " %s database not found - creating" cfg.database)
- r.DbCreate(cfg.database).RunResultAsync(cfg.conn) |> await |> ignore
+ | _ -> logStepStart (sprintf " %s database not found - creating" cfg.Database)
+ r.DbCreate(cfg.Database).RunResultAsync(cfg.Conn) |> await |> ignore
logStepDone ()
/// Ensure all required tables exist
let checkTables cfg =
logStep "|> Checking tables"
- let tables = r.Db(cfg.database).TableList().RunListAsync(cfg.conn) |> await
+ let tables = r.Db(cfg.Database).TableList().RunListAsync(cfg.Conn) |> await
[ Table.Category; Table.Comment; Table.Page; Table.Post; Table.User; Table.WebLog ]
|> List.map (fun tbl -> match tables.Contains tbl with
| true -> None
@@ -30,27 +30,27 @@ let checkTables cfg =
|> List.filter (fun create -> create.IsSome)
|> List.map (fun create -> create.Value)
|> List.iter (fun (tbl, create) -> logStepStart (sprintf " Creating table %s" tbl)
- create.RunResultAsync(cfg.conn) |> await |> ignore
+ create.RunResultAsync(cfg.Conn) |> await |> ignore
logStepDone ())
/// Shorthand to get the table
-let tbl cfg table = r.Db(cfg.database).Table(table)
+let tbl cfg table = r.Db(cfg.Database).Table(table)
/// Create the given index
let createIndex cfg table (index : string * (ReqlExpr -> obj) option) =
let idxName, idxFunc = index
logStepStart (sprintf """ Creating index "%s" on table %s""" idxName table)
match idxFunc with
- | Some f -> (tbl cfg table).IndexCreate(idxName, f).RunResultAsync(cfg.conn)
- | None -> (tbl cfg table).IndexCreate(idxName ).RunResultAsync(cfg.conn)
+ | Some f -> (tbl cfg table).IndexCreate(idxName, f).RunResultAsync(cfg.Conn)
+ | None -> (tbl cfg table).IndexCreate(idxName ).RunResultAsync(cfg.Conn)
|> await |> ignore
- (tbl cfg table).IndexWait(idxName).RunAtomAsync(cfg.conn) |> await |> ignore
+ (tbl cfg table).IndexWait(idxName).RunAtomAsync(cfg.Conn) |> await |> ignore
logStepDone ()
/// Ensure that the given indexes exist, and create them if required
let ensureIndexes cfg (indexes : (string * (string * (ReqlExpr -> obj) option) list) list) =
let ensureForTable tabl =
- let idx = (tbl cfg (fst tabl)).IndexList().RunListAsync(cfg.conn) |> await
+ let idx = (tbl cfg (fst tabl)).IndexList().RunListAsync(cfg.Conn) |> await
snd tabl
|> List.iter (fun index -> match idx.Contains (fst index) with
| true -> ()
@@ -68,21 +68,21 @@ let webLogField (name : string) : (ReqlExpr -> obj) option =
/// Ensure all the required indexes exist
let checkIndexes cfg =
logStep "|> Checking indexes"
- [ Table.Category, [ "webLogId", None
- "slug", webLogField "slug"
+ [ Table.Category, [ "WebLogId", None
+ "Slug", webLogField "Slug"
]
- Table.Comment, [ "postId", None
+ Table.Comment, [ "PostId", None
]
- Table.Page, [ "webLogId", None
- "permalink", webLogField "permalink"
+ Table.Page, [ "WebLogId", None
+ "Permalink", webLogField "Permalink"
]
- Table.Post, [ "webLogId", None
- "webLogAndStatus", webLogField "status"
- "permalink", webLogField "permalink"
+ Table.Post, [ "WebLogId", None
+ "WebLogAndStatus", webLogField "Status"
+ "Permalink", webLogField "Permalink"
]
- Table.User, [ "userName", None
+ Table.User, [ "UserName", None
]
- Table.WebLog, [ "urlBase", None
+ Table.WebLog, [ "UrlBase", None
]
]
|> ensureIndexes cfg
diff --git a/src/myWebLog.Data/Table.fs b/src/myWebLog.Data/Table.fs
index 8aec6ec..7191881 100644
--- a/src/myWebLog.Data/Table.fs
+++ b/src/myWebLog.Data/Table.fs
@@ -1,4 +1,4 @@
-module myWebLog.Data.Table
+module MyWebLog.Data.Table
/// The Category table
let Category = "Category"
diff --git a/src/myWebLog.Data/User.fs b/src/myWebLog.Data/User.fs
index d1fa356..59ce636 100644
--- a/src/myWebLog.Data/User.fs
+++ b/src/myWebLog.Data/User.fs
@@ -1,6 +1,6 @@
-module myWebLog.Data.User
+module MyWebLog.Data.User
-open myWebLog.Entities
+open MyWebLog.Entities
open Rethink
let private r = RethinkDb.Driver.RethinkDB.R
@@ -11,8 +11,8 @@ let private r = RethinkDb.Driver.RethinkDB.R
// http://rethinkdb.com/docs/secondary-indexes/java/ for more information.
let tryUserLogOn conn (email : string) (passwordHash : string) =
r.Table(Table.User)
- .GetAll(email).OptArg("index", "userName")
- .Filter(fun u -> u.["passwordHash"].Eq(passwordHash))
+ .GetAll(email).OptArg("index", "UserName")
+ .Filter(fun u -> u.["PasswordHash"].Eq(passwordHash))
.RunCursorAsync(conn)
|> await
|> Seq.tryHead
diff --git a/src/myWebLog.Data/WebLog.fs b/src/myWebLog.Data/WebLog.fs
index 0fd4ada..e2f77d5 100644
--- a/src/myWebLog.Data/WebLog.fs
+++ b/src/myWebLog.Data/WebLog.fs
@@ -1,43 +1,39 @@
-module myWebLog.Data.WebLog
+module MyWebLog.Data.WebLog
-open myWebLog.Entities
+open MyWebLog.Entities
open Rethink
+open RethinkDb.Driver.Ast
let private r = RethinkDb.Driver.RethinkDB.R
/// Counts of items displayed on the admin dashboard
-type DashboardCounts = {
- /// The number of pages for the web log
- pages : int
- /// The number of pages for the web log
- posts : int
- /// The number of categories for the web log
- categories : int
- }
+type DashboardCounts =
+ { /// The number of pages for the web log
+ Pages : int
+ /// The number of pages for the web log
+ Posts : int
+ /// The number of categories for the web log
+ Categories : int }
/// Detemine the web log by the URL base
-// 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 tryFindWebLogByUrlBase conn (urlBase : string) =
- let webLog = r.Table(Table.WebLog)
- .GetAll(urlBase).OptArg("index", "urlBase")
- .RunCursorAsync(conn)
- |> await
- |> Seq.tryHead
- match webLog with
- | Some w -> Some { w with pageList = r.Table(Table.Page)
- .GetAll(w.id).OptArg("index", "webLogId")
- .Filter(fun pg -> pg.["showInPageList"].Eq(true))
- .OrderBy("title")
- .Pluck("title", "permalink")
- .RunListAsync(conn) |> await |> Seq.toList }
- | None -> None
+ r.Table(Table.WebLog)
+ .GetAll(urlBase).OptArg("index", "urlBase")
+ .Merge(fun w -> r.HashMap("PageList", r.Table(Table.Page)
+ .GetAll(w.["Id"]).OptArg("index", "WebLogId")
+ .Filter(ReqlFunction1(fun pg -> upcast pg.["ShowInPageList"].Eq(true)))
+ .OrderBy("Title")
+ .Pluck("Title", "Permalink")
+ .CoerceTo("array")))
+ .RunCursorAsync(conn)
+ |> await
+ |> Seq.tryHead
/// Get counts for the admin dashboard
let findDashboardCounts conn (webLogId : string) =
- r.Expr( r.HashMap("pages", r.Table(Table.Page ).GetAll(webLogId).OptArg("index", "webLogId").Count()))
- .Merge(r.HashMap("posts", r.Table(Table.Post ).GetAll(webLogId).OptArg("index", "webLogId").Count()))
- .Merge(r.HashMap("categories", r.Table(Table.Category).GetAll(webLogId).OptArg("index", "webLogId").Count()))
+ r.Expr( r.HashMap("Pages", r.Table(Table.Page ).GetAll(webLogId).OptArg("index", "WebLogId").Count()))
+ .Merge(r.HashMap("Posts", r.Table(Table.Post ).GetAll(webLogId).OptArg("index", "WebLogId").Count()))
+ .Merge(r.HashMap("Categories", r.Table(Table.Category).GetAll(webLogId).OptArg("index", "WebLogId").Count()))
.RunAtomAsync(conn)
|> await
\ No newline at end of file
diff --git a/src/myWebLog.Data/myWebLog.Data.fsproj b/src/myWebLog.Data/myWebLog.Data.fsproj
index 4e52d36..9182b36 100644
--- a/src/myWebLog.Data/myWebLog.Data.fsproj
+++ b/src/myWebLog.Data/myWebLog.Data.fsproj
@@ -8,7 +8,7 @@
1fba0b84-b09e-4b16-b9b6-5730dea27192LibrarymyWebLog.Data
- myWebLog.Data
+ MyWebLog.Datav4.5.24.4.0.0true
@@ -22,7 +22,7 @@
bin\Debug\DEBUG;TRACE3
- bin\Debug\myWebLog.Data.xml
+ bin\Debug\MyWebLog.Data.xmlpdbonly
diff --git a/src/myWebLog.Resources/Resources.Designer.cs b/src/myWebLog.Resources/Resources.Designer.cs
index aef5363..1d165e9 100644
--- a/src/myWebLog.Resources/Resources.Designer.cs
+++ b/src/myWebLog.Resources/Resources.Designer.cs
@@ -8,7 +8,7 @@
//
//------------------------------------------------------------------------------
-namespace myWebLog {
+namespace MyWebLog {
using System;
@@ -39,7 +39,7 @@ namespace myWebLog {
public static global::System.Resources.ResourceManager ResourceManager {
get {
if (object.ReferenceEquals(resourceMan, null)) {
- global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("myWebLog.Resources", typeof(Resources).Assembly);
+ global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("MyWebLog.Resources", typeof(Resources).Assembly);
resourceMan = temp;
}
return resourceMan;
diff --git a/src/myWebLog.Resources/myWebLog.Resources.csproj b/src/myWebLog.Resources/myWebLog.Resources.csproj
index d9951b1..0d309fb 100644
--- a/src/myWebLog.Resources/myWebLog.Resources.csproj
+++ b/src/myWebLog.Resources/myWebLog.Resources.csproj
@@ -7,8 +7,8 @@
{A12EA8DA-88BC-4447-90CB-A0E2DCC37523}LibraryProperties
- myWebLog
- myWebLog.Resources
+ MyWebLog
+ MyWebLog.Resourcesv4.5.2512
diff --git a/src/myWebLog.Web/AdminModule.fs b/src/myWebLog.Web/AdminModule.fs
index 2fd3695..c89efa7 100644
--- a/src/myWebLog.Web/AdminModule.fs
+++ b/src/myWebLog.Web/AdminModule.fs
@@ -1,7 +1,7 @@
-namespace myWebLog
+namespace MyWebLog
-open myWebLog.Data.WebLog
-open myWebLog.Entities
+open MyWebLog.Data.WebLog
+open MyWebLog.Entities
open Nancy
open RethinkDb.Driver.Net
@@ -15,6 +15,6 @@ type AdminModule(conn : IConnection) as this =
/// 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
+ let model = DashboardModel(this.Context, this.WebLog, findDashboardCounts conn this.WebLog.Id)
+ model.PageTitle <- Resources.Dashboard
upcast this.View.["admin/dashboard", model]
diff --git a/src/myWebLog.Web/App.fs b/src/myWebLog.Web/App.fs
index b021a56..ddd5248 100644
--- a/src/myWebLog.Web/App.fs
+++ b/src/myWebLog.Web/App.fs
@@ -1,10 +1,10 @@
-module myWebLog.App
+module MyWebLog.App
-open myWebLog
-open myWebLog.Data
-open myWebLog.Data.SetUp
-open myWebLog.Data.WebLog
-open myWebLog.Entities
+open MyWebLog
+open MyWebLog.Data
+open MyWebLog.Data.SetUp
+open MyWebLog.Data.WebLog
+open MyWebLog.Entities
open Nancy
open Nancy.Authentication.Forms
open Nancy.Bootstrapper
@@ -25,7 +25,7 @@ open System.IO
open System.Text.RegularExpressions
/// Set up a database connection
-let cfg = try DataConfig.fromJson (System.IO.File.ReadAllText "data-config.json")
+let cfg = try DataConfig.FromJson (System.IO.File.ReadAllText "data-config.json")
with ex -> raise <| ApplicationException(Resources.ErrDataConfig, ex)
do
@@ -37,7 +37,7 @@ type TranslateTokenViewEngineMatcher() =
interface ISuperSimpleViewEngineMatcher with
member this.Invoke (content, model, host) =
regex.Replace(content, fun m -> let key = m.Groups.["TranslationKey"].Value
- match myWebLog.Resources.ResourceManager.GetString key with
+ match MyWebLog.Resources.ResourceManager.GetString key with
| null -> key
| xlat -> xlat)
@@ -54,8 +54,8 @@ type MyWebLogUserMapper(container : TinyIoCContainer) =
interface IUserMapper with
member this.GetUserFromIdentifier (identifier, context) =
- match context.Request.PersistableSession.GetOrDefault(Keys.User, User.empty) with
- | user when user.id = string identifier -> upcast MyWebLogUser(user.preferredName, user.claims)
+ match context.Request.PersistableSession.GetOrDefault(Keys.User, User.Empty) with
+ | user when user.Id = string identifier -> upcast MyWebLogUser(user.PreferredName, user.Claims)
| _ -> null
@@ -87,7 +87,7 @@ type MyWebLogBootstrapper() =
// Data configuration (both config and the connection; Nancy modules just need the connection)
container.Register(cfg)
|> ignore
- container.Register(cfg.conn)
+ container.Register(cfg.Conn)
|> ignore
// NodaTime
container.Register(SystemClock.Instance)
@@ -109,8 +109,8 @@ type MyWebLogBootstrapper() =
// CSRF
Csrf.Enable pipelines
// Sessions
- let sessions = RethinkDbSessionConfiguration(cfg.conn)
- sessions.Database <- cfg.database
+ let sessions = RethinkDbSessionConfiguration(cfg.Conn)
+ sessions.Database <- cfg.Database
PersistableSessions.Enable (pipelines, sessions)
()
@@ -128,17 +128,18 @@ let version =
type RequestEnvironment() =
interface IRequestStartup with
member this.Initialize (pipelines, context) =
- pipelines.BeforeRequest.AddItemToStartOfPipeline
- (fun ctx -> ctx.Items.[Keys.RequestStart] <- DateTime.Now.Ticks
- match tryFindWebLogByUrlBase cfg.conn ctx.Request.Url.HostName with
- | Some webLog -> ctx.Items.[Keys.WebLog] <- webLog
- | None -> ApplicationException
- (sprintf "%s %s" ctx.Request.Url.HostName Resources.ErrNotConfigured)
- |> raise
- ctx.Items.[Keys.Version] <- version
- null)
+ let establishEnv (ctx : NancyContext) =
+ ctx.Items.[Keys.RequestStart] <- DateTime.Now.Ticks
+ match tryFindWebLogByUrlBase cfg.Conn ctx.Request.Url.HostName with
+ | Some webLog -> ctx.Items.[Keys.WebLog] <- webLog
+ | None -> // TODO: redirect to domain set up page
+ ApplicationException (sprintf "%s %s" ctx.Request.Url.HostName Resources.ErrNotConfigured)
+ |> raise
+ ctx.Items.[Keys.Version] <- version
+ null
+ pipelines.BeforeRequest.AddItemToStartOfPipeline establishEnv
let app = OwinApp.ofMidFunc "/" (NancyMiddleware.UseNancy (NancyOptions()))
-let run () = startWebServer defaultConfig app // webPart
+let Run () = startWebServer defaultConfig app // webPart
diff --git a/src/myWebLog.Web/AssemblyInfo.fs b/src/myWebLog.Web/AssemblyInfo.fs
index ea73017..24c3209 100644
--- a/src/myWebLog.Web/AssemblyInfo.fs
+++ b/src/myWebLog.Web/AssemblyInfo.fs
@@ -4,11 +4,11 @@ open System.Reflection
open System.Runtime.CompilerServices
open System.Runtime.InteropServices
-[]
+[]
[]
[]
[]
-[]
+[]
[]
[]
[]
diff --git a/src/myWebLog.Web/CategoryModule.fs b/src/myWebLog.Web/CategoryModule.fs
index d72dfa8..a3352d6 100644
--- a/src/myWebLog.Web/CategoryModule.fs
+++ b/src/myWebLog.Web/CategoryModule.fs
@@ -1,7 +1,7 @@
-namespace myWebLog
+namespace MyWebLog
-open myWebLog.Data.Category
-open myWebLog.Entities
+open MyWebLog.Data.Category
+open MyWebLog.Entities
open Nancy
open Nancy.ModelBinding
open Nancy.Security
@@ -21,24 +21,21 @@ type CategoryModule(conn : IConnection) as this =
member this.CategoryList () =
this.RequiresAccessLevel AuthorizationLevel.Administrator
let model = CategoryListModel(this.Context, this.WebLog,
- (getAllCategories conn this.WebLog.id
- |> List.map (fun cat -> IndentedCategory.create cat (fun _ -> false))))
+ (getAllCategories conn this.WebLog.Id
+ |> List.map (fun cat -> IndentedCategory.Create cat (fun _ -> false))))
upcast this.View.["/admin/category/list", model]
/// Edit a category
member this.EditCategory (parameters : DynamicDictionary) =
this.RequiresAccessLevel AuthorizationLevel.Administrator
- let catId : string = downcast parameters.["id"]
+ let catId = parameters.["id"].ToString ()
match (match catId with
| "new" -> Some Category.empty
- | _ -> tryFindCategory conn this.WebLog.id catId) with
+ | _ -> tryFindCategory conn this.WebLog.Id catId) with
| Some cat -> let model = CategoryEditModel(this.Context, this.WebLog, cat)
- let cats = getAllCategories conn this.WebLog.id
- |> List.map (fun cat -> IndentedCategory.create cat
- (fun c -> c = defaultArg (fst cat).parentId ""))
- model.categories <- getAllCategories conn this.WebLog.id
- |> List.map (fun cat -> IndentedCategory.create cat
- (fun c -> c = defaultArg (fst cat).parentId ""))
+ model.Categories <- getAllCategories conn this.WebLog.Id
+ |> List.map (fun cat -> IndentedCategory.Create cat
+ (fun c -> c = defaultArg (fst cat).ParentId ""))
upcast this.View.["admin/category/edit", model]
| None -> this.NotFound ()
@@ -46,32 +43,32 @@ type CategoryModule(conn : IConnection) as this =
member this.SaveCategory (parameters : DynamicDictionary) =
this.ValidateCsrfToken ()
this.RequiresAccessLevel AuthorizationLevel.Administrator
- let catId : string = downcast parameters.["id"]
+ let catId = parameters.["id"].ToString ()
let form = this.Bind ()
let oldCat = match catId with
| "new" -> Some Category.empty
- | _ -> tryFindCategory conn this.WebLog.id catId
+ | _ -> tryFindCategory conn this.WebLog.Id catId
match oldCat with
- | Some old -> let cat = { old with name = form.name
- slug = form.slug
- description = match form.description with | "" -> None | d -> Some d
- parentId = match form.parentId with | "" -> None | p -> Some p }
- let newCatId = saveCategory conn this.WebLog.id cat
- match old.parentId = cat.parentId with
+ | Some old -> let cat = { old with Name = form.Name
+ Slug = form.Slug
+ Description = match form.Description with "" -> None | d -> Some d
+ ParentId = match form.ParentId with "" -> None | p -> Some p }
+ let newCatId = saveCategory conn this.WebLog.Id cat
+ match old.ParentId = cat.ParentId with
| true -> ()
- | _ -> match old.parentId with
- | Some parentId -> removeCategoryFromParent conn this.WebLog.id parentId newCatId
+ | _ -> match old.ParentId with
+ | Some parentId -> removeCategoryFromParent conn this.WebLog.Id parentId newCatId
| None -> ()
- match cat.parentId with
- | Some parentId -> addCategoryToParent conn this.WebLog.id parentId newCatId
+ match cat.ParentId with
+ | Some parentId -> addCategoryToParent conn this.WebLog.Id parentId newCatId
| None -> ()
let model = MyWebLogModel(this.Context, this.WebLog)
- { level = Level.Info
- message = System.String.Format
- (Resources.MsgCategoryEditSuccess,
- (match catId with | "new" -> Resources.Added | _ -> Resources.Updated))
- details = None }
- |> model.addMessage
+ { UserMessage.Empty with
+ Level = Level.Info
+ Message = System.String.Format
+ (Resources.MsgCategoryEditSuccess,
+ (match catId with | "new" -> Resources.Added | _ -> Resources.Updated)) }
+ |> model.AddMessage
this.Redirect (sprintf "/category/%s/edit" newCatId) model
| None -> this.NotFound ()
@@ -79,13 +76,12 @@ type CategoryModule(conn : IConnection) as this =
member this.DeleteCategory (parameters : DynamicDictionary) =
this.ValidateCsrfToken ()
this.RequiresAccessLevel AuthorizationLevel.Administrator
- let catId : string = downcast parameters.["id"]
- match tryFindCategory conn this.WebLog.id catId with
+ let catId = parameters.["id"].ToString ()
+ match tryFindCategory conn this.WebLog.Id catId with
| Some cat -> deleteCategory conn cat
let model = MyWebLogModel(this.Context, this.WebLog)
- { level = Level.Info
- message = System.String.Format(Resources.MsgCategoryDeleted, cat.name)
- details = None }
- |> model.addMessage
+ { UserMessage.Empty with Level = Level.Info
+ Message = System.String.Format(Resources.MsgCategoryDeleted, cat.Name) }
+ |> model.AddMessage
this.Redirect "/categories" model
| None -> this.NotFound ()
diff --git a/src/myWebLog.Web/Keys.fs b/src/myWebLog.Web/Keys.fs
index 282a3b7..18bc4e6 100644
--- a/src/myWebLog.Web/Keys.fs
+++ b/src/myWebLog.Web/Keys.fs
@@ -1,4 +1,4 @@
-module myWebLog.Keys
+module MyWebLog.Keys
let Messages = "messages"
diff --git a/src/myWebLog.Web/ModuleExtensions.fs b/src/myWebLog.Web/ModuleExtensions.fs
index e5deb9c..5b412b8 100644
--- a/src/myWebLog.Web/ModuleExtensions.fs
+++ b/src/myWebLog.Web/ModuleExtensions.fs
@@ -1,8 +1,7 @@
[]
-module myWebLog.ModuleExtensions
+module MyWebLog.ModuleExtensions
-open myWebLog
-open myWebLog.Entities
+open MyWebLog.Entities
open Nancy
open Nancy.Security
@@ -14,19 +13,19 @@ type NancyModule with
/// Display a view using the theme specified for the web log
member this.ThemedView view (model : MyWebLogModel) : obj =
- upcast this.View.[(sprintf "themes/%s/%s" this.WebLog.themePath view), model]
+ upcast this.View.[(sprintf "themes/%s/%s" this.WebLog.ThemePath view), model]
/// Return a 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) : obj =
- match List.length model.messages with
+ match List.length model.Messages with
| 0 -> ()
- | _ -> this.Session.[Keys.Messages] <- model.messages
+ | _ -> this.Session.[Keys.Messages] <- model.Messages
upcast this.Response.AsRedirect(url).WithStatusCode HttpStatusCode.TemporaryRedirect
/// Require a specific level of access for the current web log
member this.RequiresAccessLevel level =
this.RequiresAuthentication()
- this.RequiresClaims [| sprintf "%s|%s" this.WebLog.id level |]
+ this.RequiresClaims [| sprintf "%s|%s" this.WebLog.Id level |]
diff --git a/src/myWebLog.Web/PageModule.fs b/src/myWebLog.Web/PageModule.fs
index 9992b36..40fba18 100644
--- a/src/myWebLog.Web/PageModule.fs
+++ b/src/myWebLog.Web/PageModule.fs
@@ -1,8 +1,8 @@
-namespace myWebLog
+namespace MyWebLog
open FSharp.Markdown
-open myWebLog.Data.Page
-open myWebLog.Entities
+open MyWebLog.Data.Page
+open MyWebLog.Entities
open Nancy
open Nancy.ModelBinding
open Nancy.Security
@@ -22,9 +22,9 @@ 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
+ model.PageTitle <- Resources.Pages
upcast this.View.["admin/page/list", model]
/// Edit a page
@@ -32,17 +32,15 @@ type PageModule(conn : IConnection, clock : IClock) as this =
this.RequiresAccessLevel AuthorizationLevel.Administrator
let pageId = parameters.["id"].ToString ()
match (match pageId with
- | "new" -> Some Page.empty
- | _ -> tryFindPage conn this.WebLog.id pageId) with
- | Some page -> let rev = match page.revisions
- |> List.sortByDescending (fun r -> r.asOf)
+ | "new" -> Some Page.Empty
+ | _ -> tryFindPage conn this.WebLog.Id pageId) with
+ | Some page -> let rev = match page.Revisions
+ |> List.sortByDescending (fun r -> r.AsOf)
|> List.tryHead with
| Some r -> r
- | None -> Revision.empty
+ | None -> Revision.Empty
let model = EditPageModel(this.Context, this.WebLog, page, rev)
- model.pageTitle <- match pageId with
- | "new" -> Resources.AddNewPage
- | _ -> Resources.EditPage
+ model.PageTitle <- match pageId with "new" -> Resources.AddNewPage | _ -> Resources.EditPage
upcast this.View.["admin/page/edit", model]
| None -> this.NotFound ()
@@ -54,30 +52,28 @@ type PageModule(conn : IConnection, clock : IClock) as this =
let form = this.Bind ()
let now = clock.Now.Ticks
match (match pageId with
- | "new" -> Some Page.empty
- | _ -> tryFindPage conn this.WebLog.id pageId) with
- | Some p -> let page = match pageId with
- | "new" -> { p with webLogId = this.WebLog.id }
- | _ -> p
+ | "new" -> Some Page.Empty
+ | _ -> tryFindPage conn this.WebLog.Id pageId) with
+ | Some p -> let page = match pageId with "new" -> { p with WebLogId = this.WebLog.Id } | _ -> p
let pId = { p with
- title = form.title
- permalink = form.permalink
- publishedOn = match pageId with | "new" -> now | _ -> page.publishedOn
- updatedOn = now
- text = match form.source with
- | RevisionSource.Markdown -> Markdown.TransformHtml form.text
- | _ -> form.text
- revisions = { asOf = now
- sourceType = form.source
- text = form.text } :: page.revisions }
+ Title = form.Title
+ Permalink = form.Permalink
+ PublishedOn = match pageId with "new" -> now | _ -> page.PublishedOn
+ UpdatedOn = now
+ Text = match form.Source with
+ | RevisionSource.Markdown -> Markdown.TransformHtml form.Text
+ | _ -> form.Text
+ Revisions = { AsOf = now
+ SourceType = form.Source
+ Text = form.Text } :: page.Revisions }
|> savePage conn
let model = MyWebLogModel(this.Context, this.WebLog)
- { level = Level.Info
- message = System.String.Format
- (Resources.MsgPageEditSuccess,
- (match pageId with | "new" -> Resources.Added | _ -> Resources.Updated))
- details = None }
- |> model.addMessage
+ { UserMessage.Empty with
+ Level = Level.Info
+ Message = System.String.Format
+ (Resources.MsgPageEditSuccess,
+ (match pageId with | "new" -> Resources.Added | _ -> Resources.Updated)) }
+ |> model.AddMessage
this.Redirect (sprintf "/page/%s/edit" pId) model
| None -> this.NotFound ()
@@ -86,12 +82,11 @@ type PageModule(conn : IConnection, clock : IClock) as this =
this.ValidateCsrfToken ()
this.RequiresAccessLevel AuthorizationLevel.Administrator
let pageId = parameters.["id"].ToString ()
- match tryFindPageWithoutRevisions conn this.WebLog.id pageId with
- | Some page -> deletePage conn page.webLogId page.id
+ match tryFindPageWithoutRevisions conn this.WebLog.Id pageId with
+ | Some page -> deletePage conn page.WebLogId page.Id
let model = MyWebLogModel(this.Context, this.WebLog)
- { level = Level.Info
- message = Resources.MsgPageDeleted
- details = None }
- |> model.addMessage
+ { UserMessage.Empty with Level = Level.Info
+ Message = Resources.MsgPageDeleted }
+ |> model.AddMessage
this.Redirect "/pages" model
| None -> this.NotFound ()
diff --git a/src/myWebLog.Web/PostModule.fs b/src/myWebLog.Web/PostModule.fs
index 162158a..a994fc2 100644
--- a/src/myWebLog.Web/PostModule.fs
+++ b/src/myWebLog.Web/PostModule.fs
@@ -1,10 +1,10 @@
-namespace myWebLog
+namespace MyWebLog
open FSharp.Markdown
-open myWebLog.Data.Category
-open myWebLog.Data.Page
-open myWebLog.Data.Post
-open myWebLog.Entities
+open MyWebLog.Data.Category
+open MyWebLog.Data.Page
+open MyWebLog.Data.Post
+open MyWebLog.Entities
open Nancy
open Nancy.ModelBinding
open Nancy.Security
@@ -20,39 +20,39 @@ type PostModule(conn : IConnection, clock : IClock) as this =
/// Get the page number from the dictionary
let getPage (parameters : DynamicDictionary) =
- match parameters.ContainsKey "page" with | true -> System.Int32.Parse (parameters.["page"].ToString ()) | _ -> 1
+ match parameters.ContainsKey "page" with true -> System.Int32.Parse (parameters.["page"].ToString ()) | _ -> 1
/// 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 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,
+ 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 ()
+ | 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))
+ 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)))
+ (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
+ |> match format with "atom" -> feed.SaveAsAtom10 | _ -> feed.SaveAsRss20
stream.Position <- int64 0
upcast this.Response.FromStream(stream, sprintf "application/%s+xml" format)
@@ -75,77 +75,77 @@ type PostModule(conn : IConnection, clock : IClock) as this =
/// Display a page of published posts
member this.PublishedPostsPage pageNbr =
let model = PostsModel(this.Context, this.WebLog)
- model.pageNbr <- pageNbr
- model.posts <- findPageOfPublishedPosts conn this.WebLog.id pageNbr 10 |> forDisplay
- model.hasNewer <- match pageNbr with
+ model.PageNbr <- pageNbr
+ model.Posts <- findPageOfPublishedPosts conn this.WebLog.Id pageNbr 10 |> forDisplay
+ model.HasNewer <- match pageNbr with
| 1 -> false
- | _ -> match List.isEmpty model.posts with
+ | _ -> match List.isEmpty model.Posts with
| true -> false
- | _ -> Option.isSome <| tryFindNewerPost conn (List.last model.posts).post
- model.hasOlder <- match List.isEmpty model.posts with
+ | _ -> Option.isSome <| tryFindNewerPost conn (List.last model.Posts).Post
+ model.HasOlder <- match List.isEmpty model.Posts with
| true -> false
- | _ -> Option.isSome <| tryFindOlderPost conn (List.head model.posts).post
- model.urlPrefix <- "/posts"
- model.pageTitle <- match pageNbr with
+ | _ -> Option.isSome <| tryFindOlderPost conn (List.head model.Posts).Post
+ model.UrlPrefix <- "/posts"
+ model.PageTitle <- match pageNbr with
| 1 -> ""
| _ -> sprintf "%s%i" Resources.PageHash pageNbr
this.ThemedView "index" model
/// Display either the newest posts or the configured home page
member this.HomePage () =
- match this.WebLog.defaultPage with
+ match this.WebLog.DefaultPage with
| "posts" -> this.PublishedPostsPage 1
- | pageId -> match tryFindPageWithoutRevisions conn this.WebLog.id pageId with
+ | pageId -> match tryFindPageWithoutRevisions conn this.WebLog.Id pageId with
| Some page -> let model = PageModel(this.Context, this.WebLog, page)
- model.pageTitle <- page.title
+ model.PageTitle <- page.Title
this.ThemedView "page" model
| None -> this.NotFound ()
/// Derive a post or page from the URL, or redirect from a prior URL to the current one
member this.CatchAll (parameters : DynamicDictionary) =
let url = parameters.["permalink"].ToString ()
- match tryFindPostByPermalink conn this.WebLog.id url with
+ match tryFindPostByPermalink conn this.WebLog.Id url with
| Some post -> // Hopefully the most common result; the permalink is a permalink!
let model = PostModel(this.Context, this.WebLog, post)
- model.newerPost <- tryFindNewerPost conn post
- model.olderPost <- tryFindOlderPost conn post
- model.pageTitle <- post.title
+ model.NewerPost <- tryFindNewerPost conn post
+ model.OlderPost <- tryFindOlderPost conn post
+ model.PageTitle <- post.Title
this.ThemedView "single" model
| None -> // Maybe it's a page permalink instead...
- match tryFindPageByPermalink conn this.WebLog.id url with
+ match tryFindPageByPermalink conn this.WebLog.Id url with
| Some page -> // ...and it is!
let model = PageModel(this.Context, this.WebLog, page)
- model.pageTitle <- page.title
+ model.PageTitle <- page.Title
this.ThemedView "page" model
| None -> // Maybe it's an old permalink for a post
- match tryFindPostByPriorPermalink conn this.WebLog.id url with
+ match tryFindPostByPriorPermalink conn this.WebLog.Id url with
| Some post -> // Redirect them to the proper permalink
- upcast this.Response.AsRedirect(sprintf "/%s" post.permalink)
+ upcast this.Response.AsRedirect(sprintf "/%s" post.Permalink)
.WithStatusCode HttpStatusCode.MovedPermanently
| None -> this.NotFound ()
/// Display categorized posts
member this.CategorizedPosts (parameters : DynamicDictionary) =
let slug = parameters.["slug"].ToString ()
- match tryFindCategoryBySlug conn this.WebLog.id slug with
+ match tryFindCategoryBySlug conn this.WebLog.Id slug with
| Some cat -> let pageNbr = getPage parameters
let model = PostsModel(this.Context, this.WebLog)
- model.pageNbr <- pageNbr
- model.posts <- findPageOfCategorizedPosts conn this.WebLog.id cat.id pageNbr 10 |> forDisplay
- model.hasNewer <- match List.isEmpty model.posts with
+ model.PageNbr <- pageNbr
+ model.Posts <- findPageOfCategorizedPosts conn this.WebLog.Id cat.Id pageNbr 10 |> forDisplay
+ model.HasNewer <- match List.isEmpty model.Posts with
| true -> false
- | _ -> Option.isSome <| tryFindNewerCategorizedPost conn cat.id
- (List.head model.posts).post
- model.hasOlder <- match List.isEmpty model.posts with
+ | _ -> Option.isSome <| tryFindNewerCategorizedPost conn cat.Id
+ (List.head model.Posts).Post
+ model.HasOlder <- match List.isEmpty model.Posts with
| true -> false
- | _ -> Option.isSome <| tryFindOlderCategorizedPost conn cat.id
- (List.last model.posts).post
- model.urlPrefix <- sprintf "/category/%s" slug
- model.pageTitle <- sprintf "\"%s\" Category%s" cat.name
+ | _ -> Option.isSome <| tryFindOlderCategorizedPost conn cat.Id
+ (List.last model.Posts).Post
+ model.UrlPrefix <- sprintf "/category/%s" slug
+ model.PageTitle <- sprintf "\"%s\" Category%s" cat.Name
(match pageNbr with | 1 -> "" | n -> sprintf " | Page %i" n)
- model.subtitle <- Some <| match cat.description with
+ model.Subtitle <- Some <| match cat.Description with
| Some desc -> desc
- | None -> sprintf "Posts in the \"%s\" category" cat.name
+ | None -> sprintf "Posts in the \"%s\" category" cat.Name
this.ThemedView "index" model
| None -> this.NotFound ()
@@ -154,17 +154,17 @@ type PostModule(conn : IConnection, clock : IClock) as this =
let tag = parameters.["tag"].ToString ()
let pageNbr = getPage parameters
let model = PostsModel(this.Context, this.WebLog)
- model.pageNbr <- pageNbr
- model.posts <- findPageOfTaggedPosts conn this.WebLog.id tag pageNbr 10 |> forDisplay
- model.hasNewer <- match List.isEmpty model.posts with
+ model.PageNbr <- pageNbr
+ 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.head model.posts).post
- model.hasOlder <- match List.isEmpty model.posts with
+ | _ -> 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
- model.urlPrefix <- sprintf "/tag/%s" tag
- model.pageTitle <- sprintf "\"%s\" Tag%s" tag (match pageNbr with | 1 -> "" | n -> sprintf " | Page %i" n)
- model.subtitle <- Some <| sprintf "Posts tagged \"%s\"" tag
+ | _ -> Option.isSome <| tryFindOlderTaggedPost conn tag (List.last model.Posts).Post
+ model.UrlPrefix <- sprintf "/tag/%s" tag
+ model.PageTitle <- sprintf "\"%s\" Tag%s" tag (match pageNbr with | 1 -> "" | n -> sprintf " | Page %i" n)
+ model.Subtitle <- Some <| sprintf "Posts tagged \"%s\"" tag
this.ThemedView "index" model
/// Generate an RSS feed
@@ -183,35 +183,33 @@ type PostModule(conn : IConnection, clock : IClock) as this =
member this.PostList pageNbr =
this.RequiresAccessLevel AuthorizationLevel.Administrator
let model = PostsModel(this.Context, this.WebLog)
- model.pageNbr <- pageNbr
- model.posts <- findPageOfAllPosts conn this.WebLog.id pageNbr 25 |> forDisplay
- model.hasNewer <- pageNbr > 1
- model.hasOlder <- List.length model.posts > 24
- model.urlPrefix <- "/posts/list"
- model.pageTitle <- Resources.Posts
+ model.PageNbr <- pageNbr
+ model.Posts <- findPageOfAllPosts conn this.WebLog.Id pageNbr 25 |> forDisplay
+ model.HasNewer <- pageNbr > 1
+ model.HasOlder <- List.length model.Posts > 24
+ model.UrlPrefix <- "/posts/list"
+ model.PageTitle <- Resources.Posts
upcast this.View.["admin/post/list", model]
/// Edit a post
member this.EditPost (parameters : DynamicDictionary) =
this.RequiresAccessLevel AuthorizationLevel.Administrator
- let postId : string = downcast parameters.["postId"]
+ let postId = parameters.["postId"].ToString ()
match (match postId with
- | "new" -> Some Post.empty
- | _ -> tryFindPost conn this.WebLog.id postId) with
- | Some post -> let rev = match post.revisions
- |> List.sortByDescending (fun r -> r.asOf)
+ | "new" -> Some Post.Empty
+ | _ -> tryFindPost conn this.WebLog.Id postId) with
+ | Some post -> let rev = match post.Revisions
+ |> List.sortByDescending (fun r -> r.AsOf)
|> List.tryHead with
| Some r -> r
- | None -> Revision.empty
+ | None -> Revision.Empty
let model = EditPostModel(this.Context, this.WebLog, post, rev)
- model.categories <- getAllCategories conn this.WebLog.id
- |> List.map (fun cat -> string (fst cat).id,
+ model.Categories <- getAllCategories conn this.WebLog.Id
+ |> List.map (fun cat -> string (fst cat).Id,
sprintf "%s%s"
(String.replicate (snd cat) " ")
- (fst cat).name)
- model.pageTitle <- match post.id with
- | "new" -> Resources.AddNewPost
- | _ -> Resources.EditPost
+ (fst cat).Name)
+ model.PageTitle <- match post.Id with "new" -> Resources.AddNewPost | _ -> Resources.EditPost
upcast this.View.["admin/post/edit"]
| None -> this.NotFound ()
@@ -219,45 +217,45 @@ type PostModule(conn : IConnection, clock : IClock) as this =
member this.SavePost (parameters : DynamicDictionary) =
this.RequiresAccessLevel AuthorizationLevel.Administrator
this.ValidateCsrfToken ()
- let postId : string = downcast parameters.["postId"]
- let form = this.Bind()
- let now = clock.Now.Ticks
+ let postId = parameters.["postId"].ToString ()
+ let form = this.Bind()
+ let now = clock.Now.Ticks
match (match postId with
- | "new" -> Some Post.empty
- | _ -> tryFindPost conn this.WebLog.id postId) with
- | Some p -> let justPublished = p.publishedOn = int64 0 && form.publishNow
+ | "new" -> Some Post.Empty
+ | _ -> tryFindPost conn this.WebLog.Id postId) with
+ | Some p -> let justPublished = p.PublishedOn = int64 0 && form.PublishNow
let post = match postId with
| "new" -> { p with
- webLogId = this.WebLog.id
- authorId = (this.Request.PersistableSession.GetOrDefault
- (Keys.User, User.empty)).id }
- | _ -> p
+ WebLogId = this.WebLog.Id
+ AuthorId = (this.Request.PersistableSession.GetOrDefault
+ (Keys.User, User.Empty)).Id }
+ | _ -> p
let pId = { post with
- status = match form.publishNow with
+ Status = match form.PublishNow with
| true -> PostStatus.Published
- | _ -> PostStatus.Draft
- title = form.title
- permalink = form.permalink
- publishedOn = match justPublished with | true -> now | _ -> int64 0
- updatedOn = now
- text = match form.source with
- | RevisionSource.Markdown -> Markdown.TransformHtml form.text
- | _ -> form.text
- categoryIds = Array.toList form.categories
- tags = form.tags.Split ','
+ | _ -> PostStatus.Draft
+ Title = form.Title
+ Permalink = form.Permalink
+ PublishedOn = match justPublished with true -> now | _ -> int64 0
+ UpdatedOn = now
+ Text = match form.Source with
+ | RevisionSource.Markdown -> Markdown.TransformHtml form.Text
+ | _ -> form.Text
+ CategoryIds = Array.toList form.Categories
+ Tags = form.Tags.Split ','
|> Seq.map (fun t -> t.Trim().ToLowerInvariant())
|> Seq.toList
- revisions = { asOf = now
- sourceType = form.source
- text = form.text } :: post.revisions }
+ Revisions = { AsOf = now
+ SourceType = form.Source
+ Text = form.Text } :: post.Revisions }
|> savePost conn
let model = MyWebLogModel(this.Context, this.WebLog)
- { level = Level.Info
- message = System.String.Format
- (Resources.MsgPostEditSuccess,
- (match postId with | "new" -> Resources.Added | _ -> Resources.Updated),
- (match justPublished with | true -> Resources.AndPublished | _ -> ""))
- details = None }
- |> model.addMessage
+ { UserMessage.Empty with
+ Level = Level.Info
+ Message = System.String.Format
+ (Resources.MsgPostEditSuccess,
+ (match postId with | "new" -> Resources.Added | _ -> Resources.Updated),
+ (match justPublished with | true -> Resources.AndPublished | _ -> "")) }
+ |> model.AddMessage
this.Redirect (sprintf "/post/%s/edit" pId) model
| None -> this.NotFound ()
diff --git a/src/myWebLog.Web/UserModule.fs b/src/myWebLog.Web/UserModule.fs
index 5ef73ed..7e6fb76 100644
--- a/src/myWebLog.Web/UserModule.fs
+++ b/src/myWebLog.Web/UserModule.fs
@@ -1,7 +1,7 @@
-namespace myWebLog
+namespace MyWebLog
-open myWebLog.Data.User
-open myWebLog.Entities
+open MyWebLog.Data.User
+open MyWebLog.Entities
open Nancy
open Nancy.Authentication.Forms
open Nancy.Cryptography
@@ -21,15 +21,16 @@ type UserModule(conn : IConnection) as this =
|> Seq.fold (fun acc byt -> sprintf "%s%s" acc (byt.ToString "x2")) ""
do
- this.Get .["/logon" ] <- fun parms -> this.ShowLogOn (downcast parms)
+ this.Get .["/logon" ] <- fun _ -> this.ShowLogOn ()
this.Post.["/logon" ] <- fun parms -> this.DoLogOn (downcast parms)
- this.Get .["/logoff"] <- fun parms -> this.LogOff ()
+ this.Get .["/logoff"] <- fun _ -> this.LogOff ()
/// Show the log on page
- member this.ShowLogOn (parameters : DynamicDictionary) =
+ member this.ShowLogOn () =
let model = LogOnModel(this.Context, this.WebLog)
- model.form.returnUrl <- match parameters.ContainsKey "returnUrl" with
- | true -> parameters.["returnUrl"].ToString ()
+ let query = this.Request.Query :?> DynamicDictionary
+ model.Form.ReturnUrl <- match query.ContainsKey "returnUrl" with
+ | true -> query.["returnUrl"].ToString ()
| _ -> ""
upcast this.View.["admin/user/logon", model]
@@ -38,30 +39,28 @@ type UserModule(conn : IConnection) as this =
this.ValidateCsrfToken ()
let form = this.Bind ()
let model = MyWebLogModel(this.Context, this.WebLog)
- match tryUserLogOn conn form.email (pbkdf2 form.password) with
+ match tryUserLogOn conn form.Email (pbkdf2 form.Password) with
| Some user -> this.Session.[Keys.User] <- user
- { level = Level.Info
- message = Resources.MsgLogOnSuccess
- details = None }
- |> model.addMessage
+ { UserMessage.Empty with Level = Level.Info
+ Message = Resources.MsgLogOnSuccess }
+ |> 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
- 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 (sprintf "/user/logon?returnUrl=%s" form.returnUrl) model
+ upcast this.LoginAndRedirect (System.Guid.Parse user.Id,
+ fallbackRedirectUrl = defaultArg (Option.ofObj form.ReturnUrl) "/")
+ | None -> { UserMessage.Empty with Level = Level.Error
+ Message = Resources.ErrBadLogOnAttempt }
+ |> model.AddMessage
+ this.Redirect (sprintf "/user/logon?returnUrl=%s" form.ReturnUrl) model
/// Log a user off
member this.LogOff () =
- let user = this.Request.PersistableSession.GetOrDefault (Keys.User, User.empty)
+ // FIXME: why are we getting the user here if we don't do anything with it?
+ let user = this.Request.PersistableSession.GetOrDefault (Keys.User, User.Empty)
this.Session.DeleteAll ()
let model = MyWebLogModel(this.Context, this.WebLog)
- { level = Level.Info
- message = Resources.MsgLogOffSuccess
- details = None }
- |> model.addMessage
+ { UserMessage.Empty with Level = Level.Info
+ Message = Resources.MsgLogOffSuccess }
+ |> model.AddMessage
this.Redirect "" model |> ignore
upcast this.LogoutAndRedirect "/"
diff --git a/src/myWebLog.Web/ViewModels.fs b/src/myWebLog.Web/ViewModels.fs
index 3210117..093bb7b 100644
--- a/src/myWebLog.Web/ViewModels.fs
+++ b/src/myWebLog.Web/ViewModels.fs
@@ -1,7 +1,7 @@
-namespace myWebLog
+namespace MyWebLog
-open myWebLog.Data.WebLog
-open myWebLog.Entities
+open MyWebLog.Data.WebLog
+open MyWebLog.Entities
open Nancy
open Nancy.Session.Persistable
open Newtonsoft.Json
@@ -21,25 +21,23 @@ module Level =
/// A message for the user
-type UserMessage = {
- /// The level of the message (use Level module constants)
- level : string
- /// The text of the message
- message : string
- /// Further details regarding the message
- details : string option
- }
+type UserMessage =
+ { /// The level of the message (use Level module constants)
+ Level : string
+ /// The text of the message
+ Message : string
+ /// Further details regarding the message
+ Details : string option }
with
/// An empty message
- static member empty = {
- level = Level.Info
- message = ""
- details = None
- }
+ static member Empty =
+ { Level = Level.Info
+ Message = ""
+ Details = None }
/// Display version
[]
- member this.toDisplay =
+ member this.ToDisplay =
let classAndLabel =
dict [
Level.Error, ("danger", Resources.Error)
@@ -48,23 +46,23 @@ with
]
seq {
yield "
"
- match snd classAndLabel.[this.level] with
+ match snd classAndLabel.[this.Level] with
| "" -> ()
| lbl -> yield lbl.ToUpper ()
yield " » "
- yield this.message
+ yield this.Message
yield ""
- match this.details with
+ match this.Details with
| Some d -> yield " "
yield d
| None -> ()
yield "