diff --git a/Guide/assets.markdown b/Guide/assets.markdown
index 1dadf6b5a..408532962 100644
--- a/Guide/assets.markdown
+++ b/Guide/assets.markdown
@@ -15,7 +15,7 @@ To avoid this problem, web applications typically append a hash to the url of yo
```
-IHP provides an `assetPath` view helper to automatically add these hashes:
+IHP provides an [`assetPath`](https://ihp.digitallyinduced.com/api-docs/IHP-Assets-ViewFunctions.html#v:assetPath) view helper to automatically add these hashes:
```haskell
[hsx|
diff --git a/Guide/authentication.markdown b/Guide/authentication.markdown
index 43e14aaab..95fee1d5a 100644
--- a/Guide/authentication.markdown
+++ b/Guide/authentication.markdown
@@ -54,7 +54,7 @@ instance HasNewSessionUrl User where
type instance CurrentUserRecord = User
```
-The `instance HasNewSessionUrl User` tells the auth module where to redirect a user in case the user tries to access a action that requires login. The definition of `CurrentUserRecord` tells the auth system to use our `User` type within the login system.
+The `instance HasNewSessionUrl User` tells the auth module where to redirect a user in case the user tries to access a action that requires login. The definition of [`CurrentUserRecord`](https://ihp.digitallyinduced.com/api-docs/IHP-LoginSupport-Types.html#t:CurrentUserRecord) tells the auth system to use our `User` type within the login system.
We also need to add the type definitions for the `SessionsController`:
@@ -134,7 +134,7 @@ import IHP.LoginSupport.Middleware
import Web.Controller.Sessions
```
-We then need to mount our session controller by adding `parseRoute @SessionController`:
+We then need to mount our session controller by adding [`parseRoute @SessionController`](https://ihp.digitallyinduced.com/api-docs/IHP-RouterSupport.html#v:parseRoute):
```haskell
instance FrontController WebApplication where
@@ -154,7 +154,7 @@ instance InitControllerContext WebApplication where
initAutoRefresh
```
-We need to extend this function with a `initAuthentication @User` like this:
+We need to extend this function with a [`initAuthentication @User`](https://ihp.digitallyinduced.com/api-docs/IHP-LoginSupport-Middleware.html#v:initAuthentication) like this:
```haskell
instance InitControllerContext WebApplication where
@@ -164,7 +164,7 @@ instance InitControllerContext WebApplication where
initAuthentication @User
```
-This will fetch the user from the database when a `userId` is given in the session. The fetched user record is saved to the special `?context` variable and is used by all the helper functions like `currentUser`.
+This will fetch the user from the database when a `userId` is given in the session. The fetched user record is saved to the special `?context` variable and is used by all the helper functions like [`currentUser`](https://ihp.digitallyinduced.com/api-docs/IHP-LoginSupport-Helper-Controller.html#v:currentUser).
## Trying out the login
@@ -177,7 +177,7 @@ After you have completed the above steps, you can open the login at `/NewSession
## Accessing the current user
-Inside your actions you can then use `currentUser` to get access to the current logged in user:
+Inside your actions you can then use [`currentUser`](https://ihp.digitallyinduced.com/api-docs/IHP-LoginSupport-Helper-Controller.html#v:currentUser) to get access to the current logged in user:
```haskell
action MyAction = do
@@ -185,9 +185,9 @@ action MyAction = do
renderPlain text
```
-In case the user is logged out, an exception will be thrown when accessing `currentUser` and the browser will automatically be redirected to the `NewSessionAction`.
+In case the user is logged out, an exception will be thrown when accessing [`currentUser`](https://ihp.digitallyinduced.com/api-docs/IHP-LoginSupport-Helper-Controller.html#v:currentUser) and the browser will automatically be redirected to the `NewSessionAction`.
-You can use `currentUserOrNothing` to manually deal with the not-logged-in case:
+You can use [`currentUserOrNothing`](https://ihp.digitallyinduced.com/api-docs/IHP-LoginSupport-Helper-Controller.html#v:currentUserOrNothing) to manually deal with the not-logged-in case:
```haskell
action MyAction = do
@@ -198,9 +198,9 @@ action MyAction = do
Nothing -> renderPlain "Please login first"
```
-Additionally you can use `currentUserId` as a shortcut for `currentUser |> get #id`.
+Additionally you can use [`currentUserId`](https://ihp.digitallyinduced.com/api-docs/IHP-LoginSupport-Helper-Controller.html#v:currentUserId) as a shortcut for `currentUser |> get #id`.
-You can also access the user using `currentUser` inside your views:
+You can also access the user using [`currentUser`](https://ihp.digitallyinduced.com/api-docs/IHP-LoginSupport-Helper-Controller.html#v:currentUser) inside your views:
```html
[hsx|
@@ -210,7 +210,7 @@ You can also access the user using `currentUser` inside your views:
## Performing actions on login
-The sessioncontroller has a convenient `beforeLogin` which is run on login after the user is authenticated, but before the target page is rendered. This can be useful for updating last login time, number of logins or aborting the login when the user is blocked. Add code for it in your `Web/Controller/Sessions.hs`. To update number of logins (requires `logins` integer field in `Users` table):
+The sessioncontroller has a convenient [`beforeLogin`](https://ihp.digitallyinduced.com/api-docs/IHP-AuthSupport-Controller-Sessions.html#v:beforeLogin) which is run on login after the user is authenticated, but before the target page is rendered. This can be useful for updating last login time, number of logins or aborting the login when the user is blocked. Add code for it in your `Web/Controller/Sessions.hs`. To update number of logins (requires `logins` integer field in `Users` table):
```haskell
instance Sessions.SessionsControllerConfig User where
@@ -223,7 +223,7 @@ updateLoginHistory user = do
pure ()
```
-To block login (requires `isConfirmed`boolean field in `Users` table):
+To block login (requires `isConfirmed` boolean field in `Users` table):
```haskell
instance Sessions.SessionsControllerConfig User where
diff --git a/Guide/authorization.markdown b/Guide/authorization.markdown
index 2554003a6..66ab50652 100644
--- a/Guide/authorization.markdown
+++ b/Guide/authorization.markdown
@@ -6,7 +6,7 @@
## Restricting an action to logged-in users
-To restrict an action to a logged-in user, use `ensureIsUser`:
+To restrict an action to a logged-in user, use [`ensureIsUser`](https://ihp.digitallyinduced.com/api-docs/IHP-LoginSupport-Helper-Controller.html#v:ensureIsUser):
```haskell
action PostsAction = do
@@ -17,7 +17,7 @@ action PostsAction = do
When someone is trying to access the `PostsAction` but is not logged-in, the browser will be redirected to the login page. After the login succeeded, the user will be redirected back to the `PostsAction`.
-It's common to restrict all actions inside a controller to logged-in users only. Place the `ensureIsUser` inside the `beforeAction` hook to automatically apply it to all actions:
+It's common to restrict all actions inside a controller to logged-in users only. Place the [`ensureIsUser`](https://ihp.digitallyinduced.com/api-docs/IHP-LoginSupport-Helper-Controller.html#v:ensureIsUser) inside the [`beforeAction`](https://ihp.digitallyinduced.com/api-docs/IHP-ControllerSupport.html#v:beforeAction) hook to automatically apply it to all actions:
```haskell
instance Controller PostsController where
@@ -36,7 +36,7 @@ In this case `PostsAction` and `ShowPostAction` are only accessible to logged-in
## Restricting an action to logged-in admins
-To restrict an action to a logged-in admin, use `ensureIsAdmin` instead of `ensureIsUser`. If you get
+To restrict an action to a logged-in admin, use [`ensureIsAdmin`](https://ihp.digitallyinduced.com/api-docs/IHP-ControllerSupport.html#v:beforeAction) instead of [`ensureIsUser`](https://ihp.digitallyinduced.com/api-docs/IHP-LoginSupport-Helper-Controller.html#v:ensureIsUser). If you get
```
error:
@@ -57,7 +57,7 @@ instance Controller UserController where
## Checking for Permissions
-You can use `accessDeniedUnless` to allow certain things only for specific users. For example, to restrict a `ShowPostAction` only to the user who a post belongs to, use this:
+You can use [`accessDeniedUnless`](https://ihp.digitallyinduced.com/api-docs/IHP-AuthSupport-Authorization.html#v:accessDeniedUnless) to allow certain things only for specific users. For example, to restrict a `ShowPostAction` only to the user who a post belongs to, use this:
```haskell
action ShowPostAction { postId } = do
diff --git a/Guide/auto-refresh.markdown b/Guide/auto-refresh.markdown
index ce57f5ee9..8b4ebb88c 100644
--- a/Guide/auto-refresh.markdown
+++ b/Guide/auto-refresh.markdown
@@ -18,7 +18,7 @@ Auto Refresh offers a way to re-render views of your application when the underl
It's good to have a general understanding of how IHP Auto Refresh works.
-Auto Refresh first has to be activated for an action by calling `autoRefresh`. Once activated the framework will automatically track all tables your action is using e.g. in `SELECT * FROM ...` queries. Once the action sends a response IHP will start watching for any kind of `INSERT`, `UPDATE` or `DELETE` statement to all the tables used by your action.
+Auto Refresh first has to be activated for an action by calling [`autoRefresh`](https://ihp.digitallyinduced.com/api-docs/IHP-AutoRefresh.html#v:autoRefresh). Once activated the framework will automatically track all tables your action is using e.g. in `SELECT * FROM ...` queries. Once the action sends a response IHP will start watching for any kind of `INSERT`, `UPDATE` or `DELETE` statement to all the tables used by your action.
When the page is rendered a small JavaScript function will connect back to the IHP server using a WebSocket connection.
@@ -37,7 +37,7 @@ action ShowProjectAction { projectId } = do
render ShowView { .. }
```
-To enable auto refresh we have to add `autoRefresh` in front of the `do`:
+To enable auto refresh we have to add [`autoRefresh`](https://ihp.digitallyinduced.com/api-docs/IHP-AutoRefresh.html#v:autoRefresh) in front of the `do`:
```haskell
action ShowProjectAction { projectId } = autoRefresh do
@@ -106,7 +106,7 @@ action StatsAction = autoRefresh do
pure StatsView { ..}
```
-When using this custom query with `sqlQuery`, Auto Refresh is not aware that we're reading from the `companies` table. In this case we need to help out Auto Refresh by calling `trackTableRead`:
+When using this custom query with [`sqlQuery`](https://ihp.digitallyinduced.com/api-docs/IHP-ModelSupport.html#v:sqlQuery), Auto Refresh is not aware that we're reading from the `companies` table. In this case we need to help out Auto Refresh by calling [`trackTableRead`](https://ihp.digitallyinduced.com/api-docs/IHP-ModelSupport.html#v:trackTableRead):
```haskell
@@ -118,4 +118,4 @@ action StatsAction = autoRefresh do
pure StatsView { ..}
```
-The `trackTableRead` marks the table as accessed for Auto Refresh and leads to the table being watched.
\ No newline at end of file
+The [`trackTableRead`](https://ihp.digitallyinduced.com/api-docs/IHP-ModelSupport.html#v:trackTableRead) marks the table as accessed for Auto Refresh and leads to the table being watched.
diff --git a/Guide/controller.markdown b/Guide/controller.markdown
index 1bbc84fef..56b078653 100644
--- a/Guide/controller.markdown
+++ b/Guide/controller.markdown
@@ -26,9 +26,9 @@ data PostsController
deriving (Eq, Show, Data)
```
-This defines a type `PostsController` with a data constructor `ShowPostAction { postId :: !(Id Post) }`. The argument `postId` will later be filled with the `postId` parameter of the request URL. This is done automatically by the IHP router. IHP also requires the controller to have `Eq`, `Show` and `Data` instances. Therefore we derive them here.
+This defines a type `PostsController` with a data constructor `ShowPostAction { postId :: !(Id Post) }`. The argument `postId` will later be filled with the `postId` parameter of the request URL. This is done automatically by the IHP router. IHP also requires the controller to have [`Eq`](https://ihp.digitallyinduced.com/api-docs/IHP-Prelude.html#t:Eq), [`Show`](https://ihp.digitallyinduced.com/api-docs/IHP-Prelude.html#t:Show) and [`Data`](https://ihp.digitallyinduced.com/api-docs/IHP-Prelude.html#t:Data) instances. Therefore we derive them here.
-After we have defined the "interface" for our controller, we need to implement the actual request handling logic. IHP expects to find this inside the `action` function of the [`Controller`](https://ihp.digitallyinduced.com/api-docs/IHP-ControllerSupport.html#t:Controller) instance. We can define this instance in `Web/Controller/Posts.hs`:
+After we have defined the "interface" for our controller, we need to implement the actual request handling logic. IHP expects to find this inside the [`action`](https://ihp.digitallyinduced.com/api-docs/IHP-ControllerSupport.html#v:action) function of the [`Controller`](https://ihp.digitallyinduced.com/api-docs/IHP-ControllerSupport.html#t:Controller) instance. We can define this instance in `Web/Controller/Posts.hs`:
```haskell
module Web.Controller.Posts where
@@ -39,11 +39,11 @@ instance Controller PostsController where
action ShowPostAction { postId } = renderPlain "Hello World"
```
-This implementation for `ShowPostAction` responds with a simple plain text message. The `action` implementation is usually a big pattern match over all possible actions of a controller.
+This implementation for `ShowPostAction` responds with a simple plain text message. The [`action`](https://ihp.digitallyinduced.com/api-docs/IHP-ControllerSupport.html#v:action) implementation is usually a big pattern match over all possible actions of a controller.
## Reading Query and Body Parameters
-Inside the action, you can access request parameters using the `param` function. A parameter can either be a URL parameter like `?paramName=paramValue` (_this is also called a query parameter_), or given as a form field like `
` (_in that case we're talking about a body parameter_). The `param` function will work with query and body parameters, so you don't have to worry about that (in case a query and body parameter is set with the same name, the body parameter will take priority).
+Inside the action, you can access request parameters using the [`param`](https://ihp.digitallyinduced.com/api-docs/IHP-Controller-Param.html#v:param) function. A parameter can either be a URL parameter like `?paramName=paramValue` (_this is also called a query parameter_), or given as a form field like `` (_in that case we're talking about a body parameter_). The [`param`](https://ihp.digitallyinduced.com/api-docs/IHP-Controller-Param.html#v:param) function will work with query and body parameters, so you don't have to worry about that (in case a query and body parameter is set with the same name, the body parameter will take priority).
Given a request like `GET /UsersAction?maxItems=50`, you can access the `maxItems` like this:
@@ -61,7 +61,7 @@ An alternative request to that action can use a form for passing the `maxItems`:
```
-The value is automatically transformed to an `Int`. This parsing works out of the box for Ids, UUID, Bools, Timestamps, etc. Here are some more examples:
+The value is automatically transformed to an [`Int`](https://ihp.digitallyinduced.com/api-docs/IHP-MailPrelude.html#t:Int). This parsing works out of the box for [Ids](https://ihp.digitallyinduced.com/api-docs/IHP-Prelude.html#t:Id), [UUID](https://ihp.digitallyinduced.com/api-docs/IHP-MailPrelude.html#t:UUID), [Bools](https://ihp.digitallyinduced.com/api-docs/IHP-MailPrelude.html#t:Bool), [Timestamps](https://ihp.digitallyinduced.com/api-docs/IHP-RouterPrelude.html#t:UTCTime), etc. Here are some more examples:
```haskell
action ExampleAction = do
@@ -74,7 +74,7 @@ action ExampleAction = do
In case there is a problem parsing the request parameter, an error will be triggered.
-When the parameter is optional, use `paramOrDefault`:
+When the parameter is optional, use [`paramOrDefault`](https://ihp.digitallyinduced.com/api-docs/IHP-Controller-Param.html#v:paramOrDefault):
```haskell
action UsersAction = do
@@ -83,7 +83,7 @@ action UsersAction = do
When this action is called without the `maxItems` parameter being set (or when invalid), it will fall back to the default value `50`.
-There is also `paramOrNothing` which will return `Nothing` when the parameter is missing and `Just theValue` otherwise.
+There is also [`paramOrNothing`](https://ihp.digitallyinduced.com/api-docs/IHP-Controller-Param.html#v:paramOrNothing) which will return [`Nothing`](https://ihp.digitallyinduced.com/api-docs/IHP-Prelude.html#t:Maybe) when the parameter is missing and [`Just theValue`](https://ihp.digitallyinduced.com/api-docs/IHP-Prelude.html#t:Maybe) otherwise.
### Multiple Params With Same Name (Checkboxes)
@@ -97,7 +97,7 @@ When working with checkboxes sometimes there are multiple values for a given par
Egg
```
-When both checkboxes for Milk and Egg are checked, the usual way of calling `param @Text "ingredients"` will only return the first ingredient `"Milk"`. To access all the checked `ingredients` use `paramList`:
+When both checkboxes for Milk and Egg are checked, the usual way of calling [`param @Text "ingredients"`](https://ihp.digitallyinduced.com/api-docs/IHP-Controller-Param.html#v:param) will only return the first ingredient `"Milk"`. To access all the checked `ingredients` use [`paramList`](https://ihp.digitallyinduced.com/api-docs/IHP-Controller-Param.html#v:paramList):
```haskell
action BuildFood = do
@@ -106,7 +106,7 @@ action BuildFood = do
When this action is called with both checkboxes checked `ingredients` will be set to `["milk", "egg"]`. When no checkbox is checked it will return an empty list.
-Similar to `param` this works out of the box for Ids, UUID, Bools, Timestamps, etc.
+Similar to [`param`](https://ihp.digitallyinduced.com/api-docs/IHP-Controller-Param.html#v:param) this works out of the box for [Ids](https://ihp.digitallyinduced.com/api-docs/IHP-Prelude.html#t:Id), [UUID](https://ihp.digitallyinduced.com/api-docs/IHP-Prelude.html#t:UUID), [Bools](https://ihp.digitallyinduced.com/api-docs/IHP-Prelude.html#t:Bool), [Timestamps](https://ihp.digitallyinduced.com/api-docs/IHP-RouterPrelude.html#t:UTCTime), etc.
### Passing Data from the Action to the View
@@ -159,26 +159,26 @@ This will render `Hello World, Unnamed!` when the `ExampleAction` is called with
### Accessing the FrameworkConfig inside Controllers and Views.
-The config defined in `Config/Config.hs` is available through the implicit parameter `context`, a `ConfigProvider` that is available in controllers.
+The config defined in `Config/Config.hs` is available through the implicit parameter `context`, a [`ConfigProvider`](https://ihp.digitallyinduced.com/api-docs/IHP-FrameworkConfig.html#t:ConfigProvider) that is available in controllers.
-There are helpers that use this implicit parameter, e.g. `isDevelopment/isProduction`:
+There are helpers that use this implicit parameter, e.g. [`isDevelopment`](https://ihp.digitallyinduced.com/api-docs/IHP-FrameworkConfig.html#v:isDevelopment)/[`isProduction`](https://ihp.digitallyinduced.com/api-docs/IHP-FrameworkConfig.html#v:isProduction):
```haskell
action MyAction = do
when isDevelopment (putStrLn "Running in dev mode")
```
-or you can use the function `getFrameworkConfig` if you need to access the config yourself.
+or you can use the function [`getFrameworkConfig`](https://ihp.digitallyinduced.com/api-docs/IHP-FrameworkConfig.html#v:getFrameworkConfig) if you need to access the config yourself.
### Advanced: Working with Custom Types
-Rarely you might want to work with a custom scalar value which is not yet supported with `param`. Define a custom `ParamReader` instance to be able to use the `param` functions with your custom value type. [For that, take a look at the existing instances of `ParamReader`.](https://ihp.digitallyinduced.com/api-docs/IHP-Controller-Param.html#t:ParamReader)
+Rarely you might want to work with a custom scalar value which is not yet supported with [`param`](https://ihp.digitallyinduced.com/api-docs/IHP-Controller-Param.html#v:param). Define a custom [`ParamReader`](https://ihp.digitallyinduced.com/api-docs/IHP-Controller-Param.html#t:ParamReader) instance to be able to use the [`param`](https://ihp.digitallyinduced.com/api-docs/IHP-Controller-Param.html#v:param) functions with your custom value type. [For that, take a look at the existing instances of `ParamReader`.](https://ihp.digitallyinduced.com/api-docs/IHP-Controller-Param.html#t:ParamReader)
### Records
-When working with records, use `fill` instead of `param`. Fill automatically deals with validation failure when e.g. a field value needs to be an integer, but the submitted value is not numeric.
+When working with records, use [`fill`](https://ihp.digitallyinduced.com/api-docs/IHP-Controller-Param.html#v:fill) instead of [`param`](https://ihp.digitallyinduced.com/api-docs/IHP-Controller-Param.html#v:param). [`fill`](https://ihp.digitallyinduced.com/api-docs/IHP-Controller-Param.html#v:fill) automatically deals with validation failure when e.g. a field value needs to be an integer, but the submitted value is not numeric.
-Here is an example of using `fill`:
+Here is an example of using [`fill`](https://ihp.digitallyinduced.com/api-docs/IHP-Controller-Param.html#v:fill):
```haskell
action CreatePostAction = do
@@ -195,7 +195,7 @@ action CreatePostAction = do
## Lifecycle
-The Controller instance provides a `beforeAction` function, which is called before the `action` function is called. Common request handling logic like authentication is often placed inside `beforeAction` to protect all actions of the controller against unprotected access.
+The Controller instance provides a [`beforeAction`](https://ihp.digitallyinduced.com/api-docs/IHP-ControllerSupport.html#v:beforeAction) function, which is called before the [`action`](https://ihp.digitallyinduced.com/api-docs/IHP-ControllerSupport.html#v:action) function is called. Common request handling logic like authentication is often placed inside [`beforeAction`](https://ihp.digitallyinduced.com/api-docs/IHP-ControllerSupport.html#v:beforeAction) to protect all actions of the controller against unprotected access.
Here is an example to illustrate the lifecycle:
@@ -221,13 +221,13 @@ instance Controller PostsController where
## Accessing the Current Action
-Inside the `beforeAction` and `action` you can access the current action using the special `?theAction` variable. That is useful when writing controller helpers, because the variable is passed implicitly.
+Inside the [`beforeAction`](https://ihp.digitallyinduced.com/api-docs/IHP-ControllerSupport.html#v:beforeAction) and [`action`](https://ihp.digitallyinduced.com/api-docs/IHP-ControllerSupport.html#v:action) you can access the current action using the special `?theAction` variable. That is useful when writing controller helpers, because the variable is passed implicitly.
## Accessing the Current Request
-IHP uses the Haskell WAI standard for dealing with HTTP requests and responses. You can get access to the Wai Request data structure by using `request`:
+IHP uses the Haskell WAI standard for dealing with HTTP requests and responses. You can get access to the Wai Request data structure by using [`request`](https://ihp.digitallyinduced.com/api-docs/IHP-ControllerSupport.html#v:request):
-Take a look at [the Wai documentation](https://hackage.haskell.org/package/wai-3.2.2.1/docs/Network-Wai.html) to see what you can do with the Wai `Request`:
+Take a look at [the Wai documentation](https://hackage.haskell.org/package/wai-3.2.2.1/docs/Network-Wai.html) to see what you can do with the Wai [`Request`](https://hackage.haskell.org/package/wai-3.2.2.1/docs/Network-Wai.html#t:Request):
```haskell
action ExampleAction = do
@@ -253,7 +253,7 @@ action ExampleAction = do
### Request Headers
-Use `getHeader` to access a request header:
+Use [`getHeader`](https://ihp.digitallyinduced.com/api-docs/IHP-ControllerSupport.html#v:getHeader) to access a request header:
```haskell
action ExampleAction = do
@@ -266,7 +266,7 @@ to `Just "the user agent value"`.
The lookup for the header in the request is case insensitive.
-Use `setHeader` to set a request header:
+Use [`setHeader`](https://ihp.digitallyinduced.com/api-docs/IHP-ControllerSupport.html#v:setHeader) to set a request header:
```haskell
action ExampleAction = do
@@ -277,17 +277,17 @@ action ExampleAction = do
### Rendering Views
-Inside a controller, you have several ways of sending a response. The most common way is to use the `render` function with a `View` data structure, like this:
+Inside a controller, you have several ways of sending a response. The most common way is to use the [`render`](https://ihp.digitallyinduced.com/api-docs/IHP-Controller-Render.html#v:render) function with a [`View`](https://ihp.digitallyinduced.com/api-docs/IHP-ViewSupport.html#t:View) data structure, like this:
```
render ShowPostView { .. }
```
-The `render` function automatically picks the right response format based on the `Accept` header of the browser. It will try to send an HTML response when HTML is requested, and will also try to send a JSON response when a JSON response is expected. A [`406 Not Acceptable`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/406) will be send when the `render` function cannot fulfill the requested `Accept` formats.
+The [`render`](https://ihp.digitallyinduced.com/api-docs/IHP-Controller-Render.html#v:render) function automatically picks the right response format based on the `Accept` header of the browser. It will try to send an HTML response when HTML is requested, and will also try to send a JSON response when a JSON response is expected. A [`406 Not Acceptable`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/406) will be send when the [`render`](https://ihp.digitallyinduced.com/api-docs/IHP-Controller-Render.html#v:render) function cannot fulfill the requested `Accept` formats.
### Rendering Plain Text
-Call `renderPlain` to send a simple plain text response:
+Call [`renderPlain`](https://ihp.digitallyinduced.com/api-docs/IHP-Controller-Render.html#v:renderPlain) to send a simple plain text response:
```haskell
action ExampleAction = do
@@ -298,18 +298,18 @@ action ExampleAction = do
Usually, you want to render your HTML using a view. See `Rendering Views` for details.
-Sometimes you want to render HTML without using views, e.g. doing it inline in the action. Call `respondHtml` for that:
+Sometimes you want to render HTML without using views, e.g. doing it inline in the action. Call [`respondHtml`](https://ihp.digitallyinduced.com/api-docs/IHP-Controller-Render.html#v:respondHtml) for that:
```haskell
action ExampleAction = do
respondHtml [hsx|
Hello World
|]
```
-You will need to import `hsx` into your controller: `import IHP.ViewPrelude (hsx)`.
+You will need to import [`hsx`](https://ihp.digitallyinduced.com/api-docs/IHP-ViewPrelude.html#v:hsx) into your controller: [`import IHP.ViewPrelude (hsx)`](https://ihp.digitallyinduced.com/api-docs/IHP-ViewPrelude.html#v:hsx).
### Rendering a Static File
-Use `renderFile path contentType` to respond with a static file:
+Use [`renderFile path contentType`](https://ihp.digitallyinduced.com/api-docs/IHP-Controller-Render.html#v:renderFile) to respond with a static file:
```haskell
action ExampleAction = do
@@ -318,7 +318,7 @@ action ExampleAction = do
### Rendering a Not Found Message
-Use `renderNotFound` to render a generic not found message, e.g. when an entity cannot be found:
+Use [`renderNotFound`](https://ihp.digitallyinduced.com/api-docs/IHP-Controller-Render.html#v:renderNotFound) to render a generic not found message, e.g. when an entity cannot be found:
```haskell
action ExampleAction = do
@@ -329,20 +329,20 @@ action ExampleAction = do
### Redirect to an Action
-Use `redirectTo` to redirect to an action:
+Use [`redirectTo`](https://ihp.digitallyinduced.com/api-docs/IHP-Controller-Redirect.html#v:redirectTo) to redirect to an action:
```haskell
action ExampleAction = do
redirectTo ShowPostAction { postId = ... }
```
-When you need to pass a custom query parameter, you cannot use the `redirectTo` function. See `Redirect to a Path` for that.
+When you need to pass a custom query parameter, you cannot use the [`redirectTo`](https://ihp.digitallyinduced.com/api-docs/IHP-Controller-Redirect.html#v:redirectTo) function. See `Redirect to a Path` for that.
-The redirect will use HTTP status code `302`. The `baseUrl` in `Config/Config.hs` will be used. In development mode, the `baseUrl` might not be specified in `Config/Config.hs`. Then it will be set to localhost by default.
+The redirect will use HTTP status code `302`. The [`baseUrl`](https://ihp.digitallyinduced.com/api-docs/IHP-FrameworkConfig.html#t:FrameworkConfig) in `Config/Config.hs` will be used. In development mode, the [`baseUrl`](https://ihp.digitallyinduced.com/api-docs/IHP-FrameworkConfig.html#t:FrameworkConfig) might not be specified in `Config/Config.hs`. Then it will be set to localhost by default.
### Redirect to a Path
-Use `redirectToPath` when you want to redirect to a path on the same domain:
+Use [`redirectToPath`](https://ihp.digitallyinduced.com/api-docs/IHP-Controller-Redirect.html#v:redirectToPath) when you want to redirect to a path on the same domain:
```haskell
action ExampleAction = do
@@ -358,7 +358,7 @@ action ExampleAction = do
### Redirect to a URL
-Use `redirectToUrl` to redirect to some external URL:
+Use [`redirectToUrl`](https://ihp.digitallyinduced.com/api-docs/IHP-Controller-Redirect.html#v:redirectToUrl) to redirect to some external URL:
```haskell
action ExampleAction = do
@@ -377,7 +377,7 @@ action ExampleAction = do
putStrLn "This line here is not reachable"
```
-The `putStrLn` will never be called because the `redirectTo` already stops execution.
+The [`putStrLn`](https://ihp.digitallyinduced.com/api-docs/IHP-Prelude.html#v:putStrLn) will never be called because the [`redirectTo`](https://ihp.digitallyinduced.com/api-docs/IHP-Controller-Redirect.html#v:redirectTo) already stops execution.
When you have created a [`Response`](https://hackage.haskell.org/package/wai-3.2.2.1/docs/Network-Wai.html#t:Response) manually, you can use [`respondAndExit`](https://ihp.digitallyinduced.com/api-docs/src/IHP.ControllerSupport.html#respondAndExit) to send your response and stop action execution.
@@ -393,7 +393,7 @@ Actions have access to the Request Context via the controller context:
let requestContext = get #requestContext ?context
```
-The Request Context provides access to the Wai request as well as information like the request query and post parameters and the uploaded files. It's usually used by other functions to provide high-level functionality. E.g. the `getHeader` function uses the Request Context to access the request headers.
+The Request Context provides access to the Wai request as well as information like the request query and post parameters and the uploaded files. It's usually used by other functions to provide high-level functionality. E.g. the [`getHeader`](https://ihp.digitallyinduced.com/api-docs/IHP-ControllerSupport.html#v:getHeader) function uses the Request Context to access the request headers.
## File Uploads
diff --git a/Guide/database.markdown b/Guide/database.markdown
index 34bd23ee2..e394768eb 100644
--- a/Guide/database.markdown
+++ b/Guide/database.markdown
@@ -88,7 +88,7 @@ When dumping the database into the `Fixtures.sql` first and then rebuilding the
### Model Context
-In a pure functional programming language like Haskell, we need to pass the database connection to all functions which need to access the database. We use an implicit parameter `?modelContext :: ModelContext` to pass around the database connection without always specifying it. The `ModelContext` data structure is basically just a wrapper around the actual database connection.
+In a pure functional programming language like Haskell, we need to pass the database connection to all functions which need to access the database. We use an implicit parameter `?modelContext :: ModelContext` to pass around the database connection without always specifying it. The [`ModelContext`](https://ihp.digitallyinduced.com/api-docs/IHP-ModelSupport.html#t:ModelContext) data structure is basically just a wrapper around the actual database connection.
An implicit parameter is a parameter which is automatically passed to certain functions, it just needs to be available in the current scope.
@@ -134,7 +134,7 @@ In the Schema Designer, you can take a look at the generated Haskell code by rig
### Querying Records
-You can retrieve all records of a table using `query`
+You can retrieve all records of a table using [`query`](https://ihp.digitallyinduced.com/api-docs/IHP-QueryBuilder.html#v:query)
```haskell
do
@@ -147,7 +147,7 @@ This will run a `SELECT * FROM users` query and put a list of `User` structures.
### Fetching a single record
-When you have the id of a record, you can also use `fetch` to get it from the database:
+When you have the id of a record, you can also use [`fetch`](https://ihp.digitallyinduced.com/api-docs/IHP-Fetch.html#v:fetch) to get it from the database:
```haskell
do
@@ -158,11 +158,11 @@ do
This will run the SQL query `SELECT * FROM users WHERE id = ... LIMIT 1`.
-`fetch` knows a single entity will be returned for the id, so instead of a list of users, a single user will be returned. In case the entity is not found, an exception is thrown. Use `fetchOrNothing` to get `Nothing` instead of an exception when no result is found
+[`fetch`](https://ihp.digitallyinduced.com/api-docs/IHP-Fetch.html#v:fetch) knows a single entity will be returned for the id, so instead of a list of users, a single user will be returned. In case the entity is not found, an exception is thrown. Use [`fetchOrNothing`](https://ihp.digitallyinduced.com/api-docs/IHP-Fetch.html#v:fetchOneOrNothing) to get `Nothing` instead of an exception when no result is found
### Fetching a list of ids
-When have you a list of ids of a single record type, you can also just `fetch` them:
+When have you a list of ids of a single record type, you can also just [`fetch`](https://ihp.digitallyinduced.com/api-docs/IHP-Fetch.html#v:fetch) them:
```haskell
do
@@ -196,7 +196,7 @@ action ShowTask { taskId } = do
Nothing -> pure Nothing
```
-This contains a lot of boilerplate for wrapping and unwrapping the `Maybe` value. Therefore you can just call `fetchOneOrNothing` directly on the `Maybe (Id User)` value:
+This contains a lot of boilerplate for wrapping and unwrapping the [`Maybe`](https://ihp.digitallyinduced.com/api-docs/IHP-Prelude.html#t:Maybe) value. Therefore you can just call [`fetchOneOrNothing`](https://ihp.digitallyinduced.com/api-docs/IHP-Fetch.html#v:fetchOneOrNothing) directly on the `Maybe (Id User)` value:
```haskell
action ShowTask { taskId } = do
@@ -206,7 +206,7 @@ action ShowTask { taskId } = do
### Fetching `n` records (LIMIT)
-Use `limit` to query only up to `n` records from a table:
+Use [`limit`](https://ihp.digitallyinduced.com/api-docs/IHP-QueryBuilder.html#v:limit) to query only up to `n` records from a table:
```haskell
do
@@ -218,7 +218,7 @@ do
This will run a `SELECT * FROM users ORDER BY firstname LIMIT 10` query and will return the first 10 users ordered by their `firstname`.
-When you are only interested in the first result you can also use `fetchOne` as a shortcut for `|> limit 1`:
+When you are only interested in the first result you can also use [`fetchOne`](https://ihp.digitallyinduced.com/api-docs/IHP-Fetch.html#v:fetchOne) as a shortcut for [`|> limit 1`](https://ihp.digitallyinduced.com/api-docs/IHP-QueryBuilder.html#v:limit):
```haskell
do
@@ -229,7 +229,7 @@ do
### Skipping `n` records (OFFSET)
-Use `offset` to skip `n` records from a table:
+Use [`offset`](https://ihp.digitallyinduced.com/api-docs/IHP-QueryBuilder.html#v:offset) to skip `n` records from a table:
```haskell
do
@@ -239,11 +239,11 @@ do
|> fetch
```
-This is most often used together with `limit` to implement paging.
+This is most often used together with [`limit`](https://ihp.digitallyinduced.com/api-docs/IHP-QueryBuilder.html#v:limit) to implement paging.
### Counting records (COUNT queries)
-You can use `fetchCount` instead of `fetch` to get the count of records matching the query:
+You can use [`fetchCount`](https://ihp.digitallyinduced.com/api-docs/IHP-Fetch.html#v:fetchCount) instead of [`fetch`](https://ihp.digitallyinduced.com/api-docs/IHP-Fetch.html#v:fetch) to get the count of records matching the query:
```haskell
do
@@ -256,7 +256,7 @@ do
### Fetching distinct records
-Use `distinct` to fetch distinct records.
+Use [`distinct`](https://ihp.digitallyinduced.com/api-docs/IHP-QueryBuilder.html#v:distinct) to fetch distinct records.
```haskell
do
@@ -265,7 +265,7 @@ do
|> fetch
```
-Or `distinctOn #tableField` to fetch distinct records based on the `#tableField` value.
+Or [`distinctOn #tableField`](https://ihp.digitallyinduced.com/api-docs/IHP-QueryBuilder.html#v:distinctOn) to fetch distinct records based on the `#tableField` value.
```haskell
do
@@ -278,7 +278,7 @@ do
The IHP query builder is designed to be able to easily express many basic sql queries. When your application is growing you will typically hit a point where a complex SQL query cannot be easily expressed with the IHP query builder. In that case it's recommended to use handwritten SQL to access your data.
-Use the function `sqlQuery` to run a raw SQL query:
+Use the function [`sqlQuery`](https://ihp.digitallyinduced.com/api-docs/IHP-ModelSupport.html#v:sqlQuery) to run a raw SQL query:
```haskell
do
@@ -294,7 +294,7 @@ do
### Scalar Results
-The `sqlQuery` function always returns a list of rows as the result. When the result of your query is a single value (such as an integer or string) use `sqlQueryScalar`:
+The [`sqlQuery`](https://ihp.digitallyinduced.com/api-docs/IHP-ModelSupport.html#v:sqlQuery) function always returns a list of rows as the result. When the result of your query is a single value (such as an integer or string) use [`sqlQueryScalar`](https://ihp.digitallyinduced.com/api-docs/IHP-ModelSupport.html#v:sqlQueryScalar):
```haskell
do
@@ -433,13 +433,13 @@ query =
|]
```
-`Row` is the data type used to hold the result of the query. The use of `trackTableRead` enables the query to play nicely with Auto Refresh.
+`Row` is the data type used to hold the result of the query. The use of [`trackTableRead`](https://ihp.digitallyinduced.com/api-docs/IHP-ModelSupport.html#v:trackTableRead) enables the query to play nicely with Auto Refresh.
## Create
### Creating a single record
-To insert a record into the database, call `newRecord` to get an empty record value:
+To insert a record into the database, call [`newRecord`](https://ihp.digitallyinduced.com/api-docs/IHP-ModelSupport.html#t:Record) to get an empty record value:
```haskell
do
@@ -447,9 +447,9 @@ do
-- user = User { id = 0000-0000-0000-0000, firstname = "", lastname = "" }
```
-The `newRecord` function does not insert the record, it just returns a new empty data structure we can fill with values and then insert into the database.
+The [`newRecord`](https://ihp.digitallyinduced.com/api-docs/IHP-ModelSupport.html#t:Record) function does not insert the record, it just returns a new empty data structure we can fill with values and then insert into the database.
-We can use `set` to fill in attributes:
+We can use [`set`](https://ihp.digitallyinduced.com/api-docs/IHP-HaskellSupport.html#v:set) to fill in attributes:
```haskell
do
@@ -460,7 +460,7 @@ do
-- user = User { id = 0000-0000-0000-0000, firstname = "Max", lastname = "Mustermann" }
```
-We use `createRecord` to insert the above record into the `users` table:
+We use [`createRecord`](https://ihp.digitallyinduced.com/api-docs/IHP-HaskellSupport.html#v:set) to insert the above record into the `users` table:
```haskell
do
@@ -488,7 +488,7 @@ do
### Creating many records
-You can use `createMany` to insert multiple records with a single `INSERT` statement:
+You can use [`createMany`](https://ihp.digitallyinduced.com/api-docs/IHP-ModelSupport.html#v:createMany) to insert multiple records with a single `INSERT` statement:
```haskell
do
@@ -505,7 +505,7 @@ INSERT INTO users (id, firstname, lastname)
## Update
-The function `updateRecord` runs an `UPDATE` query for a specific record:
+The function [`updateRecord`](https://ihp.digitallyinduced.com/api-docs/IHP-ModelSupport.html#t:CanUpdate) runs an `UPDATE` query for a specific record:
```haskell
do
@@ -527,7 +527,7 @@ The `UPDATE` query will only update columns that have been changed using `|> set
### Deleting a single record
-Use `deleteRecord` to run a simple `DELETE` query:
+Use [`deleteRecord`](https://ihp.digitallyinduced.com/api-docs/IHP-ModelSupport.html#v:deleteRecord) to run a simple `DELETE` query:
```haskell
do
@@ -543,7 +543,7 @@ DELETE FROM users WHERE id = "cf633b17-c409-4df5-a2fc-8c3b3d6c2ea7"
### Deleting many records
-Use `deleteRecords` to run a `DELETE` query for multiple records:
+Use [`deleteRecords`](https://ihp.digitallyinduced.com/api-docs/IHP-ModelSupport.html#v:deleteRecords) to run a `DELETE` query for multiple records:
```haskell
do
@@ -559,7 +559,7 @@ DELETE FROM users WHERE id IN (...)
### Deleting all records
-Use `deleteAll` to run a `DELETE` query for all rows in a table:
+Use [`deleteAll`](https://ihp.digitallyinduced.com/api-docs/IHP-ModelSupport.html#v:deleteAll) to run a `DELETE` query for all rows in a table:
```haskell
do
@@ -629,7 +629,7 @@ You can use `fill` even with custom enums:
redirectTo PostsAction
```
-In your views, use `inputValue` to get a textual representation for your enum which works with `fill`:
+In your views, use [`inputValue`](https://ihp.digitallyinduced.com/api-docs/IHP-ModelSupport.html#t:InputValue) to get a textual representation for your enum which works with [`fill`](https://ihp.digitallyinduced.com/api-docs/IHP-Controller-Param.html#v:fill):
```html
[hsx|
@@ -728,7 +728,7 @@ IHP currently has support for the following postgres column types:
## Transactions
-You can use the `withTransaction` function to wrap your database operations in a postgres database transaction:
+You can use the [`withTransaction`](https://ihp.digitallyinduced.com/api-docs/IHP-ModelSupport.html#v:withTransaction) function to wrap your database operations in a postgres database transaction:
```haskell
withTransaction do
@@ -746,11 +746,11 @@ withTransaction do
In this example, when the creation of the User fails, the creation of the company will be rolled back. So that no
incomplete data is left in the database when there's an error.
-The `withTransaction` function will automatically commit after it succesfully executed the passed do-block. When any exception is thrown, it will automatically rollback.
+The [`withTransaction`](https://ihp.digitallyinduced.com/api-docs/IHP-ModelSupport.html#v:withTransaction) function will automatically commit after it succesfully executed the passed do-block. When any exception is thrown, it will automatically rollback.
### Common Pitfalls
-Keep in mind that some IHP functions like `redirectTo` or `render` throw a `ResponseException`. So code like below will not work as expected:
+Keep in mind that some IHP functions like [`redirectTo`](https://ihp.digitallyinduced.com/api-docs/IHP-Controller-Redirect.html#v:redirectTo) or [`render`](https://ihp.digitallyinduced.com/api-docs/IHP-Controller-Render.html#v:render) throw a [`ResponseException`](https://ihp.digitallyinduced.com/api-docs/IHP-ControllerSupport.html#t:ResponseException). So code like below will not work as expected:
```haskell
action CreateUserAction = do
@@ -759,7 +759,7 @@ action CreateUserAction = do
redirectTo NewSessionAction
```
-The `redirectTo` throws a `ResponseException` and will cause a rollback. This code should be structured like this:
+The [`redirectTo`](https://ihp.digitallyinduced.com/api-docs/IHP-Controller-Redirect.html#v:redirectTo) throws a [`ResponseException`](https://ihp.digitallyinduced.com/api-docs/IHP-ControllerSupport.html#t:ResponseException) and will cause a rollback. This code should be structured like this:
```haskell
action CreateUserAction = do
diff --git a/Guide/deployment.markdown b/Guide/deployment.markdown
index 44d335847..59cad1fac 100644
--- a/Guide/deployment.markdown
+++ b/Guide/deployment.markdown
@@ -371,7 +371,7 @@ CSS_FILES += static/startpage.css # The second import of app.css
We need to update the `Layout.hs` to only load `prod.css` and `prod.js` when running in production.
-For that we use `isDevelopment` and `isProduction` to conditionally load different files. Change your `Web/View/Layout.hs` to look like this:
+For that we use [`isDevelopment`](https://ihp.digitallyinduced.com/api-docs/IHP-FrameworkConfig.html#v:isDevelopment) and [`isProduction`](https://ihp.digitallyinduced.com/api-docs/IHP-FrameworkConfig.html#v:isProduction) to conditionally load different files. Change your `Web/View/Layout.hs` to look like this:
```haskell
stylesheets :: Html
@@ -470,4 +470,4 @@ config = do
Now sentry is set up.
-**When running on IHP Cloud:** You also need to update the `Config.hs` inside your IHP Cloud project settings.
\ No newline at end of file
+**When running on IHP Cloud:** You also need to update the `Config.hs` inside your IHP Cloud project settings.
diff --git a/Guide/file-storage.markdown b/Guide/file-storage.markdown
index ccc237050..ef8c68fa1 100644
--- a/Guide/file-storage.markdown
+++ b/Guide/file-storage.markdown
@@ -22,7 +22,7 @@ Open your `Config/Config.hs` and import `import IHP.FileStorage.Config`:
import IHP.FileStorage.Config
```
-Then add a call to `initStaticDirStorage`:
+Then add a call to [`initStaticDirStorage`](https://ihp.digitallyinduced.com/api-docs/IHP-FileStorage-Config.html#v:initStaticDirStorage):
```haskell
module Config where
@@ -48,7 +48,7 @@ Open your `Config/Config.hs` and import `import IHP.FileStorage.Config`:
import IHP.FileStorage.Config
```
-Then add a call to `initS3Storage`:
+Then add a call to [`initS3Storage`](https://ihp.digitallyinduced.com/api-docs/IHP-FileStorage-Config.html#v:initS3Storage):
```haskell
module Config where
@@ -93,7 +93,7 @@ Open your `Config/Config.hs` and import `import IHP.FileStorage.Config`:
import IHP.FileStorage.Config
```
-Then add a call to `initMinioStorage`:
+Then add a call to [`initMinioStorage`](https://ihp.digitallyinduced.com/api-docs/IHP-FileStorage-Config.html#v:initMinioStorage):
```haskell
module Config where
@@ -144,7 +144,7 @@ CREATE TABLE companies (
);
```
-You can use the `uploadToStorage` function to save a user upload to the storage:
+You can use the [`uploadToStorage`](https://ihp.digitallyinduced.com/api-docs/IHP-FileStorage-ControllerFunctions.html#v:uploadToStorage) function to save a user upload to the storage:
```haskell
action UpdateCompanyAction { companyId } = do
@@ -159,9 +159,9 @@ action UpdateCompanyAction { companyId } = do
redirectTo EditCompanyAction { .. }
```
-The call to `uploadToStorage #logoUrl` will upload the file provided by the user. It will be saved as `companies/` on the configured storage. The the file url will be written to the `logoUrl` attribute.
+The call to [`uploadToStorage #logoUrl`](https://ihp.digitallyinduced.com/api-docs/IHP-FileStorage-ControllerFunctions.html#v:uploadToStorage) will upload the file provided by the user. It will be saved as `companies/` on the configured storage. The the file url will be written to the `logoUrl` attribute.
-After calling `uploadToStorage` inside a action, you typically need to use `>>=` instead of `|>`:
+After calling [`uploadToStorage`](https://ihp.digitallyinduced.com/api-docs/IHP-FileStorage-ControllerFunctions.html#v:uploadToStorage) inside a action, you typically need to use [`>>=`](https://ihp.digitallyinduced.com/api-docs/IHP-Prelude.html#v:-62--62--61-) instead of [`|>`](https://ihp.digitallyinduced.com/api-docs/IHP-HaskellSupport.html#v:-124--62-):
```haskell
-- Bad, will cause a type error:
@@ -181,7 +181,7 @@ company
#### Form
-To submit a file upload, add a `{fileField #logoUrl}` to the form that calls the action:
+To submit a file upload, add a [`{fileField #logoUrl}`](https://ihp.digitallyinduced.com/api-docs/IHP-View-Form.html#v:fileField) to the form that calls the action:
```haskell
renderForm :: Company -> Html
@@ -196,7 +196,7 @@ renderForm company = formFor company [hsx|
##### Custom File Field
-If you need to more customization on the file field which the `fileField` helper doesn't allow, you can also use a handwritten file input:
+If you need to more customization on the file field which the [`fileField`](https://ihp.digitallyinduced.com/api-docs/IHP-View-Form.html#v:fileField) helper doesn't allow, you can also use a handwritten file input:
```haskell
renderForm :: Company -> Html
@@ -214,7 +214,7 @@ renderForm company = formFor company [hsx|
|]
```
-It's important that `` has `name="logoUrl` attribute, as that's where `uploadToStorage #logoUrl` expects to find the file.
+It's important that `` has `name="logoUrl` attribute, as that's where [`uploadToStorage #logoUrl`](https://ihp.digitallyinduced.com/api-docs/IHP-FileStorage-ControllerFunctions.html#v:uploadToStorage) expects to find the file.
##### Inline Preview
@@ -244,7 +244,7 @@ renderForm company = formFor company [hsx|
When dealing with images, we can use the imagemagick tool to e.g. resize the image and strip metadata:
-This accepts any kind of image file compatible with ImageMagick, converts it to a 512x512 PNG and strip all meta data we use `uploadToStorageWithOptions` together with `applyImageMagick`:
+This accepts any kind of image file compatible with ImageMagick, converts it to a 512x512 PNG and strip all meta data we use [`uploadToStorageWithOptions`](https://ihp.digitallyinduced.com/api-docs/IHP-FileStorage-ControllerFunctions.html#v:uploadToStorageWithOptions) together with [`applyImageMagick`](https://ihp.digitallyinduced.com/api-docs/IHP-FileStorage-Preprocessor-ImageMagick.html#v:applyImageMagick):
```haskell
action UpdateCompanyAction { companyId } = do
@@ -264,7 +264,7 @@ action UpdateCompanyAction { companyId } = do
#### Installing ImageMagick
-The `applyImageMagick` function requires `imagemagick` to be installed. You can install it by adding `imagemagick` to the `otherDeps` of your `default.nix`:
+The [`applyImageMagick`](https://ihp.digitallyinduced.com/api-docs/IHP-FileStorage-Preprocessor-ImageMagick.html#v:applyImageMagick) function requires `imagemagick` to be installed. You can install it by adding `imagemagick` to the `otherDeps` of your `default.nix`:
```nix
otherDeps = p: with p; [
@@ -289,7 +289,7 @@ The browser uses the [`Content-Disposition`](https://developer.mozilla.org/en-US
By default no `Content-Disposition` header is set.
-If you use the S3 Storage you can use the `contentDispositionAttachmentAndFileName` function to mark a file as an attachment and use its original provided file name as the downloaded file name:
+If you use the S3 Storage you can use the [`contentDispositionAttachmentAndFileName`](https://ihp.digitallyinduced.com/api-docs/IHP-FileStorage-ControllerFunctions.html#v:contentDispositionAttachmentAndFileName) function to mark a file as an attachment and use its original provided file name as the downloaded file name:
```haskell
let uploadAttachment = uploadToStorageWithOptions $ def
@@ -326,7 +326,7 @@ record
The above methods always require a record like `company` to store the files.
-Use `storeFile` to save uploads without a model:
+Use [`storeFile`](https://ihp.digitallyinduced.com/api-docs/IHP-FileStorage-ControllerFunctions.html#v:storeFile) to save uploads without a model:
```haskell
action UpdateLogoAction = do
@@ -337,9 +337,9 @@ action UpdateLogoAction = do
let url = get #url storedFile
```
-This will upload the provided `` to the `logos` directory. The `storeFile` function returns `StoredFile` structure. We use `get #url` to read the url where the file was saved to.
+This will upload the provided `` to the `logos` directory. The [`storeFile`](https://ihp.digitallyinduced.com/api-docs/IHP-FileStorage-ControllerFunctions.html#v:storeFile) function returns [`StoredFile`](https://ihp.digitallyinduced.com/api-docs/IHP-FileStorage-Types.html#t:StoredFile) structure. We use `get #url` to read the url where the file was saved to.
-There's also a `storeFileWithOptions` to pass additional configuration:
+There's also a [`storeFileWithOptions`](https://ihp.digitallyinduced.com/api-docs/IHP-FileStorage-ControllerFunctions.html#v:storeFileWithOptions) to pass additional configuration:
```haskell
let file = fileOrNothing "file"
@@ -393,7 +393,7 @@ let url :: Text = get #url signedUrl
let expiredAt :: UTCTime = get #expiredAt signedUrl
```
-If the `StaticDirStorage` is used, a unsigned normal URL will be returned, as these files are public anyways.
+If the [`StaticDirStorage`](https://ihp.digitallyinduced.com/api-docs/IHP-FileStorage-Types.html#t:FileStorage) is used, a unsigned normal URL will be returned, as these files are public anyways.
The signed url is valid for 7 days.
@@ -425,4 +425,4 @@ config = do
```
-[You can find a full list of `WaiParser.set...` functions on the `Network.Wai.Parse` documentation](https://hackage.haskell.org/package/wai-extra-3.1.6/docs/Network-Wai-Parse.html#v:setMaxRequestKeyLength)
\ No newline at end of file
+[You can find a full list of `WaiParser.set...` functions on the `Network.Wai.Parse` documentation](https://hackage.haskell.org/package/wai-extra-3.1.6/docs/Network-Wai-Parse.html#v:setMaxRequestKeyLength)
diff --git a/Guide/form.markdown b/Guide/form.markdown
index 952b6d4a7..e69931983 100644
--- a/Guide/form.markdown
+++ b/Guide/form.markdown
@@ -14,7 +14,7 @@ Unless javascript helpers have been deactivated, your form will be submitted usi
## Simple Forms
-Forms usually begin with a `formFor` expression. This is how a simple form can look like:
+Forms usually begin with a [`formFor`](https://ihp.digitallyinduced.com/api-docs/IHP-View-Form.html#v:formFor) expression. This is how a simple form can look like:
```haskell
renderForm :: Post -> Html
@@ -51,21 +51,19 @@ All inputs have auto-generated class names and ids for styling. Also, all `name`
IHP has the most commonly-used form controls built in. In general the form control helpers just need to be passed the field name. Here is a list of all built-in form control helpers:
-```haskell
-{textField #title}
-{textareaField #body}
-{colorField #brandColor}
-{emailField #email}
-{dateField #dueAt}
-{passwordField #password}
-{dateTimeField #createdAt}
-{numberField #quantity}
-{hiddenField #projectId}
-{checkboxField #termsAccepted}
-{selectField #projectId allProjects}
-{fileFile #profilePicture}
-{submitButton}
-```
+- [`{textField #title}`](https://ihp.digitallyinduced.com/api-docs/IHP-View-Form.html#v:textField)
+- [`{textareaField #body}`](https://ihp.digitallyinduced.com/api-docs/IHP-View-Form.html#v:textareaField)
+- [`{colorField #brandColor}`](https://ihp.digitallyinduced.com/api-docs/IHP-View-Form.html#v:colorField)
+- [`{emailField #email}`](https://ihp.digitallyinduced.com/api-docs/IHP-View-Form.html#v:emailField)
+- [`{dateField #dueAt}`](https://ihp.digitallyinduced.com/api-docs/IHP-View-Form.html#v:dateField)
+- [`{passwordField #password}`](https://ihp.digitallyinduced.com/api-docs/IHP-View-Form.html#v:passwordField)
+- [`{dateTimeField #createdAt}`](https://ihp.digitallyinduced.com/api-docs/IHP-View-Form.html#v:dateTimeField)
+- [`{numberField #quantity}`](https://ihp.digitallyinduced.com/api-docs/IHP-View-Form.html#v:numberField)
+- [`{hiddenField #projectId}`](https://ihp.digitallyinduced.com/api-docs/IHP-View-Form.html#v:hiddenField)
+- [`{checkboxField #termsAccepted}`](https://ihp.digitallyinduced.com/api-docs/IHP-View-Form.html#v:checkboxField)
+- [`{selectField #projectId allProjects}`](https://ihp.digitallyinduced.com/api-docs/IHP-View-Form.html#v:selectField)
+- [`{fileFile #profilePicture}`](https://ihp.digitallyinduced.com/api-docs/IHP-View-Form.html#v:fileField)
+- [`{submitButton}`](https://ihp.digitallyinduced.com/api-docs/IHP-View-Form.html#v:submitButton)
A form control is always filled with the value of the given field when rendering. For example, given a post
@@ -73,7 +71,7 @@ A form control is always filled with the value of the given field when rendering
let post = Post { ..., title = "Hello World" }
```
-Rendering `{textField #title}`, the input value will be set like
+Rendering [`{textField #title}`](https://ihp.digitallyinduced.com/api-docs/IHP-View-Form.html#v:textField), the input value will be set like
```html
@@ -90,7 +88,7 @@ let post = Post { ..., title = "" }
|> validateField #title nonEmpty
```
-Rendering `{textField #title}`, the input will have the css class `is-invalid` and an element with the error message will be rendered below the input:
+Rendering [`{textField #title}`](https://ihp.digitallyinduced.com/api-docs/IHP-View-Form.html#v:textField), the input will have the css class `is-invalid` and an element with the error message will be rendered below the input:
```html
@@ -108,7 +106,7 @@ Rendering `{textField #title}`, the input will have the css class `is-invalid` a
## Forms Are Also HSX
-It's important to understand that while the form helpers like `{textField #title}` are called by `formFor`, you can still use HSX there. So you can just add any kind of HSX code inside your form:
+It's important to understand that while the form helpers like [`{textField #title}`](https://ihp.digitallyinduced.com/api-docs/IHP-View-Form.html#v:textField) are called by [`formFor`](https://ihp.digitallyinduced.com/api-docs/IHP-View-Form.html#v:formFor), you can still use HSX there. So you can just add any kind of HSX code inside your form:
```haskell
renderForm :: Post -> Html
@@ -136,7 +134,7 @@ Inside the HSX block of a form, you have access to the special `?formContext` va
## Customizing Inputs
-The return values of the form control helpers are usually a value of type [FormField](https://ihp.digitallyinduced.com/api-docs/IHP-View-Form.html#t:FormField). The `FormField` value is automatically rendered as HTML when used inside an HSX expression. Before this rendering happens, you can specify options to customize the rendering.
+The return values of the form control helpers are usually a value of type [FormField](https://ihp.digitallyinduced.com/api-docs/IHP-View-Form.html#t:FormField). The [`FormField`](https://ihp.digitallyinduced.com/api-docs/IHP-View-Types.html#t:FormField) value is automatically rendered as HTML when used inside an HSX expression. Before this rendering happens, you can specify options to customize the rendering.
### Help Texts
@@ -159,7 +157,7 @@ This will render like:
### Custom Field Label Text
-By default, the field name will be used as a label text. The camel case field name will be made more human-readable of course, so `contactName` will turn to `Contact Name`, etc. Sometimes you want to change this auto-generated input label to something custom. Use `fieldLabel` for that, like this:
+By default, the field name will be used as a label text. The camel case field name will be made more human-readable of course, so `contactName` will turn to `Contact Name`, etc. Sometimes you want to change this auto-generated input label to something custom. Use [`fieldLabel`](https://ihp.digitallyinduced.com/api-docs/IHP-View-Types.html#t:FormField) for that, like this:
```haskell
{(textField #title) { fieldLabel = "Post Title"} }
@@ -176,7 +174,7 @@ This will render like:
### Custom CSS Classes
-You can add custom CSS classes to the input and label for better styling. Set `fieldClass` for adding a class to the input element and `labelClass` for the label element:
+You can add custom CSS classes to the input and label for better styling. Set [`fieldClass`](https://ihp.digitallyinduced.com/api-docs/IHP-View-Types.html#t:FormField) for adding a class to the input element and [`labelClass`](https://ihp.digitallyinduced.com/api-docs/IHP-View-Types.html#t:FormField) for the label element:
```haskell
{(textField #title) { fieldClass="title-input", labelClass = "title-label" } }
@@ -398,7 +396,7 @@ This will render like:
#### Don't render `