{-# LANGUAGE FlexibleInstances #-}{-# LANGUAGE FlexibleContexts #-}{-# LANGUAGE TypeSynonymInstances #-}{-# LANGUAGE MultiParamTypeClasses #-}{-# LANGUAGE DeriveDataTypeable #-}{-# LANGUAGE Rank2Types #-}{-# LANGUAGE CPP #-}moduleYesod.Content(-- * ContentContent,emptyContent,ToContent(..)-- * Mime types-- ** Data type,ContentType,typeHtml,typePlain,typeJson,typeXml,typeAtom,typeJpeg,typePng,typeGif,typeJavascript,typeCss,typeFlv,typeOgv,typeOctet-- ** File extensions,typeByExt,ext-- * Utilities,simpleContentType-- * Representations,ChooseRep,HasReps(..),defChooseRep-- ** Specific content types,RepHtml(..),RepJson(..),RepHtmlJson(..),RepPlain(..),RepXml(..)-- * Utilities,formatW3,formatRFC1123#if TEST,testSuite#endif)whereimportData.Maybe(mapMaybe)importqualifiedData.ByteStringasBimportqualifiedData.ByteString.LazyasLimportData.Text.Lazy(Text)importqualifiedData.TextasTimportqualifiedNetwork.WaiasWimportData.TimeimportSystem.LocaleimportqualifiedData.Text.EncodingimportqualifiedData.Text.Lazy.Encoding#if TESTimportTest.Framework(testGroup,Test)importTest.Framework.Providers.HUnitimportTest.Framework.Providers.QuickCheck2(testProperty)importTest.HUnithiding(Test)#endiftypeContent=W.ResponseBody-- | Zero-length enumerator.emptyContent::ContentemptyContent=W.ResponseLBSL.empty-- | Anything which can be converted into 'Content'. Most of the time, you will-- want to use the 'ContentEnum' constructor. An easier approach will be to use-- a pre-defined 'toContent' function, such as converting your data into a lazy-- bytestring and then calling 'toContent' on that.classToContentawheretoContent::a->ContentinstanceToContentB.ByteStringwheretoContent=W.ResponseLBS.L.fromChunks.returninstanceToContentL.ByteStringwheretoContent=W.ResponseLBSinstanceToContentT.TextwheretoContent=toContent.Data.Text.Encoding.encodeUtf8instanceToContentTextwheretoContent=W.ResponseLBS.Data.Text.Lazy.Encoding.encodeUtf8instanceToContentStringwheretoContent=toContent.T.pack-- | A function which gives targetted representations of content based on the-- content-types the user accepts.typeChooseRep=[ContentType]-- ^ list of content-types user accepts, ordered by preference->IO(ContentType,Content)-- | Any type which can be converted to representations.classHasRepsawherechooseRep::a->ChooseRep-- | A helper method for generating 'HasReps' instances.---- This function should be given a list of pairs of content type and conversion-- functions. If none of the content types match, the first pair is used.defChooseRep::[(ContentType,a->IOContent)]->a->ChooseRepdefChooseReprepsats=dolet(ct,c)=casemapMaybehelpertsof(x:_)->x[]->caserepsof[]->error"Empty reps to defChooseRep"(x:_)->xc'<-careturn(ct,c')wherehelperct=doc<-lookupctrepsreturn(ct,c)instanceHasRepsChooseRepwherechooseRep=idinstanceHasReps()wherechooseRep=defChooseRep[(typePlain,const$return$toContent"")]instanceHasReps(ContentType,Content)wherechooseRep=const.returninstanceHasReps[(ContentType,Content)]wherechooseRepacts=return$casefilter(\(ct,_)->goct`elem`mapgocts)aof((ct,c):_)->(ct,c)_->caseaof(x:_)->x_->error"chooseRep [(ContentType, Content)] of empty"wherego=simpleContentTypenewtypeRepHtml=RepHtmlContentinstanceHasRepsRepHtmlwherechooseRep(RepHtmlc)_=return(typeHtml,c)newtypeRepJson=RepJsonContentinstanceHasRepsRepJsonwherechooseRep(RepJsonc)_=return(typeJson,c)dataRepHtmlJson=RepHtmlJsonContentContentinstanceHasRepsRepHtmlJsonwherechooseRep(RepHtmlJsonhtmljson)=chooseRep[(typeHtml,html),(typeJson,json)]newtypeRepPlain=RepPlainContentinstanceHasRepsRepPlainwherechooseRep(RepPlainc)_=return(typePlain,c)newtypeRepXml=RepXmlContentinstanceHasRepsRepXmlwherechooseRep(RepXmlc)_=return(typeXml,c)typeContentType=StringtypeHtml::ContentTypetypeHtml="text/html; charset=utf-8"typePlain::ContentTypetypePlain="text/plain; charset=utf-8"typeJson::ContentTypetypeJson="application/json; charset=utf-8"typeXml::ContentTypetypeXml="text/xml"typeAtom::ContentTypetypeAtom="application/atom+xml"typeJpeg::ContentTypetypeJpeg="image/jpeg"typePng::ContentTypetypePng="image/png"typeGif::ContentTypetypeGif="image/gif"typeJavascript::ContentTypetypeJavascript="text/javascript; charset=utf-8"typeCss::ContentTypetypeCss="text/css; charset=utf-8"typeFlv::ContentTypetypeFlv="video/x-flv"typeOgv::ContentTypetypeOgv="video/ogg"typeOctet::ContentTypetypeOctet="application/octet-stream"-- | Removes \"extra\" information at the end of a content type string. In-- particular, removes everything after the semicolon, if present.---- For example, \"text/html; charset=utf-8\" is commonly used to specify the-- character encoding for HTML data. This function would return \"text/html\".simpleContentType::String->StringsimpleContentType=fst.span(/=';')-- | A default extension to mime-type dictionary.typeByExt::[(String,ContentType)]typeByExt=[("jpg",typeJpeg),("jpeg",typeJpeg),("js",typeJavascript),("css",typeCss),("html",typeHtml),("png",typePng),("gif",typeGif),("txt",typePlain),("flv",typeFlv),("ogv",typeOgv)]-- | Get a file extension (everything after last period).ext::String->Stringext=reverse.fst.break(=='.').reverse#if TEST---- TestingtestSuite::TesttestSuite=testGroup"Yesod.Resource"[testProperty"ext"propExt,testCase"typeByExt"caseTypeByExt]propExt::String->BoolpropExts=lets'=filter(/='.')sins'==ext("foobarbaz."++s')caseTypeByExt::AssertioncaseTypeByExt=doJusttypeJavascript@=?lookup(ext"foo.js")typeByExtJusttypeHtml@=?lookup(ext"foo.html")typeByExt#endif-- | Format a 'UTCTime' in W3 format; useful for setting cookies.formatW3::UTCTime->StringformatW3=formatTimedefaultTimeLocale"%FT%X-00:00"-- | Format as per RFC 1123.formatRFC1123::UTCTime->StringformatRFC1123=formatTimedefaultTimeLocale"%a, %d %b %Y %X %Z"