module IHP.Controller.NotFound ( notFoundWhen , notFoundUnless , handleNotFound , buildNotFoundResponse , renderNotFound ) where import IHP.Prelude hiding (displayException) import IHP.Controller.RequestContext import Network.HTTP.Types (status404) import Network.Wai import Network.HTTP.Types.Header import qualified Text.Blaze.Html.Renderer.Utf8 as Blaze import qualified Data.ByteString.Lazy as LBS import IHP.HSX.QQ (hsx) import qualified System.Directory as Directory import IHP.Controller.Context import IHP.Controller.Response (respondAndExit) -- | Stops the action execution with a not found message (404) when the access condition is True. -- -- __Example:__ Checking a user is the author of a blog post. -- -- > action EditPostAction { postId } = do -- > post <- fetch postId -- > notFoundWhen (post.authorId /= currentUserId) -- > -- > renderHtml EditView { .. } -- -- This will throw an error and prevent the view from being rendered when the current user is not the author of the post. notFoundWhen :: (?context :: ControllerContext) => Bool -> IO () notFoundWhen condition = when condition renderNotFound -- | Stops the action execution with a not found message (404) when the access condition is False. -- -- __Example:__ Checking a user is the author of a blog post. -- -- > action EditPostAction { postId } = do -- > post <- fetch postId -- > notFoundUnless (post.authorId == currentUserId) -- > -- > renderHtml EditView { .. } -- -- This will throw an error and prevent the view from being rendered when the current user is not the author of the post. notFoundUnless :: (?context :: ControllerContext) => Bool -> IO () notFoundUnless condition = unless condition renderNotFound -- | Renders a 404 not found response. If a static/404.html exists, that is rendered instead of the IHP not found page handleNotFound :: Request -> Respond -> IO ResponseReceived handleNotFound request respond = do response <- buildNotFoundResponse respond response buildNotFoundResponse :: IO Response buildNotFoundResponse = do hasCustomNotFound <- Directory.doesFileExist "static/404.html" if hasCustomNotFound then customNotFoundResponse else pure defaultNotFoundResponse -- | The default IHP 404 not found page defaultNotFoundResponse :: Response defaultNotFoundResponse = responseBuilder status404 [(hContentType, "text/html")] $ Blaze.renderHtmlBuilder [hsx| Action not found

Error 404

Action not found

|] -- | Renders the static/404.html file customNotFoundResponse :: IO Response customNotFoundResponse = do -- We cannot use responseFile here as responseFile ignore the status code by default -- -- See https://github.com/yesodweb/wai/issues/644 page <- LBS.readFile "static/404.html" pure $ responseLBS status404 [(hContentType, "text/html")] page -- | Renders an "Not found" page. -- -- This can be useful e.g. when an entity cannot be accessed: -- -- > action ExampleAction = do -- > renderNotFound -- -- You can override the default access denied page by creating a new file at @static/403.html@. Then IHP will render that HTML file instead of displaying the default IHP access denied page. -- renderNotFound :: (?context :: ControllerContext) => IO () renderNotFound = do response <- buildNotFoundResponse respondAndExit response