From d8ce59a6cd12356b60f1df8e49c76bd7a182cc6c Mon Sep 17 00:00:00 2001 From: "Daniel J. Summers" Date: Sat, 16 Dec 2023 12:24:45 -0500 Subject: [PATCH] WIP on module/member conversion Support types done --- src/MyWebLog.Data/Converters.fs | 100 +++--- .../Postgres/PostgresCategoryData.fs | 16 +- src/MyWebLog.Data/Postgres/PostgresHelpers.fs | 4 +- .../Postgres/PostgresPageData.fs | 25 +- .../Postgres/PostgresPostData.fs | 40 +-- .../Postgres/PostgresTagMapData.fs | 10 +- .../Postgres/PostgresThemeData.fs | 26 +- .../Postgres/PostgresUploadData.fs | 8 +- .../Postgres/PostgresWebLogData.fs | 17 +- .../Postgres/PostgresWebLogUserData.fs | 25 +- src/MyWebLog.Data/RethinkDbData.fs | 12 +- src/MyWebLog.Data/SQLite/Helpers.fs | 8 +- .../SQLite/SQLiteCategoryData.fs | 30 +- src/MyWebLog.Data/SQLite/SQLitePageData.fs | 28 +- src/MyWebLog.Data/SQLite/SQLitePostData.fs | 54 +-- src/MyWebLog.Data/SQLite/SQLiteTagMapData.fs | 14 +- src/MyWebLog.Data/SQLite/SQLiteThemeData.fs | 33 +- src/MyWebLog.Data/SQLite/SQLiteUploadData.fs | 16 +- src/MyWebLog.Data/SQLite/SQLiteWebLogData.fs | 30 +- .../SQLite/SQLiteWebLogUserData.fs | 31 +- src/MyWebLog.Data/SQLiteData.fs | 4 +- src/MyWebLog.Data/Utils.fs | 12 +- src/MyWebLog.Domain/DataTypes.fs | 30 +- src/MyWebLog.Domain/SupportTypes.fs | 326 +++++++++--------- src/MyWebLog.Domain/ViewModels.fs | 294 ++++++++-------- src/MyWebLog/Caches.fs | 18 +- src/MyWebLog/DotLiquidBespoke.fs | 6 +- src/MyWebLog/Handlers/Admin.fs | 42 +-- src/MyWebLog/Handlers/Feed.fs | 65 ++-- src/MyWebLog/Handlers/Helpers.fs | 4 +- src/MyWebLog/Handlers/Page.fs | 2 +- src/MyWebLog/Handlers/Post.fs | 12 +- src/MyWebLog/Handlers/Routes.fs | 6 +- src/MyWebLog/Handlers/Upload.fs | 20 +- src/MyWebLog/Handlers/User.fs | 28 +- src/MyWebLog/Maintenance.fs | 28 +- src/MyWebLog/Program.fs | 2 +- 37 files changed, 705 insertions(+), 721 deletions(-) diff --git a/src/MyWebLog.Data/Converters.fs b/src/MyWebLog.Data/Converters.fs index d68025d..b4e07a1 100644 --- a/src/MyWebLog.Data/Converters.fs +++ b/src/MyWebLog.Data/Converters.fs @@ -12,120 +12,120 @@ module Json = type CategoryIdConverter() = inherit JsonConverter() override _.WriteJson(writer: JsonWriter, value: CategoryId, _: JsonSerializer) = - writer.WriteValue value.Value + writer.WriteValue(string value) override _.ReadJson(reader: JsonReader, _: Type, _: CategoryId, _: bool, _: JsonSerializer) = (string >> CategoryId) reader.Value type CommentIdConverter() = inherit JsonConverter() override _.WriteJson(writer: JsonWriter, value: CommentId, _: JsonSerializer) = - writer.WriteValue value.Value + writer.WriteValue(string value) override _.ReadJson(reader: JsonReader, _: Type, _: CommentId, _: bool, _: JsonSerializer) = (string >> CommentId) reader.Value type CommentStatusConverter() = inherit JsonConverter() override _.WriteJson(writer: JsonWriter, value: CommentStatus, _: JsonSerializer) = - writer.WriteValue value.Value + writer.WriteValue(string value) override _.ReadJson(reader: JsonReader, _: Type, _: CommentStatus, _: bool, _: JsonSerializer) = (string >> CommentStatus.Parse) reader.Value - type CustomFeedIdConverter () = - inherit JsonConverter () - override _.WriteJson (writer : JsonWriter, value : CustomFeedId, _ : JsonSerializer) = - writer.WriteValue (CustomFeedId.toString value) - override _.ReadJson (reader : JsonReader, _ : Type, _ : CustomFeedId, _ : bool, _ : JsonSerializer) = + type CustomFeedIdConverter() = + inherit JsonConverter() + override _.WriteJson(writer: JsonWriter, value: CustomFeedId, _: JsonSerializer) = + writer.WriteValue(string value) + override _.ReadJson(reader: JsonReader, _: Type, _: CustomFeedId, _: bool, _: JsonSerializer) = (string >> CustomFeedId) reader.Value - type CustomFeedSourceConverter () = - inherit JsonConverter () - override _.WriteJson (writer : JsonWriter, value : CustomFeedSource, _ : JsonSerializer) = - writer.WriteValue (CustomFeedSource.toString value) - override _.ReadJson (reader : JsonReader, _ : Type, _ : CustomFeedSource, _ : bool, _ : JsonSerializer) = - (string >> CustomFeedSource.parse) reader.Value + type CustomFeedSourceConverter() = + inherit JsonConverter() + override _.WriteJson(writer: JsonWriter, value: CustomFeedSource, _: JsonSerializer) = + writer.WriteValue(string value) + override _.ReadJson(reader: JsonReader, _: Type, _: CustomFeedSource, _: bool, _: JsonSerializer) = + (string >> CustomFeedSource.Parse) reader.Value type ExplicitRatingConverter() = inherit JsonConverter() override _.WriteJson(writer: JsonWriter, value: ExplicitRating, _: JsonSerializer) = - writer.WriteValue value.Value + writer.WriteValue(string value) override _.ReadJson(reader: JsonReader, _: Type, _: ExplicitRating, _: bool, _: JsonSerializer) = (string >> ExplicitRating.Parse) reader.Value type MarkupTextConverter() = inherit JsonConverter() override _.WriteJson(writer: JsonWriter, value: MarkupText, _: JsonSerializer) = - writer.WriteValue value.Value + writer.WriteValue(string value) override _.ReadJson(reader: JsonReader, _: Type, _: MarkupText, _: bool, _: JsonSerializer) = (string >> MarkupText.Parse) reader.Value type PermalinkConverter() = inherit JsonConverter() override _.WriteJson(writer: JsonWriter, value: Permalink, _: JsonSerializer) = - writer.WriteValue value.Value + writer.WriteValue(string value) override _.ReadJson(reader: JsonReader, _: Type, _: Permalink, _: bool, _: JsonSerializer) = (string >> Permalink) reader.Value type PageIdConverter() = inherit JsonConverter() override _.WriteJson(writer: JsonWriter, value: PageId, _: JsonSerializer) = - writer.WriteValue value.Value + writer.WriteValue(string value) override _.ReadJson(reader: JsonReader, _: Type, _: PageId, _: bool, _: JsonSerializer) = (string >> PageId) reader.Value type PodcastMediumConverter() = inherit JsonConverter() override _.WriteJson(writer: JsonWriter, value: PodcastMedium, _: JsonSerializer) = - writer.WriteValue value.Value + writer.WriteValue(string value) override _.ReadJson(reader: JsonReader, _: Type, _: PodcastMedium, _: bool, _: JsonSerializer) = (string >> PodcastMedium.Parse) reader.Value type PostIdConverter() = inherit JsonConverter() override _.WriteJson(writer: JsonWriter, value: PostId, _: JsonSerializer) = - writer.WriteValue value.Value + writer.WriteValue(string value) override _.ReadJson(reader: JsonReader, _: Type, _: PostId, _: bool, _: JsonSerializer) = (string >> PostId) reader.Value - type TagMapIdConverter () = - inherit JsonConverter () - override _.WriteJson (writer : JsonWriter, value : TagMapId, _ : JsonSerializer) = - writer.WriteValue (TagMapId.toString value) - override _.ReadJson (reader : JsonReader, _ : Type, _ : TagMapId, _ : bool, _ : JsonSerializer) = + type TagMapIdConverter() = + inherit JsonConverter() + override _.WriteJson(writer: JsonWriter, value: TagMapId, _: JsonSerializer) = + writer.WriteValue(string value) + override _.ReadJson(reader: JsonReader, _: Type, _: TagMapId, _: bool, _: JsonSerializer) = (string >> TagMapId) reader.Value - type ThemeAssetIdConverter () = - inherit JsonConverter () - override _.WriteJson (writer : JsonWriter, value : ThemeAssetId, _ : JsonSerializer) = - writer.WriteValue (ThemeAssetId.toString value) - override _.ReadJson (reader : JsonReader, _ : Type, _ : ThemeAssetId, _ : bool, _ : JsonSerializer) = - (string >> ThemeAssetId.ofString) reader.Value + type ThemeAssetIdConverter() = + inherit JsonConverter() + override _.WriteJson(writer: JsonWriter, value: ThemeAssetId, _: JsonSerializer) = + writer.WriteValue(string value) + override _.ReadJson(reader: JsonReader, _: Type, _: ThemeAssetId, _: bool, _: JsonSerializer) = + (string >> ThemeAssetId.Parse) reader.Value - type ThemeIdConverter () = - inherit JsonConverter () - override _.WriteJson (writer : JsonWriter, value : ThemeId, _ : JsonSerializer) = - writer.WriteValue (ThemeId.toString value) - override _.ReadJson (reader : JsonReader, _ : Type, _ : ThemeId, _ : bool, _ : JsonSerializer) = + type ThemeIdConverter() = + inherit JsonConverter() + override _.WriteJson(writer: JsonWriter, value: ThemeId, _: JsonSerializer) = + writer.WriteValue(string value) + override _.ReadJson(reader: JsonReader, _: Type, _: ThemeId, _: bool, _: JsonSerializer) = (string >> ThemeId) reader.Value - type UploadIdConverter () = - inherit JsonConverter () - override _.WriteJson (writer : JsonWriter, value : UploadId, _ : JsonSerializer) = - writer.WriteValue (UploadId.toString value) - override _.ReadJson (reader : JsonReader, _ : Type, _ : UploadId, _ : bool, _ : JsonSerializer) = + type UploadIdConverter() = + inherit JsonConverter() + override _.WriteJson(writer: JsonWriter, value: UploadId, _: JsonSerializer) = + writer.WriteValue(string value) + override _.ReadJson(reader: JsonReader, _: Type, _: UploadId, _: bool, _: JsonSerializer) = (string >> UploadId) reader.Value - type WebLogIdConverter () = - inherit JsonConverter () - override _.WriteJson (writer : JsonWriter, value : WebLogId, _ : JsonSerializer) = - writer.WriteValue (WebLogId.toString value) - override _.ReadJson (reader : JsonReader, _ : Type, _ : WebLogId, _ : bool, _ : JsonSerializer) = + type WebLogIdConverter() = + inherit JsonConverter() + override _.WriteJson(writer: JsonWriter, value: WebLogId, _: JsonSerializer) = + writer.WriteValue(string value) + override _.ReadJson(reader: JsonReader, _: Type, _: WebLogId, _: bool, _: JsonSerializer) = (string >> WebLogId) reader.Value - type WebLogUserIdConverter () = + type WebLogUserIdConverter() = inherit JsonConverter () - override _.WriteJson (writer : JsonWriter, value : WebLogUserId, _ : JsonSerializer) = - writer.WriteValue (WebLogUserId.toString value) - override _.ReadJson (reader : JsonReader, _ : Type, _ : WebLogUserId, _ : bool, _ : JsonSerializer) = + override _.WriteJson(writer: JsonWriter, value: WebLogUserId, _: JsonSerializer) = + writer.WriteValue(string value) + override _.ReadJson(reader: JsonReader, _: Type, _: WebLogUserId, _: bool, _: JsonSerializer) = (string >> WebLogUserId) reader.Value open Microsoft.FSharpLu.Json diff --git a/src/MyWebLog.Data/Postgres/PostgresCategoryData.fs b/src/MyWebLog.Data/Postgres/PostgresCategoryData.fs index 08d041c..b78be64 100644 --- a/src/MyWebLog.Data/Postgres/PostgresCategoryData.fs +++ b/src/MyWebLog.Data/Postgres/PostgresCategoryData.fs @@ -43,7 +43,7 @@ type PostgresCategoryData(log: ILogger) = FROM {Table.Post} WHERE {Query.whereDataContains "@criteria"} AND {catIdSql}""" - [ "@criteria", Query.jsonbDocParam {| webLogDoc webLogId with Status = Published.Value |} + [ "@criteria", Query.jsonbDocParam {| webLogDoc webLogId with Status = Published |} catIdParams ] Map.toCount |> Async.AwaitTask @@ -64,7 +64,7 @@ type PostgresCategoryData(log: ILogger) = /// Find a category by its ID for the given web log let findById catId webLogId = log.LogTrace "Category.findById" - Document.findByIdAndWebLog Table.Category catId (_.Value) webLogId + Document.findByIdAndWebLog Table.Category catId string webLogId /// Find all categories for the given web log let findByWebLog webLogId = @@ -73,7 +73,7 @@ type PostgresCategoryData(log: ILogger) = /// Create parameters for a category insert / update let catParameters (cat : Category) = - Query.docParameters cat.Id.Value cat + Query.docParameters (string cat.Id) cat /// Delete a category let delete catId webLogId = backgroundTask { @@ -81,7 +81,7 @@ type PostgresCategoryData(log: ILogger) = match! findById catId webLogId with | Some cat -> // Reassign any children to the category's parent category - let! children = Find.byContains Table.Category {| ParentId = catId.Value |} + let! children = Find.byContains Table.Category {| ParentId = string catId |} let hasChildren = not (List.isEmpty children) if hasChildren then let! _ = @@ -90,7 +90,7 @@ type PostgresCategoryData(log: ILogger) = |> Sql.executeTransactionAsync [ Query.Update.partialById Table.Category, children |> List.map (fun child -> [ - "@id", Sql.string child.Id.Value + "@id", Sql.string (string child.Id) "@data", Query.jsonbDocParam {| ParentId = cat.ParentId |} ]) ] @@ -98,7 +98,7 @@ type PostgresCategoryData(log: ILogger) = // Delete the category off all posts where it is assigned let! posts = Custom.list $"SELECT data FROM {Table.Post} WHERE data -> '{nameof Post.empty.CategoryIds}' @> @id" - [ "@id", Query.jsonbDocParam [| catId.Value |] ] fromData + [ "@id", Query.jsonbDocParam [| string catId |] ] fromData if not (List.isEmpty posts) then let! _ = Configuration.dataSource () @@ -106,14 +106,14 @@ type PostgresCategoryData(log: ILogger) = |> Sql.executeTransactionAsync [ Query.Update.partialById Table.Post, posts |> List.map (fun post -> [ - "@id", Sql.string post.Id.Value + "@id", Sql.string (string post.Id) "@data", Query.jsonbDocParam {| CategoryIds = post.CategoryIds |> List.filter (fun cat -> cat <> catId) |} ]) ] () // Delete the category itself - do! Delete.byId Table.Category catId.Value + do! Delete.byId Table.Category (string catId) return if hasChildren then ReassignedChildCategories else CategoryDeleted | None -> return CategoryNotFound } diff --git a/src/MyWebLog.Data/Postgres/PostgresHelpers.fs b/src/MyWebLog.Data/Postgres/PostgresHelpers.fs index b51b1e6..ae73f49 100644 --- a/src/MyWebLog.Data/Postgres/PostgresHelpers.fs +++ b/src/MyWebLog.Data/Postgres/PostgresHelpers.fs @@ -70,7 +70,7 @@ open Npgsql.FSharp /// Create a SQL parameter for the web log ID let webLogIdParam webLogId = - "@webLogId", Sql.string (WebLogId.toString webLogId) + "@webLogId", Sql.string (string webLogId) /// Create an anonymous record with the given web log ID let webLogDoc (webLogId : WebLogId) = @@ -206,7 +206,7 @@ module Revisions = let revParams<'TKey> (key : 'TKey) (keyFunc : 'TKey -> string) rev = [ typedParam "asOf" rev.AsOf "@id", Sql.string (keyFunc key) - "@text", Sql.string rev.Text.Value + "@text", Sql.string (string rev.Text) ] /// The SQL statement to insert a revision diff --git a/src/MyWebLog.Data/Postgres/PostgresPageData.fs b/src/MyWebLog.Data/Postgres/PostgresPageData.fs index 8be3d1b..7bf8c80 100644 --- a/src/MyWebLog.Data/Postgres/PostgresPageData.fs +++ b/src/MyWebLog.Data/Postgres/PostgresPageData.fs @@ -14,7 +14,7 @@ type PostgresPageData (log: ILogger) = /// Append revisions to a page let appendPageRevisions (page: Page) = backgroundTask { log.LogTrace "Page.appendPageRevisions" - let! revisions = Revisions.findByEntityId Table.PageRevision Table.Page page.Id _.Value + let! revisions = Revisions.findByEntityId Table.PageRevision Table.Page page.Id string return { page with Revisions = revisions } } @@ -25,12 +25,12 @@ type PostgresPageData (log: ILogger) = /// Update a page's revisions let updatePageRevisions (pageId: PageId) oldRevs newRevs = log.LogTrace "Page.updatePageRevisions" - Revisions.update Table.PageRevision Table.Page pageId (_.Value) oldRevs newRevs + Revisions.update Table.PageRevision Table.Page pageId string oldRevs newRevs /// Does the given page exist? let pageExists (pageId: PageId) webLogId = log.LogTrace "Page.pageExists" - Document.existsByWebLog Table.Page pageId (_.Value) webLogId + Document.existsByWebLog Table.Page pageId string webLogId // IMPLEMENTATION FUNCTIONS @@ -51,9 +51,9 @@ type PostgresPageData (log: ILogger) = Count.byContains Table.Page {| webLogDoc webLogId with IsInPageList = true |} /// Find a page by its ID (without revisions) - let findById (pageId: PageId) webLogId = + let findById pageId webLogId = log.LogTrace "Page.findById" - Document.findByIdAndWebLog Table.Page pageId (_.Value) webLogId + Document.findByIdAndWebLog Table.Page pageId string webLogId /// Find a complete page by its ID let findFullById pageId webLogId = backgroundTask { @@ -70,7 +70,7 @@ type PostgresPageData (log: ILogger) = log.LogTrace "Page.delete" match! pageExists pageId webLogId with | true -> - do! Delete.byId Table.Page pageId.Value + do! Delete.byId Table.Page (string pageId) return true | false -> return false } @@ -78,16 +78,15 @@ type PostgresPageData (log: ILogger) = /// Find a page by its permalink for the given web log let findByPermalink (permalink: Permalink) webLogId = log.LogTrace "Page.findByPermalink" - Find.byContains Table.Page {| webLogDoc webLogId with Permalink = permalink.Value |} + Find.byContains Table.Page {| webLogDoc webLogId with Permalink = string permalink |} |> tryHead /// Find the current permalink within a set of potential prior permalinks for the given web log - let findCurrentPermalink permalinks webLogId = backgroundTask { + let findCurrentPermalink (permalinks: Permalink list) webLogId = backgroundTask { log.LogTrace "Page.findCurrentPermalink" if List.isEmpty permalinks then return None else - let linkSql, linkParam = - arrayContains (nameof Page.empty.PriorPermalinks) (fun (it: Permalink) -> it.Value) permalinks + let linkSql, linkParam = arrayContains (nameof Page.empty.PriorPermalinks) string permalinks return! Custom.single $"""SELECT data ->> '{nameof Page.empty.Permalink}' AS permalink @@ -134,9 +133,9 @@ type PostgresPageData (log: ILogger) = |> Sql.executeTransactionAsync [ Query.insert Table.Page, pages - |> List.map (fun page -> Query.docParameters page.Id.Value { page with Revisions = [] }) + |> List.map (fun page -> Query.docParameters (string page.Id) { page with Revisions = [] }) Revisions.insertSql Table.PageRevision, - revisions |> List.map (fun (pageId, rev) -> Revisions.revParams pageId (_.Value) rev) + revisions |> List.map (fun (pageId, rev) -> Revisions.revParams pageId string rev) ] () } @@ -155,7 +154,7 @@ type PostgresPageData (log: ILogger) = log.LogTrace "Page.updatePriorPermalinks" match! pageExists pageId webLogId with | true -> - do! Update.partialById Table.Page pageId.Value {| PriorPermalinks = permalinks |} + do! Update.partialById Table.Page (string pageId) {| PriorPermalinks = permalinks |} return true | false -> return false } diff --git a/src/MyWebLog.Data/Postgres/PostgresPostData.fs b/src/MyWebLog.Data/Postgres/PostgresPostData.fs index c1f6248..7984d35 100644 --- a/src/MyWebLog.Data/Postgres/PostgresPostData.fs +++ b/src/MyWebLog.Data/Postgres/PostgresPostData.fs @@ -15,7 +15,7 @@ type PostgresPostData(log: ILogger) = /// Append revisions to a post let appendPostRevisions (post: Post) = backgroundTask { log.LogTrace "Post.appendPostRevisions" - let! revisions = Revisions.findByEntityId Table.PostRevision Table.Post post.Id _.Value + let! revisions = Revisions.findByEntityId Table.PostRevision Table.Post post.Id string return { post with Revisions = revisions } } @@ -26,30 +26,30 @@ type PostgresPostData(log: ILogger) = /// Update a post's revisions let updatePostRevisions (postId: PostId) oldRevs newRevs = log.LogTrace "Post.updatePostRevisions" - Revisions.update Table.PostRevision Table.Post postId (_.Value) oldRevs newRevs + Revisions.update Table.PostRevision Table.Post postId string oldRevs newRevs /// Does the given post exist? let postExists (postId: PostId) webLogId = log.LogTrace "Post.postExists" - Document.existsByWebLog Table.Post postId (_.Value) webLogId + Document.existsByWebLog Table.Post postId string webLogId // IMPLEMENTATION FUNCTIONS /// Count posts in a status for the given web log let countByStatus (status: PostStatus) webLogId = log.LogTrace "Post.countByStatus" - Count.byContains Table.Post {| webLogDoc webLogId with Status = status.Value |} + Count.byContains Table.Post {| webLogDoc webLogId with Status = status |} /// Find a post by its ID for the given web log (excluding revisions) let findById postId webLogId = log.LogTrace "Post.findById" - Document.findByIdAndWebLog Table.Post postId (_.Value) webLogId + Document.findByIdAndWebLog Table.Post postId string webLogId /// Find a post by its permalink for the given web log (excluding revisions and prior permalinks) let findByPermalink (permalink: Permalink) webLogId = log.LogTrace "Post.findByPermalink" Custom.single (selectWithCriteria Table.Post) - [ "@criteria", Query.jsonbDocParam {| webLogDoc webLogId with Permalink = permalink.Value |} ] + [ "@criteria", Query.jsonbDocParam {| webLogDoc webLogId with Permalink = string permalink |} ] fromData /// Find a complete post by its ID for the given web log @@ -70,18 +70,17 @@ type PostgresPostData(log: ILogger) = do! Custom.nonQuery $"""DELETE FROM {Table.PostComment} WHERE {Query.whereDataContains "@criteria"}; DELETE FROM {Table.Post} WHERE id = @id""" - [ "@id", Sql.string postId.Value; "@criteria", Query.jsonbDocParam {| PostId = postId.Value |} ] + [ "@id", Sql.string (string postId); "@criteria", Query.jsonbDocParam {| PostId = postId |} ] return true | false -> return false } /// Find the current permalink from a list of potential prior permalinks for the given web log - let findCurrentPermalink permalinks webLogId = backgroundTask { + let findCurrentPermalink (permalinks: Permalink list) webLogId = backgroundTask { log.LogTrace "Post.findCurrentPermalink" if List.isEmpty permalinks then return None else - let linkSql, linkParam = - arrayContains (nameof Post.empty.PriorPermalinks) (fun (it: Permalink) -> it.Value) permalinks + let linkSql, linkParam = arrayContains (nameof Post.empty.PriorPermalinks) string permalinks return! Custom.single $"""SELECT data ->> '{nameof Post.empty.Permalink}' AS permalink @@ -102,16 +101,15 @@ type PostgresPostData(log: ILogger) = } /// Get a page of categorized posts for the given web log (excludes revisions) - let findPageOfCategorizedPosts webLogId categoryIds pageNbr postsPerPage = + let findPageOfCategorizedPosts webLogId (categoryIds: CategoryId list) pageNbr postsPerPage = log.LogTrace "Post.findPageOfCategorizedPosts" - let catSql, catParam = - arrayContains (nameof Post.empty.CategoryIds) (fun (it: CategoryId) -> it.Value) categoryIds + let catSql, catParam = arrayContains (nameof Post.empty.CategoryIds) string categoryIds Custom.list $"{selectWithCriteria Table.Post} AND {catSql} ORDER BY data ->> '{nameof Post.empty.PublishedOn}' DESC LIMIT {postsPerPage + 1} OFFSET {(pageNbr - 1) * postsPerPage}" - [ "@criteria", Query.jsonbDocParam {| webLogDoc webLogId with Status = Published.Value |} + [ "@criteria", Query.jsonbDocParam {| webLogDoc webLogId with Status = Published |} catParam ] fromData @@ -132,7 +130,7 @@ type PostgresPostData(log: ILogger) = $"{selectWithCriteria Table.Post} ORDER BY data ->> '{nameof Post.empty.PublishedOn}' DESC LIMIT {postsPerPage + 1} OFFSET {(pageNbr - 1) * postsPerPage}" - [ "@criteria", Query.jsonbDocParam {| webLogDoc webLogId with Status = Published.Value |} ] + [ "@criteria", Query.jsonbDocParam {| webLogDoc webLogId with Status = Published |} ] fromData /// Get a page of tagged posts for the given web log (excludes revisions and prior permalinks) @@ -143,7 +141,7 @@ type PostgresPostData(log: ILogger) = AND data['{nameof Post.empty.Tags}'] @> @tag ORDER BY data ->> '{nameof Post.empty.PublishedOn}' DESC LIMIT {postsPerPage + 1} OFFSET {(pageNbr - 1) * postsPerPage}" - [ "@criteria", Query.jsonbDocParam {| webLogDoc webLogId with Status = Published.Value |} + [ "@criteria", Query.jsonbDocParam {| webLogDoc webLogId with Status = Published |} "@tag", Query.jsonbDocParam [| tag |] ] fromData @@ -151,8 +149,8 @@ type PostgresPostData(log: ILogger) = let findSurroundingPosts webLogId publishedOn = backgroundTask { log.LogTrace "Post.findSurroundingPosts" let queryParams () = [ - "@criteria", Query.jsonbDocParam {| webLogDoc webLogId with Status = Published.Value |} - "@publishedOn", Sql.string ((InstantPattern.General.Format publishedOn).Substring (0, 19)) + "@criteria", Query.jsonbDocParam {| webLogDoc webLogId with Status = Published |} + "@publishedOn", Sql.string ((InstantPattern.General.Format publishedOn)[..19]) ] let pubField = nameof Post.empty.PublishedOn let! older = @@ -187,9 +185,9 @@ type PostgresPostData(log: ILogger) = |> Sql.fromDataSource |> Sql.executeTransactionAsync [ Query.insert Table.Post, - posts |> List.map (fun post -> Query.docParameters post.Id.Value { post with Revisions = [] }) + posts |> List.map (fun post -> Query.docParameters (string post.Id) { post with Revisions = [] }) Revisions.insertSql Table.PostRevision, - revisions |> List.map (fun (postId, rev) -> Revisions.revParams postId (_.Value) rev) + revisions |> List.map (fun (postId, rev) -> Revisions.revParams postId string rev) ] () } @@ -199,7 +197,7 @@ type PostgresPostData(log: ILogger) = log.LogTrace "Post.updatePriorPermalinks" match! postExists postId webLogId with | true -> - do! Update.partialById Table.Post postId.Value {| PriorPermalinks = permalinks |} + do! Update.partialById Table.Post (string postId) {| PriorPermalinks = permalinks |} return true | false -> return false } diff --git a/src/MyWebLog.Data/Postgres/PostgresTagMapData.fs b/src/MyWebLog.Data/Postgres/PostgresTagMapData.fs index 210dc14..100523a 100644 --- a/src/MyWebLog.Data/Postgres/PostgresTagMapData.fs +++ b/src/MyWebLog.Data/Postgres/PostgresTagMapData.fs @@ -12,14 +12,14 @@ type PostgresTagMapData (log : ILogger) = /// Find a tag mapping by its ID for the given web log let findById tagMapId webLogId = log.LogTrace "TagMap.findById" - Document.findByIdAndWebLog Table.TagMap tagMapId TagMapId.toString webLogId + Document.findByIdAndWebLog Table.TagMap tagMapId string webLogId /// Delete a tag mapping for the given web log - let delete tagMapId webLogId = backgroundTask { + let delete (tagMapId: TagMapId) webLogId = backgroundTask { log.LogTrace "TagMap.delete" - let! exists = Document.existsByWebLog Table.TagMap tagMapId TagMapId.toString webLogId + let! exists = Document.existsByWebLog Table.TagMap tagMapId string webLogId if exists then - do! Delete.byId Table.TagMap (TagMapId.toString tagMapId) + do! Delete.byId Table.TagMap (string tagMapId) return true else return false } @@ -55,7 +55,7 @@ type PostgresTagMapData (log : ILogger) = |> Sql.fromDataSource |> Sql.executeTransactionAsync [ Query.insert Table.TagMap, - tagMaps |> List.map (fun tagMap -> Query.docParameters (TagMapId.toString tagMap.Id) tagMap) + tagMaps |> List.map (fun tagMap -> Query.docParameters (string tagMap.Id) tagMap) ] () } diff --git a/src/MyWebLog.Data/Postgres/PostgresThemeData.fs b/src/MyWebLog.Data/Postgres/PostgresThemeData.fs index cf3a569..ed7e4bb 100644 --- a/src/MyWebLog.Data/Postgres/PostgresThemeData.fs +++ b/src/MyWebLog.Data/Postgres/PostgresThemeData.fs @@ -20,26 +20,26 @@ type PostgresThemeData (log : ILogger) = Custom.list $"{Query.selectFromTable Table.Theme} WHERE id <> 'admin' ORDER BY id" [] withoutTemplateText /// Does a given theme exist? - let exists themeId = + let exists (themeId: ThemeId) = log.LogTrace "Theme.exists" - Exists.byId Table.Theme (ThemeId.toString themeId) + Exists.byId Table.Theme (string themeId) /// Find a theme by its ID - let findById themeId = + let findById (themeId: ThemeId) = log.LogTrace "Theme.findById" - Find.byId Table.Theme (ThemeId.toString themeId) + Find.byId Table.Theme (string themeId) /// Find a theme by its ID (excludes the text of templates) - let findByIdWithoutText themeId = + let findByIdWithoutText (themeId: ThemeId) = log.LogTrace "Theme.findByIdWithoutText" - Custom.single (Query.Find.byId Table.Theme) [ "@id", Sql.string (ThemeId.toString themeId) ] withoutTemplateText + Custom.single (Query.Find.byId Table.Theme) [ "@id", Sql.string (string themeId) ] withoutTemplateText /// Delete a theme by its ID let delete themeId = backgroundTask { log.LogTrace "Theme.delete" match! exists themeId with | true -> - do! Delete.byId Table.Theme (ThemeId.toString themeId) + do! Delete.byId Table.Theme (string themeId) return true | false -> return false } @@ -67,10 +67,10 @@ type PostgresThemeAssetData (log : ILogger) = Custom.list $"SELECT theme_id, path, updated_on FROM {Table.ThemeAsset}" [] (Map.toThemeAsset false) /// Delete all assets for the given theme - let deleteByTheme themeId = + let deleteByTheme (themeId: ThemeId) = log.LogTrace "ThemeAsset.deleteByTheme" Custom.nonQuery $"DELETE FROM {Table.ThemeAsset} WHERE theme_id = @themeId" - [ "@themeId", Sql.string (ThemeId.toString themeId) ] + [ "@themeId", Sql.string (string themeId) ] /// Find a theme asset by its ID let findById assetId = @@ -80,16 +80,16 @@ type PostgresThemeAssetData (log : ILogger) = [ "@themeId", Sql.string themeId; "@path", Sql.string path ] (Map.toThemeAsset true) /// Get theme assets for the given theme (excludes data) - let findByTheme themeId = + let findByTheme (themeId: ThemeId) = log.LogTrace "ThemeAsset.findByTheme" Custom.list $"SELECT theme_id, path, updated_on FROM {Table.ThemeAsset} WHERE theme_id = @themeId" - [ "@themeId", Sql.string (ThemeId.toString themeId) ] (Map.toThemeAsset false) + [ "@themeId", Sql.string (string themeId) ] (Map.toThemeAsset false) /// Get theme assets for the given theme - let findByThemeWithData themeId = + let findByThemeWithData (themeId: ThemeId) = log.LogTrace "ThemeAsset.findByThemeWithData" Custom.list $"SELECT * FROM {Table.ThemeAsset} WHERE theme_id = @themeId" - [ "@themeId", Sql.string (ThemeId.toString themeId) ] (Map.toThemeAsset true) + [ "@themeId", Sql.string (string themeId) ] (Map.toThemeAsset true) /// Save a theme asset let save (asset : ThemeAsset) = diff --git a/src/MyWebLog.Data/Postgres/PostgresUploadData.fs b/src/MyWebLog.Data/Postgres/PostgresUploadData.fs index 136da11..e97b212 100644 --- a/src/MyWebLog.Data/Postgres/PostgresUploadData.fs +++ b/src/MyWebLog.Data/Postgres/PostgresUploadData.fs @@ -21,8 +21,8 @@ type PostgresUploadData (log : ILogger) = let upParams (upload : Upload) = [ webLogIdParam upload.WebLogId typedParam "updatedOn" upload.UpdatedOn - "@id", Sql.string (UploadId.toString upload.Id) - "@path", Sql.string upload.Path.Value + "@id", Sql.string (string upload.Id) + "@path", Sql.string (string upload.Path) "@data", Sql.bytea upload.Data ] @@ -34,14 +34,14 @@ type PostgresUploadData (log : ILogger) = /// Delete an uploaded file by its ID let delete uploadId webLogId = backgroundTask { log.LogTrace "Upload.delete" - let idParam = [ "@id", Sql.string (UploadId.toString uploadId) ] + let idParam = [ "@id", Sql.string (string uploadId) ] let! path = Custom.single $"SELECT path FROM {Table.Upload} WHERE id = @id AND web_log_id = @webLogId" (webLogIdParam webLogId :: idParam) (fun row -> row.string "path") if Option.isSome path then do! Custom.nonQuery (Query.Delete.byId Table.Upload) idParam return Ok path.Value - else return Error $"""Upload ID {UploadId.toString uploadId} not found""" + else return Error $"""Upload ID {uploadId} not found""" } /// Find an uploaded file by its path for the given web log diff --git a/src/MyWebLog.Data/Postgres/PostgresWebLogData.fs b/src/MyWebLog.Data/Postgres/PostgresWebLogData.fs index f583cc2..0efc85d 100644 --- a/src/MyWebLog.Data/Postgres/PostgresWebLogData.fs +++ b/src/MyWebLog.Data/Postgres/PostgresWebLogData.fs @@ -41,29 +41,30 @@ type PostgresWebLogData (log : ILogger) = fromData /// Find a web log by its ID - let findById webLogId = + let findById (webLogId: WebLogId) = log.LogTrace "WebLog.findById" - Find.byId Table.WebLog (WebLogId.toString webLogId) + Find.byId Table.WebLog (string webLogId) - let updateRedirectRules (webLog : WebLog) = backgroundTask { + let updateRedirectRules (webLog: WebLog) = backgroundTask { log.LogTrace "WebLog.updateRedirectRules" match! findById webLog.Id with | Some _ -> - do! Update.partialById Table.WebLog (WebLogId.toString webLog.Id) {| RedirectRules = webLog.RedirectRules |} + do! Update.partialById Table.WebLog (string webLog.Id) {| RedirectRules = webLog.RedirectRules |} | None -> () } + /// Update RSS options for a web log - let updateRssOptions (webLog : WebLog) = backgroundTask { + let updateRssOptions (webLog: WebLog) = backgroundTask { log.LogTrace "WebLog.updateRssOptions" match! findById webLog.Id with - | Some _ -> do! Update.partialById Table.WebLog (WebLogId.toString webLog.Id) {| Rss = webLog.Rss |} + | Some _ -> do! Update.partialById Table.WebLog (string webLog.Id) {| Rss = webLog.Rss |} | None -> () } /// Update settings for a web log - let updateSettings (webLog : WebLog) = + let updateSettings (webLog: WebLog) = log.LogTrace "WebLog.updateSettings" - Update.full Table.WebLog (WebLogId.toString webLog.Id) webLog + Update.full Table.WebLog (string webLog.Id) webLog interface IWebLogData with member _.Add webLog = add webLog diff --git a/src/MyWebLog.Data/Postgres/PostgresWebLogUserData.fs b/src/MyWebLog.Data/Postgres/PostgresWebLogUserData.fs index 32b912e..dba0985 100644 --- a/src/MyWebLog.Data/Postgres/PostgresWebLogUserData.fs +++ b/src/MyWebLog.Data/Postgres/PostgresWebLogUserData.fs @@ -12,7 +12,7 @@ type PostgresWebLogUserData (log : ILogger) = /// Find a user by their ID for the given web log let findById userId webLogId = log.LogTrace "WebLogUser.findById" - Document.findByIdAndWebLog Table.WebLogUser userId WebLogUserId.toString webLogId + Document.findByIdAndWebLog Table.WebLogUser userId string webLogId /// Delete a user if they have no posts or pages let delete userId webLogId = backgroundTask { @@ -29,7 +29,7 @@ type PostgresWebLogUserData (log : ILogger) = if isAuthor then return Error "User has pages or posts; cannot delete" else - do! Delete.byId Table.WebLogUser (WebLogUserId.toString userId) + do! Delete.byId Table.WebLogUser (string userId) return Ok true | None -> return Error "User does not exist" } @@ -49,41 +49,38 @@ type PostgresWebLogUserData (log : ILogger) = [ webLogContains webLogId ] fromData /// Find the names of users by their IDs for the given web log - let findNames webLogId userIds = backgroundTask { + let findNames webLogId (userIds: WebLogUserId list) = backgroundTask { log.LogTrace "WebLogUser.findNames" - let idSql, idParams = inClause "AND id" "id" WebLogUserId.toString userIds + let idSql, idParams = inClause "AND id" "id" string userIds let! users = Custom.list $"{selectWithCriteria Table.WebLogUser} {idSql}" (webLogContains webLogId :: idParams) fromData - return - users - |> List.map (fun u -> { Name = WebLogUserId.toString u.Id; Value = WebLogUser.displayName u }) + return users |> List.map (fun u -> { Name = string u.Id; Value = WebLogUser.displayName u }) } /// Restore users from a backup - let restore (users : WebLogUser list) = backgroundTask { + let restore (users: WebLogUser list) = backgroundTask { log.LogTrace "WebLogUser.restore" let! _ = Configuration.dataSource () |> Sql.fromDataSource |> Sql.executeTransactionAsync [ Query.insert Table.WebLogUser, - users |> List.map (fun user -> Query.docParameters (WebLogUserId.toString user.Id) user) + users |> List.map (fun user -> Query.docParameters (string user.Id) user) ] () } /// Set a user's last seen date/time to now - let setLastSeen userId webLogId = backgroundTask { + let setLastSeen (userId: WebLogUserId) webLogId = backgroundTask { log.LogTrace "WebLogUser.setLastSeen" - match! Document.existsByWebLog Table.WebLogUser userId WebLogUserId.toString webLogId with - | true -> - do! Update.partialById Table.WebLogUser (WebLogUserId.toString userId) {| LastSeenOn = Some (Noda.now ()) |} + match! Document.existsByWebLog Table.WebLogUser userId string webLogId with + | true -> do! Update.partialById Table.WebLogUser (string userId) {| LastSeenOn = Some (Noda.now ()) |} | false -> () } /// Save a user - let save (user : WebLogUser) = + let save (user: WebLogUser) = log.LogTrace "WebLogUser.save" save Table.WebLogUser user diff --git a/src/MyWebLog.Data/RethinkDbData.fs b/src/MyWebLog.Data/RethinkDbData.fs index e653b1f..acda3a6 100644 --- a/src/MyWebLog.Data/RethinkDbData.fs +++ b/src/MyWebLog.Data/RethinkDbData.fs @@ -96,12 +96,12 @@ type RethinkDbData (conn : Net.IConnection, config : DataConfig, log : ILogger row[nameof ThemeAsset.empty.Id].Match keyPrefix :> obj /// Function to exclude template text from themes let withoutTemplateText (row : Ast.ReqlExpr) : obj = - {| Templates = row[nameof Theme.empty.Templates].Without [| nameof ThemeTemplate.empty.Text |] |} + {| Templates = row[nameof Theme.empty.Templates].Without [| nameof ThemeTemplate.Empty.Text |] |} /// Ensure field indexes exist, as well as special indexes for selected tables let ensureIndexes table fields = backgroundTask { @@ -917,8 +917,8 @@ type RethinkDbData (conn : Net.IConnection, config : DataConfig, log : ILogger return Result.Error $"Upload ID {UploadId.toString uploadId} not found" + return Ok (string up.Path) + | None -> return Result.Error $"Upload ID {uploadId} not found" } member _.FindByPath path webLogId = @@ -1133,9 +1133,7 @@ type RethinkDbData (conn : Net.IConnection, config : DataConfig, log : ILogger List.map (fun u -> { Name = WebLogUserId.toString u.Id; Value = WebLogUser.displayName u }) + return users |> List.map (fun u -> { Name = string u.Id; Value = WebLogUser.displayName u }) } member _.Restore users = backgroundTask { diff --git a/src/MyWebLog.Data/SQLite/Helpers.fs b/src/MyWebLog.Data/SQLite/Helpers.fs index cdd31a2..b37c7bc 100644 --- a/src/MyWebLog.Data/SQLite/Helpers.fs +++ b/src/MyWebLog.Data/SQLite/Helpers.fs @@ -222,7 +222,7 @@ module Map = /// Create a custom feed from the current row in the given data reader let toCustomFeed ser rdr : CustomFeed = { Id = getString "id" rdr |> CustomFeedId - Source = getString "source" rdr |> CustomFeedSource.parse + Source = getString "source" rdr |> CustomFeedSource.Parse Path = getString "path" rdr |> Permalink Podcast = tryString "podcast" rdr |> Option.map (Utils.deserialize ser) } @@ -339,7 +339,7 @@ module Map = UrlBase = getString "url_base" rdr TimeZone = getString "time_zone" rdr AutoHtmx = getBoolean "auto_htmx" rdr - Uploads = getString "uploads" rdr |> UploadDestination.parse + Uploads = getString "uploads" rdr |> UploadDestination.Parse Rss = { IsFeedEnabled = getBoolean "is_feed_enabled" rdr FeedName = getString "feed_name" rdr @@ -368,5 +368,5 @@ module Map = } /// Add a web log ID parameter -let addWebLogId (cmd : SqliteCommand) webLogId = - cmd.Parameters.AddWithValue ("@webLogId", WebLogId.toString webLogId) |> ignore +let addWebLogId (cmd: SqliteCommand) (webLogId: WebLogId) = + cmd.Parameters.AddWithValue ("@webLogId", string webLogId) |> ignore diff --git a/src/MyWebLog.Data/SQLite/SQLiteCategoryData.fs b/src/MyWebLog.Data/SQLite/SQLiteCategoryData.fs index 3caae20..d3d96b2 100644 --- a/src/MyWebLog.Data/SQLite/SQLiteCategoryData.fs +++ b/src/MyWebLog.Data/SQLite/SQLiteCategoryData.fs @@ -5,17 +5,17 @@ open Microsoft.Data.Sqlite open MyWebLog open MyWebLog.Data -/// SQLite myWebLog category data implementation -type SQLiteCategoryData (conn : SqliteConnection) = +/// SQLite myWebLog category data implementation +type SQLiteCategoryData(conn: SqliteConnection) = /// Add parameters for category INSERT or UPDATE statements - let addCategoryParameters (cmd : SqliteCommand) (cat : Category) = - [ cmd.Parameters.AddWithValue ("@id", cat.Id.Value) - cmd.Parameters.AddWithValue ("@webLogId", WebLogId.toString cat.WebLogId) + let addCategoryParameters (cmd: SqliteCommand) (cat: Category) = + [ cmd.Parameters.AddWithValue ("@id", string cat.Id) + cmd.Parameters.AddWithValue ("@webLogId", string cat.WebLogId) cmd.Parameters.AddWithValue ("@name", cat.Name) cmd.Parameters.AddWithValue ("@slug", cat.Slug) cmd.Parameters.AddWithValue ("@description", maybe cat.Description) - cmd.Parameters.AddWithValue ("@parentId", maybe (cat.ParentId |> Option.map _.Value)) + cmd.Parameters.AddWithValue ("@parentId", maybe (cat.ParentId |> Option.map string)) ] |> ignore /// Add a category @@ -102,18 +102,18 @@ type SQLiteCategoryData (conn : SqliteConnection) = } /// Find a category by its ID for the given web log let findById (catId: CategoryId) webLogId = backgroundTask { - use cmd = conn.CreateCommand () + use cmd = conn.CreateCommand() cmd.CommandText <- "SELECT * FROM category WHERE id = @id" - cmd.Parameters.AddWithValue ("@id", catId.Value) |> ignore - use! rdr = cmd.ExecuteReaderAsync () - return Helpers.verifyWebLog webLogId (fun c -> c.WebLogId) Map.toCategory rdr + cmd.Parameters.AddWithValue ("@id", string catId) |> ignore + use! rdr = cmd.ExecuteReaderAsync() + return verifyWebLog webLogId (_.WebLogId) Map.toCategory rdr } /// Find all categories for the given web log - let findByWebLog webLogId = backgroundTask { + let findByWebLog (webLogId: WebLogId) = backgroundTask { use cmd = conn.CreateCommand () cmd.CommandText <- "SELECT * FROM category WHERE web_log_id = @webLogId" - cmd.Parameters.AddWithValue ("@webLogId", WebLogId.toString webLogId) |> ignore + cmd.Parameters.AddWithValue ("@webLogId", string webLogId) |> ignore use! rdr = cmd.ExecuteReaderAsync () return toList Map.toCategory rdr } @@ -125,11 +125,11 @@ type SQLiteCategoryData (conn : SqliteConnection) = use cmd = conn.CreateCommand () // Reassign any children to the category's parent category cmd.CommandText <- "SELECT COUNT(id) FROM category WHERE parent_id = @parentId" - cmd.Parameters.AddWithValue ("@parentId", catId.Value) |> ignore + cmd.Parameters.AddWithValue ("@parentId", string catId) |> ignore let! children = count cmd if children > 0 then cmd.CommandText <- "UPDATE category SET parent_id = @newParentId WHERE parent_id = @parentId" - cmd.Parameters.AddWithValue ("@newParentId", maybe (cat.ParentId |> Option.map _.Value)) + cmd.Parameters.AddWithValue ("@newParentId", maybe (cat.ParentId |> Option.map string)) |> ignore do! write cmd // Delete the category off all posts where it is assigned, and the category itself @@ -139,7 +139,7 @@ type SQLiteCategoryData (conn : SqliteConnection) = AND post_id IN (SELECT id FROM post WHERE web_log_id = @webLogId); DELETE FROM category WHERE id = @id" cmd.Parameters.Clear () - let _ = cmd.Parameters.AddWithValue ("@id", catId.Value) + let _ = cmd.Parameters.AddWithValue ("@id", string catId) addWebLogId cmd webLogId do! write cmd return if children = 0 then CategoryDeleted else ReassignedChildCategories diff --git a/src/MyWebLog.Data/SQLite/SQLitePageData.fs b/src/MyWebLog.Data/SQLite/SQLitePageData.fs index c3ae850..c8ebd49 100644 --- a/src/MyWebLog.Data/SQLite/SQLitePageData.fs +++ b/src/MyWebLog.Data/SQLite/SQLitePageData.fs @@ -13,11 +13,11 @@ type SQLitePageData(conn: SqliteConnection, ser: JsonSerializer) = /// Add parameters for page INSERT or UPDATE statements let addPageParameters (cmd: SqliteCommand) (page: Page) = - [ cmd.Parameters.AddWithValue ("@id", page.Id.Value) - cmd.Parameters.AddWithValue ("@webLogId", WebLogId.toString page.WebLogId) - cmd.Parameters.AddWithValue ("@authorId", WebLogUserId.toString page.AuthorId) + [ cmd.Parameters.AddWithValue ("@id", string page.Id) + cmd.Parameters.AddWithValue ("@webLogId", string page.WebLogId) + cmd.Parameters.AddWithValue ("@authorId", string page.AuthorId) cmd.Parameters.AddWithValue ("@title", page.Title) - cmd.Parameters.AddWithValue ("@permalink", page.Permalink.Value) + cmd.Parameters.AddWithValue ("@permalink", string page.Permalink) cmd.Parameters.AddWithValue ("@publishedOn", instantParam page.PublishedOn) cmd.Parameters.AddWithValue ("@updatedOn", instantParam page.UpdatedOn) cmd.Parameters.AddWithValue ("@isInPageList", page.IsInPageList) @@ -30,7 +30,7 @@ type SQLitePageData(conn: SqliteConnection, ser: JsonSerializer) = /// Append revisions and permalinks to a page let appendPageRevisionsAndPermalinks (page : Page) = backgroundTask { use cmd = conn.CreateCommand () - cmd.Parameters.AddWithValue ("@pageId", page.Id.Value) |> ignore + cmd.Parameters.AddWithValue ("@pageId", string page.Id) |> ignore cmd.CommandText <- "SELECT permalink FROM page_permalink WHERE page_id = @pageId" use! rdr = cmd.ExecuteReaderAsync () @@ -57,11 +57,11 @@ type SQLitePageData(conn: SqliteConnection, ser: JsonSerializer) = return () else use cmd = conn.CreateCommand () - [ cmd.Parameters.AddWithValue ("@pageId", pageId.Value) + [ cmd.Parameters.AddWithValue ("@pageId", string pageId) cmd.Parameters.Add ("@link", SqliteType.Text) ] |> ignore let runCmd (link: Permalink) = backgroundTask { - cmd.Parameters["@link"].Value <- link.Value + cmd.Parameters["@link"].Value <- string link do! write cmd } cmd.CommandText <- "DELETE FROM page_permalink WHERE page_id = @pageId AND permalink = @link" @@ -85,10 +85,10 @@ type SQLitePageData(conn: SqliteConnection, ser: JsonSerializer) = use cmd = conn.CreateCommand () let runCmd withText rev = backgroundTask { cmd.Parameters.Clear () - [ cmd.Parameters.AddWithValue ("@pageId", pageId.Value) + [ cmd.Parameters.AddWithValue ("@pageId", string pageId) cmd.Parameters.AddWithValue ("@asOf", instantParam rev.AsOf) ] |> ignore - if withText then cmd.Parameters.AddWithValue ("@text", rev.Text.Value) |> ignore + if withText then cmd.Parameters.AddWithValue ("@text", string rev.Text) |> ignore do! write cmd } cmd.CommandText <- "DELETE FROM page_revision WHERE page_id = @pageId AND as_of = @asOf" @@ -157,7 +157,7 @@ type SQLitePageData(conn: SqliteConnection, ser: JsonSerializer) = let findById (pageId: PageId) webLogId = backgroundTask { use cmd = conn.CreateCommand () cmd.CommandText <- "SELECT * FROM page WHERE id = @id" - cmd.Parameters.AddWithValue ("@id", pageId.Value) |> ignore + cmd.Parameters.AddWithValue ("@id", string pageId) |> ignore use! rdr = cmd.ExecuteReaderAsync () return verifyWebLog webLogId (_.WebLogId) (Map.toPage ser) rdr } @@ -175,7 +175,7 @@ type SQLitePageData(conn: SqliteConnection, ser: JsonSerializer) = match! findById pageId webLogId with | Some _ -> use cmd = conn.CreateCommand () - cmd.Parameters.AddWithValue ("@id", pageId.Value) |> ignore + cmd.Parameters.AddWithValue ("@id", string pageId) |> ignore cmd.CommandText <- "DELETE FROM page_revision WHERE page_id = @id; DELETE FROM page_permalink WHERE page_id = @id; @@ -190,15 +190,15 @@ type SQLitePageData(conn: SqliteConnection, ser: JsonSerializer) = use cmd = conn.CreateCommand () cmd.CommandText <- "SELECT * FROM page WHERE web_log_id = @webLogId AND permalink = @link" addWebLogId cmd webLogId - cmd.Parameters.AddWithValue ("@link", permalink.Value) |> ignore + cmd.Parameters.AddWithValue ("@link", string permalink) |> ignore use! rdr = cmd.ExecuteReaderAsync () return if rdr.Read () then Some (toPage rdr) else None } /// Find the current permalink within a set of potential prior permalinks for the given web log - let findCurrentPermalink permalinks webLogId = backgroundTask { + let findCurrentPermalink (permalinks: Permalink list) webLogId = backgroundTask { use cmd = conn.CreateCommand () - let linkSql, linkParams = inClause "AND pp.permalink" "link" (fun (it: Permalink) -> it.Value) permalinks + let linkSql, linkParams = inClause "AND pp.permalink" "link" string permalinks cmd.CommandText <- $" SELECT p.permalink FROM page p diff --git a/src/MyWebLog.Data/SQLite/SQLitePostData.fs b/src/MyWebLog.Data/SQLite/SQLitePostData.fs index c12ecab..97c02b4 100644 --- a/src/MyWebLog.Data/SQLite/SQLitePostData.fs +++ b/src/MyWebLog.Data/SQLite/SQLitePostData.fs @@ -14,12 +14,12 @@ type SQLitePostData(conn: SqliteConnection, ser: JsonSerializer) = /// Add parameters for post INSERT or UPDATE statements let addPostParameters (cmd: SqliteCommand) (post: Post) = - [ cmd.Parameters.AddWithValue ("@id", post.Id.Value) - cmd.Parameters.AddWithValue ("@webLogId", WebLogId.toString post.WebLogId) - cmd.Parameters.AddWithValue ("@authorId", WebLogUserId.toString post.AuthorId) - cmd.Parameters.AddWithValue ("@status", post.Status.Value) + [ cmd.Parameters.AddWithValue ("@id", string post.Id) + cmd.Parameters.AddWithValue ("@webLogId", string post.WebLogId) + cmd.Parameters.AddWithValue ("@authorId", string post.AuthorId) + cmd.Parameters.AddWithValue ("@status", string post.Status) cmd.Parameters.AddWithValue ("@title", post.Title) - cmd.Parameters.AddWithValue ("@permalink", post.Permalink.Value) + cmd.Parameters.AddWithValue ("@permalink", string post.Permalink) cmd.Parameters.AddWithValue ("@publishedOn", maybeInstant post.PublishedOn) cmd.Parameters.AddWithValue ("@updatedOn", instantParam post.UpdatedOn) cmd.Parameters.AddWithValue ("@template", maybe post.Template) @@ -34,7 +34,7 @@ type SQLitePostData(conn: SqliteConnection, ser: JsonSerializer) = /// Append category IDs and tags to a post let appendPostCategoryAndTag (post: Post) = backgroundTask { use cmd = conn.CreateCommand () - cmd.Parameters.AddWithValue ("@id", post.Id.Value) |> ignore + cmd.Parameters.AddWithValue ("@id", string post.Id) |> ignore cmd.CommandText <- "SELECT category_id AS id FROM post_category WHERE post_id = @id" use! rdr = cmd.ExecuteReaderAsync () @@ -49,7 +49,7 @@ type SQLitePostData(conn: SqliteConnection, ser: JsonSerializer) = /// Append revisions and permalinks to a post let appendPostRevisionsAndPermalinks (post: Post) = backgroundTask { use cmd = conn.CreateCommand () - cmd.Parameters.AddWithValue ("@postId", post.Id.Value) |> ignore + cmd.Parameters.AddWithValue ("@postId", string post.Id) |> ignore cmd.CommandText <- "SELECT permalink FROM post_permalink WHERE post_id = @postId" use! rdr = cmd.ExecuteReaderAsync () @@ -72,7 +72,7 @@ type SQLitePostData(conn: SqliteConnection, ser: JsonSerializer) = let findPostById (postId: PostId) webLogId = backgroundTask { use cmd = conn.CreateCommand () cmd.CommandText <- $"{selectPost} WHERE p.id = @id" - cmd.Parameters.AddWithValue ("@id", postId.Value) |> ignore + cmd.Parameters.AddWithValue ("@id", string postId) |> ignore use! rdr = cmd.ExecuteReaderAsync () return verifyWebLog webLogId (_.WebLogId) toPost rdr } @@ -83,16 +83,16 @@ type SQLitePostData(conn: SqliteConnection, ser: JsonSerializer) = /// Update a post's assigned categories let updatePostCategories (postId: PostId) oldCats newCats = backgroundTask { - let toDelete, toAdd = Utils.diffLists oldCats newCats _.Value + let toDelete, toAdd = Utils.diffLists oldCats newCats string if List.isEmpty toDelete && List.isEmpty toAdd then return () else use cmd = conn.CreateCommand () - [ cmd.Parameters.AddWithValue ("@postId", postId.Value) + [ cmd.Parameters.AddWithValue ("@postId", string postId) cmd.Parameters.Add ("@categoryId", SqliteType.Text) ] |> ignore let runCmd (catId: CategoryId) = backgroundTask { - cmd.Parameters["@categoryId"].Value <- catId.Value + cmd.Parameters["@categoryId"].Value <- string catId do! write cmd } cmd.CommandText <- "DELETE FROM post_category WHERE post_id = @postId AND category_id = @categoryId" @@ -114,7 +114,7 @@ type SQLitePostData(conn: SqliteConnection, ser: JsonSerializer) = return () else use cmd = conn.CreateCommand () - [ cmd.Parameters.AddWithValue ("@postId", postId.Value) + [ cmd.Parameters.AddWithValue ("@postId", string postId) cmd.Parameters.Add ("@tag", SqliteType.Text) ] |> ignore let runCmd (tag: string) = backgroundTask { @@ -140,11 +140,11 @@ type SQLitePostData(conn: SqliteConnection, ser: JsonSerializer) = return () else use cmd = conn.CreateCommand () - [ cmd.Parameters.AddWithValue ("@postId", postId.Value) + [ cmd.Parameters.AddWithValue ("@postId", string postId) cmd.Parameters.Add ("@link", SqliteType.Text) ] |> ignore let runCmd (link: Permalink) = backgroundTask { - cmd.Parameters["@link"].Value <- link.Value + cmd.Parameters["@link"].Value <- string link do! write cmd } cmd.CommandText <- "DELETE FROM post_permalink WHERE post_id = @postId AND permalink = @link" @@ -168,10 +168,10 @@ type SQLitePostData(conn: SqliteConnection, ser: JsonSerializer) = use cmd = conn.CreateCommand () let runCmd withText rev = backgroundTask { cmd.Parameters.Clear () - [ cmd.Parameters.AddWithValue ("@postId", postId.Value) + [ cmd.Parameters.AddWithValue ("@postId", string postId) cmd.Parameters.AddWithValue ("@asOf", instantParam rev.AsOf) ] |> ignore - if withText then cmd.Parameters.AddWithValue ("@text", rev.Text.Value) |> ignore + if withText then cmd.Parameters.AddWithValue ("@text", string rev.Text) |> ignore do! write cmd } cmd.CommandText <- "DELETE FROM post_revision WHERE post_id = @postId AND as_of = @asOf" @@ -212,7 +212,7 @@ type SQLitePostData(conn: SqliteConnection, ser: JsonSerializer) = use cmd = conn.CreateCommand () cmd.CommandText <- "SELECT COUNT(id) FROM post WHERE web_log_id = @webLogId AND status = @status" addWebLogId cmd webLogId - cmd.Parameters.AddWithValue ("@status", status.Value) |> ignore + cmd.Parameters.AddWithValue ("@status", string status) |> ignore return! count cmd } @@ -230,7 +230,7 @@ type SQLitePostData(conn: SqliteConnection, ser: JsonSerializer) = use cmd = conn.CreateCommand () cmd.CommandText <- $"{selectPost} WHERE p.web_log_id = @webLogId AND p.permalink = @link" addWebLogId cmd webLogId - cmd.Parameters.AddWithValue ("@link", permalink.Value) |> ignore + cmd.Parameters.AddWithValue ("@link", string permalink) |> ignore use! rdr = cmd.ExecuteReaderAsync () if rdr.Read () then let! post = appendPostCategoryAndTag (toPost rdr) @@ -253,7 +253,7 @@ type SQLitePostData(conn: SqliteConnection, ser: JsonSerializer) = match! findFullById postId webLogId with | Some _ -> use cmd = conn.CreateCommand () - cmd.Parameters.AddWithValue ("@id", postId.Value) |> ignore + cmd.Parameters.AddWithValue ("@id", string postId) |> ignore cmd.CommandText <- "DELETE FROM post_revision WHERE post_id = @id; DELETE FROM post_permalink WHERE post_id = @id; @@ -267,9 +267,9 @@ type SQLitePostData(conn: SqliteConnection, ser: JsonSerializer) = } /// Find the current permalink from a list of potential prior permalinks for the given web log - let findCurrentPermalink permalinks webLogId = backgroundTask { + let findCurrentPermalink (permalinks: Permalink list) webLogId = backgroundTask { use cmd = conn.CreateCommand () - let linkSql, linkParams = inClause "AND pp.permalink" "link" (fun (it: Permalink) -> it.Value) permalinks + let linkSql, linkParams = inClause "AND pp.permalink" "link" string permalinks cmd.CommandText <- $" SELECT p.permalink FROM post p @@ -299,9 +299,9 @@ type SQLitePostData(conn: SqliteConnection, ser: JsonSerializer) = } /// Get a page of categorized posts for the given web log (excludes revisions and prior permalinks) - let findPageOfCategorizedPosts webLogId categoryIds pageNbr postsPerPage = backgroundTask { + let findPageOfCategorizedPosts webLogId (categoryIds: CategoryId list) pageNbr postsPerPage = backgroundTask { use cmd = conn.CreateCommand () - let catSql, catParams = inClause "AND pc.category_id" "catId" (fun (it: CategoryId) -> it.Value) categoryIds + let catSql, catParams = inClause "AND pc.category_id" "catId" string categoryIds cmd.CommandText <- $" {selectPost} INNER JOIN post_category pc ON pc.post_id = p.id @@ -311,7 +311,7 @@ type SQLitePostData(conn: SqliteConnection, ser: JsonSerializer) = ORDER BY published_on DESC LIMIT {postsPerPage + 1} OFFSET {(pageNbr - 1) * postsPerPage}" addWebLogId cmd webLogId - cmd.Parameters.AddWithValue ("@status", Published.Value) |> ignore + cmd.Parameters.AddWithValue ("@status", string Published) |> ignore cmd.Parameters.AddRange catParams use! rdr = cmd.ExecuteReaderAsync () let! posts = @@ -348,7 +348,7 @@ type SQLitePostData(conn: SqliteConnection, ser: JsonSerializer) = ORDER BY p.published_on DESC LIMIT {postsPerPage + 1} OFFSET {(pageNbr - 1) * postsPerPage}" addWebLogId cmd webLogId - cmd.Parameters.AddWithValue ("@status", Published.Value) |> ignore + cmd.Parameters.AddWithValue ("@status", string Published) |> ignore use! rdr = cmd.ExecuteReaderAsync () let! posts = toList toPost rdr @@ -369,7 +369,7 @@ type SQLitePostData(conn: SqliteConnection, ser: JsonSerializer) = ORDER BY p.published_on DESC LIMIT {postsPerPage + 1} OFFSET {(pageNbr - 1) * postsPerPage}" addWebLogId cmd webLogId - [ cmd.Parameters.AddWithValue ("@status", Published.Value) + [ cmd.Parameters.AddWithValue ("@status", string Published) cmd.Parameters.AddWithValue ("@tag", tag) ] |> ignore use! rdr = cmd.ExecuteReaderAsync () @@ -391,7 +391,7 @@ type SQLitePostData(conn: SqliteConnection, ser: JsonSerializer) = ORDER BY p.published_on DESC LIMIT 1" addWebLogId cmd webLogId - [ cmd.Parameters.AddWithValue ("@status", Published.Value) + [ cmd.Parameters.AddWithValue ("@status", string Published) cmd.Parameters.AddWithValue ("@publishedOn", instantParam publishedOn) ] |> ignore use! rdr = cmd.ExecuteReaderAsync () diff --git a/src/MyWebLog.Data/SQLite/SQLiteTagMapData.fs b/src/MyWebLog.Data/SQLite/SQLiteTagMapData.fs index 00de07b..d17d203 100644 --- a/src/MyWebLog.Data/SQLite/SQLiteTagMapData.fs +++ b/src/MyWebLog.Data/SQLite/SQLiteTagMapData.fs @@ -8,12 +8,12 @@ open MyWebLog.Data type SQLiteTagMapData (conn : SqliteConnection) = /// Find a tag mapping by its ID for the given web log - let findById tagMapId webLogId = backgroundTask { - use cmd = conn.CreateCommand () + let findById (tagMapId: TagMapId) webLogId = backgroundTask { + use cmd = conn.CreateCommand() cmd.CommandText <- "SELECT * FROM tag_map WHERE id = @id" - cmd.Parameters.AddWithValue ("@id", TagMapId.toString tagMapId) |> ignore - use! rdr = cmd.ExecuteReaderAsync () - return Helpers.verifyWebLog webLogId (fun tm -> tm.WebLogId) Map.toTagMap rdr + cmd.Parameters.AddWithValue ("@id", string tagMapId) |> ignore + use! rdr = cmd.ExecuteReaderAsync() + return verifyWebLog webLogId (_.WebLogId) Map.toTagMap rdr } /// Delete a tag mapping for the given web log @@ -22,7 +22,7 @@ type SQLiteTagMapData (conn : SqliteConnection) = | Some _ -> use cmd = conn.CreateCommand () cmd.CommandText <- "DELETE FROM tag_map WHERE id = @id" - cmd.Parameters.AddWithValue ("@id", TagMapId.toString tagMapId) |> ignore + cmd.Parameters.AddWithValue ("@id", string tagMapId) |> ignore do! write cmd return true | None -> return false @@ -81,7 +81,7 @@ type SQLiteTagMapData (conn : SqliteConnection) = @id, @webLogId, @tag, @urlValue )" addWebLogId cmd tagMap.WebLogId - [ cmd.Parameters.AddWithValue ("@id", TagMapId.toString tagMap.Id) + [ cmd.Parameters.AddWithValue ("@id", string tagMap.Id) cmd.Parameters.AddWithValue ("@tag", tagMap.Tag) cmd.Parameters.AddWithValue ("@urlValue", tagMap.UrlValue) ] |> ignore diff --git a/src/MyWebLog.Data/SQLite/SQLiteThemeData.fs b/src/MyWebLog.Data/SQLite/SQLiteThemeData.fs index dd3d81b..ff5403b 100644 --- a/src/MyWebLog.Data/SQLite/SQLiteThemeData.fs +++ b/src/MyWebLog.Data/SQLite/SQLiteThemeData.fs @@ -27,19 +27,19 @@ type SQLiteThemeData (conn : SqliteConnection) = } /// Does a given theme exist? - let exists themeId = backgroundTask { + let exists (themeId: ThemeId) = backgroundTask { use cmd = conn.CreateCommand () cmd.CommandText <- "SELECT COUNT(id) FROM theme WHERE id = @id" - cmd.Parameters.AddWithValue ("@id", ThemeId.toString themeId) |> ignore + cmd.Parameters.AddWithValue ("@id", string themeId) |> ignore let! count = count cmd return count > 0 } /// Find a theme by its ID - let findById themeId = backgroundTask { + let findById (themeId: ThemeId) = backgroundTask { use cmd = conn.CreateCommand () cmd.CommandText <- "SELECT * FROM theme WHERE id = @id" - cmd.Parameters.AddWithValue ("@id", ThemeId.toString themeId) |> ignore + cmd.Parameters.AddWithValue ("@id", string themeId) |> ignore use! rdr = cmd.ExecuteReaderAsync () if rdr.Read () then let theme = Map.toTheme rdr @@ -71,29 +71,28 @@ type SQLiteThemeData (conn : SqliteConnection) = "DELETE FROM theme_asset WHERE theme_id = @id; DELETE FROM theme_template WHERE theme_id = @id; DELETE FROM theme WHERE id = @id" - cmd.Parameters.AddWithValue ("@id", ThemeId.toString themeId) |> ignore + cmd.Parameters.AddWithValue ("@id", string themeId) |> ignore do! write cmd return true | None -> return false } /// Save a theme - let save (theme : Theme) = backgroundTask { - use cmd = conn.CreateCommand () + let save (theme: Theme) = backgroundTask { + use cmd = conn.CreateCommand() let! oldTheme = findById theme.Id cmd.CommandText <- match oldTheme with | Some _ -> "UPDATE theme SET name = @name, version = @version WHERE id = @id" | None -> "INSERT INTO theme VALUES (@id, @name, @version)" - [ cmd.Parameters.AddWithValue ("@id", ThemeId.toString theme.Id) + [ cmd.Parameters.AddWithValue ("@id", string theme.Id) cmd.Parameters.AddWithValue ("@name", theme.Name) cmd.Parameters.AddWithValue ("@version", theme.Version) ] |> ignore do! write cmd let toDelete, toAdd = - Utils.diffLists (oldTheme |> Option.map (fun t -> t.Templates) |> Option.defaultValue []) - theme.Templates (fun t -> t.Name) + Utils.diffLists (oldTheme |> Option.map _.Templates |> Option.defaultValue []) theme.Templates _.Name let toUpdate = theme.Templates |> List.filter (fun t -> @@ -102,7 +101,7 @@ type SQLiteThemeData (conn : SqliteConnection) = cmd.CommandText <- "UPDATE theme_template SET template = @template WHERE theme_id = @themeId AND name = @name" cmd.Parameters.Clear () - [ cmd.Parameters.AddWithValue ("@themeId", ThemeId.toString theme.Id) + [ cmd.Parameters.AddWithValue ("@themeId", string theme.Id) cmd.Parameters.Add ("@name", SqliteType.Text) cmd.Parameters.Add ("@template", SqliteType.Text) ] |> ignore @@ -157,10 +156,10 @@ type SQLiteThemeAssetData (conn : SqliteConnection) = } /// Delete all assets for the given theme - let deleteByTheme themeId = backgroundTask { + let deleteByTheme (themeId: ThemeId) = backgroundTask { use cmd = conn.CreateCommand () cmd.CommandText <- "DELETE FROM theme_asset WHERE theme_id = @themeId" - cmd.Parameters.AddWithValue ("@themeId", ThemeId.toString themeId) |> ignore + cmd.Parameters.AddWithValue ("@themeId", string themeId) |> ignore do! write cmd } @@ -177,19 +176,19 @@ type SQLiteThemeAssetData (conn : SqliteConnection) = } /// Get theme assets for the given theme (excludes data) - let findByTheme themeId = backgroundTask { + let findByTheme (themeId: ThemeId) = backgroundTask { use cmd = conn.CreateCommand () cmd.CommandText <- "SELECT theme_id, path, updated_on FROM theme_asset WHERE theme_id = @themeId" - cmd.Parameters.AddWithValue ("@themeId", ThemeId.toString themeId) |> ignore + cmd.Parameters.AddWithValue ("@themeId", string themeId) |> ignore use! rdr = cmd.ExecuteReaderAsync () return toList (Map.toThemeAsset false) rdr } /// Get theme assets for the given theme - let findByThemeWithData themeId = backgroundTask { + let findByThemeWithData (themeId: ThemeId) = backgroundTask { use cmd = conn.CreateCommand () cmd.CommandText <- "SELECT *, ROWID FROM theme_asset WHERE theme_id = @themeId" - cmd.Parameters.AddWithValue ("@themeId", ThemeId.toString themeId) |> ignore + cmd.Parameters.AddWithValue ("@themeId", string themeId) |> ignore use! rdr = cmd.ExecuteReaderAsync () return toList (Map.toThemeAsset true) rdr } diff --git a/src/MyWebLog.Data/SQLite/SQLiteUploadData.fs b/src/MyWebLog.Data/SQLite/SQLiteUploadData.fs index 3614b79..cf915ae 100644 --- a/src/MyWebLog.Data/SQLite/SQLiteUploadData.fs +++ b/src/MyWebLog.Data/SQLite/SQLiteUploadData.fs @@ -5,14 +5,14 @@ open Microsoft.Data.Sqlite open MyWebLog open MyWebLog.Data -/// SQLite myWebLog web log data implementation -type SQLiteUploadData (conn : SqliteConnection) = +/// SQLite myWebLog web log data implementation +type SQLiteUploadData(conn: SqliteConnection) = /// Add parameters for uploaded file INSERT and UPDATE statements - let addUploadParameters (cmd : SqliteCommand) (upload : Upload) = - [ cmd.Parameters.AddWithValue ("@id", UploadId.toString upload.Id) - cmd.Parameters.AddWithValue ("@webLogId", WebLogId.toString upload.WebLogId) - cmd.Parameters.AddWithValue ("@path", upload.Path.Value) + let addUploadParameters (cmd: SqliteCommand) (upload: Upload) = + [ cmd.Parameters.AddWithValue ("@id", string upload.Id) + cmd.Parameters.AddWithValue ("@webLogId", string upload.WebLogId) + cmd.Parameters.AddWithValue ("@path", string upload.Path) cmd.Parameters.AddWithValue ("@updatedOn", instantParam upload.UpdatedOn) cmd.Parameters.AddWithValue ("@dataLength", upload.Data.Length) ] |> ignore @@ -46,14 +46,14 @@ type SQLiteUploadData (conn : SqliteConnection) = WHERE id = @id AND web_log_id = @webLogId" addWebLogId cmd webLogId - cmd.Parameters.AddWithValue ("@id", UploadId.toString uploadId) |> ignore + cmd.Parameters.AddWithValue ("@id", string uploadId) |> ignore let! rdr = cmd.ExecuteReaderAsync () if (rdr.Read ()) then let upload = Map.toUpload false rdr do! rdr.CloseAsync () cmd.CommandText <- "DELETE FROM upload WHERE id = @id AND web_log_id = @webLogId" do! write cmd - return Ok upload.Path.Value + return Ok (string upload.Path) else return Error $"""Upload ID {cmd.Parameters["@id"]} not found""" } diff --git a/src/MyWebLog.Data/SQLite/SQLiteWebLogData.fs b/src/MyWebLog.Data/SQLite/SQLiteWebLogData.fs index 11a347c..ea2b4ea 100644 --- a/src/MyWebLog.Data/SQLite/SQLiteWebLogData.fs +++ b/src/MyWebLog.Data/SQLite/SQLiteWebLogData.fs @@ -9,8 +9,8 @@ open Newtonsoft.Json // The web log podcast insert loop is not statically compilable; this is OK #nowarn "3511" -/// SQLite myWebLog web log data implementation -type SQLiteWebLogData (conn : SqliteConnection, ser : JsonSerializer) = +/// SQLite myWebLog web log data implementation +type SQLiteWebLogData(conn: SqliteConnection, ser: JsonSerializer) = // SUPPORT FUNCTIONS @@ -25,28 +25,28 @@ type SQLiteWebLogData (conn : SqliteConnection, ser : JsonSerializer) = ] |> ignore /// Add parameters for web log INSERT or UPDATE statements - let addWebLogParameters (cmd : SqliteCommand) (webLog : WebLog) = - [ cmd.Parameters.AddWithValue ("@id", WebLogId.toString webLog.Id) + let addWebLogParameters (cmd: SqliteCommand) (webLog: WebLog) = + [ cmd.Parameters.AddWithValue ("@id", string webLog.Id) cmd.Parameters.AddWithValue ("@name", webLog.Name) cmd.Parameters.AddWithValue ("@slug", webLog.Slug) cmd.Parameters.AddWithValue ("@subtitle", maybe webLog.Subtitle) cmd.Parameters.AddWithValue ("@defaultPage", webLog.DefaultPage) cmd.Parameters.AddWithValue ("@postsPerPage", webLog.PostsPerPage) - cmd.Parameters.AddWithValue ("@themeId", ThemeId.toString webLog.ThemeId) + cmd.Parameters.AddWithValue ("@themeId", string webLog.ThemeId) cmd.Parameters.AddWithValue ("@urlBase", webLog.UrlBase) cmd.Parameters.AddWithValue ("@timeZone", webLog.TimeZone) cmd.Parameters.AddWithValue ("@autoHtmx", webLog.AutoHtmx) - cmd.Parameters.AddWithValue ("@uploads", UploadDestination.toString webLog.Uploads) + cmd.Parameters.AddWithValue ("@uploads", string webLog.Uploads) cmd.Parameters.AddWithValue ("@redirectRules", Utils.serialize ser webLog.RedirectRules) ] |> ignore addWebLogRssParameters cmd webLog /// Add parameters for custom feed INSERT or UPDATE statements - let addCustomFeedParameters (cmd : SqliteCommand) webLogId (feed : CustomFeed) = - [ cmd.Parameters.AddWithValue ("@id", CustomFeedId.toString feed.Id) - cmd.Parameters.AddWithValue ("@webLogId", WebLogId.toString webLogId) - cmd.Parameters.AddWithValue ("@source", CustomFeedSource.toString feed.Source) - cmd.Parameters.AddWithValue ("@path", feed.Path.Value) + let addCustomFeedParameters (cmd: SqliteCommand) (webLogId: WebLogId) (feed: CustomFeed) = + [ cmd.Parameters.AddWithValue ("@id", string feed.Id) + cmd.Parameters.AddWithValue ("@webLogId", string webLogId) + cmd.Parameters.AddWithValue ("@source", string feed.Source) + cmd.Parameters.AddWithValue ("@path", string feed.Path) cmd.Parameters.AddWithValue ("@podcast", maybe (if Option.isSome feed.Podcast then Some (Utils.serialize ser feed.Podcast) else None)) @@ -74,7 +74,7 @@ type SQLiteWebLogData (conn : SqliteConnection, ser : JsonSerializer) = /// Update the custom feeds for a web log let updateCustomFeeds (webLog : WebLog) = backgroundTask { let! feeds = getCustomFeeds webLog - let toDelete, toAdd = Utils.diffLists feeds webLog.Rss.CustomFeeds (fun it -> $"{CustomFeedId.toString it.Id}") + let toDelete, toAdd = Utils.diffLists feeds webLog.Rss.CustomFeeds string let toId (feed : CustomFeed) = feed.Id let toUpdate = webLog.Rss.CustomFeeds @@ -85,7 +85,7 @@ type SQLiteWebLogData (conn : SqliteConnection, ser : JsonSerializer) = toDelete |> List.map (fun it -> backgroundTask { cmd.CommandText <- "DELETE FROM web_log_feed WHERE id = @id" - cmd.Parameters["@id"].Value <- CustomFeedId.toString it.Id + cmd.Parameters["@id"].Value <- string it.Id do! write cmd }) |> Task.WhenAll @@ -211,7 +211,7 @@ type SQLiteWebLogData (conn : SqliteConnection, ser : JsonSerializer) = use cmd = conn.CreateCommand () cmd.CommandText <- "UPDATE web_log SET redirect_rules = @redirectRules WHERE id = @id" cmd.Parameters.AddWithValue ("@redirectRules", Utils.serialize ser webLog.RedirectRules) |> ignore - cmd.Parameters.AddWithValue ("@id", WebLogId.toString webLog.Id) |> ignore + cmd.Parameters.AddWithValue ("@id", string webLog.Id) |> ignore do! write cmd } @@ -228,7 +228,7 @@ type SQLiteWebLogData (conn : SqliteConnection, ser : JsonSerializer) = copyright = @copyright WHERE id = @id" addWebLogRssParameters cmd webLog - cmd.Parameters.AddWithValue ("@id", WebLogId.toString webLog.Id) |> ignore + cmd.Parameters.AddWithValue ("@id", string webLog.Id) |> ignore do! write cmd do! updateCustomFeeds webLog } diff --git a/src/MyWebLog.Data/SQLite/SQLiteWebLogUserData.fs b/src/MyWebLog.Data/SQLite/SQLiteWebLogUserData.fs index f99bf05..20a6056 100644 --- a/src/MyWebLog.Data/SQLite/SQLiteWebLogUserData.fs +++ b/src/MyWebLog.Data/SQLite/SQLiteWebLogUserData.fs @@ -4,22 +4,22 @@ open Microsoft.Data.Sqlite open MyWebLog open MyWebLog.Data -/// SQLite myWebLog user data implementation -type SQLiteWebLogUserData (conn : SqliteConnection) = +/// SQLite myWebLog user data implementation +type SQLiteWebLogUserData(conn: SqliteConnection) = // SUPPORT FUNCTIONS /// Add parameters for web log user INSERT or UPDATE statements - let addWebLogUserParameters (cmd : SqliteCommand) (user : WebLogUser) = - [ cmd.Parameters.AddWithValue ("@id", WebLogUserId.toString user.Id) - cmd.Parameters.AddWithValue ("@webLogId", WebLogId.toString user.WebLogId) + let addWebLogUserParameters (cmd: SqliteCommand) (user: WebLogUser) = + [ cmd.Parameters.AddWithValue ("@id", string user.Id) + cmd.Parameters.AddWithValue ("@webLogId", string user.WebLogId) cmd.Parameters.AddWithValue ("@email", user.Email) cmd.Parameters.AddWithValue ("@firstName", user.FirstName) cmd.Parameters.AddWithValue ("@lastName", user.LastName) cmd.Parameters.AddWithValue ("@preferredName", user.PreferredName) cmd.Parameters.AddWithValue ("@passwordHash", user.PasswordHash) cmd.Parameters.AddWithValue ("@url", maybe user.Url) - cmd.Parameters.AddWithValue ("@accessLevel", user.AccessLevel.Value) + cmd.Parameters.AddWithValue ("@accessLevel", string user.AccessLevel) cmd.Parameters.AddWithValue ("@createdOn", instantParam user.CreatedOn) cmd.Parameters.AddWithValue ("@lastSeenOn", maybeInstant user.LastSeenOn) ] |> ignore @@ -42,12 +42,12 @@ type SQLiteWebLogUserData (conn : SqliteConnection) = } /// Find a user by their ID for the given web log - let findById userId webLogId = backgroundTask { + let findById (userId: WebLogUserId) webLogId = backgroundTask { use cmd = conn.CreateCommand () cmd.CommandText <- "SELECT * FROM web_log_user WHERE id = @id" - cmd.Parameters.AddWithValue ("@id", WebLogUserId.toString userId) |> ignore + cmd.Parameters.AddWithValue ("@id", string userId) |> ignore use! rdr = cmd.ExecuteReaderAsync () - return Helpers.verifyWebLog webLogId (fun u -> u.WebLogId) Map.toWebLogUser rdr + return verifyWebLog webLogId (_.WebLogId) Map.toWebLogUser rdr } /// Delete a user if they have no posts or pages @@ -56,7 +56,7 @@ type SQLiteWebLogUserData (conn : SqliteConnection) = | Some _ -> use cmd = conn.CreateCommand () cmd.CommandText <- "SELECT COUNT(id) FROM page WHERE author_id = @userId" - cmd.Parameters.AddWithValue ("@userId", WebLogUserId.toString userId) |> ignore + cmd.Parameters.AddWithValue ("@userId", string userId) |> ignore let! pageCount = count cmd cmd.CommandText <- "SELECT COUNT(id) FROM post WHERE author_id = @userId" let! postCount = count cmd @@ -89,16 +89,15 @@ type SQLiteWebLogUserData (conn : SqliteConnection) = } /// Find the names of users by their IDs for the given web log - let findNames webLogId userIds = backgroundTask { + let findNames webLogId (userIds: WebLogUserId list) = backgroundTask { use cmd = conn.CreateCommand () - let nameSql, nameParams = inClause "AND id" "id" WebLogUserId.toString userIds + let nameSql, nameParams = inClause "AND id" "id" string userIds cmd.CommandText <- $"SELECT * FROM web_log_user WHERE web_log_id = @webLogId {nameSql}" addWebLogId cmd webLogId cmd.Parameters.AddRange nameParams use! rdr = cmd.ExecuteReaderAsync () return - toList Map.toWebLogUser rdr - |> List.map (fun u -> { Name = WebLogUserId.toString u.Id; Value = WebLogUser.displayName u }) + toList Map.toWebLogUser rdr |> List.map (fun u -> { Name = string u.Id; Value = WebLogUser.displayName u }) } /// Restore users from a backup @@ -108,7 +107,7 @@ type SQLiteWebLogUserData (conn : SqliteConnection) = } /// Set a user's last seen date/time to now - let setLastSeen userId webLogId = backgroundTask { + let setLastSeen (userId: WebLogUserId) webLogId = backgroundTask { use cmd = conn.CreateCommand () cmd.CommandText <- "UPDATE web_log_user @@ -116,7 +115,7 @@ type SQLiteWebLogUserData (conn : SqliteConnection) = WHERE id = @id AND web_log_id = @webLogId" addWebLogId cmd webLogId - [ cmd.Parameters.AddWithValue ("@id", WebLogUserId.toString userId) + [ cmd.Parameters.AddWithValue ("@id", string userId) cmd.Parameters.AddWithValue ("@lastSeenOn", instantParam (Noda.now ())) ] |> ignore let! _ = cmd.ExecuteNonQueryAsync () diff --git a/src/MyWebLog.Data/SQLiteData.fs b/src/MyWebLog.Data/SQLiteData.fs index c25d9c2..e30aafd 100644 --- a/src/MyWebLog.Data/SQLiteData.fs +++ b/src/MyWebLog.Data/SQLiteData.fs @@ -203,7 +203,7 @@ type SQLiteData (conn : SqliteConnection, log : ILogger, ser : JsonS |> List.iter (fun (feedId, podcast) -> cmd.CommandText <- "UPDATE web_log_feed SET podcast = @podcast WHERE id = @id" [ cmd.Parameters.AddWithValue ("@podcast", Utils.serialize ser podcast) - cmd.Parameters.AddWithValue ("@id", CustomFeedId.toString feedId) ] |> ignore + cmd.Parameters.AddWithValue ("@id", string feedId) ] |> ignore let _ = cmd.ExecuteNonQuery () cmd.Parameters.Clear ()) cmd.CommandText <- "SELECT * FROM post_episode" @@ -241,7 +241,7 @@ type SQLiteData (conn : SqliteConnection, log : ILogger, ser : JsonS |> List.iter (fun (postId, episode) -> cmd.CommandText <- "UPDATE post SET episode = @episode WHERE id = @id" [ cmd.Parameters.AddWithValue ("@episode", Utils.serialize ser episode) - cmd.Parameters.AddWithValue ("@id", postId.Value) ] |> ignore + cmd.Parameters.AddWithValue ("@id", string postId) ] |> ignore let _ = cmd.ExecuteNonQuery () cmd.Parameters.Clear ()) diff --git a/src/MyWebLog.Data/Utils.fs b/src/MyWebLog.Data/Utils.fs index 285a5f2..2af59a7 100644 --- a/src/MyWebLog.Data/Utils.fs +++ b/src/MyWebLog.Data/Utils.fs @@ -12,7 +12,7 @@ let currentDbVersion = "v2.1" let rec orderByHierarchy (cats : Category list) parentId slugBase parentNames = seq { for cat in cats |> List.filter (fun c -> c.ParentId = parentId) do let fullSlug = (match slugBase with Some it -> $"{it}/" | None -> "") + cat.Slug - { Id = cat.Id.Value + { Id = string cat.Id Slug = fullSlug Name = cat.Name Description = cat.Description @@ -29,16 +29,16 @@ let diffLists<'T, 'U when 'U: equality> oldItems newItems (f: 'T -> 'U) = List.filter (diff newItems) oldItems, List.filter (diff oldItems) newItems /// Find meta items added and removed -let diffMetaItems (oldItems : MetaItem list) newItems = +let diffMetaItems (oldItems: MetaItem list) newItems = diffLists oldItems newItems (fun item -> $"{item.Name}|{item.Value}") /// Find the permalinks added and removed -let diffPermalinks oldLinks newLinks = - diffLists oldLinks newLinks (fun (it: Permalink) -> it.Value) +let diffPermalinks (oldLinks: Permalink list) newLinks = + diffLists oldLinks newLinks string /// Find the revisions added and removed -let diffRevisions oldRevs newRevs = - diffLists oldRevs newRevs (fun (rev: Revision) -> $"{rev.AsOf.ToUnixTimeTicks()}|{rev.Text.Value}") +let diffRevisions (oldRevs: Revision list) newRevs = + diffLists oldRevs newRevs (fun rev -> $"{rev.AsOf.ToUnixTimeTicks()}|{rev.Text}") open MyWebLog.Converters open Newtonsoft.Json diff --git a/src/MyWebLog.Domain/DataTypes.fs b/src/MyWebLog.Domain/DataTypes.fs index ad0c0d1..01c6a39 100644 --- a/src/MyWebLog.Domain/DataTypes.fs +++ b/src/MyWebLog.Domain/DataTypes.fs @@ -32,7 +32,7 @@ module Category = /// An empty category let empty = { Id = CategoryId.Empty - WebLogId = WebLogId.empty + WebLogId = WebLogId.Empty Name = "" Slug = "" Description = None @@ -137,8 +137,8 @@ module Page = /// An empty page let empty = { Id = PageId.Empty - WebLogId = WebLogId.empty - AuthorId = WebLogUserId.empty + WebLogId = WebLogId.Empty + AuthorId = WebLogUserId.Empty Title = "" Permalink = Permalink.Empty PublishedOn = Noda.epoch @@ -210,8 +210,8 @@ module Post = /// An empty post let empty = { Id = PostId.Empty - WebLogId = WebLogId.empty - AuthorId = WebLogUserId.empty + WebLogId = WebLogId.Empty + AuthorId = WebLogUserId.Empty Status = Draft Title = "" Permalink = Permalink.Empty @@ -248,8 +248,8 @@ module TagMap = /// An empty tag mapping let empty = { - Id = TagMapId.empty - WebLogId = WebLogId.empty + Id = TagMapId.Empty + WebLogId = WebLogId.Empty Tag = "" UrlValue = "" } @@ -328,8 +328,8 @@ module Upload = /// An empty upload let empty = { - Id = UploadId.empty - WebLogId = WebLogId.empty + Id = UploadId.Empty + WebLogId = WebLogId.Empty Path = Permalink.Empty UpdatedOn = Noda.epoch Data = [||] @@ -384,7 +384,7 @@ module WebLog = /// An empty web log let empty = { - Id = WebLogId.empty + Id = WebLogId.Empty Name = "" Slug = "" Subtitle = None @@ -393,7 +393,7 @@ module WebLog = ThemeId = ThemeId "default" UrlBase = "" TimeZone = "" - Rss = RssOptions.empty + Rss = RssOptions.Empty AutoHtmx = false Uploads = Database RedirectRules = [] @@ -407,12 +407,12 @@ module WebLog = /// Generate an absolute URL for the given link let absoluteUrl webLog (permalink: Permalink) = - $"{webLog.UrlBase}/{permalink.Value}" + $"{webLog.UrlBase}/{permalink}" /// Generate a relative URL for the given link let relativeUrl webLog (permalink: Permalink) = let _, leadPath = hostAndPath webLog - $"{leadPath}/{permalink.Value}" + $"{leadPath}/{permalink}" /// Convert an Instant (UTC reference) to the web log's local date/time let localTime webLog (date: Instant) = @@ -463,8 +463,8 @@ module WebLogUser = /// An empty web log user let empty = { - Id = WebLogUserId.empty - WebLogId = WebLogId.empty + Id = WebLogUserId.Empty + WebLogId = WebLogId.Empty Email = "" FirstName = "" LastName = "" diff --git a/src/MyWebLog.Domain/SupportTypes.fs b/src/MyWebLog.Domain/SupportTypes.fs index ed25389..ee3c01c 100644 --- a/src/MyWebLog.Domain/SupportTypes.fs +++ b/src/MyWebLog.Domain/SupportTypes.fs @@ -54,16 +54,16 @@ type AccessLevel = | Administrator /// Parse an access level from its string representation - static member Parse = - function + static member Parse level = + match level with | "Author" -> Author | "Editor" -> Editor | "WebLogAdmin" -> WebLogAdmin | "Administrator" -> Administrator - | it -> invalidArg "level" $"{it} is not a valid access level" + | _ -> invalidArg (nameof level) $"{level} is not a valid access level" /// The string representation of this access level - member this.Value = + override this.ToString() = match this with | Author -> "Author" | Editor -> "Editor" @@ -96,7 +96,7 @@ type CategoryId = newId >> CategoryId /// The string representation of this category ID - member this.Value = + override this.ToString() = match this with CategoryId it -> it @@ -113,7 +113,7 @@ type CommentId = newId >> CommentId /// The string representation of this comment ID - member this.Value = + override this.ToString() = match this with CommentId it -> it @@ -128,15 +128,15 @@ type CommentStatus = | Spam /// Parse a string into a comment status - static member Parse = - function + static member Parse status = + match status with | "Approved" -> Approved | "Pending" -> Pending | "Spam" -> Spam - | it -> invalidArg "status" $"{it} is not a valid comment status" + | _ -> invalidArg (nameof status) $"{status} is not a valid comment status" /// Convert a comment status to a string - member this.Value = + override this.ToString() = match this with Approved -> "Approved" | Pending -> "Pending" | Spam -> "Spam" @@ -148,15 +148,15 @@ type ExplicitRating = | Clean /// Parse a string into an explicit rating - static member Parse = - function + static member Parse rating = + match rating with | "yes" -> Yes | "no" -> No | "clean" -> Clean - | it -> invalidArg "rating" $"{it} is not a valid explicit rating" + | _ -> invalidArg (nameof rating) $"{rating} is not a valid explicit rating" /// The string value of this rating - member this.Value = + override this.ToString() = match this with Yes -> "yes" | No -> "no" | Clean -> "clean" @@ -289,11 +289,11 @@ type MarkupText = | Html of string /// Parse a string into a MarkupText instance - static member Parse(it: string) = - match it with - | text when text.StartsWith "Markdown: " -> Markdown text[10..] - | text when text.StartsWith "HTML: " -> Html text[6..] - | text -> invalidOp $"Cannot derive type of text ({text})" + static member Parse(text: string) = + match text with + | _ when text.StartsWith "Markdown: " -> Markdown text[10..] + | _ when text.StartsWith "HTML: " -> Html text[6..] + | _ -> invalidArg (nameof text) $"Cannot derive type of text ({text})" /// The source type for the markup text member this.SourceType = @@ -304,7 +304,8 @@ type MarkupText = match this with Markdown text -> text | Html text -> text /// The string representation of the markup text - member this.Value = $"{this.SourceType}: {this.Text}" + override this.ToString() = + $"{this.SourceType}: {this.Text}" /// The HTML representation of the markup text member this.AsHtml() = @@ -315,10 +316,10 @@ type MarkupText = [] type MetaItem = { /// The name of the metadata value - Name : string + Name: string /// The metadata value - Value : string + Value: string } with /// An empty metadata item @@ -330,10 +331,10 @@ type MetaItem = { [] type Revision = { /// When this revision was saved - AsOf : Instant + AsOf: Instant /// The text of the revision - Text : MarkupText + Text: MarkupText } with /// An empty revision @@ -350,7 +351,7 @@ type Permalink = static member Empty = Permalink "" /// The string value of this permalink - member this.Value = + override this.ToString() = match this with Permalink it -> it @@ -367,7 +368,7 @@ type PageId = newId >> PageId /// The string value of this page ID - member this.Value = + override this.ToString() = match this with PageId it -> it @@ -383,8 +384,8 @@ type PodcastMedium = | Blog /// Parse a string into a podcast medium - static member Parse = - function + static member Parse medium = + match medium with | "podcast" -> Podcast | "music" -> Music | "video" -> Video @@ -392,10 +393,10 @@ type PodcastMedium = | "audiobook" -> Audiobook | "newsletter" -> Newsletter | "blog" -> Blog - | it -> invalidArg "medium" $"{it} is not a valid podcast medium" + | _ -> invalidArg (nameof medium) $"{medium} is not a valid podcast medium" /// The string value of this podcast medium - member this.Value = + override this.ToString() = match this with | Podcast -> "podcast" | Music -> "music" @@ -415,14 +416,14 @@ type PostStatus = | Published /// Parse a string into a post status - static member Parse = - function + static member Parse status = + match status with | "Draft" -> Draft | "Published" -> Published - | it -> invalidArg "status" $"{it} is not a valid post status" + | _ -> invalidArg (nameof status) $"{status} is not a valid post status" /// The string representation of this post status - member this.Value = + override this.ToString() = match this with Draft -> "Draft" | Published -> "Published" @@ -439,7 +440,7 @@ type PostId = newId >> PostId /// Convert a post ID to a string - member this.Value = + override this.ToString() = match this with PostId it -> it @@ -465,19 +466,20 @@ type RedirectRule = { /// An identifier for a custom feed -type CustomFeedId = CustomFeedId of string +[] +type CustomFeedId = + | CustomFeedId of string -/// Functions to support custom feed IDs -module CustomFeedId = - /// An empty custom feed ID - let empty = CustomFeedId "" - - /// Convert a custom feed ID to a string - let toString = function CustomFeedId pi -> pi + static member Empty = CustomFeedId "" /// Create a new custom feed ID - let create = newId >> CustomFeedId + static member Create = + newId >> CustomFeedId + + /// Convert a custom feed ID to a string + override this.ToString() = + match this with CustomFeedId it -> it /// The source for a custom feed @@ -486,99 +488,94 @@ type CustomFeedSource = | Category of CategoryId /// A feed based on a particular tag | Tag of string - -/// Functions to support feed sources -module CustomFeedSource = - /// Create a string version of a feed source - let toString : CustomFeedSource -> string = - function - | Category (CategoryId catId) -> $"category:{catId}" - | Tag tag -> $"tag:{tag}" /// Parse a feed source from its string version - let parse : string -> CustomFeedSource = + static member Parse(source: string) = let value (it : string) = it.Split(":").[1] - function - | source when source.StartsWith "category:" -> (value >> CategoryId >> Category) source - | source when source.StartsWith "tag:" -> (value >> Tag) source - | source -> invalidArg "feedSource" $"{source} is not a valid feed source" + match source with + | _ when source.StartsWith "category:" -> (value >> CategoryId >> Category) source + | _ when source.StartsWith "tag:" -> (value >> Tag) source + | _ -> invalidArg (nameof source) $"{source} is not a valid feed source" + + /// Create a string version of a feed source + override this.ToString() = + match this with | Category (CategoryId catId) -> $"category:{catId}" | Tag tag -> $"tag:{tag}" /// Options for a feed that describes a podcast +[] type PodcastOptions = { /// The title of the podcast - Title : string + Title: string /// A subtitle for the podcast - Subtitle : string option + Subtitle: string option /// The number of items in the podcast feed - ItemsInFeed : int + ItemsInFeed: int /// A summary of the podcast (iTunes field) - Summary : string + Summary: string /// The display name of the podcast author (iTunes field) - DisplayedAuthor : string + DisplayedAuthor: string /// The e-mail address of the user who registered the podcast at iTunes - Email : string + Email: string /// The link to the image for the podcast - ImageUrl : Permalink + ImageUrl: Permalink /// The category from Apple Podcasts (iTunes) under which this podcast is categorized - AppleCategory : string + AppleCategory: string /// A further refinement of the categorization of this podcast (Apple Podcasts/iTunes field / values) - AppleSubcategory : string option + AppleSubcategory: string option /// The explictness rating (iTunes field) - Explicit : ExplicitRating + Explicit: ExplicitRating /// The default media type for files in this podcast - DefaultMediaType : string option + DefaultMediaType: string option /// The base URL for relative URL media files for this podcast (optional; defaults to web log base) - MediaBaseUrl : string option + MediaBaseUrl: string option /// A GUID for this podcast - PodcastGuid : Guid option + PodcastGuid: Guid option /// A URL at which information on supporting the podcast may be found (supports permalinks) - FundingUrl : string option + FundingUrl: string option /// The text to be displayed in the funding item within the feed - FundingText : string option + FundingText: string option /// The medium (what the podcast IS, not what it is ABOUT) - Medium : PodcastMedium option + Medium: PodcastMedium option } /// A custom feed +[] type CustomFeed = { /// The ID of the custom feed - Id : CustomFeedId + Id: CustomFeedId /// The source for the custom feed - Source : CustomFeedSource + Source: CustomFeedSource /// The path for the custom feed - Path : Permalink + Path: Permalink /// Podcast options, if the feed defines a podcast - Podcast : PodcastOptions option -} - -/// Functions to support custom feeds -module CustomFeed = + Podcast: PodcastOptions option +} with /// An empty custom feed - let empty = { - Id = CustomFeedId "" - Source = Category (CategoryId "") - Path = Permalink "" + static member Empty = { + Id = CustomFeedId.Empty + Source = Category CategoryId.Empty + Path = Permalink.Empty Podcast = None } @@ -587,32 +584,29 @@ module CustomFeed = [] type RssOptions = { /// Whether the site feed of posts is enabled - IsFeedEnabled : bool + IsFeedEnabled: bool /// The name of the file generated for the site feed - FeedName : string + FeedName: string /// Override the "posts per page" setting for the site feed - ItemsInFeed : int option + ItemsInFeed: int option /// Whether feeds are enabled for all categories - IsCategoryEnabled : bool + IsCategoryEnabled: bool /// Whether feeds are enabled for all tags - IsTagEnabled : bool + IsTagEnabled: bool /// A copyright string to be placed in all feeds - Copyright : string option + Copyright: string option /// Custom feeds for this web log CustomFeeds: CustomFeed list -} - -/// Functions to support RSS options -module RssOptions = +} with /// An empty set of RSS options - let empty = { + static member Empty = { IsFeedEnabled = true FeedName = "feed.xml" ItemsInFeed = None @@ -624,126 +618,126 @@ module RssOptions = /// An identifier for a tag mapping -type TagMapId = TagMapId of string +[] +type TagMapId = + | TagMapId of string -/// Functions to support tag mapping IDs -module TagMapId = - /// An empty tag mapping ID - let empty = TagMapId "" - - /// Convert a tag mapping ID to a string - let toString = function TagMapId tmi -> tmi + static member Empty = TagMapId "" /// Create a new tag mapping ID - let create = newId >> TagMapId + static member Create = + newId >> TagMapId + + /// Convert a tag mapping ID to a string + override this.ToString() = + match this with TagMapId it -> it /// An identifier for a theme (represents its path) -type ThemeId = ThemeId of string - -/// Functions to support theme IDs -module ThemeId = - let toString = function ThemeId ti -> ti +[] +type ThemeId = + | ThemeId of string + + /// The string representation of a theme ID + override this.ToString() = + match this with ThemeId it -> it /// An identifier for a theme asset -type ThemeAssetId = ThemeAssetId of ThemeId * string +[] +type ThemeAssetId = + | ThemeAssetId of ThemeId * string -/// Functions to support theme asset IDs -module ThemeAssetId = + /// Convert a string into a theme asset ID + static member Parse(it : string) = + let themeIdx = it.IndexOf "/" + ThemeAssetId(ThemeId it[..(themeIdx - 1)], it[(themeIdx + 1)..]) /// Convert a theme asset ID into a path string - let toString = function ThemeAssetId (ThemeId theme, asset) -> $"{theme}/{asset}" - - /// Convert a string into a theme asset ID - let ofString (it : string) = - let themeIdx = it.IndexOf "/" - ThemeAssetId (ThemeId it[..(themeIdx - 1)], it[(themeIdx + 1)..]) + override this.ToString() = + match this with ThemeAssetId (ThemeId theme, asset) -> $"{theme}/{asset}" /// A template for a theme +[] type ThemeTemplate = { /// The name of the template - Name : string + Name: string /// The text of the template - Text : string -} - -/// Functions to support theme templates -module ThemeTemplate = + Text: string +} with /// An empty theme template - let empty = + static member Empty = { Name = ""; Text = "" } /// Where uploads should be placed +[] type UploadDestination = | Database | Disk -/// Functions to support upload destinations -module UploadDestination = - - /// Convert an upload destination to its string representation - let toString = function Database -> "Database" | Disk -> "Disk" - /// Parse an upload destination from its string representation - let parse value = - match value with + static member Parse destination = + match destination with | "Database" -> Database - | "Disk" -> Disk - | it -> invalidArg "destination" $"{it} is not a valid upload destination" + | "Disk" -> Disk + | _ -> invalidArg (nameof destination) $"{destination} is not a valid upload destination" + + /// The string representation of an upload destination + override this.ToString() = + match this with Database -> "Database" | Disk -> "Disk" /// An identifier for an upload -type UploadId = UploadId of string +[] +type UploadId = + | UploadId of string -/// Functions to support upload IDs -module UploadId = - /// An empty upload ID - let empty = UploadId "" - - /// Convert an upload ID to a string - let toString = function UploadId ui -> ui + static member Empty = UploadId "" /// Create a new upload ID - let create = newId >> UploadId + static member Create = + newId >> UploadId + + /// The string representation of an upload ID + override this.ToString() = + match this with UploadId it -> it /// An identifier for a web log -type WebLogId = WebLogId of string +[] +type WebLogId = + | WebLogId of string -/// Functions to support web log IDs -module WebLogId = - /// An empty web log ID - let empty = WebLogId "" - - /// Convert a web log ID to a string - let toString = function WebLogId wli -> wli + static member Empty = WebLogId "" /// Create a new web log ID - let create = newId >> WebLogId - + static member Create = + newId >> WebLogId + + /// Convert a web log ID to a string + override this.ToString() = + match this with WebLogId it -> it /// An identifier for a web log user -type WebLogUserId = WebLogUserId of string - -/// Functions to support web log user IDs -module WebLogUserId = +[] +type WebLogUserId = + | WebLogUserId of string /// An empty web log user ID - let empty = WebLogUserId "" - - /// Convert a web log user ID to a string - let toString = function WebLogUserId wli -> wli + static member Empty = WebLogUserId "" /// Create a new web log user ID - let create = newId >> WebLogUserId - - + static member Create = + newId >> WebLogUserId + + /// The string representation of a web log user ID + override this.ToString() = + match this with WebLogUserId it -> it diff --git a/src/MyWebLog.Domain/ViewModels.fs b/src/MyWebLog.Domain/ViewModels.fs index 2f9a176..2005de8 100644 --- a/src/MyWebLog.Domain/ViewModels.fs +++ b/src/MyWebLog.Domain/ViewModels.fs @@ -73,30 +73,30 @@ type DisplayCategory = { /// A display version of a custom feed definition type DisplayCustomFeed = { /// The ID of the custom feed - Id : string + Id: string /// The source of the custom feed - Source : string + Source: string /// The relative path at which the custom feed is served - Path : string + Path: string /// Whether this custom feed is for a podcast - IsPodcast : bool + IsPodcast: bool } /// Support functions for custom feed displays module DisplayCustomFeed = /// Create a display version from a custom feed - let fromFeed (cats: DisplayCategory[]) (feed: CustomFeed) : DisplayCustomFeed = + let fromFeed (cats: DisplayCategory array) (feed: CustomFeed) : DisplayCustomFeed = let source = match feed.Source with | Category (CategoryId catId) -> $"Category: {(cats |> Array.find (fun cat -> cat.Id = catId)).Name}" | Tag tag -> $"Tag: {tag}" - { Id = CustomFeedId.toString feed.Id + { Id = string feed.Id Source = source - Path = feed.Path.Value + Path = string feed.Path IsPodcast = Option.isSome feed.Podcast } @@ -137,14 +137,14 @@ type DisplayPage = /// Create a minimal display page (no text or metadata) from a database page static member FromPageMinimal webLog (page: Page) = { - Id = page.Id.Value - AuthorId = WebLogUserId.toString page.AuthorId + Id = string page.Id + AuthorId = string page.AuthorId Title = page.Title - Permalink = page.Permalink.Value + Permalink = string page.Permalink PublishedOn = WebLog.localTime webLog page.PublishedOn UpdatedOn = WebLog.localTime webLog page.UpdatedOn IsInPageList = page.IsInPageList - IsDefault = page.Id.Value = webLog.DefaultPage + IsDefault = string page.Id = webLog.DefaultPage Text = "" Metadata = [] } @@ -152,14 +152,14 @@ type DisplayPage = /// Create a display page from a database page static member FromPage webLog (page : Page) = let _, extra = WebLog.hostAndPath webLog - { Id = page.Id.Value - AuthorId = WebLogUserId.toString page.AuthorId + { Id = string page.Id + AuthorId = string page.AuthorId Title = page.Title - Permalink = page.Permalink.Value + Permalink = string page.Permalink PublishedOn = WebLog.localTime webLog page.PublishedOn UpdatedOn = WebLog.localTime webLog page.UpdatedOn IsInPageList = page.IsInPageList - IsDefault = page.Id.Value = webLog.DefaultPage + IsDefault = string page.Id = webLog.DefaultPage Text = addBaseToRelativeUrls extra page.Text Metadata = page.Metadata } @@ -195,35 +195,35 @@ open System.IO [] type DisplayTheme = { /// The ID / path slug of the theme - Id : string + Id: string /// The name of the theme - Name : string + Name: string /// The version of the theme - Version : string + Version: string /// How many templates are contained in the theme - TemplateCount : int + TemplateCount: int /// Whether the theme is in use by any web logs - IsInUse : bool + IsInUse: bool /// Whether the theme .zip file exists on the filesystem - IsOnDisk : bool + IsOnDisk: bool } /// Functions to support displaying themes module DisplayTheme = /// Create a display theme from a theme - let fromTheme inUseFunc (theme : Theme) = - { Id = ThemeId.toString theme.Id + let fromTheme inUseFunc (theme: Theme) = + { Id = string theme.Id Name = theme.Name Version = theme.Version TemplateCount = List.length theme.Templates IsInUse = inUseFunc theme.Id - IsOnDisk = File.Exists $"{ThemeId.toString theme.Id}-theme.zip" + IsOnDisk = File.Exists $"{theme.Id}-theme.zip" } @@ -231,33 +231,33 @@ module DisplayTheme = [] type DisplayUpload = { /// The ID of the uploaded file - Id : string + Id: string /// The name of the uploaded file - Name : string + Name: string /// The path at which the file is served - Path : string + Path: string /// The date/time the file was updated - UpdatedOn : DateTime option + UpdatedOn: DateTime option /// The source for this file (created from UploadDestination DU) - Source : string + Source: string } /// Functions to support displaying uploads module DisplayUpload = /// Create a display uploaded file - let fromUpload webLog source (upload : Upload) = - let path = upload.Path.Value + let fromUpload webLog (source: UploadDestination) (upload: Upload) = + let path = string upload.Path let name = Path.GetFileName path - { Id = UploadId.toString upload.Id + { Id = string upload.Id Name = name - Path = path.Replace (name, "") + Path = path.Replace(name, "") UpdatedOn = Some (WebLog.localTime webLog upload.UpdatedOn) - Source = UploadDestination.toString source + Source = string source } @@ -265,45 +265,45 @@ module DisplayUpload = [] type DisplayUser = { /// The ID of the user - Id : string + Id: string /// The user name (e-mail address) - Email : string + Email: string /// The user's first name - FirstName : string + FirstName: string /// The user's last name - LastName : string + LastName: string /// The user's preferred name - PreferredName : string + PreferredName: string /// The URL of the user's personal site - Url : string + Url: string /// The user's access level - AccessLevel : string + AccessLevel: string /// When the user was created - CreatedOn : DateTime + CreatedOn: DateTime /// When the user last logged on - LastSeenOn : Nullable + LastSeenOn: Nullable } /// Functions to support displaying a user's information module DisplayUser = /// Construct a displayed user from a web log user - let fromUser webLog (user : WebLogUser) = - { Id = WebLogUserId.toString user.Id + let fromUser webLog (user: WebLogUser) = + { Id = string user.Id Email = user.Email FirstName = user.FirstName LastName = user.LastName PreferredName = user.PreferredName Url = defaultArg user.Url "" - AccessLevel = user.AccessLevel.Value + AccessLevel = string user.AccessLevel CreatedOn = WebLog.localTime webLog user.CreatedOn LastSeenOn = user.LastSeenOn |> Option.map (WebLog.localTime webLog) |> Option.toNullable } @@ -311,30 +311,30 @@ module DisplayUser = /// View model for editing categories [] -type EditCategoryModel = - { /// The ID of the category being edited - CategoryId : string - - /// The name of the category - Name : string - - /// The category's URL slug - Slug : string - - /// A description of the category (optional) - Description : string - - /// The ID of the category for which this is a subcategory (optional) - ParentId : string - } +type EditCategoryModel = { + /// The ID of the category being edited + CategoryId: string + + /// The name of the category + Name: string + + /// The category's URL slug + Slug: string + + /// A description of the category (optional) + Description: string + + /// The ID of the category for which this is a subcategory (optional) + ParentId: string +} with /// Create an edit model from an existing category - static member fromCategory (cat : Category) = - { CategoryId = cat.Id.Value + static member fromCategory (cat: Category) = + { CategoryId = string cat.Id Name = cat.Name Slug = cat.Slug Description = defaultArg cat.Description "" - ParentId = cat.ParentId |> Option.map _.Value |> Option.defaultValue "" + ParentId = cat.ParentId |> Option.map string |> Option.defaultValue "" } /// Is this a new category? @@ -437,10 +437,10 @@ type EditCustomFeedModel = static member fromFeed (feed: CustomFeed) = let rss = { EditCustomFeedModel.empty with - Id = CustomFeedId.toString feed.Id + Id = string feed.Id SourceType = match feed.Source with Category _ -> "category" | Tag _ -> "tag" SourceValue = match feed.Source with Category (CategoryId catId) -> catId | Tag tag -> tag - Path = feed.Path.Value + Path = string feed.Path } match feed.Podcast with | Some p -> @@ -452,16 +452,16 @@ type EditCustomFeedModel = Summary = p.Summary DisplayedAuthor = p.DisplayedAuthor Email = p.Email - ImageUrl = p.ImageUrl.Value + ImageUrl = string p.ImageUrl AppleCategory = p.AppleCategory AppleSubcategory = defaultArg p.AppleSubcategory "" - Explicit = p.Explicit.Value + Explicit = string p.Explicit DefaultMediaType = defaultArg p.DefaultMediaType "" MediaBaseUrl = defaultArg p.MediaBaseUrl "" FundingUrl = defaultArg p.FundingUrl "" FundingText = defaultArg p.FundingText "" PodcastGuid = p.PodcastGuid |> Option.map _.ToString().ToLowerInvariant() |> Option.defaultValue "" - Medium = p.Medium |> Option.map _.Value |> Option.defaultValue "" + Medium = p.Medium |> Option.map string |> Option.defaultValue "" } | None -> rss @@ -562,9 +562,9 @@ type EditPageModel = { | Some rev -> rev | None -> Revision.Empty let page = if page.Metadata |> List.isEmpty then { page with Metadata = [ MetaItem.Empty ] } else page - { PageId = page.Id.Value + { PageId = string page.Id Title = page.Title - Permalink = page.Permalink.Value + Permalink = string page.Permalink Template = defaultArg page.Template "" IsShownInPageList = page.IsInPageList Source = latest.Text.SourceType @@ -580,7 +580,7 @@ type EditPageModel = { member this.UpdatePage (page: Page) now = let revision = { AsOf = now; Text = MarkupText.Parse $"{this.Source}: {this.Text}" } // Detect a permalink change, and add the prior one to the prior list - match page.Permalink.Value with + match string page.Permalink with | "" -> page | link when link = this.Permalink -> page | _ -> { page with PriorPermalinks = page.Permalink :: page.PriorPermalinks } @@ -715,15 +715,15 @@ type EditPostModel = { | None -> Revision.Empty let post = if post.Metadata |> List.isEmpty then { post with Metadata = [ MetaItem.Empty ] } else post let episode = defaultArg post.Episode Episode.Empty - { PostId = post.Id.Value + { PostId = string post.Id Title = post.Title - Permalink = post.Permalink.Value + Permalink = string post.Permalink Source = latest.Text.SourceType Text = latest.Text.Text Tags = String.Join (", ", post.Tags) Template = defaultArg post.Template "" - CategoryIds = post.CategoryIds |> List.map _.Value |> Array.ofList - Status = post.Status.Value + CategoryIds = post.CategoryIds |> List.map string |> Array.ofList + Status = string post.Status DoPublish = false MetaNames = post.Metadata |> List.map _.Name |> Array.ofList MetaValues = post.Metadata |> List.map _.Value |> Array.ofList @@ -737,7 +737,7 @@ type EditPostModel = { MediaType = defaultArg episode.MediaType "" ImageUrl = defaultArg episode.ImageUrl "" Subtitle = defaultArg episode.Subtitle "" - Explicit = defaultArg (episode.Explicit |> Option.map _.Value) "" + Explicit = defaultArg (episode.Explicit |> Option.map string) "" ChapterFile = defaultArg episode.ChapterFile "" ChapterType = defaultArg episode.ChapterType "" TranscriptUrl = defaultArg episode.TranscriptUrl "" @@ -757,7 +757,7 @@ type EditPostModel = { member this.UpdatePost (post: Post) now = let revision = { AsOf = now; Text = MarkupText.Parse $"{this.Source}: {this.Text}" } // Detect a permalink change, and add the prior one to the prior list - match post.Permalink.Value with + match string post.Permalink with | "" -> post | link when link = this.Permalink -> post | _ -> { post with PriorPermalinks = post.Permalink :: post.PriorPermalinks } @@ -916,7 +916,7 @@ type EditTagMapModel = /// Create an edit model from the tag mapping static member fromMapping (tagMap : TagMap) : EditTagMapModel = - { Id = TagMapId.toString tagMap.Id + { Id = string tagMap.Id Tag = tagMap.Tag UrlValue = tagMap.UrlValue } @@ -924,39 +924,39 @@ type EditTagMapModel = /// View model to display a user's information [] -type EditUserModel = - { /// The ID of the user - Id : string +type EditUserModel = { + /// The ID of the user + Id: string - /// The user's access level - AccessLevel : string - - /// The user name (e-mail address) - Email : string + /// The user's access level + AccessLevel: string + + /// The user name (e-mail address) + Email: string - /// The URL of the user's personal site - Url : string + /// The URL of the user's personal site + Url: string - /// The user's first name - FirstName : string + /// The user's first name + FirstName: string - /// The user's last name - LastName : string + /// The user's last name + LastName: string - /// The user's preferred name - PreferredName : string - - /// The user's password - Password : string - - /// Confirmation of the user's password - PasswordConfirm : string - } + /// The user's preferred name + PreferredName: string + + /// The user's password + Password: string + + /// Confirmation of the user's password + PasswordConfirm: string +} with /// Construct a displayed user from a web log user - static member fromUser (user : WebLogUser) = - { Id = WebLogUserId.toString user.Id - AccessLevel = user.AccessLevel.Value + static member fromUser (user: WebLogUser) = + { Id = string user.Id + AccessLevel = string user.AccessLevel Url = defaultArg user.Url "" Email = user.Email FirstName = user.FirstName @@ -1020,20 +1020,20 @@ type ManagePermalinksModel = { /// Create a permalink model from a page static member fromPage (pg: Page) = - { Id = pg.Id.Value + { Id = string pg.Id Entity = "page" CurrentTitle = pg.Title - CurrentPermalink = pg.Permalink.Value - Prior = pg.PriorPermalinks |> List.map _.Value |> Array.ofList + CurrentPermalink = string pg.Permalink + Prior = pg.PriorPermalinks |> List.map string |> Array.ofList } /// Create a permalink model from a post static member fromPost (post: Post) = - { Id = post.Id.Value + { Id = string post.Id Entity = "post" CurrentTitle = post.Title - CurrentPermalink = post.Permalink.Value - Prior = post.PriorPermalinks |> List.map _.Value |> Array.ofList + CurrentPermalink = string post.Permalink + Prior = post.PriorPermalinks |> List.map string |> Array.ofList } @@ -1055,7 +1055,7 @@ type ManageRevisionsModel = /// Create a revision model from a page static member fromPage webLog (pg: Page) = - { Id = pg.Id.Value + { Id = string pg.Id Entity = "page" CurrentTitle = pg.Title Revisions = pg.Revisions |> List.map (DisplayRevision.fromRevision webLog) |> Array.ofList @@ -1063,7 +1063,7 @@ type ManageRevisionsModel = /// Create a revision model from a post static member fromPost webLog (post: Post) = - { Id = post.Id.Value + { Id = string post.Id Entity = "post" CurrentTitle = post.Title Revisions = post.Revisions |> List.map (DisplayRevision.fromRevision webLog) |> Array.ofList @@ -1114,15 +1114,15 @@ type PostListItem = { static member fromPost (webLog: WebLog) (post: Post) = let _, extra = WebLog.hostAndPath webLog let inTZ = WebLog.localTime webLog - { Id = post.Id.Value - AuthorId = WebLogUserId.toString post.AuthorId - Status = post.Status.Value + { Id = string post.Id + AuthorId = string post.AuthorId + Status = string post.Status Title = post.Title - Permalink = post.Permalink.Value + Permalink = string post.Permalink PublishedOn = post.PublishedOn |> Option.map inTZ |> Option.toNullable UpdatedOn = inTZ post.UpdatedOn Text = addBaseToRelativeUrls extra post.Text - CategoryIds = post.CategoryIds |> List.map _.Value + CategoryIds = post.CategoryIds |> List.map string Tags = post.Tags Episode = post.Episode Metadata = post.Metadata @@ -1156,46 +1156,46 @@ type PostDisplay = /// View model for editing web log settings [] -type SettingsModel = - { /// The name of the web log - Name : string +type SettingsModel = { + /// The name of the web log + Name: string - /// The slug of the web log - Slug : string - - /// The subtitle of the web log - Subtitle : string + /// The slug of the web log + Slug: string + + /// The subtitle of the web log + Subtitle: string - /// The default page - DefaultPage : string + /// The default page + DefaultPage: string - /// How many posts should appear on index pages - PostsPerPage : int + /// How many posts should appear on index pages + PostsPerPage: int - /// The time zone in which dates/times should be displayed - TimeZone : string - - /// The theme to use to display the web log - ThemeId : string - - /// Whether to automatically load htmx - AutoHtmx : bool - - /// The default location for uploads - Uploads : string - } + /// The time zone in which dates/times should be displayed + TimeZone: string + + /// The theme to use to display the web log + ThemeId: string + + /// Whether to automatically load htmx + AutoHtmx: bool + + /// The default location for uploads + Uploads: string +} with /// Create a settings model from a web log - static member fromWebLog (webLog : WebLog) = + static member fromWebLog (webLog: WebLog) = { Name = webLog.Name Slug = webLog.Slug Subtitle = defaultArg webLog.Subtitle "" DefaultPage = webLog.DefaultPage PostsPerPage = webLog.PostsPerPage TimeZone = webLog.TimeZone - ThemeId = ThemeId.toString webLog.ThemeId + ThemeId = string webLog.ThemeId AutoHtmx = webLog.AutoHtmx - Uploads = UploadDestination.toString webLog.Uploads + Uploads = string webLog.Uploads } /// Update a web log with settings from the form @@ -1209,7 +1209,7 @@ type SettingsModel = TimeZone = this.TimeZone ThemeId = ThemeId this.ThemeId AutoHtmx = this.AutoHtmx - Uploads = UploadDestination.parse this.Uploads + Uploads = UploadDestination.Parse this.Uploads } diff --git a/src/MyWebLog/Caches.fs b/src/MyWebLog/Caches.fs index 05bda8f..74b09d9 100644 --- a/src/MyWebLog/Caches.fs +++ b/src/MyWebLog/Caches.fs @@ -194,8 +194,8 @@ module TemplateCache = let private hasInclude = Regex ("""{% include_template \"(.*)\" %}""", RegexOptions.None, TimeSpan.FromSeconds 2) /// Get a template for the given theme and template name - let get (themeId : ThemeId) (templateName : string) (data : IData) = backgroundTask { - let templatePath = $"{ThemeId.toString themeId}/{templateName}" + let get (themeId: ThemeId) (templateName: string) (data: IData) = backgroundTask { + let templatePath = $"{themeId}/{templateName}" match _cache.ContainsKey templatePath with | true -> return Ok _cache[templatePath] | false -> @@ -215,7 +215,7 @@ module TemplateCache = if childNotFound = "" then child.Groups[1].Value else $"{childNotFound}; {child.Groups[1].Value}" "" - text <- text.Replace (child.Value, childText) + text <- text.Replace(child.Value, childText) if childNotFound <> "" then let s = if childNotFound.IndexOf ";" >= 0 then "s" else "" return Error $"Could not find the child template{s} {childNotFound} required by {templateName}" @@ -223,8 +223,8 @@ module TemplateCache = _cache[templatePath] <- Template.Parse (text, SyntaxCompatibility.DotLiquid22) return Ok _cache[templatePath] | None -> - return Error $"Theme ID {ThemeId.toString themeId} does not have a template named {templateName}" - | None -> return Result.Error $"Theme ID {ThemeId.toString themeId} does not exist" + return Error $"Theme ID {themeId} does not have a template named {templateName}" + | None -> return Error $"Theme ID {themeId} does not exist" } /// Get all theme/template names currently cached @@ -232,16 +232,16 @@ module TemplateCache = _cache.Keys |> Seq.sort |> Seq.toList /// Invalidate all template cache entries for the given theme ID - let invalidateTheme (themeId : ThemeId) = - let keyPrefix = ThemeId.toString themeId + let invalidateTheme (themeId: ThemeId) = + let keyPrefix = string themeId _cache.Keys - |> Seq.filter (fun key -> key.StartsWith keyPrefix) + |> Seq.filter _.StartsWith(keyPrefix) |> List.ofSeq |> List.iter (fun key -> match _cache.TryRemove key with _, _ -> ()) /// Remove all entries from the template cache let empty () = - _cache.Clear () + _cache.Clear() /// A cache of asset names by themes diff --git a/src/MyWebLog/DotLiquidBespoke.fs b/src/MyWebLog/DotLiquidBespoke.fs index 45006b7..2718d43 100644 --- a/src/MyWebLog/DotLiquidBespoke.fs +++ b/src/MyWebLog/DotLiquidBespoke.fs @@ -95,9 +95,9 @@ type NavLinkFilter () = /// A filter to generate a link for theme asset (image, stylesheet, script, etc.) -type ThemeAssetFilter () = - static member ThemeAsset (ctx : Context, asset : string) = - WebLog.relativeUrl ctx.WebLog (Permalink $"themes/{ThemeId.toString ctx.WebLog.ThemeId}/{asset}") +type ThemeAssetFilter() = + static member ThemeAsset(ctx: Context, asset: string) = + WebLog.relativeUrl ctx.WebLog (Permalink $"themes/{ctx.WebLog.ThemeId}/{asset}") /// Create various items in the page header based on the state of the page being generated diff --git a/src/MyWebLog/Handlers/Admin.fs b/src/MyWebLog/Handlers/Admin.fs index 0e9af33..42bed16 100644 --- a/src/MyWebLog/Handlers/Admin.fs +++ b/src/MyWebLog/Handlers/Admin.fs @@ -37,7 +37,7 @@ module Dashboard = let admin : HttpHandler = requireAccess Administrator >=> fun next ctx -> task { match! TemplateCache.get adminTheme "theme-list-body" ctx.Data with | Ok bodyTemplate -> - let! themes = ctx.Data.Theme.All () + let! themes = ctx.Data.Theme.All() let cachedTemplates = TemplateCache.allNames () let! hash = hashForPage "myWebLog Administration" @@ -50,10 +50,10 @@ module Dashboard = themes |> Seq.ofList |> Seq.map (fun it -> [| - ThemeId.toString it.Id + string it.Id it.Name cachedTemplates - |> List.filter (fun n -> n.StartsWith (ThemeId.toString it.Id)) + |> List.filter _.StartsWith(string it.Id) |> List.length |> string |]) @@ -61,8 +61,8 @@ module Dashboard = |> addToHash "web_logs" ( WebLogCache.all () |> Seq.ofList - |> Seq.sortBy (fun it -> it.Name) - |> Seq.map (fun it -> [| WebLogId.toString it.Id; it.Name; it.UrlBase |]) + |> Seq.sortBy _.Name + |> Seq.map (fun it -> [| string it.Id; it.Name; it.UrlBase |]) |> Array.ofSeq) |> addViewContext ctx return! @@ -317,7 +317,7 @@ module TagMapping = addToHash "mappings" mappings hash |> addToHash "mapping_ids" ( mappings - |> List.map (fun it -> { Name = it.Tag; Value = TagMapId.toString it.Id })) + |> List.map (fun it -> { Name = it.Tag; Value = string it.Id })) } // GET /admin/settings/tag-mappings @@ -348,13 +348,13 @@ module TagMapping = // POST /admin/settings/tag-mapping/save let save : HttpHandler = fun next ctx -> task { let data = ctx.Data - let! model = ctx.BindFormAsync () + let! model = ctx.BindFormAsync() let tagMap = - if model.IsNew then someTask { TagMap.empty with Id = TagMapId.create (); WebLogId = ctx.WebLog.Id } + if model.IsNew then someTask { TagMap.empty with Id = TagMapId.Create(); WebLogId = ctx.WebLog.Id } else data.TagMap.FindById (TagMapId model.Id) ctx.WebLog.Id match! tagMap with | Some tm -> - do! data.TagMap.Save { tm with Tag = model.Tag.ToLower (); UrlValue = model.UrlValue.ToLower () } + do! data.TagMap.Save { tm with Tag = model.Tag.ToLower(); UrlValue = model.UrlValue.ToLower() } do! addMessage ctx { UserMessage.success with Message = "Tag mapping saved successfully" } return! all next ctx | None -> return! Error.notFound next ctx @@ -395,17 +395,17 @@ module Theme = |> adminBareView "theme-upload" next ctx /// Update the name and version for a theme based on the version.txt file, if present - let private updateNameAndVersion (theme : Theme) (zip : ZipArchive) = backgroundTask { + let private updateNameAndVersion (theme: Theme) (zip: ZipArchive) = backgroundTask { let now () = DateTime.UtcNow.ToString "yyyyMMdd.HHmm" match zip.Entries |> Seq.filter (fun it -> it.FullName = "version.txt") |> Seq.tryHead with | Some versionItem -> - use versionFile = new StreamReader(versionItem.Open ()) - let! versionText = versionFile.ReadToEndAsync () + use versionFile = new StreamReader(versionItem.Open()) + let! versionText = versionFile.ReadToEndAsync() let parts = versionText.Trim().Replace("\r", "").Split "\n" - let displayName = if parts[0] > "" then parts[0] else ThemeId.toString theme.Id + let displayName = if parts[0] > "" then parts[0] else string theme.Id let version = if parts.Length > 1 && parts[1] > "" then parts[1] else now () return { theme with Name = displayName; Version = version } - | None -> return { theme with Name = ThemeId.toString theme.Id; Version = now () } + | None -> return { theme with Name = string theme.Id; Version = now () } } /// Update the theme with all templates from the ZIP archive @@ -476,16 +476,16 @@ module Theme = let data = ctx.Data let! exists = data.Theme.Exists themeId let isNew = not exists - let! model = ctx.BindFormAsync () + let! model = ctx.BindFormAsync() if isNew || model.DoOverwrite then // Load the theme to the database - use stream = new MemoryStream () + use stream = new MemoryStream() do! themeFile.CopyToAsync stream let! _ = loadFromZip themeId stream data do! ThemeAssetCache.refreshTheme themeId data TemplateCache.invalidateTheme themeId // Save the .zip file - use file = new FileStream ($"{ThemeId.toString themeId}-theme.zip", FileMode.Create) + use file = new FileStream($"{themeId}-theme.zip", FileMode.Create) do! themeFile.CopyToAsync file do! addMessage ctx { UserMessage.success with @@ -556,18 +556,18 @@ module WebLog = KeyValuePair.Create("posts", "- First Page of Posts -") yield! allPages |> List.sortBy _.Title.ToLower() - |> List.map (fun p -> KeyValuePair.Create(p.Id.Value, p.Title)) + |> List.map (fun p -> KeyValuePair.Create(string p.Id, p.Title)) } |> Array.ofSeq) |> addToHash "themes" ( themes |> Seq.ofList |> Seq.map (fun it -> - KeyValuePair.Create (ThemeId.toString it.Id, $"{it.Name} (v{it.Version})")) + KeyValuePair.Create(string it.Id, $"{it.Name} (v{it.Version})")) |> Array.ofSeq) |> addToHash "upload_values" [| - KeyValuePair.Create (UploadDestination.toString Database, "Database") - KeyValuePair.Create (UploadDestination.toString Disk, "Disk") + KeyValuePair.Create(string Database, "Database") + KeyValuePair.Create(string Disk, "Disk") |] |> addToHash "users" (users |> List.map (DisplayUser.fromUser ctx.WebLog) |> Array.ofList) |> addToHash "rss_model" (EditRssModel.fromRssOptions ctx.WebLog.Rss) diff --git a/src/MyWebLog/Handlers/Feed.fs b/src/MyWebLog/Handlers/Feed.fs index 1d8dcda..605b038 100644 --- a/src/MyWebLog/Handlers/Feed.fs +++ b/src/MyWebLog/Handlers/Feed.fs @@ -37,7 +37,7 @@ let deriveFeedType (ctx : HttpContext) feedPath : (FeedType * int) option = | false -> // Category and tag feeds are handled by defined routes; check for custom feed match webLog.Rss.CustomFeeds - |> List.tryFind (fun it -> feedPath.EndsWith it.Path.Value) with + |> List.tryFind (fun it -> feedPath.EndsWith(string it.Path)) with | Some feed -> debug (fun () -> "Found custom feed") Some (Custom (feed, feedPath), feed.Podcast |> Option.map _.ItemsInFeed |> Option.defaultValue postCount) @@ -48,7 +48,7 @@ let deriveFeedType (ctx : HttpContext) feedPath : (FeedType * int) option = /// Determine the function to retrieve posts for the given feed let private getFeedPosts ctx feedType = let childIds (catId: CategoryId) = - let cat = CategoryCache.get ctx |> Array.find (fun c -> c.Id = catId.Value) + let cat = CategoryCache.get ctx |> Array.find (fun c -> c.Id = string catId) getCategoryIds cat.Slug ctx let data = ctx.Data match feedType with @@ -86,51 +86,50 @@ module private Namespace = let rawVoice = "http://www.rawvoice.com/rawvoiceRssModule/" /// Create a feed item from the given post -let private toFeedItem webLog (authors : MetaItem list) (cats : DisplayCategory[]) (tagMaps : TagMap list) - (post : Post) = +let private toFeedItem webLog (authors: MetaItem list) (cats: DisplayCategory array) (tagMaps: TagMap list) + (post: Post) = let plainText = let endingP = post.Text.IndexOf "

