diff --git a/docs/Gemfile.lock b/docs/Gemfile.lock index c07d508..a25f7b0 100644 --- a/docs/Gemfile.lock +++ b/docs/Gemfile.lock @@ -1,13 +1,13 @@ GEM remote: https://rubygems.org/ specs: - activesupport (4.2.10) + activesupport (4.2.11.1) i18n (~> 0.7) minitest (~> 5.1) thread_safe (~> 0.3, >= 0.3.4) tzinfo (~> 1.1) - addressable (2.5.2) - public_suffix (>= 2.0.2, < 4.0) + addressable (2.7.0) + public_suffix (>= 2.0.2, < 5.0) coffee-script (2.4.1) coffee-script-source execjs @@ -15,8 +15,8 @@ GEM colorator (1.1.0) commonmarker (0.17.13) ruby-enum (~> 0.5) - concurrent-ruby (1.1.4) - dnsruby (1.61.2) + concurrent-ruby (1.1.5) + dnsruby (1.61.3) addressable (~> 2.5) em-websocket (0.5.1) eventmachine (>= 0.12.9) @@ -24,19 +24,21 @@ GEM ethon (0.12.0) ffi (>= 1.3.0) eventmachine (1.2.7) + eventmachine (1.2.7-x64-mingw32) execjs (2.7.0) - faraday (0.15.4) + faraday (0.17.0) multipart-post (>= 1.2, < 3) - ffi (1.10.0) + ffi (1.11.1) + ffi (1.11.1-x64-mingw32) forwardable-extended (2.6.0) - gemoji (3.0.0) - github-pages (197) - activesupport (= 4.2.10) + gemoji (3.0.1) + github-pages (201) + activesupport (= 4.2.11.1) github-pages-health-check (= 1.16.1) - jekyll (= 3.7.4) + jekyll (= 3.8.5) jekyll-avatar (= 0.6.0) jekyll-coffeescript (= 1.1.1) - jekyll-commonmark-ghpages (= 0.1.5) + jekyll-commonmark-ghpages (= 0.1.6) jekyll-default-layout (= 0.1.4) jekyll-feed (= 0.11.0) jekyll-gist (= 1.5.0) @@ -47,7 +49,7 @@ GEM jekyll-readme-index (= 0.2.0) jekyll-redirect-from (= 0.14.0) jekyll-relative-links (= 0.6.0) - jekyll-remote-theme (= 0.3.1) + jekyll-remote-theme (= 0.4.0) jekyll-sass-converter (= 1.5.2) jekyll-seo-tag (= 2.5.0) jekyll-sitemap (= 1.2.0) @@ -72,8 +74,8 @@ GEM listen (= 3.1.5) mercenary (~> 0.3) minima (= 2.5.0) - nokogiri (>= 1.8.5, < 2.0) - rouge (= 2.2.1) + nokogiri (>= 1.10.4, < 2.0) + rouge (= 3.11.0) terminal-table (~> 1.4) github-pages-health-check (1.16.1) addressable (~> 2.3) @@ -81,13 +83,13 @@ GEM octokit (~> 4.0) public_suffix (~> 3.0) typhoeus (~> 1.3) - html-pipeline (2.10.0) + html-pipeline (2.12.0) activesupport (>= 2) nokogiri (>= 1.4) http_parser.rb (0.6.0) i18n (0.9.5) concurrent-ruby (~> 1.0) - jekyll (3.7.4) + jekyll (3.8.5) addressable (~> 2.4) colorator (~> 1.0) em-websocket (~> 0.5) @@ -105,13 +107,13 @@ GEM jekyll-coffeescript (1.1.1) coffee-script (~> 2.2) coffee-script-source (~> 1.11.1) - jekyll-commonmark (1.2.0) + jekyll-commonmark (1.3.1) commonmarker (~> 0.14) - jekyll (>= 3.0, < 4.0) - jekyll-commonmark-ghpages (0.1.5) + jekyll (>= 3.7, < 5.0) + jekyll-commonmark-ghpages (0.1.6) commonmarker (~> 0.17.6) - jekyll-commonmark (~> 1) - rouge (~> 2) + jekyll-commonmark (~> 1.2) + rouge (>= 2.0, < 4.0) jekyll-default-layout (0.1.4) jekyll (~> 3.0) jekyll-feed (0.11.0) @@ -133,7 +135,8 @@ GEM jekyll (~> 3.3) jekyll-relative-links (0.6.0) jekyll (~> 3.3) - jekyll-remote-theme (0.3.1) + jekyll-remote-theme (0.4.0) + addressable (~> 2.0) jekyll (~> 3.5) rubyzip (>= 1.2.1, < 3.0) jekyll-sass-converter (1.5.2) @@ -185,7 +188,7 @@ GEM jekyll-seo-tag (~> 2.0) jekyll-titles-from-headings (0.5.1) jekyll (~> 3.3) - jekyll-watch (2.1.2) + jekyll-watch (2.2.1) listen (~> 3.0) jemoji (0.10.2) gemoji (~> 3.0) @@ -194,38 +197,43 @@ GEM kramdown (1.17.0) liquid (4.0.0) listen (3.1.5) + rb-fsevent (~> 0.9, >= 0.9.4) rb-inotify (~> 0.9, >= 0.9.7) + ruby_dep (~> 1.2) mercenary (0.3.6) mini_portile2 (2.4.0) minima (2.5.0) jekyll (~> 3.5) jekyll-feed (~> 0.9) jekyll-seo-tag (~> 2.1) - minitest (5.11.3) - multipart-post (2.0.0) - nokogiri (1.10.1) + minitest (5.12.2) + multipart-post (2.1.1) + nokogiri (1.10.4) mini_portile2 (~> 2.4.0) - octokit (4.13.0) + nokogiri (1.10.4-x64-mingw32) + mini_portile2 (~> 2.4.0) + octokit (4.14.0) sawyer (~> 0.8.0, >= 0.5.3) pathutil (0.16.2) forwardable-extended (~> 2.6) - public_suffix (3.0.3) + public_suffix (3.1.1) rb-fsevent (0.10.3) rb-inotify (0.10.0) ffi (~> 1.0) - rouge (2.2.1) + rouge (3.11.0) ruby-enum (0.7.2) i18n - rubyzip (1.2.2) + ruby_dep (1.5.0) + rubyzip (2.0.0) safe_yaml (1.0.5) - sass (3.7.3) + sass (3.7.4) sass-listen (~> 4.0.0) sass-listen (4.0.0) rb-fsevent (~> 0.9, >= 0.9.4) rb-inotify (~> 0.9, >= 0.9.7) - sawyer (0.8.1) - addressable (>= 2.3.5, < 2.6) - faraday (~> 0.8, < 1.0) + sawyer (0.8.2) + addressable (>= 2.3.5) + faraday (> 0.8, < 2.0) terminal-table (1.8.0) unicode-display_width (~> 1.1, >= 1.1.1) thread_safe (0.3.6) @@ -233,14 +241,19 @@ GEM ethon (>= 0.9.0) tzinfo (1.2.5) thread_safe (~> 0.1) - unicode-display_width (1.4.1) + tzinfo-data (1.2019.3) + tzinfo (>= 1.0.0) + unicode-display_width (1.6.0) + wdm (0.1.1) PLATFORMS ruby + x64-mingw32 DEPENDENCIES github-pages tzinfo-data + wdm (~> 0.1.0) BUNDLED WITH - 2.0.1 + 2.0.2 diff --git a/src/Directory.Build.props b/src/Directory.Build.props new file mode 100644 index 0000000..9c9a4e9 --- /dev/null +++ b/src/Directory.Build.props @@ -0,0 +1,9 @@ + + + 7.4.0.0 + 7.4.0.0 + danieljsummers + Bit Badger Solutions + 7.4.0 + + diff --git a/src/PrayerTracker.Data/DataAccess.fs b/src/PrayerTracker.Data/DataAccess.fs index de5615b..d36f0cb 100644 --- a/src/PrayerTracker.Data/DataAccess.fs +++ b/src/PrayerTracker.Data/DataAccess.fs @@ -163,9 +163,8 @@ type AppDbContext with """ SELECT * FROM pt."PrayerRequest" WHERE "SmallGroupId" = {0} AND "Text" ILIKE {1} UNION SELECT * FROM pt."PrayerRequest" WHERE "SmallGroupId" = {0} AND COALESCE("Requestor", '') ILIKE {1}""" - |> RawSqlString let like = sprintf "%%%s%%" - this.PrayerRequests.FromSql(sql, grp.smallGroupId, like searchTerm).AsNoTracking () + this.PrayerRequests.FromSqlRaw(sql, grp.smallGroupId, like searchTerm).AsNoTracking () |> reqSort grp.preferences.requestSort |> function | q -> diff --git a/src/PrayerTracker.Data/PrayerTracker.Data.fsproj b/src/PrayerTracker.Data/PrayerTracker.Data.fsproj index 6dd443d..238ed67 100644 --- a/src/PrayerTracker.Data/PrayerTracker.Data.fsproj +++ b/src/PrayerTracker.Data/PrayerTracker.Data.fsproj @@ -1,10 +1,7 @@  - netstandard2.0 - 7.3.2.0 - 7.3.2.0 - 7.3.2 + netstandard2.1 @@ -17,14 +14,14 @@ - - - + + + - + diff --git a/src/PrayerTracker.Tests/PrayerTracker.Tests.fsproj b/src/PrayerTracker.Tests/PrayerTracker.Tests.fsproj index 6fcfc9e..58109c3 100644 --- a/src/PrayerTracker.Tests/PrayerTracker.Tests.fsproj +++ b/src/PrayerTracker.Tests/PrayerTracker.Tests.fsproj @@ -2,10 +2,7 @@ Exe - netcoreapp2.2 - 7.3.2.0 - 7.3.2.0 - 7.3.2 + netcoreapp3.0 @@ -18,9 +15,9 @@ - + - + @@ -30,7 +27,7 @@ - + diff --git a/src/PrayerTracker.UI/Church.fs b/src/PrayerTracker.UI/Church.fs index 341d473..8e2f317 100644 --- a/src/PrayerTracker.UI/Church.fs +++ b/src/PrayerTracker.UI/Church.fs @@ -8,7 +8,7 @@ open PrayerTracker.ViewModels let edit (m : EditChurch) ctx vi = let pageTitle = match m.isNew () with true -> "Add a New Church" | false -> "Edit Church" let s = I18N.localizer.Force () - [ form [ _action "/church/save"; _method "post"; _class "pt-center-columns" ] [ + [ form [ _action "/web/church/save"; _method "post"; _class "pt-center-columns" ] [ style [ _scoped ] [ rawText "#name { width: 20rem; } #city { width: 10rem; } #st { width: 3rem; } #interfaceAddress { width: 30rem; }" ] csrfToken ctx @@ -29,11 +29,11 @@ let edit (m : EditChurch) ctx vi = ] div [ _class "pt-field-row" ] [ div [ _class "pt-checkbox-field" ] [ - input [ yield _type "checkbox" - yield _name "hasInterface" - yield _id "hasInterface" - yield _value "True" - match m.hasInterface with Some x when x -> yield _checked | _ -> () ] + input [ _type "checkbox" + _name "hasInterface" + _id "hasInterface" + _value "True" + match m.hasInterface with Some x when x -> _checked | _ -> () ] label [ _for "hasInterface" ] [ locStr s.["Has an interface with Virtual Prayer Room"] ] ] ] @@ -74,12 +74,12 @@ let maintain (churches : Church list) (stats : Map) ctx vi churches |> List.map (fun ch -> let chId = flatGuid ch.churchId - let delAction = sprintf "/church/%s/delete" chId + let delAction = sprintf "/web/church/%s/delete" chId let delPrompt = s.["Are you sure you want to delete this {0}? This action cannot be undone.", sprintf "%s (%s)" (s.["Church"].Value.ToLower ()) ch.name] tr [] [ td [] [ - a [ _href (sprintf "/church/%s/edit" chId); _title s.["Edit This Church"].Value ] [ icon "edit" ] + a [ _href (sprintf "/web/church/%s/edit" chId); _title s.["Edit This Church"].Value ] [ icon "edit" ] a [ _href delAction _title s.["Delete This Church"].Value _onclick (sprintf "return PT.confirmDelete('%s','%A')" delAction delPrompt) ] @@ -96,7 +96,7 @@ let maintain (churches : Church list) (stats : Map) ctx vi ] [ div [ _class "pt-center-text" ] [ br [] - a [ _href (sprintf "/church/%s/edit" emptyGuid); _title s.["Add a New Church"].Value ] + a [ _href (sprintf "/web/church/%s/edit" emptyGuid); _title s.["Add a New Church"].Value ] [ icon "add_circle"; rawText "  "; locStr s.["Add a New Church"] ] br [] br [] diff --git a/src/PrayerTracker.UI/CommonFunctions.fs b/src/PrayerTracker.UI/CommonFunctions.fs index ec0f705..d89fb7c 100644 --- a/src/PrayerTracker.UI/CommonFunctions.fs +++ b/src/PrayerTracker.UI/CommonFunctions.fs @@ -52,22 +52,22 @@ let tableSummary itemCount (s : IStringLocalizer) = let namedColorList name selected attrs (s : IStringLocalizer) = /// The list of HTML named colors (name, display, text color) seq { - yield ("aqua", s.["Aqua"], "black") - yield ("black", s.["Black"], "white") - yield ("blue", s.["Blue"], "white") - yield ("fuchsia", s.["Fuchsia"], "black") - yield ("gray", s.["Gray"], "white") - yield ("green", s.["Green"], "white") - yield ("lime", s.["Lime"], "black") - yield ("maroon", s.["Maroon"], "white") - yield ("navy", s.["Navy"], "white") - yield ("olive", s.["Olive"], "white") - yield ("purple", s.["Purple"], "white") - yield ("red", s.["Red"], "black") - yield ("silver", s.["Silver"], "black") - yield ("teal", s.["Teal"], "white") - yield ("white", s.["White"], "black") - yield ("yellow", s.["Yellow"], "black") + ("aqua", s.["Aqua"], "black") + ("black", s.["Black"], "white") + ("blue", s.["Blue"], "white") + ("fuchsia", s.["Fuchsia"], "black") + ("gray", s.["Gray"], "white") + ("green", s.["Green"], "white") + ("lime", s.["Lime"], "black") + ("maroon", s.["Maroon"], "white") + ("navy", s.["Navy"], "white") + ("olive", s.["Olive"], "white") + ("purple", s.["Purple"], "white") + ("red", s.["Red"], "black") + ("silver", s.["Silver"], "black") + ("teal", s.["Teal"], "white") + ("white", s.["White"], "black") + ("yellow", s.["Yellow"], "black") } |> Seq.map (fun color -> let (colorName, dispText, txtColor) = color @@ -81,18 +81,18 @@ let namedColorList name selected attrs (s : IStringLocalizer) = /// Generate an input[type=radio] that is selected if its value is the current value let radio name domId value current = - input [ yield _type "radio" - yield _name name - yield _id domId - yield _value value - match value = current with true -> yield _checked | false -> () ] + input [ _type "radio" + _name name + _id domId + _value value + match value = current with true -> _checked | false -> () ] /// Generate a select list with the current value selected let selectList name selected attrs items = items |> Seq.map (fun (value, text) -> - option [ yield _value value - match value = selected with true -> yield _selected | false -> () ] [ encodedText text ]) + option [ _value value + match value = selected with true -> _selected | false -> () ] [ encodedText text ]) |> List.ofSeq |> select (List.concat [ [ _name name; _id name ]; attrs ]) diff --git a/src/PrayerTracker.UI/Home.fs b/src/PrayerTracker.UI/Home.fs index 4f380fb..65edce0 100644 --- a/src/PrayerTracker.UI/Home.fs +++ b/src/PrayerTracker.UI/Home.fs @@ -32,9 +32,9 @@ let error code vi = raw l.["Please use your “Back” button to return to {0}.", s.["PrayerTracker"]] ] ] - yield br [] - yield hr [] - yield div [ _style "font-size:70%;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen-Sans,Ubuntu,Cantarell,'Helvetica Neue',sans-serif" ] [ + br [] + hr [] + div [ _style "font-size:70%;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen-Sans,Ubuntu,Cantarell,'Helvetica Neue',sans-serif" ] [ img [ _src (sprintf "/img/%A.png" s.["footer_en"]) _alt (sprintf "%A %A" s.["PrayerTracker"] s.["from Bit Badger Solutions"]) _title (sprintf "%A %A" s.["PrayerTracker"] s.["from Bit Badger Solutions"]) @@ -203,7 +203,7 @@ let termsOfService vi = use sw = new StringWriter () let raw = rawLocText sw let ppLink = - a [ _href "/legal/privacy-policy" ] [ str (s.["Privacy Policy"].Value.ToLower ()) ] + a [ _href "/web/legal/privacy-policy" ] [ str (s.["Privacy Policy"].Value.ToLower ()) ] |> (renderHtmlNode >> HtmlString) [ p [ _class "pt-right-text" ] [ small [] [ em [] [ raw l.["(as of May 24, 2018)"] ] ] ] diff --git a/src/PrayerTracker.UI/Layout.fs b/src/PrayerTracker.UI/Layout.fs index d9d346c..b692840 100644 --- a/src/PrayerTracker.UI/Layout.fs +++ b/src/PrayerTracker.UI/Layout.fs @@ -22,60 +22,60 @@ module Navigation = let leftLinks = [ match m.user with | Some u -> - yield li [ _class "dropdown" ] [ + li [ _class "dropdown" ] [ a [ _class "dropbtn"; _role "button"; _aria "label" s.["Requests"].Value; _title s.["Requests"].Value ] [ icon "question_answer"; space; locStr s.["Requests"]; space; icon "keyboard_arrow_down" ] div [ _class "dropdown-content"; _role "menu" ] [ - a [ _href "/prayer-requests" ] [ icon "compare_arrows"; menuSpacer; locStr s.["Maintain"] ] - a [ _href "/prayer-requests/view" ] [ icon "list"; menuSpacer; locStr s.["View List"] ] + a [ _href "/web/prayer-requests" ] [ icon "compare_arrows"; menuSpacer; locStr s.["Maintain"] ] + a [ _href "/web/prayer-requests/view" ] [ icon "list"; menuSpacer; locStr s.["View List"] ] ] ] - yield li [ _class "dropdown" ] [ + li [ _class "dropdown" ] [ a [ _class "dropbtn"; _role "button"; _aria "label" s.["Group"].Value; _title s.["Group"].Value ] [ icon "group"; space; locStr s.["Group"]; space; icon "keyboard_arrow_down" ] div [ _class "dropdown-content"; _role "menu" ] [ - a [ _href "/small-group/members" ] [ icon "email"; menuSpacer; locStr s.["Maintain Group Members"] ] - a [ _href "/small-group/announcement" ] [ icon "send"; menuSpacer; locStr s.["Send Announcement"] ] - a [ _href "/small-group/preferences" ] [ icon "build"; menuSpacer; locStr s.["Change Preferences"] ] + a [ _href "/web/small-group/members" ] [ icon "email"; menuSpacer; locStr s.["Maintain Group Members"] ] + a [ _href "/web/small-group/announcement" ] [ icon "send"; menuSpacer; locStr s.["Send Announcement"] ] + a [ _href "/web/small-group/preferences" ] [ icon "build"; menuSpacer; locStr s.["Change Preferences"] ] ] ] match u.isAdmin with | true -> - yield li [ _class "dropdown" ] [ + li [ _class "dropdown" ] [ a [ _class "dropbtn"; _role "button"; _aria "label" s.["Administration"].Value; _title s.["Administration"].Value ] [ icon "settings"; space; locStr s.["Administration"]; space; icon "keyboard_arrow_down" ] div [ _class "dropdown-content"; _role "menu" ] [ - a [ _href "/churches" ] [ icon "home"; menuSpacer; locStr s.["Churches"] ] - a [ _href "/small-groups" ] [ icon "send"; menuSpacer; locStr s.["Groups"] ] - a [ _href "/users" ] [ icon "build"; menuSpacer; locStr s.["Users"] ] + a [ _href "/web/churches" ] [ icon "home"; menuSpacer; locStr s.["Churches"] ] + a [ _href "/web/small-groups" ] [ icon "send"; menuSpacer; locStr s.["Groups"] ] + a [ _href "/web/users" ] [ icon "build"; menuSpacer; locStr s.["Users"] ] ] ] | false -> () | None -> match m.group with | Some _ -> - yield li [] [ - a [ _href "/prayer-requests/view" + li [] [ + a [ _href "/web/prayer-requests/view" _aria "label" s.["View Request List"].Value _title s.["View Request List"].Value ] [ icon "list"; space; locStr s.["View Request List"] ] ] | None -> - yield li [ _class "dropdown" ] [ + li [ _class "dropdown" ] [ a [ _class "dropbtn"; _role "button"; _aria "label" s.["Log On"].Value; _title s.["Log On"].Value ] [ icon "security"; space; locStr s.["Log On"]; space; icon "keyboard_arrow_down" ] div [ _class "dropdown-content"; _role "menu" ] [ - a [ _href "/user/log-on" ] [ icon "person"; menuSpacer; locStr s.["User"] ] - a [ _href "/small-group/log-on" ] [ icon "group"; menuSpacer; locStr s.["Group"] ] + a [ _href "/web/user/log-on" ] [ icon "person"; menuSpacer; locStr s.["User"] ] + a [ _href "/web/small-group/log-on" ] [ icon "group"; menuSpacer; locStr s.["Group"] ] ] ] - yield li [] [ - a [ _href "/prayer-requests/lists" + li [] [ + a [ _href "/web/prayer-requests/lists" _aria "label" s.["View Request List"].Value _title s.["View Request List"].Value ] [ icon "list"; space; locStr s.["View Request List"] ] ] - yield li [] [ + li [] [ a [ _href (sprintf "https://docs.prayer.bitbadger.solutions/%s" <| langCode ()) _aria "label" s.["Help"].Value; _title s.["View Help"].Value @@ -89,15 +89,15 @@ module Navigation = | Some _ -> [ match m.user with | Some _ -> - yield li [] [ - a [ _href "/user/password" + li [] [ + a [ _href "/web/user/password" _aria "label" s.["Change Your Password"].Value _title s.["Change Your Password"].Value ] [ icon "lock"; space; locStr s.["Change Your Password"] ] ] | None -> () - yield li [] [ - a [ _href "/log-off"; _aria "label" s.["Log Off"].Value; _title s.["Log Off"].Value ] + li [] [ + a [ _href "/web/log-off"; _aria "label" s.["Log Off"].Value; _title s.["Log Off"].Value ] [ icon "power_settings_new"; space; locStr s.["Log Off"] ] ] ] @@ -105,7 +105,7 @@ module Navigation = header [ _class "pt-title-bar" ] [ section [ _class "pt-title-bar-left" ] [ span [ _class "pt-title-bar-home" ] [ - a [ _href "/"; _title s.["Home"].Value ] [ locStr s.["PrayerTracker"] ] + a [ _href "/web/"; _title s.["Home"].Value ] [ locStr s.["PrayerTracker"] ] ] ul [] leftLinks ] @@ -120,35 +120,35 @@ module Navigation = let s = I18N.localizer.Force () header [ _id "pt-language" ] [ div [] [ - yield span [ _class "u" ] [ locStr s.["Language"]; rawText ": " ] + span [ _class "u" ] [ locStr s.["Language"]; rawText ": " ] match langCode () with | "es" -> - yield locStr s.["Spanish"] - yield rawText "   •   " - yield a [ _href "/language/en" ] [ locStr s.["Change to English"] ] + locStr s.["Spanish"] + rawText "   •   " + a [ _href "/web/language/en" ] [ locStr s.["Change to English"] ] | _ -> - yield locStr s.["English"] - yield rawText "   •   " - yield a [ _href "/language/es" ] [ locStr s.["Cambie a Español"] ] + locStr s.["English"] + rawText "   •   " + a [ _href "/web/language/es" ] [ locStr s.["Cambie a Español"] ] ] match m.group with | Some g -> [ match m.user with | Some u -> - yield span [ _class "u" ] [ locStr s.["Currently Logged On"] ] - yield rawText "   " - yield icon "person" - yield strong [] [ str u.fullName ] - yield rawText "    " + span [ _class "u" ] [ locStr s.["Currently Logged On"] ] + rawText "   " + icon "person" + strong [] [ str u.fullName ] + rawText "    " | None -> - yield locStr s.["Logged On as a Member of"] - yield rawText "  " - yield icon "group" - yield space + locStr s.["Logged On as a Member of"] + rawText "  " + icon "group" + space match m.user with - | Some _ -> yield a [ _href "/small-group" ] [ strong [] [ str g.name ] ] - | None -> yield strong [] [ str g.name ] - yield rawText "  " + | Some _ -> a [ _href "/web/small-group" ] [ strong [] [ str g.name ] ] + | None -> strong [] [ str g.name ] + rawText "  " ] | None -> [] |> div [] @@ -179,13 +179,13 @@ let private commonHead = let private htmlHead m pageTitle = let s = I18N.localizer.Force () head [] [ - yield meta [ _charset "UTF-8" ] - yield title [] [ locStr pageTitle; titleSep; locStr s.["PrayerTracker"] ] + meta [ _charset "UTF-8" ] + title [] [ locStr pageTitle; titleSep; locStr s.["PrayerTracker"] ] yield! commonHead for cssFile in m.style do - yield link [ _rel "stylesheet"; _href (sprintf "/css/%s.css" cssFile); _type "text/css" ] + link [ _rel "stylesheet"; _href (sprintf "/css/%s.css" cssFile); _type "text/css" ] for jsFile in m.script do - yield script [ _src (sprintf "/js/%s.js" jsFile) ] [] + script [ _src (sprintf "/js/%s.js" jsFile) ] [] ] /// Render a link to the help page for the current page @@ -202,10 +202,8 @@ let private helpLink link = /// Render the page title, and optionally a help link let private renderPageTitle m pageTitle = h2 [ _id "pt-page-title" ] [ - match m.helpLink with - | Some link -> yield Help.fullLink (langCode ()) link |> helpLink - | None -> () - yield locStr pageTitle + match m.helpLink with Some link -> Help.fullLink (langCode ()) link |> helpLink | None -> () + locStr pageTitle ] /// Render the messages that may need to be displayed to the user @@ -219,13 +217,13 @@ let private messages m = match msg.level with | "Info" -> () | lvl -> - yield strong [] [ locStr s.[lvl] ] - yield rawText " » " - yield rawText msg.text.Value + strong [] [ locStr s.[lvl] ] + rawText " » " + rawText msg.text.Value match msg.description with | Some desc -> - yield br [] - yield div [ _class "description" ] [ rawText desc.Value ] + br [] + div [ _class "description" ] [ rawText desc.Value ] | None -> () ] ] @@ -238,9 +236,9 @@ let private htmlFooter m = let resultTime = TimeSpan(DateTime.Now.Ticks - m.requestStart).TotalSeconds footer [] [ div [ _id "pt-legal" ] [ - a [ _href "/legal/privacy-policy" ] [ locStr s.["Privacy Policy"] ] + a [ _href "/web/legal/privacy-policy" ] [ locStr s.["Privacy Policy"] ] rawText " • " - a [ _href "/legal/terms-of-service" ] [ locStr s.["Terms of Service"] ] + a [ _href "/web/legal/terms-of-service" ] [ locStr s.["Terms of Service"] ] rawText " • " a [ _href "https://github.com/bit-badger/PrayerTracker" _title s.["View source code and get technical support"].Value @@ -250,7 +248,7 @@ let private htmlFooter m = ] ] div [ _id "pt-footer" ] [ - a [ _href "/"; _style "line-height:28px;" ] [ + a [ _href "/web/"; _style "line-height:28px;" ] [ img [ _src (sprintf "/img/%O.png" s.["footer_en"]); _alt imgText; _title imgText ] ] str m.version @@ -262,7 +260,7 @@ let private htmlFooter m = ] /// The standard layout for PrayerTracker -let standard m pageTitle content = +let standard m pageTitle (content : XmlNode) = let s = I18N.localizer.Force () let ttl = s.[pageTitle] html [ _lang "" ] [ @@ -270,11 +268,11 @@ let standard m pageTitle content = body [] [ Navigation.top m div [ _id "pt-body" ] [ - yield Navigation.identity m - yield renderPageTitle m ttl + Navigation.identity m + renderPageTitle m ttl yield! messages m - yield content - yield htmlFooter m + content + htmlFooter m ] ] ] @@ -288,7 +286,5 @@ let bare pageTitle content = meta [ _charset "UTF-8" ] title [] [ locStr ttl; titleSep; locStr s.["PrayerTracker"] ] ] - body [] [ - content - ] + body [] [ content ] ] diff --git a/src/PrayerTracker.UI/PrayerRequest.fs b/src/PrayerTracker.UI/PrayerRequest.fs index 9e4ec15..b619d71 100644 --- a/src/PrayerTracker.UI/PrayerRequest.fs +++ b/src/PrayerTracker.UI/PrayerRequest.fs @@ -15,18 +15,18 @@ open System.Text let edit (m : EditRequest) today ctx vi = let s = I18N.localizer.Force () let pageTitle = match m.isNew () with true -> "Add a New Request" | false -> "Edit Request" - [ form [ _action "/prayer-request/save"; _method "post"; _class "pt-center-columns" ] [ + [ form [ _action "/web/prayer-request/save"; _method "post"; _class "pt-center-columns" ] [ csrfToken ctx input [ _type "hidden"; _name "requestId"; _value (flatGuid m.requestId) ] div [ _class "pt-field-row" ] [ - yield div [ _class "pt-field" ] [ + div [ _class "pt-field" ] [ label [ _for "requestType" ] [ locStr s.["Request Type"] ] ReferenceList.requestTypeList s |> Seq.ofList |> Seq.map (fun (typ, desc) -> typ.code, desc.Value) |> selectList "requestType" m.requestType [ _required; _autofocus ] ] - yield div [ _class "pt-field" ] [ + div [ _class "pt-field" ] [ label [ _for "requestor" ] [ locStr s.["Requestor / Subject"] ] input [ _type "text" _name "requestor" @@ -35,12 +35,12 @@ let edit (m : EditRequest) today ctx vi = ] match m.isNew () with | true -> - yield div [ _class "pt-field" ] [ + div [ _class "pt-field" ] [ label [ _for "enteredDate" ] [ locStr s.["Date"] ] input [ _type "date"; _name "enteredDate"; _id "enteredDate"; _placeholder today ] ] | false -> - yield div [ _class "pt-field" ] [ + div [ _class "pt-field" ] [ div [ _class "pt-checkbox-field" ] [ br [] input [ _type "checkbox"; _name "skipDateUpdate"; _id "skipDateUpdate"; _value "True" ] @@ -118,7 +118,7 @@ let lists (grps : SmallGroup list) vi = let l = I18N.forView "Requests/Lists" use sw = new StringWriter () let raw = rawLocText sw - [ yield p [] [ + [ p [] [ raw l.["The groups listed below have either public or password-protected request lists."] space raw l.["Those with list icons are public, and those with log on icons are password-protected."] @@ -126,10 +126,10 @@ let lists (grps : SmallGroup list) vi = raw l.["Click the appropriate icon to log on or view the request list."] ] match grps.Length with - | 0 -> yield p [] [ raw l.["There are no groups with public or password-protected request lists."] ] + | 0 -> p [] [ raw l.["There are no groups with public or password-protected request lists."] ] | count -> - yield tableSummary count s - yield table [ _class "pt-table pt-action-table" ] [ + tableSummary count s + table [ _class "pt-table pt-action-table" ] [ thead [] [ tr [] [ th [] [ locStr s.["Actions"] ] @@ -143,9 +143,9 @@ let lists (grps : SmallGroup list) vi = tr [] [ match grp.preferences.isPublic with | true -> - a [ _href (sprintf "/prayer-requests/%s/list" grpId); _title s.["View"].Value ] [ icon "list" ] + a [ _href (sprintf "/web/prayer-requests/%s/list" grpId); _title s.["View"].Value ] [ icon "list" ] | false -> - a [ _href (sprintf "/small-group/log-on/%s" grpId); _title s.["Log On"].Value ] + a [ _href (sprintf "/web/small-group/log-on/%s" grpId); _title s.["Log On"].Value ] [ icon "verified_user" ] |> List.singleton |> td [] @@ -180,7 +180,7 @@ let maintain m (ctx : HttpContext) vi = |> Seq.map (fun req -> let reqId = flatGuid req.prayerRequestId let reqText = Utils.htmlToPlainText req.text - let delAction = sprintf "/prayer-request/%s/delete" reqId + let delAction = sprintf "/web/prayer-request/%s/delete" reqId let delPrompt = [ s.["Are you sure you want to delete this {0}? This action cannot be undone.", s.["Prayer Request"].Value.ToLower() ] @@ -192,19 +192,19 @@ let maintain m (ctx : HttpContext) vi = |> String.concat "" tr [] [ td [] [ - yield a [ _href (sprintf "/prayer-request/%s/edit" reqId); _title l.["Edit This Prayer Request"].Value ] + a [ _href (sprintf "/web/prayer-request/%s/edit" reqId); _title l.["Edit This Prayer Request"].Value ] [ icon "edit" ] match req.isExpired now m.smallGroup.preferences.daysToExpire with | true -> - yield a [ _href (sprintf "/prayer-request/%s/restore" reqId) - _title l.["Restore This Inactive Request"].Value ] + a [ _href (sprintf "/web/prayer-request/%s/restore" reqId) + _title l.["Restore This Inactive Request"].Value ] [ icon "visibility" ] | false -> - yield a [ _href (sprintf "/prayer-request/%s/expire" reqId) - _title l.["Expire This Request Immediately"].Value ] + a [ _href (sprintf "/web/prayer-request/%s/expire" reqId) + _title l.["Expire This Request Immediately"].Value ] [ icon "visibility_off" ] - yield a [ _href delAction; _title l.["Delete This Request"].Value; - _onclick (sprintf "return PT.confirmDelete('%s','%s')" delAction delPrompt) ] + a [ _href delAction; _title l.["Delete This Request"].Value; + _onclick (sprintf "return PT.confirmDelete('%s','%s')" delAction delPrompt) ] [ icon "delete_forever" ] ] td [ updReq req ] [ @@ -213,28 +213,27 @@ let maintain m (ctx : HttpContext) vi = td [] [ locStr typs.[req.requestType] ] td [ reqExp req ] [ str (match req.requestor with Some r -> r | None -> " ") ] td [] [ - yield - match 60 > reqText.Length with - | true -> rawText reqText - | false -> rawText (sprintf "%s…" reqText.[0..59]) + match reqText.Length with + | len when len < 60 -> rawText reqText + | _ -> rawText (sprintf "%s…" reqText.[0..59]) ] ]) |> List.ofSeq - [ yield div [ _class "pt-center-text" ] [ - yield br [] - yield a [ _href (sprintf "/prayer-request/%s/edit" emptyGuid); _title s.["Add a New Request"].Value ] + [ div [ _class "pt-center-text" ] [ + br [] + a [ _href (sprintf "/web/prayer-request/%s/edit" emptyGuid); _title s.["Add a New Request"].Value ] [ icon "add_circle"; rawText "  "; locStr s.["Add a New Request"] ] - yield rawText "       " - yield a [ _href "/prayer-requests/view"; _title s.["View Prayer Request List"].Value ] + rawText "       " + a [ _href "/web/prayer-requests/view"; _title s.["View Prayer Request List"].Value ] [ icon "list"; rawText "  "; locStr s.["View Prayer Request List"] ] match m.searchTerm with | Some _ -> - yield rawText "       " - yield a [ _href "/prayer-requests"; _title l.["Clear Search Criteria"].Value ] + rawText "       " + a [ _href "/web/prayer-requests"; _title l.["Clear Search Criteria"].Value ] [ icon "highlight_off"; rawText "  "; raw l.["Clear Search Criteria"] ] | None -> () ] - yield form [ _action "/prayer-requests"; _method "get"; _class "pt-center-text pt-search-form" ] [ + form [ _action "/web/prayer-requests"; _method "get"; _class "pt-center-text pt-search-form" ] [ input [ _type "text" _name "search" _placeholder l.["Search requests..."].Value @@ -243,12 +242,12 @@ let maintain m (ctx : HttpContext) vi = space submit [] "search" s.["Search"] ] - yield br [] - yield tableSummary requests.Length s + br [] + tableSummary requests.Length s match requests.Length with | 0 -> () | _ -> - yield table [ _class "pt-table pt-action-table" ] [ + table [ _class "pt-table pt-action-table" ] [ thead [] [ tr [] [ th [] [ locStr s.["Actions"] ] @@ -260,40 +259,41 @@ let maintain m (ctx : HttpContext) vi = ] tbody [] requests ] - yield div [ _class "pt-center-text" ] [ - yield br [] + div [ _class "pt-center-text" ] [ + br [] match m.onlyActive with | Some true -> - yield raw l.["Inactive requests are currently not shown"] - yield br [] - yield a [ _href "/prayer-requests/inactive" ] [ raw l.["Show Inactive Requests"] ] + raw l.["Inactive requests are currently not shown"] + br [] + a [ _href "/web/prayer-requests/inactive" ] [ raw l.["Show Inactive Requests"] ] | _ -> match Option.isSome m.onlyActive with | true -> - yield raw l.["Inactive requests are currently shown"] - yield br [] - yield a [ _href "/prayer-requests" ] [ raw l.["Do Not Show Inactive Requests"] ] - yield br [] - yield br [] + raw l.["Inactive requests are currently shown"] + br [] + a [ _href "/web/prayer-requests" ] [ raw l.["Do Not Show Inactive Requests"] ] + br [] + br [] | false -> () - let srch = [ match m.searchTerm with Some s -> yield "search", s | None -> () ] - let url = match m.onlyActive with Some true | None -> "" | _ -> "/inactive" |> sprintf "/prayer-requests%s" + let srch = [ match m.searchTerm with Some s -> "search", s | None -> () ] let pg = defaultArg m.pageNbr 1 + let url = + match m.onlyActive with Some true | None -> "" | _ -> "/inactive" |> sprintf "/web/prayer-requests%s" match pg with | 1 -> () | _ -> // button (_type "submit" :: attrs) [ icon ico; rawText "  "; locStr text ] let withPage = match pg with 2 -> srch | _ -> ("page", string (pg - 1)) :: srch - yield a [ _href (makeUrl url withPage) ] + a [ _href (makeUrl url withPage) ] [ icon "keyboard_arrow_left"; space; raw l.["Previous Page"] ] - yield rawText "     " + rawText "     " match requests.Length = m.smallGroup.preferences.pageSize with | true -> - yield a [ _href (makeUrl url (("page", string (pg + 1)) :: srch)) ] + a [ _href (makeUrl url (("page", string (pg + 1)) :: srch)) ] [ raw l.["Next Page"]; space; icon "keyboard_arrow_right" ] | false -> () ] - yield form [ _id "DeleteForm"; _action ""; _method "post" ] [ csrfToken ctx ] + form [ _id "DeleteForm"; _action ""; _method "post" ] [ csrfToken ctx ] ] |> Layout.Content.wide |> Layout.standard vi (match m.searchTerm with Some _ -> "Search Results" | None -> "Maintain Requests") @@ -327,15 +327,15 @@ let view m vi = let spacer = rawText "       " let dtString = m.date.ToString "yyyy-MM-dd" [ div [ _class "pt-center-text" ] [ - yield br [] - yield a [ _class "pt-icon-link" - _href (sprintf "/prayer-requests/print/%s" dtString) - _title s.["View Printable"].Value ] [ + br [] + a [ _class "pt-icon-link" + _href (sprintf "/web/prayer-requests/print/%s" dtString) + _title s.["View Printable"].Value ] [ icon "print"; rawText "  "; locStr s.["View Printable"] ] match m.canEmail with | true -> - yield spacer + spacer match m.date.DayOfWeek = DayOfWeek.Sunday with | true -> () | false -> @@ -344,21 +344,21 @@ let view m vi = | true -> date | false -> findSunday (date.AddDays 1.) let sunday = findSunday m.date - yield a [ _class "pt-icon-link" - _href (sprintf "/prayer-requests/view/%s" (sunday.ToString "yyyy-MM-dd")) - _title s.["List for Next Sunday"].Value ] [ + a [ _class "pt-icon-link" + _href (sprintf "/web/prayer-requests/view/%s" (sunday.ToString "yyyy-MM-dd")) + _title s.["List for Next Sunday"].Value ] [ icon "update"; rawText "  "; locStr s.["List for Next Sunday"] ] - yield spacer + spacer let emailPrompt = s.["This will e-mail the current list to every member of your group, without further prompting. Are you sure this is what you are ready to do?"].Value - yield a [ _class "pt-icon-link" - _href (sprintf "/prayer-requests/email/%s" dtString) - _title s.["Send via E-mail"].Value - _onclick (sprintf "return PT.requests.view.promptBeforeEmail('%s')" emailPrompt) ] [ + a [ _class "pt-icon-link" + _href (sprintf "/web/prayer-requests/email/%s" dtString) + _title s.["Send via E-mail"].Value + _onclick (sprintf "return PT.requests.view.promptBeforeEmail('%s')" emailPrompt) ] [ icon "mail_outline"; rawText "  "; locStr s.["Send via E-mail"] ] - yield spacer - yield a [ _class "pt-icon-link"; _href "/prayer-requests"; _title s.["Maintain Prayer Requests"].Value ] [ + spacer + a [ _class "pt-icon-link"; _href "/web/prayer-requests"; _title s.["Maintain Prayer Requests"].Value ] [ icon "compare_arrows"; rawText "  "; locStr s.["Maintain Prayer Requests"] ] | false -> () diff --git a/src/PrayerTracker.UI/PrayerTracker.UI.fsproj b/src/PrayerTracker.UI/PrayerTracker.UI.fsproj index 981eaf1..5481751 100644 --- a/src/PrayerTracker.UI/PrayerTracker.UI.fsproj +++ b/src/PrayerTracker.UI/PrayerTracker.UI.fsproj @@ -1,10 +1,7 @@  - netstandard2.0 - 7.3.2.0 - 7.3.2.0 - 7.3.2 + netstandard2.1 @@ -21,8 +18,8 @@ - - + + @@ -65,7 +62,7 @@ - + diff --git a/src/PrayerTracker.UI/SmallGroup.fs b/src/PrayerTracker.UI/SmallGroup.fs index 8e5235e..cdd279f 100644 --- a/src/PrayerTracker.UI/SmallGroup.fs +++ b/src/PrayerTracker.UI/SmallGroup.fs @@ -11,9 +11,9 @@ open System.IO let announcement isAdmin ctx vi = let s = I18N.localizer.Force () let reqTypes = ReferenceList.requestTypeList s - [ form [ _action "/small-group/announcement/send"; _method "post"; _class "pt-center-columns" ] [ - yield csrfToken ctx - yield div [ _class "pt-field-row" ] [ + [ form [ _action "/web/small-group/announcement/send"; _method "post"; _class "pt-center-columns" ] [ + csrfToken ctx + div [ _class "pt-field-row" ] [ div [ _class "pt-field pt-editor" ] [ label [ _for "text" ] [ locStr s.["Announcement Text"] ] textarea [ _name "text"; _id "text"; _autofocus ] [] @@ -21,7 +21,7 @@ let announcement isAdmin ctx vi = ] match isAdmin with | true -> - yield div [ _class "pt-field-row" ] [ + div [ _class "pt-field-row" ] [ div [ _class "pt-field" ] [ label [] [ locStr s.["Send Announcement to"]; rawText ":" ] div [ _class "pt-center-text" ] [ @@ -32,15 +32,14 @@ let announcement isAdmin ctx vi = ] ] ] - | false -> - yield input [ _type "hidden"; _name "sendToClass"; _value "Y" ] - yield div [ _class "pt-field-row pt-fadeable pt-shown"; _id "divAddToList" ] [ + | false -> input [ _type "hidden"; _name "sendToClass"; _value "Y" ] + div [ _class "pt-field-row pt-fadeable pt-shown"; _id "divAddToList" ] [ div [ _class "pt-checkbox-field" ] [ input [ _type "checkbox"; _name "addToRequestList"; _id "addToRequestList"; _value "True" ] label [ _for "addToRequestList" ] [ locStr s.["Add to Request List"] ] ] ] - yield div [ _class "pt-field-row pt-fadeable"; _id "divCategory" ] [ + div [ _class "pt-field-row pt-fadeable"; _id "divCategory" ] [ div [ _class "pt-field" ] [ label [ _for "requestType" ] [ locStr s.["Request Type"] ] reqTypes @@ -49,7 +48,7 @@ let announcement isAdmin ctx vi = |> selectList "requestType" "Announcement" [] ] ] - yield div [ _class "pt-field-row" ] [ submit [] "send" s.["Send Announcement"] ] + div [ _class "pt-field-row" ] [ submit [] "send" s.["Send Announcement"] ] ] script [] [ rawText "PT.onLoad(PT.smallGroup.announcement.onPageLoad)" ] ] @@ -75,7 +74,7 @@ let announcementSent (m : Announcement) vi = let edit (m : EditSmallGroup) (churches : Church list) ctx vi = let s = I18N.localizer.Force () let pageTitle = match m.isNew () with true -> "Add a New Group" | false -> "Edit Group" - form [ _action "/small-group/save"; _method "post"; _class "pt-center-columns" ] [ + form [ _action "/web/small-group/save"; _method "post"; _class "pt-center-columns" ] [ csrfToken ctx input [ _type "hidden"; _name "smallGroupId"; _value (flatGuid m.smallGroupId) ] div [ _class "pt-field-row" ] [ @@ -88,7 +87,7 @@ let edit (m : EditSmallGroup) (churches : Church list) ctx vi = div [ _class "pt-field" ] [ label [ _for "churchId" ] [ locStr s.["Church"] ] seq { - yield "", selectDefault s.["Select Church"].Value + "", selectDefault s.["Select Church"].Value yield! churches |> List.map (fun c -> flatGuid c.churchId, c.name) } |> selectList "churchId" (flatGuid m.churchId) [ _required ] @@ -105,7 +104,7 @@ let edit (m : EditSmallGroup) (churches : Church list) ctx vi = let editMember (m : EditMember) (typs : (string * LocalizedString) seq) ctx vi = let s = I18N.localizer.Force () let pageTitle = match m.isNew () with true -> "Add a New Group Member" | false -> "Edit Group Member" - form [ _action "/small-group/member/save"; _method "post"; _class "pt-center-columns" ] [ + form [ _action "/web/small-group/member/save"; _method "post"; _class "pt-center-columns" ] [ style [ _scoped ] [ rawText "#memberName { width: 15rem; } #emailAddress { width: 20rem; }" ] csrfToken ctx input [ _type "hidden"; _name "memberId"; _value (flatGuid m.memberId) ] @@ -137,16 +136,16 @@ let editMember (m : EditMember) (typs : (string * LocalizedString) seq) ctx vi = /// View for the small group log on page let logOn (grps : SmallGroup list) grpId ctx vi = let s = I18N.localizer.Force () - [ form [ _action "/small-group/log-on/submit"; _method "post"; _class "pt-center-columns" ] [ + [ form [ _action "/web/small-group/log-on/submit"; _method "post"; _class "pt-center-columns" ] [ csrfToken ctx div [ _class "pt-field-row" ] [ div [ _class "pt-field" ] [ label [ _for "smallGroupId" ] [ locStr s.["Group"] ] seq { match grps.Length with - | 0 -> yield "", s.["There are no classes with passwords defined"].Value + | 0 -> "", s.["There are no classes with passwords defined"].Value | _ -> - yield "", selectDefault s.["Select Group"].Value + "", selectDefault s.["Select Group"].Value yield! grps |> List.map (fun grp -> flatGuid grp.smallGroupId, sprintf "%s | %s" grp.church.name grp.name) } @@ -191,12 +190,12 @@ let maintain (grps : SmallGroup list) ctx vi = grps |> List.map (fun g -> let grpId = flatGuid g.smallGroupId - let delAction = sprintf "/small-group/%s/delete" grpId + let delAction = sprintf "/web/small-group/%s/delete" grpId let delPrompt = s.["Are you sure you want to delete this {0}? This action cannot be undone.", sprintf "%s (%s)" (s.["Small Group"].Value.ToLower ()) g.name].Value tr [] [ td [] [ - a [ _href (sprintf "/small-group/%s/edit" grpId); _title s.["Edit This Group"].Value ] [ icon "edit" ] + a [ _href (sprintf "/web/small-group/%s/edit" grpId); _title s.["Edit This Group"].Value ] [ icon "edit" ] a [ _href delAction _title s.["Delete This Group"].Value _onclick (sprintf "return PT.confirmDelete('%s','%s')" delAction delPrompt) ] @@ -210,7 +209,7 @@ let maintain (grps : SmallGroup list) ctx vi = ] [ div [ _class "pt-center-text" ] [ br [] - a [ _href (sprintf "/small-group/%s/edit" emptyGuid); _title s.["Add a New Group"].Value ] [ + a [ _href (sprintf "/web/small-group/%s/edit" emptyGuid); _title s.["Add a New Group"].Value ] [ icon "add_circle" rawText "  " locStr s.["Add a New Group"] @@ -245,14 +244,14 @@ let members (mbrs : Member list) (emailTyps : Map) ctx mbrs |> List.map (fun mbr -> let mbrId = flatGuid mbr.memberId - let delAction = sprintf "/small-group/member/%s/delete" mbrId + let delAction = sprintf "/web/small-group/member/%s/delete" mbrId let delPrompt = s.["Are you sure you want to delete this {0}? This action cannot be undone.", s.["group member"]] .Value .Replace("?", sprintf " (%s)?" mbr.memberName) tr [] [ td [] [ - a [ _href (sprintf "/small-group/member/%s/edit" mbrId); _title s.["Edit This Group Member"].Value ] + a [ _href (sprintf "/web/small-group/member/%s/edit" mbrId); _title s.["Edit This Group Member"].Value ] [ icon "edit" ] a [ _href delAction _title s.["Delete This Group Member"].Value @@ -267,7 +266,7 @@ let members (mbrs : Member list) (emailTyps : Map) ctx ] [ div [ _class"pt-center-text" ] [ br [] - a [ _href (sprintf "/small-group/member/%s/edit" emptyGuid); _title s.["Add a New Group Member"].Value ] + a [ _href (sprintf "/web/small-group/member/%s/edit" emptyGuid); _title s.["Add a New Group Member"].Value ] [ icon "add_circle"; rawText "  "; locStr s.["Add a New Group Member"] ] br [] br [] @@ -292,11 +291,11 @@ let overview m vi = locStr s.["Quick Actions"] ] div [] [ - a [ _href "/prayer-requests/view" ] [ icon "list"; linkSpacer; locStr s.["View Prayer Request List"] ] + a [ _href "/web/prayer-requests/view" ] [ icon "list"; linkSpacer; locStr s.["View Prayer Request List"] ] hr [] - a [ _href "/small-group/announcement" ] [ icon "send"; linkSpacer; locStr s.["Send Announcement"] ] + a [ _href "/web/small-group/announcement" ] [ icon "send"; linkSpacer; locStr s.["Send Announcement"] ] hr [] - a [ _href "/small-group/preferences" ] [ icon "build"; linkSpacer; locStr s.["Change Preferences"] ] + a [ _href "/web/small-group/preferences" ] [ icon "build"; linkSpacer; locStr s.["Change Preferences"] ] ] ] section [] [ @@ -305,21 +304,21 @@ let overview m vi = locStr s.["Prayer Requests"] ] div [] [ - yield p [ _class "pt-center-text" ] [ + p [ _class "pt-center-text" ] [ strong [] [ str (m.totalActiveReqs.ToString "N0"); space; locStr s.["Active Requests"] ] ] - yield hr [] + hr [] for cat in m.activeReqsByCat do - yield str (cat.Value.ToString "N0") - yield space - yield locStr typs.[cat.Key] - yield br [] - yield br [] - yield str (m.allReqs.ToString "N0") - yield space - yield locStr s.["Total Requests"] - yield hr [] - yield a [ _href "/prayer-requests/maintain" ] [ + str (cat.Value.ToString "N0") + space + locStr typs.[cat.Key] + br [] + br [] + str (m.allReqs.ToString "N0") + space + locStr s.["Total Requests"] + hr [] + a [ _href "/web/prayer-requests/maintain" ] [ icon "compare_arrows" linkSpacer locStr s.["Maintain Prayer Requests"] @@ -334,7 +333,7 @@ let overview m vi = div [ _class "pt-center-text" ] [ strong [] [ str (m.totalMbrs.ToString "N0"); space; locStr s.["Members"] ] hr [] - a [ _href "/small-group/members" ] [ icon "email"; linkSpacer; locStr s.["Maintain Group Members"] ] + a [ _href "/web/small-group/members" ] [ icon "email"; linkSpacer; locStr s.["Maintain Group Members"] ] ] ] ] @@ -349,7 +348,7 @@ let preferences (m : EditPreferences) (tzs : TimeZone list) ctx vi = let l = I18N.forView "SmallGroup/Preferences" use sw = new StringWriter () let raw = rawLocText sw - [ form [ _action "/small-group/preferences/save"; _method "post"; _class "pt-center-columns" ] [ + [ form [ _action "/web/small-group/preferences/save"; _method "post"; _class "pt-center-columns" ] [ style [ _scoped ] [ rawText "#expireDays, #daysToKeepNew, #longTermUpdateWeeks, #headingFontSize, #listFontSize, #pageSize { width: 3rem; } #emailFromAddress { width: 20rem; } #listFonts { width: 40rem; } @media screen and (max-width: 40rem) { #listFonts { width: 100%; } }" ] csrfToken ctx fieldset [] [ @@ -406,7 +405,7 @@ let preferences (m : EditPreferences) (tzs : TimeZone list) ctx vi = div [ _class "pt-field" ] [ label [ _for "defaultEmailType" ] [ locStr s.["E-mail Format"] ] seq { - yield "", selectDefault s.["Select"].Value + "", selectDefault s.["Select"].Value yield! ReferenceList.emailTypeList HtmlFormat s |> Seq.skip 1 |> Seq.map (fun typ -> fst typ, (snd typ).Value) @@ -424,16 +423,16 @@ let preferences (m : EditPreferences) (tzs : TimeZone list) ctx vi = radio "headingLineType" "headingLineType_Name" "Name" m.headingLineType label [ _for "headingLineType_Name" ] [ locStr s.["Named Color"] ] namedColorList "headingLineColor" m.headingLineColor - [ yield _id "headingLineColor_Select" - match m.headingLineColor.StartsWith "#" with true -> yield _disabled | false -> () ] s + [ _id "headingLineColor_Select" + match m.headingLineColor.StartsWith "#" with true -> _disabled | false -> () ] s rawText "    "; str (s.["or"].Value.ToUpper ()) radio "headingLineType" "headingLineType_RGB" "RGB" m.headingLineType label [ _for "headingLineType_RGB" ] [ locStr s.["Custom Color"] ] - input [ yield _type "color" - yield _name "headingLineColor" - yield _id "headingLineColor_Color" - yield _value m.headingLineColor - match m.headingLineColor.StartsWith "#" with true -> () | false -> yield _disabled ] + input [ _type "color" + _name "headingLineColor" + _id "headingLineColor_Color" + _value m.headingLineColor + match m.headingLineColor.StartsWith "#" with true -> () | false -> _disabled ] ] ] ] @@ -444,16 +443,16 @@ let preferences (m : EditPreferences) (tzs : TimeZone list) ctx vi = radio "headingTextType" "headingTextType_Name" "Name" m.headingTextType label [ _for "headingTextType_Name" ] [ locStr s.["Named Color"] ] namedColorList "headingTextColor" m.headingTextColor - [ yield _id "headingTextColor_Select" - match m.headingTextColor.StartsWith "#" with true -> yield _disabled | false -> () ] s + [ _id "headingTextColor_Select" + match m.headingTextColor.StartsWith "#" with true -> _disabled | false -> () ] s rawText "    "; str (s.["or"].Value.ToUpper ()) radio "headingTextType" "headingTextType_RGB" "RGB" m.headingTextType label [ _for "headingTextType_RGB" ] [ locStr s.["Custom Color"] ] - input [ yield _type "color" - yield _name "headingTextColor" - yield _id "headingTextColor_Color" - yield _value m.headingTextColor - match m.headingTextColor.StartsWith "#" with true -> () | false -> yield _disabled ] + input [ _type "color" + _name "headingTextColor" + _id "headingTextColor_Color" + _value m.headingTextColor + match m.headingTextColor.StartsWith "#" with true -> () | false -> _disabled ] ] ] ] @@ -483,7 +482,7 @@ let preferences (m : EditPreferences) (tzs : TimeZone list) ctx vi = div [ _class "pt-field" ] [ label [ _for "timeZone" ] [ locStr s.["Time Zone"] ] seq { - yield "", selectDefault s.["Select"].Value + "", selectDefault s.["Select"].Value yield! tzs |> List.map (fun tz -> tz.timeZoneId, (TimeZones.name tz.timeZoneId s).Value) } |> selectList "timeZone" m.timeZone [ _required ] @@ -502,10 +501,10 @@ let preferences (m : EditPreferences) (tzs : TimeZone list) ctx vi = label [ _for "viz_Password" ] [ locStr s.["Password Protected"] ] ] ] - div [ yield _id "divClassPassword" + div [ _id "divClassPassword" match m.listVisibility = RequestVisibility.passwordProtected with - | true -> yield _class "pt-field-row pt-fadeable pt-show" - | false -> yield _class "pt-field-row pt-fadeable" + | true -> _class "pt-field-row pt-fadeable pt-show" + | false -> _class "pt-field-row pt-fadeable" ] [ div [ _class "pt-field" ] [ label [ _for "groupPassword" ] [ locStr s.["Group Password (Used to Read Online)"] ] @@ -526,8 +525,8 @@ let preferences (m : EditPreferences) (tzs : TimeZone list) ctx vi = |> selectList "asOfDate" m.asOfDate [ _required ] ] ] - div [ _class "pt-field-row" ] [ submit [] "save" s.["Save Preferences"] ] ] + div [ _class "pt-field-row" ] [ submit [] "save" s.["Save Preferences"] ] ] p [] [ rawText "** " diff --git a/src/PrayerTracker.UI/User.fs b/src/PrayerTracker.UI/User.fs index b6527f8..9f933cc 100644 --- a/src/PrayerTracker.UI/User.fs +++ b/src/PrayerTracker.UI/User.fs @@ -8,7 +8,7 @@ open PrayerTracker.ViewModels let assignGroups m groups curGroups ctx vi = let s = I18N.localizer.Force () let pageTitle = sprintf "%s • %A" m.userName s.["Assign Groups"] - form [ _action "/user/small-groups/save"; _method "post"; _class "pt-center-columns" ] [ + form [ _action "/web/user/small-groups/save"; _method "post"; _class "pt-center-columns" ] [ csrfToken ctx input [ _type "hidden"; _name "userId"; _value (flatGuid m.userId) ] input [ _type "hidden"; _name "userName"; _value m.userName ] @@ -24,11 +24,11 @@ let assignGroups m groups curGroups ctx vi = let inputId = sprintf "id-%s" grpId tr [] [ td [] [ - input [ yield _type "checkbox" - yield _name "smallGroups" - yield _id inputId - yield _value grpId - match curGroups |> List.contains grpId with true -> yield _checked | false -> () ] + input [ _type "checkbox" + _name "smallGroups" + _id inputId + _value grpId + match curGroups |> List.contains grpId with true -> _checked | false -> () ] ] td [] [ label [ _for inputId ] [ str grpName ] ] ]) @@ -47,7 +47,7 @@ let changePassword ctx vi = [ p [ _class "pt-center-text" ] [ locStr s.["To change your password, enter your current password in the specified box below, then enter your new password twice."] ] - form [ _action "/user/password/change" + form [ _action "/web/user/password/change" _method "post" _onsubmit (sprintf "return PT.compareValidation('newPassword','newPasswordConfirm','%A')" s.["The passwords do not match"]) ] [ style [ _scoped ] [ rawText "#oldPassword, #newPassword, #newPasswordConfirm { width: 10rem; } "] @@ -83,7 +83,7 @@ let edit (m : EditUser) ctx vi = let s = I18N.localizer.Force () let pageTitle = match m.isNew () with true -> "Add a New User" | false -> "Edit User" let pwPlaceholder = s.[match m.isNew () with true -> "" | false -> "No change"].Value - [ form [ _action "/user/edit/save"; _method "post"; _class "pt-center-columns" + [ form [ _action "/web/user/edit/save"; _method "post"; _class "pt-center-columns" _onsubmit (sprintf "return PT.compareValidation('password','passwordConfirm','%A')" s.["The passwords do not match"]) ] [ style [ _scoped ] [ rawText "#firstName, #lastName, #password, #passwordConfirm { width: 10rem; } #emailAddress { width: 20rem; } " ] @@ -114,11 +114,11 @@ let edit (m : EditUser) ctx vi = ] ] div [ _class "pt-checkbox-field" ] [ - input [ yield _type "checkbox" - yield _name "isAdmin" - yield _id "isAdmin" - yield _value "True" - match m.isAdmin with Some x when x -> yield _checked | _ -> () ] + input [ _type "checkbox" + _name "isAdmin" + _id "isAdmin" + _value "True" + match m.isAdmin with Some x when x -> _checked | _ -> () ] label [ _for "isAdmin" ] [ locStr s.["This user is a PrayerTracker administrator"] ] ] div [ _class "pt-field-row" ] [ submit [] "save" s.["Save User"] ] @@ -132,7 +132,7 @@ let edit (m : EditUser) ctx vi = /// View for the user log on page let logOn (m : UserLogOn) groups ctx vi = let s = I18N.localizer.Force () - form [ _action "/user/log-on"; _method "post"; _class "pt-center-columns" ] [ + form [ _action "/web/user/log-on"; _method "post"; _class "pt-center-columns" ] [ style [ _scoped ] [ rawText "#emailAddress { width: 20rem; }" ] csrfToken ctx input [ _type "hidden"; _name "redirectUrl"; _value (defaultArg m.redirectUrl "") ] @@ -151,7 +151,7 @@ let logOn (m : UserLogOn) groups ctx vi = div [ _class "pt-field" ] [ label [ _for "smallGroupId" ] [ locStr s.["Group"] ] seq { - yield "", selectDefault s.["Select Group"].Value + "", selectDefault s.["Select Group"].Value yield! groups } |> selectList "smallGroupId" "" [ _required ] @@ -189,13 +189,13 @@ let maintain (users : User list) ctx vi = users |> List.map (fun user -> let userId = flatGuid user.userId - let delAction = sprintf "/user/%s/delete" userId + let delAction = sprintf "/web/user/%s/delete" userId let delPrompt = s.["Are you sure you want to delete this {0}? This action cannot be undone.", (sprintf "%s (%s)" (s.["User"].Value.ToLower()) user.fullName)].Value tr [] [ td [] [ - a [ _href (sprintf "/user/%s/edit" userId); _title s.["Edit This User"].Value ] [ icon "edit" ] - a [ _href (sprintf "/user/%s/small-groups" userId); _title s.["Assign Groups to This User"].Value ] + a [ _href (sprintf "/web/user/%s/edit" userId); _title s.["Edit This User"].Value ] [ icon "edit" ] + a [ _href (sprintf "/web/user/%s/small-groups" userId); _title s.["Assign Groups to This User"].Value ] [ icon "group" ] a [ _href delAction _title s.["Delete This User"].Value @@ -205,15 +205,15 @@ let maintain (users : User list) ctx vi = td [] [ str user.fullName ] td [ _class "pt-center-text" ] [ match user.isAdmin with - | true -> yield strong [] [ locStr s.["Yes"] ] - | false -> yield locStr s.["No"] + | true -> strong [] [ locStr s.["Yes"] ] + | false -> locStr s.["No"] ] ]) |> tbody [] ] [ div [ _class "pt-center-text" ] [ br [] - a [ _href (sprintf "/user/%s/edit" emptyGuid); _title s.["Add a New User"].Value ] + a [ _href (sprintf "/web/user/%s/edit" emptyGuid); _title s.["Add a New User"].Value ] [ icon "add_circle"; rawText "  "; locStr s.["Add a New User"] ] br [] br [] diff --git a/src/PrayerTracker.UI/Utils.fs b/src/PrayerTracker.UI/Utils.fs index dda074e..b7c8766 100644 --- a/src/PrayerTracker.UI/Utils.fs +++ b/src/PrayerTracker.UI/Utils.fs @@ -30,17 +30,15 @@ module String = /// string.Replace() let replace (find : string) repl (str : string) = str.Replace (find, repl) - /// Replace the first occurrence of a string with a second string within a given string let replaceFirst (needle : string) replacement (haystack : string) = match haystack.IndexOf needle with | -1 -> haystack | idx -> - seq { - yield haystack.[0..idx - 1] - yield replacement - yield haystack.[idx + needle.Length..] - } + [ haystack.[0..idx - 1] + replacement + haystack.[idx + needle.Length..] + ] |> String.concat "" diff --git a/src/PrayerTracker.UI/ViewModels.fs b/src/PrayerTracker.UI/ViewModels.fs index 85701ad..24152c1 100644 --- a/src/PrayerTracker.UI/ViewModels.fs +++ b/src/PrayerTracker.UI/ViewModels.fs @@ -25,16 +25,16 @@ module ReferenceList = | HtmlFormat -> s.["HTML Format"].Value | PlainTextFormat -> s.["Plain-Text Format"].Value seq { - yield "", LocalizedString ("", sprintf "%s (%s)" s.["Group Default"].Value defaultType) - yield HtmlFormat.code, s.["HTML Format"] - yield PlainTextFormat.code, s.["Plain-Text Format"] + "", LocalizedString ("", sprintf "%s (%s)" s.["Group Default"].Value defaultType) + HtmlFormat.code, s.["HTML Format"] + PlainTextFormat.code, s.["Plain-Text Format"] } /// A list of expiration options let expirationList (s : IStringLocalizer) includeExpireNow = - [ yield Automatic.code, s.["Expire Normally"] - yield Manual.code, s.["Request Never Expires"] - match includeExpireNow with true -> yield Forced.code, s.["Expire Immediately"] | false -> () + [ Automatic.code, s.["Expire Normally"] + Manual.code, s.["Request Never Expires"] + match includeExpireNow with true -> Forced.code, s.["Expire Immediately"] | false -> () ] /// A list of request types @@ -583,7 +583,7 @@ with let asOfSize = Math.Round (float prefs.textFontSize * 0.8, 2) [ match this.showHeader with | true -> - yield div [ _style (sprintf "text-align:center;font-family:%s" prefs.listFonts) ] [ + div [ _style (sprintf "text-align:center;font-family:%s" prefs.listFonts) ] [ span [ _style (sprintf "font-size:%ipt;" prefs.headingFontSize) ] [ strong [] [ str s.["Prayer Requests"].Value ] ] @@ -594,7 +594,7 @@ with str (this.date.ToString s.["MMMM d, yyyy"].Value) ] ] - yield br [] + br [] | false -> () let typs = ReferenceList.requestTypeList s for cat in @@ -604,7 +604,7 @@ with |> Seq.filter (fun c -> 0 < (this.requests |> List.filter (fun req -> req.requestType = c) |> List.length)) do let reqs = this.requestsInCategory cat let catName = typs |> List.filter (fun t -> fst t = cat) |> List.head |> snd - yield div [ _style "padding-left:10px;padding-bottom:.5em;" ] [ + div [ _style "padding-left:10px;padding-bottom:.5em;" ] [ table [ _style (sprintf "font-family:%s;page-break-inside:avoid;" prefs.listFonts) ] [ tr [] [ td [ _style (sprintf "font-size:%ipt;color:%s;padding:3px 0;border-top:solid 3px %s;border-bottom:solid 3px %s;font-weight:bold;" @@ -614,44 +614,43 @@ with ] ] ] - yield - reqs - |> List.map (fun req -> - let bullet = match this.isNew req with true -> "circle" | false -> "disc" - li [ _style (sprintf "list-style-type:%s;font-family:%s;font-size:%ipt;padding-bottom:.25em;" - bullet prefs.listFonts prefs.textFontSize) ] [ - match req.requestor with - | Some rqstr when rqstr <> "" -> - yield strong [] [ str rqstr ] - yield rawText " — " - | Some _ -> () - | None -> () - yield rawText req.text - match prefs.asOfDateDisplay with - | NoDisplay -> () - | ShortDate - | LongDate -> - let dt = - match prefs.asOfDateDisplay with - | ShortDate -> req.updatedDate.ToShortDateString () - | LongDate -> req.updatedDate.ToLongDateString () - | _ -> "" - yield i [ _style (sprintf "font-size:%.2fpt" asOfSize) ] [ - rawText "  ("; str s.["as of"].Value; str " "; str dt; rawText ")" - ] - ]) + reqs + |> List.map (fun req -> + let bullet = match this.isNew req with true -> "circle" | false -> "disc" + li [ _style (sprintf "list-style-type:%s;font-family:%s;font-size:%ipt;padding-bottom:.25em;" + bullet prefs.listFonts prefs.textFontSize) ] [ + match req.requestor with + | Some rqstr when rqstr <> "" -> + strong [] [ str rqstr ] + 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 (sprintf "font-size:%.2fpt" asOfSize) ] [ + rawText "  ("; str s.["as of"].Value; str " "; str dt; rawText ")" + ] + ]) |> ul [] - yield br [] + br [] ] |> renderHtmlNodes /// Generate this list as plain text member this.asText (s : IStringLocalizer) = seq { - yield this.listGroup.name - yield s.["Prayer Requests"].Value - yield this.date.ToString s.["MMMM d, yyyy"].Value - yield " " + this.listGroup.name + s.["Prayer Requests"].Value + this.date.ToString s.["MMMM d, yyyy"].Value + " " let typs = ReferenceList.requestTypeList s for cat in typs @@ -661,24 +660,23 @@ with let reqs = this.requestsInCategory cat let typ = (typs |> List.filter (fun t -> fst t = cat) |> List.head |> snd).Value let dashes = String.replicate (typ.Length + 4) "-" - yield dashes - yield sprintf @" %s" (typ.ToUpper ()) - yield dashes + dashes + sprintf @" %s" (typ.ToUpper ()) + dashes for req in reqs do let bullet = match this.isNew req with true -> "+" | false -> "-" let requestor = match req.requestor with Some r -> sprintf "%s - " r | None -> "" - yield - match this.listGroup.preferences.asOfDateDisplay with - | NoDisplay -> "" - | _ -> - let dt = - match this.listGroup.preferences.asOfDateDisplay with - | ShortDate -> req.updatedDate.ToShortDateString () - | LongDate -> req.updatedDate.ToLongDateString () - | _ -> "" - sprintf " (%s %s)" s.["as of"].Value dt - |> sprintf " %s %s%s%s" bullet requestor (htmlToPlainText req.text) - yield " " + match this.listGroup.preferences.asOfDateDisplay with + | NoDisplay -> "" + | _ -> + let dt = + match this.listGroup.preferences.asOfDateDisplay with + | ShortDate -> req.updatedDate.ToShortDateString () + | LongDate -> req.updatedDate.ToLongDateString () + | _ -> "" + sprintf " (%s %s)" s.["as of"].Value dt + |> sprintf " %s %s%s%s" bullet requestor (htmlToPlainText req.text) + " " } |> String.concat "\n" |> wordWrap 74 diff --git a/src/PrayerTracker.sln b/src/PrayerTracker.sln index a5a2c69..f7736bb 100644 --- a/src/PrayerTracker.sln +++ b/src/PrayerTracker.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 15 -VisualStudioVersion = 15.0.26228.4 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.29411.108 MinimumVisualStudioVersion = 10.0.40219.1 Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "PrayerTracker", "PrayerTracker\PrayerTracker.fsproj", "{63780D3F-D811-4BFB-9FB0-C28A83CCE28F}" EndProject @@ -11,6 +11,11 @@ Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "PrayerTracker.Tests", "Pray EndProject Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "PrayerTracker.Data", "PrayerTracker.Data\PrayerTracker.Data.fsproj", "{2B5BA107-9BDA-4A1D-A9AF-AFEE6BF12270}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{B290BA27-C8B8-44F3-BF01-D103302D815F}" + ProjectSection(SolutionItems) = preProject + Directory.Build.props = Directory.Build.props + EndProjectSection +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU diff --git a/src/PrayerTracker/App.fs b/src/PrayerTracker/App.fs index 51f8259..ab186e3 100644 --- a/src/PrayerTracker/App.fs +++ b/src/PrayerTracker/App.fs @@ -15,6 +15,7 @@ module Configure = open Microsoft.EntityFrameworkCore open Microsoft.Extensions.Configuration open Microsoft.Extensions.DependencyInjection + open Microsoft.Extensions.Hosting open Microsoft.Extensions.Localization open Microsoft.Extensions.Logging open Microsoft.Extensions.Options @@ -61,82 +62,87 @@ module Configure = /// Routes for PrayerTracker let webApp = router Handlers.CommonFunctions.fourOhFour [ - GET [ - subRoute "/church" [ - route "es" Handlers.Church.maintain - routef "/%O/edit" Handlers.Church.edit + // Traditional web app routes + subRoute"/web" [ + GET [ + subRoute "/church" [ + route "es" Handlers.Church.maintain + routef "/%O/edit" Handlers.Church.edit + ] + route "/class/logon" (redirectTo true "/web/small-group/log-on") + routef "/error/%s" Handlers.Home.error + routef "/language/%s" Handlers.Home.language + subRoute "/legal" [ + route "/privacy-policy" Handlers.Home.privacyPolicy + route "/terms-of-service" Handlers.Home.tos + ] + route "/log-off" Handlers.Home.logOff + subRoute "/prayer-request" [ + route "s" (Handlers.PrayerRequest.maintain true) + routef "s/email/%s" Handlers.PrayerRequest.email + route "s/inactive" (Handlers.PrayerRequest.maintain false) + route "s/lists" Handlers.PrayerRequest.lists + routef "s/%O/list" Handlers.PrayerRequest.list + route "s/maintain" (redirectTo true "/web/prayer-requests") + routef "s/print/%s" Handlers.PrayerRequest.print + route "s/view" (Handlers.PrayerRequest.view None) + routef "s/view/%s" (Some >> Handlers.PrayerRequest.view) + routef "/%O/edit" Handlers.PrayerRequest.edit + routef "/%O/expire" Handlers.PrayerRequest.expire + routef "/%O/restore" Handlers.PrayerRequest.restore + ] + subRoute "/small-group" [ + route "" Handlers.SmallGroup.overview + route "s" Handlers.SmallGroup.maintain + route "/announcement" Handlers.SmallGroup.announcement + routef "/%O/edit" Handlers.SmallGroup.edit + route "/log-on" (Handlers.SmallGroup.logOn None) + routef "/log-on/%O" (Some >> Handlers.SmallGroup.logOn) + route "/logon" (redirectTo true "/web/small-group/log-on") + routef "/member/%O/edit" Handlers.SmallGroup.editMember + route "/members" Handlers.SmallGroup.members + route "/preferences" Handlers.SmallGroup.preferences + ] + route "/unauthorized" Handlers.Home.unauthorized + subRoute "/user" [ + route "s" Handlers.User.maintain + routef "/%O/edit" Handlers.User.edit + routef "/%O/small-groups" Handlers.User.smallGroups + route "/log-on" Handlers.User.logOn + route "/logon" (redirectTo true "/web/user/log-on") + route "/password" Handlers.User.password + ] + route "/" Handlers.Home.homePage ] - route "/class/logon" (redirectTo true "/small-group/log-on") - routef "/error/%s" Handlers.Home.error - routef "/language/%s" Handlers.Home.language - subRoute "/legal" [ - route "/privacy-policy" Handlers.Home.privacyPolicy - route "/terms-of-service" Handlers.Home.tos - ] - route "/log-off" Handlers.Home.logOff - subRoute "/prayer-request" [ - route "s" (Handlers.PrayerRequest.maintain true) - routef "s/email/%s" Handlers.PrayerRequest.email - route "s/inactive" (Handlers.PrayerRequest.maintain false) - route "s/lists" Handlers.PrayerRequest.lists - routef "s/%O/list" Handlers.PrayerRequest.list - route "s/maintain" (redirectTo true "/prayer-requests") - routef "s/print/%s" Handlers.PrayerRequest.print - route "s/view" (Handlers.PrayerRequest.view None) - routef "s/view/%s" (Some >> Handlers.PrayerRequest.view) - routef "/%O/edit" Handlers.PrayerRequest.edit - routef "/%O/expire" Handlers.PrayerRequest.expire - routef "/%O/restore" Handlers.PrayerRequest.restore - ] - subRoute "/small-group" [ - route "" Handlers.SmallGroup.overview - route "s" Handlers.SmallGroup.maintain - route "/announcement" Handlers.SmallGroup.announcement - routef "/%O/edit" Handlers.SmallGroup.edit - route "/log-on" (Handlers.SmallGroup.logOn None) - routef "/log-on/%O" (Some >> Handlers.SmallGroup.logOn) - route "/logon" (redirectTo true "/small-group/log-on") - routef "/member/%O/edit" Handlers.SmallGroup.editMember - route "/members" Handlers.SmallGroup.members - route "/preferences" Handlers.SmallGroup.preferences - ] - route "/unauthorized" Handlers.Home.unauthorized - subRoute "/user" [ - route "s" Handlers.User.maintain - routef "/%O/edit" Handlers.User.edit - routef "/%O/small-groups" Handlers.User.smallGroups - route "/log-on" Handlers.User.logOn - route "/logon" (redirectTo true "/user/log-on") - route "/password" Handlers.User.password - ] - route "/" Handlers.Home.homePage - ] - POST [ - subRoute "/church" [ - routef "/%O/delete" Handlers.Church.delete - route "/save" Handlers.Church.save - ] - subRoute "/prayer-request" [ - routef "/%O/delete" Handlers.PrayerRequest.delete - route "/save" Handlers.PrayerRequest.save - ] - subRoute "/small-group" [ - route "/announcement/send" Handlers.SmallGroup.sendAnnouncement - routef "/%O/delete" Handlers.SmallGroup.delete - route "/log-on/submit" Handlers.SmallGroup.logOnSubmit - routef "/member/%O/delete" Handlers.SmallGroup.deleteMember - route "/member/save" Handlers.SmallGroup.saveMember - route "/preferences/save" Handlers.SmallGroup.savePreferences - route "/save" Handlers.SmallGroup.save - ] - subRoute "/user" [ - routef "/%O/delete" Handlers.User.delete - route "/edit/save" Handlers.User.save - route "/log-on" Handlers.User.doLogOn - route "/password/change" Handlers.User.changePassword - route "/small-groups/save" Handlers.User.saveGroups + POST [ + subRoute "/church" [ + routef "/%O/delete" Handlers.Church.delete + route "/save" Handlers.Church.save + ] + subRoute "/prayer-request" [ + routef "/%O/delete" Handlers.PrayerRequest.delete + route "/save" Handlers.PrayerRequest.save + ] + subRoute "/small-group" [ + route "/announcement/send" Handlers.SmallGroup.sendAnnouncement + routef "/%O/delete" Handlers.SmallGroup.delete + route "/log-on/submit" Handlers.SmallGroup.logOnSubmit + routef "/member/%O/delete" Handlers.SmallGroup.deleteMember + route "/member/save" Handlers.SmallGroup.saveMember + route "/preferences/save" Handlers.SmallGroup.savePreferences + route "/save" Handlers.SmallGroup.save + ] + subRoute "/user" [ + routef "/%O/delete" Handlers.User.delete + route "/edit/save" Handlers.User.save + route "/log-on" Handlers.User.doLogOn + route "/password/change" Handlers.User.changePassword + route "/small-groups/save" Handlers.User.saveGroups + ] ] ] + // Temp redirect to new URLs + route "/" (redirectTo false "/web/") ] let errorHandler (ex : exn) (logger : ILogger) = @@ -145,7 +151,7 @@ module Configure = /// Configure logging let logging (log : ILoggingBuilder) = - let env = log.Services.BuildServiceProvider().GetService () + let env = log.Services.BuildServiceProvider().GetService () match env.IsDevelopment () with | true -> log | false -> log.AddFilter (fun l -> l > LogLevel.Information) @@ -153,8 +159,7 @@ module Configure = |> ignore let app (app : IApplicationBuilder) = - let env = app.ApplicationServices.GetRequiredService() - let log = app.ApplicationServices.GetRequiredService() + let env = app.ApplicationServices.GetRequiredService() (match env.IsDevelopment () with | true -> app.UseDeveloperExceptionPage () diff --git a/src/PrayerTracker/Church.fs b/src/PrayerTracker/Church.fs index 55520c3..f1694f4 100644 --- a/src/PrayerTracker/Church.fs +++ b/src/PrayerTracker/Church.fs @@ -36,7 +36,7 @@ let delete churchId : HttpHandler = addInfo ctx s.["The church {0} and its {1} small groups (with {2} prayer request(s)) were deleted successfully; revoked access from {3} user(s)", ch.name, stats.smallGroups, stats.prayerRequests, stats.users] - return! redirectTo false "/churches" next ctx + return! redirectTo false "/web/churches" next ctx | None -> return! fourOhFour next ctx } @@ -71,17 +71,14 @@ let maintain : HttpHandler = requireAccess [ Admin ] >=> fun next ctx -> let startTicks = DateTime.Now.Ticks + let await = Async.AwaitTask >> Async.RunSynchronously let db = ctx.dbContext () task { let! churches = db.AllChurches () - let! stats = - churches - |> Seq.ofList - |> Seq.map (fun c -> findStats db c.churchId) - |> Task.WhenAll + let stats = churches |> List.map (fun c -> await (findStats db c.churchId)) return! viewInfo ctx startTicks - |> Views.Church.maintain churches (stats |> Map.ofArray) ctx + |> Views.Church.maintain churches (stats |> Map.ofList) ctx |> renderHtml next ctx } @@ -108,7 +105,7 @@ let save : HttpHandler = let s = Views.I18N.localizer.Force () let act = s.[match m.isNew () with true -> "Added" | _ -> "Updated"].Value.ToLower () addInfo ctx s.["Successfully {0} church “{1}”", act, m.name] - return! redirectTo false "/churches" next ctx + return! redirectTo false "/web/churches" next ctx | None -> return! fourOhFour next ctx | Error e -> return! bindError e next ctx } diff --git a/src/PrayerTracker/CommonFunctions.fs b/src/PrayerTracker/CommonFunctions.fs index 9ff8460..efb3ea0 100644 --- a/src/PrayerTracker/CommonFunctions.fs +++ b/src/PrayerTracker/CommonFunctions.fs @@ -44,12 +44,12 @@ let appVersion = sprintf "v%A" v #else seq { - yield sprintf "v%d" v.Major + sprintf "v%d" v.Major match v.Minor with - | 0 -> match v.Build with 0 -> () | _ -> yield sprintf ".0.%d" v.Build + | 0 -> match v.Build with 0 -> () | _ -> sprintf ".0.%d" v.Build | _ -> - yield sprintf ".%d" v.Minor - match v.Build with 0 -> () | _ -> yield sprintf ".%d" v.Build + sprintf ".%d" v.Minor + match v.Build with 0 -> () | _ -> sprintf ".%d" v.Build } |> String.concat "" #endif @@ -256,17 +256,17 @@ let requireAccess level : HttpHandler = | false -> let s = Views.I18N.localizer.Force () addError ctx s.["You are not authorized to view the requested page."] - return! redirectTo false "/unauthorized" next ctx + return! redirectTo false "/web/unauthorized" next ctx | _ when level |> List.contains User -> // Redirect to the user log on page ctx.Session.SetString (Key.Session.redirectUrl, ctx.Request.GetEncodedUrl ()) - return! redirectTo false "/user/log-on" next ctx + return! redirectTo false "/web/user/log-on" next ctx | _ when level |> List.contains Group -> // Redirect to the small group log on page ctx.Session.SetString (Key.Session.redirectUrl, ctx.Request.GetEncodedUrl ()) - return! redirectTo false "/small-group/log-on" next ctx + return! redirectTo false "/web/small-group/log-on" next ctx | _ -> let s = Views.I18N.localizer.Force () addError ctx s.["You are not authorized to view the requested page."] - return! redirectTo false "/unauthorized" next ctx + return! redirectTo false "/web/unauthorized" next ctx } diff --git a/src/PrayerTracker/Home.fs b/src/PrayerTracker/Home.fs index 0becb22..804f199 100644 --- a/src/PrayerTracker/Home.fs +++ b/src/PrayerTracker/Home.fs @@ -47,7 +47,7 @@ let language culture : HttpHandler = CookieRequestCultureProvider.MakeCookieValue (RequestCulture c), CookieOptions (Expires = Nullable (DateTimeOffset (DateTime.Now.AddYears 1)))) | _ -> () - let url = match string ctx.Request.Headers.["Referer"] with null | "" -> "/" | r -> r + let url = match string ctx.Request.Headers.["Referer"] with null | "" -> "/web/" | r -> r redirectTo false url next ctx @@ -78,7 +78,7 @@ let logOff : HttpHandler = Key.Cookie.logOffCookies |> List.iter ctx.Response.Cookies.Delete let s = Views.I18N.localizer.Force () addHtmlInfo ctx s.["Log Off Successful • Have a nice day!"] - redirectTo false "/" next ctx + redirectTo false "/web/" next ctx /// GET /unauthorized diff --git a/src/PrayerTracker/PrayerRequest.fs b/src/PrayerTracker/PrayerRequest.fs index af7fe2f..ec0ba5c 100644 --- a/src/PrayerTracker/PrayerRequest.fs +++ b/src/PrayerTracker/PrayerRequest.fs @@ -19,7 +19,7 @@ let private findRequest (ctx : HttpContext) reqId = | Some _ -> let s = Views.I18N.localizer.Force () addError ctx s.["The prayer request you tried to access is not assigned to your group"] - return Error (redirectTo false "/unauthorized") + return Error (redirectTo false "/web/unauthorized") | None -> return Error fourOhFour } @@ -121,7 +121,7 @@ let delete reqId : HttpHandler = db.PrayerRequests.Remove r |> ignore let! _ = db.SaveChangesAsync () addInfo ctx s.["The prayer request was deleted successfully"] - return! redirectTo false "/prayer-requests" next ctx + return! redirectTo false "/web/prayer-requests" next ctx | Error e -> return! e next ctx } @@ -139,7 +139,7 @@ let expire reqId : HttpHandler = db.UpdateEntry { r with expiration = Forced } let! _ = db.SaveChangesAsync () addInfo ctx s.["Successfully {0} prayer request", s.["Expired"].Value.ToLower ()] - return! redirectTo false "/prayer-requests" next ctx + return! redirectTo false "/web/prayer-requests" next ctx | Error e -> return! e next ctx } @@ -170,7 +170,7 @@ let list groupId : HttpHandler = | Some _ -> let s = Views.I18N.localizer.Force () addError ctx s.["The request list for the group you tried to view is not public."] - return! redirectTo false "/unauthorized" next ctx + return! redirectTo false "/web/unauthorized" next ctx | None -> return! fourOhFour next ctx } @@ -250,7 +250,7 @@ let restore reqId : HttpHandler = db.UpdateEntry { r with expiration = Automatic; updatedDate = DateTime.Now } let! _ = db.SaveChangesAsync () addInfo ctx s.["Successfully {0} prayer request", s.["Restored"].Value.ToLower ()] - return! redirectTo false "/prayer-requests" next ctx + return! redirectTo false "/web/prayer-requests" next ctx | Error e -> return! e next ctx } @@ -295,7 +295,7 @@ let save : HttpHandler = let s = Views.I18N.localizer.Force () let act = match m.isNew () with true -> "Added" | false -> "Updated" addInfo ctx s.["Successfully {0} prayer request", s.[act].Value.ToLower ()] - return! redirectTo false "/prayer-requests" next ctx + return! redirectTo false "/web/prayer-requests" next ctx | None -> return! fourOhFour next ctx | Error e -> return! bindError e next ctx } diff --git a/src/PrayerTracker/PrayerTracker.fsproj b/src/PrayerTracker/PrayerTracker.fsproj index 085722f..bddb697 100644 --- a/src/PrayerTracker/PrayerTracker.fsproj +++ b/src/PrayerTracker/PrayerTracker.fsproj @@ -1,12 +1,7 @@  - netcoreapp2.2 - 7.3.2.0 - 7.3.2.0 - - Bit Badger Solutions - 7.3.2 + netcoreapp3.0 @@ -28,11 +23,10 @@ - + - - - + + @@ -41,7 +35,7 @@ - + diff --git a/src/PrayerTracker/SmallGroup.fs b/src/PrayerTracker/SmallGroup.fs index 2384f48..5663bc2 100644 --- a/src/PrayerTracker/SmallGroup.fs +++ b/src/PrayerTracker/SmallGroup.fs @@ -47,7 +47,7 @@ let delete groupId : HttpHandler = addInfo ctx s.["The group {0} and its {1} prayer request(s) were deleted successfully; revoked access from {2} user(s)", g.name, reqs, usrs] - return! redirectTo false "/small-groups" next ctx + return! redirectTo false "/web/small-groups" next ctx | None -> return! fourOhFour next ctx } @@ -66,7 +66,7 @@ let deleteMember memberId : HttpHandler = db.RemoveEntry m let! _ = db.SaveChangesAsync () addHtmlInfo ctx s.["The group member “{0}” was deleted successfully", m.memberName] - return! redirectTo false "/small-group/members" next ctx + return! redirectTo false "/web/small-group/members" next ctx | Some _ | None -> return! fourOhFour next ctx } @@ -160,10 +160,10 @@ let logOnSubmit : HttpHandler = | Some x when x -> (setGroupCookie ctx << Utils.sha1Hash) m.password | _ -> () addInfo ctx s.["Log On Successful • Welcome to {0}", s.["PrayerTracker"]] - return! redirectTo false "/prayer-requests/view" next ctx + return! redirectTo false "/web/prayer-requests/view" next ctx | None -> addError ctx s.["Password incorrect - login unsuccessful"] - return! redirectTo false (sprintf "/small-group/log-on/%s" (flatGuid m.smallGroupId)) next ctx + return! redirectTo false (sprintf "/web/small-group/log-on/%s" (flatGuid m.smallGroupId)) next ctx | Error e -> return! bindError e next ctx } @@ -270,7 +270,7 @@ let save : HttpHandler = let! _ = db.SaveChangesAsync () let act = s.[match m.isNew () with true -> "Added" | false -> "Updated"].Value.ToLower () addHtmlInfo ctx s.["Successfully {0} group “{1}”", act, m.name] - return! redirectTo false "/small-groups" next ctx + return! redirectTo false "/web/small-groups" next ctx | None -> return! fourOhFour next ctx | Error e -> return! bindError e next ctx } @@ -309,7 +309,7 @@ let saveMember : HttpHandler = let s = Views.I18N.localizer.Force () let act = s.[match m.isNew () with true -> "Added" | false -> "Updated"].Value.ToLower () addInfo ctx s.["Successfully {0} group member", act] - return! redirectTo false "/small-group/members" next ctx + return! redirectTo false "/web/small-group/members" next ctx | Some _ | None -> return! fourOhFour next ctx | Error e -> return! bindError e next ctx @@ -339,7 +339,7 @@ let savePreferences : HttpHandler = ctx.Session.SetSmallGroup <| Some { g with preferences = prefs } let s = Views.I18N.localizer.Force () addInfo ctx s.["Group preferences updated successfully"] - return! redirectTo false "/small-group/preferences" next ctx + return! redirectTo false "/web/small-group/preferences" next ctx | None -> return! fourOhFour next ctx | Error e -> return! bindError e next ctx } diff --git a/src/PrayerTracker/User.fs b/src/PrayerTracker/User.fs index f1bec2d..cb1bd9a 100644 --- a/src/PrayerTracker/User.fs +++ b/src/PrayerTracker/User.fs @@ -84,13 +84,13 @@ let changePassword : HttpHandler = | _ -> () addInfo ctx s.["Your password was changed successfully"] | None -> addError ctx s.["Unable to change password"] - return! redirectTo false "/" next ctx + return! redirectTo false "/web/" next ctx | Some _ -> addError ctx s.["The new passwords did not match - your password was NOT changed"] - return! redirectTo false "/user/password" next ctx + return! redirectTo false "/web/user/password" next ctx | None -> addError ctx s.["The old password was incorrect - your password was NOT changed"] - return! redirectTo false "/user/password" next ctx + return! redirectTo false "/web/user/password" next ctx | Error e -> return! bindError e next ctx } @@ -109,7 +109,7 @@ let delete userId : HttpHandler = let! _ = db.SaveChangesAsync () let s = Views.I18N.localizer.Force () addInfo ctx s.["Successfully deleted user {0}", u.fullName] - return! redirectTo false "/users" next ctx + return! redirectTo false "/web/users" next ctx | _ -> return! fourOhFour next ctx } @@ -135,8 +135,8 @@ let doLogOn : HttpHandler = match m.rememberMe with Some x when x -> setUserCookie ctx pwHash | _ -> () addHtmlInfo ctx s.["Log On Successful • Welcome to {0}", s.["PrayerTracker"]] match m.redirectUrl with - | None -> "/small-group" - | Some x when x = "" -> "/small-group" + | None -> "/web/small-group" + | Some x when x = "" -> "/web/small-group" | Some x -> x | _ -> let grpName = match grp with Some g -> g.name | _ -> "N/A" @@ -156,7 +156,7 @@ let doLogOn : HttpHandler = |> (HtmlString >> Some) } |> addUserMessage ctx - "/user/log-on" + "/web/user/log-on" return! redirectTo false nextUrl next ctx | Error e -> return! bindError e next ctx } @@ -271,10 +271,10 @@ let save : HttpHandler = |> Some } |> addUserMessage ctx - return! redirectTo false (sprintf "/user/%s/small-groups" (flatGuid u.userId)) next ctx + return! redirectTo false (sprintf "/web/user/%s/small-groups" (flatGuid u.userId)) next ctx | false -> addInfo ctx s.["Successfully {0} user", s.["Updated"].Value.ToLower ()] - return! redirectTo false "/users" next ctx + return! redirectTo false "/web/users" next ctx | None -> return! fourOhFour next ctx | Error e -> return! bindError e next ctx } @@ -293,7 +293,7 @@ let saveGroups : HttpHandler = match Seq.length m.smallGroups with | 0 -> addError ctx s.["You must select at least one group to assign"] - return! redirectTo false (sprintf "/user/%s/small-groups" (flatGuid m.userId)) next ctx + return! redirectTo false (sprintf "/web/user/%s/small-groups" (flatGuid m.userId)) next ctx | _ -> let db = ctx.dbContext () let! user = db.TryUserByIdWithGroups m.userId @@ -314,7 +314,7 @@ let saveGroups : HttpHandler = |> List.iter db.AddEntry let! _ = db.SaveChangesAsync () addInfo ctx s.["Successfully updated group permissions for {0}", m.userName] - return! redirectTo false "/users" next ctx + return! redirectTo false "/web/users" next ctx | _ -> return! fourOhFour next ctx | Error e -> return! bindError e next ctx }