[] module PrayerTracker.Utils open System.Net open System.Security.Cryptography open System.Text open System.Text.RegularExpressions open System /// Hash a string with a SHA1 hash let sha1Hash (x : string) = use alg = SHA1.Create () alg.ComputeHash (Encoding.ASCII.GetBytes x) |> Seq.map (fun chr -> chr.ToString "x2") |> String.concat "" /// Hash a string using 1,024 rounds of PBKDF2 and a salt let pbkdf2Hash (salt : Guid) (x : string) = use alg = new Rfc2898DeriveBytes (x, Encoding.UTF8.GetBytes (salt.ToString "N"), 1024) (alg.GetBytes >> Convert.ToBase64String) 64 /// String helper functions module String = /// string.Trim() let trim (str: string) = str.Trim () /// 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 -> [ haystack.[0..idx - 1] replacement haystack.[idx + needle.Length..] ] |> String.concat "" /// Strip HTML tags from the given string // Adapted from http://www.dijksterhuis.org/safely-cleaning-html-with-strip_tags-in-csharp/ let stripTags allowedTags input = let stripHtmlExp = Regex @"(<\/?[^>]+>)" let mutable output = input for tag in stripHtmlExp.Matches input do let htmlTag = tag.Value.ToLower () let isAllowed = allowedTags |> List.fold (fun acc t -> acc || htmlTag.IndexOf (sprintf "<%s>" t) = 0 || htmlTag.IndexOf (sprintf "<%s " t) = 0 || htmlTag.IndexOf (sprintf " () | false -> output <- String.replaceFirst tag.Value "" output output /// Wrap a string at the specified number of characters let wordWrap charPerLine (input : string) = match input.Length with | len when len <= charPerLine -> input | _ -> seq { for line in input.Replace("\r", "").Split '\n' do let mutable remaining = line match remaining.Length with | 0 -> () | _ -> while charPerLine < remaining.Length do match charPerLine + 1 < remaining.Length && remaining.[charPerLine] = ' ' with | true -> // Line length is followed by a space; return [charPerLine] as a line yield remaining.[0..charPerLine - 1] remaining <- remaining.[charPerLine + 1..] | false -> match remaining.[0..charPerLine - 1].LastIndexOf ' ' with | -1 -> // No whitespace; just break it at [characters] yield remaining.[0..charPerLine - 1] remaining <- remaining.[charPerLine..] | spaceIdx -> // Break on the last space in the line yield remaining.[0..spaceIdx - 1] remaining <- remaining.[spaceIdx + 1..] // Leftovers - yum! match remaining.Length with 0 -> () | _ -> yield remaining } |> Seq.fold (fun (acc : StringBuilder) line -> acc.AppendFormat ("{0}\n", line)) (StringBuilder ()) |> string /// Modify the text returned by CKEditor into the format we need for request and announcement text let ckEditorToText (text : string) = let trim (str : string) = str.Trim () [ "\n\t", "" " ", " " " ", "  " "

", "

" "

", "" "

", "" ] |> List.fold (fun (txt : string) (x, y) -> String.replace x y txt) text |> trim /// Convert an HTML piece of text to plain text let htmlToPlainText html = match html with | null | "" -> "" | _ -> html.Trim () |> stripTags [ "br" ] |> String.replace "
" "\n" |> String.replace "
" "\n" |> WebUtility.HtmlDecode |> String.replace "\u00a0" " " /// Get the second portion of a tuple as a string let sndAsString x = (snd >> string) x /// Make a URL with query string parameters let makeUrl (url : string) (qs : (string * string) list) = let queryString = qs |> List.fold (fun (acc : StringBuilder) (key, value) -> acc.Append(key).Append("=").Append(WebUtility.UrlEncode value).Append "&") (StringBuilder ()) match queryString.Length with | 0 -> url | _ -> queryString.Insert(0, "?").Insert(0, url).Remove(queryString.Length - 1, 1).ToString () /// "Magic string" repository [] module Key = /// This contains constants for session-stored objects within PrayerTracker module Session = /// The currently logged-on small group let currentGroup = "CurrentGroup" /// The currently logged-on user let currentUser = "CurrentUser" /// User messages to be displayed the next time a page is sent let userMessages = "UserMessages" /// The URL to which the user should be redirected once they have logged in let redirectUrl = "RedirectUrl" /// Names and value names for use with cookies module Cookie = /// The name of the user cookie let user = "LoggedInUser" /// The name of the class cookie let group = "LoggedInClass" /// The name of the culture cookie let culture = "CurrentCulture" /// The name of the idle timeout cookie let timeout = "TimeoutCookie" /// The cookies that should be cleared when a user or group logs off let logOffCookies = [ user; group; timeout ] /// Enumerated values for small group request list visibility (derived from preferences, used in UI) module RequestVisibility = /// Requests are publicly accessible [] let ``public`` = 1 /// The small group members can enter a password to view the request list [] let passwordProtected = 2 /// No one can see the requests for a small group except its administrators ("User" access level) [] let ``private`` = 3 /// Links for help locations module Help = /// Help link for small group preference edit page let groupPreferences = "small-group/preferences" /// Help link for send announcement page let sendAnnouncement = "small-group/announcement" /// Help link for maintain group members page let maintainGroupMembers = "small-group/members" /// Help link for request edit page let editRequest = "requests/edit" /// Help link for maintain requests page let maintainRequests = "requests/maintain" /// Help link for view request list page let viewRequestList = "requests/view" /// Help link for user and class login pages let logOn = "user/log-on" /// Help link for user password change page let changePassword = "user/password" /// Create a full link for a help page let fullLink lang url = sprintf "https://docs.prayer.bitbadger.solutions/%s/%s.html" lang url /// This class serves as a common anchor for resources type Common () = do ()