Rule add/edit/move/delete works (#39)

- Begin moving auth to route definition where practical
- Fix typo on post list page
This commit is contained in:
Daniel J. Summers 2023-07-30 21:00:31 -04:00
parent 3ef4499a90
commit dc6b066e79
15 changed files with 322 additions and 97 deletions

View File

@ -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>

View File

@ -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

View File

@ -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"

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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 =

View File

@ -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>

View File

@ -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

View File

@ -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

View File

@ -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 ->

View File

@ -54,7 +54,7 @@
<span class="text-muted"> &bull; </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 &ldquo;{{ post.title | strip_html | escape }}&rdquo;? This action cannot be undone.">
hx-confirm="Are you sure you want to delete the post &ldquo;{{ post.title | strip_html | escape }}&rdquo;? This action cannot be undone.">
Delete
</a>
{% endif %}

View 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>

View File

@ -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 }}">&laquo; Back to Settings</a>
<p class="mb-3">
<a href="{{ "admin/settings" | relative_link }}">&laquo; 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"> &bull; </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"> &bull; </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"> &bull; </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"> &bull; </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"> &bull; </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 %}

View File

@ -3,7 +3,7 @@
<p class="text-muted">
Go to: <a href="#users">Users</a> &bull; <a href="#rss-settings">RSS Settings</a> &bull;
<a href="#tag-mappings">Tag Mappings</a> &bull;
<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>