Version 8 #43
| @ -11,7 +11,6 @@ | ||||
| 
 | ||||
|   <ItemGroup> | ||||
|     <PackageReference Include="Giraffe" Version="6.0.0" /> | ||||
|     <PackageReference Include="Microsoft.FSharpLu" Version="0.11.7" /> | ||||
|     <PackageReference Include="NodaTime" Version="3.1.1" /> | ||||
|     <PackageReference Update="FSharp.Core" Version="6.0.5" /> | ||||
|     <PackageReference Include="Npgsql.FSharp" Version="5.3.0" /> | ||||
|  | ||||
| @ -114,11 +114,11 @@ let listPreferencesTests = | ||||
|             Expect.equal mt.DaysToKeepNew 7 "The default days to keep new should have been 7" | ||||
|             Expect.equal mt.LongTermUpdateWeeks 4 "The default long term update weeks should have been 4" | ||||
|             Expect.equal mt.EmailFromName "PrayerTracker" "The default e-mail from name should have been PrayerTracker" | ||||
|             Expect.equal mt.EmailFromAddress "prayer@djs-consulting.com" | ||||
|                 "The default e-mail from address should have been prayer@djs-consulting.com" | ||||
|             Expect.equal mt.EmailFromAddress "prayer@bitbadger.solutions" | ||||
|                 "The default e-mail from address should have been prayer@bitbadger.solutions" | ||||
|             Expect.equal mt.Fonts "native" "The default list fonts were incorrect" | ||||
|             Expect.equal mt.HeadingColor "maroon" "The default heading text color should have been maroon" | ||||
|             Expect.equal mt.LineColor "navy" "The default heding line color should have been navy" | ||||
|             Expect.equal mt.LineColor "navy" "The default heading line color should have been navy" | ||||
|             Expect.equal mt.HeadingFontSize 16 "The default heading font size should have been 16" | ||||
|             Expect.equal mt.TextFontSize 12 "The default text font size should have been 12" | ||||
|             Expect.equal mt.RequestSort SortByDate "The default request sort should have been by date" | ||||
|  | ||||
| @ -16,7 +16,7 @@ | ||||
| 
 | ||||
|   <ItemGroup> | ||||
|     <PackageReference Include="Expecto" Version="9.0.4" /> | ||||
|     <PackageReference Include="NodaTime.Testing" Version="3.1.0" /> | ||||
|     <PackageReference Include="NodaTime.Testing" Version="3.1.1" /> | ||||
|     <PackageReference Update="FSharp.Core" Version="6.0.5" /> | ||||
|   </ItemGroup> | ||||
| 
 | ||||
