packages feed

happs-tutorial-0.4: src/Misc.hs

{-# OPTIONS -fglasgow-exts -fno-monomorphism-restriction #-}
module Misc where 

import HAppS.Server 
import qualified Data.ByteString.Char8 as B
import qualified Data.ByteString.Lazy.Char8 as L
import Control.Monad.Trans
import Data.List
import qualified Data.Set as S
import Data.Digest.Pure.MD5
import Debug.Trace
import Text.PrettyPrint as PP
import Control.Applicative 
import System.Directory
import System.FilePath

import Text.StringTemplate
import Data.Monoid
import Control.Monad.Reader
import Data.Char -- for rot13, which we shouldn't be using anyway

newtype HtmlString = HtmlString String
instance ToMessage HtmlString where
  toContentType _ = B.pack "text/html"
  toMessage (HtmlString s) = L.pack s 

exactdir staticPath = spsIf (\rq -> rqURL rq == staticPath) 

spsIf :: (Monad m) => (Request -> Bool) -> [ServerPartT m a] -> ServerPartT m a
spsIf p sps = withRequest $ \rq ->
  if p rq
    then unServerPartT (mconcat sps) rq
    else mempty

traceTrue x = trace (show x ++ "\n\n") True
traceIt x = trace (show x ++ "\n\n") x
traceMsg msg x = trace ( "\n\n" ++ msg ++ (show x) ++ "\n\n") x
traceReadableMsg msg x = trace ( "\n\n" ++ msg ++ (show . show $ x) ++ "\n\n") x

pp[] = ( PP.render . vcat . map text . map show )
traceWith f v = trace (f v) v


instance (Monad m) => Monoid (ServerPartT m a)
  where mempty = ServerPartT $ \rq -> noHandle
        mappend a b = ServerPartT $ \rq -> (unServerPartT a rq) `mappend` (unServerPartT b rq) 
instance (Monad m) => Monoid (WebT m a) where
  mempty = noHandle
  mappend a b = WebT $ do a' <- unWebT a
                          case a' of
                              NoHandle -> unWebT b
                              _        -> return a'

-- Chooses a template from an STGroup, or errors if not found.
-- Renders that template uses attrs, and gives the string.
-- if you don't clean and a template k/v pair is repeated, it appears twice.
-- Possibly this should be a fix inside StringTemplate. Tell sclv?
-- what is the expected StringTemplate behavior according to the original program?
--clean = nubBy (\(a1,b1) (a2,b2) -> a1 == a2) . sortBy (\(a1,b1) (a2,b2) -> a1 `compare` a2)
-- but then again, why should a key be repeated twice? maybe showing a repeat is a good thing
-- as it indicates buggy behavior
-- The ToSElem type is probably either String or [String]
--renderTemplateGroup :: (ToSElem a) => STGroup String -> [(String, a)] -> [Char] -> String
renderTemplateGroup :: STGroup String -> [(String, String)] -> [Char] -> String
renderTemplateGroup gr attrs tmpl = 
       maybe ( "template not found: " ++ tmpl )
             ( toString . setManyAttribSafer attrs )
             ( getStringTemplate tmpl gr )





----------------- reading data ----------------
readData :: RqData a -> Request -> Maybe a
readData rqDataReader rq = runReaderT rqDataReader $ (rqInputs rq,rqCookies rq)  
readDataDef :: b -> (a -> b) -> RqData a -> Request -> b
readDataDef def f rqDataReader rq = maybe def f . readData rqDataReader $ rq

dataReaderToWeb :: RqData a -> Request -> WebT IO (Maybe a)
dataReaderToWeb rqDataReader rq = return . readData rqDataReader $ rq
dataReaderToWebDef :: b -> (a -> b) -> RqData a -> Request -> WebT IO b
dataReaderToWebDef def f rqDataReader rq = return . readDataDef def f rqDataReader $ rq

dataReaderToSp :: RqData a -> ServerPartT IO (Maybe a)
dataReaderToSp rqDataReader = withRequest $ \rq -> dataReaderToWeb rqDataReader rq
dataReaderToSpDef :: b -> (a->b) -> RqData a -> ServerPartT IO b
dataReaderToSpDef def f rqDataReader = withRequest $ \rq -> dataReaderToWebDef def f rqDataReader rq

alltrue ps x = foldr g True ps
  where g p b = b && p x
nonetrue ps x = alltrue (map ( not . ) ps) x

pathPartsSp pps f = ServerPartT $ \rq ->
  if rqPaths rq == pps
    then f rq 
    else noHandle

-- Do something when the request has exactly one path segment left.
-- lastPathPartSp :: (Request -> String -> WebT IO Response) -> ServerPartT IO Response
lastPathPartSp0 f = ServerPartT $ \rq ->
  case rqPaths rq of
            [lastpart] -> f rq lastpart
            _ -> noHandle

{-
ifFirstPathPartSp pathpart f = ServerPartT $ \rq ->
  case rqPaths rq of
            (x:xs) -> f rq pathpart
            _ -> noHandle
-}

-- why can't I do this? ask #haskell sometime.
--instance Functor (S.Set a) where
  -- fmap :: (a-> b ) (S.Set a) (S.Set b)
--  fmap f s = S.fromList . map f . S.toList $ s

-- for now just do rot13, but get this working for real. (md5? what do people use?)
scramblepass = show . md5 . L.pack -- map ( chr . (+13) . ord )


-- HStringTemplate modifications -- copy/paste/tweak from HStringTemplate.
-- same as directoryGroup, but throws an error for template names with punctuation

directoryGroupSafer :: (Stringable a) => FilePath -> IO (STGroup a)
directoryGroupSafer path = groupStringTemplates <$>
                      (fmap <$> zip . (map dropExtension)
                       <*> mapM (newSTMP <$$> (readFile . (path </>)))
                           =<< mapM checkTmplName
                           =<< return . filter (not . or . map (=='#') {-naughty emacs backup character-} )
                                        . filter ( (".st" ==) . takeExtension )
                           =<< getDirectoryContents path)
  where checkTmplName t = if  ( badTmplVarName . takeBaseName ) t
                            then fail $ "safeDirectoryGroup, bad template name: " ++ t
                            else return t

(<$$>) :: (Functor f1, Functor f) => (a -> b) -> f (f1 a) -> f (f1 b)
(<$$>) x y = ((<$>) . (<$>)) x y

setManyAttribSafer attrs  st = 
    let mbFoundbadattr = find badTmplVarName . map fst $ attrs
    in  maybe (setManyAttrib attrs st)
              (\mbA -> newSTMP . ("setManyAttribSafer, bad template atr: "++) $ mbA)
              mbFoundbadattr




badTmplVarName t = or . map (not . isAlpha) $ t

tFromTo = fromTo 10 20 [1..1000] == [10..20] 
fromTo fr to xs = take (to-(fr-1)) . drop (fr-1) $ xs