Version 8 #43
@ -60,7 +60,7 @@ module private Helpers =
|
||||
Requestor = row.stringOrNone "requestor"
|
||||
Text = row.string "request_text"
|
||||
NotifyChaplain = row.bool "notify_chaplain"
|
||||
RequestType = PrayerRequestType.fromCode (row.string "request_id")
|
||||
RequestType = PrayerRequestType.fromCode (row.string "request_type")
|
||||
Expiration = Expiration.fromCode (row.string "expiration")
|
||||
}
|
||||
|
||||
@ -108,8 +108,7 @@ module Churches =
|
||||
|
||||
/// Get a list of all churches
|
||||
let all conn =
|
||||
conn
|
||||
|> Sql.existingConnection
|
||||
Sql.existingConnection conn
|
||||
|> Sql.query "SELECT * FROM pt.church ORDER BY church_name"
|
||||
|> Sql.executeAsync mapToChurch
|
||||
|
||||
@ -118,8 +117,7 @@ module Churches =
|
||||
let idParam = [ [ "@churchId", Sql.uuid churchId.Value ] ]
|
||||
let where = "WHERE small_group_id IN (SELECT id FROM pt.small_group WHERE church_id = @churchId)"
|
||||
let! _ =
|
||||
conn
|
||||
|> Sql.existingConnection
|
||||
Sql.existingConnection conn
|
||||
|> Sql.executeTransactionAsync
|
||||
[ $"DELETE FROM pt.prayer_request {where}", idParam
|
||||
$"DELETE FROM pt.user_small_group {where}", idParam
|
||||
@ -132,8 +130,7 @@ module Churches =
|
||||
/// Save a church's information
|
||||
let save (church : Church) conn = backgroundTask {
|
||||
let! _ =
|
||||
conn
|
||||
|> Sql.existingConnection
|
||||
Sql.existingConnection conn
|
||||
|> Sql.query """
|
||||
INSERT INTO pt.church (
|
||||
id, church_name, city, state, has_vps_interface, interface_address
|
||||
@ -159,8 +156,7 @@ module Churches =
|
||||
/// Find a church by its ID
|
||||
let tryById (churchId : ChurchId) conn = backgroundTask {
|
||||
let! church =
|
||||
conn
|
||||
|> Sql.existingConnection
|
||||
Sql.existingConnection conn
|
||||
|> Sql.query "SELECT * FROM pt.church WHERE id = @id"
|
||||
|> Sql.parameters [ "@id", Sql.uuid churchId.Value ]
|
||||
|> Sql.executeAsync mapToChurch
|
||||
@ -173,8 +169,7 @@ module Members =
|
||||
|
||||
/// Count members for the given small group
|
||||
let countByGroup (groupId : SmallGroupId) conn =
|
||||
conn
|
||||
|> Sql.existingConnection
|
||||
Sql.existingConnection conn
|
||||
|> Sql.query "SELECT COUNT(id) AS mbr_count FROM pt.member WHERE small_group_id = @groupId"
|
||||
|> Sql.parameters [ "@groupId", Sql.uuid groupId.Value ]
|
||||
|> Sql.executeRowAsync (fun row -> row.int "mbr_count")
|
||||
@ -182,8 +177,7 @@ module Members =
|
||||
/// Delete a small group member by its ID
|
||||
let deleteById (memberId : MemberId) conn = backgroundTask {
|
||||
let! _ =
|
||||
conn
|
||||
|> Sql.existingConnection
|
||||
Sql.existingConnection conn
|
||||
|> Sql.query "DELETE FROM pt.member WHERE id = @id"
|
||||
|> Sql.parameters [ "@id", Sql.uuid memberId.Value ]
|
||||
|> Sql.executeNonQueryAsync
|
||||
@ -192,8 +186,7 @@ module Members =
|
||||
|
||||
/// Retrieve all members for a given small group
|
||||
let forGroup (groupId : SmallGroupId) conn =
|
||||
conn
|
||||
|> Sql.existingConnection
|
||||
Sql.existingConnection conn
|
||||
|> Sql.query "SELECT * FROM pt.member WHERE small_group_id = @groupId ORDER BY member_name"
|
||||
|> Sql.parameters [ "@groupId", Sql.uuid groupId.Value ]
|
||||
|> Sql.executeAsync mapToMember
|
||||
@ -201,8 +194,7 @@ module Members =
|
||||
/// Save a small group member
|
||||
let save (mbr : Member) conn = backgroundTask {
|
||||
let! _ =
|
||||
conn
|
||||
|> Sql.existingConnection
|
||||
Sql.existingConnection conn
|
||||
|> Sql.query """
|
||||
INSERT INTO pt.member (
|
||||
id, small_group_id, member_name, email, email_format
|
||||
@ -225,8 +217,7 @@ module Members =
|
||||
/// Retrieve a small group member by its ID
|
||||
let tryById (memberId : MemberId) conn = backgroundTask {
|
||||
let! mbr =
|
||||
conn
|
||||
|> Sql.existingConnection
|
||||
Sql.existingConnection conn
|
||||
|> Sql.query "SELECT * FROM pt.member WHERE id = @id"
|
||||
|> Sql.parameters [ "@id", Sql.uuid memberId.Value ]
|
||||
|> Sql.executeAsync mapToMember
|
||||
@ -259,8 +250,8 @@ module PrayerRequests =
|
||||
/// Central place to append sort criteria for prayer request queries
|
||||
let private orderBy sort =
|
||||
match sort with
|
||||
| SortByDate -> "DESC updated_date, DESC entered_date, requestor"
|
||||
| SortByRequestor -> "requestor, DESC updated_date, DESC entered_date"
|
||||
| SortByDate -> "updated_date DESC, entered_date DESC, requestor"
|
||||
| SortByRequestor -> "requestor, updated_date DESC, entered_date DESC"
|
||||
|
||||
/// Paginate a prayer request query
|
||||
let private paginate (pageNbr : int) pageSize =
|
||||
@ -268,8 +259,7 @@ module PrayerRequests =
|
||||
|
||||
/// Count the number of prayer requests for a church
|
||||
let countByChurch (churchId : ChurchId) conn =
|
||||
conn
|
||||
|> Sql.existingConnection
|
||||
Sql.existingConnection conn
|
||||
|> Sql.query """
|
||||
SELECT COUNT(id) AS req_count
|
||||
FROM pt.prayer_request
|
||||
@ -279,8 +269,7 @@ module PrayerRequests =
|
||||
|
||||
/// Count the number of prayer requests for a small group
|
||||
let countByGroup (groupId : SmallGroupId) conn =
|
||||
conn
|
||||
|> Sql.existingConnection
|
||||
Sql.existingConnection conn
|
||||
|> Sql.query "SELECT COUNT(id) AS req_count FROM pt.prayer_request WHERE small_group_id = @groupId"
|
||||
|> Sql.parameters [ "@groupId", Sql.uuid groupId.Value ]
|
||||
|> Sql.executeRowAsync (fun row -> row.int "req_count")
|
||||
@ -288,8 +277,7 @@ module PrayerRequests =
|
||||
/// Delete a prayer request by its ID
|
||||
let deleteById (reqId : PrayerRequestId) conn = backgroundTask {
|
||||
let! _ =
|
||||
conn
|
||||
|> Sql.existingConnection
|
||||
Sql.existingConnection conn
|
||||
|> Sql.query "DELETE FROM pt.prayer_request WHERE id = @id"
|
||||
|> Sql.parameters [ "@id", Sql.uuid reqId.Value ]
|
||||
|> Sql.executeNonQueryAsync
|
||||
@ -306,7 +294,7 @@ module PrayerRequests =
|
||||
(theDate.AtStartOfDayInZone(SmallGroup.timeZone opts.SmallGroup)
|
||||
- Duration.FromDays opts.SmallGroup.Preferences.DaysToExpire)
|
||||
.ToInstant ())
|
||||
""" AND ( updatedDate > @asOf
|
||||
""" AND ( updated_date > @asOf
|
||||
OR expiration = @manual
|
||||
OR request_type = @longTerm
|
||||
OR request_type = @expecting)
|
||||
@ -317,11 +305,10 @@ module PrayerRequests =
|
||||
"@expecting", Sql.string (PrayerRequestType.toCode Expecting)
|
||||
"@forced", Sql.string (Expiration.toCode Forced) ]
|
||||
else "", []
|
||||
conn
|
||||
|> Sql.existingConnection
|
||||
Sql.existingConnection conn
|
||||
|> Sql.query $"""
|
||||
SELECT *
|
||||
FROM prayer_request
|
||||
FROM pt.prayer_request
|
||||
WHERE small_group_id = @groupId {where}
|
||||
ORDER BY {orderBy opts.SmallGroup.Preferences.RequestSort}
|
||||
{paginate opts.PageNumber opts.SmallGroup.Preferences.PageSize}"""
|
||||
@ -331,8 +318,7 @@ module PrayerRequests =
|
||||
/// Save a prayer request
|
||||
let save (req : PrayerRequest) conn = backgroundTask {
|
||||
let! _ =
|
||||
conn
|
||||
|> Sql.existingConnection
|
||||
Sql.existingConnection conn
|
||||
|> Sql.query """
|
||||
INSERT into pt.prayer_request (
|
||||
id, request_type, user_id, small_group_id, entered_date, updated_date, requestor, request_text,
|
||||
@ -365,8 +351,7 @@ module PrayerRequests =
|
||||
|
||||
/// Search prayer requests for the given term
|
||||
let searchForGroup group searchTerm pageNbr conn =
|
||||
conn
|
||||
|> Sql.existingConnection
|
||||
Sql.existingConnection conn
|
||||
|> Sql.query $"""
|
||||
SELECT * FROM pt.prayer_request WHERE small_group_id = @groupId AND request_text ILIKE @search
|
||||
UNION
|
||||
@ -379,8 +364,7 @@ module PrayerRequests =
|
||||
/// Retrieve a prayer request by its ID
|
||||
let tryById (reqId : PrayerRequestId) conn = backgroundTask {
|
||||
let! req =
|
||||
conn
|
||||
|> Sql.existingConnection
|
||||
Sql.existingConnection conn
|
||||
|> Sql.query "SELECT * FROM pt.prayer_request WHERE id = @id"
|
||||
|> Sql.parameters [ "@id", Sql.uuid reqId.Value ]
|
||||
|> Sql.executeAsync mapToPrayerRequest
|
||||
@ -395,8 +379,7 @@ module PrayerRequests =
|
||||
[ "@updated", Sql.parameter (NpgsqlParameter ("@updated", req.UpdatedDate)) ]
|
||||
else "", []
|
||||
let! _ =
|
||||
conn
|
||||
|> Sql.existingConnection
|
||||
Sql.existingConnection conn
|
||||
|> Sql.query $"UPDATE pt.prayer_request SET expiration = @expiration{sql} WHERE id = @id"
|
||||
|> Sql.parameters
|
||||
([ "@expiration", Sql.string (Expiration.toCode req.Expiration)
|
||||
@ -412,8 +395,7 @@ module SmallGroups =
|
||||
|
||||
/// Count the number of small groups for a church
|
||||
let countByChurch (churchId : ChurchId) conn =
|
||||
conn
|
||||
|> Sql.existingConnection
|
||||
Sql.existingConnection conn
|
||||
|> Sql.query "SELECT COUNT(id) AS group_count FROM pt.small_group WHERE church_id = @churchId"
|
||||
|> Sql.parameters [ "@churchId", Sql.uuid churchId.Value ]
|
||||
|> Sql.executeRowAsync (fun row -> row.int "group_count")
|
||||
@ -422,8 +404,7 @@ module SmallGroups =
|
||||
let deleteById (groupId : SmallGroupId) conn = backgroundTask {
|
||||
let idParam = [ [ "@groupId", Sql.uuid groupId.Value ] ]
|
||||
let! _ =
|
||||
conn
|
||||
|> Sql.existingConnection
|
||||
Sql.existingConnection conn
|
||||
|> Sql.executeTransactionAsync
|
||||
[ "DELETE FROM pt.prayer_request WHERE small_group_id = @groupId", idParam
|
||||
"DELETE FROM pt.user_small_group WHERE small_group_id = @groupId", idParam
|
||||
@ -434,10 +415,9 @@ module SmallGroups =
|
||||
|
||||
/// Get information for all small groups
|
||||
let infoForAll conn =
|
||||
conn
|
||||
|> Sql.existingConnection
|
||||
Sql.existingConnection conn
|
||||
|> Sql.query """
|
||||
SELECT sg.id, c.church_name, lp.time_zone_id
|
||||
SELECT sg.id, sg.group_name, c.church_name, lp.time_zone_id, lp.is_public
|
||||
FROM pt.small_group sg
|
||||
INNER JOIN pt.church c ON c.id = sg.church_id
|
||||
INNER JOIN pt.list_preference lp ON lp.small_group_id = sg.id
|
||||
@ -446,8 +426,7 @@ module SmallGroups =
|
||||
|
||||
/// Get a list of small group IDs along with a description that includes the church name
|
||||
let listAll conn =
|
||||
conn
|
||||
|> Sql.existingConnection
|
||||
Sql.existingConnection conn
|
||||
|> Sql.query """
|
||||
SELECT g.group_name, g.id, c.church_name
|
||||
FROM pt.small_group g
|
||||
@ -457,8 +436,7 @@ module SmallGroups =
|
||||
|
||||
/// Get a list of small group IDs and descriptions for groups with a group password
|
||||
let listProtected conn =
|
||||
conn
|
||||
|> Sql.existingConnection
|
||||
Sql.existingConnection conn
|
||||
|> Sql.query """
|
||||
SELECT g.group_name, g.id, c.church_name, lp.is_public
|
||||
FROM pt.small_group g
|
||||
@ -470,10 +448,9 @@ module SmallGroups =
|
||||
|
||||
/// Get a list of small group IDs and descriptions for groups that are public or have a group password
|
||||
let listPublicAndProtected conn =
|
||||
conn
|
||||
|> Sql.existingConnection
|
||||
Sql.existingConnection conn
|
||||
|> Sql.query """
|
||||
SELECT g.group_name, g.id, c.church_name, lp.is_public
|
||||
SELECT g.group_name, g.id, c.church_name, lp.time_zone_id, lp.is_public
|
||||
FROM pt.small_group g
|
||||
INNER JOIN pt.church c ON c.id = g.church_id
|
||||
INNER JOIN pt.list_preference lp ON lp.small_group_id = g.id
|
||||
@ -485,8 +462,7 @@ module SmallGroups =
|
||||
/// Log on for a small group (includes list preferences)
|
||||
let logOn (groupId : SmallGroupId) password conn = backgroundTask {
|
||||
let! group =
|
||||
conn
|
||||
|> Sql.existingConnection
|
||||
Sql.existingConnection conn
|
||||
|> Sql.query """
|
||||
SELECT sg.*, lp.*
|
||||
FROM pt.small_group sg
|
||||
@ -501,8 +477,7 @@ module SmallGroups =
|
||||
/// Save a small group
|
||||
let save (group : SmallGroup) isNew conn = backgroundTask {
|
||||
let! _ =
|
||||
conn
|
||||
|> Sql.existingConnection
|
||||
Sql.existingConnection conn
|
||||
|> Sql.executeTransactionAsync [
|
||||
""" INSERT INTO pt.small_group (
|
||||
id, church_id, group_name
|
||||
@ -524,8 +499,7 @@ module SmallGroups =
|
||||
/// Save a small group's list preferences
|
||||
let savePreferences (pref : ListPreferences) conn = backgroundTask {
|
||||
let! _ =
|
||||
conn
|
||||
|> Sql.existingConnection
|
||||
Sql.existingConnection conn
|
||||
|> Sql.query """
|
||||
UPDATE pt.list_preference
|
||||
SET days_to_keep_new = @daysToKeepNew,
|
||||
@ -573,8 +547,7 @@ module SmallGroups =
|
||||
/// Get a small group by its ID
|
||||
let tryById (groupId : SmallGroupId) conn = backgroundTask {
|
||||
let! group =
|
||||
conn
|
||||
|> Sql.existingConnection
|
||||
Sql.existingConnection conn
|
||||
|> Sql.query "SELECT * FROM pt.small_group WHERE id = @id"
|
||||
|> Sql.parameters [ "@id", Sql.uuid groupId.Value ]
|
||||
|> Sql.executeAsync mapToSmallGroup
|
||||
@ -584,8 +557,7 @@ module SmallGroups =
|
||||
/// Get a small group by its ID with its list preferences populated
|
||||
let tryByIdWithPreferences (groupId : SmallGroupId) conn = backgroundTask {
|
||||
let! group =
|
||||
conn
|
||||
|> Sql.existingConnection
|
||||
Sql.existingConnection conn
|
||||
|> Sql.query """
|
||||
SELECT sg.*, lp.*
|
||||
FROM pt.small_group sg
|
||||
@ -602,15 +574,13 @@ module Users =
|
||||
|
||||
/// Retrieve all PrayerTracker users
|
||||
let all conn =
|
||||
conn
|
||||
|> Sql.existingConnection
|
||||
Sql.existingConnection conn
|
||||
|> Sql.query "SELECT * FROM pt.pt_user ORDER BY last_name, first_name"
|
||||
|> Sql.executeAsync mapToUser
|
||||
|
||||
/// Count the number of users for a church
|
||||
let countByChurch (churchId : ChurchId) conn =
|
||||
conn
|
||||
|> Sql.existingConnection
|
||||
Sql.existingConnection conn
|
||||
|> Sql.query """
|
||||
SELECT COUNT(u.id) AS user_count
|
||||
FROM pt.pt_user u
|
||||
@ -625,8 +595,7 @@ module Users =
|
||||
|
||||
/// Count the number of users for a small group
|
||||
let countByGroup (groupId : SmallGroupId) conn =
|
||||
conn
|
||||
|> Sql.existingConnection
|
||||
Sql.existingConnection conn
|
||||
|> Sql.query "SELECT COUNT(user_id) AS user_count FROM pt.user_small_group WHERE small_group_id = @groupId"
|
||||
|> Sql.parameters [ "@groupId", Sql.uuid groupId.Value ]
|
||||
|> Sql.executeRowAsync (fun row -> row.int "user_count")
|
||||
@ -634,8 +603,7 @@ module Users =
|
||||
/// Delete a user by its database ID
|
||||
let deleteById (userId : UserId) conn = backgroundTask {
|
||||
let! _ =
|
||||
conn
|
||||
|> Sql.existingConnection
|
||||
Sql.existingConnection conn
|
||||
|> Sql.query "DELETE FROM pt.pt_user WHERE id = @id"
|
||||
|> Sql.parameters [ "@id", Sql.uuid userId.Value ]
|
||||
|> Sql.executeNonQueryAsync
|
||||
@ -644,16 +612,14 @@ module Users =
|
||||
|
||||
/// Get the IDs of the small groups for which the given user is authorized
|
||||
let groupIdsByUserId (userId : UserId) conn =
|
||||
conn
|
||||
|> Sql.existingConnection
|
||||
Sql.existingConnection conn
|
||||
|> Sql.query "SELECT small_group_id FROM pt.user_small_group WHERE user_id = @id"
|
||||
|> Sql.parameters [ "@id", Sql.uuid userId.Value ]
|
||||
|> Sql.executeAsync (fun row -> SmallGroupId (row.uuid "small_group_id"))
|
||||
|
||||
/// Get a list of users authorized to administer the given small group
|
||||
let listByGroupId (groupId : SmallGroupId) conn =
|
||||
conn
|
||||
|> Sql.existingConnection
|
||||
Sql.existingConnection conn
|
||||
|> Sql.query """
|
||||
SELECT u.*
|
||||
FROM pt.pt_user u
|
||||
@ -666,8 +632,7 @@ module Users =
|
||||
/// Save a user's information
|
||||
let save (user : User) conn = backgroundTask {
|
||||
let! _ =
|
||||
conn
|
||||
|> Sql.existingConnection
|
||||
Sql.existingConnection conn
|
||||
|> Sql.query """
|
||||
INSERT INTO pt.pt_user (
|
||||
id, first_name, last_name, email, is_admin, password_hash
|
||||
@ -694,8 +659,7 @@ module Users =
|
||||
/// Find a user by its e-mail address and authorized small group
|
||||
let tryByEmailAndGroup email (groupId : SmallGroupId) conn = backgroundTask {
|
||||
let! user =
|
||||
conn
|
||||
|> Sql.existingConnection
|
||||
Sql.existingConnection conn
|
||||
|> Sql.query """
|
||||
SELECT u.*
|
||||
FROM pt.pt_user u
|
||||
@ -709,8 +673,7 @@ module Users =
|
||||
/// Find a user by their database ID
|
||||
let tryById (userId : UserId) conn = backgroundTask {
|
||||
let! user =
|
||||
conn
|
||||
|> Sql.existingConnection
|
||||
Sql.existingConnection conn
|
||||
|> Sql.query "SELECT * FROM pt.pt_user WHERE id = @id"
|
||||
|> Sql.parameters [ "@id", Sql.uuid userId.Value ]
|
||||
|> Sql.executeAsync mapToUser
|
||||
@ -720,8 +683,7 @@ module Users =
|
||||
/// Update a user's last seen date/time
|
||||
let updateLastSeen (userId : UserId) (now : Instant) conn = backgroundTask {
|
||||
let! _ =
|
||||
conn
|
||||
|> Sql.existingConnection
|
||||
Sql.existingConnection conn
|
||||
|> Sql.query "UPDATE pt.pt_user SET last_seen = @now WHERE id = @id"
|
||||
|> Sql.parameters [ "@id", Sql.uuid userId.Value; "@now", Sql.parameter (NpgsqlParameter ("@now", now)) ]
|
||||
|> Sql.executeNonQueryAsync
|
||||
@ -731,8 +693,7 @@ module Users =
|
||||
/// Update a user's password hash
|
||||
let updatePassword (user : User) conn = backgroundTask {
|
||||
let! _ =
|
||||
conn
|
||||
|> Sql.existingConnection
|
||||
Sql.existingConnection conn
|
||||
|> Sql.query "UPDATE pt.pt_user SET password_hash = @passwordHash WHERE id = @id"
|
||||
|> Sql.parameters [ "@id", Sql.uuid user.Id.Value; "@passwordHash", Sql.string user.PasswordHash ]
|
||||
|> Sql.executeNonQueryAsync
|
||||
@ -757,8 +718,7 @@ module Users =
|
||||
}
|
||||
if not (Seq.isEmpty queries) then
|
||||
let! _ =
|
||||
conn
|
||||
|> Sql.existingConnection
|
||||
Sql.existingConnection conn
|
||||
|> Sql.executeTransactionAsync (List.ofSeq queries)
|
||||
()
|
||||
}
|
||||
|
@ -111,6 +111,7 @@ let lists (groups : SmallGroupInfo list) viewInfo =
|
||||
let l = I18N.forView "Requests/Lists"
|
||||
use sw = new StringWriter ()
|
||||
let raw = rawLocText sw
|
||||
let vi = AppViewInfo.withScopedStyles [ "#groupList { grid-template-columns: repeat(3, auto); }" ] viewInfo
|
||||
[ p [] [
|
||||
raw l["The groups listed below have either public or password-protected request lists."]
|
||||
space
|
||||
@ -122,27 +123,31 @@ let lists (groups : SmallGroupInfo list) viewInfo =
|
||||
| 0 -> p [] [ raw l["There are no groups with public or password-protected request lists."] ]
|
||||
| count ->
|
||||
tableSummary count s
|
||||
table [ _class "pt-table pt-action-table" ] [
|
||||
tableHeadings s [ "Actions"; "Church"; "Group" ]
|
||||
groups
|
||||
|> List.map (fun grp ->
|
||||
tr [] [
|
||||
if grp.IsPublic then
|
||||
a [ _href $"/prayer-requests/{grp.Id}/list"; _title s["View"].Value ] [ icon "list" ]
|
||||
else
|
||||
a [ _href $"/small-group/log-on/{grp.Id}"; _title s["Log On"].Value ] [
|
||||
icon "verified_user"
|
||||
]
|
||||
|> List.singleton
|
||||
|> td []
|
||||
td [] [ str grp.ChurchName ]
|
||||
td [] [ str grp.Name ]
|
||||
])
|
||||
|> tbody []
|
||||
section [ _id "groupList"; _class "pt-table"; _ariaLabel "Small group list" ] [
|
||||
div [ _class "row head" ] [
|
||||
header [ _class "cell" ] [ locStr s["Actions"] ]
|
||||
header [ _class "cell" ] [ locStr s["Church"] ]
|
||||
header [ _class "cell" ] [ locStr s["Group"] ]
|
||||
]
|
||||
for group in groups do
|
||||
div [ _class "row" ] [
|
||||
div [ _class "cell actions" ] [
|
||||
if group.IsPublic then
|
||||
a [ _href $"/prayer-requests/{group.Id}/list"; _title s["View"].Value ] [
|
||||
iconSized 18 "list"
|
||||
]
|
||||
else
|
||||
a [ _href $"/small-group/log-on/{group.Id}"; _title s["Log On"].Value ] [
|
||||
iconSized 18 "verified_user"
|
||||
]
|
||||
]
|
||||
div [ _class "cell" ] [ str group.ChurchName ]
|
||||
div [ _class "cell" ] [ str group.Name ]
|
||||
]
|
||||
]
|
||||
]
|
||||
|> Layout.Content.standard
|
||||
|> Layout.standard viewInfo "Request Lists"
|
||||
|> Layout.standard vi "Request Lists"
|
||||
|
||||
|
||||
/// View for the prayer request maintenance page
|
||||
@ -256,7 +261,7 @@ let maintain (model : MaintainRequests) (ctx : HttpContext) viewInfo =
|
||||
br []
|
||||
a [ _href "/prayer-requests/inactive" ] [ raw l["Show Inactive Requests"] ]
|
||||
| _ ->
|
||||
if defaultArg model.OnlyActive false then
|
||||
if Option.isSome model.OnlyActive then
|
||||
raw l["Inactive requests are currently shown"]
|
||||
br []
|
||||
a [ _href "/prayer-requests" ] [ raw l["Do Not Show Inactive Requests"] ]
|
||||
|
@ -203,7 +203,7 @@ let maintain (groups : SmallGroupInfo list) ctx viewInfo =
|
||||
]
|
||||
a [ _href delAction
|
||||
_title s["Delete This Group"].Value
|
||||
_hxDelete delAction
|
||||
_hxPost delAction
|
||||
_hxConfirm delPrompt ] [
|
||||
iconSized 18 "delete_forever"
|
||||
]
|
||||
|
@ -41,6 +41,7 @@ module Configure =
|
||||
open Microsoft.Extensions.DependencyInjection
|
||||
open NeoSmart.Caching.Sqlite
|
||||
open NodaTime
|
||||
open Npgsql
|
||||
|
||||
/// Configure ASP.NET Core's service collection (dependency injection container)
|
||||
let services (svc : IServiceCollection) =
|
||||
@ -67,6 +68,13 @@ module Configure =
|
||||
let _ = svc.AddAntiforgery ()
|
||||
let _ = svc.AddRouting ()
|
||||
let _ = svc.AddSingleton<IClock> SystemClock.Instance
|
||||
let _ =
|
||||
svc.AddScoped<NpgsqlConnection>(fun sp ->
|
||||
let cfg = sp.GetService<IConfiguration> ()
|
||||
let conn = new NpgsqlConnection (cfg.GetConnectionString "PrayerTracker")
|
||||
conn.OpenAsync () |> Async.AwaitTask |> Async.RunSynchronously
|
||||
conn)
|
||||
let _ = NpgsqlConnection.GlobalTypeMapper.UseNodaTime ()
|
||||
()
|
||||
|
||||
open Giraffe
|
||||
@ -215,39 +223,28 @@ module App =
|
||||
use conn = new NpgsqlConnection (config.GetConnectionString "PrayerTracker")
|
||||
do! conn.OpenAsync ()
|
||||
let! v1Users =
|
||||
conn
|
||||
|> Sql.existingConnection
|
||||
Sql.existingConnection conn
|
||||
|> Sql.query "SELECT id, password_hash FROM pt.pt_user WHERE salt IS NULL"
|
||||
|> Sql.executeAsync (fun row -> UserId (row.uuid "id"), row.string "password_hash")
|
||||
for userId, oldHash in v1Users do
|
||||
let pw =
|
||||
[| 254uy
|
||||
yield! (Encoding.UTF8.GetBytes oldHash)
|
||||
|]
|
||||
|> Convert.ToBase64String
|
||||
let pw = Convert.ToBase64String [| 254uy; yield! (Encoding.UTF8.GetBytes oldHash) |]
|
||||
let! _ =
|
||||
conn
|
||||
|> Sql.existingConnection
|
||||
Sql.existingConnection conn
|
||||
|> Sql.query "UPDATE pt.pt_user SET password_hash = @hash WHERE id = @id"
|
||||
|> Sql.parameters [ "@id", Sql.uuid userId.Value; "@hash", Sql.string pw ]
|
||||
|> Sql.executeNonQueryAsync
|
||||
()
|
||||
printfn $"Updated {v1Users.Length} users with version 1 password"
|
||||
let! v2Users =
|
||||
conn
|
||||
|> Sql.existingConnection
|
||||
Sql.existingConnection conn
|
||||
|> Sql.query "SELECT id, password_hash, salt FROM pt.pt_user WHERE salt IS NOT NULL"
|
||||
|> Sql.executeAsync (fun row -> UserId (row.uuid "id"), row.string "password_hash", row.uuid "salt")
|
||||
for userId, oldHash, salt in v2Users do
|
||||
let pw =
|
||||
[| 255uy
|
||||
yield! (salt.ToByteArray ())
|
||||
yield! (Encoding.UTF8.GetBytes oldHash)
|
||||
|]
|
||||
|> Convert.ToBase64String
|
||||
Convert.ToBase64String
|
||||
[| 255uy; yield! (salt.ToByteArray ()); yield! (Encoding.UTF8.GetBytes oldHash) |]
|
||||
let! _ =
|
||||
conn
|
||||
|> Sql.existingConnection
|
||||
Sql.existingConnection conn
|
||||
|> Sql.query "UPDATE pt.pt_user SET password_hash = @hash WHERE id = @id"
|
||||
|> Sql.parameters [ "@id", Sql.uuid userId.Value; "@hash", Sql.string pw ]
|
||||
|> Sql.executeNonQueryAsync
|
||||
|
@ -17,16 +17,15 @@ let private findStats churchId conn = task {
|
||||
|
||||
/// POST /church/[church-id]/delete
|
||||
let delete chId : HttpHandler = requireAccess [ Admin ] >=> validateCsrf >=> fun next ctx -> task {
|
||||
let churchId = ChurchId chId
|
||||
use! conn = ctx.Conn
|
||||
let churchId = ChurchId chId
|
||||
let conn = ctx.Conn
|
||||
match! Churches.tryById churchId conn with
|
||||
| Some church ->
|
||||
let! _, stats = findStats churchId conn
|
||||
do! Churches.deleteById churchId conn
|
||||
let s = Views.I18N.localizer.Force ()
|
||||
addInfo ctx
|
||||
s["The church {0} and its {1} small groups (with {2} prayer request(s)) were deleted successfully; revoked access from {3} user(s)",
|
||||
church.Name, stats.SmallGroups, stats.PrayerRequests, stats.Users]
|
||||
ctx.Strings["The church {0} and its {1} small groups (with {2} prayer request(s)) were deleted successfully; revoked access from {3} user(s)",
|
||||
church.Name, stats.SmallGroups, stats.PrayerRequests, stats.Users]
|
||||
return! redirectTo false "/churches" next ctx
|
||||
| None -> return! fourOhFour ctx
|
||||
}
|
||||
@ -41,8 +40,7 @@ let edit churchId : HttpHandler = requireAccess [ Admin ] >=> fun next ctx -> ta
|
||||
|> Views.Church.edit EditChurch.empty ctx
|
||||
|> renderHtml next ctx
|
||||
else
|
||||
use! conn = ctx.Conn
|
||||
match! Churches.tryById (ChurchId churchId) conn with
|
||||
match! Churches.tryById (ChurchId churchId) ctx.Conn with
|
||||
| Some church ->
|
||||
return!
|
||||
viewInfo ctx
|
||||
@ -53,7 +51,7 @@ let edit churchId : HttpHandler = requireAccess [ Admin ] >=> fun next ctx -> ta
|
||||
|
||||
/// GET /churches
|
||||
let maintain : HttpHandler = requireAccess [ Admin ] >=> fun next ctx -> task {
|
||||
let! conn = ctx.Conn
|
||||
let conn = ctx.Conn
|
||||
let! churches = Churches.all conn
|
||||
let stats = churches |> List.map (fun c -> findStats c.Id conn |> Async.AwaitTask |> Async.RunSynchronously)
|
||||
return!
|
||||
@ -66,16 +64,14 @@ let maintain : HttpHandler = requireAccess [ Admin ] >=> fun next ctx -> task {
|
||||
let save : HttpHandler = requireAccess [ Admin ] >=> validateCsrf >=> fun next ctx -> task {
|
||||
match! ctx.TryBindFormAsync<EditChurch> () with
|
||||
| Ok model ->
|
||||
let! conn = ctx.Conn
|
||||
let! church =
|
||||
if model.IsNew then Task.FromResult (Some { Church.empty with Id = (Guid.NewGuid >> ChurchId) () })
|
||||
else Churches.tryById (idFromShort ChurchId model.ChurchId) conn
|
||||
else Churches.tryById (idFromShort ChurchId model.ChurchId) ctx.Conn
|
||||
match church with
|
||||
| Some ch ->
|
||||
do! Churches.save (model.PopulateChurch ch) conn
|
||||
let s = Views.I18N.localizer.Force ()
|
||||
let act = s[if model.IsNew then "Added" else "Updated"].Value.ToLower ()
|
||||
addInfo ctx s["Successfully {0} church “{1}”", act, model.Name]
|
||||
do! Churches.save (model.PopulateChurch ch) ctx.Conn
|
||||
let act = ctx.Strings[if model.IsNew then "Added" else "Updated"].Value.ToLower ()
|
||||
addInfo ctx ctx.Strings["Successfully {0} church “{1}”", act, model.Name]
|
||||
return! redirectTo false "/churches" next ctx
|
||||
| None -> return! fourOhFour ctx
|
||||
| Result.Error e -> return! bindError e next ctx
|
||||
|
@ -6,13 +6,13 @@ open Microsoft.AspNetCore.Mvc.Rendering
|
||||
|
||||
/// Create a select list from an enumeration
|
||||
let toSelectList<'T> valFunc textFunc withDefault emptyText (items : 'T seq) =
|
||||
match items with null -> nullArg "items" | _ -> ()
|
||||
[ match withDefault with
|
||||
| true ->
|
||||
let s = PrayerTracker.Views.I18N.localizer.Force ()
|
||||
yield SelectListItem ($"""— %A{s[emptyText]} —""", "")
|
||||
| _ -> ()
|
||||
yield! items |> Seq.map (fun x -> SelectListItem (textFunc x, valFunc x))
|
||||
if isNull items then nullArg (nameof items)
|
||||
[ match withDefault with
|
||||
| true ->
|
||||
let s = PrayerTracker.Views.I18N.localizer.Force ()
|
||||
SelectListItem ($"""— %A{s[emptyText]} —""", "")
|
||||
| _ -> ()
|
||||
yield! items |> Seq.map (fun x -> SelectListItem (textFunc x, valFunc x))
|
||||
]
|
||||
|
||||
/// Create a select list from an enumeration
|
||||
@ -151,8 +151,7 @@ let requireAccess levels : HttpHandler = fun next ctx -> task {
|
||||
| _, Some _ when List.contains Group levels -> return! next ctx
|
||||
| Some u, _ when List.contains Admin levels && u.IsAdmin -> return! next ctx
|
||||
| _, _ when List.contains Admin levels ->
|
||||
let s = Views.I18N.localizer.Force ()
|
||||
addError ctx s["You are not authorized to view the requested page."]
|
||||
addError ctx ctx.Strings["You are not authorized to view the requested page."]
|
||||
return! redirectTo false "/unauthorized" next ctx
|
||||
| _, _ when List.contains User levels ->
|
||||
// Redirect to the user log on page
|
||||
@ -162,7 +161,6 @@ let requireAccess levels : HttpHandler = fun next ctx -> task {
|
||||
// Redirect to the small group log on page
|
||||
return! redirectTo false "/small-group/log-on" next ctx
|
||||
| _, _ ->
|
||||
let s = Views.I18N.localizer.Force ()
|
||||
addError ctx s["You are not authorized to view the requested page."]
|
||||
addError ctx ctx.Strings["You are not authorized to view the requested page."]
|
||||
return! redirectTo false "/unauthorized" next ctx
|
||||
}
|
||||
|
@ -86,9 +86,7 @@ type HttpContext with
|
||||
})
|
||||
|
||||
/// The PostgreSQL connection (configured via DI)
|
||||
member this.Conn = backgroundTask {
|
||||
return! this.LazyConn.Force ()
|
||||
}
|
||||
member this.Conn = this.GetService<NpgsqlConnection> ()
|
||||
|
||||
/// The system clock (via DI)
|
||||
member this.Clock = this.GetService<IClock> ()
|
||||
@ -96,6 +94,9 @@ type HttpContext with
|
||||
/// The current instant
|
||||
member this.Now = this.Clock.GetCurrentInstant ()
|
||||
|
||||
/// The common string localizer
|
||||
member this.Strings = Views.I18N.localizer.Force ()
|
||||
|
||||
/// The currently logged on small group (sets the value in the session if it is missing)
|
||||
member this.CurrentGroup () = task {
|
||||
match this.Session.CurrentGroup with
|
||||
@ -103,8 +104,7 @@ type HttpContext with
|
||||
| None ->
|
||||
match this.User.SmallGroupId with
|
||||
| Some groupId ->
|
||||
let! conn = this.Conn
|
||||
match! SmallGroups.tryByIdWithPreferences groupId conn with
|
||||
match! SmallGroups.tryByIdWithPreferences groupId this.Conn with
|
||||
| Some group ->
|
||||
this.Session.CurrentGroup <- Some group
|
||||
return Some group
|
||||
@ -119,11 +119,10 @@ type HttpContext with
|
||||
| None ->
|
||||
match this.User.UserId with
|
||||
| Some userId ->
|
||||
let! conn = this.Conn
|
||||
match! Users.tryById userId conn with
|
||||
match! Users.tryById userId this.Conn with
|
||||
| Some user ->
|
||||
// Set last seen for user
|
||||
do! Users.updateLastSeen userId this.Now conn
|
||||
do! Users.updateLastSeen userId this.Now this.Conn
|
||||
this.Session.CurrentUser <- Some user
|
||||
return Some user
|
||||
| None -> return None
|
||||
|
@ -61,8 +61,7 @@ open Microsoft.AspNetCore.Authentication.Cookies
|
||||
let logOff : HttpHandler = requireAccess [ AccessLevel.Public ] >=> fun next ctx -> task {
|
||||
ctx.Session.Clear ()
|
||||
do! ctx.SignOutAsync CookieAuthenticationDefaults.AuthenticationScheme
|
||||
let s = Views.I18N.localizer.Force ()
|
||||
addHtmlInfo ctx s["Log Off Successful • Have a nice day!"]
|
||||
addHtmlInfo ctx ctx.Strings["Log Off Successful • Have a nice day!"]
|
||||
return! redirectTo false "/" next ctx
|
||||
}
|
||||
|
||||
|
@ -9,12 +9,10 @@ open PrayerTracker.ViewModels
|
||||
|
||||
/// Retrieve a prayer request, and ensure that it belongs to the current class
|
||||
let private findRequest (ctx : HttpContext) reqId = task {
|
||||
let! conn = ctx.Conn
|
||||
match! PrayerRequests.tryById reqId conn with
|
||||
match! PrayerRequests.tryById reqId ctx.Conn with
|
||||
| Some req when req.SmallGroupId = ctx.Session.CurrentGroup.Value.Id -> return Ok req
|
||||
| Some _ ->
|
||||
let s = Views.I18N.localizer.Force ()
|
||||
addError ctx s["The prayer request you tried to access is not assigned to your group"]
|
||||
addError ctx ctx.Strings["The prayer request you tried to access is not assigned to your group"]
|
||||
return Result.Error (redirectTo false "/unauthorized" earlyReturn ctx)
|
||||
| None -> return Result.Error (fourOhFour ctx)
|
||||
}
|
||||
@ -23,7 +21,6 @@ let private findRequest (ctx : HttpContext) reqId = task {
|
||||
let private generateRequestList (ctx : HttpContext) date = task {
|
||||
let group = ctx.Session.CurrentGroup.Value
|
||||
let listDate = match date with Some d -> d | None -> SmallGroup.localDateNow ctx.Clock group
|
||||
let! conn = ctx.Conn
|
||||
let! reqs =
|
||||
PrayerRequests.forGroup
|
||||
{ SmallGroup = group
|
||||
@ -31,7 +28,7 @@ let private generateRequestList (ctx : HttpContext) date = task {
|
||||
ListDate = Some listDate
|
||||
ActiveOnly = true
|
||||
PageNumber = 0
|
||||
} conn
|
||||
} ctx.Conn
|
||||
return
|
||||
{ Requests = reqs
|
||||
Date = listDate
|
||||
@ -65,7 +62,7 @@ let edit reqId : HttpHandler = requireAccess [ User ] >=> fun next ctx -> task {
|
||||
else
|
||||
match! findRequest ctx requestId with
|
||||
| Ok req ->
|
||||
let s = Views.I18N.localizer.Force ()
|
||||
let s = ctx.Strings
|
||||
if PrayerRequest.isExpired now group req then
|
||||
{ UserMessage.warning with
|
||||
Text = htmlLocString s["This request is expired."]
|
||||
@ -84,12 +81,11 @@ let edit reqId : HttpHandler = requireAccess [ User ] >=> fun next ctx -> task {
|
||||
|
||||
/// GET /prayer-requests/email/[date]
|
||||
let email date : HttpHandler = requireAccess [ User ] >=> fun next ctx -> task {
|
||||
let s = Views.I18N.localizer.Force ()
|
||||
let s = ctx.Strings
|
||||
let listDate = parseListDate (Some date)
|
||||
let group = ctx.Session.CurrentGroup.Value
|
||||
let! list = generateRequestList ctx listDate
|
||||
let! conn = ctx.Conn
|
||||
let! recipients = Members.forGroup group.Id conn
|
||||
let group = ctx.Session.CurrentGroup.Value
|
||||
let! recipients = Members.forGroup group.Id ctx.Conn
|
||||
use! client = Email.getConnection ()
|
||||
do! Email.sendEmails
|
||||
{ Client = client
|
||||
@ -111,10 +107,8 @@ let delete reqId : HttpHandler = requireAccess [ User ] >=> validateCsrf >=> fun
|
||||
let requestId = PrayerRequestId reqId
|
||||
match! findRequest ctx requestId with
|
||||
| Ok req ->
|
||||
let s = Views.I18N.localizer.Force ()
|
||||
let! conn = ctx.Conn
|
||||
do! PrayerRequests.deleteById req.Id conn
|
||||
addInfo ctx s["The prayer request was deleted successfully"]
|
||||
do! PrayerRequests.deleteById req.Id ctx.Conn
|
||||
addInfo ctx ctx.Strings["The prayer request was deleted successfully"]
|
||||
return! redirectTo false "/prayer-requests" next ctx
|
||||
| Result.Error e -> return! e
|
||||
}
|
||||
@ -124,18 +118,15 @@ let expire reqId : HttpHandler = requireAccess [ User ] >=> fun next ctx -> task
|
||||
let requestId = PrayerRequestId reqId
|
||||
match! findRequest ctx requestId with
|
||||
| Ok req ->
|
||||
let s = Views.I18N.localizer.Force ()
|
||||
let! conn = ctx.Conn
|
||||
do! PrayerRequests.updateExpiration { req with Expiration = Forced } false conn
|
||||
addInfo ctx s["Successfully {0} prayer request", s["Expired"].Value.ToLower ()]
|
||||
do! PrayerRequests.updateExpiration { req with Expiration = Forced } false ctx.Conn
|
||||
addInfo ctx ctx.Strings["Successfully {0} prayer request", ctx.Strings["Expired"].Value.ToLower ()]
|
||||
return! redirectTo false "/prayer-requests" next ctx
|
||||
| Result.Error e -> return! e
|
||||
}
|
||||
|
||||
/// GET /prayer-requests/[group-id]/list
|
||||
let list groupId : HttpHandler = requireAccess [ AccessLevel.Public ] >=> fun next ctx -> task {
|
||||
let! conn = ctx.Conn
|
||||
match! SmallGroups.tryByIdWithPreferences groupId conn with
|
||||
match! SmallGroups.tryByIdWithPreferences groupId ctx.Conn with
|
||||
| Some group when group.Preferences.IsPublic ->
|
||||
let! reqs =
|
||||
PrayerRequests.forGroup
|
||||
@ -144,7 +135,7 @@ let list groupId : HttpHandler = requireAccess [ AccessLevel.Public ] >=> fun ne
|
||||
ListDate = None
|
||||
ActiveOnly = true
|
||||
PageNumber = 0
|
||||
} conn
|
||||
} ctx.Conn
|
||||
return!
|
||||
viewInfo ctx
|
||||
|> Views.PrayerRequest.list
|
||||
@ -157,16 +148,14 @@ let list groupId : HttpHandler = requireAccess [ AccessLevel.Public ] >=> fun ne
|
||||
}
|
||||
|> renderHtml next ctx
|
||||
| Some _ ->
|
||||
let s = Views.I18N.localizer.Force ()
|
||||
addError ctx s["The request list for the group you tried to view is not public."]
|
||||
addError ctx ctx.Strings["The request list for the group you tried to view is not public."]
|
||||
return! redirectTo false "/unauthorized" next ctx
|
||||
| None -> return! fourOhFour ctx
|
||||
}
|
||||
|
||||
/// GET /prayer-requests/lists
|
||||
let lists : HttpHandler = requireAccess [ AccessLevel.Public ] >=> fun next ctx -> task {
|
||||
let! conn = ctx.Conn
|
||||
let! groups = SmallGroups.listPublicAndProtected conn
|
||||
let! groups = SmallGroups.listPublicAndProtected ctx.Conn
|
||||
return!
|
||||
viewInfo ctx
|
||||
|> Views.PrayerRequest.lists groups
|
||||
@ -183,10 +172,9 @@ let maintain onlyActive : HttpHandler = requireAccess [ User ] >=> fun next ctx
|
||||
| Ok pg -> match Int32.TryParse pg with true, p -> p | false, _ -> 1
|
||||
| Result.Error _ -> 1
|
||||
let! model = backgroundTask {
|
||||
let! conn = ctx.Conn
|
||||
match ctx.GetQueryStringValue "search" with
|
||||
| Ok search ->
|
||||
let! reqs = PrayerRequests.searchForGroup group search pageNbr conn
|
||||
let! reqs = PrayerRequests.searchForGroup group search pageNbr ctx.Conn
|
||||
return
|
||||
{ MaintainRequests.empty with
|
||||
Requests = reqs
|
||||
@ -201,7 +189,7 @@ let maintain onlyActive : HttpHandler = requireAccess [ User ] >=> fun next ctx
|
||||
ListDate = None
|
||||
ActiveOnly = onlyActive
|
||||
PageNumber = pageNbr
|
||||
} conn
|
||||
} ctx.Conn
|
||||
return
|
||||
{ MaintainRequests.empty with
|
||||
Requests = reqs
|
||||
@ -228,10 +216,8 @@ let restore reqId : HttpHandler = requireAccess [ User ] >=> fun next ctx -> tas
|
||||
let requestId = PrayerRequestId reqId
|
||||
match! findRequest ctx requestId with
|
||||
| Ok req ->
|
||||
let s = Views.I18N.localizer.Force ()
|
||||
let! conn = ctx.Conn
|
||||
do! PrayerRequests.updateExpiration { req with Expiration = Automatic; UpdatedDate = ctx.Now } true conn
|
||||
addInfo ctx s["Successfully {0} prayer request", s["Restored"].Value.ToLower ()]
|
||||
do! PrayerRequests.updateExpiration { req with Expiration = Automatic; UpdatedDate = ctx.Now } true ctx.Conn
|
||||
addInfo ctx ctx.Strings["Successfully {0} prayer request", ctx.Strings["Restored"].Value.ToLower ()]
|
||||
return! redirectTo false "/prayer-requests" next ctx
|
||||
| Result.Error e -> return! e
|
||||
}
|
||||
@ -243,7 +229,6 @@ let save : HttpHandler = requireAccess [ User ] >=> validateCsrf >=> fun next ct
|
||||
match! ctx.TryBindFormAsync<EditRequest> () with
|
||||
| Ok model ->
|
||||
let group = ctx.Session.CurrentGroup.Value
|
||||
let! conn = ctx.Conn
|
||||
let! req =
|
||||
if model.IsNew then
|
||||
{ PrayerRequest.empty with
|
||||
@ -252,7 +237,7 @@ let save : HttpHandler = requireAccess [ User ] >=> validateCsrf >=> fun next ct
|
||||
UserId = ctx.User.UserId.Value
|
||||
}
|
||||
|> (Some >> Task.FromResult)
|
||||
else PrayerRequests.tryById (idFromShort PrayerRequestId model.RequestId) conn
|
||||
else PrayerRequests.tryById (idFromShort PrayerRequestId model.RequestId) ctx.Conn
|
||||
match req with
|
||||
| Some pr when pr.SmallGroupId = group.Id ->
|
||||
let now = SmallGroup.localDateNow ctx.Clock group
|
||||
@ -272,10 +257,9 @@ let save : HttpHandler = requireAccess [ User ] >=> validateCsrf >=> fun next ct
|
||||
{ it with EnteredDate = dt; UpdatedDate = dt }
|
||||
| it when defaultArg model.SkipDateUpdate false -> it
|
||||
| it -> { it with UpdatedDate = ctx.Now }
|
||||
do! PrayerRequests.save updated conn
|
||||
let s = Views.I18N.localizer.Force ()
|
||||
do! PrayerRequests.save updated ctx.Conn
|
||||
let act = if model.IsNew then "Added" else "Updated"
|
||||
addInfo ctx s["Successfully {0} prayer request", s[act].Value.ToLower ()]
|
||||
addInfo ctx ctx.Strings["Successfully {0} prayer request", ctx.Strings[act].Value.ToLower ()]
|
||||
return! redirectTo false "/prayer-requests" next ctx
|
||||
| Some _
|
||||
| None -> return! fourOhFour ctx
|
||||
|
@ -15,31 +15,28 @@ let announcement : HttpHandler = requireAccess [ User ] >=> fun next ctx ->
|
||||
|
||||
/// POST /small-group/[group-id]/delete
|
||||
let delete grpId : HttpHandler = requireAccess [ Admin ] >=> validateCsrf >=> fun next ctx -> task {
|
||||
let s = Views.I18N.localizer.Force ()
|
||||
let groupId = SmallGroupId grpId
|
||||
let! conn = ctx.Conn
|
||||
let groupId = SmallGroupId grpId
|
||||
let conn = ctx.Conn
|
||||
match! SmallGroups.tryById groupId conn with
|
||||
| Some grp ->
|
||||
let! reqs = PrayerRequests.countByGroup groupId conn
|
||||
let! users = Users.countByGroup groupId conn
|
||||
do! SmallGroups.deleteById groupId conn
|
||||
addInfo ctx
|
||||
s["The group {0} and its {1} prayer request(s) were deleted successfully; revoked access from {2} user(s)",
|
||||
grp.Name, reqs, users]
|
||||
ctx.Strings["The group {0} and its {1} prayer request(s) were deleted successfully; revoked access from {2} user(s)",
|
||||
grp.Name, reqs, users]
|
||||
return! redirectTo false "/small-groups" next ctx
|
||||
| None -> return! fourOhFour ctx
|
||||
}
|
||||
|
||||
/// POST /small-group/member/[member-id]/delete
|
||||
let deleteMember mbrId : HttpHandler = requireAccess [ User ] >=> validateCsrf >=> fun next ctx -> task {
|
||||
let s = Views.I18N.localizer.Force ()
|
||||
let group = ctx.Session.CurrentGroup.Value
|
||||
let memberId = MemberId mbrId
|
||||
let! conn = ctx.Conn
|
||||
match! Members.tryById memberId conn with
|
||||
let group = ctx.Session.CurrentGroup.Value
|
||||
let memberId = MemberId mbrId
|
||||
match! Members.tryById memberId ctx.Conn with
|
||||
| Some mbr when mbr.SmallGroupId = group.Id ->
|
||||
do! Members.deleteById memberId conn
|
||||
addHtmlInfo ctx s["The group member “{0}” was deleted successfully", mbr.Name]
|
||||
do! Members.deleteById memberId ctx.Conn
|
||||
addHtmlInfo ctx ctx.Strings["The group member “{0}” was deleted successfully", mbr.Name]
|
||||
return! redirectTo false "/small-group/members" next ctx
|
||||
| Some _
|
||||
| None -> return! fourOhFour ctx
|
||||
@ -47,8 +44,7 @@ let deleteMember mbrId : HttpHandler = requireAccess [ User ] >=> validateCsrf >
|
||||
|
||||
/// GET /small-group/[group-id]/edit
|
||||
let edit grpId : HttpHandler = requireAccess [ Admin ] >=> fun next ctx -> task {
|
||||
let! conn = ctx.Conn
|
||||
let! churches = Churches.all conn
|
||||
let! churches = Churches.all ctx.Conn
|
||||
let groupId = SmallGroupId grpId
|
||||
if groupId.Value = Guid.Empty then
|
||||
return!
|
||||
@ -56,7 +52,7 @@ let edit grpId : HttpHandler = requireAccess [ Admin ] >=> fun next ctx -> task
|
||||
|> Views.SmallGroup.edit EditSmallGroup.empty churches ctx
|
||||
|> renderHtml next ctx
|
||||
else
|
||||
match! SmallGroups.tryById groupId conn with
|
||||
match! SmallGroups.tryById groupId ctx.Conn with
|
||||
| Some grp ->
|
||||
return!
|
||||
viewInfo ctx
|
||||
@ -67,9 +63,8 @@ let edit grpId : HttpHandler = requireAccess [ Admin ] >=> fun next ctx -> task
|
||||
|
||||
/// GET /small-group/member/[member-id]/edit
|
||||
let editMember mbrId : HttpHandler = requireAccess [ User ] >=> fun next ctx -> task {
|
||||
let s = Views.I18N.localizer.Force ()
|
||||
let group = ctx.Session.CurrentGroup.Value
|
||||
let types = ReferenceList.emailTypeList group.Preferences.DefaultEmailType s
|
||||
let types = ReferenceList.emailTypeList group.Preferences.DefaultEmailType ctx.Strings
|
||||
let memberId = MemberId mbrId
|
||||
if memberId.Value = Guid.Empty then
|
||||
return!
|
||||
@ -77,8 +72,7 @@ let editMember mbrId : HttpHandler = requireAccess [ User ] >=> fun next ctx ->
|
||||
|> Views.SmallGroup.editMember EditMember.empty types ctx
|
||||
|> renderHtml next ctx
|
||||
else
|
||||
let! conn = ctx.Conn
|
||||
match! Members.tryById memberId conn with
|
||||
match! Members.tryById memberId ctx.Conn with
|
||||
| Some mbr when mbr.SmallGroupId = group.Id ->
|
||||
return!
|
||||
viewInfo ctx
|
||||
@ -90,8 +84,7 @@ let editMember mbrId : HttpHandler = requireAccess [ User ] >=> fun next ctx ->
|
||||
|
||||
/// GET /small-group/log-on/[group-id?]
|
||||
let logOn grpId : HttpHandler = requireAccess [ AccessLevel.Public ] >=> fun next ctx -> task {
|
||||
let! conn = ctx.Conn
|
||||
let! groups = SmallGroups.listProtected conn
|
||||
let! groups = SmallGroups.listProtected ctx.Conn
|
||||
let groupId = match grpId with Some gid -> shortGuid gid | None -> ""
|
||||
return!
|
||||
{ viewInfo ctx with HelpLink = Some Help.logOn }
|
||||
@ -107,9 +100,7 @@ open Microsoft.AspNetCore.Authentication.Cookies
|
||||
let logOnSubmit : HttpHandler = requireAccess [ AccessLevel.Public ] >=> validateCsrf >=> fun next ctx -> task {
|
||||
match! ctx.TryBindFormAsync<GroupLogOn> () with
|
||||
| Ok model ->
|
||||
let s = Views.I18N.localizer.Force ()
|
||||
let! conn = ctx.Conn
|
||||
match! SmallGroups.logOn (idFromShort SmallGroupId model.SmallGroupId) model.Password conn with
|
||||
match! SmallGroups.logOn (idFromShort SmallGroupId model.SmallGroupId) model.Password ctx.Conn with
|
||||
| Some group ->
|
||||
ctx.Session.CurrentGroup <- Some group
|
||||
let identity = ClaimsIdentity (
|
||||
@ -120,18 +111,17 @@ let logOnSubmit : HttpHandler = requireAccess [ AccessLevel.Public ] >=> validat
|
||||
AuthenticationProperties (
|
||||
IssuedUtc = DateTimeOffset.UtcNow,
|
||||
IsPersistent = defaultArg model.RememberMe false))
|
||||
addInfo ctx s["Log On Successful • Welcome to {0}", s["PrayerTracker"]]
|
||||
addInfo ctx ctx.Strings["Log On Successful • Welcome to {0}", ctx.Strings["PrayerTracker"]]
|
||||
return! redirectTo false "/prayer-requests/view" next ctx
|
||||
| None ->
|
||||
addError ctx s["Password incorrect - login unsuccessful"]
|
||||
addError ctx ctx.Strings["Password incorrect - login unsuccessful"]
|
||||
return! redirectTo false $"/small-group/log-on/{model.SmallGroupId}" next ctx
|
||||
| Result.Error e -> return! bindError e next ctx
|
||||
}
|
||||
|
||||
/// GET /small-groups
|
||||
let maintain : HttpHandler = requireAccess [ Admin ] >=> fun next ctx -> task {
|
||||
let! conn = ctx.Conn
|
||||
let! groups = SmallGroups.infoForAll conn
|
||||
let! groups = SmallGroups.infoForAll ctx.Conn
|
||||
return!
|
||||
viewInfo ctx
|
||||
|> Views.SmallGroup.maintain groups ctx
|
||||
@ -141,10 +131,8 @@ let maintain : HttpHandler = requireAccess [ Admin ] >=> fun next ctx -> task {
|
||||
/// GET /small-group/members
|
||||
let members : HttpHandler = requireAccess [ User ] >=> fun next ctx -> task {
|
||||
let group = ctx.Session.CurrentGroup.Value
|
||||
let s = Views.I18N.localizer.Force ()
|
||||
let! conn = ctx.Conn
|
||||
let! members = Members.forGroup group.Id conn
|
||||
let types = ReferenceList.emailTypeList group.Preferences.DefaultEmailType s |> Map.ofSeq
|
||||
let! members = Members.forGroup group.Id ctx.Conn
|
||||
let types = ReferenceList.emailTypeList group.Preferences.DefaultEmailType ctx.Strings |> Map.ofSeq
|
||||
return!
|
||||
{ viewInfo ctx with HelpLink = Some Help.maintainGroupMembers }
|
||||
|> Views.SmallGroup.members members types ctx
|
||||
@ -154,7 +142,7 @@ let members : HttpHandler = requireAccess [ User ] >=> fun next ctx -> task {
|
||||
/// GET /small-group
|
||||
let overview : HttpHandler = requireAccess [ User ] >=> fun next ctx -> task {
|
||||
let group = ctx.Session.CurrentGroup.Value
|
||||
let! conn = ctx.Conn
|
||||
let conn = ctx.Conn
|
||||
let! reqs = PrayerRequests.forGroup
|
||||
{ SmallGroup = group
|
||||
Clock = ctx.Clock
|
||||
@ -199,16 +187,14 @@ open System.Threading.Tasks
|
||||
let save : HttpHandler = requireAccess [ Admin ] >=> validateCsrf >=> fun next ctx -> task {
|
||||
match! ctx.TryBindFormAsync<EditSmallGroup> () with
|
||||
| Ok model ->
|
||||
let s = Views.I18N.localizer.Force ()
|
||||
let! conn = ctx.Conn
|
||||
let! tryGroup =
|
||||
if model.IsNew then Task.FromResult (Some { SmallGroup.empty with Id = (Guid.NewGuid >> SmallGroupId) () })
|
||||
else SmallGroups.tryById (idFromShort SmallGroupId model.SmallGroupId) conn
|
||||
else SmallGroups.tryById (idFromShort SmallGroupId model.SmallGroupId) ctx.Conn
|
||||
match tryGroup with
|
||||
| Some group ->
|
||||
do! SmallGroups.save (model.populateGroup group) model.IsNew conn
|
||||
let act = s[if model.IsNew then "Added" else "Updated"].Value.ToLower ()
|
||||
addHtmlInfo ctx s["Successfully {0} group “{1}”", act, model.Name]
|
||||
do! SmallGroups.save (model.populateGroup group) model.IsNew ctx.Conn
|
||||
let act = ctx.Strings[if model.IsNew then "Added" else "Updated"].Value.ToLower ()
|
||||
addHtmlInfo ctx ctx.Strings["Successfully {0} group “{1}”", act, model.Name]
|
||||
return! redirectTo false "/small-groups" next ctx
|
||||
| None -> return! fourOhFour ctx
|
||||
| Result.Error e -> return! bindError e next ctx
|
||||
@ -219,11 +205,10 @@ let saveMember : HttpHandler = requireAccess [ User ] >=> validateCsrf >=> fun n
|
||||
match! ctx.TryBindFormAsync<EditMember> () with
|
||||
| Ok model ->
|
||||
let group = ctx.Session.CurrentGroup.Value
|
||||
let! conn = ctx.Conn
|
||||
let! tryMbr =
|
||||
if model.IsNew then
|
||||
Task.FromResult (Some { Member.empty with Id = (Guid.NewGuid >> MemberId) (); SmallGroupId = group.Id })
|
||||
else Members.tryById (idFromShort MemberId model.MemberId) conn
|
||||
else Members.tryById (idFromShort MemberId model.MemberId) ctx.Conn
|
||||
match tryMbr with
|
||||
| Some mbr when mbr.SmallGroupId = group.Id ->
|
||||
do! Members.save
|
||||
@ -231,10 +216,9 @@ let saveMember : HttpHandler = requireAccess [ User ] >=> validateCsrf >=> fun n
|
||||
Name = model.Name
|
||||
Email = model.Email
|
||||
Format = String.noneIfBlank model.Format |> Option.map EmailFormat.fromCode
|
||||
} conn
|
||||
let s = Views.I18N.localizer.Force ()
|
||||
let act = s[if model.IsNew then "Added" else "Updated"].Value.ToLower ()
|
||||
addInfo ctx s["Successfully {0} group member", act]
|
||||
} ctx.Conn
|
||||
let act = ctx.Strings[if model.IsNew then "Added" else "Updated"].Value.ToLower ()
|
||||
addInfo ctx ctx.Strings["Successfully {0} group member", act]
|
||||
return! redirectTo false "/small-group/members" next ctx
|
||||
| Some _
|
||||
| None -> return! fourOhFour ctx
|
||||
@ -249,15 +233,13 @@ let savePreferences : HttpHandler = requireAccess [ User ] >=> validateCsrf >=>
|
||||
// we can repopulate the session instance. That way, if the update fails, the page should still show the
|
||||
// database values, not the then out-of-sync session ones.
|
||||
let group = ctx.Session.CurrentGroup.Value
|
||||
let! conn = ctx.Conn
|
||||
match! SmallGroups.tryByIdWithPreferences group.Id conn with
|
||||
| Some grp ->
|
||||
let pref = model.PopulatePreferences grp.Preferences
|
||||
do! SmallGroups.savePreferences pref conn
|
||||
match! SmallGroups.tryByIdWithPreferences group.Id ctx.Conn with
|
||||
| Some group ->
|
||||
let pref = model.PopulatePreferences group.Preferences
|
||||
do! SmallGroups.savePreferences pref ctx.Conn
|
||||
// Refresh session instance
|
||||
ctx.Session.CurrentGroup <- Some { grp with Preferences = pref }
|
||||
let s = Views.I18N.localizer.Force ()
|
||||
addInfo ctx s["Group preferences updated successfully"]
|
||||
ctx.Session.CurrentGroup <- Some { group with Preferences = pref }
|
||||
addInfo ctx ctx.Strings["Group preferences updated successfully"]
|
||||
return! redirectTo false "/small-group/preferences" next ctx
|
||||
| None -> return! fourOhFour ctx
|
||||
| Result.Error e -> return! bindError e next ctx
|
||||
@ -274,7 +256,7 @@ let sendAnnouncement : HttpHandler = requireAccess [ User ] >=> validateCsrf >=>
|
||||
let pref = group.Preferences
|
||||
let usr = ctx.Session.CurrentUser.Value
|
||||
let now = SmallGroup.localTimeNow ctx.Clock group
|
||||
let s = Views.I18N.localizer.Force ()
|
||||
let s = ctx.Strings
|
||||
// Reformat the text to use the class's font stylings
|
||||
let requestText = ckEditorToText model.Text
|
||||
let htmlText =
|
||||
@ -282,12 +264,11 @@ let sendAnnouncement : HttpHandler = requireAccess [ User ] >=> validateCsrf >=>
|
||||
|> renderHtmlNode
|
||||
let plainText = (htmlToPlainText >> wordWrap 74) htmlText
|
||||
// Send the e-mails
|
||||
let! conn = ctx.Conn
|
||||
let! recipients = task {
|
||||
if model.SendToClass = "N" && usr.IsAdmin then
|
||||
let! users = Users.all conn
|
||||
let! users = Users.all ctx.Conn
|
||||
return users |> List.map (fun u -> { Member.empty with Name = u.Name; Email = u.Email })
|
||||
else return! Members.forGroup group.Id conn
|
||||
else return! Members.forGroup group.Id ctx.Conn
|
||||
}
|
||||
use! client = Email.getConnection ()
|
||||
do! Email.sendEmails
|
||||
@ -316,7 +297,7 @@ let sendAnnouncement : HttpHandler = requireAccess [ User ] >=> validateCsrf >=>
|
||||
Text = requestText
|
||||
EnteredDate = now.Date.AtStartOfDayInZone(zone).ToInstant()
|
||||
UpdatedDate = now.InZoneLeniently(zone).ToInstant()
|
||||
} conn
|
||||
} ctx.Conn
|
||||
// Tell 'em what they've won, Johnny!
|
||||
let toWhom =
|
||||
if model.SendToClass = "N" then s["{0} users", s["PrayerTracker"]].Value
|
||||
|
@ -78,12 +78,10 @@ let sanitizeUrl providedUrl defaultUrl =
|
||||
let changePassword : HttpHandler = requireAccess [ User ] >=> validateCsrf >=> fun next ctx -> task {
|
||||
match! ctx.TryBindFormAsync<ChangePassword> () with
|
||||
| Ok model ->
|
||||
let s = Views.I18N.localizer.Force ()
|
||||
let curUsr = ctx.Session.CurrentUser.Value
|
||||
let hasher = PrayerTrackerPasswordHasher ()
|
||||
let! conn = ctx.Conn
|
||||
let! user = task {
|
||||
match! Users.tryById curUsr.Id conn with
|
||||
match! Users.tryById curUsr.Id ctx.Conn with
|
||||
| Some usr ->
|
||||
if hasher.VerifyHashedPassword (usr, usr.PasswordHash, model.OldPassword)
|
||||
= PasswordVerificationResult.Success then
|
||||
@ -93,27 +91,25 @@ let changePassword : HttpHandler = requireAccess [ User ] >=> validateCsrf >=> f
|
||||
}
|
||||
match user with
|
||||
| Some usr when model.NewPassword = model.NewPasswordConfirm ->
|
||||
do! Users.updatePassword { usr with PasswordHash = hasher.HashPassword (usr, model.NewPassword) } conn
|
||||
addInfo ctx s["Your password was changed successfully"]
|
||||
do! Users.updatePassword { usr with PasswordHash = hasher.HashPassword (usr, model.NewPassword) } ctx.Conn
|
||||
addInfo ctx ctx.Strings["Your password was changed successfully"]
|
||||
return! redirectTo false "/" next ctx
|
||||
| Some _ ->
|
||||
addError ctx s["The new passwords did not match - your password was NOT changed"]
|
||||
addError ctx ctx.Strings["The new passwords did not match - your password was NOT changed"]
|
||||
return! redirectTo false "/user/password" next ctx
|
||||
| None ->
|
||||
addError ctx s["The old password was incorrect - your password was NOT changed"]
|
||||
addError ctx ctx.Strings["The old password was incorrect - your password was NOT changed"]
|
||||
return! redirectTo false "/user/password" next ctx
|
||||
| Result.Error e -> return! bindError e next ctx
|
||||
}
|
||||
|
||||
/// POST /user/[user-id]/delete
|
||||
let delete usrId : HttpHandler = requireAccess [ Admin ] >=> validateCsrf >=> fun next ctx -> task {
|
||||
let userId = UserId usrId
|
||||
let! conn = ctx.Conn
|
||||
match! Users.tryById userId conn with
|
||||
let userId = UserId usrId
|
||||
match! Users.tryById userId ctx.Conn with
|
||||
| Some user ->
|
||||
do! Users.deleteById userId conn
|
||||
let s = Views.I18N.localizer.Force ()
|
||||
addInfo ctx s["Successfully deleted user {0}", user.Name]
|
||||
do! Users.deleteById userId ctx.Conn
|
||||
addInfo ctx ctx.Strings["Successfully deleted user {0}", user.Name]
|
||||
return! redirectTo false "/users" next ctx
|
||||
| _ -> return! fourOhFour ctx
|
||||
}
|
||||
@ -128,11 +124,10 @@ open Microsoft.AspNetCore.Html
|
||||
let doLogOn : HttpHandler = requireAccess [ AccessLevel.Public ] >=> validateCsrf >=> fun next ctx -> task {
|
||||
match! ctx.TryBindFormAsync<UserLogOn> () with
|
||||
| Ok model ->
|
||||
let s = Views.I18N.localizer.Force ()
|
||||
let! conn = ctx.Conn
|
||||
match! findUserByPassword model conn with
|
||||
let s = ctx.Strings
|
||||
match! findUserByPassword model ctx.Conn with
|
||||
| Some user ->
|
||||
match! SmallGroups.tryByIdWithPreferences (idFromShort SmallGroupId model.SmallGroupId) conn with
|
||||
match! SmallGroups.tryByIdWithPreferences (idFromShort SmallGroupId model.SmallGroupId) ctx.Conn with
|
||||
| Some group ->
|
||||
ctx.Session.CurrentUser <- Some user
|
||||
ctx.Session.CurrentGroup <- Some group
|
||||
@ -146,7 +141,7 @@ let doLogOn : HttpHandler = requireAccess [ AccessLevel.Public ] >=> validateCsr
|
||||
AuthenticationProperties (
|
||||
IssuedUtc = DateTimeOffset.UtcNow,
|
||||
IsPersistent = defaultArg model.RememberMe false))
|
||||
do! Users.updateLastSeen user.Id ctx.Now conn
|
||||
do! Users.updateLastSeen user.Id ctx.Now ctx.Conn
|
||||
addHtmlInfo ctx s["Log On Successful • Welcome to {0}", s["PrayerTracker"]]
|
||||
return! redirectTo false (sanitizeUrl model.RedirectUrl "/small-group") next ctx
|
||||
| None -> return! fourOhFour ctx
|
||||
@ -177,8 +172,7 @@ let edit usrId : HttpHandler = requireAccess [ Admin ] >=> fun next ctx -> task
|
||||
|> Views.User.edit EditUser.empty ctx
|
||||
|> renderHtml next ctx
|
||||
else
|
||||
let! conn = ctx.Conn
|
||||
match! Users.tryById userId conn with
|
||||
match! Users.tryById userId ctx.Conn with
|
||||
| Some user ->
|
||||
return!
|
||||
viewInfo ctx
|
||||
@ -189,14 +183,12 @@ let edit usrId : HttpHandler = requireAccess [ Admin ] >=> fun next ctx -> task
|
||||
|
||||
/// GET /user/log-on
|
||||
let logOn : HttpHandler = requireAccess [ AccessLevel.Public ] >=> fun next ctx -> task {
|
||||
let s = Views.I18N.localizer.Force ()
|
||||
let! conn = ctx.Conn
|
||||
let! groups = SmallGroups.listAll conn
|
||||
let! groups = SmallGroups.listAll ctx.Conn
|
||||
let url = Option.ofObj <| ctx.Session.GetString Key.Session.redirectUrl
|
||||
match url with
|
||||
| Some _ ->
|
||||
ctx.Session.Remove Key.Session.redirectUrl
|
||||
addWarning ctx s["The page you requested requires authentication; please log on below."]
|
||||
addWarning ctx ctx.Strings["The page you requested requires authentication; please log on below."]
|
||||
| None -> ()
|
||||
return!
|
||||
{ viewInfo ctx with HelpLink = Some Help.logOn }
|
||||
@ -206,8 +198,7 @@ let logOn : HttpHandler = requireAccess [ AccessLevel.Public ] >=> fun next ctx
|
||||
|
||||
/// GET /users
|
||||
let maintain : HttpHandler = requireAccess [ Admin ] >=> fun next ctx -> task {
|
||||
let! conn = ctx.Conn
|
||||
let! users = Users.all conn
|
||||
let! users = Users.all ctx.Conn
|
||||
return!
|
||||
viewInfo ctx
|
||||
|> Views.User.maintain users ctx
|
||||
@ -226,23 +217,23 @@ open System.Threading.Tasks
|
||||
let save : HttpHandler = requireAccess [ Admin ] >=> validateCsrf >=> fun next ctx -> task {
|
||||
match! ctx.TryBindFormAsync<EditUser> () with
|
||||
| Ok model ->
|
||||
let! conn = ctx.Conn
|
||||
let! user =
|
||||
if model.IsNew then Task.FromResult (Some { User.empty with Id = (Guid.NewGuid >> UserId) () })
|
||||
else Users.tryById (idFromShort UserId model.UserId) conn
|
||||
else Users.tryById (idFromShort UserId model.UserId) ctx.Conn
|
||||
match user with
|
||||
| Some usr ->
|
||||
let hasher = PrayerTrackerPasswordHasher ()
|
||||
let updatedUser = model.PopulateUser usr (fun pw -> hasher.HashPassword (usr, pw))
|
||||
do! Users.save updatedUser conn
|
||||
let s = Views.I18N.localizer.Force ()
|
||||
do! Users.save updatedUser ctx.Conn
|
||||
let s = ctx.Strings
|
||||
if model.IsNew then
|
||||
let h = CommonFunctions.htmlString
|
||||
{ UserMessage.info with
|
||||
Text = h s["Successfully {0} user", s["Added"].Value.ToLower ()]
|
||||
Description =
|
||||
h s["Please select at least one group for which this user ({0}) is authorized", updatedUser.Name]
|
||||
|> Some
|
||||
h s["Please select at least one group for which this user ({0}) is authorized",
|
||||
updatedUser.Name]
|
||||
|> Some
|
||||
}
|
||||
|> addUserMessage ctx
|
||||
return! redirectTo false $"/user/{shortGuid usr.Id.Value}/small-groups" next ctx
|
||||
@ -257,28 +248,25 @@ let save : HttpHandler = requireAccess [ Admin ] >=> validateCsrf >=> fun next c
|
||||
let saveGroups : HttpHandler = requireAccess [ Admin ] >=> validateCsrf >=> fun next ctx -> task {
|
||||
match! ctx.TryBindFormAsync<AssignGroups> () with
|
||||
| Ok model ->
|
||||
let s = Views.I18N.localizer.Force ()
|
||||
match Seq.length model.SmallGroups with
|
||||
| 0 ->
|
||||
addError ctx s["You must select at least one group to assign"]
|
||||
addError ctx ctx.Strings["You must select at least one group to assign"]
|
||||
return! redirectTo false $"/user/{model.UserId}/small-groups" next ctx
|
||||
| _ ->
|
||||
let! conn = ctx.Conn
|
||||
do! Users.updateSmallGroups (idFromShort UserId model.UserId)
|
||||
(model.SmallGroups.Split ',' |> Array.map (idFromShort SmallGroupId) |> List.ofArray) conn
|
||||
addInfo ctx s["Successfully updated group permissions for {0}", model.UserName]
|
||||
(model.SmallGroups.Split ',' |> Array.map (idFromShort SmallGroupId) |> List.ofArray) ctx.Conn
|
||||
addInfo ctx ctx.Strings["Successfully updated group permissions for {0}", model.UserName]
|
||||
return! redirectTo false "/users" next ctx
|
||||
| Result.Error e -> return! bindError e next ctx
|
||||
}
|
||||
|
||||
/// GET /user/[user-id]/small-groups
|
||||
let smallGroups usrId : HttpHandler = requireAccess [ Admin ] >=> fun next ctx -> task {
|
||||
let! conn = ctx.Conn
|
||||
let userId = UserId usrId
|
||||
match! Users.tryById userId conn with
|
||||
let userId = UserId usrId
|
||||
match! Users.tryById userId ctx.Conn with
|
||||
| Some user ->
|
||||
let! groups = SmallGroups.listAll conn
|
||||
let! groupIds = Users.groupIdsByUserId userId conn
|
||||
let! groups = SmallGroups.listAll ctx.Conn
|
||||
let! groupIds = Users.groupIdsByUserId userId ctx.Conn
|
||||
let curGroups = groupIds |> List.map (fun g -> shortGuid g.Value)
|
||||
return!
|
||||
viewInfo ctx
|
||||
|
@ -156,6 +156,7 @@ input[type=text],
|
||||
input[type=password],
|
||||
input[type=date],
|
||||
input[type=number],
|
||||
input[type=url],
|
||||
select {
|
||||
border-radius: .2rem;
|
||||
border-color: var(--lighter-dark);
|
||||
|
@ -76,13 +76,9 @@ ALTER TABLE pt."SmallGroup" RENAME TO small_group;
|
||||
|
||||
ALTER INDEX pt."IX_SmallGroup_ChurchId" RENAME TO ix_small_group_church_id;
|
||||
|
||||
-- Time Zone
|
||||
ALTER TABLE pt."TimeZone" RENAME COLUMN "TimeZoneId" TO id;
|
||||
ALTER TABLE pt."TimeZone" RENAME COLUMN "Description" TO description;
|
||||
ALTER TABLE pt."TimeZone" RENAME COLUMN "SortOrder" TO sort_order;
|
||||
ALTER TABLE pt."TimeZone" RENAME COLUMN "IsActive" TO is_active;
|
||||
ALTER TABLE pt."TimeZone" RENAME CONSTRAINT "PK_TimeZone" TO pk_time_zone;
|
||||
ALTER TABLE pt."TimeZone" RENAME TO time_zone;
|
||||
-- Time Zone (goes away)
|
||||
ALTER TABLE pt.list_preference DROP CONSTRAINT fk_list_preference_time_zone_id;
|
||||
DROP TABLE pt."TimeZone";
|
||||
|
||||
-- User
|
||||
ALTER TABLE pt."User" RENAME COLUMN "UserId" TO id;
|
||||
|
Loading…
Reference in New Issue
Block a user