Version 2.1 #41
@ -259,6 +259,9 @@ type IWebLogData =
|
||||
/// Find a web log by its ID
|
||||
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
|
||||
abstract member UpdateRssOptions : WebLog -> Task<unit>
|
||||
|
||||
|
@ -45,11 +45,13 @@ type PostgresWebLogData (log : ILogger) =
|
||||
log.LogTrace "WebLog.findById"
|
||||
Find.byId<WebLog> Table.WebLog (WebLogId.toString webLogId)
|
||||
|
||||
/// Update settings for a web log
|
||||
let updateSettings (webLog : WebLog) =
|
||||
log.LogTrace "WebLog.updateSettings"
|
||||
Update.full Table.WebLog (WebLogId.toString webLog.Id) webLog
|
||||
|
||||
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 |}
|
||||
| None -> ()
|
||||
}
|
||||
/// Update RSS options for a web log
|
||||
let updateRssOptions (webLog : WebLog) = backgroundTask {
|
||||
log.LogTrace "WebLog.updateRssOptions"
|
||||
@ -58,11 +60,17 @@ type PostgresWebLogData (log : ILogger) =
|
||||
| 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
|
||||
member _.Add webLog = add webLog
|
||||
member _.All () = all ()
|
||||
member _.Delete webLogId = delete webLogId
|
||||
member _.FindByHost url = findByHost url
|
||||
member _.FindById webLogId = findById webLogId
|
||||
member _.UpdateSettings webLog = updateSettings webLog
|
||||
member _.UpdateRedirectRules webLog = updateRedirectRules 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
|
||||
let migrateV2ToV2point1 () = backgroundTask {
|
||||
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"
|
||||
do! setDbVersion "v2.1"
|
||||
|
@ -1031,6 +1031,13 @@ type RethinkDbData (conn : Net.IConnection, config : DataConfig, log : ILogger<R
|
||||
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 {
|
||||
withTable Table.WebLog
|
||||
get webLog.Id
|
||||
|
@ -206,6 +206,33 @@ type SQLiteWebLogData (conn : SqliteConnection, ser : JsonSerializer) =
|
||||
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
|
||||
let updateSettings webLog = backgroundTask {
|
||||
use cmd = conn.CreateCommand ()
|
||||
@ -233,29 +260,12 @@ type SQLiteWebLogData (conn : SqliteConnection, ser : JsonSerializer) =
|
||||
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
|
||||
member _.Add webLog = add webLog
|
||||
member _.All () = all ()
|
||||
member _.Delete webLogId = delete webLogId
|
||||
member _.FindByHost url = findByHost url
|
||||
member _.FindById webLogId = findById webLogId
|
||||
member _.UpdateSettings webLog = updateSettings webLog
|
||||
member _.UpdateRedirectRules webLog = updateRedirectRules webLog
|
||||
member _.UpdateRssOptions webLog = updateRssOptions webLog
|
||||
member _.UpdateSettings webLog = updateSettings webLog
|
||||
|
@ -426,12 +426,24 @@ module PostId =
|
||||
type RedirectRule =
|
||||
{ /// The From string or pattern
|
||||
From : string
|
||||
|
||||
/// The To string or pattern
|
||||
To : string
|
||||
|
||||
/// Whether to use regular expressions on this rule
|
||||
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
|
||||
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
|
||||
[<CLIMutable; NoComparison; NoEquality>]
|
||||
type EditRssModel =
|
||||
|
@ -224,15 +224,17 @@ let register () =
|
||||
Template.RegisterTag<UserLinksTag> "user_links"
|
||||
|
||||
[ // Domain types
|
||||
typeof<CustomFeed>; typeof<Episode>; typeof<Episode option>; typeof<MetaItem>; typeof<Page>
|
||||
typeof<RssOptions>; typeof<TagMap>; typeof<UploadDestination>; typeof<WebLog>
|
||||
typeof<CustomFeed>; typeof<Episode>; typeof<Episode option>; typeof<MetaItem>; typeof<Page>
|
||||
typeof<RedirectRule>; typeof<RssOptions>; typeof<TagMap>; typeof<UploadDestination>; typeof<WebLog>
|
||||
// View models
|
||||
typeof<DashboardModel>; typeof<DisplayCategory>; typeof<DisplayCustomFeed>; typeof<DisplayPage>
|
||||
typeof<DisplayRevision>; typeof<DisplayTheme>; typeof<DisplayUpload>; typeof<DisplayUser>
|
||||
typeof<EditCategoryModel>; typeof<EditCustomFeedModel>; typeof<EditMyInfoModel>; typeof<EditPageModel>
|
||||
typeof<EditPostModel>; typeof<EditRssModel>; typeof<EditTagMapModel>; typeof<EditUserModel>
|
||||
typeof<LogOnModel>; typeof<ManagePermalinksModel>; typeof<ManageRevisionsModel>; typeof<PostDisplay>
|
||||
typeof<PostListItem>; typeof<SettingsModel>; typeof<UserMessage>
|
||||
typeof<DashboardModel>; typeof<DisplayCategory>; typeof<DisplayCustomFeed>
|
||||
typeof<DisplayPage>; typeof<DisplayRevision>; typeof<DisplayTheme>
|
||||
typeof<DisplayUpload>; typeof<DisplayUser>; typeof<EditCategoryModel>
|
||||
typeof<EditCustomFeedModel>; typeof<EditMyInfoModel>; typeof<EditPageModel>
|
||||
typeof<EditPostModel>; typeof<EditRedirectRuleModel>; typeof<EditRssModel>
|
||||
typeof<EditTagMapModel>; typeof<EditUserModel>; typeof<LogOnModel>
|
||||
typeof<ManagePermalinksModel>; typeof<ManageRevisionsModel>; typeof<PostDisplay>
|
||||
typeof<PostListItem>; typeof<SettingsModel>; typeof<UserMessage>
|
||||
// Framework types
|
||||
typeof<AntiforgeryTokenSet>; typeof<DateTime option>; typeof<int option>; typeof<KeyValuePair>
|
||||
typeof<MetaItem list>; typeof<string list>; typeof<string option>; typeof<TagMap list>
|
||||
|
@ -132,7 +132,7 @@ module Category =
|
||||
open MyWebLog.Data
|
||||
|
||||
// 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
|
||||
| Ok catListTemplate ->
|
||||
let! hash =
|
||||
@ -146,14 +146,14 @@ module Category =
|
||||
}
|
||||
|
||||
// GET /admin/categories/bare
|
||||
let bare : HttpHandler = requireAccess WebLogAdmin >=> fun next ctx ->
|
||||
let bare : HttpHandler = fun next ctx ->
|
||||
hashForPage "Categories"
|
||||
|> withAntiCsrf ctx
|
||||
|> adminBareView "category-list-body" next ctx
|
||||
|
||||
|
||||
// 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 {
|
||||
match catId with
|
||||
| "new" -> return Some ("Add a New Category", { Category.empty with Id = CategoryId "new" })
|
||||
@ -173,7 +173,7 @@ module Category =
|
||||
}
|
||||
|
||||
// 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! model = ctx.BindFormAsync<EditCategoryModel> ()
|
||||
let category =
|
||||
@ -196,7 +196,7 @@ module Category =
|
||||
}
|
||||
|
||||
// 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
|
||||
match result with
|
||||
| CategoryDeleted
|
||||
@ -217,8 +217,10 @@ module Category =
|
||||
/// ~~~ REDIRECT RULES ~~~
|
||||
module RedirectRules =
|
||||
|
||||
// GET /admin/redirect-rules
|
||||
let all : HttpHandler = requireAccess WebLogAdmin >=> fun next ctx -> task {
|
||||
open Microsoft.AspNetCore.Http
|
||||
|
||||
// GET /admin/settings/redirect-rules
|
||||
let all : HttpHandler = fun next ctx -> task {
|
||||
return!
|
||||
hashForPage "Redirect Rules"
|
||||
|> withAntiCsrf ctx
|
||||
@ -226,6 +228,82 @@ module RedirectRules =
|
||||
|> 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 ~~~
|
||||
module TagMapping =
|
||||
@ -243,7 +321,7 @@ module TagMapping =
|
||||
}
|
||||
|
||||
// GET /admin/settings/tag-mappings
|
||||
let all : HttpHandler = requireAccess WebLogAdmin >=> fun next ctx -> task {
|
||||
let all : HttpHandler = fun next ctx -> task {
|
||||
let! hash =
|
||||
hashForPage ""
|
||||
|> withAntiCsrf ctx
|
||||
@ -252,7 +330,7 @@ module TagMapping =
|
||||
}
|
||||
|
||||
// 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 tagMap =
|
||||
if isNew then someTask { TagMap.empty with Id = TagMapId "new" }
|
||||
@ -268,7 +346,7 @@ module TagMapping =
|
||||
}
|
||||
|
||||
// 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! model = ctx.BindFormAsync<EditTagMapModel> ()
|
||||
let tagMap =
|
||||
@ -283,7 +361,7 @@ module TagMapping =
|
||||
}
|
||||
|
||||
// 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
|
||||
| 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" }
|
||||
@ -460,7 +538,7 @@ module WebLog =
|
||||
open System.IO
|
||||
|
||||
// GET /admin/settings
|
||||
let settings : HttpHandler = requireAccess WebLogAdmin >=> fun next ctx -> task {
|
||||
let settings : HttpHandler = fun next ctx -> task {
|
||||
let data = ctx.Data
|
||||
match! TemplateCache.get adminTheme "user-list-body" data with
|
||||
| Ok userTemplate ->
|
||||
@ -508,7 +586,7 @@ module WebLog =
|
||||
}
|
||||
|
||||
// POST /admin/settings
|
||||
let saveSettings : HttpHandler = requireAccess WebLogAdmin >=> fun next ctx -> task {
|
||||
let saveSettings : HttpHandler = fun next ctx -> task {
|
||||
let data = ctx.Data
|
||||
let! model = ctx.BindFormAsync<SettingsModel> ()
|
||||
match! data.WebLog.FindById ctx.WebLog.Id with
|
||||
|
@ -107,7 +107,7 @@ let router : HttpHandler = choose [
|
||||
subRoute "/admin" (requireUser >=> choose [
|
||||
GET_HEAD >=> choose [
|
||||
route "/administration" >=> Admin.Dashboard.admin
|
||||
subRoute "/categor" (choose [
|
||||
subRoute "/categor" (requireAccess WebLogAdmin >=> choose [
|
||||
route "ies" >=> Admin.Category.all
|
||||
route "ies/bare" >=> Admin.Category.bare
|
||||
routef "y/%s/edit" Admin.Category.edit
|
||||
@ -130,20 +130,21 @@ let router : HttpHandler = choose [
|
||||
routef "/%s/revision/%s/preview" Post.previewRevision
|
||||
routef "/%s/revisions" Post.editRevisions
|
||||
])
|
||||
subRoute "/redirect-rules" (choose [
|
||||
route "" >=> Admin.RedirectRules.all
|
||||
])
|
||||
subRoute "/settings" (choose [
|
||||
route "" >=> Admin.WebLog.settings
|
||||
routef "/rss/%s/edit" Feed.editCustomFeed
|
||||
subRoute "/user" (choose [
|
||||
route "s" >=> User.all
|
||||
routef "/%s/edit" User.edit
|
||||
subRoute "/settings" (requireAccess WebLogAdmin >=> choose [
|
||||
route "" >=> Admin.WebLog.settings
|
||||
routef "/rss/%s/edit" Feed.editCustomFeed
|
||||
subRoute "/redirect-rules" (choose [
|
||||
route "" >=> Admin.RedirectRules.all
|
||||
routef "/%i" Admin.RedirectRules.edit
|
||||
])
|
||||
subRoute "/tag-mapping" (choose [
|
||||
route "s" >=> Admin.TagMapping.all
|
||||
routef "/%s/edit" Admin.TagMapping.edit
|
||||
])
|
||||
subRoute "/user" (choose [
|
||||
route "s" >=> User.all
|
||||
routef "/%s/edit" User.edit
|
||||
])
|
||||
])
|
||||
subRoute "/theme" (choose [
|
||||
route "/list" >=> Admin.Theme.all
|
||||
@ -159,7 +160,7 @@ let router : HttpHandler = choose [
|
||||
routef "/theme/%s/refresh" Admin.Cache.refreshTheme
|
||||
routef "/web-log/%s/refresh" Admin.Cache.refreshWebLog
|
||||
])
|
||||
subRoute "/category" (choose [
|
||||
subRoute "/category" (requireAccess WebLogAdmin >=> choose [
|
||||
route "/save" >=> Admin.Category.save
|
||||
routef "/%s/delete" Admin.Category.delete
|
||||
])
|
||||
@ -180,13 +181,19 @@ let router : HttpHandler = choose [
|
||||
routef "/%s/revision/%s/restore" Post.restoreRevision
|
||||
routef "/%s/revisions/purge" Post.purgeRevisions
|
||||
])
|
||||
subRoute "/settings" (choose [
|
||||
subRoute "/settings" (requireAccess WebLogAdmin >=> choose [
|
||||
route "" >=> Admin.WebLog.saveSettings
|
||||
subRoute "/rss" (choose [
|
||||
route "" >=> Feed.saveSettings
|
||||
route "/save" >=> Feed.saveCustomFeed
|
||||
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 [
|
||||
route "/save" >=> Admin.TagMapping.save
|
||||
routef "/%s/delete" Admin.TagMapping.delete
|
||||
|
@ -95,7 +95,7 @@ open Giraffe.Htmx
|
||||
let private goAway : HttpHandler = RequestErrors.BAD_REQUEST "really?"
|
||||
|
||||
// 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
|
||||
return!
|
||||
hashForPage "User Administration"
|
||||
@ -119,7 +119,7 @@ let private showEdit (model : EditUserModel) : HttpHandler = fun next ctx ->
|
||||
|> adminBareView "user-edit" next ctx
|
||||
|
||||
// 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 userId = WebLogUserId usrId
|
||||
let tryUser =
|
||||
@ -131,7 +131,7 @@ let edit usrId : HttpHandler = requireAccess WebLogAdmin >=> fun next ctx -> tas
|
||||
}
|
||||
|
||||
// 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
|
||||
match! data.WebLogUser.FindById (WebLogUserId userId) ctx.WebLog.Id with
|
||||
| Some user ->
|
||||
|
@ -54,7 +54,7 @@
|
||||
<span class="text-muted"> • </span>
|
||||
{%- 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"
|
||||
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
|
||||
</a>
|
||||
{% 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>
|
||||
<a href="{{ "admin/settings" | relative_link }}">« Back to Settings</a>
|
||||
<p class="mb-3">
|
||||
<a href="{{ "admin/settings" | relative_link }}">« Back to Settings</a>
|
||||
</p>
|
||||
<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="col">
|
||||
{%- assign redir_count = redirections | size -%}
|
||||
@ -13,42 +23,45 @@
|
||||
<div class="col">RegEx?</div>
|
||||
</div>
|
||||
</div>
|
||||
<form method="post" class="container">
|
||||
<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 }}">
|
||||
<div class="row mwl-table-detail" id="redir_new"></div>
|
||||
{% for redir in redirections -%}
|
||||
{%- assign map_id = mapping_ids | value: map.tag -%}
|
||||
<div class="row mwl-table-detail" id="redir_{{ forloop.index0 }}">
|
||||
<div class="col no-wrap">
|
||||
{{ redir.from }}<br>
|
||||
<small>
|
||||
{%- assign redir_url = "admin/settings/redirect-rules/" | append: forloop.index0 -%}
|
||||
<a href="{{ redir_url | relative_link }}" hx-target="#tag_{{ forloop.index0 }}"
|
||||
hx-swap="innerHTML show:#redir_{{ forloop.index0 }}:top">
|
||||
Edit
|
||||
</a>
|
||||
{% unless forloop.first %}
|
||||
<span class="text-muted"> • </span>
|
||||
{%- assign move_up = redir_url | append: "/up" | relative_link -%}
|
||||
<a href="{{ move_up }}" hx-post="{{ move_up }}">Move Up</a>
|
||||
{% endunless %}
|
||||
{% unless forloop.last %}
|
||||
<span class="text-muted"> • </span>
|
||||
{%- assign move_down = redir_url | append: "/down" | relative_link -%}
|
||||
<a href="{{ move_down }}" hx-post="{{ move_down }}">Move Down</a>
|
||||
{% endunless %}
|
||||
{% for redir in redirections -%}
|
||||
{%- assign redir_id = "redir_" | append: forloop.index0 -%}
|
||||
<div class="row mwl-table-detail" id="{{ redir_id }}">
|
||||
<div class="col no-wrap">
|
||||
{{ redir.from }}<br>
|
||||
<small>
|
||||
{%- assign redir_url = "admin/settings/redirect-rules/" | append: forloop.index0 -%}
|
||||
<a href="{{ redir_url | relative_link }}" hx-target="#{{ redir_id }}"
|
||||
hx-swap="innerHTML show:#{{ redir_id }}:top">
|
||||
Edit
|
||||
</a>
|
||||
{% unless forloop.first %}
|
||||
<span class="text-muted"> • </span>
|
||||
{%- assign del_url = redir_url | append: "/delete" | relative_link -%}
|
||||
<a href="{{ del_url }}" hx-post="{{ del_url }}" class="text-danger">Delete</a>
|
||||
</small>
|
||||
</div>
|
||||
<div class="col">{{ redir.to }}</div>
|
||||
<div class="col">{% if redir.is_regex %}Yes{% else %}No{% endif %}</div>
|
||||
{%- assign move_up = redir_url | append: "/up" | relative_link -%}
|
||||
<a href="{{ move_up }}" hx-post="{{ move_up }}">Move Up</a>
|
||||
{% endunless %}
|
||||
{% unless forloop.last %}
|
||||
<span class="text-muted"> • </span>
|
||||
{%- assign move_down = redir_url | append: "/down" | relative_link -%}
|
||||
<a href="{{ move_down }}" hx-post="{{ move_down }}">Move Down</a>
|
||||
{% endunless %}
|
||||
<span class="text-muted"> • </span>
|
||||
{%- assign del_url = redir_url | append: "/delete" | relative_link -%}
|
||||
<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>
|
||||
</div>
|
||||
{%- endfor %}
|
||||
<div class="col">{{ redir.to }}</div>
|
||||
<div class="col">{% if redir.is_regex %}Yes{% else %}No{% endif %}</div>
|
||||
</div>
|
||||
{%- endfor %}
|
||||
</form>
|
||||
{%- 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>
|
||||
</div>
|
||||
{%- endif %}
|
||||
|
@ -3,7 +3,7 @@
|
||||
<p class="text-muted">
|
||||
Go to: <a href="#users">Users</a> • <a href="#rss-settings">RSS Settings</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>
|
||||
<fieldset class="container mb-3">
|
||||
<legend>Web Log Settings</legend>
|
||||
|
Loading…
Reference in New Issue
Block a user