Version 8 #43
|
@ -2,6 +2,7 @@
|
||||||
module PrayerTracker.DataAccess
|
module PrayerTracker.DataAccess
|
||||||
|
|
||||||
open System.Linq
|
open System.Linq
|
||||||
|
open NodaTime
|
||||||
open PrayerTracker.Entities
|
open PrayerTracker.Entities
|
||||||
|
|
||||||
[<AutoOpen>]
|
[<AutoOpen>]
|
||||||
|
@ -24,7 +25,6 @@ module private Helpers =
|
||||||
if pageNbr > 0 then q.Skip((pageNbr - 1) * pageSize).Take pageSize else q
|
if pageNbr > 0 then q.Skip((pageNbr - 1) * pageSize).Take pageSize else q
|
||||||
|
|
||||||
|
|
||||||
open System
|
|
||||||
open Microsoft.EntityFrameworkCore
|
open Microsoft.EntityFrameworkCore
|
||||||
open Microsoft.FSharpLu
|
open Microsoft.FSharpLu
|
||||||
|
|
||||||
|
@ -90,12 +90,14 @@ type AppDbContext with
|
||||||
|
|
||||||
/// Get all (or active) requests for a small group as of now or the specified date
|
/// Get all (or active) requests for a small group as of now or the specified date
|
||||||
member this.AllRequestsForSmallGroup (grp : SmallGroup) clock listDate activeOnly pageNbr = backgroundTask {
|
member this.AllRequestsForSmallGroup (grp : SmallGroup) clock listDate activeOnly pageNbr = backgroundTask {
|
||||||
let theDate = match listDate with Some dt -> dt | _ -> grp.LocalDateNow clock
|
let theDate = match listDate with Some dt -> dt | _ -> SmallGroup.localDateNow clock grp
|
||||||
let query =
|
let query =
|
||||||
this.PrayerRequests.Where(fun req -> req.SmallGroupId = grp.Id)
|
this.PrayerRequests.Where(fun req -> req.SmallGroupId = grp.Id)
|
||||||
|> function
|
|> function
|
||||||
| q when activeOnly ->
|
| q when activeOnly ->
|
||||||
let asOf = DateTime (theDate.AddDays(-(float grp.Preferences.DaysToExpire)).Date.Ticks, DateTimeKind.Utc)
|
let asOf =
|
||||||
|
(theDate.AtStartOfDayInZone(SmallGroup.timeZone grp) - Duration.FromDays grp.Preferences.DaysToExpire)
|
||||||
|
.ToInstant ()
|
||||||
q.Where(fun req ->
|
q.Where(fun req ->
|
||||||
( req.UpdatedDate > asOf
|
( req.UpdatedDate > asOf
|
||||||
|| req.Expiration = Manual
|
|| req.Expiration = Manual
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
namespace PrayerTracker.Entities
|
namespace PrayerTracker.Entities
|
||||||
|
|
||||||
// fsharplint:disable RecordFieldNames MemberNames
|
|
||||||
|
|
||||||
(*-- SUPPORT TYPES --*)
|
(*-- SUPPORT TYPES --*)
|
||||||
|
|
||||||
/// How as-of dates should (or should not) be displayed with requests
|
/// How as-of dates should (or should not) be displayed with requests
|
||||||
|
@ -633,10 +631,10 @@ and [<CLIMutable; NoComparison; NoEquality>] PrayerRequest =
|
||||||
SmallGroupId : SmallGroupId
|
SmallGroupId : SmallGroupId
|
||||||
|
|
||||||
/// The date/time on which this request was entered
|
/// The date/time on which this request was entered
|
||||||
EnteredDate : DateTime
|
EnteredDate : Instant
|
||||||
|
|
||||||
/// The date/time this request was last updated
|
/// The date/time this request was last updated
|
||||||
UpdatedDate : DateTime
|
UpdatedDate : Instant
|
||||||
|
|
||||||
/// The name of the requestor or subject, or title of announcement
|
/// The name of the requestor or subject, or title of announcement
|
||||||
Requestor : string option
|
Requestor : string option
|
||||||
|
@ -664,8 +662,8 @@ with
|
||||||
RequestType = CurrentRequest
|
RequestType = CurrentRequest
|
||||||
UserId = UserId Guid.Empty
|
UserId = UserId Guid.Empty
|
||||||
SmallGroupId = SmallGroupId Guid.Empty
|
SmallGroupId = SmallGroupId Guid.Empty
|
||||||
EnteredDate = DateTime.MinValue
|
EnteredDate = Instant.MinValue
|
||||||
UpdatedDate = DateTime.MinValue
|
UpdatedDate = Instant.MinValue
|
||||||
Requestor = None
|
Requestor = None
|
||||||
Text = ""
|
Text = ""
|
||||||
NotifyChaplain = false
|
NotifyChaplain = false
|
||||||
|
@ -674,20 +672,6 @@ with
|
||||||
Expiration = Automatic
|
Expiration = Automatic
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Is this request expired?
|
|
||||||
member this.IsExpired (curr : DateTime) expDays =
|
|
||||||
match this.Expiration, this.RequestType with
|
|
||||||
| Forced, _ -> true
|
|
||||||
| Manual, _
|
|
||||||
| Automatic, LongTermRequest
|
|
||||||
| Automatic, Expecting -> false
|
|
||||||
| Automatic, _ -> curr.AddDays(-(float expDays)).Date > this.UpdatedDate.Date // Automatic expiration
|
|
||||||
|
|
||||||
/// Is an update required for this long-term request?
|
|
||||||
member this.UpdateRequired curr expDays updWeeks =
|
|
||||||
if this.IsExpired curr expDays then false
|
|
||||||
else curr.AddDays(-(float (updWeeks * 7))).Date > this.UpdatedDate.Date
|
|
||||||
|
|
||||||
/// Configure EF for this entity
|
/// Configure EF for this entity
|
||||||
static member internal ConfigureEF (mb : ModelBuilder) =
|
static member internal ConfigureEF (mb : ModelBuilder) =
|
||||||
mb.Entity<PrayerRequest> (fun it ->
|
mb.Entity<PrayerRequest> (fun it ->
|
||||||
|
@ -759,19 +743,6 @@ with
|
||||||
Users = ResizeArray<UserSmallGroup> ()
|
Users = ResizeArray<UserSmallGroup> ()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the local date for this group
|
|
||||||
member this.LocalTimeNow (clock : IClock) =
|
|
||||||
if isNull clock then nullArg (nameof clock)
|
|
||||||
let tzId = TimeZoneId.toString this.Preferences.TimeZoneId
|
|
||||||
let tz =
|
|
||||||
if DateTimeZoneProviders.Tzdb.Ids.Contains tzId then DateTimeZoneProviders.Tzdb[tzId]
|
|
||||||
else DateTimeZone.Utc
|
|
||||||
clock.GetCurrentInstant().InZone(tz).ToDateTimeUnspecified ()
|
|
||||||
|
|
||||||
/// Get the local date for this group
|
|
||||||
member this.LocalDateNow clock =
|
|
||||||
(this.LocalTimeNow clock).Date
|
|
||||||
|
|
||||||
/// Configure EF for this entity
|
/// Configure EF for this entity
|
||||||
static member internal ConfigureEF (mb : ModelBuilder) =
|
static member internal ConfigureEF (mb : ModelBuilder) =
|
||||||
mb.Entity<SmallGroup> (fun it ->
|
mb.Entity<SmallGroup> (fun it ->
|
||||||
|
@ -855,7 +826,7 @@ and [<CLIMutable; NoComparison; NoEquality>] User =
|
||||||
Salt : Guid option
|
Salt : Guid option
|
||||||
|
|
||||||
/// The last time the user was seen (set whenever the user is loaded into a session)
|
/// The last time the user was seen (set whenever the user is loaded into a session)
|
||||||
LastSeen : DateTime option
|
LastSeen : Instant option
|
||||||
|
|
||||||
/// The small groups which this user is authorized
|
/// The small groups which this user is authorized
|
||||||
SmallGroups : ResizeArray<UserSmallGroup>
|
SmallGroups : ResizeArray<UserSmallGroup>
|
||||||
|
@ -900,7 +871,7 @@ with
|
||||||
mb.Model.FindEntityType(typeof<User>).FindProperty(nameof User.empty.Salt)
|
mb.Model.FindEntityType(typeof<User>).FindProperty(nameof User.empty.Salt)
|
||||||
.SetValueConverter (OptionConverter<Guid> ())
|
.SetValueConverter (OptionConverter<Guid> ())
|
||||||
mb.Model.FindEntityType(typeof<User>).FindProperty(nameof User.empty.LastSeen)
|
mb.Model.FindEntityType(typeof<User>).FindProperty(nameof User.empty.LastSeen)
|
||||||
.SetValueConverter (OptionConverter<DateTime> ())
|
.SetValueConverter (OptionConverter<Instant> ())
|
||||||
|
|
||||||
|
|
||||||
/// Cross-reference between user and small group
|
/// Cross-reference between user and small group
|
||||||
|
@ -947,4 +918,44 @@ with
|
||||||
.SetValueConverter (Converters.UserIdConverter ())
|
.SetValueConverter (Converters.UserIdConverter ())
|
||||||
mb.Model.FindEntityType(typeof<UserSmallGroup>).FindProperty(nameof UserSmallGroup.empty.SmallGroupId)
|
mb.Model.FindEntityType(typeof<UserSmallGroup>).FindProperty(nameof UserSmallGroup.empty.SmallGroupId)
|
||||||
.SetValueConverter (Converters.SmallGroupIdConverter ())
|
.SetValueConverter (Converters.SmallGroupIdConverter ())
|
||||||
|
|
||||||
|
|
||||||
|
/// Support functions for small groups
|
||||||
|
module SmallGroup =
|
||||||
|
|
||||||
|
/// The DateTimeZone for the time zone ID for this small group
|
||||||
|
let timeZone group =
|
||||||
|
let tzId = TimeZoneId.toString group.Preferences.TimeZoneId
|
||||||
|
if DateTimeZoneProviders.Tzdb.Ids.Contains tzId then DateTimeZoneProviders.Tzdb[tzId]
|
||||||
|
else DateTimeZone.Utc
|
||||||
|
|
||||||
|
/// Get the local date/time for this group
|
||||||
|
let localTimeNow (clock : IClock) group =
|
||||||
|
if isNull clock then nullArg (nameof clock)
|
||||||
|
clock.GetCurrentInstant().InZone(timeZone group).LocalDateTime
|
||||||
|
|
||||||
|
/// Get the local date for this group
|
||||||
|
let localDateNow clock group =
|
||||||
|
(localTimeNow clock group).Date
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/// Support functions for prayer requests
|
||||||
|
module PrayerRequest =
|
||||||
|
|
||||||
|
/// Is this request expired?
|
||||||
|
let isExpired (asOf : LocalDate) group req =
|
||||||
|
match req.Expiration, req.RequestType with
|
||||||
|
| Forced, _ -> true
|
||||||
|
| Manual, _
|
||||||
|
| Automatic, LongTermRequest
|
||||||
|
| Automatic, Expecting -> false
|
||||||
|
| Automatic, _ ->
|
||||||
|
// Automatic expiration
|
||||||
|
asOf.PlusDays -group.Preferences.DaysToExpire > req.UpdatedDate.InZone(SmallGroup.timeZone group).Date
|
||||||
|
|
||||||
|
/// Is an update required for this long-term request?
|
||||||
|
let updateRequired asOf group req =
|
||||||
|
if isExpired asOf group req then false
|
||||||
|
else asOf.PlusWeeks -group.Preferences.LongTermUpdateWeeks
|
||||||
|
>= req.UpdatedDate.InZone(SmallGroup.timeZone group).Date
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
<PackageReference Include="Giraffe" Version="6.0.0" />
|
<PackageReference Include="Giraffe" Version="6.0.0" />
|
||||||
<PackageReference Include="Microsoft.FSharpLu" Version="0.11.7" />
|
<PackageReference Include="Microsoft.FSharpLu" Version="0.11.7" />
|
||||||
<PackageReference Include="NodaTime" Version="3.1.0" />
|
<PackageReference Include="NodaTime" Version="3.1.0" />
|
||||||
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="6.0.5" />
|
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="6.0.6" />
|
||||||
<PackageReference Update="FSharp.Core" Version="6.0.5" />
|
<PackageReference Update="FSharp.Core" Version="6.0.5" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
|
|
@ -255,13 +255,13 @@ let private messages viewInfo =
|
||||||
|> List.singleton
|
|> List.singleton
|
||||||
|
|
||||||
|
|
||||||
open System
|
open NodaTime
|
||||||
|
|
||||||
/// Render the <footer> at the bottom of the page
|
/// Render the <footer> at the bottom of the page
|
||||||
let private htmlFooter viewInfo =
|
let private htmlFooter viewInfo =
|
||||||
let s = I18N.localizer.Force ()
|
let s = I18N.localizer.Force ()
|
||||||
let imgText = $"""%O{s["PrayerTracker"]} %O{s["from Bit Badger Solutions"]}"""
|
let imgText = $"""%O{s["PrayerTracker"]} %O{s["from Bit Badger Solutions"]}"""
|
||||||
let resultTime = TimeSpan(DateTime.Now.Ticks - viewInfo.RequestStart).TotalSeconds
|
let resultTime = (SystemClock.Instance.GetCurrentInstant () - viewInfo.RequestStart).TotalSeconds
|
||||||
footer [ _class "pt-footer" ] [
|
footer [ _class "pt-footer" ] [
|
||||||
div [ _id "pt-legal" ] [
|
div [ _id "pt-legal" ] [
|
||||||
a [ _href "/legal/privacy-policy" ] [ locStr s["Privacy Policy"] ]
|
a [ _href "/legal/privacy-policy" ] [ locStr s["Privacy Policy"] ]
|
||||||
|
|
|
@ -154,23 +154,22 @@ let maintain (model : MaintainRequests) (ctx : HttpContext) viewInfo =
|
||||||
let l = I18N.forView "Requests/Maintain"
|
let l = I18N.forView "Requests/Maintain"
|
||||||
use sw = new StringWriter ()
|
use sw = new StringWriter ()
|
||||||
let raw = rawLocText sw
|
let raw = rawLocText sw
|
||||||
let now = model.SmallGroup.LocalDateNow (ctx.GetService<IClock> ())
|
let group = model.SmallGroup
|
||||||
let prefs = model.SmallGroup.Preferences
|
let now = SmallGroup.localDateNow (ctx.GetService<IClock> ()) group
|
||||||
let types = ReferenceList.requestTypeList s |> Map.ofList
|
let types = ReferenceList.requestTypeList s |> Map.ofList
|
||||||
let updReq (req : PrayerRequest) =
|
let vi = AppViewInfo.withScopedStyles [ "#requestList { grid-template-columns: repeat(5, auto); }" ] viewInfo
|
||||||
if req.UpdateRequired now prefs.DaysToExpire prefs.LongTermUpdateWeeks then "cell pt-request-update" else "cell"
|
|
||||||
|> _class
|
|
||||||
let reqExp (req : PrayerRequest) =
|
|
||||||
_class (if req.IsExpired now prefs.DaysToExpire then "cell pt-request-expired" else "cell")
|
|
||||||
let vi = AppViewInfo.withScopedStyles [ "#requestList { grid-template-columns: repeat(5, auto); }" ] viewInfo
|
|
||||||
/// Iterate the sequence once, before we render, so we can get the count of it at the top of the table
|
/// Iterate the sequence once, before we render, so we can get the count of it at the top of the table
|
||||||
let requests =
|
let requests =
|
||||||
model.Requests
|
model.Requests
|
||||||
|> List.map (fun req ->
|
|> List.map (fun req ->
|
||||||
let reqId = shortGuid req.Id.Value
|
let updateClass =
|
||||||
let reqText = htmlToPlainText req.Text
|
_class (if PrayerRequest.updateRequired now group req then "cell pt-request-update" else "cell")
|
||||||
let delAction = $"/prayer-request/{reqId}/delete"
|
let isExpired = PrayerRequest.isExpired now group req
|
||||||
let delPrompt =
|
let expiredClass = _class (if isExpired then "cell pt-request-expired" else "cell")
|
||||||
|
let reqId = shortGuid req.Id.Value
|
||||||
|
let reqText = htmlToPlainText req.Text
|
||||||
|
let delAction = $"/prayer-request/{reqId}/delete"
|
||||||
|
let delPrompt =
|
||||||
[ s["Are you sure you want to delete this {0}? This action cannot be undone.",
|
[ s["Are you sure you want to delete this {0}? This action cannot be undone.",
|
||||||
s["Prayer Request"].Value.ToLower() ].Value
|
s["Prayer Request"].Value.ToLower() ].Value
|
||||||
"\\n"
|
"\\n"
|
||||||
|
@ -183,7 +182,7 @@ let maintain (model : MaintainRequests) (ctx : HttpContext) viewInfo =
|
||||||
a [ _href $"/prayer-request/{reqId}/edit"; _title l["Edit This Prayer Request"].Value ] [
|
a [ _href $"/prayer-request/{reqId}/edit"; _title l["Edit This Prayer Request"].Value ] [
|
||||||
iconSized 18 "edit"
|
iconSized 18 "edit"
|
||||||
]
|
]
|
||||||
if req.IsExpired now prefs.DaysToExpire then
|
if isExpired then
|
||||||
a [ _href $"/prayer-request/{reqId}/restore"
|
a [ _href $"/prayer-request/{reqId}/restore"
|
||||||
_title l["Restore This Inactive Request"].Value ] [
|
_title l["Restore This Inactive Request"].Value ] [
|
||||||
iconSized 18 "visibility"
|
iconSized 18 "visibility"
|
||||||
|
@ -200,11 +199,11 @@ let maintain (model : MaintainRequests) (ctx : HttpContext) viewInfo =
|
||||||
iconSized 18 "delete_forever"
|
iconSized 18 "delete_forever"
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
div [ updReq req ] [
|
div [ updateClass ] [
|
||||||
str (req.UpdatedDate.ToString(s["MMMM d, yyyy"].Value, Globalization.CultureInfo.CurrentUICulture))
|
str (req.UpdatedDate.ToString(s["MMMM d, yyyy"].Value, Globalization.CultureInfo.CurrentUICulture))
|
||||||
]
|
]
|
||||||
div [ _class "cell" ] [ locStr types[req.RequestType] ]
|
div [ _class "cell" ] [ locStr types[req.RequestType] ]
|
||||||
div [ reqExp req ] [ str (match req.Requestor with Some r -> r | None -> " ") ]
|
div [ expiredClass ] [ str (match req.Requestor with Some r -> r | None -> " ") ]
|
||||||
div [ _class "cell" ] [
|
div [ _class "cell" ] [
|
||||||
match reqText.Length with
|
match reqText.Length with
|
||||||
| len when len < 60 -> rawText reqText
|
| len when len < 60 -> rawText reqText
|
||||||
|
@ -316,7 +315,7 @@ let view model viewInfo =
|
||||||
let s = I18N.localizer.Force ()
|
let s = I18N.localizer.Force ()
|
||||||
let pageTitle = $"""{s["Prayer Requests"].Value} • {model.SmallGroup.Name}"""
|
let pageTitle = $"""{s["Prayer Requests"].Value} • {model.SmallGroup.Name}"""
|
||||||
let spacer = rawText " "
|
let spacer = rawText " "
|
||||||
let dtString = model.Date.ToString "yyyy-MM-dd"
|
let dtString = model.Date.ToString ("yyyy-MM-dd", null) // TODO: this should be invariant
|
||||||
[ div [ _class "pt-center-text" ] [
|
[ div [ _class "pt-center-text" ] [
|
||||||
br []
|
br []
|
||||||
a [ _class "pt-icon-link"
|
a [ _class "pt-icon-link"
|
||||||
|
@ -327,12 +326,12 @@ let view model viewInfo =
|
||||||
]
|
]
|
||||||
if model.CanEmail then
|
if model.CanEmail then
|
||||||
spacer
|
spacer
|
||||||
if model.Date.DayOfWeek <> DayOfWeek.Sunday then
|
if model.Date.DayOfWeek <> IsoDayOfWeek.Sunday then
|
||||||
let rec findSunday (date : DateTime) =
|
let rec findSunday (date : LocalDate) =
|
||||||
if date.DayOfWeek = DayOfWeek.Sunday then date else findSunday (date.AddDays 1.)
|
if date.DayOfWeek = IsoDayOfWeek.Sunday then date else findSunday (date.PlusDays 1)
|
||||||
let sunday = findSunday model.Date
|
let sunday = findSunday model.Date
|
||||||
a [ _class "pt-icon-link"
|
a [ _class "pt-icon-link"
|
||||||
_href $"""/prayer-requests/view/{sunday.ToString "yyyy-MM-dd"}"""
|
_href $"""/prayer-requests/view/{sunday.ToString ("yyyy-MM-dd", null)}""" // TODO: make invariant
|
||||||
_title s["List for Next Sunday"].Value ] [
|
_title s["List for Next Sunday"].Value ] [
|
||||||
icon "update"; rawText " "; locStr s["List for Next Sunday"]
|
icon "update"; rawText " "; locStr s["List for Next Sunday"]
|
||||||
]
|
]
|
||||||
|
|
|
@ -217,7 +217,10 @@ let maintain (users : User list) ctx viewInfo =
|
||||||
]
|
]
|
||||||
div [ _class "cell" ] [ str user.Name ]
|
div [ _class "cell" ] [ str user.Name ]
|
||||||
div [ _class "cell" ] [
|
div [ _class "cell" ] [
|
||||||
str (match user.LastSeen with Some dt -> dt.ToString s["MMMM d, yyyy"] | None -> "--")
|
match user.LastSeen with
|
||||||
|
| Some dt -> dt.ToString (s["MMMM d, yyyy"].Value, null)
|
||||||
|
| None -> "--"
|
||||||
|
|> str
|
||||||
]
|
]
|
||||||
div [ _class "cell pt-center-text" ] [
|
div [ _class "cell pt-center-text" ] [
|
||||||
if user.IsAdmin then strong [] [ locStr s["Yes"] ] else locStr s["No"]
|
if user.IsAdmin then strong [] [ locStr s["Yes"] ] else locStr s["No"]
|
||||||
|
|
|
@ -116,7 +116,7 @@ type LayoutType =
|
||||||
| ContentOnly
|
| ContentOnly
|
||||||
|
|
||||||
|
|
||||||
open System
|
open NodaTime
|
||||||
|
|
||||||
/// View model required by the layout template, given as first parameter for all pages in PrayerTracker
|
/// View model required by the layout template, given as first parameter for all pages in PrayerTracker
|
||||||
[<NoComparison; NoEquality>]
|
[<NoComparison; NoEquality>]
|
||||||
|
@ -134,7 +134,7 @@ type AppViewInfo =
|
||||||
Version : string
|
Version : string
|
||||||
|
|
||||||
/// The ticks when the request started
|
/// The ticks when the request started
|
||||||
RequestStart : int64
|
RequestStart : Instant
|
||||||
|
|
||||||
/// The currently logged on user, if there is one
|
/// The currently logged on user, if there is one
|
||||||
User : User option
|
User : User option
|
||||||
|
@ -151,7 +151,6 @@ type AppViewInfo =
|
||||||
/// A JavaScript function to run on page load
|
/// A JavaScript function to run on page load
|
||||||
OnLoadScript : string option
|
OnLoadScript : string option
|
||||||
}
|
}
|
||||||
// TODO: add onload script option to this, modify layout to add it
|
|
||||||
|
|
||||||
/// Support for the AppViewInfo type
|
/// Support for the AppViewInfo type
|
||||||
module AppViewInfo =
|
module AppViewInfo =
|
||||||
|
@ -162,7 +161,7 @@ module AppViewInfo =
|
||||||
HelpLink = None
|
HelpLink = None
|
||||||
Messages = []
|
Messages = []
|
||||||
Version = ""
|
Version = ""
|
||||||
RequestStart = DateTime.Now.Ticks
|
RequestStart = Instant.MinValue
|
||||||
User = None
|
User = None
|
||||||
Group = None
|
Group = None
|
||||||
Layout = FullPage
|
Layout = FullPage
|
||||||
|
@ -467,7 +466,7 @@ type EditRequest =
|
||||||
RequestType : string
|
RequestType : string
|
||||||
|
|
||||||
/// The date of the request
|
/// The date of the request
|
||||||
EnteredDate : DateTime option
|
EnteredDate : string option
|
||||||
|
|
||||||
/// Whether to update the date or not
|
/// Whether to update the date or not
|
||||||
SkipDateUpdate : bool option
|
SkipDateUpdate : bool option
|
||||||
|
@ -724,6 +723,7 @@ module UserLogOn =
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
open System
|
||||||
open Giraffe.ViewEngine
|
open Giraffe.ViewEngine
|
||||||
|
|
||||||
/// This represents a list of requests
|
/// This represents a list of requests
|
||||||
|
@ -732,7 +732,7 @@ type RequestList =
|
||||||
Requests : PrayerRequest list
|
Requests : PrayerRequest list
|
||||||
|
|
||||||
/// The date for which this list is being generated
|
/// The date for which this list is being generated
|
||||||
Date : DateTime
|
Date : LocalDate
|
||||||
|
|
||||||
/// The small group to which this list belongs
|
/// The small group to which this list belongs
|
||||||
SmallGroup : SmallGroup
|
SmallGroup : SmallGroup
|
||||||
|
@ -767,30 +767,31 @@ with
|
||||||
|
|
||||||
/// Is this request new?
|
/// Is this request new?
|
||||||
member this.IsNew (req : PrayerRequest) =
|
member this.IsNew (req : PrayerRequest) =
|
||||||
(this.Date - req.UpdatedDate).Days <= this.SmallGroup.Preferences.DaysToKeepNew
|
let reqDate = req.UpdatedDate.InZone(SmallGroup.timeZone this.SmallGroup).Date
|
||||||
|
Period.Between(this.Date, reqDate, PeriodUnits.Days).Days <= this.SmallGroup.Preferences.DaysToKeepNew
|
||||||
|
|
||||||
/// Generate this list as HTML
|
/// Generate this list as HTML
|
||||||
member this.AsHtml (s : IStringLocalizer) =
|
member this.AsHtml (s : IStringLocalizer) =
|
||||||
let prefs = this.SmallGroup.Preferences
|
let p = this.SmallGroup.Preferences
|
||||||
let asOfSize = Math.Round (float prefs.TextFontSize * 0.8, 2)
|
let asOfSize = Math.Round (float p.TextFontSize * 0.8, 2)
|
||||||
[ if this.ShowHeader then
|
[ if this.ShowHeader then
|
||||||
div [ _style $"text-align:center;font-family:{prefs.Fonts}" ] [
|
div [ _style $"text-align:center;font-family:{p.Fonts}" ] [
|
||||||
span [ _style $"font-size:%i{prefs.HeadingFontSize}pt;" ] [
|
span [ _style $"font-size:%i{p.HeadingFontSize}pt;" ] [
|
||||||
strong [] [ str s["Prayer Requests"].Value ]
|
strong [] [ str s["Prayer Requests"].Value ]
|
||||||
]
|
]
|
||||||
br []
|
br []
|
||||||
span [ _style $"font-size:%i{prefs.TextFontSize}pt;" ] [
|
span [ _style $"font-size:%i{p.TextFontSize}pt;" ] [
|
||||||
strong [] [ str this.SmallGroup.Name ]
|
strong [] [ str this.SmallGroup.Name ]
|
||||||
br []
|
br []
|
||||||
str (this.Date.ToString s["MMMM d, yyyy"].Value)
|
str (this.Date.ToString (s["MMMM d, yyyy"].Value, null))
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
br []
|
br []
|
||||||
for _, name, reqs in this.RequestsByType s do
|
for _, name, reqs in this.RequestsByType s do
|
||||||
div [ _style "padding-left:10px;padding-bottom:.5em;" ] [
|
div [ _style "padding-left:10px;padding-bottom:.5em;" ] [
|
||||||
table [ _style $"font-family:{prefs.Fonts};page-break-inside:avoid;" ] [
|
table [ _style $"font-family:{p.Fonts};page-break-inside:avoid;" ] [
|
||||||
tr [] [
|
tr [] [
|
||||||
td [ _style $"font-size:%i{prefs.HeadingFontSize}pt;color:{prefs.HeadingColor};padding:3px 0;border-top:solid 3px {prefs.LineColor};border-bottom:solid 3px {prefs.LineColor};font-weight:bold;" ] [
|
td [ _style $"font-size:%i{p.HeadingFontSize}pt;color:{p.HeadingColor};padding:3px 0;border-top:solid 3px {p.LineColor};border-bottom:solid 3px {p.LineColor};font-weight:bold;" ] [
|
||||||
rawText " "; str name.Value; rawText " "
|
rawText " "; str name.Value; rawText " "
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
|
@ -799,7 +800,7 @@ with
|
||||||
reqs
|
reqs
|
||||||
|> List.map (fun req ->
|
|> List.map (fun req ->
|
||||||
let bullet = if this.IsNew req then "circle" else "disc"
|
let bullet = if this.IsNew req then "circle" else "disc"
|
||||||
li [ _style $"list-style-type:{bullet};font-family:{prefs.Fonts};font-size:%i{prefs.TextFontSize}pt;padding-bottom:.25em;" ] [
|
li [ _style $"list-style-type:{bullet};font-family:{p.Fonts};font-size:%i{p.TextFontSize}pt;padding-bottom:.25em;" ] [
|
||||||
match req.Requestor with
|
match req.Requestor with
|
||||||
| Some r when r <> "" ->
|
| Some r when r <> "" ->
|
||||||
strong [] [ str r ]
|
strong [] [ str r ]
|
||||||
|
@ -807,14 +808,14 @@ with
|
||||||
| Some _ -> ()
|
| Some _ -> ()
|
||||||
| None -> ()
|
| None -> ()
|
||||||
rawText req.Text
|
rawText req.Text
|
||||||
match prefs.AsOfDateDisplay with
|
match p.AsOfDateDisplay with
|
||||||
| NoDisplay -> ()
|
| NoDisplay -> ()
|
||||||
| ShortDate
|
| ShortDate
|
||||||
| LongDate ->
|
| LongDate ->
|
||||||
let dt =
|
let dt =
|
||||||
match prefs.AsOfDateDisplay with
|
match p.AsOfDateDisplay with
|
||||||
| ShortDate -> req.UpdatedDate.ToShortDateString ()
|
| ShortDate -> req.UpdatedDate.ToString ("d", null)
|
||||||
| LongDate -> req.UpdatedDate.ToLongDateString ()
|
| LongDate -> req.UpdatedDate.ToString ("D", null)
|
||||||
| _ -> ""
|
| _ -> ""
|
||||||
i [ _style $"font-size:%.2f{asOfSize}pt" ] [
|
i [ _style $"font-size:%.2f{asOfSize}pt" ] [
|
||||||
rawText " ("; str s["as of"].Value; str " "; str dt; rawText ")"
|
rawText " ("; str s["as of"].Value; str " "; str dt; rawText ")"
|
||||||
|
@ -830,7 +831,7 @@ with
|
||||||
seq {
|
seq {
|
||||||
this.SmallGroup.Name
|
this.SmallGroup.Name
|
||||||
s["Prayer Requests"].Value
|
s["Prayer Requests"].Value
|
||||||
this.Date.ToString s["MMMM d, yyyy"].Value
|
this.Date.ToString (s["MMMM d, yyyy"].Value, null)
|
||||||
" "
|
" "
|
||||||
for _, name, reqs in this.RequestsByType s do
|
for _, name, reqs in this.RequestsByType s do
|
||||||
let dashes = String.replicate (name.Value.Length + 4) "-"
|
let dashes = String.replicate (name.Value.Length + 4) "-"
|
||||||
|
@ -845,8 +846,8 @@ with
|
||||||
| _ ->
|
| _ ->
|
||||||
let dt =
|
let dt =
|
||||||
match this.SmallGroup.Preferences.AsOfDateDisplay with
|
match this.SmallGroup.Preferences.AsOfDateDisplay with
|
||||||
| ShortDate -> req.UpdatedDate.ToShortDateString ()
|
| ShortDate -> req.UpdatedDate.ToString ("d", null)
|
||||||
| LongDate -> req.UpdatedDate.ToLongDateString ()
|
| LongDate -> req.UpdatedDate.ToString ("D", null)
|
||||||
| _ -> ""
|
| _ -> ""
|
||||||
$""" ({s["as of"].Value} {dt})"""
|
$""" ({s["as of"].Value} {dt})"""
|
||||||
|> sprintf " %s %s%s%s" bullet requestor (htmlToPlainText req.Text)
|
|> sprintf " %s %s%s%s" bullet requestor (htmlToPlainText req.Text)
|
||||||
|
|
|
@ -1,17 +1,17 @@
|
||||||
namespace PrayerTracker
|
namespace PrayerTracker
|
||||||
|
|
||||||
open System
|
|
||||||
open Microsoft.AspNetCore.Http
|
open Microsoft.AspNetCore.Http
|
||||||
|
|
||||||
/// Middleware to add the starting ticks for the request
|
/// Middleware to add the starting ticks for the request
|
||||||
type RequestStartMiddleware (next : RequestDelegate) =
|
type RequestStartMiddleware (next : RequestDelegate) =
|
||||||
|
|
||||||
member this.InvokeAsync (ctx : HttpContext) = task {
|
member this.InvokeAsync (ctx : HttpContext) = task {
|
||||||
ctx.Items[Key.startTime] <- DateTime.Now.Ticks
|
ctx.Items[Key.startTime] <- ctx.Now
|
||||||
return! next.Invoke ctx
|
return! next.Invoke ctx
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
open System
|
||||||
open Microsoft.AspNetCore.Builder
|
open Microsoft.AspNetCore.Builder
|
||||||
open Microsoft.AspNetCore.Hosting
|
open Microsoft.AspNetCore.Hosting
|
||||||
|
|
||||||
|
@ -71,9 +71,13 @@ module Configure =
|
||||||
let _ = svc.AddSingleton<IClock> SystemClock.Instance
|
let _ = svc.AddSingleton<IClock> SystemClock.Instance
|
||||||
|
|
||||||
let config = svc.BuildServiceProvider().GetRequiredService<IConfiguration> ()
|
let config = svc.BuildServiceProvider().GetRequiredService<IConfiguration> ()
|
||||||
|
//NpgsqlConnection.GlobalTypeMapper.
|
||||||
let _ =
|
let _ =
|
||||||
svc.AddDbContext<AppDbContext> (
|
svc.AddDbContext<AppDbContext> (
|
||||||
(fun options -> options.UseNpgsql (config.GetConnectionString "PrayerTracker") |> ignore),
|
(fun options ->
|
||||||
|
options.UseNpgsql (
|
||||||
|
config.GetConnectionString "PrayerTracker", fun o -> o.UseNodaTime () |> ignore)
|
||||||
|
|> ignore),
|
||||||
ServiceLifetime.Scoped, ServiceLifetime.Singleton)
|
ServiceLifetime.Scoped, ServiceLifetime.Singleton)
|
||||||
()
|
()
|
||||||
|
|
||||||
|
|
|
@ -44,6 +44,7 @@ let appVersion =
|
||||||
open Giraffe
|
open Giraffe
|
||||||
open Giraffe.Htmx
|
open Giraffe.Htmx
|
||||||
open Microsoft.AspNetCore.Http
|
open Microsoft.AspNetCore.Http
|
||||||
|
open NodaTime
|
||||||
open PrayerTracker
|
open PrayerTracker
|
||||||
open PrayerTracker.ViewModels
|
open PrayerTracker.ViewModels
|
||||||
|
|
||||||
|
@ -63,7 +64,7 @@ let viewInfo (ctx : HttpContext) =
|
||||||
{ AppViewInfo.fresh with
|
{ AppViewInfo.fresh with
|
||||||
Version = appVersion
|
Version = appVersion
|
||||||
Messages = msg
|
Messages = msg
|
||||||
RequestStart = ctx.Items[Key.startTime] :?> int64
|
RequestStart = ctx.Items[Key.startTime] :?> Instant
|
||||||
User = ctx.Session.CurrentUser
|
User = ctx.Session.CurrentUser
|
||||||
Group = ctx.Session.CurrentGroup
|
Group = ctx.Session.CurrentGroup
|
||||||
Layout = layout
|
Layout = layout
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
[<AutoOpen>]
|
[<AutoOpen>]
|
||||||
module PrayerTracker.Extensions
|
module PrayerTracker.Extensions
|
||||||
|
|
||||||
open System
|
|
||||||
open Microsoft.AspNetCore.Http
|
open Microsoft.AspNetCore.Http
|
||||||
open Microsoft.FSharpLu
|
open Microsoft.FSharpLu
|
||||||
open Newtonsoft.Json
|
open Newtonsoft.Json
|
||||||
|
@ -76,6 +75,9 @@ type HttpContext with
|
||||||
/// The system clock (via DI)
|
/// The system clock (via DI)
|
||||||
member this.Clock = this.GetService<IClock> ()
|
member this.Clock = this.GetService<IClock> ()
|
||||||
|
|
||||||
|
/// The current instant
|
||||||
|
member this.Now = this.Clock.GetCurrentInstant ()
|
||||||
|
|
||||||
/// The currently logged on small group (sets the value in the session if it is missing)
|
/// The currently logged on small group (sets the value in the session if it is missing)
|
||||||
member this.CurrentGroup () = task {
|
member this.CurrentGroup () = task {
|
||||||
match this.Session.CurrentGroup with
|
match this.Session.CurrentGroup with
|
||||||
|
@ -101,7 +103,7 @@ type HttpContext with
|
||||||
match! this.Db.TryUserById userId with
|
match! this.Db.TryUserById userId with
|
||||||
| Some user ->
|
| Some user ->
|
||||||
// Set last seen for user
|
// Set last seen for user
|
||||||
this.Db.UpdateEntry { user with LastSeen = Some DateTime.UtcNow }
|
this.Db.UpdateEntry { user with LastSeen = Some this.Now }
|
||||||
let! _ = this.Db.SaveChangesAsync ()
|
let! _ = this.Db.SaveChangesAsync ()
|
||||||
this.Session.CurrentUser <- Some user
|
this.Session.CurrentUser <- Some user
|
||||||
return Some user
|
return Some user
|
||||||
|
|
|
@ -20,7 +20,7 @@ let private findRequest (ctx : HttpContext) reqId = task {
|
||||||
/// Generate a list of requests for the given date
|
/// Generate a list of requests for the given date
|
||||||
let private generateRequestList (ctx : HttpContext) date = task {
|
let private generateRequestList (ctx : HttpContext) date = task {
|
||||||
let group = ctx.Session.CurrentGroup.Value
|
let group = ctx.Session.CurrentGroup.Value
|
||||||
let listDate = match date with Some d -> d | None -> group.LocalDateNow ctx.Clock
|
let listDate = match date with Some d -> d | None -> SmallGroup.localDateNow ctx.Clock group
|
||||||
let! reqs = ctx.Db.AllRequestsForSmallGroup group ctx.Clock (Some listDate) true 0
|
let! reqs = ctx.Db.AllRequestsForSmallGroup group ctx.Clock (Some listDate) true 0
|
||||||
return
|
return
|
||||||
{ Requests = reqs
|
{ Requests = reqs
|
||||||
|
@ -32,29 +32,31 @@ let private generateRequestList (ctx : HttpContext) date = task {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
open System
|
open NodaTime.Text
|
||||||
|
|
||||||
/// Parse a string into a date (optionally, of course)
|
/// Parse a string into a date (optionally, of course)
|
||||||
let private parseListDate (date : string option) =
|
let private parseListDate (date : string option) =
|
||||||
match date with
|
match date with
|
||||||
| Some dt -> match DateTime.TryParse dt with true, d -> Some d | false, _ -> None
|
| Some dt -> match LocalDatePattern.Iso.Parse dt with it when it.Success -> Some it.Value | _ -> None
|
||||||
| None -> None
|
| None -> None
|
||||||
|
|
||||||
|
open System
|
||||||
|
|
||||||
/// GET /prayer-request/[request-id]/edit
|
/// GET /prayer-request/[request-id]/edit
|
||||||
let edit reqId : HttpHandler = requireAccess [ User ] >=> fun next ctx -> task {
|
let edit reqId : HttpHandler = requireAccess [ User ] >=> fun next ctx -> task {
|
||||||
let group = ctx.Session.CurrentGroup.Value
|
let group = ctx.Session.CurrentGroup.Value
|
||||||
let now = group.LocalDateNow ctx.Clock
|
let now = SmallGroup.localDateNow ctx.Clock group
|
||||||
let requestId = PrayerRequestId reqId
|
let requestId = PrayerRequestId reqId
|
||||||
if requestId.Value = Guid.Empty then
|
if requestId.Value = Guid.Empty then
|
||||||
return!
|
return!
|
||||||
{ viewInfo ctx with HelpLink = Some Help.editRequest }
|
{ viewInfo ctx with HelpLink = Some Help.editRequest }
|
||||||
|> Views.PrayerRequest.edit EditRequest.empty (now.ToString "yyyy-MM-dd") ctx
|
|> Views.PrayerRequest.edit EditRequest.empty (now.ToString ("R", null)) ctx
|
||||||
|> renderHtml next ctx
|
|> renderHtml next ctx
|
||||||
else
|
else
|
||||||
match! findRequest ctx requestId with
|
match! findRequest ctx requestId with
|
||||||
| Ok req ->
|
| Ok req ->
|
||||||
let s = Views.I18N.localizer.Force ()
|
let s = Views.I18N.localizer.Force ()
|
||||||
if req.IsExpired now group.Preferences.DaysToExpire then
|
if PrayerRequest.isExpired now group req then
|
||||||
{ UserMessage.warning with
|
{ UserMessage.warning with
|
||||||
Text = htmlLocString s["This request is expired."]
|
Text = htmlLocString s["This request is expired."]
|
||||||
Description =
|
Description =
|
||||||
|
@ -128,7 +130,7 @@ let list groupId : HttpHandler = requireAccess [ AccessLevel.Public ] >=> fun ne
|
||||||
viewInfo ctx
|
viewInfo ctx
|
||||||
|> Views.PrayerRequest.list
|
|> Views.PrayerRequest.list
|
||||||
{ Requests = reqs
|
{ Requests = reqs
|
||||||
Date = group.LocalDateNow ctx.Clock
|
Date = SmallGroup.localDateNow ctx.Clock group
|
||||||
SmallGroup = group
|
SmallGroup = group
|
||||||
ShowHeader = true
|
ShowHeader = true
|
||||||
CanEmail = Option.isSome ctx.User.UserId
|
CanEmail = Option.isSome ctx.User.UserId
|
||||||
|
@ -199,7 +201,7 @@ let restore reqId : HttpHandler = requireAccess [ User ] >=> fun next ctx -> tas
|
||||||
match! findRequest ctx requestId with
|
match! findRequest ctx requestId with
|
||||||
| Ok req ->
|
| Ok req ->
|
||||||
let s = Views.I18N.localizer.Force ()
|
let s = Views.I18N.localizer.Force ()
|
||||||
ctx.Db.UpdateEntry { req with Expiration = Automatic; UpdatedDate = DateTime.Now }
|
ctx.Db.UpdateEntry { req with Expiration = Automatic; UpdatedDate = ctx.Now }
|
||||||
let! _ = ctx.Db.SaveChangesAsync ()
|
let! _ = ctx.Db.SaveChangesAsync ()
|
||||||
addInfo ctx s["Successfully {0} prayer request", s["Restored"].Value.ToLower ()]
|
addInfo ctx s["Successfully {0} prayer request", s["Restored"].Value.ToLower ()]
|
||||||
return! redirectTo false "/prayer-requests" next ctx
|
return! redirectTo false "/prayer-requests" next ctx
|
||||||
|
@ -226,10 +228,13 @@ let save : HttpHandler = requireAccess [ User ] >=> validateCsrf >=> fun next ct
|
||||||
Expiration = Expiration.fromCode model.Expiration
|
Expiration = Expiration.fromCode model.Expiration
|
||||||
}
|
}
|
||||||
let group = ctx.Session.CurrentGroup.Value
|
let group = ctx.Session.CurrentGroup.Value
|
||||||
let now = group.LocalDateNow ctx.Clock
|
let now = SmallGroup.localDateNow ctx.Clock group
|
||||||
match model.IsNew with
|
match model.IsNew with
|
||||||
| true ->
|
| true ->
|
||||||
let dt = defaultArg model.EnteredDate now
|
let dt =
|
||||||
|
(defaultArg (parseListDate model.EnteredDate) now)
|
||||||
|
.AtStartOfDayInZone(SmallGroup.timeZone group)
|
||||||
|
.ToInstant()
|
||||||
{ upd8 with
|
{ upd8 with
|
||||||
SmallGroupId = group.Id
|
SmallGroupId = group.Id
|
||||||
UserId = ctx.User.UserId.Value
|
UserId = ctx.User.UserId.Value
|
||||||
|
@ -237,7 +242,7 @@ let save : HttpHandler = requireAccess [ User ] >=> validateCsrf >=> fun next ct
|
||||||
UpdatedDate = dt
|
UpdatedDate = dt
|
||||||
}
|
}
|
||||||
| false when defaultArg model.SkipDateUpdate false -> upd8
|
| false when defaultArg model.SkipDateUpdate false -> upd8
|
||||||
| false -> { upd8 with UpdatedDate = now }
|
| false -> { upd8 with UpdatedDate = ctx.Now }
|
||||||
|> if model.IsNew then ctx.Db.AddEntry else ctx.Db.UpdateEntry
|
|> if model.IsNew then ctx.Db.AddEntry else ctx.Db.UpdateEntry
|
||||||
let! _ = ctx.Db.SaveChangesAsync ()
|
let! _ = ctx.Db.SaveChangesAsync ()
|
||||||
let s = Views.I18N.localizer.Force ()
|
let s = Views.I18N.localizer.Force ()
|
||||||
|
|
|
@ -27,8 +27,9 @@
|
||||||
<PackageReference Include="Giraffe" Version="6.0.0" />
|
<PackageReference Include="Giraffe" Version="6.0.0" />
|
||||||
<PackageReference Include="Giraffe.Htmx" Version="1.8.0" />
|
<PackageReference Include="Giraffe.Htmx" Version="1.8.0" />
|
||||||
<PackageReference Include="NeoSmart.Caching.Sqlite" Version="6.0.1" />
|
<PackageReference Include="NeoSmart.Caching.Sqlite" Version="6.0.1" />
|
||||||
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="6.0.5" />
|
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="6.0.6" />
|
||||||
<PackageReference Update="FSharp.Core" Version="6.0.5" />
|
<PackageReference Update="FSharp.Core" Version="6.0.5" />
|
||||||
|
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL.NodaTime" Version="6.0.6" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|
|
@ -261,14 +261,14 @@ let sendAnnouncement : HttpHandler = requireAccess [ User ] >=> validateCsrf >=>
|
||||||
match! ctx.TryBindFormAsync<Announcement> () with
|
match! ctx.TryBindFormAsync<Announcement> () with
|
||||||
| Ok model ->
|
| Ok model ->
|
||||||
let group = ctx.Session.CurrentGroup.Value
|
let group = ctx.Session.CurrentGroup.Value
|
||||||
let prefs = group.Preferences
|
let pref = group.Preferences
|
||||||
let usr = ctx.Session.CurrentUser.Value
|
let usr = ctx.Session.CurrentUser.Value
|
||||||
let now = group.LocalTimeNow ctx.Clock
|
let now = SmallGroup.localTimeNow ctx.Clock group
|
||||||
let s = Views.I18N.localizer.Force ()
|
let s = Views.I18N.localizer.Force ()
|
||||||
// Reformat the text to use the class's font stylings
|
// Reformat the text to use the class's font stylings
|
||||||
let requestText = ckEditorToText model.Text
|
let requestText = ckEditorToText model.Text
|
||||||
let htmlText =
|
let htmlText =
|
||||||
p [ _style $"font-family:{prefs.Fonts};font-size:%d{prefs.TextFontSize}pt;" ] [ rawText requestText ]
|
p [ _style $"font-family:{pref.Fonts};font-size:%d{pref.TextFontSize}pt;" ] [ rawText requestText ]
|
||||||
|> renderHtmlNode
|
|> renderHtmlNode
|
||||||
let plainText = (htmlToPlainText >> wordWrap 74) htmlText
|
let plainText = (htmlToPlainText >> wordWrap 74) htmlText
|
||||||
// Send the e-mails
|
// Send the e-mails
|
||||||
|
@ -282,7 +282,7 @@ let sendAnnouncement : HttpHandler = requireAccess [ User ] >=> validateCsrf >=>
|
||||||
Recipients = recipients
|
Recipients = recipients
|
||||||
Group = group
|
Group = group
|
||||||
Subject = s["Announcement for {0} - {1:MMMM d, yyyy} {2}", group.Name, now.Date,
|
Subject = s["Announcement for {0} - {1:MMMM d, yyyy} {2}", group.Name, now.Date,
|
||||||
(now.ToString "h:mm tt").ToLower ()].Value
|
(now.ToString ("h:mm tt", null)).ToLower ()].Value
|
||||||
HtmlBody = htmlText
|
HtmlBody = htmlText
|
||||||
PlainTextBody = plainText
|
PlainTextBody = plainText
|
||||||
Strings = s
|
Strings = s
|
||||||
|
@ -293,14 +293,15 @@ let sendAnnouncement : HttpHandler = requireAccess [ User ] >=> validateCsrf >=>
|
||||||
| _, None -> ()
|
| _, None -> ()
|
||||||
| _, Some x when not x -> ()
|
| _, Some x when not x -> ()
|
||||||
| _, _ ->
|
| _, _ ->
|
||||||
|
let zone = SmallGroup.timeZone group
|
||||||
{ PrayerRequest.empty with
|
{ PrayerRequest.empty with
|
||||||
Id = (Guid.NewGuid >> PrayerRequestId) ()
|
Id = (Guid.NewGuid >> PrayerRequestId) ()
|
||||||
SmallGroupId = group.Id
|
SmallGroupId = group.Id
|
||||||
UserId = usr.Id
|
UserId = usr.Id
|
||||||
RequestType = (Option.get >> PrayerRequestType.fromCode) model.RequestType
|
RequestType = (Option.get >> PrayerRequestType.fromCode) model.RequestType
|
||||||
Text = requestText
|
Text = requestText
|
||||||
EnteredDate = now
|
EnteredDate = now.Date.AtStartOfDayInZone(zone).ToInstant()
|
||||||
UpdatedDate = now
|
UpdatedDate = now.InZoneLeniently(zone).ToInstant()
|
||||||
}
|
}
|
||||||
|> ctx.Db.AddEntry
|
|> ctx.Db.AddEntry
|
||||||
let! _ = ctx.Db.SaveChangesAsync ()
|
let! _ = ctx.Db.SaveChangesAsync ()
|
||||||
|
|
|
@ -145,7 +145,7 @@ let doLogOn : HttpHandler = requireAccess [ AccessLevel.Public ] >=> validateCsr
|
||||||
AuthenticationProperties (
|
AuthenticationProperties (
|
||||||
IssuedUtc = DateTimeOffset.UtcNow,
|
IssuedUtc = DateTimeOffset.UtcNow,
|
||||||
IsPersistent = defaultArg model.RememberMe false))
|
IsPersistent = defaultArg model.RememberMe false))
|
||||||
ctx.Db.UpdateEntry { user with LastSeen = Some DateTime.UtcNow }
|
ctx.Db.UpdateEntry { user with LastSeen = Some ctx.Now }
|
||||||
let! _ = ctx.Db.SaveChangesAsync ()
|
let! _ = ctx.Db.SaveChangesAsync ()
|
||||||
addHtmlInfo ctx s["Log On Successful • Welcome to {0}", s["PrayerTracker"]]
|
addHtmlInfo ctx s["Log On Successful • Welcome to {0}", s["PrayerTracker"]]
|
||||||
return! redirectTo false (sanitizeUrl model.RedirectUrl "/small-group") next ctx
|
return! redirectTo false (sanitizeUrl model.RedirectUrl "/small-group") next ctx
|
||||||
|
|
|
@ -94,7 +94,7 @@ ALTER TABLE pt."User" RENAME COLUMN "PasswordHash" TO password_hash;
|
||||||
ALTER TABLE pt."User" RENAME COLUMN "Salt" TO salt;
|
ALTER TABLE pt."User" RENAME COLUMN "Salt" TO salt;
|
||||||
ALTER TABLE pt."User" RENAME CONSTRAINT "PK_User" TO pk_pt_user;
|
ALTER TABLE pt."User" RENAME CONSTRAINT "PK_User" TO pk_pt_user;
|
||||||
ALTER TABLE pt."User" RENAME TO pt_user;
|
ALTER TABLE pt."User" RENAME TO pt_user;
|
||||||
ALTER TABLE pt.pt_user ADD COLUMN last_seen timestamp;
|
ALTER TABLE pt.pt_user ADD COLUMN last_seen timestamptz;
|
||||||
|
|
||||||
-- User / Small Group
|
-- User / Small Group
|
||||||
ALTER TABLE pt."User_SmallGroup" RENAME COLUMN "UserId" TO user_id;
|
ALTER TABLE pt."User_SmallGroup" RENAME COLUMN "UserId" TO user_id;
|
||||||
|
@ -105,3 +105,8 @@ ALTER TABLE pt."User_SmallGroup" RENAME CONSTRAINT "FK_User_SmallGroup_SmallGrou
|
||||||
ALTER TABLE pt."User_SmallGroup" RENAME TO user_small_group;
|
ALTER TABLE pt."User_SmallGroup" RENAME TO user_small_group;
|
||||||
|
|
||||||
ALTER INDEX pt."IX_User_SmallGroup_SmallGroupId" RENAME TO ix_user_small_group_small_group_id;
|
ALTER INDEX pt."IX_User_SmallGroup_SmallGroupId" RENAME TO ix_user_small_group_small_group_id;
|
||||||
|
|
||||||
|
-- #41 - change to timestamptz
|
||||||
|
SET TimeZone = 'UTC';
|
||||||
|
ALTER TABLE pt.prayer_request ALTER COLUMN entered_date TYPE timestamptz;
|
||||||
|
ALTER TABLE pt.prayer_request ALTER COLUMN updated_date TYPE timestamptz;
|
||||||
|
|
Loading…
Reference in New Issue
Block a user