I'm working on a board game where each team must submit an order each turn. To prevent abuse, I'm trying to make a login page, where you can select a team, give the team's password, and continue to the next page.
I'm using Haskell, using the resources provided here, specifically the "Getting user input" section.
Relevant documentation: Network.CGI, Text.XHtml
Imports, relevant data/types, and page function:
import Network.CGI
import Text.XHtml
data Team = Team
{teamID :: Int,
teamName :: String} deriving Eq
type Lang = Int
type Teams = [Team]
page :: String -> Html -> Html
page t b = header << thetitle << t +++ body << b
I have the following loginPage function:
loginPage :: Lang -> Teams -> Html
loginPage lang teams = page (["Lépés Bejelentkezés", "Turn Login"] !! lang) $
form ! [method "post"] <<
-- [paragraph << (["Csapat: ", "Team: "] !! lang +++ (select ! [name "teamID"] << teamOpts)), -- Generated version, does not work
{- -}
[paragraph << (["Csapat: ", "Team: "] !! lang +++
(select ! [name "teamID"] <<
[option ! [value "0"] << "Anglia", option ! [value "1"] << "Franciaország"])), --} -- Manually typed version, works perfectly
paragraph << (["Jelszó: ", "Password: "] !! lang +++ password "password"),
submit "" (["Tovább", "Next"] !! lang) ]
where
teamOpts = map (\t -> option ! [value . show $ teamID t] << teamName t) $ teams
The commented line uses teamOpts to generate a list of options, and put them in a select tag, with name "teamID". In the currently uncommented lines, I wrote (part of) the list I expect when calling teamOpts.
In ghci, both methods produce the exact same HTML. Yet, when calling this program on my webserver (Apache on Rocky Linux), I get the following outputs:
- Generated:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
><head
><title
>Lépés Bejelentkezés</t
- Manually written:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
><head
><title
>Lépés Bejelentkezés</title
></head
><body
><form method="post"
><p
>Csapat: <select name="teamID"
><option value="0"
>Anglia</option
><option value="1"
>Franciaország</option
></select
></p
><p
>Jelszó: <input type="password" name="password" id="password"
/></p
><input type="submit" value="Tovább"
/></form
></body
></html
>
As you can see, the generated version simply terminates before even finishing the title tag.
When running the script on the command line (on the webserver), I get the expected outcome (same as Manually written), with the CGI Header: Content-type: text/html; charset=ISO-8859-1 .
I've also set it to Content-type: text/html; charset=UTF-8, but the same problem persists.
Other things I tried:
Using (teams
seq) before creating the list, and other methods of forcing evaluation (Usually the program terminates after returning<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/Trying to debug using a simpler generator: (Works perfectli in cli, but not when loading it, returning up to </t)
showTeams :: Teams -> Html
showTeams teams = page "Teams" $
paragraph << (concat . map (\t -> teamName t ++ ", ") $ teams)
Trying to use strict versions of IO functions (as recommended here). Worked in cli, but gave
hGetContents: invalid argument (invalid byte sequence)errors over the web.Making sure everything in the directory is owned by apache:apache
The necessary code (newUnitsPage, cgiMain, and main):
newUnitsPage :: Lang -> Teams -> Units -> Maybe String -> Maybe String -> Html
newUnitsPage lang teams units tid passwd = page (["Új egységek", "New units"] !! lang) body
where
body = paragraph << "PLACEHOLDER" +++ paragraph << fromJust tid +++ paragraph << fromJust passwd
cgiMain = do
-- General setup
liftIO $ hSetEncoding stdin utf8 -- This doesn't change anything either
-- (What I use) {- -
paths' <- liftIO $ listDirectory "./"
let mapPaths = sort $ filter (=~ "\\.hmap$") paths'
hmap <- liftIO $ getNewestMap mapPaths
let teams = fetchTeams hmap --}
{- For your convenience:
Write the following to a file named "test.hmap":
Team {teamID = 0, teamName = "Anglia"}
Team {teamID = 1, teamName = "Franciaország"}
-}
test <- readFile "test.hmap" -- Pretty sure this is where it all goes wrong, but strict reading (Sysem.IO.Strict) does not fix it
let teams = map (\line -> read line :: Team) . lines $ test
-- Defaults to 0 (Hungarian)
mlang <- getInput "lang"
let lang = maybe 0 (\l -> if l `elem` ["1", "en"] then 1 else 0) mlang
-- All Inputs
-- Authentication
tid <- getInput "teamID"
password <- getInput "password"
newUnitOrders <- getInput "newUnitOrders" -- This is for the next page, not yet implemented, since login doesn't work yet.
-- Number coding for which form to show - method to show certain form based on what inputs exist
let code = fromJust $ foldM (\lastCode (mInput, code) -> if isNothing mInput then Just lastCode else Just code)
0 -- If username / password is not supplied, be on login page
[(tid,1),(password,1), -- If newUnitOrders are not supplied, be on newUnit page
(newUnitOrders,2)] -- Etc.
-- The html output
let pages =
[loginPage lang teams,
-- [showTeams teams,
newUnitsPage lang teams units tid password]
setHeader "Content-type" "text/html; charset=UTF-8" -- Optional
output . renderHtml $ pages !! code
main = runCGI $ handleErrors cgiMain
I've checked over and over in the documentation, and I've found no indication of what is going wrong.
Thanks for any help!