packages feed

nomyx-web-1.0.0: src/Nomyx/Web/MainPage.hs

{-# LANGUAGE DoAndIfThenElse      #-}
{-# LANGUAGE ExtendedDefaultRules #-}
{-# LANGUAGE OverloadedStrings    #-}
{-# LANGUAGE ScopedTypeVariables  #-}

module Nomyx.Web.MainPage (launchWebServer) where

import           Control.Applicative
import           Control.Concurrent.STM
import           Control.Monad
import           Control.Monad.Error
import           Control.Monad.State
import           Control.Lens
import           Data.List
import           Data.Maybe
import           Data.Monoid
import           Data.String
import           Data.Text                             as T (Text, pack, unpack, length)
import           Happstack.Authenticate.OpenId.Route   (initOpenId)
import           Happstack.Authenticate.Password.Route (initPassword)
import           Happstack.Authenticate.Password.Core(PasswordConfig(..), PasswordState)
import           Happstack.Authenticate.Route          (initAuthentication)
import           Happstack.Authenticate.Core (AuthenticateURL(..), AuthenticateConfig(..), AuthenticateState, Email(..), User(..), Username(..), UserId(..), GetAuthenticateState(..), decodeAndVerifyToken, tokenUser, usernamePolicy)
import           Happstack.Server                      as HS
import           Nomyx.Language
import           Nomyx.Core.Engine                     hiding (JoinGame,
                                                        LeaveGame)
import           Nomyx.Core.Profile                    as Profile
import qualified Nomyx.Core.Session                    as S
import           Nomyx.Core.Types                      as T hiding (JoinGame, LeaveGame)
import           Nomyx.Core.Utils
import           Nomyx.Core.Engine.Evaluation
import           Nomyx.Web.Common                      as W
import           Nomyx.Web.Game.Details
import           Nomyx.Web.Game.Infos
import           Nomyx.Web.Game.Actions
import           Nomyx.Web.Game.Templates
import           Nomyx.Web.Game.Rules
import           Nomyx.Web.Game.Modules
import qualified Nomyx.Web.Help                        as Help
import           Nomyx.Web.Login
import           Nomyx.Web.NewGame
import           Nomyx.Web.Settings
import           Nomyx.Web.Types
import qualified Nomyx.Auth                            as Auth
import           Paths_nomyx_language
import           Prelude                               hiding (div)
import           Safe
import           System.FilePath
import           Text.Blaze.Html5                      hiding (head, map)
import qualified Text.Blaze.Html5                      as H
import           Text.Blaze.Html5.Attributes           hiding (dir, id)
import qualified Text.Blaze.Html5.Attributes           as A
import           Web.Routes.Happstack
import           Web.Routes.PathInfo
import           Web.Routes.RouteT                     (runRouteT)
import           Web.Routes.Site
import           Imprevu.Happstack.Forms
import           Imprevu.Happstack.Types
import           Imprevu.Evaluation
import qualified Nomyx.Core.Engine.Types as ET
import qualified Nomyx.Core.Session as S

default (Integer, Double, T.Text)

viewMulti :: (Maybe PlayerNumber) -> FilePath -> GameTab -> GameName -> Session -> RoutedNomyxServer Html
viewMulti mpn saveDir gt gn s = do
   (isAdmin, lr, lib) <- case mpn of
      Just pn -> do
         pfd <- getProfile s pn
         let isAdmin = _pIsAdmin $ fromJustNote "viewMulti" pfd
         let lr = _pLastRule $ fromJustNote "viewMulti" pfd
         let lib = _pLibrary $ fromJustNote "viewMulti" pfd
         return (isAdmin, lr, lib)
      Nothing -> return (False, Nothing, _mLibrary $ _multi s)
   let gi = fromJust $ getGameByName gn s  --TODO fix
   gns <- viewGamesTab gi isAdmin saveDir mpn
   vg <- viewGameInfo gi mpn lr isAdmin gt lib 
   ok $ do
      div ! A.id "gameList" $ gns
      vg

viewGamesTab :: GameInfo -> Bool -> FilePath -> (Maybe PlayerNumber) -> RoutedNomyxServer Html
viewGamesTab gi isAdmin saveDir mpn = do
   let g = getGame gi
   let gn = _gameName g
   ok $ do
     table $ do
       tr $ td ! A.class_ "buttonTD" $ H.a "Home "         ! A.class_ "button" ! href (toValue $ showRelURL $ Menu Home gn)
       tr $ td ! A.class_ "buttonTD" $ H.a "Constitution " ! A.class_ "button" ! href (toValue $ showRelURL $ Menu Rules gn)
       tr $ td ! A.class_ "buttonTD" $ H.a "Actions "      ! A.class_ "button" ! href (toValue $ showRelURL $ Menu Actions gn)
       tr $ td ! A.class_ "buttonTD" $ H.a "Library "      ! A.class_ "button" ! href (toValue $ showRelURL $ Menu Lib gn)
     -- br >> b "Help files:" >> br
     -- H.a "Nomyx language"    ! (href "/html/Language-Nomyx.html") ! target "_blank" >> br

viewGameInfo :: GameInfo -> (Maybe PlayerNumber) -> Maybe LastRule -> Bool -> GameTab -> Library -> RoutedNomyxServer Html
viewGameInfo gi mpn mlr isAdmin gt lib = do
   let g = getGame gi
   let gn = _gameName g
   let pi = join $ (Profile.getPlayerInfo g) <$> mpn
   let isGameAdmin = isAdmin || maybe False (== mpn) (Just $ _ownedBy gi)
   let playAs = mpn >> maybe Nothing _playingAs pi
   let pn = fromMaybe 0 mpn
   vrf <- viewLibrary lib mlr gn isGameAdmin
   vms <- viewModules lib mlr gn isGameAdmin
   vios <- viewIOs (fromMaybe pn playAs) g
   vgd <- viewGameDesc g mpn playAs isGameAdmin
   vrs <- viewAllRules pn g
   ok $ case gt of
        Home    -> div ! A.id "gameDescGameDiv" ! A.class_ "game" $ vgd
        Rules   -> div ! A.id "rulesGameDiv"    ! A.class_ "game" $ vrs
        Actions -> div ! A.id "iosGameDiv"      ! A.class_ "game" $ vios
        Lib     -> div ! A.id "newRuleGameDiv"  ! A.class_ "game" $ vrf
        Modules -> div ! A.id "moduleDiv"       ! A.class_ "game" $ vms
        Details -> div ! A.id "detailsGameDiv"  ! A.class_ "game" $ viewDetails pn g

joinGame :: GameName -> RoutedNomyxServer Response
joinGame gn = do
   pn <- fromJust <$> getPlayerNumber
   webCommand (S.joinGame gn pn)
   seeOther (showRelURL MainPage) $ toResponse "Redirecting..."

leaveGame :: GameName -> RoutedNomyxServer Response
leaveGame gn = do
   pn <- fromJust <$> getPlayerNumber
   webCommand (S.leaveGame gn pn)
   seeOther (showRelURL MainPage) $ toResponse "Redirecting..."

delGame :: GameName -> RoutedNomyxServer Response
delGame gn = do
   webCommand (S.delGame gn)
   seeOther (showRelURL MainPage) $ toResponse "Redirecting..."

nomyxPage :: Maybe GameName -> GameTab -> RoutedNomyxServer Response
nomyxPage mgn tab = do
   mpn <- getPlayerNumber
   s <- getSession
   let saveDir = _saveDir $ _mSettings $ _multi s
   name <- case mpn of
      Just pn -> liftIO $ Profile.getPlayerName pn s
      Nothing -> return "Guest"
   let gn = maybe (_gameName $ _game $ _loggedGame $ head $ _gameInfos $ _multi s) id mgn
   m <- viewMulti mpn saveDir tab gn s
   title <- titleBar name gn
   mainPage' "Nomyx: the game where you can change the rules"
            title
            False
            (H.div $ m)

titleBar :: PlayerName -> GameName -> RoutedNomyxServer Html
titleBar name gn = ok $ table ! A.id "headerTitle" $ tr $ do
   let linkLogin = showRelURL Login 
   let linkHome = showRelURL MainPage
   let linkNewGame = showRelURL $ GamesPage gn
   td $ do
      H.a "Nomyx:  " ! (href $ toValue linkHome)
      H.a ! A.id "gameButton" ! (href $ toValue linkNewGame) $ do
      (fromString gn) 
   td ! A.style "text-align:right;" $ H.a ! (href $ toValue linkLogin) $ do
      (fromString name)
      img ! src "/static/pictures/person.png" ! A.style "vertical-align:middle;" 


nomyxSite :: WebSession -> Site PlayerCommand (ServerPartT IO Response)
nomyxSite ws = setDefault MainPage $ mkSitePI $ (\a b -> evalStateT (runRouteT (catchRouteError . routedNomyxCommands) a b) ws)

routedNomyxCommands :: PlayerCommand -> RoutedNomyxServer Response
-- Authentication and login
routedNomyxCommands (Auth auth)          = authenticate auth
routedNomyxCommands Login                = loginPage
routedNomyxCommands Logout               = logout
routedNomyxCommands ResetPassword        = resetPasswordPage
routedNomyxCommands ChangePassword       = changePasswordPanel
routedNomyxCommands OpenIdRealm          = openIdRealmPanel
routedNomyxCommands PostAuth             = postAuthenticate
-- Game menu
routedNomyxCommands (Menu tab game)      = nomyxPage (Just game) tab
routedNomyxCommands MainPage             = nomyxPage Nothing Home
-- Game management
routedNomyxCommands (JoinGame game)      = joinGame          game
routedNomyxCommands (LeaveGame game)     = leaveGame         game
routedNomyxCommands (DelGame game)       = delGame           game
routedNomyxCommands (GamesPage game)     = newGamePage       game
routedNomyxCommands SubmitNewGame        = newGamePost
-- Game actions
routedNomyxCommands (DoInput is en game) = newInput' is en game
routedNomyxCommands (SubmitRule game)    = submitRuleTemplatePost game
-- Templates
routedNomyxCommands (NewRuleTemplate game)    = newRuleTemplate game
routedNomyxCommands (DelRuleTemplate game rn) = delRuleTemplate game rn
-- Modules
routedNomyxCommands (NewModule game)     = newModule game
--Settings
routedNomyxCommands Advanced             = advanced
routedNomyxCommands (SubmitPlayAs game)  = newPlayAs         game
routedNomyxCommands SubmitAdminPass      = newAdminPass
routedNomyxCommands SubmitSettings       = newSettings
routedNomyxCommands SaveFilePage         = saveFilePage
-- Misc
routedNomyxCommands NomyxJS              = ok $ toResponse nomyxJS

launchWebServer :: TVar Session -> Network -> IO ()
launchWebServer tv net = do
   putStrLn $ "Starting web server...\nTo connect, drive your browser to \"" ++ nomyxURL net ++ "/Nomyx\""
   s <- liftIO $ atomically $ readTVar tv
   let set = _mSettings $ _multi s
   let conf = nullConf {HS.port = T._port net}
   docdir <- liftIO getDocDir
   auth <- Auth.launchAuth (_saveDir set)
   simpleHTTP conf $ server (WebSession tv auth) set net docdir


--serving Nomyx web page as well as data from this package and the language library package
server :: WebSession -> Settings -> Network -> String -> ServerPartT IO Response
server ws set net docdir = mconcat [
    serveDirectory EnableBrowsing [] (_saveDir set),
    serveDirectory EnableBrowsing [] docdir,
    serveDirectory EnableBrowsing [] (_webDir set),
    serveDirectory EnableBrowsing [] (_sourceDir set),
    do decodeBody (defaultBodyPolicy "/tmp/" 102400 4096 4096)
       html <- implSite (pack $ nomyxURL net) "/Nomyx" (nomyxSite ws)
       return $ toResponse html]

catchRouteError :: RoutedNomyxServer Response -> RoutedNomyxServer Response
catchRouteError page = page `catchError` const backToLogin where

backToLogin :: RoutedNomyxServer Response
backToLogin = toResponse <$> seeOther (showRelURL Login) ("Redirecting..." :: String)

getDocDir :: IO FilePath
getDocDir = do
   datadir <- getDataDir
   let (as, _:bs) = break (== "share") $ splitDirectories datadir
   return $ joinPath $ as ++ ["share", "doc"] ++ bs