{-# LANGUAGE DeriveDataTypeable, FlexibleContexts, FlexibleInstances, GeneralizedNewtypeDeriving, MultiParamTypeClasses, OverloadedStrings, RecordWildCards, TemplateHaskell, TypeFamilies, TypeSynonymInstances, OverloadedStrings #-} {-# OPTIONS_GHC -F -pgmFhsx2hs #-} module Tiny.Pages ( route ) where import Data.Text (Text) import qualified Data.Text as Text (null, unpack) import qualified Data.Text.Lazy as Lazy (pack, Text) import Data.Time.Clock (getCurrentTime) import HSP (toName) import HSP.XML as HTML (Attribute(MkAttr), pAttrVal, XML(CDATA)) import Happstack.Auth (AuthProfileURL, getUserId, handleAuthProfile, AuthState, ProfileState) import Happstack.Foundation ((++>), (<$>), (<++), Applicative((<*), (<*>)), asContentType, Attr(..), EmbedAsAttr(..), EmbedAsChild(..), errorList, fieldset, form, fromStringLit, Happstack, inputSubmit, inputText, label, li, liftM, method, Method(GET), MonadIO(liftIO), nestURL, notFound, ok, PathInfo(toPathSegments), query, reform, Response, RouteT, seeOtherURL, select, serveFile, showURL, textarea, ToMessage(toResponse), transformEither, transformEitherM, ul, update, whereami, XMLGen(..), XMLGenerator, XMLGenT(XMLGenT), AcidState, HasAcidState(..)) import Text.Blaze.Html (Html) import Text.Blaze.Html.Renderer.Text (renderHtml) import Tiny.Events (GetPasteById(GetPasteById), GetRecentPastes(GetRecentPastes), InsertPaste(InsertPaste)) import Tiny.Route (App, AppForm, Route(..)) import Tiny.Template (appTemplateHSP, appTemplateBlaze) import Tiny.Types (Format(..), Paste(..), PasteId(..), PasteMeta(PasteMeta, format, owner, pasteId, pasted, title)) ------------------------------------------------------------------------------ -- Pages ------------------------------------------------------------------------------ -- | page handler for 'ViewRecent' viewRecentPage :: App Response viewRecentPage = do method GET recent <- query (GetRecentPastes 20 0) case recent of [] -> appTemplateHSP "Recent Pastes" ()

There are no pastes yet.

_ -> appTemplateHSP "Recent Pastes" () $ <%>

Recent Pastes

<% mapM mkTableRow recent %>
id title author date format
where mkTableRow PasteMeta{..} = <% show $ unPasteId pasteId %> <% title %> <% show owner %> <% pasted %> <% show format %> -- | page handler for 'ViewPaste' viewPastePage :: PasteId -> App Response viewPastePage pid = do method GET mPaste <- query (GetPasteById pid) case mPaste of Nothing -> do notFound () appTemplateHSP "Paste not found." () $

Paste <% pid %> could not be found.

(Just (Paste (PasteMeta{..}) paste)) -> do ok () appTemplateHSP (Lazy.pack $ "Paste " ++ (show $ unPasteId pid)) () $
Paste:
<% pid %>
Title:
<% title %>
Author:
<% show owner %>
<% formatPaste format paste %>
-- | convert the paste to HTML. We currently only support 'PlainText', -- but eventually it might do syntax hightlighting, markdown, etc. -- -- Note that we do not have to worry about escaping the txt -- value.. that is done automatically by HSP. formatPaste :: Format -> Text -> App XML formatPaste PlainText txt =
<% txt %>
-- | page handler for 'NewPaste' newPastePage :: App Response newPastePage = do here <- whereami acidAuth <- getAcidState acidProfile <- getAcidState mUserId <- getUserId acidAuth acidProfile case mUserId of Nothing -> appTemplateHSP "Add a Paste" () $ <%>

You Are Not Logged In

(Just _uid) -> appTemplateHSP "Add a Paste" () $ <%>

Add a paste

<% reform (form here) "add" success Nothing pasteForm %> where success :: Paste -> App Response success paste = do pid <- update (InsertPaste paste) seeOtherURL (ViewPaste pid) -- | the 'Form' used for entering a new paste pasteForm :: AppForm Paste pasteForm = (fieldset $ ul $ (,,,) <$> (li $ label title ++> (inputText "" `transformEither` required) <++ errorList) <*> (li $ label nick ++> (inputText "" `transformEither` required) <++ errorList) <*> (li $ label format ++> formatForm) <*> (li $ label
paste
++> errorList ++> (textarea 80 25 "" `transformEither` required)) <* inputSubmit "paste!" ) `transformEitherM` toPaste where formatForm = select [(a, show a) | a <- [minBound .. maxBound]] (== PlainText) toPaste (ttl, nick, fmt, bdy) = do now <- liftIO getCurrentTime acidAuth <- getAcidState acidProfile <- getAcidState Just userId <- getUserId acidAuth acidProfile return $ Right $ (Paste { pasteMeta = PasteMeta { pasteId = PasteId 0 , title = ttl , owner = userId , format = fmt , pasted = now } , paste = bdy }) required txt | Text.null txt = Left "Required" | otherwise = Right txt ------------------------------------------------------------------------------ -- Auth Support Functions ------------------------------------------------------------------------------ -- the key to using happstack-authenticate with HSP is simple. First -- you need to be able to embed Html in your HSP monad like this: -- -- I (Robin Lee Powell) don't know how to fix this warning: -- -- Warning: orphan instance: -- instance (Functor m, Monad m) => EmbedAsChild (RouteT url m) Html -- instance (Functor m, Monad m) => EmbedAsChild (RouteT url m) Html where asChild html = asChild (HTML.CDATA False (renderHtml html)) -- If you want to use your usual app template, then the auth stuff, -- which is in RouteT AuthProfileURL, needs to have a way to render -- your routes inside that context. -- instance (Functor m, Monad m) => EmbedAsAttr (RouteT AuthProfileURL m) (Attr Lazy.Text Route) where asAttr (n := u) = do asAttr $ MkAttr (toName n, pAttrVal (Lazy.pack $ concatMap (((++) "/") . Text.unpack) (toPathSegments u))) ------------------------------------------------------------------------------ -- route ------------------------------------------------------------------------------ -- | the route mapping function route :: Text -> Route -> App Response route baseURL url = case url of -- FIXME: replace the ViewRecent thing here with "go back to -- the last page we were on". - rlpowell (U_AuthProfile authProfileURL) -> do vr <- showURL ViewRecent acidAuth <- getAcidState acidProfile <- getAcidState XMLGenT $ nestURL U_AuthProfile $ handleAuthProfile acidAuth acidProfile (\s -> appTemplateBlaze acidAuth acidProfile (Lazy.pack s)) Nothing (Just baseURL) vr authProfileURL ViewRecent -> viewRecentPage (ViewPaste pid) -> viewPastePage pid NewPaste -> newPastePage CSS -> serveFile (asContentType "text/css") "style.css"