Version 8 #43

Merged
danieljsummers merged 37 commits from version-8 into main 2022-08-19 19:08:31 +00:00
17 changed files with 159 additions and 79 deletions
Showing only changes of commit 1b48acd66a - Show all commits

View File

@ -11,7 +11,6 @@
<ItemGroup>
<PackageReference Include="Giraffe" Version="6.0.0" />
<PackageReference Include="Microsoft.FSharpLu" Version="0.11.7" />
<PackageReference Include="NodaTime" Version="3.1.1" />
<PackageReference Update="FSharp.Core" Version="6.0.5" />
<PackageReference Include="Npgsql.FSharp" Version="5.3.0" />

View File

@ -114,11 +114,11 @@ let listPreferencesTests =
Expect.equal mt.DaysToKeepNew 7 "The default days to keep new should have been 7"
Expect.equal mt.LongTermUpdateWeeks 4 "The default long term update weeks should have been 4"
Expect.equal mt.EmailFromName "PrayerTracker" "The default e-mail from name should have been PrayerTracker"
Expect.equal mt.EmailFromAddress "prayer@djs-consulting.com"
"The default e-mail from address should have been prayer@djs-consulting.com"
Expect.equal mt.EmailFromAddress "prayer@bitbadger.solutions"
"The default e-mail from address should have been prayer@bitbadger.solutions"
Expect.equal mt.Fonts "native" "The default list fonts were incorrect"
Expect.equal mt.HeadingColor "maroon" "The default heading text color should have been maroon"
Expect.equal mt.LineColor "navy" "The default heding line color should have been navy"
Expect.equal mt.LineColor "navy" "The default heading line color should have been navy"
Expect.equal mt.HeadingFontSize 16 "The default heading font size should have been 16"
Expect.equal mt.TextFontSize 12 "The default text font size should have been 12"
Expect.equal mt.RequestSort SortByDate "The default request sort should have been by date"

View File

@ -16,7 +16,7 @@
<ItemGroup>
<PackageReference Include="Expecto" Version="9.0.4" />
<PackageReference Include="NodaTime.Testing" Version="3.1.0" />
<PackageReference Include="NodaTime.Testing" Version="3.1.1" />
<PackageReference Update="FSharp.Core" Version="6.0.5" />
</ItemGroup>

View File