" stripHtml <| if endingP >= 0 then post.Text[..(endingP - 1)] else post.Text - let item = SyndicationItem ( + let item = SyndicationItem( Id = WebLog.absoluteUrl webLog post.Permalink, Title = TextSyndicationContent.CreateHtmlContent post.Title, - PublishDate = post.PublishedOn.Value.ToDateTimeOffset (), - LastUpdatedTime = post.UpdatedOn.ToDateTimeOffset (), + PublishDate = post.PublishedOn.Value.ToDateTimeOffset(), + LastUpdatedTime = post.UpdatedOn.ToDateTimeOffset(), Content = TextSyndicationContent.CreatePlaintextContent plainText) item.AddPermalink (Uri item.Id) - let xmlDoc = XmlDocument () + let xmlDoc = XmlDocument() let encoded = let txt = post.Text .Replace("src=\"/", $"src=\"{webLog.UrlBase}/") - .Replace ("href=\"/", $"href=\"{webLog.UrlBase}/") - let it = xmlDoc.CreateElement ("content", "encoded", Namespace.content) - let _ = it.AppendChild (xmlDoc.CreateCDataSection txt) + .Replace("href=\"/", $"href=\"{webLog.UrlBase}/") + let it = xmlDoc.CreateElement("content", "encoded", Namespace.content) + let _ = it.AppendChild(xmlDoc.CreateCDataSection txt) it item.ElementExtensions.Add encoded - item.Authors.Add (SyndicationPerson ( - Name = (authors |> List.find (fun a -> a.Name = WebLogUserId.toString post.AuthorId)).Value)) + item.Authors.Add(SyndicationPerson(Name = (authors |> List.find (fun a -> a.Name = string post.AuthorId)).Value)) [ post.CategoryIds |> List.map (fun catId -> - let cat = cats |> Array.find (fun c -> c.Id = catId.Value) - SyndicationCategory (cat.Name, WebLog.absoluteUrl webLog (Permalink $"category/{cat.Slug}/"), cat.Name)) + let cat = cats |> Array.find (fun c -> c.Id = string catId) + SyndicationCategory(cat.Name, WebLog.absoluteUrl webLog (Permalink $"category/{cat.Slug}/"), cat.Name)) post.Tags |> List.map (fun tag -> let urlTag = match tagMaps |> List.tryFind (fun tm -> tm.Tag = tag) with | Some tm -> tm.UrlValue | None -> tag.Replace (" ", "+") - SyndicationCategory (tag, WebLog.absoluteUrl webLog (Permalink $"tag/{urlTag}/"), $"{tag} (tag)")) + SyndicationCategory(tag, WebLog.absoluteUrl webLog (Permalink $"tag/{urlTag}/"), $"{tag} (tag)")) ] |> List.concat |> List.iter item.Categories.Add item /// Convert non-absolute URLs to an absolute URL for this web log -let toAbsolute webLog (link : string) = +let toAbsolute webLog (link: string) = if link.StartsWith "http" then link else WebLog.absoluteUrl webLog (Permalink link) /// Add episode information to a podcast feed item @@ -141,8 +140,8 @@ let private addEpisode webLog (podcast : PodcastOptions) (episode : Episode) (po | link when Option.isSome podcast.MediaBaseUrl -> $"{podcast.MediaBaseUrl.Value}{link}" | link -> WebLog.absoluteUrl webLog (Permalink link) let epMediaType = [ episode.MediaType; podcast.DefaultMediaType ] |> List.tryFind Option.isSome |> Option.flatten - let epImageUrl = defaultArg episode.ImageUrl podcast.ImageUrl.Value |> toAbsolute webLog - let epExplicit = (defaultArg episode.Explicit podcast.Explicit).Value + let epImageUrl = defaultArg episode.ImageUrl (string podcast.ImageUrl) |> toAbsolute webLog + let epExplicit = string (defaultArg episode.Explicit podcast.Explicit) let xmlDoc = XmlDocument() let enclosure = @@ -298,7 +297,7 @@ let private addPodcast webLog (rssFeed : SyndicationFeed) (feed : CustomFeed) = rssFeed.ElementExtensions.Add rawVoice rssFeed.ElementExtensions.Add("summary", Namespace.iTunes, podcast.Summary) rssFeed.ElementExtensions.Add("author", Namespace.iTunes, podcast.DisplayedAuthor) - rssFeed.ElementExtensions.Add("explicit", Namespace.iTunes, podcast.Explicit.Value) + rssFeed.ElementExtensions.Add("explicit", Namespace.iTunes, string podcast.Explicit) podcast.Subtitle |> Option.iter (fun sub -> rssFeed.ElementExtensions.Add ("subtitle", Namespace.iTunes, sub)) podcast.FundingUrl |> Option.iter (fun url -> @@ -309,7 +308,7 @@ let private addPodcast webLog (rssFeed : SyndicationFeed) (feed : CustomFeed) = podcast.PodcastGuid |> Option.iter (fun guid -> rssFeed.ElementExtensions.Add("guid", Namespace.podcast, guid.ToString().ToLowerInvariant())) - podcast.Medium |> Option.iter (fun med -> rssFeed.ElementExtensions.Add("medium", Namespace.podcast, med.Value)) + podcast.Medium |> Option.iter (fun med -> rssFeed.ElementExtensions.Add("medium", Namespace.podcast, string med)) /// Get the feed's self reference and non-feed link let private selfAndLink webLog feedType ctx = @@ -368,7 +367,7 @@ let createFeed (feedType : FeedType) posts : HttpHandler = fun next ctx -> backg match podcast, post.Episode with | Some feed, Some episode -> addEpisode webLog (Option.get feed.Podcast) episode post item | Some _, _ -> - warn "Feed" ctx $"[{webLog.Name} {self.Value}] \"{stripHtml post.Title}\" has no media" + warn "Feed" ctx $"[{webLog.Name} {self}] \"{stripHtml post.Title}\" has no media" item | _ -> item @@ -427,7 +426,7 @@ let saveSettings : HttpHandler = requireAccess WebLogAdmin >=> fun next ctx -> t let editCustomFeed feedId : HttpHandler = requireAccess WebLogAdmin >=> fun next ctx -> let customFeed = match feedId with - | "new" -> Some { CustomFeed.empty with Id = CustomFeedId "new" } + | "new" -> Some { CustomFeed.Empty with Id = CustomFeedId "new" } | _ -> ctx.WebLog.Rss.CustomFeeds |> List.tryFind (fun f -> f.Id = CustomFeedId feedId) match customFeed with | Some f -> @@ -436,13 +435,13 @@ let editCustomFeed feedId : HttpHandler = requireAccess WebLogAdmin >=> fun next |> addToHash ViewContext.Model (EditCustomFeedModel.fromFeed f) |> addToHash "medium_values" [| KeyValuePair.Create("", "– Unspecified –") - KeyValuePair.Create(Podcast.Value, "Podcast") - KeyValuePair.Create(Music.Value, "Music") - KeyValuePair.Create(Video.Value, "Video") - KeyValuePair.Create(Film.Value, "Film") - KeyValuePair.Create(Audiobook.Value, "Audiobook") - KeyValuePair.Create(Newsletter.Value, "Newsletter") - KeyValuePair.Create(Blog.Value, "Blog") + KeyValuePair.Create(string Podcast, "Podcast") + KeyValuePair.Create(string Music, "Music") + KeyValuePair.Create(string Video, "Video") + KeyValuePair.Create(string Film, "Film") + KeyValuePair.Create(string Audiobook, "Audiobook") + KeyValuePair.Create(string Newsletter, "Newsletter") + KeyValuePair.Create(string Blog, "Blog") |] |> adminView "custom-feed-edit" next ctx | None -> Error.notFound next ctx @@ -455,8 +454,8 @@ let saveCustomFeed : HttpHandler = requireAccess WebLogAdmin >=> fun next ctx -> let! model = ctx.BindFormAsync () let theFeed = match model.Id with - | "new" -> Some { CustomFeed.empty with Id = CustomFeedId.create () } - | _ -> webLog.Rss.CustomFeeds |> List.tryFind (fun it -> CustomFeedId.toString it.Id = model.Id) + | "new" -> Some { CustomFeed.Empty with Id = CustomFeedId.Create() } + | _ -> webLog.Rss.CustomFeeds |> List.tryFind (fun it -> string it.Id = model.Id) match theFeed with | Some feed -> let feeds = model.UpdateFeed feed :: (webLog.Rss.CustomFeeds |> List.filter (fun it -> it.Id <> feed.Id)) @@ -467,7 +466,7 @@ let saveCustomFeed : HttpHandler = requireAccess WebLogAdmin >=> fun next ctx -> UserMessage.success with Message = $"""Successfully {if model.Id = "new" then "add" else "sav"}ed custom feed""" } - return! redirectToGet $"admin/settings/rss/{CustomFeedId.toString feed.Id}/edit" next ctx + return! redirectToGet $"admin/settings/rss/{feed.Id}/edit" next ctx | None -> return! Error.notFound next ctx | None -> return! Error.notFound next ctx } diff --git a/src/MyWebLog/Handlers/Helpers.fs b/src/MyWebLog/Handlers/Helpers.fs index b1f4bd3..a2a9ded 100644 --- a/src/MyWebLog/Handlers/Helpers.fs +++ b/src/MyWebLog/Handlers/Helpers.fs @@ -352,8 +352,8 @@ let requireAccess level : HttpHandler = fun next ctx -> task { | Some userLevel -> do! addMessage ctx { UserMessage.warning with - Message = $"The page you tried to access requires {level.Value} privileges" - Detail = Some $"Your account only has {userLevel.Value} privileges" + Message = $"The page you tried to access requires {level} privileges" + Detail = Some $"Your account only has {userLevel} privileges" } return! Error.notAuthorized next ctx | None -> diff --git a/src/MyWebLog/Handlers/Page.fs b/src/MyWebLog/Handlers/Page.fs index 1eece85..1c442f5 100644 --- a/src/MyWebLog/Handlers/Page.fs +++ b/src/MyWebLog/Handlers/Page.fs @@ -193,7 +193,7 @@ let save : HttpHandler = requireAccess Author >=> fun next ctx -> task { do! (if model.IsNew then data.Page.Add else data.Page.Update) updatedPage if updateList then do! PageListCache.update ctx do! addMessage ctx { UserMessage.success with Message = "Page saved successfully" } - return! redirectToGet $"admin/page/{page.Id.Value}/edit" next ctx + return! redirectToGet $"admin/page/{page.Id}/edit" next ctx | Some _ -> return! Error.notAuthorized next ctx | None -> return! Error.notFound next ctx } diff --git a/src/MyWebLog/Handlers/Post.fs b/src/MyWebLog/Handlers/Post.fs index d9e87cd..ee8b3db 100644 --- a/src/MyWebLog/Handlers/Post.fs +++ b/src/MyWebLog/Handlers/Post.fs @@ -58,7 +58,7 @@ let preparePostList webLog posts listType (url: string) pageNbr perPage (data: I | _ -> Task.FromResult (None, None) let newerLink = match listType, pageNbr with - | SinglePost, _ -> newerPost |> Option.map _.Permalink.Value + | SinglePost, _ -> newerPost |> Option.map (fun it -> string it.Permalink) | _, 1 -> None | PostList, 2 when webLog.DefaultPage = "posts" -> Some "" | PostList, _ -> relUrl $"page/{pageNbr - 1}" @@ -70,7 +70,7 @@ let preparePostList webLog posts listType (url: string) pageNbr perPage (data: I | AdminList, _ -> relUrl $"admin/posts/page/{pageNbr - 1}" let olderLink = match listType, List.length posts > perPage with - | SinglePost, _ -> olderPost |> Option.map _.Permalink.Value + | SinglePost, _ -> olderPost |> Option.map (fun it -> string it.Permalink) | _, false -> None | PostList, true -> relUrl $"page/{pageNbr + 1}" | CategoryList, true -> relUrl $"category/{url}/page/{pageNbr + 1}" @@ -243,9 +243,9 @@ let edit postId : HttpHandler = requireAccess Author >=> fun next ctx -> task { |> addToHash "templates" templates |> addToHash "explicit_values" [| KeyValuePair.Create("", "– Default –") - KeyValuePair.Create(Yes.Value, "Yes") - KeyValuePair.Create(No.Value, "No") - KeyValuePair.Create(Clean.Value, "Clean") + KeyValuePair.Create(string Yes, "Yes") + KeyValuePair.Create(string No, "No") + KeyValuePair.Create(string Clean, "Clean") |] |> adminView "post-edit" next ctx | Some _ -> return! Error.notAuthorized next ctx @@ -410,7 +410,7 @@ let save : HttpHandler = requireAccess Author >=> fun next ctx -> task { |> List.length = List.length priorCats) then do! CategoryCache.update ctx do! addMessage ctx { UserMessage.success with Message = "Post saved successfully" } - return! redirectToGet $"admin/post/{post.Id.Value}/edit" next ctx + return! redirectToGet $"admin/post/{post.Id}/edit" next ctx | Some _ -> return! Error.notAuthorized next ctx | None -> return! Error.notFound next ctx } diff --git a/src/MyWebLog/Handlers/Routes.fs b/src/MyWebLog/Handlers/Routes.fs index c128864..72582ad 100644 --- a/src/MyWebLog/Handlers/Routes.fs +++ b/src/MyWebLog/Handlers/Routes.fs @@ -88,13 +88,13 @@ module CatchAll = module Asset = // GET /theme/{theme}/{**path} - let serve (urlParts : string seq) : HttpHandler = fun next ctx -> task { + let serve (urlParts: string seq) : HttpHandler = fun next ctx -> task { let path = urlParts |> Seq.skip 1 |> Seq.head - match! ctx.Data.ThemeAsset.FindById (ThemeAssetId.ofString path) with + match! ctx.Data.ThemeAsset.FindById(ThemeAssetId.Parse path) with | Some asset -> match Upload.checkModified asset.UpdatedOn ctx with | Some threeOhFour -> return! threeOhFour next ctx - | None -> return! Upload.sendFile (asset.UpdatedOn.ToDateTimeUtc ()) path asset.Data next ctx + | None -> return! Upload.sendFile (asset.UpdatedOn.ToDateTimeUtc()) path asset.Data next ctx | None -> return! Error.notFound next ctx } diff --git a/src/MyWebLog/Handlers/Upload.fs b/src/MyWebLog/Handlers/Upload.fs index c1c840d..f192f76 100644 --- a/src/MyWebLog/Handlers/Upload.fs +++ b/src/MyWebLog/Handlers/Upload.fs @@ -107,7 +107,7 @@ let list : HttpHandler = requireAccess Author >=> fun next ctx -> task { Name = name Path = file.Replace($"{path}{slash}", "").Replace(name, "").Replace (slash, '/') UpdatedOn = create - Source = UploadDestination.toString Disk + Source = string Disk }) |> List.ofSeq with @@ -131,7 +131,7 @@ let list : HttpHandler = requireAccess Author >=> fun next ctx -> task { let showNew : HttpHandler = requireAccess Author >=> fun next ctx -> hashForPage "Upload a File" |> withAntiCsrf ctx - |> addToHash "destination" (UploadDestination.toString ctx.WebLog.Uploads) + |> addToHash "destination" (string ctx.WebLog.Uploads) |> adminView "upload-new" next ctx @@ -144,29 +144,29 @@ let save : HttpHandler = requireAccess Author >=> fun next ctx -> task { if ctx.Request.HasFormContentType && ctx.Request.Form.Files.Count > 0 then let upload = Seq.head ctx.Request.Form.Files let fileName = String.Concat (makeSlug (Path.GetFileNameWithoutExtension upload.FileName), - Path.GetExtension(upload.FileName).ToLowerInvariant ()) + Path.GetExtension(upload.FileName).ToLowerInvariant()) let now = Noda.now () let localNow = WebLog.localTime ctx.WebLog now let year = localNow.ToString "yyyy" let month = localNow.ToString "MM" - let! form = ctx.BindFormAsync () + let! form = ctx.BindFormAsync() - match UploadDestination.parse form.Destination with + match UploadDestination.Parse form.Destination with | Database -> - use stream = new MemoryStream () + use stream = new MemoryStream() do! upload.CopyToAsync stream let file = - { Id = UploadId.create () + { Id = UploadId.Create() WebLogId = ctx.WebLog.Id Path = Permalink $"{year}/{month}/{fileName}" UpdatedOn = now - Data = stream.ToArray () + Data = stream.ToArray() } do! ctx.Data.Upload.Add file | Disk -> - let fullPath = Path.Combine (uploadDir, ctx.WebLog.Slug, year, month) + let fullPath = Path.Combine(uploadDir, ctx.WebLog.Slug, year, month) let _ = Directory.CreateDirectory fullPath - use stream = new FileStream (Path.Combine (fullPath, fileName), FileMode.Create) + use stream = new FileStream(Path.Combine(fullPath, fileName), FileMode.Create) do! upload.CopyToAsync stream do! addMessage ctx { UserMessage.success with Message = $"File uploaded to {form.Destination} successfully" } diff --git a/src/MyWebLog/Handlers/User.fs b/src/MyWebLog/Handlers/User.fs index 389fe56..f9a039d 100644 --- a/src/MyWebLog/Handlers/User.fs +++ b/src/MyWebLog/Handlers/User.fs @@ -48,22 +48,22 @@ open Microsoft.AspNetCore.Authentication.Cookies // POST /user/log-on let doLogOn : HttpHandler = fun next ctx -> task { - let! model = ctx.BindFormAsync () + let! model = ctx.BindFormAsync() let data = ctx.Data let! tryUser = data.WebLogUser.FindByEmail model.EmailAddress ctx.WebLog.Id match! verifyPassword tryUser model.Password ctx with | Ok _ -> let user = tryUser.Value let claims = seq { - Claim (ClaimTypes.NameIdentifier, WebLogUserId.toString user.Id) - Claim (ClaimTypes.Name, $"{user.FirstName} {user.LastName}") - Claim (ClaimTypes.GivenName, user.PreferredName) - Claim (ClaimTypes.Role, user.AccessLevel.Value) + Claim(ClaimTypes.NameIdentifier, string user.Id) + Claim(ClaimTypes.Name, $"{user.FirstName} {user.LastName}") + Claim(ClaimTypes.GivenName, user.PreferredName) + Claim(ClaimTypes.Role, string user.AccessLevel) } - let identity = ClaimsIdentity (claims, CookieAuthenticationDefaults.AuthenticationScheme) + let identity = ClaimsIdentity(claims, CookieAuthenticationDefaults.AuthenticationScheme) - do! ctx.SignInAsync (identity.AuthenticationType, ClaimsPrincipal identity, - AuthenticationProperties (IssuedUtc = DateTimeOffset.UtcNow)) + do! ctx.SignInAsync(identity.AuthenticationType, ClaimsPrincipal identity, + AuthenticationProperties(IssuedUtc = DateTimeOffset.UtcNow)) do! data.WebLogUser.SetLastSeen user.Id user.WebLogId do! addMessage ctx { UserMessage.success with @@ -110,10 +110,10 @@ let private showEdit (model : EditUserModel) : HttpHandler = fun next ctx -> |> withAntiCsrf ctx |> addToHash ViewContext.Model model |> addToHash "access_levels" [| - KeyValuePair.Create(Author.Value, "Author") - KeyValuePair.Create(Editor.Value, "Editor") - KeyValuePair.Create(WebLogAdmin.Value, "Web Log Admin") - if ctx.HasAccessLevel Administrator then KeyValuePair.Create(Administrator.Value, "Administrator") + KeyValuePair.Create(string Author, "Author") + KeyValuePair.Create(string Editor, "Editor") + KeyValuePair.Create(string WebLogAdmin, "Web Log Admin") + if ctx.HasAccessLevel Administrator then KeyValuePair.Create(string Administrator, "Administrator") |] |> adminBareView "user-edit" next ctx @@ -159,7 +159,7 @@ let private showMyInfo (model : EditMyInfoModel) (user : WebLogUser) : HttpHandl hashForPage "Edit Your Information" |> withAntiCsrf ctx |> addToHash ViewContext.Model model - |> addToHash "access_level" (user.AccessLevel.Value) + |> addToHash "access_level" (string user.AccessLevel) |> addToHash "created_on" (WebLog.localTime ctx.WebLog user.CreatedOn) |> addToHash "last_seen_on" (WebLog.localTime ctx.WebLog (defaultArg user.LastSeenOn (Instant.FromUnixTimeSeconds 0))) @@ -208,7 +208,7 @@ let save : HttpHandler = requireAccess WebLogAdmin >=> fun next ctx -> task { let tryUser = if model.IsNew then { WebLogUser.empty with - Id = WebLogUserId.create () + Id = WebLogUserId.Create() WebLogId = ctx.WebLog.Id CreatedOn = Noda.now () } |> someTask diff --git a/src/MyWebLog/Maintenance.fs b/src/MyWebLog/Maintenance.fs index 8d0f68f..685c9dc 100644 --- a/src/MyWebLog/Maintenance.fs +++ b/src/MyWebLog/Maintenance.fs @@ -21,8 +21,8 @@ let private doCreateWebLog (args : string[]) (sp : IServiceProvider) = task { | false, _ -> raise <| TimeZoneNotFoundException $"Cannot find IANA timezone for {local}" // Create the web log - let webLogId = WebLogId.create () - let userId = WebLogUserId.create () + let webLogId = WebLogId.Create() + let userId = WebLogUserId.Create() let homePageId = PageId.Create() let slug = Handlers.Upload.makeSlug args[2] @@ -37,7 +37,7 @@ let private doCreateWebLog (args : string[]) (sp : IServiceProvider) = task { Name = args[2] Slug = slug UrlBase = args[1] - DefaultPage = homePageId.Value + DefaultPage = string homePageId TimeZone = timeZone } @@ -110,8 +110,8 @@ let private importPriorPermalinks urlBase file (sp : IServiceProvider) = task { let! withLinks = data.Post.FindFullById post.Id post.WebLogId let! _ = data.Post.UpdatePriorPermalinks post.Id post.WebLogId (old :: withLinks.Value.PriorPermalinks) - printfn $"{old.Value} -> {current.Value}" - | None -> eprintfn $"Cannot find current post for {current.Value}" + printfn $"{old} -> {current}" + | None -> eprintfn $"Cannot find current post for {current}" printfn "Done!" | None -> eprintfn $"No web log found at {urlBase}" } @@ -144,7 +144,7 @@ let loadTheme (args : string[]) (sp : IServiceProvider) = task { let! theme = Handlers.Admin.Theme.loadFromZip themeId copy data let fac = sp.GetRequiredService () let log = fac.CreateLogger "MyWebLog.Themes" - log.LogInformation $"{theme.Name} v{theme.Version} ({ThemeId.toString theme.Id}) loaded" + log.LogInformation $"{theme.Name} v{theme.Version} ({theme.Id}) loaded" | Error message -> eprintfn $"{message}" else eprintfn "Usage: myWebLog load-theme [theme-zip-file-name]" @@ -333,13 +333,13 @@ module Backup = return { archive with WebLog = { archive.WebLog with UrlBase = defaultArg newUrlBase webLog.UrlBase } } | Some _ -> // Err'body gets new IDs... - let newWebLogId = WebLogId.create () - let newCatIds = archive.Categories |> List.map (fun cat -> cat.Id, CategoryId.Create ()) |> dict - let newMapIds = archive.TagMappings |> List.map (fun tm -> tm.Id, TagMapId.create ()) |> dict - let newPageIds = archive.Pages |> List.map (fun page -> page.Id, PageId.Create ()) |> dict - let newPostIds = archive.Posts |> List.map (fun post -> post.Id, PostId.Create ()) |> dict - let newUserIds = archive.Users |> List.map (fun user -> user.Id, WebLogUserId.create ()) |> dict - let newUpIds = archive.Uploads |> List.map (fun up -> up.Id, UploadId.create ()) |> dict + let newWebLogId = WebLogId.Create() + let newCatIds = archive.Categories |> List.map (fun cat -> cat.Id, CategoryId.Create() ) |> dict + let newMapIds = archive.TagMappings |> List.map (fun tm -> tm.Id, TagMapId.Create() ) |> dict + let newPageIds = archive.Pages |> List.map (fun page -> page.Id, PageId.Create() ) |> dict + let newPostIds = archive.Posts |> List.map (fun post -> post.Id, PostId.Create() ) |> dict + let newUserIds = archive.Users |> List.map (fun user -> user.Id, WebLogUserId.Create()) |> dict + let newUpIds = archive.Uploads |> List.map (fun up -> up.Id, UploadId.Create() ) |> dict return { archive with WebLog = { archive.WebLog with Id = newWebLogId; UrlBase = Option.get newUrlBase } @@ -481,7 +481,7 @@ let private doUserUpgrade urlBase email (data : IData) = task { | WebLogAdmin -> do! data.WebLogUser.Update { user with AccessLevel = Administrator } printfn $"{email} is now an Administrator user" - | other -> eprintfn $"ERROR: {email} is an {other.Value}, not a WebLogAdmin" + | other -> eprintfn $"ERROR: {email} is an {other}, not a WebLogAdmin" | None -> eprintfn $"ERROR: no user {email} found at {urlBase}" | None -> eprintfn $"ERROR: no web log found for {urlBase}" } diff --git a/src/MyWebLog/Program.fs b/src/MyWebLog/Program.fs index a48fc2e..450b383 100644 --- a/src/MyWebLog/Program.fs +++ b/src/MyWebLog/Program.fs @@ -15,7 +15,7 @@ type WebLogMiddleware (next : RequestDelegate, log : ILogger) let path = $"{ctx.Request.Scheme}://{ctx.Request.Host.Value}{ctx.Request.Path.Value}" match WebLogCache.tryGet path with | Some webLog -> - if isDebug then log.LogDebug $"Resolved web log {WebLogId.toString webLog.Id} for {path}" + if isDebug then log.LogDebug $"Resolved web log {webLog.Id} for {path}" ctx.Items["webLog"] <- webLog if PageListCache.exists ctx then () else do! PageListCache.update ctx if CategoryCache.exists ctx then () else do! CategoryCache.update ctx