* almost done moving to Hamlet from Blaze

This commit is contained in:
Vincent Ambo 2012-04-25 20:22:45 +02:00
parent e28c43f018
commit 8a750bd133

View file

@ -1,25 +1,20 @@
{-# LANGUAGE OverloadedStrings, ScopedTypeVariables, DeriveDataTypeable, RecordWildCards #-}
{-# LANGUAGE OverloadedStrings, ScopedTypeVariables, DeriveDataTypeable, QuasiQuotes, RecordWildCards #-}
module Blog where
import Control.Monad (when, unless)
import Data.Data (Data, Typeable)
import Data.List (intersperse)
import Data.Monoid (mempty)
import Data.Text (Text)
import qualified Data.Text as T
import Data.Time
import Network.Captcha.ReCaptcha
import System.Locale (defaultTimeLocale)
import Text.Blaze (toValue, preEscapedText, preEscapedString)
import Text.Blaze.Html5 (Html, (!), a, form, input, p, toHtml, label)
import Text.Blaze.Html5.Attributes (action, enctype, href, name, size, type_, value)
import qualified Text.Blaze.Html5 as H
import qualified Text.Blaze.Html5.Attributes as A
import Text.XHtml.Strict (showHtmlFragment)
import Control.Monad (when, unless)
import Data.Data (Data, Typeable)
import Data.List (intersperse)
import Data.Monoid (mempty)
import Data.Text (Text, append, pack, empty)
import Data.Time
import Network.Captcha.ReCaptcha
import System.Locale (defaultTimeLocale)
import Text.Hamlet
import Locales
import BlogDB
import Locales
import BlogDB
import qualified Data.Text as T
-- custom list functions
intersperse' :: a -> [a] -> [a]
@ -28,21 +23,12 @@ intersperse' sep l = sep : intersperse sep l
replace :: Eq a => a -> a -> [a] -> [a]
replace x y = map (\z -> if z == x then y else z)
show' :: Show a => a -> Text
show' = pack . show
data BlogURL = BlogURL
-- javascript and others
captcha :: Html
captcha = H.div ! A.class_ "cCaptcha" $
do H.script ! A.src "http://api.recaptcha.net/challenge?k=6LfQXccSAAAAAIjKm26XlFnBMAgvaKlOAjVWEEnM" ! A.type_ "text/javascript" $ ""
H.noscript $ H.iframe ! A.src "http://api.recaptcha.net/noscript?k=6LfQXccSAAAAAIjKm26XlFnBMAgvaKlOAjVWEEnM" ! A.height "300" !
A.width "500" ! A.seamless "" $ do
H.br
H.textarea ! A.name "recaptcha_challenge_field" ! A.rows "3" ! A.cols "40" $ ""
H.input ! A.type_ "hidden" ! A.name "recaptcha_response_field" ! A.value "manual_challenge"
captchaOptions :: BlogLang -> Html
captchaOptions lang = H.script ! A.type_ "text/javascript" $ toHtml $
T.concat ["var RecaptchaOptions = { theme: 'clean', lang: '", showLangText lang, "'};"]
analytics :: Text
analytics = T.pack $ unlines ["<script type=\"text/javascript\">"
," var _gaq = _gaq || [];"
@ -56,240 +42,279 @@ analytics = T.pack $ unlines ["<script type=\"text/javascript\">"
,"</script>"]
-- blog HTML
blogTemplate :: BlogLang -> Text -> Html -> Html
blogTemplate lang t_append body = H.docTypeHtml $ do --add body
H.head $ do
H.title $ (toHtml $ blogTitle lang t_append)
H.link ! A.rel "alternate" ! A.type_ "application/rss+xml" ! A.title "RSS-Feed" ! A.href (toValue feedURL)
H.link ! A.rel "stylesheet" ! A.type_ "text/css" ! A.href "/static/blogv33.css" ! A.media "all"
--H.link ! A.rel "stylesheet" ! A.type_ "text/css" ! A.href "/res/blogstyle.css" ! A.media "all"
H.meta ! A.httpEquiv "content-type" ! A.content "text/html;charset=UTF-8"
--H.style ! A.type_ "text/css" ! A.title "iOS iMessage" ! A.media "screen and (max-device-width: 1024px)" $ "#cosx{display:none;}"
preEscapedText analytics
H.body $ do
H.div ! A.class_ "header" $ do
H.a ! A.class_ "btitle" ! A.href (toValue $ "/" ++ show lang) $
toHtml $ blogTitle lang ""
H.p ! A.style "clear: both;" $ do
H.span ! A.class_ "contacts" ! A.id "cosx" $ contactInfo iMessage
-- H.span ! A.id "cios" ! A.style "display:none;" $ H.b $ contactInfo "sms:tazjin@me.com"
H.span ! A.class_ "righttext" $ preEscapedText $ rightText lang
H.div ! A.class_ "middle" $ do
body
H.div ! A.class_ "footer" $ do
showFooter lang $ T.pack version
H.div ! A.class_ "centerbox" $
H.span ! A.style "font-size: 17px; font-family: Helvetica;" $ "ಠ_ಠ"
--H.img ! A.src "http://cl.ly/F9m4/idiots.png" ! A.alt ""
where
contactInfo (imu :: Text) = do
toHtml $ contactText lang
H.a ! A.class_ "link" ! A.href (toValue mailTo) $ "Mail"
", "
H.a ! A.class_ "link" ! A.href (toValue twitter) ! A.target "_blank" $ "Twitter"
toHtml $ orText lang
H.a ! A.class_ "link" ! A.href (toValue imu) ! A.target "_blank" $ "iMessage"
"."
feedURL = "/" ++ show lang ++ "/rss.xml"
renderEntries :: Bool -> [Entry] -> Text -> Maybe Html -> Html
renderEntries showAll entries topText footerLinks = do
H.span ! A.class_ "innerTitle" $ toHtml topText
H.div ! A.class_ "innerContainer" $ do
H.ul ! A.style "max-width: 57em;" $ if' showAll
(mapM_ showEntry entries)
(mapM_ showEntry $ take 6 entries)
getFooterLinks footerLinks
where
showEntry :: Entry -> Html
showEntry e = H.li $ do
entryLink e $ T.pack $ show(length $ comments e)
preEscapedText $ T.append " " $ btext e
when ( mtext e /= T.empty ) $
H.p $ entryLink e $ readMore $ lang e
unless ( mtext e /= T.empty ) $
preEscapedText "<br>&nbsp;"
entryLink :: Entry -> Text -> Html
entryLink e s = H.a ! A.href (toValue $ concat $ intersperse' "/" $ linkElems e) $
toHtml (T.concat ["[", s, "]"])
linkElems e = [show(lang e), show $ entryId e]
getFooterLinks (Just h) = h
getFooterLinks Nothing = mempty
renderEntry :: Entry -> Html
renderEntry (Entry{..}) = do
H.span ! A.class_ "innerTitle" $ toHtml $ title
H.span ! A.class_ "righttext" $ H.i $ toHtml $ woText
H.div ! A.class_ "innerContainer" $ do
H.article $ H.ul ! A.style "max-width: 57em;" $ H.li $ do
preEscapedText $ btext
H.p $ preEscapedText $ mtext
H.div ! A.class_ "innerBoxComments" $ do
H.div ! A.class_ "cHead" $ toHtml $ cHead lang -- ! A.style "font-size:large;font-weight:bold;"
H.ul ! A.style "max-width: 57em;" $ renderComments comments lang
renderCommentBox lang entryId
where
woText = flip T.append author $ T.pack $ (formatTime defaultTimeLocale (eTimeFormat lang) edate)
renderCommentBox :: BlogLang -> EntryId -> Html
renderCommentBox cLang cId = do
H.div ! A.class_ "cHead" $ toHtml $ cwHead cLang
captchaOptions cLang
H.form ! A.method "POST" ! A.action (toValue $ "/" ++ (show cLang) ++ "/postcomment/" ++ show cId) $ do
H.p $ H.input ! A.name "cname" ! A.placeholder "Name" ! A.class_ "cInput"
H.p $ H.label $ H.textarea ! A.name "ctext" ! A.cols "50" ! A.rows "13" ! A.class_ "cInput" !
A.placeholder (toValue $ cTextPlaceholder cLang) $ mempty
-- H.p $ H.label $ captcha
H.p $ H.input ! A.class_ "cInput" ! A.style "width: 120px;" ! A.type_ "submit" ! A.value (toValue $ cSend cLang)
renderComments :: [Comment] -> BlogLang -> Html
renderComments [] lang = H.li $ toHtml $ noComments lang
renderComments comments lang = mapM_ showComment comments
where
showComment :: Comment -> Html
showComment (Comment{..}) = H.li $ do
H.i $ toHtml $ T.append cauthor ": "
preEscapedText ctext
H.p ! A.class_ "tt" $ toHtml $ timeString cdate
timeString t = formatTime defaultTimeLocale (cTimeFormat lang) t
showLinks :: Maybe Int -> BlogLang -> Html
showLinks (Just i) lang
| ( i > 1) = H.div ! A.class_ "centerbox" $ do
H.a ! A.href (toValue $ "/" ++ show lang ++ "/?page=" ++ show (i+1)) $
toHtml $ backText lang
toHtml (" -- " :: Text)
H.a ! A.href (toValue $ "/" ++ show lang ++ "/?page=" ++ show (i-1)) $
toHtml $ nextText lang
| ( i <= 1 ) = showLinks Nothing lang
showLinks Nothing lang = H.div ! A.class_ "centerbox" $
H.a ! A.href (toValue $ "/" ++ show lang ++ "/?page=2") $
toHtml $ backText lang
blogTemplate lang t_append body = [shamlet|
$doctype 5
<head>
<title>#{blogTitle lang t_append}
<link rel="stylesheet" type="text/css" href="/static/blogv33.css" media="all">
<link rel="alternate" type="application/rss+xml" title="RSS-Feed" href=#{rssUrl}>
<meta http-equiv="content-type" content="text/html;charset=UTF-8">
#{analytics}
<body>
<div class="header">
<a class="btitle" href=#{append "/" (show' lang)}>#{blogTitle lang empty}
<p style="clear: both;">
<span class="contacts" id="cosx">#{contactInfo iMessage}
<span class="righttext">#{rightText lang}
<div class="middle">
#{body}
<div class="footer">
#{showFooter lang $ pack version}
<div class="centerbox">
<span style="font-size:17px;font-family:Helvetica;">_ಠ
|]
where
rssUrl = T.concat ["/", show' lang, "/rss.xml"]
contactInfo imu = [shamlet|
#{contactText lang}
<a class="link" href=#{mailTo}>Mail
, #
<a class="link" href=#{twitter} target="_blank">Twitter
#{orText lang}
<a class="link" href=#{imu} target="_blank">iMessage
.
|]
showFooter :: BlogLang -> Text -> Html
showFooter l v = H.div ! A.class_ "rightbox" ! A.style "text-align:right;" $ do
toHtml ("Proudly made with " :: Text)
H.a ! A.class_ "link" ! A.href "http://haskell.org" $ "Haskell"
toHtml (", " :: Text)
H.a ! A.class_ "link" ! A.href "http://hackage.haskell.org/package/acid-state-0.6.3" $ "Acid-State"
toHtml (" and without PHP, Java, Perl, MySQL and Python." :: Text)
H.br
H.a ! A.class_ "link" ! A.href (toValue repoURL) $ toHtml $ T.append "Version " v
preEscapedText "&nbsp;"
H.a ! A.class_ "link" ! A.href "/notice" $ toHtml $ noticeText l
showFooter l v = [shamlet|
<div class="rightbox" style="text-align:right;">
Proudly made with #
<a class="link" href="http://haskell.org">Haskell
, #
<a class="link" href="http://hackage.haskell.org/package/acid-state-0.6.3">Acid-State
/ and without PHP, Java, Perl, MySQL and Python.
<p>
<a class="link" href=#{repoURL}>#{append "Version " v}
&nbsp;
<a class="link" href="/notice">#{noticeText l}
|]
renderEntries :: Bool -> [Entry] -> Text -> Maybe Html -> Html
renderEntries showAll entries topText footerLinks = [shamlet|
<span class="innerTitle">#{topText}
<div class="innerContainer">
<ul style="max-width:57em;">
$forall entry <- elist
<li>
<a href=#{linkElems entry}>#{linkText $ length $ comments entry}
#{append " " $ btext entry}
$if ((/=) (mtext entry) empty)
<p><a href=#{linkElems entry}>#{readMore $ lang entry}
$else
<br>&nbsp;
$maybe links <- footerLinks
#{links}
|]
where
elist = if' showAll entries (take 6 entries)
linkElems Entry{..} = concat $ intersperse' "/" [show lang, show entryId]
linkText n = T.concat ["[", show' n, "]"]
showLinks :: Maybe Int -> BlogLang -> Html
showLinks (Just i) lang = [shamlet|
$if ((>) i 1)
<div class="centerbox">
<a href=#{nLink $ succ i}>#{backText lang}
/ -- #
<a href=#{nLink $ pred i}>#{nextText lang}
$elseif ((<=) i 1)
#{showLinks Nothing lang}
|]
where
nLink page = T.concat ["/", show' lang, "/?page=", show' page]
showLinks Nothing lang = [shamlet|
<div class="centerbox">
<a href=#{nLink}>#{backText lang}
|]
where
nLink = T.concat ["/", show' lang, "/?page=2"]
renderEntry :: Entry -> Html
renderEntry Entry{..} = [shamlet|
<span class="innerTitle">#{title}
<span class="righttext"><i>#{woText}
<div class="innerContainer">
<article>
<ul style="max-width:57em;">
<li>
#{btext}
<p>#{mtext}
<div class="innerBoxComments">
<div class="cHead">#{cHead lang}
<ul style="max-width:57em;">#{renderComments comments lang}
#{renderCommentBox lang entryId}
|]
where
woText = flip T.append author $ T.pack $ (formatTime defaultTimeLocale (eTimeFormat lang) edate)
renderComments :: [Comment] -> BlogLang -> Html
renderComments [] lang = [shamlet|<li>#{noComments lang}|]
renderComments comments lang = [shamlet|
$forall comment <- comments
<li>
<i>#{append (cauthor comment) ": "}
#{ctext comment}
<p class="tt">#{timeString $ cdate comment}
|]
where
timeString = formatTime defaultTimeLocale (cTimeFormat lang)
renderCommentBox :: BlogLang -> EntryId -> Html
renderCommentBox cLang cId = [shamlet|
<div class="cHead">#{cwHead cLang}
<form method="POST" action=#{aLink}>
<p><input name="cname" placeholder="Name" class="cInput">
<p>
<label>
<textarea name="ctext" cols="50" rows="13" class="cInput" placeholder=#{cTextPlaceholder cLang}>
<p><input class="cInput" style="width:120px;" type="submit" value=#{cSend cLang}>
|]
where
aLink = T.concat ["/", show' cLang, "/postcomment", show' cId]
showSiteNotice :: Html
showSiteNotice = H.docTypeHtml $ do
H.title $ "Impressum"
H.h2 $ preEscapedText "Impressum und <a alt=\"Verantwortlich im Sinne des Presserechtes\">ViSdP</a>"
H.i $ "[German law demands this]"
H.br
H.p $ do
toHtml ("Vincent Ambo" :: Text)
H.br
toHtml ("Benfleetstr. 8" :: Text)
H.br
toHtml ("50858 Köln" :: Text)
H.p $ H.a ! A.href "/" ! A.style "color:black" $ "Back"
showSiteNotice = [shamlet|
$doctype 5
<head>
<title>Impressum
<body>
<h2>
Impressum und #
<a alt="Verantwortlich im Sinne des Presserechtes">ViSdP
<i>[German law demands this]
<br>
<p>
Vincent Ambo
<br>
Benfleetstr. 8
<br>
50858 Köln
<p><a href="/" style="color:black;">Back
|]
{- Administration pages -}
adminTemplate :: Text -> Html -> Html
adminTemplate title body = H.docTypeHtml $ do
H.head $ do
H.link ! A.rel "stylesheet" ! A.type_ "text/css" ! A.href "/static/admin.css" ! A.media "all"
H.meta ! A.httpEquiv "content-type" ! A.content "text/html;charset=UTF-8"
H.title $ toHtml $ T.append "TazBlog Admin: " title
H.body
body
adminTemplate title body = [shamlet|
$doctype 5
<head>
<link rel="stylesheet" type="text/css" href="/static/admin.css" media="all">
<meta http-equiv="content-type" content="text/html;charset=UTF-8">
<title>#{append "TazBlog Admin: " title}
<body>
#{body}
|]
adminLogin :: Html
adminLogin = adminTemplate "Login" $
H.div ! A.class_ "loginBox" $ do
H.div ! A.class_ "loginBoxTop" $ "TazBlog Admin: Login"
H.div ! A.class_ "loginBoxMiddle" $ H.form ! A.action "/dologin" ! A.method "post" $ do
H.p $ "Account ID"
H.p $ H.input ! A.type_ "text" ! A.style "font-size: 2;"
! A.name "account" -- ! A.value "tazjin" ! A.readonly "1"
H.p $ "Passwort"
H.p $ H.input ! A.type_ "password" ! A.style "font-size: 2;" ! A.name "password"
H.p $ H.input ! A.alt "Anmelden" ! A.type_ "image" ! A.src "/static/signin.gif"
adminLogin = adminTemplate "Login" $ [shamlet|
<div class="loginBox">
<div class="loginBoxTop">TazBlog Admin: Login
<div class="loginBoxMiddle">
<form action="/dologin" method="POST">
<p>Account ID
<p><input type="text" style="font-size:2;" name="account" value="tazjin" readonly="1">
<p>Passwort
<p><input type="password" style="font-size:2;" name="password">
<p><input alt="Anmelden" type="image" src="/static/signin.gif">
|]
adminIndex :: Text -> Html
adminIndex sUser = adminTemplate "Index" $
H.div ! A.style "float: center;" $
H.form ! A.action "/admin/postentry" ! A.method "POST" $ do
H.table $ do
H.tr $ do H.td $ "Titel:"
H.td $ H.input ! A.type_ "text" ! A.name "title"
H.tr $ do H.td $ "Sprache:"
H.td $ H.select ! A.name "lang" $ do
H.option ! A.value "de" $ "Deutsch"
H.option ! A.value "en" $ "Englisch"
H.tr $ do H.td ! A.style "vertical-align: top;" $ "Text:"
H.td $ H.textarea ! A.name "btext" ! A.cols "100" ! A.rows "15" $ mempty
H.tr $ do H.td ! A.style "vertical-align: top;" $ "Mehr Text:"
H.td $ H.textarea ! A.name "mtext" ! A.cols "100" ! A.rows "15" $ mempty
H.input ! A.type_ "hidden" ! A.name "author" ! A.value (toValue sUser)
H.input ! A.style "margin-left: 20px" ! A.type_ "submit" ! A.value "Absenden"
adminFooter
adminIndex sUser = adminTemplate "Index" $ [shamlet|
<div style="float:center;">
<form action="/admin/postentry" method="POST">
<table>
<tr>
<thead><td>Titel:
<td><input type="text" name="title">
<tr>
<thead><td>Sprache:
<td><select name="lang">
<option value="de">Deutsch
<option value="en">Englisch
<tr>
<thead><td>Text:
<td><textarea name="btext" cols="100" rows="15">
<tr>
<thead><td style="vertical-align:top;">Mehr Text:
<td><textarea name="mtext" cols="100" rows="15">
<input type="hidden" name="author" value=#{sUser}>
<input style="margin-left:20px;" type="submit" value="Absenden">
#{adminFooter}
|]
adminFooter :: Html
adminFooter = H.p $ do
preEscapedText "<a href=/>Startseite</a> -- Entrylist: <a href=/admin/entrylist/de>DE</a>"
preEscapedText " & <a href=/admin/entrylist/en>EN</a> -- <a href=#>Backup</a> (NYI)"
adminFooter = [shamlet|
<a href="/">Startseite
/ -- Entrylist: #
<a href="/admin/entrylist/de">DE
/ & #
<a href="/admin/entrylist/en">EN
/ -- #
<a href="#">Backup
/ (NYI)
|]
adminEntryList :: [Entry] -> Html
adminEntryList entries = adminTemplate "Entrylist" $
H.div ! A.style "float: center;" $ do
H.table $ do
mapM_ showEntryItem entries
adminFooter
where
showEntryItem :: Entry -> Html
showEntryItem (Entry{..}) = H.tr $ do
H.td $ H.a ! A.href (toValue $ "/admin/edit/" ++ show entryId) $ toHtml title
H.td $ toHtml $ formatTime defaultTimeLocale "[On %D at %H:%M]" edate
adminEntryList entries = adminTemplate "EntryList" $ [shamlet|
<div style="float: center;">
<table>
$forall entry <- entries
<tr>
<td><a href=#{append "/admin/edit" (show' $ entryId entry)}>#{title entry}
<td>#{formatPostDate $ edate entry}
|]
where
formatPostDate = formatTime defaultTimeLocale "[On %D at %H:%M]"
editPage :: Entry -> Html
editPage (Entry{..}) = adminTemplate "Index" $
H.div ! A.style "float: center;" $
H.form ! A.action "/admin/updateentry" ! A.method "POST" $ do
H.table $ do
H.tr $ do H.td $ "Titel:"
H.td $ H.input ! A.type_ "text" ! A.name "title" ! A.value (toValue title)
H.tr $ do H.td ! A.style "vertical-align: top;" $ "Text:"
H.td $ H.textarea ! A.name "btext" ! A.cols "100" ! A.rows "15" $ toHtml btext
H.tr $ do H.td ! A.style "vertical-align: top;" $ "Mehr Text:"
H.td $ H.textarea ! A.name "mtext" ! A.cols "100" ! A.rows "15" $ toHtml mtext
H.input ! A.type_ "hidden" ! A.name "eid" ! A.value (toValue $ unEntryId entryId)
H.input ! A.style "margin-left: 20px" ! A.type_ "submit" ! A.value "Absenden"
H.div ! A.class_ "editComments" $ editComments comments entryId
H.p $ do preEscapedText "<a href=/>Startseite</a> -- Entrylist: <a href=/admin/entrylist/de>DE</a>"
preEscapedText " & <a href=/admin/entrylist/en>EN</a> -- <a href=#>Backup</a> (NYI)"
editPage (Entry{..}) = adminTemplate "Index" $ [shamlet|
<div style="float:center;">
<form action="/admin/updateentry" method="POST">
<table>
<tr>
<td>Titel:
<td><input type="text" name="title" value=#{title}>
<tr>
<td style="vertical-align:top;">Text:
<td><textarea name="btext" cols="100" rows="15">#{btext}
<tr>
<td style="vertical-align:top;">Mehr Text:
<td><textarea name="mtext" cols="100" rows="15">#{mtext}
<input type="hidden" name="eid" value=#{unEntryId entryId}>
<input type="submit" style="margin-left:20px;" value="Absenden">
<div class="editComments">#{editComments comments entryId}
<p>#{adminFooter}
|]
editComments :: [Comment] -> EntryId -> Html
editComments clist eId = H.table $ mapM_ editComment clist
where
editComment (Comment{..}) = H.tr $ do H.td $ toHtml cauthor
H.td $ toHtml $ formatTime defaultTimeLocale "%c" cdate
H.td $ cDeleteLink cdate
cDeleteLink cdate = H.a ! A.href (toValue $ "/admin/cdelete/" ++ show eId
++ formatTime defaultTimeLocale "/%s%Q" cdate) $ "Löschen"
editComments comments eId = [shamlet|
<table>
$forall c <- comments
<tr>
<td>#{cauthor c}
<td>#{cPostTime $ cdate c}
<tr>
<td><a href=#{cDeleteLink $ cdate c}>Löschen
|]
where
cPostTime = formatTime defaultTimeLocale "%c"
cDeleteLink cd = concat ["/admin/cdelete", show eId, formatTime defaultTimeLocale "/%s%Q" cd]
commentDeleted :: EntryId -> Html
commentDeleted eId = adminTemplate "Kommentar gelöscht" $ do
H.div $ "Der Kommentar wurde gelöscht."
H.br
H.a ! A.href (toValue $ "/de/" ++ show eId) $ "Eintrag ansehen | "
H.a ! A.href (toValue $ "/admin/edit/" ++ show eId) $ "Eintrag bearbeiten"
commentDeleted eId = adminTemplate "Kommentar gelöscht" $ [shamlet|
<div>Der Kommentar wurde gelöscht.
<br>
<a href=#{append "/de/" $ show' eId}>Eintrag ansehen | #
<a href=#{append "/admin/edit/" $ show' eId}>Eintrag bearbeiten
|]
-- Error pages
showError :: BlogError -> BlogLang -> Html
showError NotFound l = blogTemplate l (T.append ": " $ notFoundTitle l) $ do
H.span ! A.class_ "innerTitle" $ toHtml $ notFoundTitle l
H.div ! A.class_ "innerContainer" $ do
H.p ! A.class_ "notFoundFace" $ toHtml (":'(" :: Text)
H.p ! A.class_ "notFoundText" $ toHtml $ notFoundText l
showError NotFound l = blogTemplate l (T.append ": " $ notFoundTitle l) $ [shamlet|
<span class="innerTitle">#{notFoundTitle l}
<div class="innerTitle">
<p class="notFoundFace">:(
<p class="notFoundText">#{notFoundText l}
|]