Version 2.1 #41
@ -259,6 +259,9 @@ type IWebLogData =
|
|||||||
/// Find a web log by its ID
|
/// Find a web log by its ID
|
||||||
abstract member FindById : WebLogId -> Task<WebLog option>
|
abstract member FindById : WebLogId -> Task<WebLog option>
|
||||||
|
|
||||||
|
/// Update redirect rules for a web log
|
||||||
|
abstract member UpdateRedirectRules : WebLog -> Task<unit>
|
||||||
|
|
||||||
/// Update RSS options for a web log
|
/// Update RSS options for a web log
|
||||||
abstract member UpdateRssOptions : WebLog -> Task<unit>
|
abstract member UpdateRssOptions : WebLog -> Task<unit>
|
||||||
|
|
||||||
|
@ -45,11 +45,13 @@ type PostgresWebLogData (log : ILogger) =
|
|||||||
log.LogTrace "WebLog.findById"
|
log.LogTrace "WebLog.findById"
|
||||||
Find.byId<WebLog> Table.WebLog (WebLogId.toString webLogId)
|
Find.byId<WebLog> Table.WebLog (WebLogId.toString webLogId)
|
||||||
|
|
||||||
/// Update settings for a web log
|
let updateRedirectRules (webLog : WebLog) = backgroundTask {
|
||||||
let updateSettings (webLog : WebLog) =
|
log.LogTrace "WebLog.updateRedirectRules"
|
||||||
log.LogTrace "WebLog.updateSettings"
|
match! findById webLog.Id with
|
||||||
Update.full Table.WebLog (WebLogId.toString webLog.Id) webLog
|
| Some _ ->
|
||||||
|
do! Update.partialById Table.WebLog (WebLogId.toString webLog.Id) {| RedirectRules = webLog.RedirectRules |}
|
||||||
|
| None -> ()
|
||||||
|
}
|
||||||
/// Update RSS options for a web log
|
/// Update RSS options for a web log
|
||||||
let updateRssOptions (webLog : WebLog) = backgroundTask {
|
let updateRssOptions (webLog : WebLog) = backgroundTask {
|
||||||
log.LogTrace "WebLog.updateRssOptions"
|
log.LogTrace "WebLog.updateRssOptions"
|
||||||
@ -58,11 +60,17 @@ type PostgresWebLogData (log : ILogger) =
|
|||||||
| None -> ()
|
| None -> ()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Update settings for a web log
|
||||||
|
let updateSettings (webLog : WebLog) =
|
||||||
|
log.LogTrace "WebLog.updateSettings"
|
||||||
|
Update.full Table.WebLog (WebLogId.toString webLog.Id) webLog
|
||||||
|
|
||||||
interface IWebLogData with
|
interface IWebLogData with
|
||||||
member _.Add webLog = add webLog
|
member _.Add webLog = add webLog
|
||||||
member _.All () = all ()
|
member _.All () = all ()
|
||||||
member _.Delete webLogId = delete webLogId
|
member _.Delete webLogId = delete webLogId
|
||||||
member _.FindByHost url = findByHost url
|
member _.FindByHost url = findByHost url
|
||||||
member _.FindById webLogId = findById webLogId
|
member _.FindById webLogId = findById webLogId
|
||||||
member _.UpdateSettings webLog = updateSettings webLog
|
member _.UpdateRedirectRules webLog = updateRedirectRules webLog
|
||||||
member _.UpdateRssOptions webLog = updateRssOptions webLog
|
member _.UpdateRssOptions webLog = updateRssOptions webLog
|
||||||
|
member _.UpdateSettings webLog = updateSettings webLog
|
||||||
|
@ -162,7 +162,7 @@ type PostgresData (log : ILogger<PostgresData>, ser : JsonSerializer) =
|
|||||||
/// Migrate from v2 to v2.1
|
/// Migrate from v2 to v2.1
|
||||||
let migrateV2ToV2point1 () = backgroundTask {
|
let migrateV2ToV2point1 () = backgroundTask {
|
||||||
Utils.logMigrationStep log "v2 to v2.1" "Adding empty redirect rule set to all weblogs"
|
Utils.logMigrationStep log "v2 to v2.1" "Adding empty redirect rule set to all weblogs"
|
||||||
do! Custom.nonQuery $"UPDATE {Table.WebLog} SET data['RedirectRules'] = '[]'::json" []
|
do! Custom.nonQuery $"""UPDATE {Table.WebLog} SET data = data + '{{ "RedirectRules": [] }}'::json""" []
|
||||||
|
|
||||||
Utils.logMigrationStep log "v2 to v2.1" "Setting database to version 2.1"
|
Utils.logMigrationStep log "v2 to v2.1" "Setting database to version 2.1"
|
||||||
do! setDbVersion "v2.1"
|
do! setDbVersion "v2.1"
|
||||||
|
@ -1031,6 +1031,13 @@ type RethinkDbData (conn : Net.IConnection, config : DataConfig, log : ILogger<R
|
|||||||
resultOption; withRetryOptionDefault conn
|
resultOption; withRetryOptionDefault conn
|
||||||
}
|
}
|
||||||
|
|
||||||
|
member _.UpdateRedirectRules webLog = rethink {
|
||||||
|
withTable Table.WebLog
|
||||||
|
get webLog.Id
|
||||||
|
update [ nameof WebLog.empty.RedirectRules, webLog.RedirectRules :> obj ]
|
||||||
|
write; withRetryDefault; ignoreResult conn
|
||||||
|
}
|
||||||
|
|
||||||
member _.UpdateRssOptions webLog = rethink {
|
member _.UpdateRssOptions webLog = rethink {
|
||||||
withTable Table.WebLog
|
withTable Table.WebLog
|
||||||
get webLog.Id
|
get webLog.Id
|
||||||
|
@ -206,6 +206,33 @@ type SQLiteWebLogData (conn : SqliteConnection, ser : JsonSerializer) =
|
|||||||
return None
|
return None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Update redirect rules for a web log
|
||||||
|
let updateRedirectRules webLog = backgroundTask {
|
||||||
|
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
|
||||||
|
do! write cmd
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Update RSS options for a web log
|
||||||
|
let updateRssOptions webLog = backgroundTask {
|
||||||
|
use cmd = conn.CreateCommand ()
|
||||||
|
cmd.CommandText <-
|
||||||
|
"UPDATE web_log
|
||||||
|
SET is_feed_enabled = @isFeedEnabled,
|
||||||
|
feed_name = @feedName,
|
||||||
|
items_in_feed = @itemsInFeed,
|
||||||
|
is_category_enabled = @isCategoryEnabled,
|
||||||
|
is_tag_enabled = @isTagEnabled,
|
||||||
|
copyright = @copyright
|
||||||
|
WHERE id = @id"
|
||||||
|
addWebLogRssParameters cmd webLog
|
||||||
|
cmd.Parameters.AddWithValue ("@id", WebLogId.toString webLog.Id) |> ignore
|
||||||
|
do! write cmd
|
||||||
|
do! updateCustomFeeds webLog
|
||||||
|
}
|
||||||
|
|
||||||
/// Update settings for a web log
|
/// Update settings for a web log
|
||||||
let updateSettings webLog = backgroundTask {
|
let updateSettings webLog = backgroundTask {
|
||||||
use cmd = conn.CreateCommand ()
|
use cmd = conn.CreateCommand ()
|
||||||
@ -233,29 +260,12 @@ type SQLiteWebLogData (conn : SqliteConnection, ser : JsonSerializer) =
|
|||||||
do! write cmd
|
do! write cmd
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Update RSS options for a web log
|
|
||||||
let updateRssOptions webLog = backgroundTask {
|
|
||||||
use cmd = conn.CreateCommand ()
|
|
||||||
cmd.CommandText <-
|
|
||||||
"UPDATE web_log
|
|
||||||
SET is_feed_enabled = @isFeedEnabled,
|
|
||||||
feed_name = @feedName,
|
|
||||||
items_in_feed = @itemsInFeed,
|
|
||||||
is_category_enabled = @isCategoryEnabled,
|
|
||||||
is_tag_enabled = @isTagEnabled,
|
|
||||||
copyright = @copyright
|
|
||||||
WHERE id = @id"
|
|
||||||
addWebLogRssParameters cmd webLog
|
|
||||||
cmd.Parameters.AddWithValue ("@id", WebLogId.toString webLog.Id) |> ignore
|
|
||||||
do! write cmd
|
|
||||||
do! updateCustomFeeds webLog
|
|
||||||
}
|
|
||||||
|
|
||||||
interface IWebLogData with
|
interface IWebLogData with
|
||||||
member _.Add webLog = add webLog
|
member _.Add webLog = add webLog
|
||||||
member _.All () = all ()
|
member _.All () = all ()
|
||||||
member _.Delete webLogId = delete webLogId
|
member _.Delete webLogId = delete webLogId
|
||||||
member _.FindByHost url = findByHost url
|
member _.FindByHost url = findByHost url
|
||||||
member _.FindById webLogId = findById webLogId
|
member _.FindById webLogId = findById webLogId
|
||||||
member _.UpdateSettings webLog = updateSettings webLog
|
member _.UpdateRedirectRules webLog = updateRedirectRules webLog
|
||||||
member _.UpdateRssOptions webLog = updateRssOptions webLog
|
member _.UpdateRssOptions webLog = updateRssOptions webLog
|
||||||
|
member _.UpdateSettings webLog = updateSettings webLog
|
||||||
|
@ -426,12 +426,24 @@ module PostId =
|
|||||||
type RedirectRule =
|
type RedirectRule =
|
||||||
{ /// The From string or pattern
|
{ /// The From string or pattern
|
||||||
From : string
|
From : string
|
||||||
|
|
||||||
/// The To string or pattern
|
/// The To string or pattern
|
||||||
To : string
|
To : string
|
||||||
|
|
||||||
/// Whether to use regular expressions on this rule
|
/// Whether to use regular expressions on this rule
|
||||||
IsRegex : bool
|
IsRegex : bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Functions to support redirect rules
|
||||||
|
module RedirectRule =
|
||||||
|
|
||||||
|
/// An empty redirect rule
|
||||||
|
let empty =
|
||||||
|
{ From = ""
|
||||||
|
To = ""
|
||||||
|
IsRegex = false
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/// An identifier for a custom feed
|
/// An identifier for a custom feed
|
||||||
type CustomFeedId = CustomFeedId of string
|
type CustomFeedId = CustomFeedId of string
|
||||||
|
@ -807,6 +807,43 @@ type EditPostModel =
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// View model to add/edit a redirect rule
|
||||||
|
[<CLIMutable; NoComparison; NoEquality>]
|
||||||
|
type EditRedirectRuleModel =
|
||||||
|
{ /// The ID (index) of the rule being edited
|
||||||
|
RuleId : int
|
||||||
|
|
||||||
|
/// The "from" side of the rule
|
||||||
|
From : string
|
||||||
|
|
||||||
|
/// The "to" side of the rule
|
||||||
|
To : string
|
||||||
|
|
||||||
|
/// Whether this rule uses a regular expression
|
||||||
|
IsRegex : bool
|
||||||
|
|
||||||
|
/// Whether a new rule should be inserted at the top or appended to the end (ignored for edits)
|
||||||
|
InsertAtTop : bool
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a model from an existing rule
|
||||||
|
static member fromRule idx (rule : RedirectRule) =
|
||||||
|
{ RuleId = idx
|
||||||
|
From = rule.From
|
||||||
|
To = rule.To
|
||||||
|
IsRegex = rule.IsRegex
|
||||||
|
InsertAtTop = false
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Update a rule with the values from this model
|
||||||
|
member this.UpdateRule (rule : RedirectRule) =
|
||||||
|
{ rule with
|
||||||
|
From = this.From
|
||||||
|
To = this.To
|
||||||
|
IsRegex = this.IsRegex
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/// View model to edit RSS settings
|
/// View model to edit RSS settings
|
||||||
[<CLIMutable; NoComparison; NoEquality>]
|
[<CLIMutable; NoComparison; NoEquality>]
|
||||||
type EditRssModel =
|
type EditRssModel =
|
||||||
|
@ -225,13 +225,15 @@ let register () =
|
|||||||
|
|
||||||
[ // Domain types
|
[ // Domain types
|
||||||
typeof<CustomFeed>; typeof<Episode>; typeof<Episode option>; typeof<MetaItem>; typeof<Page>
|
typeof<CustomFeed>; typeof<Episode>; typeof<Episode option>; typeof<MetaItem>; typeof<Page>
|
||||||
typeof<RssOptions>; typeof<TagMap>; typeof<UploadDestination>; typeof<WebLog>
|
typeof<RedirectRule>; typeof<RssOptions>; typeof<TagMap>; typeof<UploadDestination>; typeof<WebLog>
|
||||||
// View models
|
// View models
|
||||||
typeof<DashboardModel>; typeof<DisplayCategory>; typeof<DisplayCustomFeed>; typeof<DisplayPage>
|
typeof<DashboardModel>; typeof<DisplayCategory>; typeof<DisplayCustomFeed>
|
||||||
typeof<DisplayRevision>; typeof<DisplayTheme>; typeof<DisplayUpload>; typeof<DisplayUser>
|
typeof<DisplayPage>; typeof<DisplayRevision>; typeof<DisplayTheme>
|
||||||
typeof<EditCategoryModel>; typeof<EditCustomFeedModel>; typeof<EditMyInfoModel>; typeof<EditPageModel>
|
typeof<DisplayUpload>; typeof<DisplayUser>; typeof<EditCategoryModel>
|
||||||
typeof<EditPostModel>; typeof<EditRssModel>; typeof<EditTagMapModel>; typeof<EditUserModel>
|
typeof<EditCustomFeedModel>; typeof<EditMyInfoModel>; typeof<EditPageModel>
|
||||||
typeof<LogOnModel>; typeof<ManagePermalinksModel>; typeof<ManageRevisionsModel>; typeof<PostDisplay>
|
typeof<EditPostModel>; typeof<EditRedirectRuleModel>; typeof<EditRssModel>
|
||||||
|
typeof<EditTagMapModel>; typeof<EditUserModel>; typeof<LogOnModel>
|
||||||
|
typeof<ManagePermalinksModel>; typeof<ManageRevisionsModel>; typeof<PostDisplay>
|
||||||
typeof<PostListItem>; typeof<SettingsModel>; typeof<UserMessage>
|
typeof<PostListItem>; typeof<SettingsModel>; typeof<UserMessage>
|
||||||
// Framework types
|
// Framework types
|
||||||
typeof<AntiforgeryTokenSet>; typeof<DateTime option>; typeof<int option>; typeof<KeyValuePair>
|
typeof<AntiforgeryTokenSet>; typeof<DateTime option>; typeof<int option>; typeof<KeyValuePair>
|
||||||
|
@ -132,7 +132,7 @@ module Category =
|
|||||||
open MyWebLog.Data
|
open MyWebLog.Data
|
||||||
|
|
||||||
// GET /admin/categories
|
// GET /admin/categories
|
||||||
let all : HttpHandler = requireAccess WebLogAdmin >=> fun next ctx -> task {
|
let all : HttpHandler = fun next ctx -> task {
|
||||||
match! TemplateCache.get adminTheme "category-list-body" ctx.Data with
|
match! TemplateCache.get adminTheme "category-list-body" ctx.Data with
|
||||||
| Ok catListTemplate ->
|
| Ok catListTemplate ->
|
||||||
let! hash =
|
let! hash =
|
||||||
@ -146,14 +146,14 @@ module Category =
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GET /admin/categories/bare
|
// GET /admin/categories/bare
|
||||||
let bare : HttpHandler = requireAccess WebLogAdmin >=> fun next ctx ->
|
let bare : HttpHandler = fun next ctx ->
|
||||||
hashForPage "Categories"
|
hashForPage "Categories"
|
||||||
|> withAntiCsrf ctx
|
|> withAntiCsrf ctx
|
||||||
|> adminBareView "category-list-body" next ctx
|
|> adminBareView "category-list-body" next ctx
|
||||||
|
|
||||||
|
|
||||||
// GET /admin/category/{id}/edit
|
// GET /admin/category/{id}/edit
|
||||||
let edit catId : HttpHandler = requireAccess WebLogAdmin >=> fun next ctx -> task {
|
let edit catId : HttpHandler = fun next ctx -> task {
|
||||||
let! result = task {
|
let! result = task {
|
||||||
match catId with
|
match catId with
|
||||||
| "new" -> return Some ("Add a New Category", { Category.empty with Id = CategoryId "new" })
|
| "new" -> return Some ("Add a New Category", { Category.empty with Id = CategoryId "new" })
|
||||||
@ -173,7 +173,7 @@ module Category =
|
|||||||
}
|
}
|
||||||
|
|
||||||
// POST /admin/category/save
|
// POST /admin/category/save
|
||||||
let save : HttpHandler = requireAccess WebLogAdmin >=> fun next ctx -> task {
|
let save : HttpHandler = fun next ctx -> task {
|
||||||
let data = ctx.Data
|
let data = ctx.Data
|
||||||
let! model = ctx.BindFormAsync<EditCategoryModel> ()
|
let! model = ctx.BindFormAsync<EditCategoryModel> ()
|
||||||
let category =
|
let category =
|
||||||
@ -196,7 +196,7 @@ module Category =
|
|||||||
}
|
}
|
||||||
|
|
||||||
// POST /admin/category/{id}/delete
|
// POST /admin/category/{id}/delete
|
||||||
let delete catId : HttpHandler = requireAccess WebLogAdmin >=> fun next ctx -> task {
|
let delete catId : HttpHandler = fun next ctx -> task {
|
||||||
let! result = ctx.Data.Category.Delete (CategoryId catId) ctx.WebLog.Id
|
let! result = ctx.Data.Category.Delete (CategoryId catId) ctx.WebLog.Id
|
||||||
match result with
|
match result with
|
||||||
| CategoryDeleted
|
| CategoryDeleted
|
||||||
@ -217,8 +217,10 @@ module Category =
|
|||||||
/// ~~~ REDIRECT RULES ~~~
|
/// ~~~ REDIRECT RULES ~~~
|
||||||
module RedirectRules =
|
module RedirectRules =
|
||||||
|
|
||||||
// GET /admin/redirect-rules
|
open Microsoft.AspNetCore.Http
|
||||||
let all : HttpHandler = requireAccess WebLogAdmin >=> fun next ctx -> task {
|
|
||||||
|
// GET /admin/settings/redirect-rules
|
||||||
|
let all : HttpHandler = fun next ctx -> task {
|
||||||
return!
|
return!
|
||||||
hashForPage "Redirect Rules"
|
hashForPage "Redirect Rules"
|
||||||
|> withAntiCsrf ctx
|
|> withAntiCsrf ctx
|
||||||
@ -226,6 +228,82 @@ module RedirectRules =
|
|||||||
|> adminView "redirect-list" next ctx
|
|> adminView "redirect-list" next ctx
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GET /admin/settings/redirect-rules/[index]
|
||||||
|
let edit idx : HttpHandler = fun next ctx -> task {
|
||||||
|
if idx = -1 then
|
||||||
|
return!
|
||||||
|
hashForPage "Add Redirect Rule"
|
||||||
|
|> addToHash "model" (EditRedirectRuleModel.fromRule -1 RedirectRule.empty)
|
||||||
|
|> withAntiCsrf ctx
|
||||||
|
|> adminBareView "redirect-edit" next ctx
|
||||||
|
else
|
||||||
|
let rules = ctx.WebLog.RedirectRules
|
||||||
|
if rules.Length < idx || idx < 0 then
|
||||||
|
return! Error.notFound next ctx
|
||||||
|
else
|
||||||
|
return!
|
||||||
|
hashForPage "Edit Redirect Rule"
|
||||||
|
|> addToHash "model" (EditRedirectRuleModel.fromRule idx (List.item idx rules))
|
||||||
|
|> withAntiCsrf ctx
|
||||||
|
|> adminBareView "redirect-edit" next ctx
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Update the web log's redirect rules in the database, the request web log, and the web log cache
|
||||||
|
let private updateRedirectRules (ctx : HttpContext) webLog = backgroundTask {
|
||||||
|
do! ctx.Data.WebLog.UpdateRedirectRules webLog
|
||||||
|
ctx.Items["webLog"] <- webLog
|
||||||
|
WebLogCache.set webLog
|
||||||
|
}
|
||||||
|
|
||||||
|
// POST /admin/settings/redirect-rules/[index]
|
||||||
|
let save idx : HttpHandler = fun next ctx -> task {
|
||||||
|
let! model = ctx.BindFormAsync<EditRedirectRuleModel> ()
|
||||||
|
let isNew = idx = -1
|
||||||
|
let rules = ctx.WebLog.RedirectRules
|
||||||
|
let rule = model.UpdateRule (if isNew then RedirectRule.empty else List.item idx rules)
|
||||||
|
let newRules =
|
||||||
|
match isNew with
|
||||||
|
| true when model.InsertAtTop -> List.insertAt 0 rule rules
|
||||||
|
| true -> List.insertAt (rules.Length) rule rules
|
||||||
|
| false -> rules |> List.removeAt idx |> List.insertAt idx rule
|
||||||
|
do! updateRedirectRules ctx { ctx.WebLog with RedirectRules = newRules }
|
||||||
|
do! addMessage ctx { UserMessage.success with Message = "Redirect rule saved successfully" }
|
||||||
|
return! all next ctx
|
||||||
|
}
|
||||||
|
|
||||||
|
// POST /admin/settings/redirect-rules/[index]/up
|
||||||
|
let moveUp idx : HttpHandler = fun next ctx -> task {
|
||||||
|
if idx < 1 || idx >= ctx.WebLog.RedirectRules.Length then
|
||||||
|
return! Error.notFound next ctx
|
||||||
|
else
|
||||||
|
let toMove = List.item idx ctx.WebLog.RedirectRules
|
||||||
|
let newRules = ctx.WebLog.RedirectRules |> List.removeAt idx |> List.insertAt (idx - 1) toMove
|
||||||
|
do! updateRedirectRules ctx { ctx.WebLog with RedirectRules = newRules }
|
||||||
|
return! all next ctx
|
||||||
|
}
|
||||||
|
|
||||||
|
// POST /admin/settings/redirect-rules/[index]/down
|
||||||
|
let moveDown idx : HttpHandler = fun next ctx -> task {
|
||||||
|
if idx < 0 || idx >= ctx.WebLog.RedirectRules.Length - 1 then
|
||||||
|
return! Error.notFound next ctx
|
||||||
|
else
|
||||||
|
let toMove = List.item idx ctx.WebLog.RedirectRules
|
||||||
|
let newRules = ctx.WebLog.RedirectRules |> List.removeAt idx |> List.insertAt (idx + 1) toMove
|
||||||
|
do! updateRedirectRules ctx { ctx.WebLog with RedirectRules = newRules }
|
||||||
|
return! all next ctx
|
||||||
|
}
|
||||||
|
|
||||||
|
// POST /admin/settings/redirect-rules/[index]/delete
|
||||||
|
let delete idx : HttpHandler = fun next ctx -> task {
|
||||||
|
if idx < 0 || idx >= ctx.WebLog.RedirectRules.Length then
|
||||||
|
return! Error.notFound next ctx
|
||||||
|
else
|
||||||
|
let rules = ctx.WebLog.RedirectRules |> List.removeAt idx
|
||||||
|
do! updateRedirectRules ctx { ctx.WebLog with RedirectRules = rules }
|
||||||
|
do! addMessage ctx { UserMessage.success with Message = "Redirect rule deleted successfully" }
|
||||||
|
return! all next ctx
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/// ~~~ TAG MAPPINGS ~~~
|
/// ~~~ TAG MAPPINGS ~~~
|
||||||
module TagMapping =
|
module TagMapping =
|
||||||
@ -243,7 +321,7 @@ module TagMapping =
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GET /admin/settings/tag-mappings
|
// GET /admin/settings/tag-mappings
|
||||||
let all : HttpHandler = requireAccess WebLogAdmin >=> fun next ctx -> task {
|
let all : HttpHandler = fun next ctx -> task {
|
||||||
let! hash =
|
let! hash =
|
||||||
hashForPage ""
|
hashForPage ""
|
||||||
|> withAntiCsrf ctx
|
|> withAntiCsrf ctx
|
||||||
@ -252,7 +330,7 @@ module TagMapping =
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GET /admin/settings/tag-mapping/{id}/edit
|
// GET /admin/settings/tag-mapping/{id}/edit
|
||||||
let edit tagMapId : HttpHandler = requireAccess WebLogAdmin >=> fun next ctx -> task {
|
let edit tagMapId : HttpHandler = fun next ctx -> task {
|
||||||
let isNew = tagMapId = "new"
|
let isNew = tagMapId = "new"
|
||||||
let tagMap =
|
let tagMap =
|
||||||
if isNew then someTask { TagMap.empty with Id = TagMapId "new" }
|
if isNew then someTask { TagMap.empty with Id = TagMapId "new" }
|
||||||
@ -268,7 +346,7 @@ module TagMapping =
|
|||||||
}
|
}
|
||||||
|
|
||||||
// POST /admin/settings/tag-mapping/save
|
// POST /admin/settings/tag-mapping/save
|
||||||
let save : HttpHandler = requireAccess WebLogAdmin >=> fun next ctx -> task {
|
let save : HttpHandler = fun next ctx -> task {
|
||||||
let data = ctx.Data
|
let data = ctx.Data
|
||||||
let! model = ctx.BindFormAsync<EditTagMapModel> ()
|
let! model = ctx.BindFormAsync<EditTagMapModel> ()
|
||||||
let tagMap =
|
let tagMap =
|
||||||
@ -283,7 +361,7 @@ module TagMapping =
|
|||||||
}
|
}
|
||||||
|
|
||||||
// POST /admin/settings/tag-mapping/{id}/delete
|
// POST /admin/settings/tag-mapping/{id}/delete
|
||||||
let delete tagMapId : HttpHandler = requireAccess WebLogAdmin >=> fun next ctx -> task {
|
let delete tagMapId : HttpHandler = fun next ctx -> task {
|
||||||
match! ctx.Data.TagMap.Delete (TagMapId tagMapId) ctx.WebLog.Id with
|
match! ctx.Data.TagMap.Delete (TagMapId tagMapId) ctx.WebLog.Id with
|
||||||
| true -> do! addMessage ctx { UserMessage.success with Message = "Tag mapping deleted successfully" }
|
| true -> do! addMessage ctx { UserMessage.success with Message = "Tag mapping deleted successfully" }
|
||||||
| false -> do! addMessage ctx { UserMessage.error with Message = "Tag mapping not found; nothing deleted" }
|
| false -> do! addMessage ctx { UserMessage.error with Message = "Tag mapping not found; nothing deleted" }
|
||||||
@ -460,7 +538,7 @@ module WebLog =
|
|||||||
open System.IO
|
open System.IO
|
||||||
|
|
||||||
// GET /admin/settings
|
// GET /admin/settings
|
||||||
let settings : HttpHandler = requireAccess WebLogAdmin >=> fun next ctx -> task {
|
let settings : HttpHandler = fun next ctx -> task {
|
||||||
let data = ctx.Data
|
let data = ctx.Data
|
||||||
match! TemplateCache.get adminTheme "user-list-body" data with
|
match! TemplateCache.get adminTheme "user-list-body" data with
|
||||||
| Ok userTemplate ->
|
| Ok userTemplate ->
|
||||||
@ -508,7 +586,7 @@ module WebLog =
|
|||||||
}
|
}
|
||||||
|
|
||||||
// POST /admin/settings
|
// POST /admin/settings
|
||||||
let saveSettings : HttpHandler = requireAccess WebLogAdmin >=> fun next ctx -> task {
|
let saveSettings : HttpHandler = fun next ctx -> task {
|
||||||
let data = ctx.Data
|
let data = ctx.Data
|
||||||
let! model = ctx.BindFormAsync<SettingsModel> ()
|
let! model = ctx.BindFormAsync<SettingsModel> ()
|
||||||
match! data.WebLog.FindById ctx.WebLog.Id with
|
match! data.WebLog.FindById ctx.WebLog.Id with
|
||||||
|
@ -107,7 +107,7 @@ let router : HttpHandler = choose [
|
|||||||
subRoute "/admin" (requireUser >=> choose [
|
subRoute "/admin" (requireUser >=> choose [
|
||||||
GET_HEAD >=> choose [
|
GET_HEAD >=> choose [
|
||||||
route "/administration" >=> Admin.Dashboard.admin
|
route "/administration" >=> Admin.Dashboard.admin
|
||||||
subRoute "/categor" (choose [
|
subRoute "/categor" (requireAccess WebLogAdmin >=> choose [
|
||||||
route "ies" >=> Admin.Category.all
|
route "ies" >=> Admin.Category.all
|
||||||
route "ies/bare" >=> Admin.Category.bare
|
route "ies/bare" >=> Admin.Category.bare
|
||||||
routef "y/%s/edit" Admin.Category.edit
|
routef "y/%s/edit" Admin.Category.edit
|
||||||
@ -130,20 +130,21 @@ let router : HttpHandler = choose [
|
|||||||
routef "/%s/revision/%s/preview" Post.previewRevision
|
routef "/%s/revision/%s/preview" Post.previewRevision
|
||||||
routef "/%s/revisions" Post.editRevisions
|
routef "/%s/revisions" Post.editRevisions
|
||||||
])
|
])
|
||||||
subRoute "/redirect-rules" (choose [
|
subRoute "/settings" (requireAccess WebLogAdmin >=> choose [
|
||||||
route "" >=> Admin.RedirectRules.all
|
|
||||||
])
|
|
||||||
subRoute "/settings" (choose [
|
|
||||||
route "" >=> Admin.WebLog.settings
|
route "" >=> Admin.WebLog.settings
|
||||||
routef "/rss/%s/edit" Feed.editCustomFeed
|
routef "/rss/%s/edit" Feed.editCustomFeed
|
||||||
subRoute "/user" (choose [
|
subRoute "/redirect-rules" (choose [
|
||||||
route "s" >=> User.all
|
route "" >=> Admin.RedirectRules.all
|
||||||
routef "/%s/edit" User.edit
|
routef "/%i" Admin.RedirectRules.edit
|
||||||
])
|
])
|
||||||
subRoute "/tag-mapping" (choose [
|
subRoute "/tag-mapping" (choose [
|
||||||
route "s" >=> Admin.TagMapping.all
|
route "s" >=> Admin.TagMapping.all
|
||||||
routef "/%s/edit" Admin.TagMapping.edit
|
routef "/%s/edit" Admin.TagMapping.edit
|
||||||
])
|
])
|
||||||
|
subRoute "/user" (choose [
|
||||||
|
route "s" >=> User.all
|
||||||
|
routef "/%s/edit" User.edit
|
||||||
|
])
|
||||||
])
|
])
|
||||||
subRoute "/theme" (choose [
|
subRoute "/theme" (choose [
|
||||||
route "/list" >=> Admin.Theme.all
|
route "/list" >=> Admin.Theme.all
|
||||||
@ -159,7 +160,7 @@ let router : HttpHandler = choose [
|
|||||||
routef "/theme/%s/refresh" Admin.Cache.refreshTheme
|
routef "/theme/%s/refresh" Admin.Cache.refreshTheme
|
||||||
routef "/web-log/%s/refresh" Admin.Cache.refreshWebLog
|
routef "/web-log/%s/refresh" Admin.Cache.refreshWebLog
|
||||||
])
|
])
|
||||||
subRoute "/category" (choose [
|
subRoute "/category" (requireAccess WebLogAdmin >=> choose [
|
||||||
route "/save" >=> Admin.Category.save
|
route "/save" >=> Admin.Category.save
|
||||||
routef "/%s/delete" Admin.Category.delete
|
routef "/%s/delete" Admin.Category.delete
|
||||||
])
|
])
|
||||||
@ -180,13 +181,19 @@ let router : HttpHandler = choose [
|
|||||||
routef "/%s/revision/%s/restore" Post.restoreRevision
|
routef "/%s/revision/%s/restore" Post.restoreRevision
|
||||||
routef "/%s/revisions/purge" Post.purgeRevisions
|
routef "/%s/revisions/purge" Post.purgeRevisions
|
||||||
])
|
])
|
||||||
subRoute "/settings" (choose [
|
subRoute "/settings" (requireAccess WebLogAdmin >=> choose [
|
||||||
route "" >=> Admin.WebLog.saveSettings
|
route "" >=> Admin.WebLog.saveSettings
|
||||||
subRoute "/rss" (choose [
|
subRoute "/rss" (choose [
|
||||||
route "" >=> Feed.saveSettings
|
route "" >=> Feed.saveSettings
|
||||||
route "/save" >=> Feed.saveCustomFeed
|
route "/save" >=> Feed.saveCustomFeed
|
||||||
routef "/%s/delete" Feed.deleteCustomFeed
|
routef "/%s/delete" Feed.deleteCustomFeed
|
||||||
])
|
])
|
||||||
|
subRoute "/redirect-rules" (choose [
|
||||||
|
routef "/%i" Admin.RedirectRules.save
|
||||||
|
routef "/%i/up" Admin.RedirectRules.moveUp
|
||||||
|
routef "/%i/down" Admin.RedirectRules.moveDown
|
||||||
|
routef "/%i/delete" Admin.RedirectRules.delete
|
||||||
|
])
|
||||||
subRoute "/tag-mapping" (choose [
|
subRoute "/tag-mapping" (choose [
|
||||||
route "/save" >=> Admin.TagMapping.save
|
route "/save" >=> Admin.TagMapping.save
|
||||||
routef "/%s/delete" Admin.TagMapping.delete
|
routef "/%s/delete" Admin.TagMapping.delete
|
||||||
|
@ -95,7 +95,7 @@ open Giraffe.Htmx
|
|||||||
let private goAway : HttpHandler = RequestErrors.BAD_REQUEST "really?"
|
let private goAway : HttpHandler = RequestErrors.BAD_REQUEST "really?"
|
||||||
|
|
||||||
// GET /admin/settings/users
|
// GET /admin/settings/users
|
||||||
let all : HttpHandler = requireAccess WebLogAdmin >=> fun next ctx -> task {
|
let all : HttpHandler = fun next ctx -> task {
|
||||||
let! users = ctx.Data.WebLogUser.FindByWebLog ctx.WebLog.Id
|
let! users = ctx.Data.WebLogUser.FindByWebLog ctx.WebLog.Id
|
||||||
return!
|
return!
|
||||||
hashForPage "User Administration"
|
hashForPage "User Administration"
|
||||||
@ -119,7 +119,7 @@ let private showEdit (model : EditUserModel) : HttpHandler = fun next ctx ->
|
|||||||
|> adminBareView "user-edit" next ctx
|
|> adminBareView "user-edit" next ctx
|
||||||
|
|
||||||
// GET /admin/settings/user/{id}/edit
|
// GET /admin/settings/user/{id}/edit
|
||||||
let edit usrId : HttpHandler = requireAccess WebLogAdmin >=> fun next ctx -> task {
|
let edit usrId : HttpHandler = fun next ctx -> task {
|
||||||
let isNew = usrId = "new"
|
let isNew = usrId = "new"
|
||||||
let userId = WebLogUserId usrId
|
let userId = WebLogUserId usrId
|
||||||
let tryUser =
|
let tryUser =
|
||||||
@ -131,7 +131,7 @@ let edit usrId : HttpHandler = requireAccess WebLogAdmin >=> fun next ctx -> tas
|
|||||||
}
|
}
|
||||||
|
|
||||||
// POST /admin/settings/user/{id}/delete
|
// POST /admin/settings/user/{id}/delete
|
||||||
let delete userId : HttpHandler = requireAccess WebLogAdmin >=> fun next ctx -> task {
|
let delete userId : HttpHandler = fun next ctx -> task {
|
||||||
let data = ctx.Data
|
let data = ctx.Data
|
||||||
match! data.WebLogUser.FindById (WebLogUserId userId) ctx.WebLog.Id with
|
match! data.WebLogUser.FindById (WebLogUserId userId) ctx.WebLog.Id with
|
||||||
| Some user ->
|
| Some user ->
|
||||||
|
@ -54,7 +54,7 @@
|
|||||||
<span class="text-muted"> • </span>
|
<span class="text-muted"> • </span>
|
||||||
{%- assign post_del_link = "admin/post/" | append: post.id | append: "/delete" | relative_link -%}
|
{%- assign post_del_link = "admin/post/" | append: post.id | append: "/delete" | relative_link -%}
|
||||||
<a href="{{ post_del_link }}" hx-post="{{ post_del_link }}" class="text-danger"
|
<a href="{{ post_del_link }}" hx-post="{{ post_del_link }}" class="text-danger"
|
||||||
hx-confirm="Are you sure you want to delete the page “{{ post.title | strip_html | escape }}”? This action cannot be undone.">
|
hx-confirm="Are you sure you want to delete the post “{{ post.title | strip_html | escape }}”? This action cannot be undone.">
|
||||||
Delete
|
Delete
|
||||||
</a>
|
</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
48
src/admin-theme/redirect-edit.liquid
Normal file
48
src/admin-theme/redirect-edit.liquid
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
<h3>{% if model.rule_id < 0 %}Add{% else %}Edit{% endif %} Redirect Rule</h3>
|
||||||
|
{%- assign post_url = "admin/settings/redirect-rules/" | append: model.rule_id | relative_link -%}
|
||||||
|
<form action="{{ post_url }}" hx-post="{{ post_url }}" hx-target="body" method="POST" class="container">
|
||||||
|
<input type="hidden" name="{{ csrf.form_field_name }}" value="{{ csrf.request_token }}">
|
||||||
|
<input type="hidden" name="RuleId" value="{{ model.rule_id }}">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-12 col-lg-5 mb-3">
|
||||||
|
<div class="form-floating">
|
||||||
|
<input type="text" name="From" id="from" class="form-control" placeholder="From local URL/pattern" autofocus
|
||||||
|
required value="{{ model.from | escape }}">
|
||||||
|
<label for="from">From</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-12 col-lg-5 mb-3">
|
||||||
|
<div class="form-floating">
|
||||||
|
<input type="text" name="To" id="to" class="form-control" placeholder="To URL/pattern" required
|
||||||
|
value="{{ model.to | escape }}">
|
||||||
|
<label for="from">To</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-12 col-lg-2 mb-3">
|
||||||
|
<div class="form-check form-switch">
|
||||||
|
<input type="checkbox" name="IsRegex" id="isRegex" class="form-check-input" value="true"
|
||||||
|
{%- if model.is_regex %} checked="checked"{% endif %}>
|
||||||
|
<label for="isRegex">Use RegEx</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% if model.rule_id < 0 %}
|
||||||
|
<div class="row mb-3">
|
||||||
|
<div class="col-12 text-center">
|
||||||
|
<label>Add Rule</label>
|
||||||
|
<div class="btn-group btn-group-sm" role="group" aria-label="New rule placement button group">
|
||||||
|
<input type="radio" name="InsertAtTop" id="insert_top" class="btn-check" value="true">
|
||||||
|
<label class="btn btn-sm btn-outline-secondary" for="insert_top">Top</label>
|
||||||
|
<input type="radio" name="InsertAtTop" id="insert_bottom" class="btn-check" value="false" checked="checked">
|
||||||
|
<label class="btn btn-sm btn-outline-secondary" for="insert_bottom">Bottom</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
<div class="row mb-3">
|
||||||
|
<div class="col text-center">
|
||||||
|
<button type="submit" class="btn btn-sm btn-primary">Save Changes</button>
|
||||||
|
<a href="{{ "admin/settings/redirect-rules" | relative_link }}" class="btn btn-sm btn-secondary ms-3">Cancel</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
@ -1,7 +1,17 @@
|
|||||||
<h2 class="my-3">Redirect Rules</h2>
|
<h2 class="my-3">{{ page_title }}</h2>
|
||||||
<article>
|
<article>
|
||||||
|
<p class="mb-3">
|
||||||
<a href="{{ "admin/settings" | relative_link }}">« Back to Settings</a>
|
<a href="{{ "admin/settings" | relative_link }}">« Back to Settings</a>
|
||||||
|
</p>
|
||||||
<div class="container">
|
<div class="container">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col">
|
||||||
|
<a href="{{ "admin/settings/redirect-rules/-1" | relative_link }}" class="btn btn-primary btn-sm mb-3"
|
||||||
|
hx-target="#redir_new">
|
||||||
|
Add Redirect Rule
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col">
|
<div class="col">
|
||||||
{%- assign redir_count = redirections | size -%}
|
{%- assign redir_count = redirections | size -%}
|
||||||
@ -13,18 +23,18 @@
|
|||||||
<div class="col">RegEx?</div>
|
<div class="col">RegEx?</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<form method="post" class="container">
|
|
||||||
<input type="hidden" name="{{ csrf.form_field_name }}" value="{{ csrf.request_token }}">
|
|
||||||
<div class="row mwl-table-detail" id="redir_new"></div>
|
<div class="row mwl-table-detail" id="redir_new"></div>
|
||||||
|
<form method="post" class="container" hx-target="body">
|
||||||
|
<input type="hidden" name="{{ csrf.form_field_name }}" value="{{ csrf.request_token }}">
|
||||||
{% for redir in redirections -%}
|
{% for redir in redirections -%}
|
||||||
{%- assign map_id = mapping_ids | value: map.tag -%}
|
{%- assign redir_id = "redir_" | append: forloop.index0 -%}
|
||||||
<div class="row mwl-table-detail" id="redir_{{ forloop.index0 }}">
|
<div class="row mwl-table-detail" id="{{ redir_id }}">
|
||||||
<div class="col no-wrap">
|
<div class="col no-wrap">
|
||||||
{{ redir.from }}<br>
|
{{ redir.from }}<br>
|
||||||
<small>
|
<small>
|
||||||
{%- assign redir_url = "admin/settings/redirect-rules/" | append: forloop.index0 -%}
|
{%- assign redir_url = "admin/settings/redirect-rules/" | append: forloop.index0 -%}
|
||||||
<a href="{{ redir_url | relative_link }}" hx-target="#tag_{{ forloop.index0 }}"
|
<a href="{{ redir_url | relative_link }}" hx-target="#{{ redir_id }}"
|
||||||
hx-swap="innerHTML show:#redir_{{ forloop.index0 }}:top">
|
hx-swap="innerHTML show:#{{ redir_id }}:top">
|
||||||
Edit
|
Edit
|
||||||
</a>
|
</a>
|
||||||
{% unless forloop.first %}
|
{% unless forloop.first %}
|
||||||
@ -39,7 +49,10 @@
|
|||||||
{% endunless %}
|
{% endunless %}
|
||||||
<span class="text-muted"> • </span>
|
<span class="text-muted"> • </span>
|
||||||
{%- assign del_url = redir_url | append: "/delete" | relative_link -%}
|
{%- assign del_url = redir_url | append: "/delete" | relative_link -%}
|
||||||
<a href="{{ del_url }}" hx-post="{{ del_url }}" class="text-danger">Delete</a>
|
<a href="{{ del_url }}" hx-post="{{ del_url }}" class="text-danger"
|
||||||
|
hx-confirm="Are you sure you want to delete this redirect rule?">
|
||||||
|
Delete
|
||||||
|
</a>
|
||||||
</small>
|
</small>
|
||||||
</div>
|
</div>
|
||||||
<div class="col">{{ redir.to }}</div>
|
<div class="col">{{ redir.to }}</div>
|
||||||
@ -48,7 +61,7 @@
|
|||||||
{%- endfor %}
|
{%- endfor %}
|
||||||
</form>
|
</form>
|
||||||
{%- else -%}
|
{%- else -%}
|
||||||
<div id="tag_new">
|
<div id="redir_new">
|
||||||
<p class="text-muted text-center fst-italic">This web log has no redirect rules defined</p>
|
<p class="text-muted text-center fst-italic">This web log has no redirect rules defined</p>
|
||||||
</div>
|
</div>
|
||||||
{%- endif %}
|
{%- endif %}
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
<p class="text-muted">
|
<p class="text-muted">
|
||||||
Go to: <a href="#users">Users</a> • <a href="#rss-settings">RSS Settings</a> •
|
Go to: <a href="#users">Users</a> • <a href="#rss-settings">RSS Settings</a> •
|
||||||
<a href="#tag-mappings">Tag Mappings</a> •
|
<a href="#tag-mappings">Tag Mappings</a> •
|
||||||
<a href="{{ "admin/redirect-rules" | relative_link }}">Redirect Rules</a>
|
<a href="{{ "admin/settings/redirect-rules" | relative_link }}">Redirect Rules</a>
|
||||||
</p>
|
</p>
|
||||||
<fieldset class="container mb-3">
|
<fieldset class="container mb-3">
|
||||||
<legend>Web Log Settings</legend>
|
<legend>Web Log Settings</legend>
|
||||||
|
Loading…
Reference in New Issue
Block a user