Update deps; WIP on comments
This commit is contained in:
+154
-144
@@ -3,29 +3,29 @@
|
||||
open MyWebLog
|
||||
open NodaTime
|
||||
|
||||
/// A category under which a post may be identified
|
||||
/// <summary>A category under which a post may be identified</summary>
|
||||
[<CLIMutable; NoComparison; NoEquality>]
|
||||
type Category = {
|
||||
/// The ID of the category
|
||||
/// <summary>The ID of the category</summary>
|
||||
Id: CategoryId
|
||||
|
||||
/// The ID of the web log to which the category belongs
|
||||
/// <summary>The ID of the web log to which the category belongs</summary>
|
||||
WebLogId: WebLogId
|
||||
|
||||
/// The displayed name
|
||||
/// <summary>The displayed name</summary>
|
||||
Name: string
|
||||
|
||||
/// The slug (used in category URLs)
|
||||
/// <summary>The slug (used in category URLs)</summary>
|
||||
Slug: string
|
||||
|
||||
/// A longer description of the category
|
||||
/// <summary>A longer description of the category</summary>
|
||||
Description: string option
|
||||
|
||||
/// The parent ID of this category (if a subcategory)
|
||||
/// <summary>The parent ID of this category (if a subcategory)</summary>
|
||||
ParentId: CategoryId option
|
||||
} with
|
||||
|
||||
/// An empty category
|
||||
|
||||
/// <summary>An empty category</summary>
|
||||
static member Empty =
|
||||
{ Id = CategoryId.Empty
|
||||
WebLogId = WebLogId.Empty
|
||||
@@ -35,38 +35,38 @@ type Category = {
|
||||
ParentId = None }
|
||||
|
||||
|
||||
/// A comment on a post
|
||||
/// <summary>A comment on a post</summary>
|
||||
[<CLIMutable; NoComparison; NoEquality>]
|
||||
type Comment = {
|
||||
/// The ID of the comment
|
||||
/// <summary>The ID of the comment</summary>
|
||||
Id: CommentId
|
||||
|
||||
/// The ID of the post to which this comment applies
|
||||
/// <summary>The ID of the post to which this comment applies</summary>
|
||||
PostId: PostId
|
||||
|
||||
/// The ID of the comment to which this comment is a reply
|
||||
/// <summary>The ID of the comment to which this comment is a reply</summary>
|
||||
InReplyToId: CommentId option
|
||||
|
||||
/// The name of the commentor
|
||||
/// <summary>The name of the commentor</summary>
|
||||
Name: string
|
||||
|
||||
/// The e-mail address of the commentor
|
||||
/// <summary>The e-mail address of the commentor</summary>
|
||||
Email: string
|
||||
|
||||
/// The URL of the commentor's personal website
|
||||
/// <summary>The URL of the commentor's personal website</summary>
|
||||
Url: string option
|
||||
|
||||
/// The status of the comment
|
||||
/// <summary>The status of the comment</summary>
|
||||
Status: CommentStatus
|
||||
|
||||
/// When the comment was posted
|
||||
/// <summary>When the comment was posted</summary>
|
||||
PostedOn: Instant
|
||||
|
||||
/// The text of the comment
|
||||
/// <summary>The text of the comment</summary>
|
||||
Text: string
|
||||
} with
|
||||
|
||||
/// An empty comment
|
||||
|
||||
/// <summary>An empty comment</summary>
|
||||
static member Empty =
|
||||
{ Id = CommentId.Empty
|
||||
PostId = PostId.Empty
|
||||
@@ -79,50 +79,50 @@ type Comment = {
|
||||
Text = "" }
|
||||
|
||||
|
||||
/// A page (text not associated with a date/time)
|
||||
/// <summary>A page (text not associated with a date/time)</summary>
|
||||
[<CLIMutable; NoComparison; NoEquality>]
|
||||
type Page = {
|
||||
/// The ID of this page
|
||||
/// <summary>The ID of this page</summary>
|
||||
Id: PageId
|
||||
|
||||
/// The ID of the web log to which this page belongs
|
||||
/// <summary>The ID of the web log to which this page belongs</summary>
|
||||
WebLogId: WebLogId
|
||||
|
||||
/// The ID of the author of this page
|
||||
/// <summary>The ID of the author of this page</summary>
|
||||
AuthorId: WebLogUserId
|
||||
|
||||
/// The title of the page
|
||||
/// <summary>The title of the page</summary>
|
||||
Title: string
|
||||
|
||||
/// The link at which this page is displayed
|
||||
/// <summary>The link at which this page is displayed</summary>
|
||||
Permalink: Permalink
|
||||
|
||||
/// When this page was published
|
||||
/// <summary>When this page was published</summary>
|
||||
PublishedOn: Instant
|
||||
|
||||
/// When this page was last updated
|
||||
/// <summary>When this page was last updated</summary>
|
||||
UpdatedOn: Instant
|
||||
|
||||
/// Whether this page shows as part of the web log's navigation
|
||||
/// <summary>Whether this page shows as part of the web log's navigation</summary>
|
||||
IsInPageList: bool
|
||||
|
||||
/// The template to use when rendering this page
|
||||
/// <summary>The template to use when rendering this page</summary>
|
||||
Template: string option
|
||||
|
||||
/// The current text of the page
|
||||
/// <summary>The current text of the page</summary>
|
||||
Text: string
|
||||
|
||||
/// Metadata for this page
|
||||
/// <summary>Metadata for this page</summary>
|
||||
Metadata: MetaItem list
|
||||
|
||||
/// Permalinks at which this page may have been previously served (useful for migrated content)
|
||||
|
||||
/// <summary>Permalinks at which this page may have been previously served (useful for migrated content)</summary>
|
||||
PriorPermalinks: Permalink list
|
||||
|
||||
/// Revisions of this page
|
||||
/// <summary>Revisions of this page</summary>
|
||||
Revisions: Revision list
|
||||
} with
|
||||
|
||||
/// An empty page
|
||||
|
||||
/// <summary>An empty page</summary>
|
||||
static member Empty =
|
||||
{ Id = PageId.Empty
|
||||
WebLogId = WebLogId.Empty
|
||||
@@ -139,59 +139,59 @@ type Page = {
|
||||
Revisions = [] }
|
||||
|
||||
|
||||
/// A web log post
|
||||
/// <summary>A web log post</summary>
|
||||
[<CLIMutable; NoComparison; NoEquality>]
|
||||
type Post = {
|
||||
/// The ID of this post
|
||||
/// <summary>The ID of this post</summary>
|
||||
Id: PostId
|
||||
|
||||
/// The ID of the web log to which this post belongs
|
||||
/// <summary>The ID of the web log to which this post belongs</summary>
|
||||
WebLogId: WebLogId
|
||||
|
||||
/// The ID of the author of this post
|
||||
/// <summary>The ID of the author of this post</summary>
|
||||
AuthorId: WebLogUserId
|
||||
|
||||
/// The status
|
||||
/// <summary>The status</summary>
|
||||
Status: PostStatus
|
||||
|
||||
/// The title
|
||||
/// <summary>The title</summary>
|
||||
Title: string
|
||||
|
||||
/// The link at which the post resides
|
||||
/// <summary>The link at which the post resides</summary>
|
||||
Permalink: Permalink
|
||||
|
||||
/// The instant on which the post was originally published
|
||||
/// <summary>The instant on which the post was originally published</summary>
|
||||
PublishedOn: Instant option
|
||||
|
||||
/// The instant on which the post was last updated
|
||||
/// <summary>The instant on which the post was last updated</summary>
|
||||
UpdatedOn: Instant
|
||||
|
||||
/// The template to use in displaying the post
|
||||
/// <summary>The template to use in displaying the post</summary>
|
||||
Template: string option
|
||||
|
||||
/// The text of the post in HTML (ready to display) format
|
||||
|
||||
/// <summary>The text of the post in HTML (ready to display) format</summary>
|
||||
Text: string
|
||||
|
||||
/// The Ids of the categories to which this is assigned
|
||||
/// <summary>The Ids of the categories to which this is assigned</summary>
|
||||
CategoryIds: CategoryId list
|
||||
|
||||
/// The tags for the post
|
||||
/// <summary>The tags for the post</summary>
|
||||
Tags: string list
|
||||
|
||||
/// Podcast episode information for this post
|
||||
/// <summary>Podcast episode information for this post</summary>
|
||||
Episode: Episode option
|
||||
|
||||
/// Metadata for the post
|
||||
|
||||
/// <summary>Metadata for the post</summary>
|
||||
Metadata: MetaItem list
|
||||
|
||||
/// Permalinks at which this post may have been previously served (useful for migrated content)
|
||||
|
||||
/// <summary>Permalinks at which this post may have been previously served (useful for migrated content)</summary>
|
||||
PriorPermalinks: Permalink list
|
||||
|
||||
/// The revisions for this post
|
||||
/// <summary>The revisions for this post</summary>
|
||||
Revisions: Revision list
|
||||
} with
|
||||
|
||||
/// An empty post
|
||||
|
||||
/// <summary>An empty post</summary>
|
||||
static member Empty =
|
||||
{ Id = PostId.Empty
|
||||
WebLogId = WebLogId.Empty
|
||||
@@ -211,136 +211,138 @@ type Post = {
|
||||
Revisions = [] }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// A mapping between a tag and its URL value, used to translate restricted characters (ex. "#1" -> "number-1")
|
||||
/// </summary>
|
||||
[<CLIMutable; NoComparison; NoEquality>]
|
||||
type TagMap = {
|
||||
/// The ID of this tag mapping
|
||||
/// <summary>The ID of this tag mapping</summary>
|
||||
Id: TagMapId
|
||||
|
||||
/// The ID of the web log to which this tag mapping belongs
|
||||
|
||||
/// <summary>The ID of the web log to which this tag mapping belongs</summary>
|
||||
WebLogId: WebLogId
|
||||
|
||||
/// The tag which should be mapped to a different value in links
|
||||
|
||||
/// <summary>The tag which should be mapped to a different value in links</summary>
|
||||
Tag: string
|
||||
|
||||
/// The value by which the tag should be linked
|
||||
|
||||
/// <summary>The value by which the tag should be linked</summary>
|
||||
UrlValue: string
|
||||
} with
|
||||
|
||||
/// An empty tag mapping
|
||||
|
||||
/// <summary>An empty tag mapping</summary>
|
||||
static member Empty =
|
||||
{ Id = TagMapId.Empty; WebLogId = WebLogId.Empty; Tag = ""; UrlValue = "" }
|
||||
|
||||
|
||||
/// A theme
|
||||
/// <summary>A theme</summary>
|
||||
[<CLIMutable; NoComparison; NoEquality>]
|
||||
type Theme = {
|
||||
/// The ID / path of the theme
|
||||
/// <summary>The ID / path of the theme</summary>
|
||||
Id: ThemeId
|
||||
|
||||
/// A long name of the theme
|
||||
|
||||
/// <summary>A long name of the theme</summary>
|
||||
Name: string
|
||||
|
||||
/// The version of the theme
|
||||
|
||||
/// <summary>The version of the theme</summary>
|
||||
Version: string
|
||||
|
||||
/// The templates for this theme
|
||||
|
||||
/// <summary>The templates for this theme</summary>
|
||||
Templates: ThemeTemplate list
|
||||
} with
|
||||
|
||||
/// An empty theme
|
||||
|
||||
/// <summary>An empty theme</summary>
|
||||
static member Empty =
|
||||
{ Id = ThemeId.Empty; Name = ""; Version = ""; Templates = [] }
|
||||
|
||||
|
||||
/// A theme asset (a file served as part of a theme, at /themes/[theme]/[asset-path])
|
||||
/// <summary>A theme asset (a file served as part of a theme, at /themes/[theme]/[asset-path])</summary>
|
||||
[<CLIMutable; NoComparison; NoEquality>]
|
||||
type ThemeAsset = {
|
||||
/// The ID of the asset (consists of theme and path)
|
||||
/// <summary>The ID of the asset (consists of theme and path)</summary>
|
||||
Id: ThemeAssetId
|
||||
|
||||
/// The updated date (set from the file date from the ZIP archive)
|
||||
|
||||
/// <summary>The updated date (set from the file date from the ZIP archive)</summary>
|
||||
UpdatedOn: Instant
|
||||
|
||||
/// The data for the asset
|
||||
|
||||
/// <summary>The data for the asset</summary>
|
||||
Data: byte array
|
||||
} with
|
||||
|
||||
/// An empty theme asset
|
||||
|
||||
/// <summary>An empty theme asset</summary>
|
||||
static member Empty =
|
||||
{ Id = ThemeAssetId.Empty; UpdatedOn = Noda.epoch; Data = [||] }
|
||||
|
||||
|
||||
/// An uploaded file
|
||||
/// <summary>An uploaded file</summary>
|
||||
[<CLIMutable; NoComparison; NoEquality>]
|
||||
type Upload = {
|
||||
/// The ID of the upload
|
||||
/// <summary>The ID of the upload</summary>
|
||||
Id: UploadId
|
||||
|
||||
/// The ID of the web log to which this upload belongs
|
||||
|
||||
/// <summary>The ID of the web log to which this upload belongs</summary>
|
||||
WebLogId: WebLogId
|
||||
|
||||
/// The link at which this upload is served
|
||||
|
||||
/// <summary>The link at which this upload is served</summary>
|
||||
Path: Permalink
|
||||
|
||||
/// The updated date/time for this upload
|
||||
|
||||
/// <summary>The updated date/time for this upload</summary>
|
||||
UpdatedOn: Instant
|
||||
|
||||
/// The data for the upload
|
||||
|
||||
/// <summary>The data for the upload</summary>
|
||||
Data: byte array
|
||||
} with
|
||||
|
||||
/// An empty upload
|
||||
|
||||
/// <summary>An empty upload</summary>
|
||||
static member Empty =
|
||||
{ Id = UploadId.Empty; WebLogId = WebLogId.Empty; Path = Permalink.Empty; UpdatedOn = Noda.epoch; Data = [||] }
|
||||
|
||||
|
||||
open Newtonsoft.Json
|
||||
|
||||
/// A web log
|
||||
/// <summary>A web log</summary>
|
||||
[<CLIMutable; NoComparison; NoEquality>]
|
||||
type WebLog = {
|
||||
/// The ID of the web log
|
||||
/// <summary>The ID of the web log</summary>
|
||||
Id: WebLogId
|
||||
|
||||
/// The name of the web log
|
||||
/// <summary>The name of the web log</summary>
|
||||
Name: string
|
||||
|
||||
/// The slug of the web log
|
||||
/// <summary>The slug of the web log</summary>
|
||||
Slug: string
|
||||
|
||||
/// A subtitle for the web log
|
||||
|
||||
/// <summary>A subtitle for the web log</summary>
|
||||
Subtitle: string option
|
||||
|
||||
/// The default page ("posts" or a page Id)
|
||||
/// <summary>The default page ("posts" or a page Id)</summary>
|
||||
DefaultPage: string
|
||||
|
||||
/// The number of posts to display on pages of posts
|
||||
/// <summary>The number of posts to display on pages of posts</summary>
|
||||
PostsPerPage: int
|
||||
|
||||
/// The ID of the theme (also the path within /themes)
|
||||
/// <summary>The ID of the theme (also the path within /themes)</summary>
|
||||
ThemeId: ThemeId
|
||||
|
||||
/// The URL base
|
||||
/// <summary>The URL base</summary>
|
||||
UrlBase: string
|
||||
|
||||
/// The time zone in which dates/times should be displayed
|
||||
/// <summary>The time zone in which dates/times should be displayed</summary>
|
||||
TimeZone: string
|
||||
|
||||
/// The RSS options for this web log
|
||||
|
||||
/// <summary>The RSS options for this web log</summary>
|
||||
Rss: RssOptions
|
||||
|
||||
/// Whether to automatically load htmx
|
||||
|
||||
/// <summary>Whether to automatically load htmx</summary>
|
||||
AutoHtmx: bool
|
||||
|
||||
/// Where uploads are placed
|
||||
|
||||
/// <summary>Where uploads are placed</summary>
|
||||
Uploads: UploadDestination
|
||||
|
||||
/// Redirect rules for this weblog
|
||||
/// <summary>Redirect rules for this weblog</summary>
|
||||
RedirectRules: RedirectRule list
|
||||
} with
|
||||
|
||||
/// An empty web log
|
||||
|
||||
/// <summary>An empty web log</summary>
|
||||
static member Empty =
|
||||
{ Id = WebLogId.Empty
|
||||
Name = ""
|
||||
@@ -355,8 +357,10 @@ type WebLog = {
|
||||
AutoHtmx = false
|
||||
Uploads = Database
|
||||
RedirectRules = [] }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Any extra path where this web log is hosted (blank if web log is hosted at the root of the domain)
|
||||
/// </summary>
|
||||
[<JsonIgnore>]
|
||||
member this.ExtraPath =
|
||||
let pathParts = this.UrlBase.Split "://"
|
||||
@@ -365,16 +369,22 @@ type WebLog = {
|
||||
else
|
||||
let path = pathParts[1].Split "/"
|
||||
if path.Length > 1 then $"""/{path |> Array.skip 1 |> String.concat "/"}""" else ""
|
||||
|
||||
/// Generate an absolute URL for the given link
|
||||
|
||||
/// <summary>Generate an absolute URL for the given link</summary>
|
||||
/// <param name="permalink">The permalink for which an absolute URL should be generated</param>
|
||||
/// <returns>An absolute URL for the given link</returns>
|
||||
member this.AbsoluteUrl(permalink: Permalink) =
|
||||
$"{this.UrlBase}/{permalink}"
|
||||
|
||||
/// Generate a relative URL for the given link
|
||||
|
||||
/// <summary>Generate a relative URL for the given link</summary>
|
||||
/// <param name="permalink">The permalink for which a relative URL should be generated</param>
|
||||
/// <returns>A relative URL for the given link</returns>
|
||||
member this.RelativeUrl(permalink: Permalink) =
|
||||
$"{this.ExtraPath}/{permalink}"
|
||||
|
||||
/// Convert an Instant (UTC reference) to the web log's local date/time
|
||||
|
||||
/// <summary>Convert an Instant (UTC reference) to the web log's local date/time</summary>
|
||||
/// <param name="date">The UTC <c>Instant</c> to be converted</param>
|
||||
/// <returns>The local date/time for this web log</returns>
|
||||
member this.LocalTime(date: Instant) =
|
||||
DateTimeZoneProviders.Tzdb.GetZoneOrNull this.TimeZone
|
||||
|> Option.ofObj
|
||||
@@ -382,44 +392,44 @@ type WebLog = {
|
||||
|> Option.defaultValue (date.ToDateTimeUtc())
|
||||
|
||||
|
||||
/// A user of the web log
|
||||
/// <summary>A user of the web log</summary>
|
||||
[<CLIMutable; NoComparison; NoEquality>]
|
||||
type WebLogUser = {
|
||||
/// The ID of the user
|
||||
/// <summary>The ID of the user</summary>
|
||||
Id: WebLogUserId
|
||||
|
||||
/// The ID of the web log to which this user belongs
|
||||
/// <summary>The ID of the web log to which this user belongs</summary>
|
||||
WebLogId: WebLogId
|
||||
|
||||
/// The user name (e-mail address)
|
||||
/// <summary>The user name (e-mail address)</summary>
|
||||
Email: string
|
||||
|
||||
/// The user's first name
|
||||
/// <summary>The user's first name</summary>
|
||||
FirstName: string
|
||||
|
||||
/// The user's last name
|
||||
/// <summary>The user's last name</summary>
|
||||
LastName: string
|
||||
|
||||
/// The user's preferred name
|
||||
/// <summary>The user's preferred name</summary>
|
||||
PreferredName: string
|
||||
|
||||
/// The hash of the user's password
|
||||
/// <summary>The hash of the user's password</summary>
|
||||
PasswordHash: string
|
||||
|
||||
/// The URL of the user's personal site
|
||||
/// <summary>The URL of the user's personal site</summary>
|
||||
Url: string option
|
||||
|
||||
/// The user's access level
|
||||
/// <summary>The user's access level</summary>
|
||||
AccessLevel: AccessLevel
|
||||
|
||||
/// When the user was created
|
||||
|
||||
/// <summary>When the user was created</summary>
|
||||
CreatedOn: Instant
|
||||
|
||||
/// When the user last logged on
|
||||
|
||||
/// <summary>When the user last logged on</summary>
|
||||
LastSeenOn: Instant option
|
||||
} with
|
||||
|
||||
/// An empty web log user
|
||||
|
||||
/// <summary>An empty web log user</summary>
|
||||
static member Empty =
|
||||
{ Id = WebLogUserId.Empty
|
||||
WebLogId = WebLogId.Empty
|
||||
@@ -432,8 +442,8 @@ type WebLogUser = {
|
||||
AccessLevel = Author
|
||||
CreatedOn = Noda.epoch
|
||||
LastSeenOn = None }
|
||||
|
||||
/// Get the user's displayed name
|
||||
|
||||
/// <summary>Get the user's displayed name</summary>
|
||||
[<JsonIgnore>]
|
||||
member this.DisplayName =
|
||||
(seq { (match this.PreferredName with "" -> this.FirstName | n -> n); " "; this.LastName }
|
||||
|
||||
@@ -7,11 +7,11 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Markdig" Version="0.39.1" />
|
||||
<PackageReference Include="Markdig" Version="0.40.0" />
|
||||
<PackageReference Include="Markdown.ColorCode" Version="2.3.0" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||
<PackageReference Include="NodaTime" Version="3.2.0" />
|
||||
<PackageReference Update="FSharp.Core" Version="9.0.100" />
|
||||
<PackageReference Include="NodaTime" Version="3.2.1" />
|
||||
<PackageReference Update="FSharp.Core" Version="9.0.101" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
+337
-285
File diff suppressed because it is too large
Load Diff
+516
-433
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user