|
| 1 | +module Foundation |
| 2 | + ( App (..) |
| 3 | + , Route (..) |
| 4 | + , AppMessage (..) |
| 5 | + , resourcesApp |
| 6 | + , Handler |
| 7 | + , Widget |
| 8 | + , Form |
| 9 | + , maybeAuth |
| 10 | + , requireAuth |
| 11 | + , module Settings |
| 12 | + , module Model |
| 13 | + ) where |
| 14 | + |
| 15 | +import Prelude |
| 16 | +import Yesod |
| 17 | +import Yesod.Static |
| 18 | +import Yesod.Auth |
| 19 | +import Yesod.Auth.BrowserId |
| 20 | +import Yesod.Auth.GoogleEmail |
| 21 | +import Yesod.Default.Config |
| 22 | +import Yesod.Default.Util (addStaticContentExternal) |
| 23 | +import Yesod.Logger (Logger, logMsg, formatLogText) |
| 24 | +import Network.HTTP.Conduit (Manager) |
| 25 | +import qualified Settings |
| 26 | +import qualified Database.Persist.Store |
| 27 | +import Settings.StaticFiles |
| 28 | +import Database.Persist.GenericSql |
| 29 | +import Settings (widgetFile, Extra (..)) |
| 30 | +import Model |
| 31 | +import Text.Jasmine (minifym) |
| 32 | +import Web.ClientSession (getKey) |
| 33 | +import Text.Hamlet (hamletFile) |
| 34 | + |
| 35 | +-- | The site argument for your application. This can be a good place to |
| 36 | +-- keep settings and values requiring initialization before your application |
| 37 | +-- starts running, such as database connections. Every handler will have |
| 38 | +-- access to the data present here. |
| 39 | +data App = App |
| 40 | + { settings :: AppConfig DefaultEnv Extra |
| 41 | + , getLogger :: Logger |
| 42 | + , getStatic :: Static -- ^ Settings for static file serving. |
| 43 | + , connPool :: Database.Persist.Store.PersistConfigPool Settings.PersistConfig -- ^ Database connection pool. |
| 44 | + , httpManager :: Manager |
| 45 | + , persistConfig :: Settings.PersistConfig |
| 46 | + } |
| 47 | + |
| 48 | +-- Set up i18n messages. See the message folder. |
| 49 | +mkMessage "App" "messages" "en" |
| 50 | + |
| 51 | +-- This is where we define all of the routes in our application. For a full |
| 52 | +-- explanation of the syntax, please see: |
| 53 | +-- http://www.yesodweb.com/book/handler |
| 54 | +-- |
| 55 | +-- This function does three things: |
| 56 | +-- |
| 57 | +-- * Creates the route datatype AppRoute. Every valid URL in your |
| 58 | +-- application can be represented as a value of this type. |
| 59 | +-- * Creates the associated type: |
| 60 | +-- type instance Route App = AppRoute |
| 61 | +-- * Creates the value resourcesApp which contains information on the |
| 62 | +-- resources declared below. This is used in Handler.hs by the call to |
| 63 | +-- mkYesodDispatch |
| 64 | +-- |
| 65 | +-- What this function does *not* do is create a YesodSite instance for |
| 66 | +-- App. Creating that instance requires all of the handler functions |
| 67 | +-- for our application to be in scope. However, the handler functions |
| 68 | +-- usually require access to the AppRoute datatype. Therefore, we |
| 69 | +-- split these actions into two functions and place them in separate files. |
| 70 | +mkYesodData "App" $(parseRoutesFile "config/routes") |
| 71 | + |
| 72 | +type Form x = Html -> MForm App App (FormResult x, Widget) |
| 73 | + |
| 74 | +-- Please see the documentation for the Yesod typeclass. There are a number |
| 75 | +-- of settings which can be configured by overriding methods here. |
| 76 | +instance Yesod App where |
| 77 | + approot = ApprootMaster $ appRoot . settings |
| 78 | + |
| 79 | + -- Store session data on the client in encrypted cookies, |
| 80 | + -- default session idle timeout is 120 minutes |
| 81 | + makeSessionBackend _ = do |
| 82 | + key <- getKey "config/client_session_key.aes" |
| 83 | + return . Just $ clientSessionBackend key 120 |
| 84 | + |
| 85 | + defaultLayout widget = do |
| 86 | + master <- getYesod |
| 87 | + mmsg <- getMessage |
| 88 | + |
| 89 | + -- We break up the default layout into two components: |
| 90 | + -- default-layout is the contents of the body tag, and |
| 91 | + -- default-layout-wrapper is the entire page. Since the final |
| 92 | + -- value passed to hamletToRepHtml cannot be a widget, this allows |
| 93 | + -- you to use normal widget features in default-layout. |
| 94 | + |
| 95 | + pc <- widgetToPageContent $ do |
| 96 | + $(widgetFile "normalize") |
| 97 | + addStylesheet $ StaticR css_bootstrap_css |
| 98 | + $(widgetFile "default-layout") |
| 99 | + hamletToRepHtml $(hamletFile "templates/default-layout-wrapper.hamlet") |
| 100 | + |
| 101 | + -- This is done to provide an optimization for serving static files from |
| 102 | + -- a separate domain. Please see the staticRoot setting in Settings.hs |
| 103 | + urlRenderOverride y (StaticR s) = |
| 104 | + Just $ uncurry (joinPath y (Settings.staticRoot $ settings y)) $ renderRoute s |
| 105 | + urlRenderOverride _ _ = Nothing |
| 106 | + |
| 107 | + -- The page to be redirected to when authentication is required. |
| 108 | + authRoute _ = Just $ AuthR LoginR |
| 109 | + |
| 110 | + messageLogger y loc level msg = |
| 111 | + formatLogText (getLogger y) loc level msg >>= logMsg (getLogger y) |
| 112 | + |
| 113 | + -- This function creates static content files in the static folder |
| 114 | + -- and names them based on a hash of their content. This allows |
| 115 | + -- expiration dates to be set far in the future without worry of |
| 116 | + -- users receiving stale content. |
| 117 | + addStaticContent = addStaticContentExternal minifym base64md5 Settings.staticDir (StaticR . flip StaticRoute []) |
| 118 | + |
| 119 | + -- Place Javascript at bottom of the body tag so the rest of the page loads first |
| 120 | + jsLoader _ = BottomOfBody |
| 121 | + |
| 122 | +-- How to run database actions. |
| 123 | +instance YesodPersist App where |
| 124 | + type YesodPersistBackend App = SqlPersist |
| 125 | + runDB f = do |
| 126 | + master <- getYesod |
| 127 | + Database.Persist.Store.runPool |
| 128 | + (persistConfig master) |
| 129 | + f |
| 130 | + (connPool master) |
| 131 | + |
| 132 | +instance YesodAuth App where |
| 133 | + type AuthId App = UserId |
| 134 | + |
| 135 | + -- Where to send a user after successful login |
| 136 | + loginDest _ = HomeR |
| 137 | + -- Where to send a user after logout |
| 138 | + logoutDest _ = HomeR |
| 139 | + |
| 140 | + getAuthId creds = runDB $ do |
| 141 | + x <- getBy $ UniqueUser $ credsIdent creds |
| 142 | + case x of |
| 143 | + Just (Entity uid _) -> return $ Just uid |
| 144 | + Nothing -> do |
| 145 | + fmap Just $ insert $ User (credsIdent creds) Nothing |
| 146 | + |
| 147 | + -- You can add other plugins like BrowserID, email or OAuth here |
| 148 | + authPlugins _ = [authBrowserId, authGoogleEmail] |
| 149 | + |
| 150 | + authHttpManager = httpManager |
| 151 | + |
| 152 | +-- This instance is required to use forms. You can modify renderMessage to |
| 153 | +-- achieve customized and internationalized form validation messages. |
| 154 | +instance RenderMessage App FormMessage where |
| 155 | + renderMessage _ _ = defaultFormMessage |
| 156 | + |
| 157 | +-- Note: previous versions of the scaffolding included a deliver function to |
| 158 | +-- send emails. Unfortunately, there are too many different options for us to |
| 159 | +-- give a reasonable default. Instead, the information is available on the |
| 160 | +-- wiki: |
| 161 | +-- |
| 162 | +-- https://github.com/yesodweb/yesod/wiki/Sending-email |
0 commit comments