diff --git a/Guide/helpful-tips.markdown b/Guide/helpful-tips.markdown index dc97707ac..34c9d252b 100644 --- a/Guide/helpful-tips.markdown +++ b/Guide/helpful-tips.markdown @@ -127,7 +127,7 @@ In general the `\case ...` can be expanded to `\value -> case value of ...`. ### The Pipe operator [`|>`](https://ihp.digitallyinduced.com/api-docs/IHP-HaskellSupport.html#v:-124--62-) -In IHP code bases you find lot's of usage of the [`|>`](https://ihp.digitallyinduced.com/api-docs/IHP-HaskellSupport.html#v:-124--62-) operator. We usually call it the `pipe operator`. +In IHP code bases you find lots of usage of the [`|>`](https://ihp.digitallyinduced.com/api-docs/IHP-HaskellSupport.html#v:-124--62-) operator. We usually call it the `pipe operator`. The operator allows you to write code like this: @@ -147,7 +147,7 @@ validateField #firstname nonEmpty ( In general: ```haskell -function arg1 arg2 object +function arg1 arg2 object = object |> function arg1 arg2 ``` diff --git a/Guide/ihp-pro.markdown b/Guide/ihp-pro.markdown index c3f5e6d22..a1b5ec49b 100644 --- a/Guide/ihp-pro.markdown +++ b/Guide/ihp-pro.markdown @@ -30,7 +30,7 @@ With bugs we have the same story. Instead of just a GitHub issue, it’s now a c Here’s the big plan in a short form: -1. Introduce a IHP Pro subscription +1. Introduce an IHP Pro subscription 2. Use the funding by our new customers to make IHP & the ecosystem a lot better 3. Become the well-known best framework for building web applications @@ -46,4 +46,3 @@ We recommend you try out these features after upgrading to pro: - [Google Login](oauth.html#introduction) - [Email Confirmation](authentication.html#email-confirmation) - diff --git a/Guide/mail.markdown b/Guide/mail.markdown index b37d240a9..5acfa2c22 100644 --- a/Guide/mail.markdown +++ b/Guide/mail.markdown @@ -38,7 +38,7 @@ instance BuildMail ConfirmationMail where |] ``` -### Changing Subject +### Changing the Subject Let's first change the subject of our mail from `Subject` to something more useful: @@ -66,9 +66,17 @@ to ConfirmationMail { .. } = Address { addressName = Just (get #name user), addr The email sender is set to `hi@example.com` by default. Usually, you want to use your domain here. For this example, we will stick with the `hi@example.com` for now. +### Changing the Reply-To address + +By default the "Reply" button in an email programs creates a reply to the From address. You can change that behavior by setting the `Reply-To` header to another target email address: + +```haskell +replyTo ConfirmationMail { .. } = Just Address { addessName = Just "Support", addressEmail = "support@example.com" } +``` + ### Email Content -Last we need to change the email text a little bit. The mail supports HSX so this is similar to writing a IHP view: +Last we need to change the email text a little bit. The mail supports HSX so this is similar to writing an IHP view: ```haskell html ConfirmationMail { .. } = [hsx| @@ -87,6 +95,21 @@ action MyAction = do sendMail ConfirmationMail { user } ``` +## Custom Headers + +If you need to send specific mail headers you can do so as well: + +```haskell +headers ConfirmationMail { .. } = + [ ("X-Mailer", "mail4j 2.17.0") + , ("In-Reply-To", "<123456@list.example.com>") + ] +``` + +Implementation detail: IHP first adds headers set by itself (like `Subject` and the optional `Reply-To`), then headers provided via `headers`. If you don't want to use the `replyTo` helper from above it's absolutely fine to add the `Reply-To` header manually. + + + ## Mail Servers By default, IHP uses your local `sendmail` to send out the email. IHP also supports sending mail via AWS Simple Email Service (SES), SendGrid (via Azure or directly) or via any standard SMTP server. @@ -107,13 +130,14 @@ config = do option $ SMTP { host = "smtp.myisp.com" , port = 2525 - , credentials = Nothing -- or Just ("myusername","hunter2") + , credentials = Nothing -- or: Just ("myusername","hunter2") + , encryption = TLS -- <-- other options: `Unencrypted` or `STARTTLS` } ``` ### Local (For Debugging) -A convinient way to see sent mails is to use a local mail testing such as [MailHog](https://github.com/mailhog/MailHog). This service will catch all outgoing emails, and show their HTML to you - which is handy while developing. +A convinient way to see sent mails is to use a local mail testing such as [MailHog](https://github.com/mailhog/MailHog). This service will catch all outgoing emails, and show their HTML to you - which is handy while developing. 1. Make sure `sendmail` is locally installed and configured. 2. Install MailHog. @@ -133,6 +157,7 @@ config = do { host = "127.0.1.1" , port = 1025 , credentials = Nothing + , encryption = Unencrypted } ``` @@ -196,4 +221,25 @@ instance BuildMail ConfirmationMail where ## Plain Text Emails -TODO +Every email should have a plain text version for people with reasonable mail clients. If you don't specify one and only set the HTML content via `html` (see above), then IHP automatically creates a plain text version from you by stripping away all HTML tags. This is suboptimal. + +The better option is to manually provide a useful plain text version of your emails: + +```haskell +import NeatInterpolation + +text ConfirmationMail { .. } = cs [trimming| + Hey ${userName}, + + Thanks for signing up! Please confirm your account by following this link: + https://.... +|] + where + userName = get #name user +``` + +Note a few differences to the `html` version here: + +- We use `[trimming| ... |]` instead of `[hsx| ... |]` so we can't use HSX's inline Haskell like `{get #userName user}` but have to live with simple substitution. Note the dollar sign in front of these substitutions: `${userName}`. +- The `[trimming||]` quasiquoter takes care of removing the whitespace that our indentations introduced, which we don't want in the actual emails. +- We use `cs` to convert the `[Char]` to the required `Text` type. diff --git a/Guide/recipes.markdown b/Guide/recipes.markdown index 60f873966..9782e9c59 100644 --- a/Guide/recipes.markdown +++ b/Guide/recipes.markdown @@ -293,7 +293,7 @@ To confirm before a link is fired add an `onclick` to the link. To generate a random string which can be used as a secure token or hash use [`generateAuthenticationToken`](https://hackage.haskell.org/package/wreq): ```haskell -import IHP.AuthSupport.Authentication -- Not needed if you're inside a IHP controller +import IHP.AuthSupport.Authentication -- Not needed if you're inside an IHP controller do token <- generateAuthenticationToken @@ -346,7 +346,7 @@ import Network.HTTP.Types.Header (hContentType) import Network.HTTP.Types (status404) import Network.Wai (responseLBS) -customNotFoundResponse :: (?context :: ControllerContext) => IO () +customNotFoundResponse :: (?context :: ControllerContext) => IO () customNotFoundResponse = do page <- LBS.readFile "static/404.html" respondAndExit $ responseLBS status404 [(hContentType, "text/html")] page @@ -356,7 +356,7 @@ Now you can use your `customNotFoundResponse`: ```haskell action WelcomeAction = do -post <- fetchOneOrNothing ("30a73014-101e-4269-be91-be6c019de289" :: Id Post) +post <- fetchOneOrNothing ("30a73014-101e-4269-be91-be6c019de289" :: Id Post) case post of Nothing -> customNotFoundResponse -- Database record disappeared !!! Just post -> render ShowView { .. } diff --git a/IHP/Mail.hs b/IHP/Mail.hs index eeebf4e4f..0d9069a08 100644 --- a/IHP/Mail.hs +++ b/IHP/Mail.hs @@ -119,11 +119,11 @@ class BuildMail mail where bcc :: (?context :: context, ConfigProvider context) => mail -> [Address] bcc mail = [] - -- | Custom headers, excluding `from`, `to`, `cc`, and `bcc` + -- | Custom headers, excluding @from@, @to@, @cc@, @bcc@, @subject@, and @reply-to@ -- - -- __Example:__ Add a custom X-Sender header + -- __Example:__ Add a custom X-Mailer header -- - -- > headers CreateAccountMail { .. } = [("X-Sender", "mail4j 2.17.0")] + -- > headers CreateAccountMail { .. } = [("X-Mailer", "mail4j 2.17.0")] -- headers :: (?context :: context, ConfigProvider context) => mail -> Headers headers mail = [] diff --git a/README.md b/README.md index 6e6073ac0..58d18fa1b 100644 --- a/README.md +++ b/README.md @@ -67,7 +67,7 @@ Auto Refresh can be enabled for IHP views with a single line of code. [Watch it in action!](https://twitter.com/digitallyinduce/status/1312017800223956992) **Longterm Roadmap** -Lot's of frameworks are already gone a year after launch. Especially in the fast moving JS world. But don't worry about IHP. We have been using it at digitally induced since 2017. It's actively used by us and our friends and partners. Even without external contributors we will build new features and do periodic maintenance releases in the future. We have big plans for IHP and as a profitable and independent software company we have the ability to actually execute them over the longterm. +Lots of frameworks are already gone a year after launch. Especially in the fast moving JS world. But don't worry about IHP. We have been using it at digitally induced since 2017. It's actively used by us and our friends and partners. Even without external contributors we will build new features and do periodic maintenance releases in the future. We have big plans for IHP and as a profitable and independent software company we have the ability to actually execute them over the longterm. ## Reviews