@ -261,7 +261,7 @@ let editMemberTests =
[<Tests>]
let editPreferencesTests =
testList "EditPreferences" [
test "fromPreferences succeeds for named colors and private list" {
test "fromPreferences succeeds for native fonts, named colors, and private list" {
let prefs = ListPreferences.empty
let edit = EditPreferences.fromPreferences prefs
Expect.equal edit.ExpireDays prefs.DaysToExpire "The expiration days were not filled correctly"
@ -278,7 +278,8 @@ let editPreferencesTests =
Expect.equal edit.LineColor prefs.LineColor "The heading line color was not filled correctly"
Expect.equal edit.HeadingColorType "Name" "The heading text color type was not derived correctly"
Expect.equal edit.HeadingColor prefs.HeadingColor "The heading text color was not filled correctly"
Expect.equal edit.Fonts prefs.Fonts "The list fonts were not filled correctly"
Expect.isTrue edit.IsNative "The IsNative flag should have been true (default value)"
Expect.isNone edit.Fonts "The list fonts should not exist for native font stack"
Expect.equal edit.HeadingFontSize prefs.HeadingFontSize "The heading font size was not filled correctly"
Expect.equal edit.ListFontSize prefs.TextFontSize "The list text font size was not filled correctly"
Expect.equal edit.TimeZone (TimeZoneId.toString prefs.TimeZoneId) "The time zone was not filled correctly"
@ -310,6 +311,13 @@ let editPreferencesTests =
Expect.equal edit.Visibility GroupVisibility.PublicList
"The list visibility was not derived correctly"
}
test "fromPreferences succeeds for non-native fonts" {
let prefs = { ListPreferences.empty with Fonts = "Arial,sans-serif" }
let edit = EditPreferences.fromPreferences prefs
Expect.isFalse edit.IsNative "The IsNative flag should have been false"
Expect.isSome edit.Fonts "The fonts should have been filled for non-native fonts"
Expect.equal edit.Fonts.Value prefs.Fonts "The fonts were not filled correctly"
}
]
[<Tests>]
@ -529,11 +537,12 @@ let requestListTests =
"AsHtml succeeds without header or as-of date",
fun reqList ->
let htmlList = { reqList with SmallGroup = { reqList.SmallGroup with Name = "Test HTML Group" } }
let html = htmlList.AsHtml _s
let html = htmlList.AsHtml _s
let fonts = reqList.SmallGroup.Preferences.FontStack.Replace ("\"", "&quot;")
Expect.equal -1 (html.IndexOf "Test HTML Group")
"The small group name should not have existed (no header)"
let curReqHeading =
[ """<table style="font-family:Century Gothic,Tahoma,Luxi Sans,sans-serif;page-break-inside:avoid;">"""
[ $"""<table style="font-family:{fonts};page-break-inside:avoid;">"""
"<tr>"
"""<td style="font-size:16pt;color:maroon;padding:3px 0;border-top:solid 3px navy;border-bottom:solid 3px navy;font-weight:bold;">"""
"&nbsp; &nbsp; Current Requests&nbsp; &nbsp; </td></tr></table>"
@ -541,16 +550,16 @@ let requestListTests =
|> String.concat ""
Expect.stringContains html curReqHeading """Heading for category "Current Requests" not found"""
let curReqHtml =
[ "<ul>"
"""<li style="list-style-type:circle;font-family:Century Gothic,Tahoma,Luxi Sans,sans-serif;font-size:12pt;padding-bottom:.25em;">"""
[ $"""<ul style="font-family:{fonts};font-size:12pt">"""
"""<li style="list-style-type:circle;padding-bottom:.25em;">"""
"<strong>Zeb</strong> &ndash; zyx</li>"
"""<li style="list-style-type:disc;font-family:Century Gothic,Tahoma,Luxi Sans,sans-serif;font-size:12pt;padding-bottom:.25em;">"""
"""<li style="list-style-type:disc;padding-bottom:.25em;">"""
"<strong>Aaron</strong> &ndash; abc</li></ul>"
]
|> String.concat ""
Expect.stringContains html curReqHtml """Expected HTML for "Current Requests" requests not found"""
let praiseHeading =
[ """<table style="font-family:Century Gothic,Tahoma,Luxi Sans,sans-serif;page-break-inside:avoid;">"""
[ $"""<table style="font-family:{fonts};page-break-inside:avoid;">"""
"<tr>"
"""<td style="font-size:16pt;color:maroon;padding:3px 0;border-top:solid 3px navy;border-bottom:solid 3px navy;font-weight:bold;">"""
"&nbsp; &nbsp; Praise Reports&nbsp; &nbsp; </td></tr></table>"
@ -558,8 +567,8 @@ let requestListTests =
|> String.concat ""
Expect.stringContains html praiseHeading """Heading for category "Praise Reports" not found"""
let praiseHtml =
[ "<ul>"
"""<li style="list-style-type:circle;font-family:Century Gothic,Tahoma,Luxi Sans,sans-serif;font-size:12pt;padding-bottom:.25em;">"""
[ $"""<ul style="font-family:{fonts};font-size:12pt">"""
"""<li style="list-style-type:circle;padding-bottom:.25em;">"""
"nmo</li></ul>"
]
|> String.concat ""
@ -571,9 +580,10 @@ let requestListTests =
SmallGroup = { reqList.SmallGroup with Name = "Test HTML Group" }
ShowHeader = true
}
let html = htmlList.AsHtml _s
let html = htmlList.AsHtml _s
let fonts = reqList.SmallGroup.Preferences.FontStack.Replace ("\"", "&quot;")
let lstHeading =
[ """<div style="text-align:center;font-family:Century Gothic,Tahoma,Luxi Sans,sans-serif">"""
[ $"""<div style="text-align:center;font-family:{fonts}">"""
"""<span style="font-size:16pt;"><strong>Prayer Requests</strong></span><br>"""
"""<span style="font-size:12pt;"><strong>Test HTML Group</strong><br>"""
htmlList.Date.ToString ("MMMM d, yyyy", null)

View File

@ -41,12 +41,14 @@ let edit (model : EditChurch) ctx viewInfo =
div [ _checkboxField ] [
inputField "checkbox" (nameof model.HasInterface) "True"
[ if defaultArg model.HasInterface false then _checked ]
label [ _for (nameof model.HasInterface) ] [ locStr s["Has an interface with Virtual Prayer Room"] ]
label [ _for (nameof model.HasInterface) ] [
locStr s["Has an Interface with “{0}”", "Virtual Prayer Space"]
]
]
]
div [ _fieldRowWith [ "pt-fadeable" ]; _id "divInterfaceAddress" ] [
div [ _inputField ] [
label [ _for (nameof model.InterfaceAddress) ] [ locStr s["VPR Interface URL"] ]
label [ _for (nameof model.InterfaceAddress) ] [ locStr s["Interface URL"] ]
inputField "url" (nameof model.InterfaceAddress) (defaultArg model.InterfaceAddress "") []
]
]

View File

@ -302,11 +302,12 @@ let private contentSection viewInfo pgTitle (content : XmlNode) = [
htmlFooter viewInfo
match viewInfo.OnLoadScript with
| Some onLoad ->
let doCall = if onLoad.EndsWith ")" then "" else "()"
script [] [
rawText $"""
window.doOnLoad = () => {{
if (window.PT) {{
{onLoad}()
{onLoad}{doCall}
delete window.doOnLoad
}} else {{ setTimeout(window.doOnLoad, 500) }}
}}

View File

@ -54,15 +54,10 @@ let edit (model : EditRequest) today ctx viewInfo =
div [ _fieldRow ] [
div [ _inputField ] [
label [] [ locStr s["Expiration"] ]
ReferenceList.expirationList s (not model.IsNew)
|> List.map (fun exp ->
let radioId = String.concat "_" [ nameof model.Expiration; fst exp ]
span [ _class "text-nowrap" ] [
radio (nameof model.Expiration) radioId (fst exp) model.Expiration
label [ _for radioId ] [ space; locStr (snd exp) ]
rawText " &nbsp; &nbsp; "
])
|> div [ _class "pt-center-text" ]
span [ _class "pt-radio-group" ] [
for code, name in ReferenceList.expirationList s (not model.IsNew) do
label [] [ radio (nameof model.Expiration) "" code model.Expiration; locStr name ]
]
]
]
div [ _fieldRow ] [

View File

@ -396,8 +396,8 @@
<data name="The group member “{0}” was deleted successfully" xml:space="preserve">
<value>El miembro del grupo “{0}” se eliminó con éxito</value>
</data>
<data name="The group {0} and its {1} prayer request(s) was deleted successfully (revoked access from {2} user(s))" xml:space="preserve">
<value>El grupo {0} y sus {1} peticion(es) de oración se ha eliminado correctamente (acceso revocada por {2} usuario(s))</value>
<data name="The group “{0}” and its {1} prayer request(s) were deleted successfully; revoked access from {2} user(s)" xml:space="preserve">
<value>El grupo {0} y sus {1} peticion(es) de oración se ha eliminado correctamente; acceso revocada por {2} usuario(s)</value>
</data>
<data name="The old password was incorrect - your password was NOT changed" xml:space="preserve">
<value>La contraseña antigua es incorrecta - la contraseña NO ha cambiado</value>
@ -742,7 +742,7 @@
<value>Este</value>
</data>
<data name="MMMM d, yyyy" xml:space="preserve">
<value>d \de MMMM yyyy</value>
<value>d \d\e MMMM yyyy</value>
</data>
<data name="Mountain" xml:space="preserve">
<value>Montaña</value>
@ -831,4 +831,55 @@
<data name="Administrators" xml:space="preserve">
<value>Administradores</value>
</data>
<data name="Native Fonts" xml:space="preserve">
<value>Fuentes Nativas</value>
</data>
<data name="Named Fonts" xml:space="preserve">
<value>Fuentes con Nombre</value>
</data>
<data name="Select Church" xml:space="preserve">
<value>Seleccione una Iglesia</value>
</data>
<data name="Select Group" xml:space="preserve">
<value>Seleccione un Grupo</value>
</data>
<data name="Member Name" xml:space="preserve">
<value>Nombre de Miembro</value>
</data>
<data name="Custom Color" xml:space="preserve">
<value>Color Personalizado</value>
</data>
<data name="Church Name" xml:space="preserve">
<value>Nombre de la Iglesia</value>
</data>
<data name="City" xml:space="preserve">
<value>Ciudad</value>
</data>
<data name="Has an Interface with “{0}”" xml:space="preserve">
<value>Tiene una Interfaz con “{0}”</value>
</data>
<data name="Interface URL" xml:space="preserve">
<value>URL de la Interfaz</value>
</data>
<data name="Successfully {0} church “{1}”" xml:space="preserve">
<value>Iglesia “{1}” {0} con éxito</value>
</data>
<data name="The church “{0}” and its {1} small group(s) (with {2} prayer request(s)) were deleted successfully; revoked access from {3} user(s)" xml:space="preserve">
<value>La iglesia "{0}" y sus {1} grupo(s) (con {2} peticion(es) de oración) se eliminaron correctamente; acceso revocado de {3} usuario(s)</value>
</data>
<data name="Successfully {0} group “{1}”" xml:space="preserve">
<value>El grupo “{1}” {0} con éxito</value>
</data>
<data name="First Name" xml:space="preserve">
<value>Primer Nombre</value>
</data>
<data name="Last Name" xml:space="preserve">
<value>Apellido</value>
</data>
<data name="Password Again" xml:space="preserve">
<value>Contraseña otra Vez</value>
</data>
<data name="This User Is a {0} Administrator" xml:space="preserve">
<value>Este Usuario Es un Administrador de {0}</value>
</data>
</root>

View File

@ -117,16 +117,16 @@
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="Ending with either “serif” or “sans-serif” will cause the user's browser to use the default “serif” font (“Times New Roman” on Windows) or “sans-serif” font (“Arial” on Windows) if no other fonts in the list are found." xml:space="preserve">
<value>Con la extensión “serif” o el “sans-serif” hará que el navegador del usuario para utilizar el valor predeterminado “serif” de la fuente (“Times New Roman” en Windows) o “sans-serif” de la fuente (“Arial” en Windows) si no otras fuentes en la lista se encuentran.</value>
<data name="Ending this list with either “serif” or “sans-serif” will cause the user's browser to use the default “serif” font (“Times New Roman” on Windows) or “sans-serif” font (“Arial” on Windows) if no other fonts in the list are found." xml:space="preserve">
<value>Terminar esta lista con “serif” o “sans-serif” hará que el navegador del usuario utilice la fuente predeterminado “serif” (“Times New Roman” en Windows) o “sans-serif” (“Arial” en Windows) si no se encuentran otras fuentes en la lista.</value>
</data>
<data name="If you want a custom color, you may be able to get some ideas (and a list of RGB values for those colors) from the W3 School's &lt;a href=&quot;http://www.w3schools.com/html/html_colornames.asp&quot; title=&quot;HTML Color List - W3 School&quot;&gt;HTML color name list&lt;/a&gt;." xml:space="preserve">
<value>Si desea un color personalizado, que puede ser capaz de obtener algunas ideas (y una lista de valores RGB para los colores) de la lista de la Escuela de W3 de &lt;a href="http://www.w3schools.com/html/html_colornames.asp" title="La Lista de Nombres de Colores HTML - La Escuela de W3"&gt;nombres de colores HTML&lt;/a&gt;.</value>
</data>
<data name="List font names, separated by commas." xml:space="preserve">
<value>Lista de nombres de fuentes separados por comas.</value>
<data name="Native fonts match the default font based on the user's device (computer, phone, tablet, etc.)." xml:space="preserve">
<value>Las fuentes nativas coinciden con la fuente predeterminada según el dispositivo del usuario (computadora, teléfono, tableta, etc.).</value>
</data>
<data name="The first font that is matched is the one that is used." xml:space="preserve">
<value>La primera fuente que se corresponde es el que se utiliza.</value>
<data name="Named fonts should be separated by commas, and will be displayed using the first one the user has in the list." xml:space="preserve">
<value>Las fuentes con nombre deben estar separadas por comas y se mostrarán usando la primera que el usuario tenga en la lista.</value>
</data>
</root>

View File

@ -30,13 +30,15 @@ let announcement isAdmin ctx viewInfo =
div [ _fieldRow ] [
div [ _inputField ] [
label [] [ locStr s["Send Announcement to"]; rawText ":" ]
div [ _class "pt-center-text" ] [
radio (nameof model.SendToClass) $"{nameof model.SendToClass}_Y" "Y" "Y"
label [ _for $"{nameof model.SendToClass}_Y" ] [
locStr s["This Group"]; rawText " &nbsp; &nbsp; "
div [ _class "pt-radio-group" ] [
label [] [
radio (nameof model.SendToClass) $"{nameof model.SendToClass}_Y" "Y" "Y"
locStr s["This Group"]
]
label [] [
radio (nameof model.SendToClass) $"{nameof model.SendToClass}_N" "N" "Y"
locStr s["All {0} Users", s["PrayerTracker"]]
]
radio (nameof model.SendToClass) $"{nameof model.SendToClass}_N" "N" "Y"
label [ _for $"{nameof model.SendToClass}_N" ] [ locStr s["All {0} Users", s["PrayerTracker"]] ]
]
]
]
@ -519,7 +521,21 @@ let preferences (model : EditPreferences) ctx viewInfo =
legend [] [ strong [] [ icon "font_download"; rawText " &nbsp;"; locStr s["Fonts"] ] ]
div [ _inputField ] [
label [ _for (nameof model.Fonts) ] [ locStr s["Fonts** for List"] ]
inputField "text" (nameof model.Fonts) model.Fonts [ _required ]
let value = if model.IsNative then "True" else "False"
span [ _class "pt-radio-group" ] [
label [] [
radio (nameof model.IsNative) $"{nameof model.IsNative}_Y" "True" value
locStr s["Native Fonts"]
]
inputField "text" "nativeFontSpacer" "" [ _style "visibility:hidden" ]
]
span [ _class "pt-radio-group" ] [
label [] [
radio (nameof model.IsNative) $"{nameof model.IsNative}_N" "False" value
locStr s["Named Fonts"]
]
inputField "text" (nameof model.Fonts) (defaultArg model.Fonts "") [ _required ]
]
]
div [ _fieldRow ] [
div [ _inputField ] [
@ -595,11 +611,11 @@ let preferences (model : EditPreferences) ctx viewInfo =
]
p [] [
rawText "** "
raw l["List font names, separated by commas."]
raw l["Native fonts match the default font based on the user's device (computer, phone, tablet, etc.)."]
space
raw l["The first font that is matched is the one that is used."]
raw l["Named fonts should be separated by commas, and will be displayed using the first one the user has in the list."]
space
raw l["Ending with either “serif” or “sans-serif” will cause the user's browser to use the default “serif” font (“Times New Roman” on Windows) or “sans-serif” font (“Arial” on Windows) if no other fonts in the list are found."]
raw l["Ending this list with either “serif” or “sans-serif” will cause the user's browser to use the default “serif” font (“Times New Roman” on Windows) or “sans-serif” font (“Arial” on Windows) if no other fonts in the list are found."]
]
p [] [
rawText "*** "

View File

@ -133,7 +133,7 @@ let edit (model : EditUser) ctx viewInfo =
]
div [ _checkboxField ] [
inputField "checkbox" (nameof model.IsAdmin) "True" [ if defaultArg model.IsAdmin false then _checked ]
label [ _for (nameof model.IsAdmin) ] [ locStr s["This user is a PrayerTracker administrator"] ]
label [ _for (nameof model.IsAdmin) ] [ locStr s["This User Is a {0} Administrator", s["PrayerTracker"]] ]
]
div [ _fieldRow ] [ submit [] "save" s["Save User"] ]
]

View File

@ -374,8 +374,11 @@ type EditPreferences =
/// The named color for the heading text
HeadingColor : string
/// Whether the class uses the native font stack
IsNative : bool
/// The fonts to use for the list
Fonts : string
Fonts : string option
/// The font size for the heading text
HeadingFontSize : int
@ -416,7 +419,7 @@ with
DefaultEmailType = EmailFormat.fromCode this.DefaultEmailType
LineColor = this.LineColor
HeadingColor = this.HeadingColor
Fonts = this.Fonts
Fonts = if this.IsNative || Option.isNone this.Fonts then "native" else this.Fonts.Value
HeadingFontSize = this.HeadingFontSize
TextFontSize = this.ListFontSize
TimeZoneId = TimeZoneId this.TimeZone
@ -442,7 +445,8 @@ module EditPreferences =
LineColor = prefs.LineColor
HeadingColorType = setType prefs.HeadingColor
HeadingColor = prefs.HeadingColor
Fonts = prefs.Fonts
IsNative = (prefs.Fonts = "native")
Fonts = if prefs.Fonts = "native" then None else Some prefs.Fonts
HeadingFontSize = prefs.HeadingFontSize
ListFontSize = prefs.TextFontSize
TimeZone = TimeZoneId.toString prefs.TimeZoneId
@ -804,7 +808,7 @@ with
reqs
|> List.map (fun req ->
let bullet = if this.IsNew req then "circle" else "disc"
li [ _style $"list-style-type:{bullet};font-family:{p.FontStack};font-size:%i{p.TextFontSize}pt;padding-bottom:.25em;" ] [
li [ _style $"list-style-type:{bullet};padding-bottom:.25em;" ] [
match req.Requestor with
| Some r when r <> "" ->
strong [] [ str r ]
@ -825,7 +829,7 @@ with
rawText "&nbsp; ("; str s["as of"].Value; str " "; str dt; rawText ")"
]
])
|> ul []
|> ul [ _style $"font-family:{p.FontStack};font-size:%i{p.TextFontSize}pt" ]
br []
]
|> RenderView.AsString.htmlNodes

View File

@ -24,7 +24,7 @@ let delete chId : HttpHandler = requireAccess [ Admin ] >=> validateCsrf >=> fun
let! _, stats = findStats churchId conn
do! Churches.deleteById churchId conn
addInfo ctx
ctx.Strings["The church {0} and its {1} small groups (with {2} prayer request(s)) were deleted successfully; revoked access from {3} user(s)",
ctx.Strings["The church {0} and its {1} small group(s) (with {2} prayer request(s)) were deleted successfully; revoked access from {3} user(s)",
church.Name, stats.SmallGroups, stats.PrayerRequests, stats.Users]
return! redirectTo false "/churches" next ctx
| None -> return! fourOhFour ctx

View File

@ -2,7 +2,6 @@
module PrayerTracker.Extensions
open Microsoft.AspNetCore.Http
open Microsoft.FSharpLu
open Newtonsoft.Json
open NodaTime
open NodaTime.Serialization.JsonNet
@ -17,18 +16,18 @@ let private jsonSettings = JsonSerializerSettings().ConfigureForNodaTime DateTim
type ISession with
/// Set an object in the session
member this.SetObject key value =
member this.SetObject<'T> key (value : 'T) =
this.SetString (key, JsonConvert.SerializeObject (value, jsonSettings))
/// Get an object from the session
member this.GetObject<'T> key =
member this.TryGetObject<'T> key =
match this.GetString key with
| null -> Unchecked.defaultof<'T>
| v -> JsonConvert.DeserializeObject<'T> (v, jsonSettings)
| null -> None
| v -> Some (JsonConvert.DeserializeObject<'T> (v, jsonSettings))
/// The currently logged on small group
member this.CurrentGroup
with get () = this.GetObject<SmallGroup> Key.Session.currentGroup |> Option.fromObject
with get () = this.TryGetObject<SmallGroup> Key.Session.currentGroup
and set (v : SmallGroup option) =
match v with
| Some group -> this.SetObject Key.Session.currentGroup group
@ -36,7 +35,7 @@ type ISession with
/// The currently logged on user
member this.CurrentUser
with get () = this.GetObject<User> Key.Session.currentUser |> Option.fromObject
with get () = this.TryGetObject<User> Key.Session.currentUser
and set (v : User option) =
match v with
| Some user -> this.SetObject Key.Session.currentUser { user with PasswordHash = "" }
@ -45,9 +44,8 @@ type ISession with
/// Current messages for the session
member this.Messages
with get () =
match box (this.GetObject<UserMessage list> Key.Session.userMessages) with
| null -> List.empty<UserMessage>
| msgs -> unbox msgs
this.TryGetObject<UserMessage list> Key.Session.userMessages
|> Option.defaultValue List.empty<UserMessage>
and set (v : UserMessage list) = this.SetObject Key.Session.userMessages v
@ -69,28 +67,18 @@ type ClaimsPrincipal with
else None
open System.Threading.Tasks
open Giraffe
open Microsoft.Extensions.Configuration
open Npgsql
/// Extensions on the ASP.NET Core HTTP context
type HttpContext with
// TODO: is this disposed?
member private this.LazyConn : Lazy<Task<NpgsqlConnection>> = lazy (backgroundTask {
let cfg = this.GetService<IConfiguration> ()
let conn = new NpgsqlConnection (cfg.GetConnectionString "PrayerTracker")
do! conn.OpenAsync ()
return conn
})
/// The system clock (via DI)
member this.Clock = this.GetService<IClock> ()
/// The PostgreSQL connection (configured via DI)
member this.Conn = this.GetService<NpgsqlConnection> ()
/// The system clock (via DI)
member this.Clock = this.GetService<IClock> ()
/// The current instant
member this.Now = this.Clock.GetCurrentInstant ()

View File

@ -23,7 +23,7 @@ let delete grpId : HttpHandler = requireAccess [ Admin ] >=> validateCsrf >=> fu
let! users = Users.countByGroup groupId conn
do! SmallGroups.deleteById groupId conn
addInfo ctx
ctx.Strings["The group {0} and its {1} prayer request(s) were deleted successfully; revoked access from {2} user(s)",
ctx.Strings["The group {0} and its {1} prayer request(s) were deleted successfully; revoked access from {2} user(s)",
grp.Name, reqs, users]
return! redirectTo false "/small-groups" next ctx
| None -> return! fourOhFour ctx

View File

@ -257,7 +257,8 @@ footer a:hover {
.pt-radio-group {
display: flex;
flex-flow: row;
gap: 2rem;
gap: 1.5rem;
align-items: baseline;
}
.pt-center-text {
text-align: center;

View File

@ -243,6 +243,13 @@ this.PT = {
}
},
/**
* Enable or disable the font list based on whether the native font stack is selected or not
*/
checkFonts() {
document.getElementById("Fonts").disabled = document.getElementById("IsNative_Y").checked
},
/**
* Bind the event handlers
*/
@ -261,6 +268,12 @@ this.PT = {
})
PT.smallGroup.preferences.toggleType(name)
})
;["Y", "N"].map(name => {
document.getElementById(`IsNative_${name}`).addEventListener("click", () => {
PT.smallGroup.preferences.checkFonts()
})
})
PT.smallGroup.preferences.checkFonts()
},
},
},