|  | ||||
| @ -261,7 +261,7 @@ let editMemberTests = | ||||
| [<Tests>] | ||||
| let editPreferencesTests = | ||||
|     testList "EditPreferences" [ | ||||
|         test "fromPreferences succeeds for named colors and private list" { | ||||
|         test "fromPreferences succeeds for native fonts, named colors, and private list" { | ||||
|             let prefs = ListPreferences.empty | ||||
|             let edit  = EditPreferences.fromPreferences prefs | ||||
|             Expect.equal edit.ExpireDays prefs.DaysToExpire "The expiration days were not filled correctly" | ||||
| @ -278,7 +278,8 @@ let editPreferencesTests = | ||||
|             Expect.equal edit.LineColor prefs.LineColor "The heading line color was not filled correctly" | ||||
|             Expect.equal edit.HeadingColorType "Name" "The heading text color type was not derived correctly" | ||||
|             Expect.equal edit.HeadingColor prefs.HeadingColor "The heading text color was not filled correctly" | ||||
|             Expect.equal edit.Fonts prefs.Fonts "The list fonts were not filled correctly" | ||||
|             Expect.isTrue edit.IsNative "The IsNative flag should have been true (default value)" | ||||
|             Expect.isNone edit.Fonts "The list fonts should not exist for native font stack" | ||||
|             Expect.equal edit.HeadingFontSize prefs.HeadingFontSize "The heading font size was not filled correctly" | ||||
|             Expect.equal edit.ListFontSize prefs.TextFontSize "The list text font size was not filled correctly" | ||||
|             Expect.equal edit.TimeZone (TimeZoneId.toString prefs.TimeZoneId) "The time zone was not filled correctly" | ||||
| @ -310,6 +311,13 @@ let editPreferencesTests = | ||||
|             Expect.equal edit.Visibility GroupVisibility.PublicList | ||||
|                 "The list visibility was not derived correctly" | ||||
|         } | ||||
|         test "fromPreferences succeeds for non-native fonts" { | ||||
|             let prefs = { ListPreferences.empty with Fonts = "Arial,sans-serif" } | ||||
|             let edit  = EditPreferences.fromPreferences prefs | ||||
|             Expect.isFalse edit.IsNative "The IsNative flag should have been false" | ||||
|             Expect.isSome edit.Fonts "The fonts should have been filled for non-native fonts" | ||||
|             Expect.equal edit.Fonts.Value prefs.Fonts "The fonts were not filled correctly" | ||||
|         } | ||||
|     ] | ||||
| 
 | ||||
| [<Tests>] | ||||
| @ -529,11 +537,12 @@ let requestListTests = | ||||
|             "AsHtml succeeds without header or as-of date", | ||||
|             fun reqList -> | ||||
|                 let htmlList = { reqList with SmallGroup = { reqList.SmallGroup with Name = "Test HTML Group" } } | ||||
|                 let html = htmlList.AsHtml _s | ||||
|                 let html     = htmlList.AsHtml _s | ||||
|                 let fonts    = reqList.SmallGroup.Preferences.FontStack.Replace ("\"", """) | ||||
|                 Expect.equal -1 (html.IndexOf "Test HTML Group") | ||||
|                     "The small group name should not have existed (no header)" | ||||
|                 let curReqHeading = | ||||
|                     [ """<table style="font-family:Century Gothic,Tahoma,Luxi Sans,sans-serif;page-break-inside:avoid;">""" | ||||
|                     [ $"""<table style="font-family:{fonts};page-break-inside:avoid;">""" | ||||
|                       "<tr>" | ||||
|                       """<td style="font-size:16pt;color:maroon;padding:3px 0;border-top:solid 3px navy;border-bottom:solid 3px navy;font-weight:bold;">""" | ||||
|                       "    Current Requests    </td></tr></table>" | ||||
| @ -541,16 +550,16 @@ let requestListTests = | ||||
|                     |> String.concat "" | ||||
|                 Expect.stringContains html curReqHeading """Heading for category "Current Requests" not found""" | ||||
|                 let curReqHtml = | ||||
|                     [ "<ul>" | ||||
|                       """<li style="list-style-type:circle;font-family:Century Gothic,Tahoma,Luxi Sans,sans-serif;font-size:12pt;padding-bottom:.25em;">""" | ||||
|                     [ $"""<ul style="font-family:{fonts};font-size:12pt">""" | ||||
|                       """<li style="list-style-type:circle;padding-bottom:.25em;">""" | ||||
|                       "<strong>Zeb</strong> – zyx</li>" | ||||
|                       """<li style="list-style-type:disc;font-family:Century Gothic,Tahoma,Luxi Sans,sans-serif;font-size:12pt;padding-bottom:.25em;">""" | ||||
|                       """<li style="list-style-type:disc;padding-bottom:.25em;">""" | ||||
|                       "<strong>Aaron</strong> – abc</li></ul>" | ||||
|                     ] | ||||
|                     |> String.concat "" | ||||
|                 Expect.stringContains html curReqHtml """Expected HTML for "Current Requests" requests not found""" | ||||
|                 let praiseHeading = | ||||
|                     [ """<table style="font-family:Century Gothic,Tahoma,Luxi Sans,sans-serif;page-break-inside:avoid;">""" | ||||
|                     [ $"""<table style="font-family:{fonts};page-break-inside:avoid;">""" | ||||
|                       "<tr>" | ||||
|                       """<td style="font-size:16pt;color:maroon;padding:3px 0;border-top:solid 3px navy;border-bottom:solid 3px navy;font-weight:bold;">""" | ||||
|                       "    Praise Reports    </td></tr></table>" | ||||
| @ -558,8 +567,8 @@ let requestListTests = | ||||
|                     |> String.concat "" | ||||
|                 Expect.stringContains html praiseHeading """Heading for category "Praise Reports" not found""" | ||||
|                 let praiseHtml = | ||||
|                     [ "<ul>" | ||||
|                       """<li style="list-style-type:circle;font-family:Century Gothic,Tahoma,Luxi Sans,sans-serif;font-size:12pt;padding-bottom:.25em;">""" | ||||
|                     [ $"""<ul style="font-family:{fonts};font-size:12pt">""" | ||||
|                       """<li style="list-style-type:circle;padding-bottom:.25em;">""" | ||||
|                       "nmo</li></ul>" | ||||
|                     ] | ||||
|                     |> String.concat "" | ||||
| @ -571,9 +580,10 @@ let requestListTests = | ||||
|                         SmallGroup  = { reqList.SmallGroup with Name = "Test HTML Group" } | ||||
|                         ShowHeader = true | ||||
|                     } | ||||
|                 let html = htmlList.AsHtml _s | ||||
|                 let html  = htmlList.AsHtml _s | ||||
|                 let fonts = reqList.SmallGroup.Preferences.FontStack.Replace ("\"", """) | ||||
|                 let lstHeading = | ||||
|                     [ """<div style="text-align:center;font-family:Century Gothic,Tahoma,Luxi Sans,sans-serif">""" | ||||
|                     [ $"""<div style="text-align:center;font-family:{fonts}">""" | ||||
|                       """<span style="font-size:16pt;"><strong>Prayer Requests</strong></span><br>""" | ||||
|                       """<span style="font-size:12pt;"><strong>Test HTML Group</strong><br>""" | ||||
|                       htmlList.Date.ToString ("MMMM d, yyyy", null) | ||||
|  | ||||
| @ -41,12 +41,14 @@ let edit (model : EditChurch) ctx viewInfo = | ||||
|             div [ _checkboxField ] [ | ||||
|                 inputField "checkbox" (nameof model.HasInterface) "True" | ||||
|                            [ if defaultArg model.HasInterface false then _checked ] | ||||
|                 label [ _for (nameof model.HasInterface) ] [ locStr s["Has an interface with Virtual Prayer Room"] ] | ||||
|                 label [ _for (nameof model.HasInterface) ] [ | ||||
|                     locStr s["Has an Interface with “{0}”", "Virtual Prayer Space"] | ||||
|                 ] | ||||
|             ] | ||||
|         ] | ||||
|         div [ _fieldRowWith [ "pt-fadeable" ]; _id "divInterfaceAddress" ] [ | ||||
|             div [ _inputField ] [ | ||||
|                 label [ _for (nameof model.InterfaceAddress) ] [ locStr s["VPR Interface URL"] ] | ||||
|                 label [ _for (nameof model.InterfaceAddress) ] [ locStr s["Interface URL"] ] | ||||
|                 inputField "url" (nameof model.InterfaceAddress) (defaultArg model.InterfaceAddress "") [] | ||||
|             ] | ||||
|         ] | ||||
|  | ||||
| @ -302,11 +302,12 @@ let private contentSection viewInfo pgTitle (content : XmlNode) = [ | ||||
|     htmlFooter viewInfo | ||||
|     match viewInfo.OnLoadScript with | ||||
|     | Some onLoad -> | ||||
|         let doCall = if onLoad.EndsWith ")" then "" else "()" | ||||
|         script [] [ | ||||
|             rawText $""" | ||||
|                 window.doOnLoad = () => {{ | ||||
|                     if (window.PT) {{ | ||||
|                         {onLoad}() | ||||
|                         {onLoad}{doCall} | ||||
|                         delete window.doOnLoad | ||||
|                     }} else {{ setTimeout(window.doOnLoad, 500) }} | ||||
|                 }} | ||||
|  | ||||
| @ -54,15 +54,10 @@ let edit (model : EditRequest) today ctx viewInfo = | ||||
|         div [ _fieldRow ] [ | ||||
|             div [ _inputField ] [ | ||||
|                 label [] [ locStr s["Expiration"] ] | ||||
|                 ReferenceList.expirationList s (not model.IsNew) | ||||
|                 |> List.map (fun exp -> | ||||
|                     let radioId = String.concat "_" [ nameof model.Expiration; fst exp ]  | ||||
|                     span [ _class "text-nowrap" ] [ | ||||
|                         radio (nameof model.Expiration) radioId (fst exp) model.Expiration | ||||
|                         label [ _for radioId ] [ space; locStr (snd exp) ] | ||||
|                         rawText "     " | ||||
|                     ]) | ||||
|                 |> div [ _class "pt-center-text" ] | ||||
|                 span [ _class "pt-radio-group" ] [ | ||||
|                     for code, name in ReferenceList.expirationList s (not model.IsNew) do | ||||
|                         label [] [ radio (nameof model.Expiration) "" code model.Expiration; locStr name ] | ||||
|                 ] | ||||
|             ] | ||||
|         ] | ||||
|         div [ _fieldRow ] [ | ||||
|  | ||||
| @ -396,8 +396,8 @@ | ||||
|   <data name="The group member “{0}” was deleted successfully" xml:space="preserve"> | ||||
|     <value>El miembro del grupo “{0}” se eliminó con éxito</value> | ||||
|   </data> | ||||
|   <data name="The group {0} and its {1} prayer request(s) was deleted successfully (revoked access from {2} user(s))" xml:space="preserve"> | ||||
|     <value>El grupo {0} y sus {1} peticion(es) de oración se ha eliminado correctamente (acceso revocada por {2} usuario(s))</value> | ||||
|   <data name="The group “{0}” and its {1} prayer request(s) were deleted successfully; revoked access from {2} user(s)" xml:space="preserve"> | ||||
|     <value>El grupo “{0}” y sus {1} peticion(es) de oración se ha eliminado correctamente; acceso revocada por {2} usuario(s)</value> | ||||
|   </data> | ||||
|   <data name="The old password was incorrect - your password was NOT changed" xml:space="preserve"> | ||||
|     <value>La contraseña antigua es incorrecta - la contraseña NO ha cambiado</value> | ||||
| @ -742,7 +742,7 @@ | ||||
|     <value>Este</value> | ||||
|   </data> | ||||
|   <data name="MMMM d, yyyy" xml:space="preserve"> | ||||
|     <value>d \de MMMM yyyy</value> | ||||
|     <value>d \d\e MMMM yyyy</value> | ||||
|   </data> | ||||
|   <data name="Mountain" xml:space="preserve"> | ||||
|     <value>Montaña</value> | ||||
| @ -831,4 +831,55 @@ | ||||
|   <data name="Administrators" xml:space="preserve"> | ||||
|       <value>Administradores</value> | ||||
|   </data> | ||||
|   <data name="Native Fonts" xml:space="preserve"> | ||||
|     <value>Fuentes Nativas</value> | ||||
|   </data> | ||||
|   <data name="Named Fonts" xml:space="preserve"> | ||||
|     <value>Fuentes con Nombre</value> | ||||
|   </data> | ||||
|   <data name="Select Church" xml:space="preserve"> | ||||
|     <value>Seleccione una Iglesia</value> | ||||
|   </data> | ||||
|   <data name="Select Group" xml:space="preserve"> | ||||
|     <value>Seleccione un Grupo</value> | ||||
|   </data> | ||||
|   <data name="Member Name" xml:space="preserve"> | ||||
|     <value>Nombre de Miembro</value> | ||||
|   </data> | ||||
|   <data name="Custom Color" xml:space="preserve"> | ||||
|     <value>Color Personalizado</value> | ||||
|   </data> | ||||
|   <data name="Church Name" xml:space="preserve"> | ||||
|     <value>Nombre de la Iglesia</value> | ||||
|   </data> | ||||
|   <data name="City" xml:space="preserve"> | ||||
|     <value>Ciudad</value> | ||||
|   </data> | ||||
|   <data name="Has an Interface with “{0}”" xml:space="preserve"> | ||||
|     <value>Tiene una Interfaz con “{0}”</value> | ||||
|   </data> | ||||
|   <data name="Interface URL" xml:space="preserve"> | ||||
|     <value>URL de la Interfaz</value> | ||||
|   </data> | ||||
|   <data name="Successfully {0} church “{1}”" xml:space="preserve"> | ||||
|     <value>Iglesia “{1}” {0} con éxito</value> | ||||
|   </data> | ||||
|   <data name="The church “{0}” and its {1} small group(s) (with {2} prayer request(s)) were deleted successfully; revoked access from {3} user(s)" xml:space="preserve"> | ||||
|     <value>La iglesia "{0}" y sus {1} grupo(s) (con {2} peticion(es) de oración) se eliminaron correctamente; acceso revocado de {3} usuario(s)</value> | ||||
|   </data> | ||||
|   <data name="Successfully {0} group “{1}”" xml:space="preserve"> | ||||
|     <value>El grupo “{1}” {0} con éxito</value> | ||||
|   </data> | ||||
|   <data name="First Name" xml:space="preserve"> | ||||
|     <value>Primer Nombre</value> | ||||
|   </data> | ||||
|   <data name="Last Name" xml:space="preserve"> | ||||
|     <value>Apellido</value> | ||||
|   </data> | ||||
|   <data name="Password Again" xml:space="preserve"> | ||||
|     <value>Contraseña otra Vez</value> | ||||
|   </data> | ||||
|   <data name="This User Is a {0} Administrator" xml:space="preserve"> | ||||
|     <value>Este Usuario Es un Administrador de {0}</value> | ||||
|   </data> | ||||
| </root> | ||||
| @ -117,16 +117,16 @@ | ||||
|   <resheader name="writer"> | ||||
|     <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> | ||||
|   </resheader> | ||||
|   <data name="Ending with either “serif” or “sans-serif” will cause the user's browser to use the default “serif” font (“Times New Roman” on Windows) or “sans-serif” font (“Arial” on Windows) if no other fonts in the list are found." xml:space="preserve"> | ||||
|     <value>Con la extensión “serif” o el “sans-serif” hará que el navegador del usuario para utilizar el valor predeterminado “serif” de la fuente (“Times New Roman” en Windows) o “sans-serif” de la fuente (“Arial” en Windows) si no otras fuentes en la lista se encuentran.</value> | ||||
|   <data name="Ending this list with either “serif” or “sans-serif” will cause the user's browser to use the default “serif” font (“Times New Roman” on Windows) or “sans-serif” font (“Arial” on Windows) if no other fonts in the list are found." xml:space="preserve"> | ||||
|     <value>Terminar esta lista con “serif” o “sans-serif” hará que el navegador del usuario utilice la fuente predeterminado “serif” (“Times New Roman” en Windows) o “sans-serif” (“Arial” en Windows) si no se encuentran otras fuentes en la lista.</value> | ||||
|   </data> | ||||
|   <data name="If you want a custom color, you may be able to get some ideas (and a list of RGB values for those colors) from the W3 School's <a href="http://www.w3schools.com/html/html_colornames.asp" title="HTML Color List - W3 School">HTML color name list</a>." xml:space="preserve"> | ||||
|     <value>Si desea un color personalizado, que puede ser capaz de obtener algunas ideas (y una lista de valores RGB para los colores) de la lista de la Escuela de W3 de <a href="http://www.w3schools.com/html/html_colornames.asp" title="La Lista de Nombres de Colores HTML - La Escuela de W3">nombres de colores HTML</a>.</value> | ||||
|   </data> | ||||
|   <data name="List font names, separated by commas." xml:space="preserve"> | ||||
|     <value>Lista de nombres de fuentes separados por comas.</value> | ||||
|   <data name="Native fonts match the default font based on the user's device (computer, phone, tablet, etc.)." xml:space="preserve"> | ||||
|     <value>Las fuentes nativas coinciden con la fuente predeterminada según el dispositivo del usuario (computadora, teléfono, tableta, etc.).</value> | ||||
|   </data> | ||||
|   <data name="The first font that is matched is the one that is used." xml:space="preserve"> | ||||
|     <value>La primera fuente que se corresponde es el que se utiliza.</value> | ||||
|   <data name="Named fonts should be separated by commas, and will be displayed using the first one the user has in the list." xml:space="preserve"> | ||||
|     <value>Las fuentes con nombre deben estar separadas por comas y se mostrarán usando la primera que el usuario tenga en la lista.</value> | ||||
|   </data> | ||||
| </root> | ||||
| @ -30,13 +30,15 @@ let announcement isAdmin ctx viewInfo = | ||||
|             div [ _fieldRow ] [ | ||||
|                 div [ _inputField ] [ | ||||
|                     label [] [ locStr s["Send Announcement to"]; rawText ":" ] | ||||
|                     div [ _class "pt-center-text" ] [ | ||||
|                         radio (nameof model.SendToClass) $"{nameof model.SendToClass}_Y" "Y" "Y" | ||||
|                         label [ _for $"{nameof model.SendToClass}_Y" ] [ | ||||
|                             locStr s["This Group"]; rawText "     " | ||||
|                     div [ _class "pt-radio-group" ] [ | ||||
|                         label [] [ | ||||
|                             radio (nameof model.SendToClass) $"{nameof model.SendToClass}_Y" "Y" "Y" | ||||
|                             locStr s["This Group"] | ||||
|                         ] | ||||
|                         label [] [ | ||||
|                             radio (nameof model.SendToClass) $"{nameof model.SendToClass}_N" "N" "Y" | ||||
|                             locStr s["All {0} Users", s["PrayerTracker"]] | ||||
|                         ] | ||||
|                         radio (nameof model.SendToClass) $"{nameof model.SendToClass}_N" "N" "Y" | ||||
|                         label [ _for $"{nameof model.SendToClass}_N" ] [ locStr s["All {0} Users", s["PrayerTracker"]] ] | ||||
|                     ] | ||||
|                 ] | ||||
|             ] | ||||
| @ -519,7 +521,21 @@ let preferences (model : EditPreferences) ctx viewInfo = | ||||
|                 legend [] [ strong [] [ icon "font_download"; rawText "  "; locStr s["Fonts"] ] ] | ||||
|                 div [ _inputField ] [ | ||||
|                     label [ _for (nameof model.Fonts) ] [ locStr s["Fonts** for List"] ] | ||||
|                     inputField "text" (nameof model.Fonts) model.Fonts [ _required ] | ||||
|                     let value = if model.IsNative then "True" else "False" | ||||
|                     span [ _class "pt-radio-group" ] [ | ||||
|                         label [] [ | ||||
|                             radio (nameof model.IsNative) $"{nameof model.IsNative}_Y" "True" value | ||||
|                             locStr s["Native Fonts"] | ||||
|                         ] | ||||
|                         inputField "text" "nativeFontSpacer" "" [ _style "visibility:hidden" ] | ||||
|                     ] | ||||
|                     span [ _class "pt-radio-group" ] [ | ||||
|                         label [] [ | ||||
|                             radio (nameof model.IsNative) $"{nameof model.IsNative}_N" "False" value | ||||
|                             locStr s["Named Fonts"] | ||||
|                         ] | ||||
|                         inputField "text" (nameof model.Fonts) (defaultArg model.Fonts "") [ _required ] | ||||
|                     ] | ||||
|                 ] | ||||
|                 div [ _fieldRow ] [ | ||||
|                     div [ _inputField ] [ | ||||
| @ -595,11 +611,11 @@ let preferences (model : EditPreferences) ctx viewInfo = | ||||
|         ] | ||||
|         p [] [ | ||||
|             rawText "** " | ||||
|             raw l["List font names, separated by commas."] | ||||
|             raw l["Native fonts match the default font based on the user's device (computer, phone, tablet, etc.)."] | ||||
|             space | ||||
|             raw l["The first font that is matched is the one that is used."] | ||||
|             raw l["Named fonts should be separated by commas, and will be displayed using the first one the user has in the list."] | ||||
|             space | ||||
|             raw l["Ending with either “serif” or “sans-serif” will cause the user's browser to use the default “serif” font (“Times New Roman” on Windows) or “sans-serif” font (“Arial” on Windows) if no other fonts in the list are found."] | ||||
|             raw l["Ending this list with either “serif” or “sans-serif” will cause the user's browser to use the default “serif” font (“Times New Roman” on Windows) or “sans-serif” font (“Arial” on Windows) if no other fonts in the list are found."] | ||||
|         ] | ||||
|         p [] [ | ||||
|             rawText "*** " | ||||
|  | ||||
| @ -133,7 +133,7 @@ let edit (model : EditUser) ctx viewInfo = | ||||
|         ] | ||||
|         div [ _checkboxField ] [ | ||||
|             inputField "checkbox" (nameof model.IsAdmin) "True" [ if defaultArg model.IsAdmin false then _checked ] | ||||
|             label [ _for (nameof model.IsAdmin) ] [ locStr s["This user is a PrayerTracker administrator"] ] | ||||
|             label [ _for (nameof model.IsAdmin) ] [ locStr s["This User Is a {0} Administrator", s["PrayerTracker"]] ] | ||||
|         ] | ||||
|         div [ _fieldRow ] [ submit [] "save" s["Save User"] ] | ||||
|     ] | ||||
|  | ||||
| @ -374,8 +374,11 @@ type EditPreferences = | ||||
|         /// The named color for the heading text | ||||
|         HeadingColor : string | ||||
|          | ||||
|         /// Whether the class uses the native font stack | ||||
|         IsNative : bool | ||||
|          | ||||
|         /// The fonts to use for the list | ||||
|         Fonts : string | ||||
|         Fonts : string option | ||||
|          | ||||
|         /// The font size for the heading text | ||||
|         HeadingFontSize : int | ||||
| @ -416,7 +419,7 @@ with | ||||
|             DefaultEmailType    = EmailFormat.fromCode this.DefaultEmailType | ||||
|             LineColor           = this.LineColor | ||||
|             HeadingColor        = this.HeadingColor | ||||
|             Fonts               = this.Fonts | ||||
|             Fonts               = if this.IsNative || Option.isNone this.Fonts then "native" else this.Fonts.Value | ||||
|             HeadingFontSize     = this.HeadingFontSize | ||||
|             TextFontSize        = this.ListFontSize | ||||
|             TimeZoneId          = TimeZoneId this.TimeZone | ||||
| @ -442,7 +445,8 @@ module EditPreferences = | ||||
|             LineColor           = prefs.LineColor | ||||
|             HeadingColorType    = setType prefs.HeadingColor | ||||
|             HeadingColor        = prefs.HeadingColor | ||||
|             Fonts               = prefs.Fonts | ||||
|             IsNative            = (prefs.Fonts = "native") | ||||
|             Fonts               = if prefs.Fonts = "native" then None else Some prefs.Fonts | ||||
|             HeadingFontSize     = prefs.HeadingFontSize | ||||
|             ListFontSize        = prefs.TextFontSize | ||||
|             TimeZone            = TimeZoneId.toString prefs.TimeZoneId | ||||
| @ -804,7 +808,7 @@ with | ||||
|                 reqs | ||||
|                 |> List.map (fun req -> | ||||
|                     let bullet = if this.IsNew req then "circle" else "disc" | ||||
|                     li [ _style $"list-style-type:{bullet};font-family:{p.FontStack};font-size:%i{p.TextFontSize}pt;padding-bottom:.25em;" ] [ | ||||
|                     li [ _style $"list-style-type:{bullet};padding-bottom:.25em;" ] [ | ||||
|                         match req.Requestor with | ||||
|                         | Some r when r <> "" -> | ||||
|                             strong [] [ str r ] | ||||
| @ -825,7 +829,7 @@ with | ||||
|                                 rawText "  ("; str s["as of"].Value; str " "; str dt; rawText ")" | ||||
|                             ] | ||||
|                     ]) | ||||
|                   |> ul [] | ||||
|                   |> ul [ _style $"font-family:{p.FontStack};font-size:%i{p.TextFontSize}pt" ] | ||||
|                 br [] | ||||
|           ] | ||||
|         |> RenderView.AsString.htmlNodes | ||||
|  | ||||
| @ -24,7 +24,7 @@ let delete chId : HttpHandler = requireAccess [ Admin ] >=> validateCsrf >=> fun | ||||
|         let! _, stats = findStats churchId conn | ||||
|         do! Churches.deleteById churchId conn | ||||
|         addInfo ctx | ||||
|             ctx.Strings["The church {0} and its {1} small groups (with {2} prayer request(s)) were deleted successfully; revoked access from {3} user(s)", | ||||
|             ctx.Strings["The church “{0}” and its {1} small group(s) (with {2} prayer request(s)) were deleted successfully; revoked access from {3} user(s)", | ||||
|                         church.Name, stats.SmallGroups, stats.PrayerRequests, stats.Users] | ||||
|         return! redirectTo false "/churches" next ctx | ||||
|     | None -> return! fourOhFour ctx | ||||
|  | ||||
| @ -2,7 +2,6 @@ | ||||
| module PrayerTracker.Extensions | ||||
| 
 | ||||
| open Microsoft.AspNetCore.Http | ||||
| open Microsoft.FSharpLu | ||||
| open Newtonsoft.Json | ||||
| open NodaTime | ||||
| open NodaTime.Serialization.JsonNet | ||||
| @ -17,18 +16,18 @@ let private jsonSettings = JsonSerializerSettings().ConfigureForNodaTime DateTim | ||||
| type ISession with | ||||
|      | ||||
|     /// Set an object in the session | ||||
|     member this.SetObject key value = | ||||
|     member this.SetObject<'T> key (value : 'T) = | ||||
|         this.SetString (key, JsonConvert.SerializeObject (value, jsonSettings)) | ||||
|      | ||||
|     /// Get an object from the session | ||||
|     member this.GetObject<'T> key = | ||||
|     member this.TryGetObject<'T> key = | ||||
|         match this.GetString key with | ||||
|         | null -> Unchecked.defaultof<'T> | ||||
|         | v -> JsonConvert.DeserializeObject<'T> (v, jsonSettings) | ||||
|         | null -> None | ||||
|         | v -> Some (JsonConvert.DeserializeObject<'T> (v, jsonSettings)) | ||||
| 
 | ||||
|     /// The currently logged on small group | ||||
|     member this.CurrentGroup | ||||
|       with get () = this.GetObject<SmallGroup> Key.Session.currentGroup |> Option.fromObject | ||||
|       with get () = this.TryGetObject<SmallGroup> Key.Session.currentGroup | ||||
|        and set (v : SmallGroup option) =  | ||||
|           match v with | ||||
|           | Some group -> this.SetObject Key.Session.currentGroup group | ||||
| @ -36,7 +35,7 @@ type ISession with | ||||
| 
 | ||||
|     /// The currently logged on user | ||||
|     member this.CurrentUser | ||||
|       with get () = this.GetObject<User> Key.Session.currentUser |> Option.fromObject | ||||
|       with get () = this.TryGetObject<User> Key.Session.currentUser | ||||
|        and set (v : User option) = | ||||
|           match v with | ||||
|           | Some user -> this.SetObject Key.Session.currentUser { user with PasswordHash = "" } | ||||
| @ -45,9 +44,8 @@ type ISession with | ||||
|     /// Current messages for the session | ||||
|     member this.Messages | ||||
|       with get () = | ||||
|           match box (this.GetObject<UserMessage list> Key.Session.userMessages) with | ||||
|           | null -> List.empty<UserMessage> | ||||
|           | msgs -> unbox msgs | ||||
|           this.TryGetObject<UserMessage list> Key.Session.userMessages | ||||
|           |> Option.defaultValue List.empty<UserMessage> | ||||
|        and set (v : UserMessage list) = this.SetObject Key.Session.userMessages v | ||||
| 
 | ||||
| 
 | ||||
| @ -69,28 +67,18 @@ type ClaimsPrincipal with | ||||
|         else None | ||||
| 
 | ||||
| 
 | ||||
| open System.Threading.Tasks | ||||
| open Giraffe | ||||
| open Microsoft.Extensions.Configuration | ||||
| open Npgsql | ||||
| 
 | ||||
| /// Extensions on the ASP.NET Core HTTP context | ||||
| type HttpContext with | ||||
|      | ||||
|     // TODO: is this disposed? | ||||
|     member private this.LazyConn : Lazy<Task<NpgsqlConnection>> = lazy (backgroundTask { | ||||
|         let cfg  = this.GetService<IConfiguration> () | ||||
|         let conn = new NpgsqlConnection (cfg.GetConnectionString "PrayerTracker") | ||||
|         do! conn.OpenAsync () | ||||
|         return conn | ||||
|     }) | ||||
|     /// The system clock (via DI) | ||||
|     member this.Clock = this.GetService<IClock> () | ||||
|      | ||||
|     /// The PostgreSQL connection (configured via DI) | ||||
|     member this.Conn = this.GetService<NpgsqlConnection> () | ||||
|      | ||||
|     /// The system clock (via DI) | ||||
|     member this.Clock = this.GetService<IClock> () | ||||
|      | ||||
|     /// The current instant | ||||
|     member this.Now = this.Clock.GetCurrentInstant () | ||||
|      | ||||
|  | ||||
| @ -23,7 +23,7 @@ let delete grpId : HttpHandler = requireAccess [ Admin ] >=> validateCsrf >=> fu | ||||
|         let! users = Users.countByGroup          groupId conn | ||||
|         do! SmallGroups.deleteById groupId conn | ||||
|         addInfo ctx | ||||
|             ctx.Strings["The group {0} and its {1} prayer request(s) were deleted successfully; revoked access from {2} user(s)", | ||||
|             ctx.Strings["The group “{0}” and its {1} prayer request(s) were deleted successfully; revoked access from {2} user(s)", | ||||
|                         grp.Name, reqs, users] | ||||
|         return! redirectTo false "/small-groups" next ctx | ||||
|     | None -> return! fourOhFour ctx | ||||
|  | ||||
| @ -257,7 +257,8 @@ footer a:hover { | ||||
| .pt-radio-group { | ||||
|   display: flex; | ||||
|   flex-flow: row; | ||||
|   gap: 2rem; | ||||
|   gap: 1.5rem; | ||||
|   align-items: baseline; | ||||
| } | ||||
| .pt-center-text { | ||||
|   text-align: center; | ||||
|  | ||||
| @ -243,6 +243,13 @@ this.PT = { | ||||
|         } | ||||
|       }, | ||||
| 
 | ||||
|       /** | ||||
|        * Enable or disable the font list based on whether the native font stack is selected or not | ||||
|        */ | ||||
|       checkFonts() { | ||||
|         document.getElementById("Fonts").disabled = document.getElementById("IsNative_Y").checked | ||||
|       }, | ||||
| 
 | ||||
|       /** | ||||
|        * Bind the event handlers | ||||
|        */ | ||||
| @ -261,6 +268,12 @@ this.PT = { | ||||
|           }) | ||||
|           PT.smallGroup.preferences.toggleType(name) | ||||
|         }) | ||||
|         ;["Y", "N"].map(name => { | ||||
|           document.getElementById(`IsNative_${name}`).addEventListener("click", () => { | ||||
|             PT.smallGroup.preferences.checkFonts() | ||||
|           }) | ||||
|         }) | ||||
|         PT.smallGroup.preferences.checkFonts() | ||||
|       }, | ||||
|     }, | ||||
|   }, | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user