Daniel J. Summers
f068d20612
- Fully implement single-case DUs for previously aliased IDs - Capitalize entity data items
864 lines
28 KiB
Forth
864 lines
28 KiB
Forth
namespace PrayerTracker.ViewModels
|
|
|
|
open Microsoft.AspNetCore.Html
|
|
open Microsoft.Extensions.Localization
|
|
open PrayerTracker
|
|
open PrayerTracker.Entities
|
|
|
|
|
|
/// Helper module to return localized reference lists
|
|
module ReferenceList =
|
|
|
|
/// A localized list of the AsOfDateDisplay DU cases
|
|
let asOfDateList (s : IStringLocalizer) = [
|
|
AsOfDateDisplay.toCode NoDisplay, s["Do not display the “as of” date"]
|
|
AsOfDateDisplay.toCode ShortDate, s["Display a short “as of” date"]
|
|
AsOfDateDisplay.toCode LongDate, s["Display a full “as of” date"]
|
|
]
|
|
|
|
/// A list of e-mail type options
|
|
let emailTypeList def (s : IStringLocalizer) =
|
|
// Localize the default type
|
|
let defaultType =
|
|
s[match def with HtmlFormat -> "HTML Format" | PlainTextFormat -> "Plain-Text Format"].Value
|
|
seq {
|
|
"", LocalizedString ("", $"""{s["Group Default"].Value} ({defaultType})""")
|
|
EmailFormat.toCode HtmlFormat, s["HTML Format"]
|
|
EmailFormat.toCode PlainTextFormat, s["Plain-Text Format"]
|
|
}
|
|
|
|
/// A list of expiration options
|
|
let expirationList (s : IStringLocalizer) includeExpireNow = [
|
|
Expiration.toCode Automatic, s["Expire Normally"]
|
|
Expiration.toCode Manual, s["Request Never Expires"]
|
|
if includeExpireNow then Expiration.toCode Forced, s["Expire Immediately"]
|
|
]
|
|
|
|
/// A list of request types
|
|
let requestTypeList (s : IStringLocalizer) = [
|
|
CurrentRequest, s["Current Requests"]
|
|
LongTermRequest, s["Long-Term Requests"]
|
|
PraiseReport, s["Praise Reports"]
|
|
Expecting, s["Expecting"]
|
|
Announcement, s["Announcements"]
|
|
]
|
|
|
|
|
|
/// A user message level
|
|
type MessageLevel =
|
|
/// An informational message to the user
|
|
| Info
|
|
/// A message with information the user should consider
|
|
| Warning
|
|
/// A message indicating that something went wrong
|
|
| Error
|
|
|
|
/// Support for the MessageLevel type
|
|
module MessageLevel =
|
|
|
|
/// Convert a message level to its string representation
|
|
let toString =
|
|
function
|
|
| Info -> "Info"
|
|
| Warning -> "WARNING"
|
|
| Error -> "ERROR"
|
|
|
|
let toCssClass level = (toString level).ToLowerInvariant ()
|
|
|
|
|
|
/// This is used to create a message that is displayed to the user
|
|
[<NoComparison; NoEquality>]
|
|
type UserMessage =
|
|
{ /// The type
|
|
Level : MessageLevel
|
|
|
|
/// The actual message
|
|
Text : HtmlString
|
|
|
|
/// The description (further information)
|
|
Description : HtmlString option
|
|
}
|
|
|
|
/// Support for the UserMessage type
|
|
module UserMessage =
|
|
|
|
/// Error message template
|
|
let error =
|
|
{ Level = Error
|
|
Text = HtmlString.Empty
|
|
Description = None
|
|
}
|
|
|
|
/// Warning message template
|
|
let warning =
|
|
{ Level = Warning
|
|
Text = HtmlString.Empty
|
|
Description = None
|
|
}
|
|
|
|
/// Info message template
|
|
let info =
|
|
{ Level = Info
|
|
Text = HtmlString.Empty
|
|
Description = None
|
|
}
|
|
|
|
/// The template with which the content will be rendered
|
|
type LayoutType =
|
|
|
|
/// A full page load
|
|
| FullPage
|
|
|
|
/// A response that will provide a new body tag
|
|
| PartialPage
|
|
|
|
/// A response that will replace the page content
|
|
| ContentOnly
|
|
|
|
|
|
open System
|
|
|
|
/// View model required by the layout template, given as first parameter for all pages in PrayerTracker
|
|
[<NoComparison; NoEquality>]
|
|
type AppViewInfo =
|
|
{ /// CSS files for the page
|
|
Style : string list
|
|
|
|
/// JavaScript files for the page
|
|
Script : string list
|
|
|
|
/// The link for help on this page
|
|
HelpLink : string option
|
|
|
|
/// Messages to be displayed to the user
|
|
Messages : UserMessage list
|
|
|
|
/// The current version of PrayerTracker
|
|
Version : string
|
|
|
|
/// The ticks when the request started
|
|
RequestStart : int64
|
|
|
|
/// The currently logged on user, if there is one
|
|
User : User option
|
|
|
|
/// The currently logged on small group, if there is one
|
|
Group : SmallGroup option
|
|
|
|
/// The layout with which the content will be rendered
|
|
Layout : LayoutType
|
|
|
|
/// Scoped styles for this view
|
|
ScopedStyle : string list
|
|
|
|
/// A JavaScript function to run on page load
|
|
OnLoadScript : string option
|
|
}
|
|
// TODO: add onload script option to this, modify layout to add it
|
|
|
|
/// Support for the AppViewInfo type
|
|
module AppViewInfo =
|
|
|
|
/// A fresh version that can be populated to process the current request
|
|
let fresh =
|
|
{ Style = []
|
|
Script = []
|
|
HelpLink = None
|
|
Messages = []
|
|
Version = ""
|
|
RequestStart = DateTime.Now.Ticks
|
|
User = None
|
|
Group = None
|
|
Layout = FullPage
|
|
ScopedStyle = []
|
|
OnLoadScript = None
|
|
}
|
|
|
|
/// Add scoped styles to the given view info object
|
|
let withScopedStyles styles viewInfo =
|
|
{ viewInfo with ScopedStyle = styles }
|
|
|
|
/// Add an onload action to the given view info object
|
|
let withOnLoadScript script viewInfo =
|
|
{ viewInfo with OnLoadScript = Some script }
|
|
|
|
|
|
/// Form for sending a small group or system-wide announcement
|
|
[<CLIMutable; NoComparison; NoEquality>]
|
|
type Announcement =
|
|
{ /// Whether the announcement should be sent to the class or to PrayerTracker users
|
|
SendToClass : string
|
|
|
|
/// The text of the announcement
|
|
Text : string
|
|
|
|
/// Whether this announcement should be added to the "Announcements" of the prayer list
|
|
AddToRequestList : bool option
|
|
|
|
/// The ID of the request type to which this announcement should be added
|
|
RequestType : string option
|
|
}
|
|
with
|
|
|
|
/// The text of the announcement, in plain text
|
|
member this.PlainText
|
|
with get () = (htmlToPlainText >> wordWrap 74) this.Text
|
|
|
|
|
|
/// Form for assigning small groups to a user
|
|
[<CLIMutable; NoComparison; NoEquality>]
|
|
type AssignGroups =
|
|
{ /// The Id of the user being assigned
|
|
UserId : string
|
|
|
|
/// The full name of the user being assigned
|
|
UserName : string
|
|
|
|
/// The Ids of the small groups to which the user is authorized
|
|
SmallGroups : string
|
|
}
|
|
|
|
/// Support for the AssignGroups type
|
|
module AssignGroups =
|
|
|
|
/// Create an instance of this form from an existing user
|
|
let fromUser (user : User) =
|
|
{ UserId = shortGuid user.Id.Value
|
|
UserName = user.fullName
|
|
SmallGroups = ""
|
|
}
|
|
|
|
|
|
/// Form to allow users to change their password
|
|
[<CLIMutable; NoComparison; NoEquality>]
|
|
type ChangePassword =
|
|
{ /// The user's current password
|
|
OldPassword : string
|
|
|
|
/// The user's new password
|
|
NewPassword : string
|
|
|
|
/// The user's new password, confirmed
|
|
NewPasswordConfirm : string
|
|
}
|
|
|
|
|
|
/// Form for adding or editing a church
|
|
[<CLIMutable; NoComparison; NoEquality>]
|
|
type EditChurch =
|
|
{ /// The ID of the church
|
|
ChurchId : string
|
|
|
|
/// The name of the church
|
|
Name : string
|
|
|
|
/// The city for the church
|
|
City : string
|
|
|
|
/// The state or province for the church
|
|
State : string
|
|
|
|
/// Whether the church has an active Virtual Prayer Room interface
|
|
HasInterface : bool option
|
|
|
|
/// The address for the interface
|
|
InterfaceAddress : string option
|
|
}
|
|
with
|
|
|
|
/// Is this a new church?
|
|
member this.IsNew = emptyGuid = this.ChurchId
|
|
|
|
/// Populate a church from this form
|
|
member this.PopulateChurch (church : Church) =
|
|
{ church with
|
|
Name = this.Name
|
|
City = this.City
|
|
State = this.State
|
|
HasInterface = match this.HasInterface with Some x -> x | None -> false
|
|
InterfaceAddress = match this.HasInterface with Some x when x -> this.InterfaceAddress | _ -> None
|
|
}
|
|
|
|
/// Support for the EditChurch type
|
|
module EditChurch =
|
|
|
|
/// Create an instance from an existing church
|
|
let fromChurch (church : Church) =
|
|
{ ChurchId = shortGuid church.Id.Value
|
|
Name = church.Name
|
|
City = church.City
|
|
State = church.State
|
|
HasInterface = match church.HasInterface with true -> Some true | false -> None
|
|
InterfaceAddress = church.InterfaceAddress
|
|
}
|
|
|
|
/// An instance to use for adding churches
|
|
let empty =
|
|
{ ChurchId = emptyGuid
|
|
Name = ""
|
|
City = ""
|
|
State = ""
|
|
HasInterface = None
|
|
InterfaceAddress = None
|
|
}
|
|
|
|
|
|
/// Form for adding/editing small group members
|
|
[<CLIMutable; NoComparison; NoEquality>]
|
|
type EditMember =
|
|
{ /// The Id for this small group member (not user-entered)
|
|
MemberId : string
|
|
|
|
/// The name of the member
|
|
Name : string
|
|
|
|
/// The e-mail address
|
|
Email : string
|
|
|
|
/// The e-mail format
|
|
Format : string
|
|
}
|
|
with
|
|
|
|
/// Is this a new member?
|
|
member this.IsNew = emptyGuid = this.MemberId
|
|
|
|
/// Support for the EditMember type
|
|
module EditMember =
|
|
|
|
/// Create an instance from an existing member
|
|
let fromMember (mbr : Member) =
|
|
{ MemberId = shortGuid mbr.Id.Value
|
|
Name = mbr.Name
|
|
Email = mbr.Email
|
|
Format = match mbr.Format with Some fmt -> EmailFormat.toCode fmt | None -> ""
|
|
}
|
|
|
|
/// An empty instance
|
|
let empty =
|
|
{ MemberId = emptyGuid
|
|
Name = ""
|
|
Email = ""
|
|
Format = ""
|
|
}
|
|
|
|
|
|
/// This form allows the user to set class preferences
|
|
[<CLIMutable; NoComparison; NoEquality>]
|
|
type EditPreferences =
|
|
{ /// The number of days after which requests are automatically expired
|
|
ExpireDays : int
|
|
|
|
/// The number of days requests are considered "new"
|
|
DaysToKeepNew : int
|
|
|
|
/// The number of weeks after which a long-term requests is flagged as requiring an update
|
|
LongTermUpdateWeeks : int
|
|
|
|
/// Whether to sort by updated date or requestor/subject
|
|
RequestSort : string
|
|
|
|
/// The name from which e-mail will be sent
|
|
EmailFromName : string
|
|
|
|
/// The e-mail address from which e-mail will be sent
|
|
EmailFromAddress : string
|
|
|
|
/// The default e-mail type for this group
|
|
DefaultEmailType : string
|
|
|
|
/// Whether the heading line color uses named colors or R/G/B
|
|
LineColorType : string
|
|
|
|
/// The named color for the heading lines
|
|
LineColor : string
|
|
|
|
/// Whether the heading text color uses named colors or R/G/B
|
|
HeadingColorType : string
|
|
|
|
/// The named color for the heading text
|
|
HeadingColor : string
|
|
|
|
/// The fonts to use for the list
|
|
Fonts : string
|
|
|
|
/// The font size for the heading text
|
|
HeadingFontSize : int
|
|
|
|
/// The font size for the list text
|
|
ListFontSize : int
|
|
|
|
/// The time zone for the class
|
|
TimeZone : string
|
|
|
|
/// The list visibility
|
|
Visibility : int
|
|
|
|
/// The small group password
|
|
GroupPassword : string option
|
|
|
|
/// The page size for search / inactive requests
|
|
PageSize : int
|
|
|
|
/// How the as-of date should be displayed
|
|
AsOfDate : string
|
|
}
|
|
with
|
|
|
|
/// Set the properties of a small group based on the form's properties
|
|
member this.PopulatePreferences (prefs : ListPreferences) =
|
|
let isPublic, grpPw =
|
|
match this.Visibility with
|
|
| RequestVisibility.``public`` -> true, ""
|
|
| RequestVisibility.passwordProtected -> false, (defaultArg this.GroupPassword "")
|
|
| RequestVisibility.``private``
|
|
| _ -> false, ""
|
|
{ prefs with
|
|
DaysToExpire = this.ExpireDays
|
|
DaysToKeepNew = this.DaysToKeepNew
|
|
LongTermUpdateWeeks = this.LongTermUpdateWeeks
|
|
RequestSort = RequestSort.fromCode this.RequestSort
|
|
EmailFromName = this.EmailFromName
|
|
EmailFromAddress = this.EmailFromAddress
|
|
DefaultEmailType = EmailFormat.fromCode this.DefaultEmailType
|
|
LineColor = this.LineColor
|
|
HeadingColor = this.HeadingColor
|
|
Fonts = this.Fonts
|
|
HeadingFontSize = this.HeadingFontSize
|
|
TextFontSize = this.ListFontSize
|
|
TimeZoneId = TimeZoneId this.TimeZone
|
|
IsPublic = isPublic
|
|
GroupPassword = grpPw
|
|
PageSize = this.PageSize
|
|
AsOfDateDisplay = AsOfDateDisplay.fromCode this.AsOfDate
|
|
}
|
|
|
|
/// Support for the EditPreferences type
|
|
module EditPreferences =
|
|
/// Populate an edit form from existing preferences
|
|
let fromPreferences (prefs : ListPreferences) =
|
|
let setType (x : string) = match x.StartsWith "#" with true -> "RGB" | false -> "Name"
|
|
{ ExpireDays = prefs.DaysToExpire
|
|
DaysToKeepNew = prefs.DaysToKeepNew
|
|
LongTermUpdateWeeks = prefs.LongTermUpdateWeeks
|
|
RequestSort = RequestSort.toCode prefs.RequestSort
|
|
EmailFromName = prefs.EmailFromName
|
|
EmailFromAddress = prefs.EmailFromAddress
|
|
DefaultEmailType = EmailFormat.toCode prefs.DefaultEmailType
|
|
LineColorType = setType prefs.LineColor
|
|
LineColor = prefs.LineColor
|
|
HeadingColorType = setType prefs.HeadingColor
|
|
HeadingColor = prefs.HeadingColor
|
|
Fonts = prefs.Fonts
|
|
HeadingFontSize = prefs.HeadingFontSize
|
|
ListFontSize = prefs.TextFontSize
|
|
TimeZone = TimeZoneId.toString prefs.TimeZoneId
|
|
GroupPassword = Some prefs.GroupPassword
|
|
PageSize = prefs.PageSize
|
|
AsOfDate = AsOfDateDisplay.toCode prefs.AsOfDateDisplay
|
|
Visibility =
|
|
match true with
|
|
| _ when prefs.IsPublic -> RequestVisibility.``public``
|
|
| _ when prefs.GroupPassword = "" -> RequestVisibility.``private``
|
|
| _ -> RequestVisibility.passwordProtected
|
|
}
|
|
|
|
|
|
/// Form for adding or editing prayer requests
|
|
[<CLIMutable; NoComparison; NoEquality>]
|
|
type EditRequest =
|
|
{ /// The ID of the request
|
|
RequestId : string
|
|
|
|
/// The type of the request
|
|
RequestType : string
|
|
|
|
/// The date of the request
|
|
EnteredDate : DateTime option
|
|
|
|
/// Whether to update the date or not
|
|
SkipDateUpdate : bool option
|
|
|
|
/// The requestor or subject
|
|
Requestor : string option
|
|
|
|
/// How this request is expired
|
|
Expiration : string
|
|
|
|
/// The text of the request
|
|
Text : string
|
|
}
|
|
with
|
|
|
|
/// Is this a new request?
|
|
member this.IsNew = emptyGuid = this.RequestId
|
|
|
|
/// Support for the EditRequest type
|
|
module EditRequest =
|
|
|
|
/// An empty instance to use for new requests
|
|
let empty =
|
|
{ RequestId = emptyGuid
|
|
RequestType = PrayerRequestType.toCode CurrentRequest
|
|
EnteredDate = None
|
|
SkipDateUpdate = None
|
|
Requestor = None
|
|
Expiration = Expiration.toCode Automatic
|
|
Text = ""
|
|
}
|
|
|
|
/// Create an instance from an existing request
|
|
let fromRequest (req : PrayerRequest) =
|
|
{ empty with
|
|
RequestId = shortGuid req.Id.Value
|
|
RequestType = PrayerRequestType.toCode req.RequestType
|
|
Requestor = req.Requestor
|
|
Expiration = Expiration.toCode req.Expiration
|
|
Text = req.Text
|
|
}
|
|
|
|
|
|
/// Form for the admin-level editing of small groups
|
|
[<CLIMutable; NoComparison; NoEquality>]
|
|
type EditSmallGroup =
|
|
{ /// The ID of the small group
|
|
SmallGroupId : string
|
|
|
|
/// The name of the small group
|
|
Name : string
|
|
|
|
/// The ID of the church to which this small group belongs
|
|
ChurchId : string
|
|
}
|
|
with
|
|
|
|
/// Is this a new small group?
|
|
member this.IsNew = emptyGuid = this.SmallGroupId
|
|
|
|
/// Populate a small group from this form
|
|
member this.populateGroup (grp : SmallGroup) =
|
|
{ grp with
|
|
Name = this.Name
|
|
ChurchId = idFromShort ChurchId this.ChurchId
|
|
}
|
|
|
|
/// Support for the EditSmallGroup type
|
|
module EditSmallGroup =
|
|
|
|
/// Create an instance from an existing small group
|
|
let fromGroup (grp : SmallGroup) =
|
|
{ SmallGroupId = shortGuid grp.Id.Value
|
|
Name = grp.Name
|
|
ChurchId = shortGuid grp.ChurchId.Value
|
|
}
|
|
|
|
/// An empty instance (used when adding a new group)
|
|
let empty =
|
|
{ SmallGroupId = emptyGuid
|
|
Name = ""
|
|
ChurchId = emptyGuid
|
|
}
|
|
|
|
|
|
/// Form for the user edit page
|
|
[<CLIMutable; NoComparison; NoEquality>]
|
|
type EditUser =
|
|
{ /// The ID of the user
|
|
UserId : string
|
|
|
|
/// The first name of the user
|
|
FirstName : string
|
|
|
|
/// The last name of the user
|
|
LastName : string
|
|
|
|
/// The e-mail address for the user
|
|
Email : string
|
|
|
|
/// The password for the user
|
|
Password : string
|
|
|
|
/// The password hash for the user a second time
|
|
PasswordConfirm : string
|
|
|
|
/// Is this user a PrayerTracker administrator?
|
|
IsAdmin : bool option
|
|
}
|
|
with
|
|
|
|
/// Is this a new user?
|
|
member this.IsNew = emptyGuid = this.UserId
|
|
|
|
/// Populate a user from the form
|
|
member this.PopulateUser (user : User) hasher =
|
|
{ user with
|
|
FirstName = this.FirstName
|
|
LastName = this.LastName
|
|
Email = this.Email
|
|
IsAdmin = defaultArg this.IsAdmin false
|
|
}
|
|
|> function
|
|
| u when isNull this.Password || this.Password = "" -> u
|
|
| u -> { u with PasswordHash = hasher this.Password }
|
|
|
|
/// Support for the EditUser type
|
|
module EditUser =
|
|
|
|
/// An empty instance
|
|
let empty =
|
|
{ UserId = emptyGuid
|
|
FirstName = ""
|
|
LastName = ""
|
|
Email = ""
|
|
Password = ""
|
|
PasswordConfirm = ""
|
|
IsAdmin = None
|
|
}
|
|
|
|
/// Create an instance from an existing user
|
|
let fromUser (user : User) =
|
|
{ empty with
|
|
UserId = shortGuid user.Id.Value
|
|
FirstName = user.FirstName
|
|
LastName = user.LastName
|
|
Email = user.Email
|
|
IsAdmin = if user.IsAdmin then Some true else None
|
|
}
|
|
|
|
|
|
/// Form for the small group log on page
|
|
[<CLIMutable; NoComparison; NoEquality>]
|
|
type GroupLogOn =
|
|
{ /// The ID of the small group to which the user is logging on
|
|
SmallGroupId : string
|
|
|
|
/// The password entered
|
|
Password : string
|
|
|
|
/// Whether to remember the login
|
|
RememberMe : bool option
|
|
}
|
|
|
|
/// Support for the GroupLogOn type
|
|
module GroupLogOn =
|
|
|
|
/// An empty instance
|
|
let empty =
|
|
{ SmallGroupId = emptyGuid
|
|
Password = ""
|
|
RememberMe = None
|
|
}
|
|
|
|
|
|
/// Items needed to display the request maintenance page
|
|
[<NoComparison; NoEquality>]
|
|
type MaintainRequests =
|
|
{ /// The requests to be displayed
|
|
Requests : PrayerRequest list
|
|
|
|
/// The small group to which the requests belong
|
|
SmallGroup : SmallGroup
|
|
|
|
/// Whether only active requests are included
|
|
OnlyActive : bool option
|
|
|
|
/// The search term for the requests
|
|
SearchTerm : string option
|
|
|
|
/// The page number of the results
|
|
PageNbr : int option
|
|
}
|
|
|
|
/// Support for the MaintainRequests type
|
|
module MaintainRequests =
|
|
|
|
/// An empty instance
|
|
let empty =
|
|
{ Requests = []
|
|
SmallGroup = SmallGroup.empty
|
|
OnlyActive = None
|
|
SearchTerm = None
|
|
PageNbr = None
|
|
}
|
|
|
|
|
|
/// Items needed to display the small group overview page
|
|
[<NoComparison; NoEquality>]
|
|
type Overview =
|
|
{ /// The total number of active requests
|
|
TotalActiveReqs : int
|
|
|
|
/// The numbers of active requests by request type
|
|
ActiveReqsByType : Map<PrayerRequestType, int>
|
|
|
|
/// A count of all requests
|
|
AllReqs : int
|
|
|
|
/// A count of all members
|
|
TotalMembers : int
|
|
}
|
|
|
|
|
|
/// Form for the user log on page
|
|
[<CLIMutable; NoComparison; NoEquality>]
|
|
type UserLogOn =
|
|
{ /// The e-mail address of the user
|
|
Email : string
|
|
|
|
/// The password entered
|
|
Password : string
|
|
|
|
/// The ID of the small group to which the user is logging on
|
|
SmallGroupId : string
|
|
|
|
/// Whether to remember the login
|
|
RememberMe : bool option
|
|
|
|
/// The URL to which the user should be redirected once login is successful
|
|
RedirectUrl : string option
|
|
}
|
|
|
|
/// Support for the UserLogOn type
|
|
module UserLogOn =
|
|
|
|
/// An empty instance
|
|
let empty =
|
|
{ Email = ""
|
|
Password = ""
|
|
SmallGroupId = emptyGuid
|
|
RememberMe = None
|
|
RedirectUrl = None
|
|
}
|
|
|
|
|
|
open Giraffe.ViewEngine
|
|
|
|
/// This represents a list of requests
|
|
type RequestList =
|
|
{ /// The prayer request list
|
|
Requests : PrayerRequest list
|
|
|
|
/// The date for which this list is being generated
|
|
Date : DateTime
|
|
|
|
/// The small group to which this list belongs
|
|
SmallGroup : SmallGroup
|
|
|
|
/// Whether to show the class header
|
|
ShowHeader : bool
|
|
|
|
/// The list of recipients (populated if requests are e-mailed)
|
|
Recipients : Member list
|
|
|
|
/// Whether the user can e-mail this list
|
|
CanEmail : bool
|
|
}
|
|
with
|
|
|
|
/// Group requests by their type, along with the type and its localized string
|
|
member this.RequestsByType (s : IStringLocalizer) =
|
|
ReferenceList.requestTypeList s
|
|
|> List.map (fun (typ, name) ->
|
|
let sort =
|
|
match this.SmallGroup.Preferences.RequestSort with
|
|
| SortByDate -> Seq.sortByDescending (fun req -> req.UpdatedDate)
|
|
| SortByRequestor -> Seq.sortBy (fun req -> req.Requestor)
|
|
let reqs =
|
|
this.Requests
|
|
|> Seq.ofList
|
|
|> Seq.filter (fun req -> req.RequestType = typ)
|
|
|> sort
|
|
|> List.ofSeq
|
|
typ, name, reqs)
|
|
|> List.filter (fun (_, _, reqs) -> not (List.isEmpty reqs))
|
|
|
|
/// Is this request new?
|
|
member this.IsNew (req : PrayerRequest) =
|
|
(this.Date - req.UpdatedDate).Days <= this.SmallGroup.Preferences.DaysToKeepNew
|
|
|
|
/// Generate this list as HTML
|
|
member this.AsHtml (s : IStringLocalizer) =
|
|
let prefs = this.SmallGroup.Preferences
|
|
let asOfSize = Math.Round (float prefs.TextFontSize * 0.8, 2)
|
|
[ if this.ShowHeader then
|
|
div [ _style $"text-align:center;font-family:{prefs.Fonts}" ] [
|
|
span [ _style $"font-size:%i{prefs.HeadingFontSize}pt;" ] [
|
|
strong [] [ str s["Prayer Requests"].Value ]
|
|
]
|
|
br []
|
|
span [ _style $"font-size:%i{prefs.TextFontSize}pt;" ] [
|
|
strong [] [ str this.SmallGroup.Name ]
|
|
br []
|
|
str (this.Date.ToString s["MMMM d, yyyy"].Value)
|
|
]
|
|
]
|
|
br []
|
|
for _, name, reqs in this.RequestsByType s do
|
|
div [ _style "padding-left:10px;padding-bottom:.5em;" ] [
|
|
table [ _style $"font-family:{prefs.Fonts};page-break-inside:avoid;" ] [
|
|
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;" ] [
|
|
rawText " "; str name.Value; rawText " "
|
|
]
|
|
]
|
|
]
|
|
]
|
|
reqs
|
|
|> List.map (fun req ->
|
|
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;" ] [
|
|
match req.Requestor with
|
|
| Some r when r <> "" ->
|
|
strong [] [ str r ]
|
|
rawText " — "
|
|
| Some _ -> ()
|
|
| None -> ()
|
|
rawText req.Text
|
|
match prefs.AsOfDateDisplay with
|
|
| NoDisplay -> ()
|
|
| ShortDate
|
|
| LongDate ->
|
|
let dt =
|
|
match prefs.AsOfDateDisplay with
|
|
| ShortDate -> req.UpdatedDate.ToShortDateString ()
|
|
| LongDate -> req.UpdatedDate.ToLongDateString ()
|
|
| _ -> ""
|
|
i [ _style $"font-size:%.2f{asOfSize}pt" ] [
|
|
rawText " ("; str s["as of"].Value; str " "; str dt; rawText ")"
|
|
]
|
|
])
|
|
|> ul []
|
|
br []
|
|
]
|
|
|> RenderView.AsString.htmlNodes
|
|
|
|
/// Generate this list as plain text
|
|
member this.AsText (s : IStringLocalizer) =
|
|
seq {
|
|
this.SmallGroup.Name
|
|
s["Prayer Requests"].Value
|
|
this.Date.ToString s["MMMM d, yyyy"].Value
|
|
" "
|
|
for _, name, reqs in this.RequestsByType s do
|
|
let dashes = String.replicate (name.Value.Length + 4) "-"
|
|
dashes
|
|
$" {name.Value.ToUpper ()}"
|
|
dashes
|
|
for req in reqs do
|
|
let bullet = if this.IsNew req then "+" else "-"
|
|
let requestor = match req.Requestor with Some r -> $"{r} - " | None -> ""
|
|
match this.SmallGroup.Preferences.AsOfDateDisplay with
|
|
| NoDisplay -> ""
|
|
| _ ->
|
|
let dt =
|
|
match this.SmallGroup.Preferences.AsOfDateDisplay with
|
|
| ShortDate -> req.UpdatedDate.ToShortDateString ()
|
|
| LongDate -> req.UpdatedDate.ToLongDateString ()
|
|
| _ -> ""
|
|
$""" ({s["as of"].Value} {dt})"""
|
|
|> sprintf " %s %s%s%s" bullet requestor (htmlToPlainText req.Text)
|
|
" "
|
|
}
|
|
|> String.concat "\n"
|
|
|> wordWrap 74
|