Version 8 #43
|
@ -8,53 +8,53 @@ open PrayerTracker.ViewModels
|
||||||
let edit (m : EditChurch) ctx vi =
|
let edit (m : EditChurch) ctx vi =
|
||||||
let pageTitle = if m.IsNew then "Add a New Church" else "Edit Church"
|
let pageTitle = if m.IsNew then "Add a New Church" else "Edit Church"
|
||||||
let s = I18N.localizer.Force ()
|
let s = I18N.localizer.Force ()
|
||||||
[ form [ _action "/church/save"; _method "post"; _class "pt-center-columns" ] [
|
[ form [ _action "/church/save"; _method "post"; _class "pt-center-columns"; Target.content ] [
|
||||||
style [ _scoped ] [
|
style [ _scoped ] [
|
||||||
rawText "#name { width: 20rem; } #city { width: 10rem; } #st { width: 3rem; } #interfaceAddress { width: 30rem; }"
|
rawText "#name { width: 20rem; } #city { width: 10rem; } #st { width: 3rem; } #interfaceAddress { width: 30rem; }"
|
||||||
]
|
|
||||||
csrfToken ctx
|
|
||||||
input [ _type "hidden"; _name (nameof m.ChurchId); _value (flatGuid m.ChurchId) ]
|
|
||||||
div [ _class "pt-field-row" ] [
|
|
||||||
div [ _class "pt-field" ] [
|
|
||||||
label [ _for "name" ] [ locStr s["Church Name"] ]
|
|
||||||
input [ _type "text"; _name (nameof m.Name); _id "name"; _required; _autofocus; _value m.Name ]
|
|
||||||
]
|
]
|
||||||
div [ _class "pt-field" ] [
|
csrfToken ctx
|
||||||
label [ _for "City"] [ locStr s["City"] ]
|
input [ _type "hidden"; _name (nameof m.ChurchId); _value (flatGuid m.ChurchId) ]
|
||||||
input [ _type "text"; _name (nameof m.City); _id "city"; _required; _value m.City ]
|
div [ _class "pt-field-row" ] [
|
||||||
|
div [ _class "pt-field" ] [
|
||||||
|
label [ _for "name" ] [ locStr s["Church Name"] ]
|
||||||
|
input [ _type "text"; _name (nameof m.Name); _id "name"; _required; _autofocus; _value m.Name ]
|
||||||
|
]
|
||||||
|
div [ _class "pt-field" ] [
|
||||||
|
label [ _for "City"] [ locStr s["City"] ]
|
||||||
|
input [ _type "text"; _name (nameof m.City); _id "city"; _required; _value m.City ]
|
||||||
|
]
|
||||||
|
div [ _class "pt-field" ] [
|
||||||
|
label [ _for "state" ] [ locStr s["State or Province"] ]
|
||||||
|
input [ _type "text"
|
||||||
|
_name (nameof m.State)
|
||||||
|
_id "state"
|
||||||
|
_minlength "2"; _maxlength "2"
|
||||||
|
_value m.State
|
||||||
|
_required ]
|
||||||
|
]
|
||||||
]
|
]
|
||||||
div [ _class "pt-field" ] [
|
div [ _class "pt-field-row" ] [
|
||||||
label [ _for "state" ] [ locStr s["State or Province"] ]
|
div [ _class "pt-checkbox-field" ] [
|
||||||
input [ _type "text"
|
input [ _type "checkbox"
|
||||||
_name (nameof m.State)
|
_name (nameof m.HasInterface)
|
||||||
_id "state"
|
_id "hasInterface"
|
||||||
_required
|
_value "True"
|
||||||
_minlength "2"; _maxlength "2"
|
if defaultArg m.HasInterface false then _checked ]
|
||||||
_value m.State ]
|
label [ _for "hasInterface" ] [ locStr s["Has an interface with Virtual Prayer Room"] ]
|
||||||
|
]
|
||||||
]
|
]
|
||||||
]
|
div [ _class "pt-field-row pt-fadeable"; _id "divInterfaceAddress" ] [
|
||||||
div [ _class "pt-field-row" ] [
|
div [ _class "pt-field" ] [
|
||||||
div [ _class "pt-checkbox-field" ] [
|
label [ _for "interfaceAddress" ] [ locStr s["VPR Interface URL"] ]
|
||||||
input [ _type "checkbox"
|
input [ _type "url"
|
||||||
_name (nameof m.HasInterface)
|
_name (nameof m.InterfaceAddress)
|
||||||
_id "hasInterface"
|
_id "interfaceAddress";
|
||||||
_value "True"
|
_value (defaultArg m.InterfaceAddress "") ]
|
||||||
if defaultArg m.HasInterface false then _checked ]
|
]
|
||||||
label [ _for "hasInterface" ] [ locStr s["Has an interface with Virtual Prayer Room"] ]
|
|
||||||
]
|
]
|
||||||
|
div [ _class "pt-field-row" ] [ submit [] "save" s["Save Church"] ]
|
||||||
]
|
]
|
||||||
div [ _class "pt-field-row pt-fadeable"; _id "divInterfaceAddress" ] [
|
script [] [ rawText "PT.onLoad(PT.church.edit.onPageLoad)" ]
|
||||||
div [ _class "pt-field" ] [
|
|
||||||
label [ _for "interfaceAddress" ] [ locStr s["VPR Interface URL"] ]
|
|
||||||
input [ _type "url"
|
|
||||||
_name (nameof m.InterfaceAddress)
|
|
||||||
_id "interfaceAddress";
|
|
||||||
_value (defaultArg m.InterfaceAddress "") ]
|
|
||||||
]
|
|
||||||
]
|
|
||||||
div [ _class "pt-field-row" ] [ submit [] "save" s["Save Church"] ]
|
|
||||||
]
|
|
||||||
script [] [ rawText "PT.onLoad(PT.church.edit.onPageLoad)" ]
|
|
||||||
]
|
]
|
||||||
|> Layout.Content.standard
|
|> Layout.Content.standard
|
||||||
|> Layout.standard vi pageTitle
|
|> Layout.standard vi pageTitle
|
||||||
|
@ -87,12 +87,13 @@ let maintain (churches : Church list) (stats : Map<string, ChurchStats>) ctx vi
|
||||||
$"""{s["Church"].Value.ToLower ()} ({ch.name})"""]
|
$"""{s["Church"].Value.ToLower ()} ({ch.name})"""]
|
||||||
tr [] [
|
tr [] [
|
||||||
td [] [
|
td [] [
|
||||||
a [ _href $"/church/{chId}/edit"; _title s["Edit This Church"].Value ] [ icon "edit" ]
|
a [ _href $"/church/{chId}/edit"; _title s["Edit This Church"].Value ] [ icon "edit" ]
|
||||||
a [ _href delAction
|
a [ _href delAction
|
||||||
_title s["Delete This Church"].Value
|
_title s["Delete This Church"].Value
|
||||||
_onclick $"return PT.confirmDelete('{delAction}','{delPrompt}')" ]
|
_onclick $"return PT.confirmDelete('{delAction}','{delPrompt}')" ] [
|
||||||
[ icon "delete_forever" ]
|
icon "delete_forever"
|
||||||
]
|
]
|
||||||
|
]
|
||||||
td [] [ str ch.name ]
|
td [] [ str ch.name ]
|
||||||
td [] [ str ch.city; rawText ", "; str ch.st ]
|
td [] [ str ch.city; rawText ", "; str ch.st ]
|
||||||
td [ _class "pt-right-text" ] [ rawText (stats[chId].smallGroups.ToString "N0") ]
|
td [ _class "pt-right-text" ] [ rawText (stats[chId].smallGroups.ToString "N0") ]
|
||||||
|
@ -102,16 +103,17 @@ let maintain (churches : Church list) (stats : Map<string, ChurchStats>) ctx vi
|
||||||
])
|
])
|
||||||
|> tbody []
|
|> tbody []
|
||||||
]
|
]
|
||||||
[ div [ _class "pt-center-text" ] [
|
[ div [ _class "pt-center-text" ] [
|
||||||
br []
|
br []
|
||||||
a [ _href $"/church/{emptyGuid}/edit"; _title s["Add a New Church"].Value ]
|
a [ _href $"/church/{emptyGuid}/edit"; _title s["Add a New Church"].Value ] [
|
||||||
[ icon "add_circle"; rawText " "; locStr s["Add a New Church"] ]
|
icon "add_circle"; rawText " "; locStr s["Add a New Church"]
|
||||||
br []
|
]
|
||||||
br []
|
br []
|
||||||
]
|
br []
|
||||||
tableSummary churches.Length s
|
]
|
||||||
chTbl
|
tableSummary churches.Length s
|
||||||
form [ _id "DeleteForm"; _action ""; _method "post" ] [ csrfToken ctx ]
|
chTbl
|
||||||
|
form [ _id "DeleteForm"; _action ""; _method "post" ] [ csrfToken ctx ]
|
||||||
]
|
]
|
||||||
|> Layout.Content.wide
|
|> Layout.Content.wide
|
||||||
|> Layout.standard vi "Maintain Churches"
|
|> Layout.standard vi "Maintain Churches"
|
||||||
|
|
|
@ -113,6 +113,12 @@ let flatGuid (x : Guid) = x.ToString "N"
|
||||||
/// An empty GUID string (used for "add" actions)
|
/// An empty GUID string (used for "add" actions)
|
||||||
let emptyGuid = flatGuid Guid.Empty
|
let emptyGuid = flatGuid Guid.Empty
|
||||||
|
|
||||||
|
/// Create an HTML onsubmit event handler
|
||||||
|
let _onsubmit = attr "onsubmit"
|
||||||
|
|
||||||
|
/// A "rel='noopener'" attribute
|
||||||
|
let _relNoOpener = _rel "noopener"
|
||||||
|
|
||||||
/// The name this function used to have when the view engine was part of Giraffe
|
/// The name this function used to have when the view engine was part of Giraffe
|
||||||
let renderHtmlNode = RenderView.AsString.htmlNode
|
let renderHtmlNode = RenderView.AsString.htmlNode
|
||||||
|
|
||||||
|
@ -143,3 +149,15 @@ module TimeZones =
|
||||||
let name tzId (s : IStringLocalizer) =
|
let name tzId (s : IStringLocalizer) =
|
||||||
try s[xref[tzId]]
|
try s[xref[tzId]]
|
||||||
with :? KeyNotFoundException -> LocalizedString (tzId, tzId)
|
with :? KeyNotFoundException -> LocalizedString (tzId, tzId)
|
||||||
|
|
||||||
|
|
||||||
|
open Giraffe.ViewEngine.Htmx
|
||||||
|
|
||||||
|
/// Known htmx targets
|
||||||
|
module Target =
|
||||||
|
|
||||||
|
/// htmx links target the body element
|
||||||
|
let body = _hxTarget "body"
|
||||||
|
|
||||||
|
/// htmx links target the #pt-body element
|
||||||
|
let content = _hxTarget "#pt-body"
|
||||||
|
|
|
@ -50,7 +50,6 @@ let index vi =
|
||||||
let l = I18N.forView "Home/Index"
|
let l = I18N.forView "Home/Index"
|
||||||
use sw = new StringWriter ()
|
use sw = new StringWriter ()
|
||||||
let raw = rawLocText sw
|
let raw = rawLocText sw
|
||||||
|
|
||||||
[ p [] [
|
[ p [] [
|
||||||
raw l["Welcome to <strong>{0}</strong>!", s["PrayerTracker"]]
|
raw l["Welcome to <strong>{0}</strong>!", s["PrayerTracker"]]
|
||||||
space
|
space
|
||||||
|
@ -128,7 +127,6 @@ let privacyPolicy vi =
|
||||||
let l = I18N.forView "Home/PrivacyPolicy"
|
let l = I18N.forView "Home/PrivacyPolicy"
|
||||||
use sw = new StringWriter ()
|
use sw = new StringWriter ()
|
||||||
let raw = rawLocText sw
|
let raw = rawLocText sw
|
||||||
|
|
||||||
[ p [ _class "pt-right-text" ] [ small [] [ em [] [ raw l["(as of July 31, 2018)"] ] ] ]
|
[ p [ _class "pt-right-text" ] [ small [] [ em [] [ raw l["(as of July 31, 2018)"] ] ] ]
|
||||||
p [] [
|
p [] [
|
||||||
raw l["The nature of the service is one where privacy is a must."]
|
raw l["The nature of the service is one where privacy is a must."]
|
||||||
|
@ -150,7 +148,7 @@ let privacyPolicy vi =
|
||||||
rawText " – "
|
rawText " – "
|
||||||
raw l["{0} stores the text of prayer requests.", s["PrayerTracker"]]
|
raw l["{0} stores the text of prayer requests.", s["PrayerTracker"]]
|
||||||
space
|
space
|
||||||
raw l["It also stores names and e-mail addreses of small group members, and plain-text passwords for small groups with password-protected lists."]
|
raw l["It also stores names and e-mail addresses of small group members, and plain-text passwords for small groups with password-protected lists."]
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
h3 [] [ raw l["How Your Data Is Accessed / Secured"] ]
|
h3 [] [ raw l["How Your Data Is Accessed / Secured"] ]
|
||||||
|
|
|
@ -3,39 +3,26 @@ module PrayerTracker.Views.Layout
|
||||||
|
|
||||||
open Giraffe.ViewEngine
|
open Giraffe.ViewEngine
|
||||||
open Giraffe.ViewEngine.Accessibility
|
open Giraffe.ViewEngine.Accessibility
|
||||||
open Giraffe.ViewEngine.Htmx
|
|
||||||
open PrayerTracker
|
|
||||||
open PrayerTracker.ViewModels
|
open PrayerTracker.ViewModels
|
||||||
open System
|
|
||||||
open System.Globalization
|
open System.Globalization
|
||||||
|
|
||||||
|
|
||||||
/// Get the two-character language code for the current request
|
/// Get the two-character language code for the current request
|
||||||
let langCode () = if CultureInfo.CurrentCulture.Name.StartsWith "es" then "es" else "en"
|
let langCode () = if CultureInfo.CurrentCulture.Name.StartsWith "es" then "es" else "en"
|
||||||
|
|
||||||
|
|
||||||
/// Known htmx targets
|
|
||||||
module Target =
|
|
||||||
|
|
||||||
/// htmx links target the body element
|
|
||||||
let body = _hxTarget "body"
|
|
||||||
|
|
||||||
/// htmx links target the #pt-body element
|
|
||||||
let content = _hxTarget "#pt-body"
|
|
||||||
|
|
||||||
|
|
||||||
/// Navigation items
|
/// Navigation items
|
||||||
module Navigation =
|
module Navigation =
|
||||||
|
|
||||||
/// Top navigation bar
|
/// Top navigation bar
|
||||||
let top m =
|
let top m =
|
||||||
let s = I18N.localizer.Force ()
|
let s = I18N.localizer.Force ()
|
||||||
let menuSpacer = rawText " "
|
let menuSpacer = rawText " "
|
||||||
|
let _dropdown = _class "dropbtn"
|
||||||
let leftLinks = [
|
let leftLinks = [
|
||||||
match m.User with
|
match m.User with
|
||||||
| Some u ->
|
| Some u ->
|
||||||
li [ _class "dropdown" ] [
|
li [ _class "dropdown" ] [
|
||||||
a [ _class "dropbtn"; _ariaLabel s["Requests"].Value; _title s["Requests"].Value; _roleButton ] [
|
a [ _dropdown; _ariaLabel s["Requests"].Value; _title s["Requests"].Value; _roleButton ] [
|
||||||
icon "question_answer"; space; locStr s["Requests"]; space; icon "keyboard_arrow_down"
|
icon "question_answer"; space; locStr s["Requests"]; space; icon "keyboard_arrow_down"
|
||||||
]
|
]
|
||||||
div [ _class "dropdown-content"; _roleMenuBar ] [
|
div [ _class "dropdown-content"; _roleMenuBar ] [
|
||||||
|
@ -48,7 +35,7 @@ module Navigation =
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
li [ _class "dropdown" ] [
|
li [ _class "dropdown" ] [
|
||||||
a [ _class "dropbtn"; _ariaLabel s["Group"].Value; _title s["Group"].Value; _roleButton ] [
|
a [ _dropdown; _ariaLabel s["Group"].Value; _title s["Group"].Value; _roleButton ] [
|
||||||
icon "group"; space; locStr s["Group"]; space; icon "keyboard_arrow_down"
|
icon "group"; space; locStr s["Group"]; space; icon "keyboard_arrow_down"
|
||||||
]
|
]
|
||||||
div [ _class "dropdown-content"; _roleMenuBar ] [
|
div [ _class "dropdown-content"; _roleMenuBar ] [
|
||||||
|
@ -65,7 +52,7 @@ module Navigation =
|
||||||
]
|
]
|
||||||
if u.isAdmin then
|
if u.isAdmin then
|
||||||
li [ _class "dropdown" ] [
|
li [ _class "dropdown" ] [
|
||||||
a [ _class "dropbtn"
|
a [ _dropdown
|
||||||
_ariaLabel s["Administration"].Value
|
_ariaLabel s["Administration"].Value
|
||||||
_title s["Administration"].Value
|
_title s["Administration"].Value
|
||||||
_roleButton ] [
|
_roleButton ] [
|
||||||
|
@ -89,7 +76,7 @@ module Navigation =
|
||||||
]
|
]
|
||||||
| None ->
|
| None ->
|
||||||
li [ _class "dropdown" ] [
|
li [ _class "dropdown" ] [
|
||||||
a [ _class "dropbtn"; _ariaLabel s["Log On"].Value; _title s["Log On"].Value; _roleButton ] [
|
a [ _dropdown; _ariaLabel s["Log On"].Value; _title s["Log On"].Value; _roleButton ] [
|
||||||
icon "security"; space; locStr s["Log On"]; space; icon "keyboard_arrow_down"
|
icon "security"; space; locStr s["Log On"]; space; icon "keyboard_arrow_down"
|
||||||
]
|
]
|
||||||
div [ _class "dropdown-content"; _roleMenuBar ] [
|
div [ _class "dropdown-content"; _roleMenuBar ] [
|
||||||
|
@ -111,7 +98,7 @@ module Navigation =
|
||||||
_ariaLabel s["Help"].Value
|
_ariaLabel s["Help"].Value
|
||||||
_title s["View Help"].Value
|
_title s["View Help"].Value
|
||||||
_target "_blank"
|
_target "_blank"
|
||||||
_rel "noopener" ] [
|
_relNoOpener ] [
|
||||||
icon "help"; space; locStr s["Help"]
|
icon "help"; space; locStr s["Help"]
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
|
@ -130,10 +117,7 @@ module Navigation =
|
||||||
]
|
]
|
||||||
| None -> ()
|
| None -> ()
|
||||||
li [] [
|
li [] [
|
||||||
a [ _href "/log-off"
|
a [ _href "/log-off"; _ariaLabel s["Log Off"].Value; _title s["Log Off"].Value; Target.body ] [
|
||||||
_ariaLabel s["Log Off"].Value
|
|
||||||
_title s["Log Off"].Value
|
|
||||||
_hxTarget "body" ] [
|
|
||||||
icon "power_settings_new"; space; locStr s["Log Off"]
|
icon "power_settings_new"; space; locStr s["Log Off"]
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
|
@ -222,10 +206,11 @@ let private htmlHead m pageTitle =
|
||||||
yield! commonHead
|
yield! commonHead
|
||||||
for cssFile in m.Style do
|
for cssFile in m.Style do
|
||||||
link [ _rel "stylesheet"; _href $"/css/{cssFile}.css"; _type "text/css" ]
|
link [ _rel "stylesheet"; _href $"/css/{cssFile}.css"; _type "text/css" ]
|
||||||
for jsFile in m.Script do
|
|
||||||
script [ _src $"/js/{jsFile}.js" ] []
|
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
open Giraffe.ViewEngine.Htmx
|
||||||
|
|
||||||
/// Render a link to the help page for the current page
|
/// Render a link to the help page for the current page
|
||||||
let private helpLink link =
|
let private helpLink link =
|
||||||
let s = I18N.localizer.Force ()
|
let s = I18N.localizer.Force ()
|
||||||
|
@ -241,7 +226,7 @@ let private helpLink link =
|
||||||
/// Render the page title, and optionally a help link
|
/// Render the page title, and optionally a help link
|
||||||
let private renderPageTitle m pageTitle =
|
let private renderPageTitle m pageTitle =
|
||||||
h2 [ _id "pt-page-title" ] [
|
h2 [ _id "pt-page-title" ] [
|
||||||
match m.HelpLink with Some link -> Help.fullLink (langCode ()) link |> helpLink | None -> ()
|
match m.HelpLink with Some link -> PrayerTracker.Utils.Help.fullLink (langCode ()) link |> helpLink | None -> ()
|
||||||
locStr pageTitle
|
locStr pageTitle
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -268,6 +253,9 @@ let private messages m =
|
||||||
]
|
]
|
||||||
])
|
])
|
||||||
|
|
||||||
|
|
||||||
|
open System
|
||||||
|
|
||||||
/// Render the <footer> at the bottom of the page
|
/// Render the <footer> at the bottom of the page
|
||||||
let private htmlFooter m =
|
let private htmlFooter m =
|
||||||
let s = I18N.localizer.Force ()
|
let s = I18N.localizer.Force ()
|
||||||
|
@ -282,7 +270,7 @@ let private htmlFooter m =
|
||||||
a [ _href "https://github.com/bit-badger/PrayerTracker"
|
a [ _href "https://github.com/bit-badger/PrayerTracker"
|
||||||
_title s["View source code and get technical support"].Value
|
_title s["View source code and get technical support"].Value
|
||||||
_target "_blank"
|
_target "_blank"
|
||||||
_rel "noopener" ] [
|
_relNoOpener ] [
|
||||||
locStr s["Source & Support"]
|
locStr s["Source & Support"]
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
|
@ -300,32 +288,56 @@ let private htmlFooter m =
|
||||||
script [ _src "/js/app.js" ] []
|
script [ _src "/js/app.js" ] []
|
||||||
]
|
]
|
||||||
|
|
||||||
/// The standard layout for PrayerTracker
|
/// The content portion of the PrayerTracker layout
|
||||||
let standard m pageTitle (content : XmlNode) =
|
let private contentSection viewInfo title (content : XmlNode) = [
|
||||||
let s = I18N.localizer.Force ()
|
Navigation.identity viewInfo
|
||||||
let ttl = s[pageTitle]
|
renderPageTitle viewInfo title
|
||||||
|
yield! messages viewInfo
|
||||||
|
content
|
||||||
|
htmlFooter viewInfo
|
||||||
|
for jsFile in viewInfo.Script do
|
||||||
|
script [ _src $"/js/{jsFile}.js" ] []
|
||||||
|
]
|
||||||
|
|
||||||
|
/// The HTML head element for partial responses
|
||||||
|
let private partialHead pgTitle =
|
||||||
|
let s = I18N.localizer.Force ()
|
||||||
|
head [] [
|
||||||
|
meta [ _charset "UTF-8" ]
|
||||||
|
title [] [ locStr pgTitle; titleSep; locStr s["PrayerTracker"] ]
|
||||||
|
]
|
||||||
|
|
||||||
|
open Giraffe.Htmx.Common
|
||||||
|
|
||||||
|
/// The body of the PrayerTracker layout
|
||||||
|
let private pageLayout viewInfo title content =
|
||||||
|
body [ _hxBoost ] [
|
||||||
|
Navigation.top viewInfo
|
||||||
|
div [ _id "pt-body"; Target.content; _hxSwap $"{HxSwap.InnerHtml} show:window:top" ]
|
||||||
|
(contentSection viewInfo title content)
|
||||||
|
]
|
||||||
|
|
||||||
|
/// The standard layout(s) for PrayerTracker
|
||||||
|
let standard viewInfo pageTitle content =
|
||||||
|
let s = I18N.localizer.Force ()
|
||||||
|
let pgTitle = s[pageTitle]
|
||||||
html [ _lang (langCode ()) ] [
|
html [ _lang (langCode ()) ] [
|
||||||
htmlHead m ttl
|
match viewInfo.Layout with
|
||||||
body [ _hxBoost ] [
|
| FullPage ->
|
||||||
Navigation.top m
|
htmlHead viewInfo pgTitle
|
||||||
div [ _id "pt-body" ] [
|
pageLayout viewInfo pgTitle content
|
||||||
Navigation.identity m
|
| PartialPage ->
|
||||||
renderPageTitle m ttl
|
partialHead pgTitle
|
||||||
yield! messages m
|
pageLayout viewInfo pgTitle content
|
||||||
content
|
| ContentOnly ->
|
||||||
htmlFooter m
|
partialHead pgTitle
|
||||||
]
|
body [] (contentSection viewInfo pgTitle content)
|
||||||
]
|
|
||||||
]
|
]
|
||||||
|
|
||||||
/// A layout with nothing but a title and content
|
/// A layout with nothing but a title and content
|
||||||
let bare pageTitle content =
|
let bare pageTitle content =
|
||||||
let s = I18N.localizer.Force ()
|
let s = I18N.localizer.Force ()
|
||||||
let ttl = s[pageTitle]
|
|
||||||
html [ _lang (langCode ()) ] [
|
html [ _lang (langCode ()) ] [
|
||||||
head [] [
|
partialHead s[pageTitle]
|
||||||
meta [ _charset "UTF-8" ]
|
|
||||||
title [] [ locStr ttl; titleSep; locStr s["PrayerTracker"] ]
|
|
||||||
]
|
|
||||||
body [] [ content ]
|
body [] [ content ]
|
||||||
]
|
]
|
||||||
|
|
|
@ -14,63 +14,66 @@ open PrayerTracker.ViewModels
|
||||||
let edit (m : EditRequest) today ctx vi =
|
let edit (m : EditRequest) today ctx vi =
|
||||||
let s = I18N.localizer.Force ()
|
let s = I18N.localizer.Force ()
|
||||||
let pageTitle = if m.IsNew then "Add a New Request" else "Edit Request"
|
let pageTitle = if m.IsNew then "Add a New Request" else "Edit Request"
|
||||||
[ form [ _action "/prayer-request/save"; _method "post"; _class "pt-center-columns" ] [
|
[ form [ _action "/prayer-request/save"; _method "post"; _class "pt-center-columns"; Target.content ] [
|
||||||
csrfToken ctx
|
csrfToken ctx
|
||||||
input [ _type "hidden"; _name (nameof m.RequestId); _value (flatGuid m.RequestId) ]
|
input [ _type "hidden"; _name (nameof m.RequestId); _value (flatGuid m.RequestId) ]
|
||||||
div [ _class "pt-field-row" ] [
|
div [ _class "pt-field-row" ] [
|
||||||
div [ _class "pt-field" ] [
|
|
||||||
label [ _for (nameof m.RequestType) ] [ locStr s["Request Type"] ]
|
|
||||||
ReferenceList.requestTypeList s
|
|
||||||
|> Seq.ofList
|
|
||||||
|> Seq.map (fun (typ, desc) -> typ.code, desc.Value)
|
|
||||||
|> selectList (nameof m.RequestType) m.RequestType [ _required; _autofocus ]
|
|
||||||
]
|
|
||||||
div [ _class "pt-field" ] [
|
|
||||||
label [ _for "requestor" ] [ locStr s["Requestor / Subject"] ]
|
|
||||||
input [ _type "text"
|
|
||||||
_name (nameof m.Requestor)
|
|
||||||
_id "requestor"
|
|
||||||
_value (defaultArg m.Requestor "") ]
|
|
||||||
]
|
|
||||||
if m.IsNew then
|
|
||||||
div [ _class "pt-field" ] [
|
div [ _class "pt-field" ] [
|
||||||
label [ _for "enteredDate" ] [ locStr s["Date"] ]
|
label [ _for (nameof m.RequestType) ] [ locStr s["Request Type"] ]
|
||||||
input [ _type "date"; _name (nameof m.EnteredDate); _id "enteredDate"; _placeholder today ]
|
ReferenceList.requestTypeList s
|
||||||
|
|> Seq.ofList
|
||||||
|
|> Seq.map (fun (typ, desc) -> typ.code, desc.Value)
|
||||||
|
|> selectList (nameof m.RequestType) m.RequestType [ _required; _autofocus ]
|
||||||
]
|
]
|
||||||
else
|
|
||||||
div [ _class "pt-field" ] [
|
div [ _class "pt-field" ] [
|
||||||
div [ _class "pt-checkbox-field" ] [
|
label [ _for "requestor" ] [ locStr s["Requestor / Subject"] ]
|
||||||
br []
|
input [ _type "text"
|
||||||
input [ _type "checkbox"; _name (nameof m.SkipDateUpdate); _id "skipDateUpdate"; _value "True" ]
|
_id "requestor"
|
||||||
label [ _for "skipDateUpdate" ] [ locStr s["Check to not update the date"] ]
|
_name (nameof m.Requestor)
|
||||||
br []
|
_value (defaultArg m.Requestor "") ]
|
||||||
small [] [ em [] [ str (s["Typo Corrections"].Value.ToLower ()); rawText ", etc." ] ]
|
]
|
||||||
|
if m.IsNew then
|
||||||
|
div [ _class "pt-field" ] [
|
||||||
|
label [ _for "enteredDate" ] [ locStr s["Date"] ]
|
||||||
|
input [ _type "date"; _name (nameof m.EnteredDate); _id "enteredDate"; _placeholder today ]
|
||||||
]
|
]
|
||||||
|
else
|
||||||
|
div [ _class "pt-field" ] [
|
||||||
|
div [ _class "pt-checkbox-field" ] [
|
||||||
|
br []
|
||||||
|
input [ _type "checkbox"
|
||||||
|
_name (nameof m.SkipDateUpdate)
|
||||||
|
_id "skipDateUpdate"
|
||||||
|
_value "True" ]
|
||||||
|
label [ _for "skipDateUpdate" ] [ locStr s["Check to not update the date"] ]
|
||||||
|
br []
|
||||||
|
small [] [ em [] [ str (s["Typo Corrections"].Value.ToLower ()); rawText ", etc." ] ]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
div [ _class "pt-field-row" ] [
|
||||||
|
div [ _class "pt-field" ] [
|
||||||
|
label [] [ locStr s["Expiration"] ]
|
||||||
|
ReferenceList.expirationList s (not m.IsNew)
|
||||||
|
|> List.map (fun exp ->
|
||||||
|
let radioId = $"expiration_{fst exp}"
|
||||||
|
span [ _class "text-nowrap" ] [
|
||||||
|
radio (nameof m.Expiration) radioId (fst exp) m.Expiration
|
||||||
|
label [ _for radioId ] [ locStr (snd exp) ]
|
||||||
|
rawText " "
|
||||||
|
])
|
||||||
|
|> div [ _class "pt-center-text" ]
|
||||||
]
|
]
|
||||||
]
|
|
||||||
div [ _class "pt-field-row" ] [
|
|
||||||
div [ _class "pt-field" ] [
|
|
||||||
label [] [ locStr s["Expiration"] ]
|
|
||||||
ReferenceList.expirationList s (not m.IsNew)
|
|
||||||
|> List.map (fun exp ->
|
|
||||||
let radioId = $"expiration_{fst exp}"
|
|
||||||
span [ _class "text-nowrap" ] [
|
|
||||||
radio (nameof m.Expiration) radioId (fst exp) m.Expiration
|
|
||||||
label [ _for radioId ] [ locStr (snd exp) ]
|
|
||||||
rawText " "
|
|
||||||
])
|
|
||||||
|> div [ _class "pt-center-text" ]
|
|
||||||
]
|
]
|
||||||
]
|
div [ _class "pt-field-row" ] [
|
||||||
div [ _class "pt-field-row" ] [
|
div [ _class "pt-field pt-editor" ] [
|
||||||
div [ _class "pt-field pt-editor" ] [
|
label [ _for "text" ] [ locStr s["Request"] ]
|
||||||
label [ _for "text" ] [ locStr s["Request"] ]
|
textarea [ _name (nameof m.Text); _id "text" ] [ str m.Text ]
|
||||||
textarea [ _name (nameof m.Text); _id "text" ] [ str m.Text ]
|
]
|
||||||
]
|
]
|
||||||
|
div [ _class "pt-field-row" ] [ submit [] "save" s["Save Request"] ]
|
||||||
]
|
]
|
||||||
div [ _class "pt-field-row" ] [ submit [] "save" s["Save Request"] ]
|
script [] [ rawText "PT.onLoad(PT.initCKEditor)" ]
|
||||||
]
|
|
||||||
script [] [ rawText "PT.onLoad(PT.initCKEditor)" ]
|
|
||||||
]
|
]
|
||||||
|> Layout.Content.standard
|
|> Layout.Content.standard
|
||||||
|> Layout.standard vi pageTitle
|
|> Layout.standard vi pageTitle
|
||||||
|
@ -81,18 +84,18 @@ let email m vi =
|
||||||
let pageTitle = $"""{s["Prayer Requests"].Value} • {m.SmallGroup.name}"""
|
let pageTitle = $"""{s["Prayer Requests"].Value} • {m.SmallGroup.name}"""
|
||||||
let prefs = m.SmallGroup.preferences
|
let prefs = m.SmallGroup.preferences
|
||||||
let addresses = m.Recipients |> List.map (fun mbr -> $"{mbr.memberName} <{mbr.email}>") |> String.concat ", "
|
let addresses = m.Recipients |> List.map (fun mbr -> $"{mbr.memberName} <{mbr.email}>") |> String.concat ", "
|
||||||
[ p [ _style $"font-family:{prefs.listFonts};font-size:%i{prefs.textFontSize}pt;" ] [
|
[ p [ _style $"font-family:{prefs.listFonts};font-size:%i{prefs.textFontSize}pt;" ] [
|
||||||
locStr s["The request list was sent to the following people, via individual e-mails"]
|
locStr s["The request list was sent to the following people, via individual e-mails"]
|
||||||
rawText ":"
|
rawText ":"
|
||||||
br []
|
br []
|
||||||
small [] [ str addresses ]
|
small [] [ str addresses ]
|
||||||
]
|
]
|
||||||
span [ _class "pt-email-heading" ] [ locStr s["HTML Format"]; rawText ":" ]
|
span [ _class "pt-email-heading" ] [ locStr s["HTML Format"]; rawText ":" ]
|
||||||
div [ _class "pt-email-canvas" ] [ rawText (m.AsHtml s) ]
|
div [ _class "pt-email-canvas" ] [ rawText (m.AsHtml s) ]
|
||||||
br []
|
br []
|
||||||
br []
|
br []
|
||||||
span [ _class "pt-email-heading" ] [ locStr s["Plain-Text Format"]; rawText ":" ]
|
span [ _class "pt-email-heading" ] [ locStr s["Plain-Text Format"]; rawText ":" ]
|
||||||
div [ _class "pt-email-canvas" ] [ pre [] [ str (m.AsText s) ] ]
|
div [ _class "pt-email-canvas" ] [ pre [] [ str (m.AsText s) ] ]
|
||||||
]
|
]
|
||||||
|> Layout.Content.standard
|
|> Layout.Content.standard
|
||||||
|> Layout.standard vi pageTitle
|
|> Layout.standard vi pageTitle
|
||||||
|
@ -100,8 +103,8 @@ let email m vi =
|
||||||
|
|
||||||
/// View for a small group's public prayer request list
|
/// View for a small group's public prayer request list
|
||||||
let list (m : RequestList) vi =
|
let list (m : RequestList) vi =
|
||||||
[ br []
|
[ br []
|
||||||
I18N.localizer.Force () |> (m.AsHtml >> rawText)
|
I18N.localizer.Force () |> (m.AsHtml >> rawText)
|
||||||
]
|
]
|
||||||
|> Layout.Content.standard
|
|> Layout.Content.standard
|
||||||
|> Layout.standard vi "View Request List"
|
|> Layout.standard vi "View Request List"
|
||||||
|
@ -113,40 +116,42 @@ let lists (groups : SmallGroup list) vi =
|
||||||
let l = I18N.forView "Requests/Lists"
|
let l = I18N.forView "Requests/Lists"
|
||||||
use sw = new StringWriter ()
|
use sw = new StringWriter ()
|
||||||
let raw = rawLocText sw
|
let raw = rawLocText sw
|
||||||
[ p [] [
|
[ p [] [
|
||||||
raw l["The groups listed below have either public or password-protected request lists."]
|
raw l["The groups listed below have either public or password-protected request lists."]
|
||||||
space
|
space
|
||||||
raw l["Those with list icons are public, and those with log on icons are password-protected."]
|
raw l["Those with list icons are public, and those with log on icons are password-protected."]
|
||||||
space
|
space
|
||||||
raw l["Click the appropriate icon to log on or view the request list."]
|
raw l["Click the appropriate icon to log on or view the request list."]
|
||||||
]
|
]
|
||||||
match groups.Length with
|
match groups.Length with
|
||||||
| 0 -> p [] [ raw l["There are no groups with public or password-protected request lists."] ]
|
| 0 -> p [] [ raw l["There are no groups with public or password-protected request lists."] ]
|
||||||
| count ->
|
| count ->
|
||||||
tableSummary count s
|
tableSummary count s
|
||||||
table [ _class "pt-table pt-action-table" ] [
|
table [ _class "pt-table pt-action-table" ] [
|
||||||
thead [] [
|
thead [] [
|
||||||
tr [] [
|
tr [] [
|
||||||
th [] [ locStr s["Actions"] ]
|
th [] [ locStr s["Actions"] ]
|
||||||
th [] [ locStr s["Church"] ]
|
th [] [ locStr s["Church"] ]
|
||||||
th [] [ locStr s["Group"] ]
|
th [] [ locStr s["Group"] ]
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
groups
|
groups
|
||||||
|> List.map (fun grp ->
|
|> List.map (fun grp ->
|
||||||
let grpId = flatGuid grp.smallGroupId
|
let grpId = flatGuid grp.smallGroupId
|
||||||
tr [] [
|
tr [] [
|
||||||
if grp.preferences.isPublic then
|
if grp.preferences.isPublic then
|
||||||
a [ _href $"/prayer-requests/{grpId}/list"; _title s["View"].Value ] [ icon "list" ]
|
a [ _href $"/prayer-requests/{grpId}/list"; _title s["View"].Value ] [ icon "list" ]
|
||||||
else
|
else
|
||||||
a [ _href $"/small-group/log-on/{grpId}"; _title s["Log On"].Value ] [ icon "verified_user" ]
|
a [ _href $"/small-group/log-on/{grpId}"; _title s["Log On"].Value ] [
|
||||||
|> List.singleton
|
icon "verified_user"
|
||||||
|> td []
|
]
|
||||||
td [] [ str grp.church.name ]
|
|> List.singleton
|
||||||
td [] [ str grp.name ]
|
|> td []
|
||||||
])
|
td [] [ str grp.church.name ]
|
||||||
|> tbody []
|
td [] [ str grp.name ]
|
||||||
]
|
])
|
||||||
|
|> tbody []
|
||||||
|
]
|
||||||
]
|
]
|
||||||
|> Layout.Content.standard
|
|> Layout.Content.standard
|
||||||
|> Layout.standard vi "Request Lists"
|
|> Layout.standard vi "Request Lists"
|
||||||
|
@ -159,14 +164,13 @@ let maintain (m : MaintainRequests) (ctx : HttpContext) vi =
|
||||||
use sw = new StringWriter ()
|
use sw = new StringWriter ()
|
||||||
let raw = rawLocText sw
|
let raw = rawLocText sw
|
||||||
let now = m.SmallGroup.localDateNow (ctx.GetService<IClock> ())
|
let now = m.SmallGroup.localDateNow (ctx.GetService<IClock> ())
|
||||||
|
let prefs = m.SmallGroup.preferences
|
||||||
let types = ReferenceList.requestTypeList s |> Map.ofList
|
let types = ReferenceList.requestTypeList s |> Map.ofList
|
||||||
let updReq (req : PrayerRequest) =
|
let updReq (req : PrayerRequest) =
|
||||||
if req.updateRequired now m.SmallGroup.preferences.daysToExpire m.SmallGroup.preferences.longTermUpdateWeeks then
|
if req.updateRequired now prefs.daysToExpire prefs.longTermUpdateWeeks then "pt-request-update" else ""
|
||||||
"pt-request-update"
|
|
||||||
else ""
|
|
||||||
|> _class
|
|> _class
|
||||||
let reqExp (req : PrayerRequest) =
|
let reqExp (req : PrayerRequest) =
|
||||||
_class (if req.isExpired now m.SmallGroup.preferences.daysToExpire then "pt-request-expired" else "")
|
_class (if req.isExpired now prefs.daysToExpire then "pt-request-expired" else "")
|
||||||
/// Iterate the sequence once, before we render, so we can get the count of it at the top of the table
|
/// Iterate the sequence once, before we render, so we can get the count of it at the top of the table
|
||||||
let requests =
|
let requests =
|
||||||
m.Requests
|
m.Requests
|
||||||
|
@ -175,28 +179,33 @@ let maintain (m : MaintainRequests) (ctx : HttpContext) vi =
|
||||||
let reqText = htmlToPlainText req.text
|
let reqText = htmlToPlainText req.text
|
||||||
let delAction = $"/prayer-request/{reqId}/delete"
|
let delAction = $"/prayer-request/{reqId}/delete"
|
||||||
let delPrompt =
|
let delPrompt =
|
||||||
[ s["Are you sure you want to delete this {0}? This action cannot be undone.",
|
[ s["Are you sure you want to delete this {0}? This action cannot be undone.",
|
||||||
s["Prayer Request"].Value.ToLower() ].Value
|
s["Prayer Request"].Value.ToLower() ].Value
|
||||||
"\\n"
|
"\\n"
|
||||||
l["(If the prayer request has been answered, or an event has passed, consider inactivating it instead.)"]
|
l["(If the prayer request has been answered, or an event has passed, consider inactivating it instead.)"]
|
||||||
.Value
|
.Value
|
||||||
]
|
]
|
||||||
|> String.concat ""
|
|> String.concat ""
|
||||||
tr [] [
|
tr [] [
|
||||||
td [] [
|
td [] [
|
||||||
a [ _href $"/prayer-request/{reqId}/edit"; _title l["Edit This Prayer Request"].Value ]
|
a [ _href $"/prayer-request/{reqId}/edit"; _title l["Edit This Prayer Request"].Value ] [
|
||||||
[ icon "edit" ]
|
icon "edit"
|
||||||
if req.isExpired now m.SmallGroup.preferences.daysToExpire then
|
]
|
||||||
a [ _href $"/prayer-request/{reqId}/restore"
|
if req.isExpired now prefs.daysToExpire then
|
||||||
_title l["Restore This Inactive Request"].Value ]
|
a [ _href $"/prayer-request/{reqId}/restore"
|
||||||
[ icon "visibility" ]
|
_title l["Restore This Inactive Request"].Value ] [
|
||||||
|
icon "visibility"
|
||||||
|
]
|
||||||
else
|
else
|
||||||
a [ _href $"/prayer-request/{reqId}/expire"
|
a [ _href $"/prayer-request/{reqId}/expire"
|
||||||
_title l["Expire This Request Immediately"].Value ]
|
_title l["Expire This Request Immediately"].Value ] [
|
||||||
[ icon "visibility_off" ]
|
icon "visibility_off"
|
||||||
a [ _href delAction; _title l["Delete This Request"].Value;
|
]
|
||||||
_onclick $"return PT.confirmDelete('{delAction}','{delPrompt}')" ]
|
a [ _href delAction
|
||||||
[ icon "delete_forever" ]
|
_title l["Delete This Request"].Value
|
||||||
|
_onclick $"return PT.confirmDelete('{delAction}','{delPrompt}')" ] [
|
||||||
|
icon "delete_forever"
|
||||||
|
]
|
||||||
]
|
]
|
||||||
td [ updReq req ] [
|
td [ updReq req ] [
|
||||||
str (req.updatedDate.ToString(s["MMMM d, yyyy"].Value, Globalization.CultureInfo.CurrentUICulture))
|
str (req.updatedDate.ToString(s["MMMM d, yyyy"].Value, Globalization.CultureInfo.CurrentUICulture))
|
||||||
|
@ -210,79 +219,83 @@ let maintain (m : MaintainRequests) (ctx : HttpContext) vi =
|
||||||
]
|
]
|
||||||
])
|
])
|
||||||
|> List.ofSeq
|
|> List.ofSeq
|
||||||
[ div [ _class "pt-center-text" ] [
|
[ div [ _class "pt-center-text" ] [
|
||||||
br []
|
br []
|
||||||
a [ _href $"/prayer-request/{emptyGuid}/edit"; _title s["Add a New Request"].Value ]
|
a [ _href $"/prayer-request/{emptyGuid}/edit"; _title s["Add a New Request"].Value ] [
|
||||||
[ icon "add_circle"; rawText " "; locStr s["Add a New Request"] ]
|
icon "add_circle"; rawText " "; locStr s["Add a New Request"]
|
||||||
rawText " "
|
]
|
||||||
a [ _href "/prayer-requests/view"; _title s["View Prayer Request List"].Value ]
|
|
||||||
[ icon "list"; rawText " "; locStr s["View Prayer Request List"] ]
|
|
||||||
match m.SearchTerm with
|
|
||||||
| Some _ ->
|
|
||||||
rawText " "
|
rawText " "
|
||||||
a [ _href "/prayer-requests"; _title l["Clear Search Criteria"].Value ]
|
a [ _href "/prayer-requests/view"; _title s["View Prayer Request List"].Value ] [
|
||||||
[ icon "highlight_off"; rawText " "; raw l["Clear Search Criteria"] ]
|
icon "list"; rawText " "; locStr s["View Prayer Request List"]
|
||||||
| None -> ()
|
]
|
||||||
]
|
match m.SearchTerm with
|
||||||
form [ _action "/prayer-requests"; _method "get"; _class "pt-center-text pt-search-form" ] [
|
| Some _ ->
|
||||||
input [ _type "text"
|
rawText " "
|
||||||
_name "search"
|
a [ _href "/prayer-requests"; _title l["Clear Search Criteria"].Value ] [
|
||||||
_placeholder l["Search requests..."].Value
|
icon "highlight_off"; rawText " "; raw l["Clear Search Criteria"]
|
||||||
_value (defaultArg m.SearchTerm "")
|
|
||||||
]
|
]
|
||||||
space
|
| None -> ()
|
||||||
submit [] "search" s["Search"]
|
]
|
||||||
]
|
form [ _action "/prayer-requests"; _method "get"; _class "pt-center-text pt-search-form"; Target.content ] [
|
||||||
br []
|
input [ _type "text"
|
||||||
tableSummary requests.Length s
|
_name "search"
|
||||||
match requests.Length with
|
_placeholder l["Search requests..."].Value
|
||||||
| 0 -> ()
|
_value (defaultArg m.SearchTerm "")
|
||||||
| _ ->
|
|
||||||
table [ _class "pt-table pt-action-table" ] [
|
|
||||||
thead [] [
|
|
||||||
tr [] [
|
|
||||||
th [] [ locStr s["Actions"] ]
|
|
||||||
th [] [ locStr s["Updated Date"] ]
|
|
||||||
th [] [ locStr s["Type"] ]
|
|
||||||
th [] [ locStr s["Requestor"] ]
|
|
||||||
th [] [ locStr s["Request"] ]
|
|
||||||
]
|
]
|
||||||
]
|
space
|
||||||
tbody [] requests
|
submit [] "search" s["Search"]
|
||||||
]
|
]
|
||||||
div [ _class "pt-center-text" ] [
|
br []
|
||||||
br []
|
tableSummary requests.Length s
|
||||||
match m.OnlyActive with
|
match requests.Length with
|
||||||
| Some true ->
|
| 0 -> ()
|
||||||
raw l["Inactive requests are currently not shown"]
|
| _ ->
|
||||||
br []
|
table [ _class "pt-table pt-action-table" ] [
|
||||||
a [ _href "/prayer-requests/inactive" ] [ raw l["Show Inactive Requests"] ]
|
thead [] [
|
||||||
| _ ->
|
tr [] [
|
||||||
if defaultArg m.OnlyActive false then
|
th [] [ locStr s["Actions"] ]
|
||||||
raw l["Inactive requests are currently shown"]
|
th [] [ locStr s["Updated Date"] ]
|
||||||
br []
|
th [] [ locStr s["Type"] ]
|
||||||
a [ _href "/prayer-requests" ] [ raw l["Do Not Show Inactive Requests"] ]
|
th [] [ locStr s["Requestor"] ]
|
||||||
br []
|
th [] [ locStr s["Request"] ]
|
||||||
br []
|
]
|
||||||
let search = [ match m.SearchTerm with Some s -> "search", s | None -> () ]
|
]
|
||||||
let pg = defaultArg m.PageNbr 1
|
tbody [] requests
|
||||||
let url =
|
]
|
||||||
match m.OnlyActive with Some true | None -> "" | _ -> "/inactive" |> sprintf "/prayer-requests%s"
|
div [ _class "pt-center-text" ] [
|
||||||
match pg with
|
br []
|
||||||
| 1 -> ()
|
match m.OnlyActive with
|
||||||
| _ ->
|
| Some true ->
|
||||||
// button (_type "submit" :: attrs) [ icon ico; rawText " "; locStr text ]
|
raw l["Inactive requests are currently not shown"]
|
||||||
let withPage = match pg with 2 -> search | _ -> ("page", string (pg - 1)) :: search
|
br []
|
||||||
a [ _href (makeUrl url withPage) ]
|
a [ _href "/prayer-requests/inactive" ] [ raw l["Show Inactive Requests"] ]
|
||||||
[ icon "keyboard_arrow_left"; space; raw l["Previous Page"] ]
|
| _ ->
|
||||||
rawText " "
|
if defaultArg m.OnlyActive false then
|
||||||
match requests.Length = m.SmallGroup.preferences.pageSize with
|
raw l["Inactive requests are currently shown"]
|
||||||
| true ->
|
br []
|
||||||
a [ _href (makeUrl url (("page", string (pg + 1)) :: search)) ]
|
a [ _href "/prayer-requests" ] [ raw l["Do Not Show Inactive Requests"] ]
|
||||||
[ raw l["Next Page"]; space; icon "keyboard_arrow_right" ]
|
br []
|
||||||
| false -> ()
|
br []
|
||||||
]
|
let search = [ match m.SearchTerm with Some s -> "search", s | None -> () ]
|
||||||
form [ _id "DeleteForm"; _action ""; _method "post" ] [ csrfToken ctx ]
|
let pg = defaultArg m.PageNbr 1
|
||||||
|
let url =
|
||||||
|
match m.OnlyActive with Some true | None -> "" | _ -> "/inactive"
|
||||||
|
|> sprintf "/prayer-requests%s"
|
||||||
|
match pg with
|
||||||
|
| 1 -> ()
|
||||||
|
| _ ->
|
||||||
|
// button (_type "submit" :: attrs) [ icon ico; rawText " "; locStr text ]
|
||||||
|
let withPage = match pg with 2 -> search | _ -> ("page", string (pg - 1)) :: search
|
||||||
|
a [ _href (makeUrl url withPage) ] [ icon "keyboard_arrow_left"; space; raw l["Previous Page"] ]
|
||||||
|
rawText " "
|
||||||
|
match requests.Length = m.SmallGroup.preferences.pageSize with
|
||||||
|
| true ->
|
||||||
|
a [ _href (makeUrl url (("page", string (pg + 1)) :: search)) ] [
|
||||||
|
raw l["Next Page"]; space; icon "keyboard_arrow_right"
|
||||||
|
]
|
||||||
|
| false -> ()
|
||||||
|
]
|
||||||
|
form [ _id "DeleteForm"; _action ""; _method "post" ] [ csrfToken ctx ]
|
||||||
]
|
]
|
||||||
|> Layout.Content.wide
|
|> Layout.Content.wide
|
||||||
|> Layout.standard vi (match m.SearchTerm with Some _ -> "Search Results" | None -> "Maintain Requests")
|
|> Layout.standard vi (match m.SearchTerm with Some _ -> "Search Results" | None -> "Maintain Requests")
|
||||||
|
@ -315,38 +328,40 @@ let view m vi =
|
||||||
let pageTitle = $"""{s["Prayer Requests"].Value} • {m.SmallGroup.name}"""
|
let pageTitle = $"""{s["Prayer Requests"].Value} • {m.SmallGroup.name}"""
|
||||||
let spacer = rawText " "
|
let spacer = rawText " "
|
||||||
let dtString = m.Date.ToString "yyyy-MM-dd"
|
let dtString = m.Date.ToString "yyyy-MM-dd"
|
||||||
[ div [ _class "pt-center-text" ] [
|
[ div [ _class "pt-center-text" ] [
|
||||||
br []
|
br []
|
||||||
a [ _class "pt-icon-link"
|
a [ _class "pt-icon-link"
|
||||||
_href $"/prayer-requests/print/{dtString}"
|
_href $"/prayer-requests/print/{dtString}"
|
||||||
_title s["View Printable"].Value
|
_target "_blank"
|
||||||
] [ icon "print"; rawText " "; locStr s["View Printable"] ]
|
_title s["View Printable"].Value ] [
|
||||||
if m.CanEmail then
|
icon "print"; rawText " "; locStr s["View Printable"]
|
||||||
spacer
|
]
|
||||||
if m.Date.DayOfWeek <> DayOfWeek.Sunday then
|
if m.CanEmail then
|
||||||
let rec findSunday (date : DateTime) =
|
spacer
|
||||||
if date.DayOfWeek = DayOfWeek.Sunday then date else findSunday (date.AddDays 1.)
|
if m.Date.DayOfWeek <> DayOfWeek.Sunday then
|
||||||
let sunday = findSunday m.Date
|
let rec findSunday (date : DateTime) =
|
||||||
a [ _class "pt-icon-link"
|
if date.DayOfWeek = DayOfWeek.Sunday then date else findSunday (date.AddDays 1.)
|
||||||
_href $"""/prayer-requests/view/{sunday.ToString "yyyy-MM-dd"}"""
|
let sunday = findSunday m.Date
|
||||||
_title s["List for Next Sunday"].Value ] [
|
a [ _class "pt-icon-link"
|
||||||
icon "update"; rawText " "; locStr s["List for Next Sunday"]
|
_href $"""/prayer-requests/view/{sunday.ToString "yyyy-MM-dd"}"""
|
||||||
]
|
_title s["List for Next Sunday"].Value ] [
|
||||||
spacer
|
icon "update"; rawText " "; locStr s["List for Next Sunday"]
|
||||||
let emailPrompt = s["This will e-mail the current list to every member of your group, without further prompting. Are you sure this is what you are ready to do?"].Value
|
]
|
||||||
a [ _class "pt-icon-link"
|
spacer
|
||||||
_href $"/prayer-requests/email/{dtString}"
|
let emailPrompt = s["This will e-mail the current list to every member of your group, without further prompting. Are you sure this is what you are ready to do?"].Value
|
||||||
_title s["Send via E-mail"].Value
|
a [ _class "pt-icon-link"
|
||||||
_onclick $"return PT.requests.view.promptBeforeEmail('{emailPrompt}')" ] [
|
_href $"/prayer-requests/email/{dtString}"
|
||||||
icon "mail_outline"; rawText " "; locStr s["Send via E-mail"]
|
_title s["Send via E-mail"].Value
|
||||||
|
_onclick $"return PT.requests.view.promptBeforeEmail('{emailPrompt}')" ] [
|
||||||
|
icon "mail_outline"; rawText " "; locStr s["Send via E-mail"]
|
||||||
|
]
|
||||||
|
spacer
|
||||||
|
a [ _class "pt-icon-link"; _href "/prayer-requests"; _title s["Maintain Prayer Requests"].Value ] [
|
||||||
|
icon "compare_arrows"; rawText " "; locStr s["Maintain Prayer Requests"]
|
||||||
]
|
]
|
||||||
spacer
|
|
||||||
a [ _class "pt-icon-link"; _href "/prayer-requests"; _title s["Maintain Prayer Requests"].Value ] [
|
|
||||||
icon "compare_arrows"; rawText " "; locStr s["Maintain Prayer Requests"]
|
|
||||||
]
|
]
|
||||||
]
|
br []
|
||||||
br []
|
rawText (m.AsHtml s)
|
||||||
rawText (m.AsHtml s)
|
|
||||||
]
|
]
|
||||||
|> Layout.Content.standard
|
|> Layout.Content.standard
|
||||||
|> Layout.standard vi pageTitle
|
|> Layout.standard vi pageTitle
|
||||||
|
|
|
@ -147,7 +147,7 @@
|
||||||
<data name="If you utilize the “{0}” box on sign in, a second cookie is stored, and transmitted to establish a session; this cookie is removed by clicking the “{1}” link." xml:space="preserve">
|
<data name="If you utilize the “{0}” box on sign in, a second cookie is stored, and transmitted to establish a session; this cookie is removed by clicking the “{1}” link." xml:space="preserve">
|
||||||
<value>Si utilizas el cuadro "{0}" al iniciar sesión, se almacena una segunda cookie y se transmite para establecer una sesión; esta cookie se elimina haciendo clic en el enlace "{1}".</value>
|
<value>Si utilizas el cuadro "{0}" al iniciar sesión, se almacena una segunda cookie y se transmite para establecer una sesión; esta cookie se elimina haciendo clic en el enlace "{1}".</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="It also stores names and e-mail addreses of small group members, and plain-text passwords for small groups with password-protected lists." xml:space="preserve">
|
<data name="It also stores names and e-mail addresses of small group members, and plain-text passwords for small groups with password-protected lists." xml:space="preserve">
|
||||||
<value>También almacena nombres y direcciones de correo electrónico de miembros de grupos pequeños, y contraseñas de texto sin formato para grupos pequeños con listas protegidas por contraseña.</value>
|
<value>También almacena nombres y direcciones de correo electrónico de miembros de grupos pequeños, y contraseñas de texto sin formato para grupos pequeños con listas protegidas por contraseña.</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="On the server, all data is stored in a controlled-access database." xml:space="preserve">
|
<data name="On the server, all data is stored in a controlled-access database." xml:space="preserve">
|
||||||
|
|
|
@ -1,9 +1,7 @@
|
||||||
module PrayerTracker.Views.SmallGroup
|
module PrayerTracker.Views.SmallGroup
|
||||||
|
|
||||||
open System.IO
|
|
||||||
open Giraffe.ViewEngine
|
open Giraffe.ViewEngine
|
||||||
open Microsoft.Extensions.Localization
|
open Microsoft.Extensions.Localization
|
||||||
open PrayerTracker
|
|
||||||
open PrayerTracker.Entities
|
open PrayerTracker.Entities
|
||||||
open PrayerTracker.ViewModels
|
open PrayerTracker.ViewModels
|
||||||
|
|
||||||
|
@ -12,45 +10,45 @@ let announcement isAdmin ctx vi =
|
||||||
let s = I18N.localizer.Force ()
|
let s = I18N.localizer.Force ()
|
||||||
let m = { SendToClass = ""; Text = ""; AddToRequestList = None; RequestType = None }
|
let m = { SendToClass = ""; Text = ""; AddToRequestList = None; RequestType = None }
|
||||||
let reqTypes = ReferenceList.requestTypeList s
|
let reqTypes = ReferenceList.requestTypeList s
|
||||||
[ form [ _action "/small-group/announcement/send"; _method "post"; _class "pt-center-columns" ] [
|
[ form [ _action "/small-group/announcement/send"; _method "post"; _class "pt-center-columns"; Target.content ] [
|
||||||
csrfToken ctx
|
csrfToken ctx
|
||||||
div [ _class "pt-field-row" ] [
|
|
||||||
div [ _class "pt-field pt-editor" ] [
|
|
||||||
label [ _for "text" ] [ locStr s["Announcement Text"] ]
|
|
||||||
textarea [ _name (nameof m.Text); _id "text"; _autofocus ] []
|
|
||||||
]
|
|
||||||
]
|
|
||||||
if isAdmin then
|
|
||||||
div [ _class "pt-field-row" ] [
|
div [ _class "pt-field-row" ] [
|
||||||
div [ _class "pt-field" ] [
|
div [ _class "pt-field pt-editor" ] [
|
||||||
label [] [ locStr s["Send Announcement to"]; rawText ":" ]
|
label [ _for "text" ] [ locStr s["Announcement Text"] ]
|
||||||
div [ _class "pt-center-text" ] [
|
textarea [ _name (nameof m.Text); _id "text"; _autofocus ] []
|
||||||
radio (nameof m.SendToClass) "sendY" "Y" "Y"
|
|
||||||
label [ _for "sendY" ] [ locStr s["This Group"]; rawText " " ]
|
|
||||||
radio (nameof m.SendToClass) "sendN" "N" "Y"
|
|
||||||
label [ _for "sendN" ] [ locStr s["All {0} Users", s["PrayerTracker"]] ]
|
|
||||||
]
|
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
else input [ _type "hidden"; _name (nameof m.SendToClass); _value "Y" ]
|
if isAdmin then
|
||||||
div [ _class "pt-field-row pt-fadeable pt-shown"; _id "divAddToList" ] [
|
div [ _class "pt-field-row" ] [
|
||||||
div [ _class "pt-checkbox-field" ] [
|
div [ _class "pt-field" ] [
|
||||||
input [ _type "checkbox"; _name (nameof m.AddToRequestList); _id "addToRequestList"; _value "True" ]
|
label [] [ locStr s["Send Announcement to"]; rawText ":" ]
|
||||||
label [ _for "addToRequestList" ] [ locStr s["Add to Request List"] ]
|
div [ _class "pt-center-text" ] [
|
||||||
|
radio (nameof m.SendToClass) "sendY" "Y" "Y"
|
||||||
|
label [ _for "sendY" ] [ locStr s["This Group"]; rawText " " ]
|
||||||
|
radio (nameof m.SendToClass) "sendN" "N" "Y"
|
||||||
|
label [ _for "sendN" ] [ locStr s["All {0} Users", s["PrayerTracker"]] ]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
else input [ _type "hidden"; _name (nameof m.SendToClass); _value "Y" ]
|
||||||
|
div [ _class "pt-field-row pt-fadeable pt-shown"; _id "divAddToList" ] [
|
||||||
|
div [ _class "pt-checkbox-field" ] [
|
||||||
|
input [ _type "checkbox"; _name (nameof m.AddToRequestList); _id "addToRequestList"; _value "True" ]
|
||||||
|
label [ _for "addToRequestList" ] [ locStr s["Add to Request List"] ]
|
||||||
|
]
|
||||||
]
|
]
|
||||||
]
|
div [ _class "pt-field-row pt-fadeable"; _id "divCategory" ] [
|
||||||
div [ _class "pt-field-row pt-fadeable"; _id "divCategory" ] [
|
div [ _class "pt-field" ] [
|
||||||
div [ _class "pt-field" ] [
|
label [ _for (nameof m.RequestType) ] [ locStr s["Request Type"] ]
|
||||||
label [ _for (nameof m.RequestType) ] [ locStr s["Request Type"] ]
|
reqTypes
|
||||||
reqTypes
|
|> Seq.ofList
|
||||||
|> Seq.ofList
|
|> Seq.map (fun (typ, desc) -> typ.code, desc.Value)
|
||||||
|> Seq.map (fun (typ, desc) -> typ.code, desc.Value)
|
|> selectList (nameof m.RequestType) Announcement.code []
|
||||||
|> selectList (nameof m.RequestType) Announcement.code []
|
]
|
||||||
]
|
]
|
||||||
|
div [ _class "pt-field-row" ] [ submit [] "send" s["Send Announcement"] ]
|
||||||
]
|
]
|
||||||
div [ _class "pt-field-row" ] [ submit [] "send" s["Send Announcement"] ]
|
script [] [ rawText "PT.onLoad(PT.smallGroup.announcement.onPageLoad)" ]
|
||||||
]
|
|
||||||
script [] [ rawText "PT.onLoad(PT.smallGroup.announcement.onPageLoad)" ]
|
|
||||||
]
|
]
|
||||||
|> Layout.Content.standard
|
|> Layout.Content.standard
|
||||||
|> Layout.standard vi "Send Announcement"
|
|> Layout.standard vi "Send Announcement"
|
||||||
|
@ -59,12 +57,12 @@ let announcement isAdmin ctx vi =
|
||||||
/// View for once an announcement has been sent
|
/// View for once an announcement has been sent
|
||||||
let announcementSent (m : Announcement) vi =
|
let announcementSent (m : Announcement) vi =
|
||||||
let s = I18N.localizer.Force ()
|
let s = I18N.localizer.Force ()
|
||||||
[ span [ _class "pt-email-heading" ] [ locStr s["HTML Format"]; rawText ":" ]
|
[ span [ _class "pt-email-heading" ] [ locStr s["HTML Format"]; rawText ":" ]
|
||||||
div [ _class "pt-email-canvas" ] [ rawText m.Text ]
|
div [ _class "pt-email-canvas" ] [ rawText m.Text ]
|
||||||
br []
|
br []
|
||||||
br []
|
br []
|
||||||
span [ _class "pt-email-heading" ] [ locStr s["Plain-Text Format"]; rawText ":" ]
|
span [ _class "pt-email-heading" ] [ locStr s["Plain-Text Format"]; rawText ":" ]
|
||||||
div [ _class "pt-email-canvas" ] [ pre [] [ str m.PlainText ] ]
|
div [ _class "pt-email-canvas" ] [ pre [] [ str m.PlainText ] ]
|
||||||
]
|
]
|
||||||
|> Layout.Content.standard
|
|> Layout.Content.standard
|
||||||
|> Layout.standard vi "Announcement Sent"
|
|> Layout.standard vi "Announcement Sent"
|
||||||
|
@ -74,7 +72,7 @@ let announcementSent (m : Announcement) vi =
|
||||||
let edit (m : EditSmallGroup) (churches : Church list) ctx vi =
|
let edit (m : EditSmallGroup) (churches : Church list) ctx vi =
|
||||||
let s = I18N.localizer.Force ()
|
let s = I18N.localizer.Force ()
|
||||||
let pageTitle = if m.IsNew then "Add a New Group" else "Edit Group"
|
let pageTitle = if m.IsNew then "Add a New Group" else "Edit Group"
|
||||||
form [ _action "/small-group/save"; _method "post"; _class "pt-center-columns" ] [
|
form [ _action "/small-group/save"; _method "post"; _class "pt-center-columns"; Target.content ] [
|
||||||
csrfToken ctx
|
csrfToken ctx
|
||||||
input [ _type "hidden"; _name (nameof m.SmallGroupId); _value (flatGuid m.SmallGroupId) ]
|
input [ _type "hidden"; _name (nameof m.SmallGroupId); _value (flatGuid m.SmallGroupId) ]
|
||||||
div [ _class "pt-field-row" ] [
|
div [ _class "pt-field-row" ] [
|
||||||
|
@ -104,7 +102,7 @@ let edit (m : EditSmallGroup) (churches : Church list) ctx vi =
|
||||||
let editMember (m : EditMember) (types : (string * LocalizedString) seq) ctx vi =
|
let editMember (m : EditMember) (types : (string * LocalizedString) seq) ctx vi =
|
||||||
let s = I18N.localizer.Force ()
|
let s = I18N.localizer.Force ()
|
||||||
let pageTitle = if m.IsNew then "Add a New Group Member" else "Edit Group Member"
|
let pageTitle = if m.IsNew then "Add a New Group Member" else "Edit Group Member"
|
||||||
form [ _action "/small-group/member/save"; _method "post"; _class "pt-center-columns" ] [
|
form [ _action "/small-group/member/save"; _method "post"; _class "pt-center-columns"; Target.content ] [
|
||||||
style [ _scoped ] [ rawText "#name { width: 15rem; } #email { width: 20rem; }" ]
|
style [ _scoped ] [ rawText "#name { width: 15rem; } #email { width: 20rem; }" ]
|
||||||
csrfToken ctx
|
csrfToken ctx
|
||||||
input [ _type "hidden"; _name (nameof m.MemberId); _value (flatGuid m.MemberId) ]
|
input [ _type "hidden"; _name (nameof m.MemberId); _value (flatGuid m.MemberId) ]
|
||||||
|
@ -137,40 +135,39 @@ let editMember (m : EditMember) (types : (string * LocalizedString) seq) ctx vi
|
||||||
let logOn (groups : SmallGroup list) grpId ctx vi =
|
let logOn (groups : SmallGroup list) grpId ctx vi =
|
||||||
let s = I18N.localizer.Force ()
|
let s = I18N.localizer.Force ()
|
||||||
let m = { SmallGroupId = System.Guid.Empty; Password = ""; RememberMe = None }
|
let m = { SmallGroupId = System.Guid.Empty; Password = ""; RememberMe = None }
|
||||||
[ form [ _action "/small-group/log-on/submit"; _method "post"; _class "pt-center-columns" ] [
|
[ form [ _action "/small-group/log-on/submit"; _method "post"; _class "pt-center-columns"; Target.body ] [
|
||||||
csrfToken ctx
|
csrfToken ctx
|
||||||
div [ _class "pt-field-row" ] [
|
div [ _class "pt-field-row" ] [
|
||||||
div [ _class "pt-field" ] [
|
div [ _class "pt-field" ] [
|
||||||
label [ _for (nameof m.SmallGroupId) ] [ locStr s["Group"] ]
|
label [ _for (nameof m.SmallGroupId) ] [ locStr s["Group"] ]
|
||||||
seq {
|
seq {
|
||||||
match groups.Length with
|
if groups.Length = 0 then "", s["There are no classes with passwords defined"].Value
|
||||||
| 0 -> "", s["There are no classes with passwords defined"].Value
|
else
|
||||||
| _ ->
|
"", selectDefault s["Select Group"].Value
|
||||||
"", selectDefault s["Select Group"].Value
|
yield!
|
||||||
yield!
|
groups
|
||||||
groups
|
|> List.map (fun grp -> flatGuid grp.smallGroupId, $"{grp.church.name} | {grp.name}")
|
||||||
|> List.map (fun grp -> flatGuid grp.smallGroupId, $"{grp.church.name} | {grp.name}")
|
}
|
||||||
}
|
|> selectList (nameof m.SmallGroupId) grpId [ _required ]
|
||||||
|> selectList (nameof m.SmallGroupId) grpId [ _required ]
|
]
|
||||||
|
div [ _class "pt-field" ] [
|
||||||
|
label [ _for "password" ] [ locStr s["Password"] ]
|
||||||
|
input [ _type "password"
|
||||||
|
_name (nameof m.Password)
|
||||||
|
_id "password"
|
||||||
|
_placeholder (s["Case-Sensitive"].Value.ToLower ())
|
||||||
|
_required ]
|
||||||
|
]
|
||||||
]
|
]
|
||||||
div [ _class "pt-field" ] [
|
div [ _class "pt-checkbox-field" ] [
|
||||||
label [ _for "password" ] [ locStr s["Password"] ]
|
input [ _type "checkbox"; _name (nameof m.RememberMe); _id "rememberMe"; _value "True" ]
|
||||||
input [ _type "password"
|
label [ _for "rememberMe" ] [ locStr s["Remember Me"] ]
|
||||||
_name (nameof m.Password)
|
br []
|
||||||
_id "password"
|
small [] [ em [] [ str (s["Requires Cookies"].Value.ToLower ()) ] ]
|
||||||
_required;
|
|
||||||
_placeholder (s["Case-Sensitive"].Value.ToLower ()) ]
|
|
||||||
]
|
]
|
||||||
|
div [ _class "pt-field-row" ] [ submit [] "account_circle" s["Log On"] ]
|
||||||
]
|
]
|
||||||
div [ _class "pt-checkbox-field" ] [
|
script [] [ rawText "PT.onLoad(PT.smallGroup.logOn.onPageLoad)" ]
|
||||||
input [ _type "checkbox"; _name (nameof m.RememberMe); _id "rememberMe"; _value "True" ]
|
|
||||||
label [ _for "rememberMe" ] [ locStr s["Remember Me"] ]
|
|
||||||
br []
|
|
||||||
small [] [ em [] [ str (s["Requires Cookies"].Value.ToLower ()) ] ]
|
|
||||||
]
|
|
||||||
div [ _class "pt-field-row" ] [ submit [] "account_circle" s["Log On"] ]
|
|
||||||
]
|
|
||||||
script [] [ rawText "PT.onLoad(PT.smallGroup.logOn.onPageLoad)" ]
|
|
||||||
]
|
]
|
||||||
|> Layout.Content.standard
|
|> Layout.Content.standard
|
||||||
|> Layout.standard vi "Group Log On"
|
|> Layout.standard vi "Group Log On"
|
||||||
|
@ -200,12 +197,12 @@ let maintain (groups : SmallGroup list) ctx vi =
|
||||||
$"""{s["Small Group"].Value.ToLower ()} ({g.name})""" ].Value
|
$"""{s["Small Group"].Value.ToLower ()} ({g.name})""" ].Value
|
||||||
tr [] [
|
tr [] [
|
||||||
td [] [
|
td [] [
|
||||||
a [ _href $"/small-group/{grpId}/edit"; _title s["Edit This Group"].Value ]
|
a [ _href $"/small-group/{grpId}/edit"; _title s["Edit This Group"].Value ] [ icon "edit" ]
|
||||||
[ icon "edit" ]
|
a [ _href delAction
|
||||||
a [ _href delAction
|
_title s["Delete This Group"].Value
|
||||||
_title s["Delete This Group"].Value
|
_onclick $"return PT.confirmDelete('{delAction}','{delPrompt}')" ] [
|
||||||
_onclick $"return PT.confirmDelete('{delAction}','{delPrompt}')" ]
|
icon "delete_forever"
|
||||||
[ icon "delete_forever" ]
|
]
|
||||||
]
|
]
|
||||||
td [] [ str g.name ]
|
td [] [ str g.name ]
|
||||||
td [] [ str g.church.name ]
|
td [] [ str g.church.name ]
|
||||||
|
@ -213,19 +210,17 @@ let maintain (groups : SmallGroup list) ctx vi =
|
||||||
])
|
])
|
||||||
|> tbody []
|
|> tbody []
|
||||||
]
|
]
|
||||||
[ div [ _class "pt-center-text" ] [
|
[ div [ _class "pt-center-text" ] [
|
||||||
br []
|
br []
|
||||||
a [ _href $"/small-group/{emptyGuid}/edit"; _title s["Add a New Group"].Value ] [
|
a [ _href $"/small-group/{emptyGuid}/edit"; _title s["Add a New Group"].Value ] [
|
||||||
icon "add_circle"
|
icon "add_circle"; rawText " "; locStr s["Add a New Group"]
|
||||||
rawText " "
|
]
|
||||||
locStr s["Add a New Group"]
|
br []
|
||||||
]
|
br []
|
||||||
br []
|
]
|
||||||
br []
|
tableSummary groups.Length s
|
||||||
]
|
grpTbl
|
||||||
tableSummary groups.Length s
|
form [ _id "DeleteForm"; _action ""; _method "post" ] [ csrfToken ctx ]
|
||||||
grpTbl
|
|
||||||
form [ _id "DeleteForm"; _action ""; _method "post" ] [ csrfToken ctx ]
|
|
||||||
]
|
]
|
||||||
|> Layout.Content.standard
|
|> Layout.Content.standard
|
||||||
|> Layout.standard vi "Maintain Groups"
|
|> Layout.standard vi "Maintain Groups"
|
||||||
|
@ -256,12 +251,14 @@ let members (members : Member list) (emailTyps : Map<string, LocalizedString>) c
|
||||||
.Value.Replace("?", $" ({mbr.memberName})?")
|
.Value.Replace("?", $" ({mbr.memberName})?")
|
||||||
tr [] [
|
tr [] [
|
||||||
td [] [
|
td [] [
|
||||||
a [ _href $"/small-group/member/{mbrId}/edit"; _title s["Edit This Group Member"].Value ]
|
a [ _href $"/small-group/member/{mbrId}/edit"; _title s["Edit This Group Member"].Value ] [
|
||||||
[ icon "edit" ]
|
icon "edit"
|
||||||
a [ _href delAction
|
]
|
||||||
_title s["Delete This Group Member"].Value
|
a [ _href delAction
|
||||||
_onclick $"return PT.confirmDelete('{delAction}','{delPrompt}')" ]
|
_title s["Delete This Group Member"].Value
|
||||||
[ icon "delete_forever" ]
|
_onclick $"return PT.confirmDelete('{delAction}','{delPrompt}')" ] [
|
||||||
|
icon "delete_forever"
|
||||||
|
]
|
||||||
]
|
]
|
||||||
td [] [ str mbr.memberName ]
|
td [] [ str mbr.memberName ]
|
||||||
td [] [ str mbr.email ]
|
td [] [ str mbr.email ]
|
||||||
|
@ -269,73 +266,64 @@ let members (members : Member list) (emailTyps : Map<string, LocalizedString>) c
|
||||||
])
|
])
|
||||||
|> tbody []
|
|> tbody []
|
||||||
]
|
]
|
||||||
[ div [ _class"pt-center-text" ] [
|
[ div [ _class"pt-center-text" ] [
|
||||||
br []
|
br []
|
||||||
a [ _href $"/small-group/member/{emptyGuid}/edit"; _title s["Add a New Group Member"].Value ]
|
a [ _href $"/small-group/member/{emptyGuid}/edit"; _title s["Add a New Group Member"].Value ] [
|
||||||
[ icon "add_circle"; rawText " "; locStr s["Add a New Group Member"] ]
|
icon "add_circle"; rawText " "; locStr s["Add a New Group Member"]
|
||||||
br []
|
]
|
||||||
br []
|
br []
|
||||||
]
|
br []
|
||||||
tableSummary members.Length s
|
]
|
||||||
mbrTbl
|
tableSummary members.Length s
|
||||||
form [ _id "DeleteForm"; _action ""; _method "post" ] [ csrfToken ctx ]
|
mbrTbl
|
||||||
|
form [ _id "DeleteForm"; _action ""; _method "post" ] [ csrfToken ctx ]
|
||||||
]
|
]
|
||||||
|> Layout.Content.standard
|
|> Layout.Content.standard
|
||||||
|> Layout.standard vi "Maintain Group Members"
|
|> Layout.standard vi "Maintain Group Members"
|
||||||
|
|
||||||
|
|
||||||
|
open Giraffe.ViewEngine.Accessibility
|
||||||
|
|
||||||
/// View for the small group overview page
|
/// View for the small group overview page
|
||||||
let overview m vi =
|
let overview m vi =
|
||||||
let s = I18N.localizer.Force ()
|
let s = I18N.localizer.Force ()
|
||||||
let linkSpacer = rawText " "
|
let linkSpacer = rawText " "
|
||||||
let types = ReferenceList.requestTypeList s |> dict
|
let types = ReferenceList.requestTypeList s |> dict
|
||||||
article [ _class "pt-overview" ] [
|
article [ _class "pt-overview" ] [
|
||||||
section [] [
|
section [ _ariaLabel "Quick actions" ] [
|
||||||
header [ _role "heading" ] [
|
header [ _roleHeading ] [ iconSized 72 "bookmark_border"; locStr s["Quick Actions"] ]
|
||||||
iconSized 72 "bookmark_border"
|
|
||||||
locStr s["Quick Actions"]
|
|
||||||
]
|
|
||||||
div [] [
|
div [] [
|
||||||
a [ _href "/prayer-requests/view" ]
|
a [ _href "/prayer-requests/view" ] [ icon "list"; linkSpacer; locStr s["View Prayer Request List"] ]
|
||||||
[ icon "list"; linkSpacer; locStr s["View Prayer Request List"] ]
|
|
||||||
hr []
|
hr []
|
||||||
a [ _href "/small-group/announcement" ] [ icon "send"; linkSpacer; locStr s["Send Announcement"] ]
|
a [ _href "/small-group/announcement" ] [ icon "send"; linkSpacer; locStr s["Send Announcement"] ]
|
||||||
hr []
|
hr []
|
||||||
a [ _href "/small-group/preferences" ] [ icon "build"; linkSpacer; locStr s["Change Preferences"] ]
|
a [ _href "/small-group/preferences" ] [ icon "build"; linkSpacer; locStr s["Change Preferences"] ]
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
section [] [
|
section [ _ariaLabel "Prayer requests" ] [
|
||||||
header [ _role "heading" ] [
|
header [ _roleHeading ] [ iconSized 72 "question_answer"; locStr s["Prayer Requests"] ]
|
||||||
iconSized 72 "question_answer"
|
|
||||||
locStr s["Prayer Requests"]
|
|
||||||
]
|
|
||||||
div [] [
|
div [] [
|
||||||
p [ _class "pt-center-text" ] [
|
p [ _class "pt-center-text" ] [
|
||||||
strong [] [ str (m.TotalActiveReqs.ToString "N0"); space; locStr s["Active Requests"] ]
|
strong [] [ str (m.TotalActiveReqs.ToString "N0"); space; locStr s["Active Requests"] ]
|
||||||
]
|
]
|
||||||
hr []
|
hr []
|
||||||
for cat in m.ActiveReqsByType do
|
for cat in m.ActiveReqsByType do
|
||||||
str (cat.Value.ToString "N0")
|
str (cat.Value.ToString "N0")
|
||||||
space
|
space
|
||||||
locStr types[cat.Key]
|
locStr types[cat.Key]
|
||||||
br []
|
br []
|
||||||
br []
|
br []
|
||||||
str (m.AllReqs.ToString "N0")
|
str (m.AllReqs.ToString "N0")
|
||||||
space
|
space
|
||||||
locStr s["Total Requests"]
|
locStr s["Total Requests"]
|
||||||
hr []
|
hr []
|
||||||
a [ _href "/prayer-requests/maintain" ] [
|
a [ _href "/prayer-requests/maintain" ] [
|
||||||
icon "compare_arrows"
|
icon "compare_arrows"; linkSpacer; locStr s["Maintain Prayer Requests"]
|
||||||
linkSpacer
|
|
||||||
locStr s["Maintain Prayer Requests"]
|
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
section [] [
|
section [ _ariaLabel "Small group members" ] [
|
||||||
header [ _role "heading" ] [
|
header [ _roleHeading ] [ iconSized 72 "people_outline"; locStr s["Group Members"] ]
|
||||||
iconSized 72 "people_outline"
|
|
||||||
locStr s["Group Members"]
|
|
||||||
]
|
|
||||||
div [ _class "pt-center-text" ] [
|
div [ _class "pt-center-text" ] [
|
||||||
strong [] [ str (m.TotalMembers.ToString "N0"); space; locStr s["Members"] ]
|
strong [] [ str (m.TotalMembers.ToString "N0"); space; locStr s["Members"] ]
|
||||||
hr []
|
hr []
|
||||||
|
@ -348,240 +336,246 @@ let overview m vi =
|
||||||
|> Layout.standard vi "Small Group Overview"
|
|> Layout.standard vi "Small Group Overview"
|
||||||
|
|
||||||
|
|
||||||
|
open System.IO
|
||||||
|
open PrayerTracker
|
||||||
|
|
||||||
/// View for the small group preferences page
|
/// View for the small group preferences page
|
||||||
let preferences (m : EditPreferences) (tzs : TimeZone list) ctx vi =
|
let preferences (m : EditPreferences) (tzs : TimeZone list) ctx vi =
|
||||||
let s = I18N.localizer.Force ()
|
let s = I18N.localizer.Force ()
|
||||||
let l = I18N.forView "SmallGroup/Preferences"
|
let l = I18N.forView "SmallGroup/Preferences"
|
||||||
use sw = new StringWriter ()
|
use sw = new StringWriter ()
|
||||||
let raw = rawLocText sw
|
let raw = rawLocText sw
|
||||||
[ form [ _action "/small-group/preferences/save"; _method "post"; _class "pt-center-columns" ] [
|
[ style [ _scoped ] [
|
||||||
style [ _scoped ]
|
rawText "#expireDays, #daysToKeepNew, #longTermUpdateWeeks, #headingFontSize, #listFontSize, #pageSize { width: 3rem; } #emailFromAddress { width: 20rem; } #fonts { width: 40rem; } @media screen and (max-width: 40rem) { #fonts { width: 100%; } }"
|
||||||
[ rawText "#expireDays, #daysToKeepNew, #longTermUpdateWeeks, #headingFontSize, #listFontSize, #pageSize { width: 3rem; } #emailFromAddress { width: 20rem; } #fonts { width: 40rem; } @media screen and (max-width: 40rem) { #fonts { width: 100%; } }" ]
|
]
|
||||||
csrfToken ctx
|
form [ _action "/small-group/preferences/save"; _method "post"; _class "pt-center-columns"; Target.content ] [
|
||||||
fieldset [] [
|
csrfToken ctx
|
||||||
legend [] [ strong [] [ icon "date_range"; rawText " "; locStr s["Dates"] ] ]
|
fieldset [] [
|
||||||
div [ _class "pt-field-row" ] [
|
legend [] [ strong [] [ icon "date_range"; rawText " "; locStr s["Dates"] ] ]
|
||||||
div [ _class "pt-field" ] [
|
div [ _class "pt-field-row" ] [
|
||||||
label [ _for "expireDays" ] [ locStr s["Requests Expire After"] ]
|
div [ _class "pt-field" ] [
|
||||||
span [] [
|
label [ _for "expireDays" ] [ locStr s["Requests Expire After"] ]
|
||||||
input [ _type "number"
|
span [] [
|
||||||
_name (nameof m.ExpireDays)
|
input [ _type "number"
|
||||||
_id "expireDays"
|
_name (nameof m.ExpireDays)
|
||||||
_min "1"; _max "30"
|
_id "expireDays"
|
||||||
_required
|
_value (string m.ExpireDays)
|
||||||
_autofocus
|
_min "1"; _max "30"
|
||||||
_value (string m.ExpireDays) ]
|
_required
|
||||||
space; str (s["Days"].Value.ToLower ())
|
_autofocus ]
|
||||||
|
space
|
||||||
|
str (s["Days"].Value.ToLower ())
|
||||||
|
]
|
||||||
]
|
]
|
||||||
]
|
div [ _class "pt-field" ] [
|
||||||
div [ _class "pt-field" ] [
|
label [ _for "daysToKeepNew" ] [ locStr s["Requests “New” For"] ]
|
||||||
label [ _for "daysToKeepNew" ] [ locStr s["Requests “New” For"] ]
|
span [] [
|
||||||
span [] [
|
input [ _type "number"
|
||||||
input [ _type "number"
|
_name (nameof m.DaysToKeepNew)
|
||||||
_name (nameof m.DaysToKeepNew)
|
_id "daysToKeepNew"
|
||||||
_id "daysToKeepNew"
|
_min "1"; _max "30"
|
||||||
_min "1"; _max "30"
|
_value (string m.DaysToKeepNew)
|
||||||
_required
|
_required ]
|
||||||
_value (string m.DaysToKeepNew) ]
|
space; str (s["Days"].Value.ToLower ())
|
||||||
space; str (s["Days"].Value.ToLower ())
|
]
|
||||||
]
|
]
|
||||||
]
|
div [ _class "pt-field" ] [
|
||||||
div [ _class "pt-field" ] [
|
label [ _for "longTermUpdateWeeks" ] [ locStr s["Long-Term Requests Alerted for Update"] ]
|
||||||
label [ _for "longTermUpdateWeeks" ] [ locStr s["Long-Term Requests Alerted for Update"] ]
|
span [] [
|
||||||
span [] [
|
input [ _type "number"
|
||||||
input [ _type "number"
|
_name (nameof m.LongTermUpdateWeeks)
|
||||||
_name (nameof m.LongTermUpdateWeeks)
|
_id "longTermUpdateWeeks"
|
||||||
_id "longTermUpdateWeeks"
|
_min "1"; _max "30"
|
||||||
_min "1"; _max "30"
|
_value (string m.LongTermUpdateWeeks)
|
||||||
_required
|
_required ]
|
||||||
_value (string m.LongTermUpdateWeeks) ]
|
space; str (s["Weeks"].Value.ToLower ())
|
||||||
space; str (s["Weeks"].Value.ToLower ())
|
]
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
]
|
fieldset [] [
|
||||||
fieldset [] [
|
legend [] [ strong [] [ icon "sort"; rawText " "; locStr s["Request Sorting"] ] ]
|
||||||
legend [] [ strong [] [ icon "sort"; rawText " "; locStr s["Request Sorting"] ] ]
|
radio (nameof m.RequestSort) "requestSort_D" "D" m.RequestSort
|
||||||
radio (nameof m.RequestSort) "requestSort_D" "D" m.RequestSort
|
label [ _for "requestSort_D" ] [ locStr s["Sort by Last Updated Date"] ]
|
||||||
label [ _for "requestSort_D" ] [ locStr s["Sort by Last Updated Date"] ]
|
rawText " "
|
||||||
rawText " "
|
radio (nameof m.RequestSort) "requestSort_R" "R" m.RequestSort
|
||||||
radio (nameof m.RequestSort) "requestSort_R" "R" m.RequestSort
|
label [ _for "requestSort_R" ] [ locStr s["Sort by Requestor Name"] ]
|
||||||
label [ _for "requestSort_R" ] [ locStr s["Sort by Requestor Name"] ]
|
|
||||||
]
|
|
||||||
fieldset [] [
|
|
||||||
legend [] [ strong [] [ icon "mail_outline"; rawText " "; locStr s["E-mail"] ] ]
|
|
||||||
div [ _class "pt-field-row" ] [
|
|
||||||
div [ _class "pt-field" ] [
|
|
||||||
label [ _for "emailFromName" ] [ locStr s["From Name"] ]
|
|
||||||
input [ _type "text"
|
|
||||||
_name (nameof m.EmailFromName)
|
|
||||||
_id "emailFromName"
|
|
||||||
_required
|
|
||||||
_value m.EmailFromName ]
|
|
||||||
]
|
|
||||||
div [ _class "pt-field" ] [
|
|
||||||
label [ _for "emailFromAddress" ] [ locStr s["From Address"] ]
|
|
||||||
input [ _type "email"
|
|
||||||
_name (nameof m.EmailFromAddress)
|
|
||||||
_id "emailFromAddress"
|
|
||||||
_required
|
|
||||||
_value m.EmailFromAddress ]
|
|
||||||
]
|
|
||||||
]
|
]
|
||||||
div [ _class "pt-field-row" ] [
|
fieldset [] [
|
||||||
div [ _class "pt-field" ] [
|
legend [] [ strong [] [ icon "mail_outline"; rawText " "; locStr s["E-mail"] ] ]
|
||||||
label [ _for (nameof m.DefaultEmailType) ] [ locStr s["E-mail Format"] ]
|
div [ _class "pt-field-row" ] [
|
||||||
seq {
|
div [ _class "pt-field" ] [
|
||||||
"", selectDefault s["Select"].Value
|
label [ _for "emailFromName" ] [ locStr s["From Name"] ]
|
||||||
yield!
|
input [ _type "text"
|
||||||
ReferenceList.emailTypeList HtmlFormat s
|
_name (nameof m.EmailFromName)
|
||||||
|> Seq.skip 1
|
_id "emailFromName"
|
||||||
|> Seq.map (fun typ -> fst typ, (snd typ).Value)
|
_value m.EmailFromName
|
||||||
}
|
_required ]
|
||||||
|> selectList (nameof m.DefaultEmailType) m.DefaultEmailType [ _required ]
|
]
|
||||||
|
div [ _class "pt-field" ] [
|
||||||
|
label [ _for "emailFromAddress" ] [ locStr s["From Address"] ]
|
||||||
|
input [ _type "email"
|
||||||
|
_name (nameof m.EmailFromAddress)
|
||||||
|
_id "emailFromAddress"
|
||||||
|
_value m.EmailFromAddress
|
||||||
|
_required ]
|
||||||
|
]
|
||||||
]
|
]
|
||||||
]
|
div [ _class "pt-field-row" ] [
|
||||||
]
|
div [ _class "pt-field" ] [
|
||||||
fieldset [] [
|
label [ _for (nameof m.DefaultEmailType) ] [ locStr s["E-mail Format"] ]
|
||||||
legend [] [ strong [] [ icon "color_lens"; rawText " "; locStr s["Colors"] ]; rawText " ***" ]
|
seq {
|
||||||
div [ _class "pt-field-row" ] [
|
"", selectDefault s["Select"].Value
|
||||||
div [ _class "pt-field" ] [
|
yield!
|
||||||
label [ _class "pt-center-text" ] [ locStr s["Color of Heading Lines"] ]
|
ReferenceList.emailTypeList HtmlFormat s
|
||||||
span [] [
|
|> Seq.skip 1
|
||||||
radio (nameof m.LineColorType) "lineColorType_Name" "Name" m.LineColorType
|
|> Seq.map (fun typ -> fst typ, (snd typ).Value)
|
||||||
label [ _for "lineColorType_Name" ] [ locStr s["Named Color"] ]
|
}
|
||||||
namedColorList (nameof m.LineColor) m.LineColor
|
|> selectList (nameof m.DefaultEmailType) m.DefaultEmailType [ _required ]
|
||||||
[ _id "lineColor_Select"
|
|
||||||
if m.LineColor.StartsWith "#" then _disabled ] s
|
|
||||||
rawText " "; str (s["or"].Value.ToUpper ())
|
|
||||||
radio (nameof m.LineColorType) "lineColorType_RGB" "RGB" m.LineColorType
|
|
||||||
label [ _for "lineColorType_RGB" ] [ locStr s["Custom Color"] ]
|
|
||||||
input [ _type "color"
|
|
||||||
_name (nameof m.LineColor)
|
|
||||||
_id "lineColor_Color"
|
|
||||||
_value m.LineColor // TODO: convert to hex or skip if named
|
|
||||||
if not (m.LineColor.StartsWith "#") then _disabled ]
|
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
div [ _class "pt-field-row" ] [
|
fieldset [] [
|
||||||
|
legend [] [ strong [] [ icon "color_lens"; rawText " "; locStr s["Colors"] ]; rawText " ***" ]
|
||||||
|
div [ _class "pt-field-row" ] [
|
||||||
|
div [ _class "pt-field" ] [
|
||||||
|
label [ _class "pt-center-text" ] [ locStr s["Color of Heading Lines"] ]
|
||||||
|
span [] [
|
||||||
|
radio (nameof m.LineColorType) "lineColorType_Name" "Name" m.LineColorType
|
||||||
|
label [ _for "lineColorType_Name" ] [ locStr s["Named Color"] ]
|
||||||
|
namedColorList (nameof m.LineColor) m.LineColor [
|
||||||
|
_id "lineColor_Select"
|
||||||
|
if m.LineColor.StartsWith "#" then _disabled ] s
|
||||||
|
rawText " "; str (s["or"].Value.ToUpper ())
|
||||||
|
radio (nameof m.LineColorType) "lineColorType_RGB" "RGB" m.LineColorType
|
||||||
|
label [ _for "lineColorType_RGB" ] [ locStr s["Custom Color"] ]
|
||||||
|
input [ _type "color"
|
||||||
|
_name (nameof m.LineColor)
|
||||||
|
_id "lineColor_Color"
|
||||||
|
_value m.LineColor // TODO: convert to hex or skip if named
|
||||||
|
if not (m.LineColor.StartsWith "#") then _disabled ]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
div [ _class "pt-field-row" ] [
|
||||||
|
div [ _class "pt-field" ] [
|
||||||
|
label [ _class "pt-center-text" ] [ locStr s["Color of Heading Text"] ]
|
||||||
|
span [] [
|
||||||
|
radio (nameof m.HeadingColorType) "headingColorType_Name" "Name" m.HeadingColorType
|
||||||
|
label [ _for "headingColorType_Name" ] [ locStr s["Named Color"] ]
|
||||||
|
namedColorList (nameof m.HeadingColor) m.HeadingColor [
|
||||||
|
_id "headingColor_Select"
|
||||||
|
if m.HeadingColor.StartsWith "#" then _disabled ] s
|
||||||
|
rawText " "; str (s["or"].Value.ToUpper ())
|
||||||
|
radio (nameof m.HeadingColorType) "headingColorType_RGB" "RGB" m.HeadingColorType
|
||||||
|
label [ _for "headingColorType_RGB" ] [ locStr s["Custom Color"] ]
|
||||||
|
input [ _type "color"
|
||||||
|
_name (nameof m.HeadingColor)
|
||||||
|
_id "headingColor_Color"
|
||||||
|
_value m.HeadingColor // TODO: convert to hex or skip if named
|
||||||
|
if not (m.HeadingColor.StartsWith "#") then _disabled ]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
fieldset [] [
|
||||||
|
legend [] [ strong [] [ icon "font_download"; rawText " "; locStr s["Fonts"] ] ]
|
||||||
div [ _class "pt-field" ] [
|
div [ _class "pt-field" ] [
|
||||||
label [ _class "pt-center-text" ] [ locStr s["Color of Heading Text"] ]
|
label [ _for "fonts" ] [ locStr s["Fonts** for List"] ]
|
||||||
|
input [ _type "text"; _name (nameof m.Fonts); _id "fonts"; _required; _value m.Fonts ]
|
||||||
|
]
|
||||||
|
div [ _class "pt-field-row" ] [
|
||||||
|
div [ _class "pt-field" ] [
|
||||||
|
label [ _for "headingFontSize" ] [ locStr s["Heading Text Size"] ]
|
||||||
|
input [ _type "number"
|
||||||
|
_name (nameof m.HeadingFontSize)
|
||||||
|
_id "headingFontSize"
|
||||||
|
_min "8"; _max "24"
|
||||||
|
_value (string m.HeadingFontSize)
|
||||||
|
_required ]
|
||||||
|
]
|
||||||
|
div [ _class "pt-field" ] [
|
||||||
|
label [ _for "listFontSize" ] [ locStr s["List Text Size"] ]
|
||||||
|
input [ _type "number"
|
||||||
|
_name (nameof m.ListFontSize)
|
||||||
|
_id "listFontSize"
|
||||||
|
_min "8"; _max "24"
|
||||||
|
_value (string m.ListFontSize)
|
||||||
|
_required ]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
fieldset [] [
|
||||||
|
legend [] [ strong [] [ icon "settings"; rawText " "; locStr s["Other Settings"] ] ]
|
||||||
|
div [ _class "pt-field-row" ] [
|
||||||
|
div [ _class "pt-field" ] [
|
||||||
|
label [ _for (nameof m.TimeZone) ] [ locStr s["Time Zone"] ]
|
||||||
|
seq {
|
||||||
|
"", selectDefault s["Select"].Value
|
||||||
|
yield! tzs |> List.map (fun tz -> tz.timeZoneId, (TimeZones.name tz.timeZoneId s).Value)
|
||||||
|
}
|
||||||
|
|> selectList (nameof m.TimeZone) m.TimeZone [ _required ]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
div [ _class "pt-field" ] [
|
||||||
|
label [] [ locStr s["Request List Visibility"] ]
|
||||||
span [] [
|
span [] [
|
||||||
radio (nameof m.HeadingColorType) "headingColorType_Name" "Name" m.HeadingColorType
|
radio (nameof m.Visibility) "viz_Public" (string RequestVisibility.``public``)
|
||||||
label [ _for "headingColorType_Name" ] [ locStr s["Named Color"] ]
|
(string m.Visibility)
|
||||||
namedColorList (nameof m.HeadingColor) m.HeadingColor
|
label [ _for "viz_Public" ] [ locStr s["Public"] ]
|
||||||
[ _id "headingColor_Select"
|
rawText " "
|
||||||
if m.HeadingColor.StartsWith "#" then _disabled ] s
|
radio (nameof m.Visibility) "viz_Private" (string RequestVisibility.``private``)
|
||||||
rawText " "; str (s["or"].Value.ToUpper ())
|
(string m.Visibility)
|
||||||
radio (nameof m.HeadingColorType) "headingColorType_RGB" "RGB" m.HeadingColorType
|
label [ _for "viz_Private" ] [ locStr s["Private"] ]
|
||||||
label [ _for "headingColorType_RGB" ] [ locStr s["Custom Color"] ]
|
rawText " "
|
||||||
input [ _type "color"
|
radio (nameof m.Visibility) "viz_Password" (string RequestVisibility.passwordProtected)
|
||||||
_name (nameof m.HeadingColor)
|
(string m.Visibility)
|
||||||
_id "headingColor_Color"
|
label [ _for "viz_Password" ] [ locStr s["Password Protected"] ]
|
||||||
_value m.HeadingColor // TODO: convert to hex or skip if named
|
]
|
||||||
if not (m.HeadingColor.StartsWith "#") then _disabled ]
|
]
|
||||||
]
|
let classSuffix = if m.Visibility = RequestVisibility.passwordProtected then " pt-show" else ""
|
||||||
|
div [ _id "divClassPassword"; _class $"pt-field-row pt-fadeable{classSuffix}" ] [
|
||||||
|
div [ _class "pt-field" ] [
|
||||||
|
label [ _for "groupPassword" ] [ locStr s["Group Password (Used to Read Online)"] ]
|
||||||
|
input [ _type "text"
|
||||||
|
_name (nameof m.GroupPassword)
|
||||||
|
_id "groupPassword"
|
||||||
|
_value (defaultArg m.GroupPassword "") ]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
div [ _class "pt-field-row" ] [
|
||||||
|
div [ _class "pt-field" ] [
|
||||||
|
label [ _for "pageSize" ] [ locStr s["Page Size"] ]
|
||||||
|
input [ _type "number"
|
||||||
|
_name (nameof m.PageSize)
|
||||||
|
_id "pageSize"
|
||||||
|
_min "10"; _max "255"
|
||||||
|
_value (string m.PageSize)
|
||||||
|
_required ]
|
||||||
|
]
|
||||||
|
div [ _class "pt-field" ] [
|
||||||
|
label [ _for (nameof m.AsOfDate) ] [ locStr s["“As of” Date Display"] ]
|
||||||
|
ReferenceList.asOfDateList s
|
||||||
|
|> List.map (fun (code, desc) -> code, desc.Value)
|
||||||
|
|> selectList (nameof m.AsOfDate) m.AsOfDate [ _required ]
|
||||||
|
]
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
|
div [ _class "pt-field-row" ] [ submit [] "save" s["Save Preferences"] ]
|
||||||
]
|
]
|
||||||
fieldset [] [
|
p [] [
|
||||||
legend [] [ strong [] [ icon "font_download"; rawText " "; locStr s["Fonts"] ] ]
|
rawText "** "
|
||||||
div [ _class "pt-field" ] [
|
raw l["List font names, separated by commas."]
|
||||||
label [ _for "fonts" ] [ locStr s["Fonts** for List"] ]
|
space
|
||||||
input [ _type "text"; _name (nameof m.Fonts); _id "fonts"; _required; _value m.Fonts ]
|
raw l["The first font that is matched is the one that is used."]
|
||||||
]
|
space
|
||||||
div [ _class "pt-field-row" ] [
|
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."]
|
||||||
div [ _class "pt-field" ] [
|
|
||||||
label [ _for "headingFontSize" ] [ locStr s["Heading Text Size"] ]
|
|
||||||
input [ _type "number"
|
|
||||||
_name (nameof m.HeadingFontSize)
|
|
||||||
_id "headingFontSize"
|
|
||||||
_min "8"; _max "24"
|
|
||||||
_required
|
|
||||||
_value (string m.HeadingFontSize) ]
|
|
||||||
]
|
|
||||||
div [ _class "pt-field" ] [
|
|
||||||
label [ _for "listFontSize" ] [ locStr s["List Text Size"] ]
|
|
||||||
input [ _type "number"
|
|
||||||
_name (nameof m.ListFontSize)
|
|
||||||
_id "listFontSize"
|
|
||||||
_min "8"; _max "24"
|
|
||||||
_required
|
|
||||||
_value (string m.ListFontSize) ]
|
|
||||||
]
|
|
||||||
]
|
|
||||||
]
|
]
|
||||||
fieldset [] [
|
p [] [
|
||||||
legend [] [ strong [] [ icon "settings"; rawText " "; locStr s["Other Settings"] ] ]
|
rawText "*** "
|
||||||
div [ _class "pt-field-row" ] [
|
raw l["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 <a href=\"http://www.w3schools.com/html/html_colornames.asp\" title=\"HTML Color List - W3 School\">HTML color name list</a>."]
|
||||||
div [ _class "pt-field" ] [
|
|
||||||
label [ _for (nameof m.TimeZone) ] [ locStr s["Time Zone"] ]
|
|
||||||
seq {
|
|
||||||
"", selectDefault s["Select"].Value
|
|
||||||
yield! tzs |> List.map (fun tz -> tz.timeZoneId, (TimeZones.name tz.timeZoneId s).Value)
|
|
||||||
}
|
|
||||||
|> selectList (nameof m.TimeZone) m.TimeZone [ _required ]
|
|
||||||
]
|
|
||||||
]
|
|
||||||
div [ _class "pt-field" ] [
|
|
||||||
label [] [ locStr s["Request List Visibility"] ]
|
|
||||||
span [] [
|
|
||||||
radio (nameof m.Visibility) "viz_Public" (string RequestVisibility.``public``) (string m.Visibility)
|
|
||||||
label [ _for "viz_Public" ] [ locStr s["Public"] ]
|
|
||||||
rawText " "
|
|
||||||
radio (nameof m.Visibility) "viz_Private" (string RequestVisibility.``private``)
|
|
||||||
(string m.Visibility)
|
|
||||||
label [ _for "viz_Private" ] [ locStr s["Private"] ]
|
|
||||||
rawText " "
|
|
||||||
radio (nameof m.Visibility) "viz_Password" (string RequestVisibility.passwordProtected)
|
|
||||||
(string m.Visibility)
|
|
||||||
label [ _for "viz_Password" ] [ locStr s["Password Protected"] ]
|
|
||||||
]
|
|
||||||
]
|
|
||||||
let classSuffix = if m.Visibility = RequestVisibility.passwordProtected then " pt-show" else ""
|
|
||||||
div [ _id "divClassPassword"; _class $"pt-field-row pt-fadeable{classSuffix}" ] [
|
|
||||||
div [ _class "pt-field" ] [
|
|
||||||
label [ _for "groupPassword" ] [ locStr s["Group Password (Used to Read Online)"] ]
|
|
||||||
input [ _type "text"
|
|
||||||
_name (nameof m.GroupPassword)
|
|
||||||
_id "groupPassword"
|
|
||||||
_value (defaultArg m.GroupPassword "") ]
|
|
||||||
]
|
|
||||||
]
|
|
||||||
div [ _class "pt-field-row" ] [
|
|
||||||
div [ _class "pt-field" ] [
|
|
||||||
label [ _for "pageSize" ] [ locStr s["Page Size"] ]
|
|
||||||
input [ _type "number"
|
|
||||||
_name (nameof m.PageSize)
|
|
||||||
_id "pageSize"
|
|
||||||
_min "10"; _max "255"
|
|
||||||
_required
|
|
||||||
_value (string m.PageSize) ]
|
|
||||||
]
|
|
||||||
div [ _class "pt-field" ] [
|
|
||||||
label [ _for (nameof m.AsOfDate) ] [ locStr s["“As of” Date Display"] ]
|
|
||||||
ReferenceList.asOfDateList s
|
|
||||||
|> List.map (fun (code, desc) -> code, desc.Value)
|
|
||||||
|> selectList (nameof m.AsOfDate) m.AsOfDate [ _required ]
|
|
||||||
]
|
|
||||||
]
|
|
||||||
]
|
]
|
||||||
div [ _class "pt-field-row" ] [ submit [] "save" s["Save Preferences"] ]
|
script [] [ rawText "PT.onLoad(PT.smallGroup.preferences.onPageLoad)" ]
|
||||||
]
|
|
||||||
p [] [
|
|
||||||
rawText "** "
|
|
||||||
raw l["List font names, separated by commas."]
|
|
||||||
space
|
|
||||||
raw l["The first font that is matched is the one that is used."]
|
|
||||||
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."]
|
|
||||||
]
|
|
||||||
p [] [
|
|
||||||
rawText "*** "
|
|
||||||
raw l["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 <a href=\"http://www.w3schools.com/html/html_colornames.asp\" title=\"HTML Color List - W3 School\">HTML color name list</a>."]
|
|
||||||
]
|
|
||||||
script [] [ rawText "PT.onLoad(PT.smallGroup.preferences.onPageLoad)" ]
|
|
||||||
]
|
]
|
||||||
|> Layout.Content.standard
|
|> Layout.Content.standard
|
||||||
|> Layout.standard vi "Group Preferences"
|
|> Layout.standard vi "Group Preferences"
|
||||||
|
|
|
@ -1,14 +1,13 @@
|
||||||
module PrayerTracker.Views.User
|
module PrayerTracker.Views.User
|
||||||
|
|
||||||
open Giraffe.ViewEngine
|
open Giraffe.ViewEngine
|
||||||
open PrayerTracker.Entities
|
|
||||||
open PrayerTracker.ViewModels
|
open PrayerTracker.ViewModels
|
||||||
|
|
||||||
/// View for the group assignment page
|
/// View for the group assignment page
|
||||||
let assignGroups m groups curGroups ctx vi =
|
let assignGroups m groups curGroups ctx vi =
|
||||||
let s = I18N.localizer.Force ()
|
let s = I18N.localizer.Force ()
|
||||||
let pageTitle = sprintf "%s • %A" m.UserName s["Assign Groups"]
|
let pageTitle = sprintf "%s • %A" m.UserName s["Assign Groups"]
|
||||||
form [ _action "/user/small-groups/save"; _method "post"; _class "pt-center-columns" ] [
|
form [ _action "/user/small-groups/save"; _method "post"; _class "pt-center-columns"; Target.content ] [
|
||||||
csrfToken ctx
|
csrfToken ctx
|
||||||
input [ _type "hidden"; _name (nameof m.UserId); _value (flatGuid m.UserId) ]
|
input [ _type "hidden"; _name (nameof m.UserId); _value (flatGuid m.UserId) ]
|
||||||
input [ _type "hidden"; _name (nameof m.UserName); _value m.UserName ]
|
input [ _type "hidden"; _name (nameof m.UserName); _value m.UserName ]
|
||||||
|
@ -24,9 +23,9 @@ let assignGroups m groups curGroups ctx vi =
|
||||||
let inputId = $"id-{grpId}"
|
let inputId = $"id-{grpId}"
|
||||||
tr [] [
|
tr [] [
|
||||||
td [] [
|
td [] [
|
||||||
input [ _type "checkbox"
|
input [ _type "checkbox"
|
||||||
_name (nameof m.SmallGroups)
|
_name (nameof m.SmallGroups)
|
||||||
_id inputId
|
_id inputId
|
||||||
_value grpId
|
_value grpId
|
||||||
if List.contains grpId curGroups then _checked ]
|
if List.contains grpId curGroups then _checked ]
|
||||||
]
|
]
|
||||||
|
@ -45,35 +44,36 @@ let assignGroups m groups curGroups ctx vi =
|
||||||
let changePassword ctx vi =
|
let changePassword ctx vi =
|
||||||
let s = I18N.localizer.Force ()
|
let s = I18N.localizer.Force ()
|
||||||
let m = { OldPassword = ""; NewPassword = ""; NewPasswordConfirm = "" }
|
let m = { OldPassword = ""; NewPassword = ""; NewPasswordConfirm = "" }
|
||||||
[ p [ _class "pt-center-text" ] [
|
[ p [ _class "pt-center-text" ] [
|
||||||
locStr s["To change your password, enter your current password in the specified box below, then enter your new password twice."]
|
locStr s["To change your password, enter your current password in the specified box below, then enter your new password twice."]
|
||||||
]
|
]
|
||||||
form [ _action "/user/password/change"
|
style [ _scoped ] [ rawText "#oldPassword, #newPassword, #newPasswordConfirm { width: 10rem; } "]
|
||||||
_method "post"
|
form [ _action "/user/password/change"
|
||||||
_onsubmit $"""return PT.compareValidation('newPassword','newPasswordConfirm','%A{s["The passwords do not match"]}')""" ] [
|
_method "post"
|
||||||
style [ _scoped ] [ rawText "#oldPassword, #newPassword, #newPasswordConfirm { width: 10rem; } "]
|
_onsubmit $"""return PT.compareValidation('newPassword','newPasswordConfirm','%A{s["The passwords do not match"]}')"""
|
||||||
csrfToken ctx
|
Target.content ] [
|
||||||
div [ _class "pt-field-row" ] [
|
csrfToken ctx
|
||||||
div [ _class "pt-field" ] [
|
div [ _class "pt-field-row" ] [
|
||||||
label [ _for "oldPassword" ] [ locStr s["Current Password"] ]
|
div [ _class "pt-field" ] [
|
||||||
input [ _type "password"; _name (nameof m.OldPassword); _id "oldPassword"; _required; _autofocus ]
|
label [ _for "oldPassword" ] [ locStr s["Current Password"] ]
|
||||||
]
|
input [ _type "password"; _name (nameof m.OldPassword); _id "oldPassword"; _required; _autofocus ]
|
||||||
]
|
]
|
||||||
div [ _class "pt-field-row" ] [
|
]
|
||||||
div [ _class "pt-field" ] [
|
div [ _class "pt-field-row" ] [
|
||||||
label [ _for "newPassword" ] [ locStr s["New Password Twice"] ]
|
div [ _class "pt-field" ] [
|
||||||
input [ _type "password"; _name (nameof m.NewPassword); _id "newPassword"; _required ]
|
label [ _for "newPassword" ] [ locStr s["New Password Twice"] ]
|
||||||
]
|
input [ _type "password"; _name (nameof m.NewPassword); _id "newPassword"; _required ]
|
||||||
div [ _class "pt-field" ] [
|
]
|
||||||
label [] [ rawText " " ]
|
div [ _class "pt-field" ] [
|
||||||
input [ _type "password"; _name (nameof m.NewPasswordConfirm); _id "newPasswordConfirm"; _required ]
|
label [] [ rawText " " ]
|
||||||
]
|
input [ _type "password"; _name (nameof m.NewPasswordConfirm); _id "newPasswordConfirm"; _required ]
|
||||||
]
|
]
|
||||||
div [ _class "pt-field-row" ] [
|
]
|
||||||
submit [ _onclick "document.getElementById('newPasswordConfirm').setCustomValidity('')" ] "done"
|
div [ _class "pt-field-row" ] [
|
||||||
s["Change Your Password"]
|
submit [ _onclick "document.getElementById('newPasswordConfirm').setCustomValidity('')" ] "done"
|
||||||
]
|
s["Change Your Password"]
|
||||||
]
|
]
|
||||||
|
]
|
||||||
]
|
]
|
||||||
|> Layout.Content.standard
|
|> Layout.Content.standard
|
||||||
|> Layout.standard vi "Change Your Password"
|
|> Layout.standard vi "Change Your Password"
|
||||||
|
@ -84,55 +84,58 @@ let edit (m : EditUser) ctx vi =
|
||||||
let s = I18N.localizer.Force ()
|
let s = I18N.localizer.Force ()
|
||||||
let pageTitle = if m.IsNew then "Add a New User" else "Edit User"
|
let pageTitle = if m.IsNew then "Add a New User" else "Edit User"
|
||||||
let pwPlaceholder = s[if m.IsNew then "" else "No change"].Value
|
let pwPlaceholder = s[if m.IsNew then "" else "No change"].Value
|
||||||
[ form [ _action "/user/edit/save"; _method "post"; _class "pt-center-columns"
|
[ style [ _scoped ]
|
||||||
_onsubmit $"""return PT.compareValidation('password','passwordConfirm','%A{s["The passwords do not match"]}')""" ] [
|
|
||||||
style [ _scoped ]
|
|
||||||
[ rawText "#firstName, #lastName, #password, #passwordConfirm { width: 10rem; } #email { width: 20rem; } " ]
|
[ rawText "#firstName, #lastName, #password, #passwordConfirm { width: 10rem; } #email { width: 20rem; } " ]
|
||||||
csrfToken ctx
|
form [ _action "/user/edit/save"
|
||||||
input [ _type "hidden"; _name (nameof m.UserId); _value (flatGuid m.UserId) ]
|
_method "post"
|
||||||
div [ _class "pt-field-row" ] [
|
_class "pt-center-columns"
|
||||||
div [ _class "pt-field" ] [
|
_onsubmit $"""return PT.compareValidation('password','passwordConfirm','%A{s["The passwords do not match"]}')"""
|
||||||
label [ _for "firstName" ] [ locStr s["First Name"] ]
|
Target.content ] [
|
||||||
input [ _type "text"
|
csrfToken ctx
|
||||||
_name (nameof m.FirstName)
|
input [ _type "hidden"; _name (nameof m.UserId); _value (flatGuid m.UserId) ]
|
||||||
_id "firstName"
|
div [ _class "pt-field-row" ] [
|
||||||
_value m.FirstName
|
div [ _class "pt-field" ] [
|
||||||
_required
|
label [ _for "firstName" ] [ locStr s["First Name"] ]
|
||||||
_autofocus ]
|
input [ _type "text"
|
||||||
|
_name (nameof m.FirstName)
|
||||||
|
_id "firstName"
|
||||||
|
_value m.FirstName
|
||||||
|
_required
|
||||||
|
_autofocus ]
|
||||||
|
]
|
||||||
|
div [ _class "pt-field" ] [
|
||||||
|
label [ _for "lastName" ] [ locStr s["Last Name"] ]
|
||||||
|
input [ _type "text"; _name (nameof m.LastName); _id "lastName"; _value m.LastName; _required ]
|
||||||
|
]
|
||||||
|
div [ _class "pt-field" ] [
|
||||||
|
label [ _for "email" ] [ locStr s["E-mail Address"] ]
|
||||||
|
input [ _type "email"; _name (nameof m.Email); _id "email"; _value m.Email; _required ]
|
||||||
|
]
|
||||||
]
|
]
|
||||||
div [ _class "pt-field" ] [
|
div [ _class "pt-field-row" ] [
|
||||||
label [ _for "lastName" ] [ locStr s["Last Name"] ]
|
div [ _class "pt-field" ] [
|
||||||
input [ _type "text"; _name (nameof m.LastName); _id "lastName"; _value m.LastName; _required ]
|
label [ _for "password" ] [ locStr s["Password"] ]
|
||||||
|
input [ _type "password"; _name (nameof m.Password); _id "password"; _placeholder pwPlaceholder ]
|
||||||
|
]
|
||||||
|
div [ _class "pt-field" ] [
|
||||||
|
label [ _for "passwordConfirm" ] [ locStr s["Password Again"] ]
|
||||||
|
input [ _type "password"
|
||||||
|
_name (nameof m.PasswordConfirm)
|
||||||
|
_id "passwordConfirm"
|
||||||
|
_placeholder pwPlaceholder ]
|
||||||
|
]
|
||||||
]
|
]
|
||||||
div [ _class "pt-field" ] [
|
div [ _class "pt-checkbox-field" ] [
|
||||||
label [ _for "email" ] [ locStr s["E-mail Address"] ]
|
input [ _type "checkbox"
|
||||||
input [ _type "email"; _name (nameof m.Email); _id "email"; _value m.Email; _required ]
|
_name (nameof m.IsAdmin)
|
||||||
|
_id "isAdmin"
|
||||||
|
_value "True"
|
||||||
|
if defaultArg m.IsAdmin false then _checked ]
|
||||||
|
label [ _for "isAdmin" ] [ locStr s["This user is a PrayerTracker administrator"] ]
|
||||||
]
|
]
|
||||||
|
div [ _class "pt-field-row" ] [ submit [] "save" s["Save User"] ]
|
||||||
]
|
]
|
||||||
div [ _class "pt-field-row" ] [
|
script [] [ rawText $"PT.onLoad(PT.user.edit.onPageLoad({(string m.IsNew).ToLowerInvariant ()}))" ]
|
||||||
div [ _class "pt-field" ] [
|
|
||||||
label [ _for "password" ] [ locStr s["Password"] ]
|
|
||||||
input [ _type "password"; _name (nameof m.Password); _id "password"; _placeholder pwPlaceholder ]
|
|
||||||
]
|
|
||||||
div [ _class "pt-field" ] [
|
|
||||||
label [ _for "passwordConfirm" ] [ locStr s["Password Again"] ]
|
|
||||||
input [ _type "password"
|
|
||||||
_name (nameof m.PasswordConfirm)
|
|
||||||
_id "passwordConfirm"
|
|
||||||
_placeholder pwPlaceholder ]
|
|
||||||
]
|
|
||||||
]
|
|
||||||
div [ _class "pt-checkbox-field" ] [
|
|
||||||
input [ _type "checkbox"
|
|
||||||
_name (nameof m.IsAdmin)
|
|
||||||
_id "isAdmin"
|
|
||||||
_value "True"
|
|
||||||
if defaultArg m.IsAdmin false then _checked ]
|
|
||||||
label [ _for "isAdmin" ] [ locStr s["This user is a PrayerTracker administrator"] ]
|
|
||||||
]
|
|
||||||
div [ _class "pt-field-row" ] [ submit [] "save" s["Save User"] ]
|
|
||||||
]
|
|
||||||
script [] [ rawText $"PT.onLoad(PT.user.edit.onPageLoad({(string m.IsNew).ToLowerInvariant ()}))" ]
|
|
||||||
]
|
]
|
||||||
|> Layout.Content.standard
|
|> Layout.Content.standard
|
||||||
|> Layout.standard vi pageTitle
|
|> Layout.standard vi pageTitle
|
||||||
|
@ -141,7 +144,7 @@ let edit (m : EditUser) ctx vi =
|
||||||
/// View for the user log on page
|
/// View for the user log on page
|
||||||
let logOn (m : UserLogOn) groups ctx vi =
|
let logOn (m : UserLogOn) groups ctx vi =
|
||||||
let s = I18N.localizer.Force ()
|
let s = I18N.localizer.Force ()
|
||||||
form [ _action "/user/log-on"; _method "post"; _class "pt-center-columns" ] [
|
form [ _action "/user/log-on"; _method "post"; _class "pt-center-columns"; Target.body ] [
|
||||||
style [ _scoped ] [ rawText "#email { width: 20rem; }" ]
|
style [ _scoped ] [ rawText "#email { width: 20rem; }" ]
|
||||||
csrfToken ctx
|
csrfToken ctx
|
||||||
input [ _type "hidden"; _name (nameof m.RedirectUrl); _value (defaultArg m.RedirectUrl "") ]
|
input [ _type "hidden"; _name (nameof m.RedirectUrl); _value (defaultArg m.RedirectUrl "") ]
|
||||||
|
@ -152,20 +155,17 @@ let logOn (m : UserLogOn) groups ctx vi =
|
||||||
]
|
]
|
||||||
div [ _class "pt-field" ] [
|
div [ _class "pt-field" ] [
|
||||||
label [ _for "password" ] [ locStr s["Password"] ]
|
label [ _for "password" ] [ locStr s["Password"] ]
|
||||||
input [ _type "password"
|
input [ _type "password"
|
||||||
_name (nameof m.Password)
|
_name (nameof m.Password)
|
||||||
_id "password"
|
_id "password"
|
||||||
_required;
|
_placeholder $"""({s["Case-Sensitive"].Value.ToLower ()})"""
|
||||||
_placeholder (sprintf "(%s)" (s["Case-Sensitive"].Value.ToLower ())) ]
|
_required ]
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
div [ _class "pt-field-row" ] [
|
div [ _class "pt-field-row" ] [
|
||||||
div [ _class "pt-field" ] [
|
div [ _class "pt-field" ] [
|
||||||
label [ _for (nameof m.SmallGroupId) ] [ locStr s["Group"] ]
|
label [ _for (nameof m.SmallGroupId) ] [ locStr s["Group"] ]
|
||||||
seq {
|
seq { "", selectDefault s["Select Group"].Value; yield! groups }
|
||||||
"", selectDefault s["Select Group"].Value
|
|
||||||
yield! groups
|
|
||||||
}
|
|
||||||
|> selectList (nameof m.SmallGroupId) "" [ _required ]
|
|> selectList (nameof m.SmallGroupId) "" [ _required ]
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
|
@ -173,7 +173,7 @@ let logOn (m : UserLogOn) groups ctx vi =
|
||||||
input [ _type "checkbox"; _name (nameof m.RememberMe); _id "rememberMe"; _value "True" ]
|
input [ _type "checkbox"; _name (nameof m.RememberMe); _id "rememberMe"; _value "True" ]
|
||||||
label [ _for "rememberMe" ] [ locStr s["Remember Me"] ]
|
label [ _for "rememberMe" ] [ locStr s["Remember Me"] ]
|
||||||
br []
|
br []
|
||||||
small [] [ em [] [ rawText "("; str (s["Requires Cookies"].Value.ToLower ()); rawText ")" ] ]
|
small [] [ em [] [ str $"""({s["Requires Cookies"].Value.ToLower ()})""" ] ]
|
||||||
]
|
]
|
||||||
div [ _class "pt-field-row" ] [ submit [] "account_circle" s["Log On"] ]
|
div [ _class "pt-field-row" ] [ submit [] "account_circle" s["Log On"] ]
|
||||||
]
|
]
|
||||||
|
@ -182,6 +182,8 @@ let logOn (m : UserLogOn) groups ctx vi =
|
||||||
|> Layout.standard vi "User Log On"
|
|> Layout.standard vi "User Log On"
|
||||||
|
|
||||||
|
|
||||||
|
open PrayerTracker.Entities
|
||||||
|
|
||||||
/// View for the user maintenance page
|
/// View for the user maintenance page
|
||||||
let maintain (users : User list) ctx vi =
|
let maintain (users : User list) ctx vi =
|
||||||
let s = I18N.localizer.Force ()
|
let s = I18N.localizer.Force ()
|
||||||
|
@ -206,12 +208,14 @@ let maintain (users : User list) ctx vi =
|
||||||
tr [] [
|
tr [] [
|
||||||
td [] [
|
td [] [
|
||||||
a [ _href $"/user/{userId}/edit"; _title s["Edit This User"].Value ] [ icon "edit" ]
|
a [ _href $"/user/{userId}/edit"; _title s["Edit This User"].Value ] [ icon "edit" ]
|
||||||
a [ _href $"/user/{userId}/small-groups"; _title s["Assign Groups to This User"].Value ]
|
a [ _href $"/user/{userId}/small-groups"; _title s["Assign Groups to This User"].Value ] [
|
||||||
[ icon "group" ]
|
icon "group"
|
||||||
a [ _href delAction
|
]
|
||||||
_title s["Delete This User"].Value
|
a [ _href delAction
|
||||||
_onclick $"return PT.confirmDelete('{delAction}','{delPrompt}')" ]
|
_title s["Delete This User"].Value
|
||||||
[ icon "delete_forever" ]
|
_onclick $"return PT.confirmDelete('{delAction}','{delPrompt}')" ] [
|
||||||
|
icon "delete_forever"
|
||||||
|
]
|
||||||
]
|
]
|
||||||
td [] [ str user.fullName ]
|
td [] [ str user.fullName ]
|
||||||
td [ _class "pt-center-text" ] [
|
td [ _class "pt-center-text" ] [
|
||||||
|
@ -219,17 +223,18 @@ let maintain (users : User list) ctx vi =
|
||||||
]
|
]
|
||||||
])
|
])
|
||||||
|> tbody []
|
|> tbody []
|
||||||
]
|
]
|
||||||
[ div [ _class "pt-center-text" ] [
|
[ div [ _class "pt-center-text" ] [
|
||||||
br []
|
br []
|
||||||
a [ _href $"/user/{emptyGuid}/edit"; _title s["Add a New User"].Value ]
|
a [ _href $"/user/{emptyGuid}/edit"; _title s["Add a New User"].Value ] [
|
||||||
[ icon "add_circle"; rawText " "; locStr s["Add a New User"] ]
|
icon "add_circle"; rawText " "; locStr s["Add a New User"]
|
||||||
br []
|
]
|
||||||
br []
|
br []
|
||||||
]
|
br []
|
||||||
tableSummary users.Length s
|
]
|
||||||
usrTbl
|
tableSummary users.Length s
|
||||||
form [ _id "DeleteForm"; _action ""; _method "post" ] [ csrfToken ctx ]
|
usrTbl
|
||||||
|
form [ _id "DeleteForm"; _action ""; _method "post" ] [ csrfToken ctx ]
|
||||||
]
|
]
|
||||||
|> Layout.Content.standard
|
|> Layout.Content.standard
|
||||||
|> Layout.standard vi "Maintain Users"
|
|> Layout.standard vi "Maintain Users"
|
||||||
|
|
|
@ -103,6 +103,18 @@ module UserMessage =
|
||||||
Description = None
|
Description = None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The template with which the content will be rendered
|
||||||
|
type LayoutType =
|
||||||
|
|
||||||
|
/// A full page load
|
||||||
|
| FullPage
|
||||||
|
|
||||||
|
/// A response that will provide a new body tag
|
||||||
|
| PartialPage
|
||||||
|
|
||||||
|
/// A response that will replace the page content
|
||||||
|
| ContentOnly
|
||||||
|
|
||||||
|
|
||||||
open System
|
open System
|
||||||
|
|
||||||
|
@ -132,6 +144,9 @@ type AppViewInfo =
|
||||||
|
|
||||||
/// The currently logged on small group, if there is one
|
/// The currently logged on small group, if there is one
|
||||||
Group : SmallGroup option
|
Group : SmallGroup option
|
||||||
|
|
||||||
|
/// The layout with which the content will be rendered
|
||||||
|
Layout : LayoutType
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Support for the AppViewInfo type
|
/// Support for the AppViewInfo type
|
||||||
|
@ -147,6 +162,7 @@ module AppViewInfo =
|
||||||
RequestStart = DateTime.Now.Ticks
|
RequestStart = DateTime.Now.Ticks
|
||||||
User = None
|
User = None
|
||||||
Group = None
|
Group = None
|
||||||
|
Layout = FullPage
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -18,7 +18,7 @@ let private findStats (db : AppDbContext) churchId = task {
|
||||||
|
|
||||||
|
|
||||||
/// POST /church/[church-id]/delete
|
/// POST /church/[church-id]/delete
|
||||||
let delete churchId : HttpHandler = requireAccess [ Admin ] >=> validateCSRF >=> fun next ctx -> task {
|
let delete churchId : HttpHandler = requireAccess [ Admin ] >=> validateCsrf >=> fun next ctx -> task {
|
||||||
match! ctx.db.TryChurchById churchId with
|
match! ctx.db.TryChurchById churchId with
|
||||||
| Some church ->
|
| Some church ->
|
||||||
let! _, stats = findStats ctx.db churchId
|
let! _, stats = findStats ctx.db churchId
|
||||||
|
@ -66,7 +66,7 @@ let maintain : HttpHandler = requireAccess [ Admin ] >=> fun next ctx -> task {
|
||||||
|
|
||||||
|
|
||||||
/// POST /church/save
|
/// POST /church/save
|
||||||
let save : HttpHandler = requireAccess [ Admin ] >=> validateCSRF >=> fun next ctx -> task {
|
let save : HttpHandler = requireAccess [ Admin ] >=> validateCsrf >=> fun next ctx -> task {
|
||||||
match! ctx.TryBindFormAsync<EditChurch> () with
|
match! ctx.TryBindFormAsync<EditChurch> () with
|
||||||
| Ok m ->
|
| Ok m ->
|
||||||
let! church =
|
let! church =
|
||||||
|
|
|
@ -2,20 +2,7 @@
|
||||||
[<AutoOpen>]
|
[<AutoOpen>]
|
||||||
module PrayerTracker.Handlers.CommonFunctions
|
module PrayerTracker.Handlers.CommonFunctions
|
||||||
|
|
||||||
open System
|
|
||||||
open System.Net
|
|
||||||
open System.Reflection
|
|
||||||
open System.Threading.Tasks
|
|
||||||
open Giraffe
|
|
||||||
open Microsoft.AspNetCore.Antiforgery
|
|
||||||
open Microsoft.AspNetCore.Html
|
|
||||||
open Microsoft.AspNetCore.Http
|
|
||||||
open Microsoft.AspNetCore.Http.Extensions
|
|
||||||
open Microsoft.AspNetCore.Mvc.Rendering
|
open Microsoft.AspNetCore.Mvc.Rendering
|
||||||
open Microsoft.Extensions.Localization
|
|
||||||
open PrayerTracker
|
|
||||||
open PrayerTracker.Cookies
|
|
||||||
open PrayerTracker.ViewModels
|
|
||||||
|
|
||||||
/// Create a select list from an enumeration
|
/// Create a select list from an enumeration
|
||||||
let toSelectList<'T> valFunc textFunc withDefault emptyText (items : 'T seq) =
|
let toSelectList<'T> valFunc textFunc withDefault emptyText (items : 'T seq) =
|
||||||
|
@ -38,7 +25,7 @@ let toSelectListWithDefault<'T> valFunc textFunc (items : 'T seq) =
|
||||||
|
|
||||||
/// The version of PrayerTracker
|
/// The version of PrayerTracker
|
||||||
let appVersion =
|
let appVersion =
|
||||||
let v = Assembly.GetExecutingAssembly().GetName().Version
|
let v = System.Reflection.Assembly.GetExecutingAssembly().GetName().Version
|
||||||
#if (DEBUG)
|
#if (DEBUG)
|
||||||
$"v{v}"
|
$"v{v}"
|
||||||
#else
|
#else
|
||||||
|
@ -53,6 +40,10 @@ let appVersion =
|
||||||
|> String.concat ""
|
|> String.concat ""
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
open Microsoft.AspNetCore.Http
|
||||||
|
open PrayerTracker
|
||||||
|
|
||||||
/// The currently signed-in user (will raise if none exists)
|
/// The currently signed-in user (will raise if none exists)
|
||||||
let currentUser (ctx : HttpContext) =
|
let currentUser (ctx : HttpContext) =
|
||||||
match ctx.Session.user with Some u -> u | None -> nullArg "User"
|
match ctx.Session.user with Some u -> u | None -> nullArg "User"
|
||||||
|
@ -61,6 +52,12 @@ let currentUser (ctx : HttpContext) =
|
||||||
let currentGroup (ctx : HttpContext) =
|
let currentGroup (ctx : HttpContext) =
|
||||||
match ctx.Session.smallGroup with Some g -> g | None -> nullArg "SmallGroup"
|
match ctx.Session.smallGroup with Some g -> g | None -> nullArg "SmallGroup"
|
||||||
|
|
||||||
|
|
||||||
|
open System
|
||||||
|
open Giraffe
|
||||||
|
open PrayerTracker.Cookies
|
||||||
|
open PrayerTracker.ViewModels
|
||||||
|
|
||||||
/// Create the common view information heading
|
/// Create the common view information heading
|
||||||
let viewInfo (ctx : HttpContext) startTicks =
|
let viewInfo (ctx : HttpContext) startTicks =
|
||||||
let msg =
|
let msg =
|
||||||
|
@ -84,12 +81,18 @@ let viewInfo (ctx : HttpContext) startTicks =
|
||||||
CookieOptions (Expires = Nullable<DateTimeOffset> (DateTimeOffset (DateTime timeout.Until)),
|
CookieOptions (Expires = Nullable<DateTimeOffset> (DateTimeOffset (DateTime timeout.Until)),
|
||||||
HttpOnly = true))
|
HttpOnly = true))
|
||||||
| None -> ()
|
| None -> ()
|
||||||
|
let layout =
|
||||||
|
match ctx.TryGetRequestHeader "X-Target" with
|
||||||
|
| Some hdr when hdr = "#pt-body" -> ContentOnly
|
||||||
|
| Some _ -> PartialPage
|
||||||
|
| None -> FullPage
|
||||||
{ AppViewInfo.fresh with
|
{ AppViewInfo.fresh with
|
||||||
Version = appVersion
|
Version = appVersion
|
||||||
Messages = msg
|
Messages = msg
|
||||||
RequestStart = startTicks
|
RequestStart = startTicks
|
||||||
User = ctx.Session.user
|
User = ctx.Session.user
|
||||||
Group = ctx.Session.smallGroup
|
Group = ctx.Session.smallGroup
|
||||||
|
Layout = layout
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The view is the last parameter, so it can be composed
|
/// The view is the last parameter, so it can be composed
|
||||||
|
@ -107,23 +110,24 @@ let fourOhFour next (ctx : HttpContext) =
|
||||||
ctx.SetStatusCode 404
|
ctx.SetStatusCode 404
|
||||||
text "Not Found" next ctx
|
text "Not Found" next ctx
|
||||||
|
|
||||||
|
|
||||||
/// Handler to validate CSRF prevention token
|
/// Handler to validate CSRF prevention token
|
||||||
let validateCSRF : HttpHandler = fun next ctx -> task {
|
let validateCsrf : HttpHandler = fun next ctx -> task {
|
||||||
match! (ctx.GetService<IAntiforgery> ()).IsRequestValidAsync ctx with
|
match! (ctx.GetService<Microsoft.AspNetCore.Antiforgery.IAntiforgery> ()).IsRequestValidAsync ctx with
|
||||||
| true -> return! next ctx
|
| true -> return! next ctx
|
||||||
| false ->
|
| false -> return! (clearResponse >=> setStatusCode 400 >=> text "Quit hacking...") earlyReturn ctx
|
||||||
return! (clearResponse >=> setStatusCode 400 >=> text "Quit hacking...") (fun _ -> Task.FromResult None) ctx
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Add a message to the session
|
/// Add a message to the session
|
||||||
let addUserMessage (ctx : HttpContext) msg =
|
let addUserMessage (ctx : HttpContext) msg =
|
||||||
ctx.Session.messages <- msg :: ctx.Session.messages
|
ctx.Session.messages <- msg :: ctx.Session.messages
|
||||||
|
|
||||||
|
|
||||||
|
open Microsoft.AspNetCore.Html
|
||||||
|
open Microsoft.Extensions.Localization
|
||||||
|
|
||||||
/// Convert a localized string to an HTML string
|
/// Convert a localized string to an HTML string
|
||||||
let htmlLocString (x : LocalizedString) =
|
let htmlLocString (x : LocalizedString) =
|
||||||
(WebUtility.HtmlEncode >> HtmlString) x.Value
|
(System.Net.WebUtility.HtmlEncode >> HtmlString) x.Value
|
||||||
|
|
||||||
let htmlString (x : LocalizedString) =
|
let htmlString (x : LocalizedString) =
|
||||||
HtmlString x.Value
|
HtmlString x.Value
|
||||||
|
@ -157,6 +161,8 @@ type AccessLevel =
|
||||||
| Public
|
| Public
|
||||||
|
|
||||||
|
|
||||||
|
open Microsoft.AspNetCore.Http.Extensions
|
||||||
|
|
||||||
/// Require the given access role (also refreshes "Remember Me" user and group logons)
|
/// Require the given access role (also refreshes "Remember Me" user and group logons)
|
||||||
let requireAccess level : HttpHandler =
|
let requireAccess level : HttpHandler =
|
||||||
|
|
||||||
|
|
|
@ -94,7 +94,7 @@ let email date : HttpHandler = requireAccess [ User ] >=> fun next ctx -> task {
|
||||||
|
|
||||||
|
|
||||||
/// POST /prayer-request/[request-id]/delete
|
/// POST /prayer-request/[request-id]/delete
|
||||||
let delete reqId : HttpHandler = requireAccess [ User ] >=> validateCSRF >=> fun next ctx -> task {
|
let delete reqId : HttpHandler = requireAccess [ User ] >=> validateCsrf >=> fun next ctx -> task {
|
||||||
match! findRequest ctx reqId with
|
match! findRequest ctx reqId with
|
||||||
| Ok req ->
|
| Ok req ->
|
||||||
let s = Views.I18N.localizer.Force ()
|
let s = Views.I18N.localizer.Force ()
|
||||||
|
@ -215,7 +215,7 @@ let restore reqId : HttpHandler = requireAccess [ User ] >=> fun next ctx -> tas
|
||||||
|
|
||||||
|
|
||||||
/// POST /prayer-request/save
|
/// POST /prayer-request/save
|
||||||
let save : HttpHandler = requireAccess [ User ] >=> validateCSRF >=> fun next ctx -> task {
|
let save : HttpHandler = requireAccess [ User ] >=> validateCsrf >=> fun next ctx -> task {
|
||||||
match! ctx.TryBindFormAsync<EditRequest> () with
|
match! ctx.TryBindFormAsync<EditRequest> () with
|
||||||
| Ok m ->
|
| Ok m ->
|
||||||
let! req =
|
let! req =
|
||||||
|
|
|
@ -26,6 +26,7 @@
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Giraffe" Version="6.0.0" />
|
<PackageReference Include="Giraffe" Version="6.0.0" />
|
||||||
|
<PackageReference Include="Giraffe.Htmx" Version="1.8.0" />
|
||||||
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="6.0.5" />
|
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="6.0.5" />
|
||||||
<PackageReference Update="FSharp.Core" Version="6.0.5" />
|
<PackageReference Update="FSharp.Core" Version="6.0.5" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
|
@ -27,7 +27,7 @@ let announcement : HttpHandler = requireAccess [ User ] >=> fun next ctx ->
|
||||||
|
|
||||||
|
|
||||||
/// POST /small-group/[group-id]/delete
|
/// POST /small-group/[group-id]/delete
|
||||||
let delete groupId : HttpHandler = requireAccess [ Admin ] >=> validateCSRF >=> fun next ctx -> task {
|
let delete groupId : HttpHandler = requireAccess [ Admin ] >=> validateCsrf >=> fun next ctx -> task {
|
||||||
let s = Views.I18N.localizer.Force ()
|
let s = Views.I18N.localizer.Force ()
|
||||||
match! ctx.db.TryGroupById groupId with
|
match! ctx.db.TryGroupById groupId with
|
||||||
| Some grp ->
|
| Some grp ->
|
||||||
|
@ -44,7 +44,7 @@ let delete groupId : HttpHandler = requireAccess [ Admin ] >=> validateCSRF >=>
|
||||||
|
|
||||||
|
|
||||||
/// POST /small-group/member/[member-id]/delete
|
/// POST /small-group/member/[member-id]/delete
|
||||||
let deleteMember memberId : HttpHandler = requireAccess [ User ] >=> validateCSRF >=> fun next ctx -> task {
|
let deleteMember memberId : HttpHandler = requireAccess [ User ] >=> validateCsrf >=> fun next ctx -> task {
|
||||||
let s = Views.I18N.localizer.Force ()
|
let s = Views.I18N.localizer.Force ()
|
||||||
match! ctx.db.TryMemberById memberId with
|
match! ctx.db.TryMemberById memberId with
|
||||||
| Some mbr when mbr.smallGroupId = (currentGroup ctx).smallGroupId ->
|
| Some mbr when mbr.smallGroupId = (currentGroup ctx).smallGroupId ->
|
||||||
|
@ -113,7 +113,7 @@ let logOn (groupId : SmallGroupId option) : HttpHandler = requireAccess [ Access
|
||||||
|
|
||||||
|
|
||||||
/// POST /small-group/log-on/submit
|
/// POST /small-group/log-on/submit
|
||||||
let logOnSubmit : HttpHandler = requireAccess [ AccessLevel.Public ] >=> validateCSRF >=> fun next ctx -> task {
|
let logOnSubmit : HttpHandler = requireAccess [ AccessLevel.Public ] >=> validateCsrf >=> fun next ctx -> task {
|
||||||
match! ctx.TryBindFormAsync<GroupLogOn> () with
|
match! ctx.TryBindFormAsync<GroupLogOn> () with
|
||||||
| Ok m ->
|
| Ok m ->
|
||||||
let s = Views.I18N.localizer.Force ()
|
let s = Views.I18N.localizer.Force ()
|
||||||
|
@ -193,7 +193,7 @@ let preferences : HttpHandler = requireAccess [ User ] >=> fun next ctx -> task
|
||||||
|
|
||||||
|
|
||||||
/// POST /small-group/save
|
/// POST /small-group/save
|
||||||
let save : HttpHandler = requireAccess [ Admin ] >=> validateCSRF >=> fun next ctx -> task {
|
let save : HttpHandler = requireAccess [ Admin ] >=> validateCsrf >=> fun next ctx -> task {
|
||||||
match! ctx.TryBindFormAsync<EditSmallGroup> () with
|
match! ctx.TryBindFormAsync<EditSmallGroup> () with
|
||||||
| Ok m ->
|
| Ok m ->
|
||||||
let s = Views.I18N.localizer.Force ()
|
let s = Views.I18N.localizer.Force ()
|
||||||
|
@ -218,7 +218,7 @@ let save : HttpHandler = requireAccess [ Admin ] >=> validateCSRF >=> fun next c
|
||||||
|
|
||||||
|
|
||||||
/// POST /small-group/member/save
|
/// POST /small-group/member/save
|
||||||
let saveMember : HttpHandler = requireAccess [ User ] >=> validateCSRF >=> fun next ctx -> task {
|
let saveMember : HttpHandler = requireAccess [ User ] >=> validateCsrf >=> fun next ctx -> task {
|
||||||
match! ctx.TryBindFormAsync<EditMember> () with
|
match! ctx.TryBindFormAsync<EditMember> () with
|
||||||
| Ok m ->
|
| Ok m ->
|
||||||
let grp = currentGroup ctx
|
let grp = currentGroup ctx
|
||||||
|
@ -246,7 +246,7 @@ let saveMember : HttpHandler = requireAccess [ User ] >=> validateCSRF >=> fun n
|
||||||
|
|
||||||
|
|
||||||
/// POST /small-group/preferences/save
|
/// POST /small-group/preferences/save
|
||||||
let savePreferences : HttpHandler = requireAccess [ User ] >=> validateCSRF >=> fun next ctx -> task {
|
let savePreferences : HttpHandler = requireAccess [ User ] >=> validateCsrf >=> fun next ctx -> task {
|
||||||
match! ctx.TryBindFormAsync<EditPreferences> () with
|
match! ctx.TryBindFormAsync<EditPreferences> () with
|
||||||
| Ok m ->
|
| Ok m ->
|
||||||
// Since the class is stored in the session, we'll use an intermediate instance to persist it; once that works,
|
// Since the class is stored in the session, we'll use an intermediate instance to persist it; once that works,
|
||||||
|
@ -268,7 +268,7 @@ let savePreferences : HttpHandler = requireAccess [ User ] >=> validateCSRF >=>
|
||||||
|
|
||||||
|
|
||||||
/// POST /small-group/announcement/send
|
/// POST /small-group/announcement/send
|
||||||
let sendAnnouncement : HttpHandler = requireAccess [ User ] >=> validateCSRF >=> fun next ctx -> task {
|
let sendAnnouncement : HttpHandler = requireAccess [ User ] >=> validateCsrf >=> fun next ctx -> task {
|
||||||
let startTicks = DateTime.Now.Ticks
|
let startTicks = DateTime.Now.Ticks
|
||||||
match! ctx.TryBindFormAsync<Announcement> () with
|
match! ctx.TryBindFormAsync<Announcement> () with
|
||||||
| Ok m ->
|
| Ok m ->
|
||||||
|
|
|
@ -44,7 +44,7 @@ let private findUserByPassword m (db : AppDbContext) = task {
|
||||||
|
|
||||||
|
|
||||||
/// POST /user/password/change
|
/// POST /user/password/change
|
||||||
let changePassword : HttpHandler = requireAccess [ User ] >=> validateCSRF >=> fun next ctx -> task {
|
let changePassword : HttpHandler = requireAccess [ User ] >=> validateCsrf >=> fun next ctx -> task {
|
||||||
match! ctx.TryBindFormAsync<ChangePassword> () with
|
match! ctx.TryBindFormAsync<ChangePassword> () with
|
||||||
| Ok m ->
|
| Ok m ->
|
||||||
let s = Views.I18N.localizer.Force ()
|
let s = Views.I18N.localizer.Force ()
|
||||||
|
@ -81,7 +81,7 @@ let changePassword : HttpHandler = requireAccess [ User ] >=> validateCSRF >=> f
|
||||||
|
|
||||||
|
|
||||||
/// POST /user/[user-id]/delete
|
/// POST /user/[user-id]/delete
|
||||||
let delete userId : HttpHandler = requireAccess [ Admin ] >=> validateCSRF >=> fun next ctx -> task {
|
let delete userId : HttpHandler = requireAccess [ Admin ] >=> validateCsrf >=> fun next ctx -> task {
|
||||||
match! ctx.db.TryUserById userId with
|
match! ctx.db.TryUserById userId with
|
||||||
| Some user ->
|
| Some user ->
|
||||||
ctx.db.RemoveEntry user
|
ctx.db.RemoveEntry user
|
||||||
|
@ -94,7 +94,7 @@ let delete userId : HttpHandler = requireAccess [ Admin ] >=> validateCSRF >=> f
|
||||||
|
|
||||||
|
|
||||||
/// POST /user/log-on
|
/// POST /user/log-on
|
||||||
let doLogOn : HttpHandler = requireAccess [ AccessLevel.Public ] >=> validateCSRF >=> fun next ctx -> task {
|
let doLogOn : HttpHandler = requireAccess [ AccessLevel.Public ] >=> validateCsrf >=> fun next ctx -> task {
|
||||||
match! ctx.TryBindFormAsync<UserLogOn> () with
|
match! ctx.TryBindFormAsync<UserLogOn> () with
|
||||||
| Ok m ->
|
| Ok m ->
|
||||||
let s = Views.I18N.localizer.Force ()
|
let s = Views.I18N.localizer.Force ()
|
||||||
|
@ -192,7 +192,7 @@ let password : HttpHandler = requireAccess [ User ] >=> fun next ctx ->
|
||||||
|
|
||||||
|
|
||||||
/// POST /user/save
|
/// POST /user/save
|
||||||
let save : HttpHandler = requireAccess [ Admin ] >=> validateCSRF >=> fun next ctx -> task {
|
let save : HttpHandler = requireAccess [ Admin ] >=> validateCsrf >=> fun next ctx -> task {
|
||||||
match! ctx.TryBindFormAsync<EditUser> () with
|
match! ctx.TryBindFormAsync<EditUser> () with
|
||||||
| Ok m ->
|
| Ok m ->
|
||||||
let! user =
|
let! user =
|
||||||
|
@ -235,7 +235,7 @@ let save : HttpHandler = requireAccess [ Admin ] >=> validateCSRF >=> fun next c
|
||||||
|
|
||||||
|
|
||||||
/// POST /user/small-groups/save
|
/// POST /user/small-groups/save
|
||||||
let saveGroups : HttpHandler = requireAccess [ Admin ] >=> validateCSRF >=> fun next ctx -> task {
|
let saveGroups : HttpHandler = requireAccess [ Admin ] >=> validateCsrf >=> fun next ctx -> task {
|
||||||
match! ctx.TryBindFormAsync<AssignGroups> () with
|
match! ctx.TryBindFormAsync<AssignGroups> () with
|
||||||
| Ok m ->
|
| Ok m ->
|
||||||
let s = Views.I18N.localizer.Force ()
|
let s = Views.I18N.localizer.Force ()
|
||||||
|
|
Loading…
Reference in New Issue
Block a user