Version 8 #43
@ -12,7 +12,7 @@ module private Helpers =
|
||||
/// Map a row to a Church instance
|
||||
let mapToChurch (row : RowReader) =
|
||||
{ Id = ChurchId (row.uuid "id")
|
||||
Name = row.string "name"
|
||||
Name = row.string "church_name"
|
||||
City = row.string "city"
|
||||
State = row.string "state"
|
||||
HasVpsInterface = row.bool "has_vps_interface"
|
||||
@ -21,27 +21,37 @@ module private Helpers =
|
||||
|
||||
/// Map a row to a ListPreferences instance
|
||||
let mapToListPreferences (row : RowReader) =
|
||||
{ SmallGroupId = SmallGroupId (row.uuid "small_group_id")
|
||||
DaysToKeepNew = row.int "days_to_keep_new"
|
||||
DaysToExpire = row.int "days_to_expire"
|
||||
LongTermUpdateWeeks = row.int "long_term_update_weeks"
|
||||
EmailFromName = row.string "email_from_name"
|
||||
EmailFromAddress = row.string "email_from_address"
|
||||
Fonts = row.string "fonts"
|
||||
HeadingColor = row.string "heading_color"
|
||||
LineColor = row.string "line_color"
|
||||
HeadingFontSize = row.int "heading_font_size"
|
||||
TextFontSize = row.int "text_font_size"
|
||||
RequestSort = RequestSort.fromCode (row.string "request_sort")
|
||||
GroupPassword = row.string "group_password"
|
||||
DefaultEmailType = EmailFormat.fromCode (row.string "default_email_type")
|
||||
IsPublic = row.bool "is_public"
|
||||
TimeZoneId = TimeZoneId (row.string "time_zone_id")
|
||||
PageSize = row.int "page_size"
|
||||
{ SmallGroupId = SmallGroupId (row.uuid "small_group_id")
|
||||
DaysToKeepNew = row.int "days_to_keep_new"
|
||||
DaysToExpire = row.int "days_to_expire"
|
||||
LongTermUpdateWeeks = row.int "long_term_update_weeks"
|
||||
EmailFromName = row.string "email_from_name"
|
||||
EmailFromAddress = row.string "email_from_address"
|
||||
Fonts = row.string "fonts"
|
||||
HeadingColor = row.string "heading_color"
|
||||
LineColor = row.string "line_color"
|
||||
HeadingFontSize = row.int "heading_font_size"
|
||||
TextFontSize = row.int "text_font_size"
|
||||
GroupPassword = row.string "group_password"
|
||||
IsPublic = row.bool "is_public"
|
||||
PageSize = row.int "page_size"
|
||||
TimeZoneId = TimeZoneId (row.string "time_zone_id")
|
||||
RequestSort = RequestSort.fromCode (row.string "request_sort")
|
||||
DefaultEmailType = EmailFormat.fromCode (row.string "default_email_type")
|
||||
AsOfDateDisplay = AsOfDateDisplay.fromCode (row.string "as_of_date_display")
|
||||
TimeZone = TimeZone.empty
|
||||
}
|
||||
|
||||
/// Map a row to a Member instance
|
||||
let mapToMember (row : RowReader) =
|
||||
{ Id = MemberId (row.uuid "id")
|
||||
SmallGroupId = SmallGroupId (row.uuid "small_group_id")
|
||||
Name = row.string "member_name"
|
||||
Email = row.string "email"
|
||||
Format = row.stringOrNone "email_format" |> Option.map EmailFormat.fromCode
|
||||
SmallGroup = SmallGroup.empty
|
||||
}
|
||||
|
||||
/// Map a row to a Small Group instance
|
||||
let mapToSmallGroup (row : RowReader) =
|
||||
{ Id = SmallGroupId (row.uuid "id")
|
||||
@ -54,6 +64,10 @@ module private Helpers =
|
||||
Users = ResizeArray ()
|
||||
}
|
||||
|
||||
/// Map a row to a Small Group list item
|
||||
let mapToSmallGroupItem (row : RowReader) =
|
||||
Giraffe.ShortGuid.fromGuid (row.uuid "id"), $"""{row.string "church_name"} | {row.string "group_name"}"""
|
||||
|
||||
/// Map a row to a Small Group instance with populated list preferences
|
||||
let mapToSmallGroupWithPreferences (row : RowReader) =
|
||||
{ mapToSmallGroup row with
|
||||
@ -74,7 +88,60 @@ module private Helpers =
|
||||
}
|
||||
|
||||
|
||||
/// Functions to manipulate churches
|
||||
module Churches =
|
||||
|
||||
/// Get a list of all churches
|
||||
let all conn =
|
||||
conn
|
||||
|> Sql.existingConnection
|
||||
|> Sql.query "SELECT * FROM pt.church ORDER BY church_name"
|
||||
|> Sql.executeAsync mapToChurch
|
||||
|
||||
/// Delete a church by its ID
|
||||
let deleteById (churchId : ChurchId) conn = backgroundTask {
|
||||
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.executeTransactionAsync
|
||||
[ $"DELETE FROM pt.prayer_request {where}", idParam
|
||||
$"DELETE FROM pt.user_small_group {where}", idParam
|
||||
$"DELETE FROM pt.list_preference {where}", idParam
|
||||
"DELETE FROM pt.small_group WHERE church_id = @churchId", idParam
|
||||
"DELETE FROM pt.church WHERE id = @churchId", idParam ]
|
||||
return ()
|
||||
}
|
||||
|
||||
/// Save a church's information
|
||||
let save (church : Church) conn = backgroundTask {
|
||||
let! _ =
|
||||
conn
|
||||
|> Sql.existingConnection
|
||||
|> Sql.query """
|
||||
INSERT INTO pt.church (
|
||||
id, church_name, city, state, has_vps_interface, interface_address
|
||||
) VALUES (
|
||||
@id, @name, @city, @state, @hasVpsInterface, @interfaceAddress
|
||||
) ON CONFLICT (id) DO UPDATE
|
||||
SET church_name = EXCLUDED.church_name,
|
||||
city = EXCLUDED.city,
|
||||
state = EXCLUDED.state,
|
||||
has_vps_interface = EXCLUDED.has_vps_interface,
|
||||
interface_address = EXCLUDED.interface_address"""
|
||||
|> Sql.parameters
|
||||
[ "@id", Sql.uuid church.Id.Value
|
||||
"@name", Sql.string church.Name
|
||||
"@city", Sql.string church.City
|
||||
"@state", Sql.string church.State
|
||||
"@hasVpsInterface", Sql.bool church.HasVpsInterface
|
||||
"@interfaceAddress", Sql.stringOrNone church.InterfaceAddress ]
|
||||
|> Sql.executeNonQueryAsync
|
||||
return ()
|
||||
}
|
||||
|
||||
/// Find a church by its ID
|
||||
let tryById (churchId : ChurchId) conn = backgroundTask {
|
||||
let! church =
|
||||
conn
|
||||
@ -86,9 +153,80 @@ module Churches =
|
||||
}
|
||||
|
||||
|
||||
/// Functions to manipulate small group members
|
||||
module Members =
|
||||
|
||||
/// Delete a small group member by its ID
|
||||
let deleteById (memberId : MemberId) conn = backgroundTask {
|
||||
let! _ =
|
||||
conn
|
||||
|> Sql.existingConnection
|
||||
|> Sql.query "DELETE FROM pt.member WHERE id = @id"
|
||||
|> Sql.parameters [ "@id", Sql.uuid memberId.Value ]
|
||||
|> Sql.executeNonQueryAsync
|
||||
return ()
|
||||
}
|
||||
|
||||
/// Retrieve a small group member by its ID
|
||||
let tryById (memberId : MemberId) conn = backgroundTask {
|
||||
let! mbr =
|
||||
conn
|
||||
|> Sql.existingConnection
|
||||
|> Sql.query "SELECT * FROM pt.member WHERE id = @id"
|
||||
|> Sql.parameters [ "@id", Sql.uuid memberId.Value ]
|
||||
|> Sql.executeAsync mapToMember
|
||||
return List.tryHead mbr
|
||||
}
|
||||
|
||||
|
||||
/// Functions to manipulate prayer requests
|
||||
module PrayerRequests =
|
||||
|
||||
/// Count the number of prayer requests for a church
|
||||
let countByChurch (churchId : ChurchId) conn =
|
||||
conn
|
||||
|> Sql.existingConnection
|
||||
|> Sql.query """
|
||||
SELECT COUNT(id) AS req_count
|
||||
FROM pt.prayer_request
|
||||
WHERE small_group_id IN (SELECT id FROM pt.small_group WHERE church_id = @churchId)"""
|
||||
|> Sql.parameters [ "@churchId", Sql.uuid churchId.Value ]
|
||||
|> Sql.executeRowAsync (fun row -> row.int "req_count")
|
||||
|
||||
/// Count the number of prayer requests for a small group
|
||||
let countByGroup (groupId : SmallGroupId) conn =
|
||||
conn
|
||||
|> Sql.existingConnection
|
||||
|> 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")
|
||||
|
||||
|
||||
/// Functions to retrieve small group information
|
||||
module SmallGroups =
|
||||
|
||||
/// Count the number of small groups for a church
|
||||
let countByChurch (churchId : ChurchId) conn =
|
||||
conn
|
||||
|> Sql.existingConnection
|
||||
|> 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")
|
||||
|
||||
/// Delete a small group by its ID
|
||||
let deleteById (groupId : SmallGroupId) conn = backgroundTask {
|
||||
let idParam = [ [ "@groupId", Sql.uuid groupId.Value ] ]
|
||||
let! _ =
|
||||
conn
|
||||
|> Sql.existingConnection
|
||||
|> 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
|
||||
"DELETE FROM pt.list_preference WHERE small_group_id = @groupId", idParam
|
||||
"DELETE FROM pt.small_group WHERE id = @groupId", idParam ]
|
||||
return ()
|
||||
}
|
||||
|
||||
/// Get a list of small group IDs along with a description that includes the church name
|
||||
let listAll conn =
|
||||
conn
|
||||
@ -98,9 +236,33 @@ module SmallGroups =
|
||||
FROM pt.small_group g
|
||||
INNER JOIN pt.church c ON c.id = g.church_id
|
||||
ORDER BY c.church_name, g.group_name"""
|
||||
|> Sql.executeAsync (fun row ->
|
||||
Giraffe.ShortGuid.fromGuid (row.uuid "id"), $"""{row.string "church_name"} | {row.string "group_name"}""")
|
||||
|> Sql.executeAsync mapToSmallGroupItem
|
||||
|
||||
/// Get a list of small group IDs and descriptions for groups with a group password
|
||||
let listProtected conn =
|
||||
conn
|
||||
|> Sql.existingConnection
|
||||
|> Sql.query """
|
||||
SELECT g.group_name, g.id, c.church_name
|
||||
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
|
||||
WHERE COALESCE(lp.group_password, '') <> ''
|
||||
ORDER BY c.church_name, g.group_name"""
|
||||
|> Sql.executeAsync mapToSmallGroupItem
|
||||
|
||||
/// Get a small group by its ID
|
||||
let tryById (groupId : SmallGroupId) conn = backgroundTask {
|
||||
let! group =
|
||||
conn
|
||||
|> Sql.existingConnection
|
||||
|> Sql.query "SELECT * FROM pt.small_group WHERE id = @id"
|
||||
|> Sql.parameters [ "@id", Sql.uuid groupId.Value ]
|
||||
|> Sql.executeAsync mapToSmallGroup
|
||||
return List.tryHead group
|
||||
}
|
||||
|
||||
/// Get a small group by its ID with its list preferences populated
|
||||
let tryByIdWithPreferences (groupId : SmallGroupId) conn = backgroundTask {
|
||||
let! group =
|
||||
conn
|
||||
@ -108,7 +270,7 @@ module SmallGroups =
|
||||
|> Sql.query """
|
||||
SELECT sg.*, lp.*
|
||||
FROM pt.small_group sg
|
||||
INNER JOIN list_preference lp ON lp.small_group_id = sg.id
|
||||
INNER JOIN pt.list_preference lp ON lp.small_group_id = sg.id
|
||||
WHERE sg.id = @id"""
|
||||
|> Sql.parameters [ "@id", Sql.uuid groupId.Value ]
|
||||
|> Sql.executeAsync mapToSmallGroupWithPreferences
|
||||
@ -126,6 +288,30 @@ module Users =
|
||||
|> 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.query """
|
||||
SELECT COUNT(u.id) AS user_count
|
||||
FROM pt.pt_user u
|
||||
WHERE EXISTS (
|
||||
SELECT 1
|
||||
FROM pt.user_small_group usg
|
||||
INNER JOIN pt.small_group sg ON sg.id = usg.small_group_id
|
||||
WHERE usg.user_id = u.id
|
||||
AND sg.church_id = @churchId)"""
|
||||
|> Sql.parameters [ "@churchId", Sql.uuid churchId.Value ]
|
||||
|> Sql.executeRowAsync (fun row -> row.int "user_count")
|
||||
|
||||
/// Count the number of users for a small group
|
||||
let countByGroup (groupId : SmallGroupId) conn =
|
||||
conn
|
||||
|> Sql.existingConnection
|
||||
|> 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")
|
||||
|
||||
/// Delete a user by its database ID
|
||||
let deleteById (userId : UserId) conn = backgroundTask {
|
||||
let! _ =
|
||||
@ -137,6 +323,27 @@ module Users =
|
||||
return ()
|
||||
}
|
||||
|
||||
/// Get the IDs of the small groups for which the given user is authorized
|
||||
let groupIdsByUserId (userId : UserId) conn =
|
||||
conn
|
||||
|> Sql.existingConnection
|
||||
|> 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.query """
|
||||
SELECT u.*
|
||||
FROM pt.pt_user u
|
||||
INNER JOIN pt.user_small_group usg ON usg.user_id = u.id
|
||||
WHERE usg.small_group_id = @groupId
|
||||
ORDER BY u.last_name, u.first_name"""
|
||||
|> Sql.parameters [ "@groupId", Sql.uuid groupId.Value ]
|
||||
|> Sql.executeAsync mapToUser
|
||||
|
||||
/// Save a user's information
|
||||
let save user conn = backgroundTask {
|
||||
let! _ =
|
||||
@ -212,3 +419,27 @@ module Users =
|
||||
|> Sql.executeNonQueryAsync
|
||||
return ()
|
||||
}
|
||||
|
||||
/// Update a user's authorized small groups
|
||||
let updateSmallGroups (userId : UserId) groupIds conn = backgroundTask {
|
||||
let! existingGroupIds = groupIdsByUserId userId conn
|
||||
let toAdd =
|
||||
groupIds |> List.filter (fun it -> existingGroupIds |> List.exists (fun grpId -> grpId = it) |> not)
|
||||
let toDelete =
|
||||
existingGroupIds |> List.filter (fun it -> groupIds |> List.exists (fun grpId -> grpId = it) |> not)
|
||||
let queries = seq {
|
||||
if not (List.isEmpty toAdd) then
|
||||
"INSERT INTO pt.user_small_group VALUES (@userId, @smallGroupId)",
|
||||
toAdd |> List.map (fun it -> [ "@userId", Sql.uuid userId.Value; "@smallGroupId", Sql.uuid it.Value ])
|
||||
if not (List.isEmpty toDelete) then
|
||||
"DELETE FROM pt.user_small_group WHERE user_id = @userId AND small_group_id = @smallGroupId",
|
||||
toDelete
|
||||
|> List.map (fun it -> [ "@userId", Sql.uuid userId.Value; "@smallGroupId", Sql.uuid it.Value ])
|
||||
}
|
||||
if not (Seq.isEmpty queries) then
|
||||
let! _ =
|
||||
conn
|
||||
|> Sql.existingConnection
|
||||
|> Sql.executeTransactionAsync (List.ofSeq queries)
|
||||
()
|
||||
}
|
||||
|
@ -828,4 +828,7 @@
|
||||
<data name="Last Seen" xml:space="preserve">
|
||||
<value>Ultima vez Visto</value>
|
||||
</data>
|
||||
<data name="Administrators" xml:space="preserve">
|
||||
<value>Administradores</value>
|
||||
</data>
|
||||
</root>
|
@ -141,7 +141,7 @@ let editMember (model : EditMember) (types : (string * LocalizedString) seq) ctx
|
||||
|
||||
|
||||
/// View for the small group log on page
|
||||
let logOn (groups : SmallGroup list) grpId ctx viewInfo =
|
||||
let logOn (groups : (string * string) list) grpId ctx viewInfo =
|
||||
let s = I18N.localizer.Force ()
|
||||
let model = { SmallGroupId = emptyGuid; Password = ""; RememberMe = None }
|
||||
let vi = AppViewInfo.withOnLoadScript "PT.smallGroup.logOn.onPageLoad" viewInfo
|
||||
@ -154,9 +154,7 @@ let logOn (groups : SmallGroup list) grpId ctx viewInfo =
|
||||
if groups.Length = 0 then "", s["There are no classes with passwords defined"].Value
|
||||
else
|
||||
"", selectDefault s["Select Group"].Value
|
||||
yield!
|
||||
groups
|
||||
|> List.map (fun grp -> shortGuid grp.Id.Value, $"{grp.Church.Name} | {grp.Name}")
|
||||
yield! groups
|
||||
}
|
||||
|> selectList (nameof model.SmallGroupId) grpId [ _required ]
|
||||
]
|
||||
@ -336,6 +334,13 @@ let overview model viewInfo =
|
||||
strong [] [ str (model.TotalMembers.ToString "N0"); space; locStr s["Members"] ]
|
||||
hr []
|
||||
a [ _href "/small-group/members" ] [ icon "email"; linkSpacer; locStr s["Maintain Group Members"] ]
|
||||
hr []
|
||||
strong [] [ str ((List.length model.Admins).ToString "N0"); space; locStr s["Administrators"] ]
|
||||
for admin in model.Admins do
|
||||
hr []
|
||||
str admin.Name
|
||||
br []
|
||||
small [] [ a [ _href $"mailto:{admin.Email}" ] [ str admin.Email ] ]
|
||||
]
|
||||
]
|
||||
]
|
||||
|
@ -688,6 +688,9 @@ type Overview =
|
||||
|
||||
/// A count of all members
|
||||
TotalMembers : int
|
||||
|
||||
/// The users authorized to administer this group
|
||||
Admins : User list
|
||||
}
|
||||
|
||||
|
||||
|
@ -1,28 +1,29 @@
|
||||
module PrayerTracker.Handlers.Church
|
||||
|
||||
open System.Threading.Tasks
|
||||
open Giraffe
|
||||
open PrayerTracker
|
||||
open PrayerTracker.Data
|
||||
open PrayerTracker.Entities
|
||||
open PrayerTracker.ViewModels
|
||||
|
||||
/// Find statistics for the given church
|
||||
let private findStats (db : AppDbContext) churchId = task {
|
||||
let! grps = db.CountGroupsByChurch churchId
|
||||
let! reqs = db.CountRequestsByChurch churchId
|
||||
let! usrs = db.CountUsersByChurch churchId
|
||||
return shortGuid churchId.Value, { SmallGroups = grps; PrayerRequests = reqs; Users = usrs }
|
||||
let private findStats churchId conn = task {
|
||||
let! groups = SmallGroups.countByChurch churchId conn
|
||||
let! requests = PrayerRequests.countByChurch churchId conn
|
||||
let! users = Users.countByChurch churchId conn
|
||||
return shortGuid churchId.Value, { SmallGroups = groups; PrayerRequests = requests; Users = users }
|
||||
}
|
||||
|
||||
/// POST /church/[church-id]/delete
|
||||
let delete chId : HttpHandler = requireAccess [ Admin ] >=> validateCsrf >=> fun next ctx -> task {
|
||||
let churchId = ChurchId chId
|
||||
use! conn = ctx.Conn
|
||||
match! Data.Churches.tryById churchId conn with
|
||||
match! Churches.tryById churchId conn with
|
||||
| Some church ->
|
||||
let! _, stats = findStats ctx.Db churchId
|
||||
ctx.Db.RemoveEntry church
|
||||
let! _ = ctx.Db.SaveChangesAsync ()
|
||||
let s = Views.I18N.localizer.Force ()
|
||||
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]
|
||||
@ -41,7 +42,7 @@ let edit churchId : HttpHandler = requireAccess [ Admin ] >=> fun next ctx -> ta
|
||||
|> renderHtml next ctx
|
||||
else
|
||||
use! conn = ctx.Conn
|
||||
match! Data.Churches.tryById (ChurchId churchId) conn with
|
||||
match! Churches.tryById (ChurchId churchId) conn with
|
||||
| Some church ->
|
||||
return!
|
||||
viewInfo ctx
|
||||
@ -52,17 +53,15 @@ let edit churchId : HttpHandler = requireAccess [ Admin ] >=> fun next ctx -> ta
|
||||
|
||||
/// GET /churches
|
||||
let maintain : HttpHandler = requireAccess [ Admin ] >=> fun next ctx -> task {
|
||||
let await = Async.AwaitTask >> Async.RunSynchronously
|
||||
let! churches = ctx.Db.AllChurches ()
|
||||
let stats = churches |> List.map (fun c -> await (findStats ctx.Db c.Id))
|
||||
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!
|
||||
viewInfo ctx
|
||||
|> Views.Church.maintain churches (stats |> Map.ofList) ctx
|
||||
|> renderHtml next ctx
|
||||
}
|
||||
|
||||
open System.Threading.Tasks
|
||||
|
||||
/// POST /church/save
|
||||
let save : HttpHandler = requireAccess [ Admin ] >=> validateCsrf >=> fun next ctx -> task {
|
||||
match! ctx.TryBindFormAsync<EditChurch> () with
|
||||
@ -70,14 +69,12 @@ let save : HttpHandler = requireAccess [ Admin ] >=> validateCsrf >=> fun next c
|
||||
let! conn = ctx.Conn
|
||||
let! church =
|
||||
if model.IsNew then Task.FromResult (Some { Church.empty with Id = (Guid.NewGuid >> ChurchId) () })
|
||||
else Data.Churches.tryById (idFromShort ChurchId model.ChurchId) conn
|
||||
else Churches.tryById (idFromShort ChurchId model.ChurchId) conn
|
||||
match church with
|
||||
| Some ch ->
|
||||
model.PopulateChurch ch
|
||||
|> (if model.IsNew then ctx.Db.AddEntry else ctx.Db.UpdateEntry)
|
||||
let! _ = ctx.Db.SaveChangesAsync ()
|
||||
let s = Views.I18N.localizer.Force ()
|
||||
let act = s[if model.IsNew then "Added" else "Updated"].Value.ToLower ()
|
||||
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]
|
||||
return! redirectTo false "/churches" next ctx
|
||||
| None -> return! fourOhFour ctx
|
||||
|
@ -6,6 +6,7 @@ open Microsoft.FSharpLu
|
||||
open Newtonsoft.Json
|
||||
open NodaTime
|
||||
open NodaTime.Serialization.JsonNet
|
||||
open PrayerTracker.Data
|
||||
open PrayerTracker.Entities
|
||||
open PrayerTracker.ViewModels
|
||||
|
||||
@ -107,7 +108,8 @@ type HttpContext with
|
||||
| None ->
|
||||
match this.User.SmallGroupId with
|
||||
| Some groupId ->
|
||||
match! this.Db.TryGroupById groupId with
|
||||
let! conn = this.Conn
|
||||
match! SmallGroups.tryByIdWithPreferences groupId conn with
|
||||
| Some group ->
|
||||
this.Session.CurrentGroup <- Some group
|
||||
return Some group
|
||||
@ -122,11 +124,11 @@ type HttpContext with
|
||||
| None ->
|
||||
match this.User.UserId with
|
||||
| Some userId ->
|
||||
match! this.Db.TryUserById userId with
|
||||
let! conn = this.Conn
|
||||
match! Users.tryById userId conn with
|
||||
| Some user ->
|
||||
// Set last seen for user
|
||||
this.Db.UpdateEntry { user with LastSeen = Some this.Now }
|
||||
let! _ = this.Db.SaveChangesAsync ()
|
||||
do! Users.updateLastSeen userId this.Now conn
|
||||
this.Session.CurrentUser <- Some user
|
||||
return Some user
|
||||
| None -> return None
|
||||
|
@ -3,6 +3,7 @@
|
||||
open System
|
||||
open Giraffe
|
||||
open PrayerTracker
|
||||
open PrayerTracker.Data
|
||||
open PrayerTracker.Entities
|
||||
open PrayerTracker.ViewModels
|
||||
|
||||
@ -14,14 +15,14 @@ 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
|
||||
match! ctx.Db.TryGroupById groupId with
|
||||
let s = Views.I18N.localizer.Force ()
|
||||
let groupId = SmallGroupId grpId
|
||||
let! conn = ctx.Conn
|
||||
match! SmallGroups.tryById groupId conn with
|
||||
| Some grp ->
|
||||
let! reqs = ctx.Db.CountRequestsBySmallGroup groupId
|
||||
let! users = ctx.Db.CountUsersBySmallGroup groupId
|
||||
ctx.Db.RemoveEntry grp
|
||||
let! _ = ctx.Db.SaveChangesAsync ()
|
||||
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]
|
||||
@ -31,13 +32,13 @@ let delete grpId : HttpHandler = requireAccess [ Admin ] >=> validateCsrf >=> fu
|
||||
|
||||
/// 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
|
||||
match! ctx.Db.TryMemberById memberId with
|
||||
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
|
||||
| Some mbr when mbr.SmallGroupId = group.Id ->
|
||||
ctx.Db.RemoveEntry mbr
|
||||
let! _ = ctx.Db.SaveChangesAsync ()
|
||||
do! Members.deleteById memberId conn
|
||||
addHtmlInfo ctx s["The group member “{0}” was deleted successfully", mbr.Name]
|
||||
return! redirectTo false "/small-group/members" next ctx
|
||||
| Some _
|
||||
@ -46,7 +47,8 @@ let deleteMember mbrId : HttpHandler = requireAccess [ User ] >=> validateCsrf >
|
||||
|
||||
/// GET /small-group/[group-id]/edit
|
||||
let edit grpId : HttpHandler = requireAccess [ Admin ] >=> fun next ctx -> task {
|
||||
let! churches = ctx.Db.AllChurches ()
|
||||
let! conn = ctx.Conn
|
||||
let! churches = Churches.all conn
|
||||
let groupId = SmallGroupId grpId
|
||||
if groupId.Value = Guid.Empty then
|
||||
return!
|
||||
@ -54,7 +56,7 @@ let edit grpId : HttpHandler = requireAccess [ Admin ] >=> fun next ctx -> task
|
||||
|> Views.SmallGroup.edit EditSmallGroup.empty churches ctx
|
||||
|> renderHtml next ctx
|
||||
else
|
||||
match! ctx.Db.TryGroupById groupId with
|
||||
match! SmallGroups.tryById groupId conn with
|
||||
| Some grp ->
|
||||
return!
|
||||
viewInfo ctx
|
||||
@ -75,7 +77,8 @@ let editMember mbrId : HttpHandler = requireAccess [ User ] >=> fun next ctx ->
|
||||
|> Views.SmallGroup.editMember EditMember.empty types ctx
|
||||
|> renderHtml next ctx
|
||||
else
|
||||
match! ctx.Db.TryMemberById memberId with
|
||||
let! conn = ctx.Conn
|
||||
match! Members.tryById memberId conn with
|
||||
| Some mbr when mbr.SmallGroupId = group.Id ->
|
||||
return!
|
||||
viewInfo ctx
|
||||
@ -87,7 +90,8 @@ 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! groups = ctx.Db.ProtectedGroups ()
|
||||
let! conn = ctx.Conn
|
||||
let! groups = SmallGroups.listProtected conn
|
||||
let groupId = match grpId with Some gid -> shortGuid gid | None -> ""
|
||||
return!
|
||||
{ viewInfo ctx with HelpLink = Some Help.logOn }
|
||||
@ -147,21 +151,24 @@ 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! reqs = ctx.Db.AllRequestsForSmallGroup group ctx.Clock None true 0
|
||||
let! reqCount = ctx.Db.CountRequestsBySmallGroup group.Id
|
||||
let! mbrCount = ctx.Db.CountMembersForSmallGroup group.Id
|
||||
let! admins = Users.listByGroupId group.Id conn
|
||||
let model =
|
||||
{ TotalActiveReqs = List.length reqs
|
||||
AllReqs = reqCount
|
||||
TotalMembers = mbrCount
|
||||
ActiveReqsByType =
|
||||
(reqs
|
||||
{ TotalActiveReqs = List.length reqs
|
||||
AllReqs = reqCount
|
||||
TotalMembers = mbrCount
|
||||
ActiveReqsByType = (
|
||||
reqs
|
||||
|> Seq.ofList
|
||||
|> Seq.map (fun req -> req.RequestType)
|
||||
|> Seq.distinct
|
||||
|> Seq.map (fun reqType -> reqType, reqs |> List.filter (fun r -> r.RequestType = reqType) |> List.length)
|
||||
|> Map.ofSeq)
|
||||
}
|
||||
Admins = admins
|
||||
}
|
||||
return!
|
||||
viewInfo ctx
|
||||
|> Views.SmallGroup.overview model
|
||||
|
@ -263,25 +263,11 @@ let saveGroups : HttpHandler = requireAccess [ Admin ] >=> validateCsrf >=> fun
|
||||
addError ctx s["You must select at least one group to assign"]
|
||||
return! redirectTo false $"/user/{model.UserId}/small-groups" next ctx
|
||||
| _ ->
|
||||
match! ctx.Db.TryUserByIdWithGroups (idFromShort UserId model.UserId) with
|
||||
| Some user ->
|
||||
let groups =
|
||||
model.SmallGroups.Split ','
|
||||
|> Array.map (idFromShort SmallGroupId)
|
||||
|> List.ofArray
|
||||
user.SmallGroups
|
||||
|> Seq.filter (fun x -> not (groups |> List.exists (fun y -> y = x.SmallGroupId)))
|
||||
|> ctx.Db.UserGroupXref.RemoveRange
|
||||
groups
|
||||
|> Seq.ofList
|
||||
|> Seq.filter (fun x -> not (user.SmallGroups |> Seq.exists (fun y -> y.SmallGroupId = x)))
|
||||
|> Seq.map (fun x -> { UserSmallGroup.empty with UserId = user.Id; SmallGroupId = x })
|
||||
|> List.ofSeq
|
||||
|> List.iter ctx.Db.AddEntry
|
||||
let! _ = ctx.Db.SaveChangesAsync ()
|
||||
addInfo ctx s["Successfully updated group permissions for {0}", model.UserName]
|
||||
return! redirectTo false "/users" next ctx
|
||||
| _ -> return! fourOhFour 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]
|
||||
return! redirectTo false "/users" next ctx
|
||||
| Result.Error e -> return! bindError e next ctx
|
||||
}
|
||||
|
||||
@ -289,10 +275,11 @@ let saveGroups : HttpHandler = requireAccess [ Admin ] >=> validateCsrf >=> fun
|
||||
let smallGroups usrId : HttpHandler = requireAccess [ Admin ] >=> fun next ctx -> task {
|
||||
let! conn = ctx.Conn
|
||||
let userId = UserId usrId
|
||||
match! ctx.Db.TryUserByIdWithGroups userId with
|
||||
match! Users.tryById userId conn with
|
||||
| Some user ->
|
||||
let! groups = SmallGroups.listAll conn
|
||||
let curGroups = user.SmallGroups |> Seq.map (fun g -> shortGuid g.SmallGroupId.Value) |> List.ofSeq
|
||||
let! groupIds = Users.groupIdsByUserId userId conn
|
||||
let curGroups = groupIds |> List.map (fun g -> shortGuid g.Value)
|
||||
return!
|
||||
viewInfo ctx
|
||||
|> Views.User.assignGroups (AssignGroups.fromUser user) groups curGroups ctx
|
||||
|
Loading…
Reference in New Issue
Block a user