148 lines
5.8 KiB
Haskell
148 lines
5.8 KiB
Haskell
{-# LANGUAGE FlexibleInstances #-}
|
|
{-# LANGUAGE MultiParamTypeClasses #-}
|
|
{-# LANGUAGE OverloadedStrings #-}
|
|
{-# LANGUAGE QuasiQuotes #-}
|
|
{-# LANGUAGE RankNTypes #-}
|
|
{-# LANGUAGE TemplateHaskell #-}
|
|
{-# LANGUAGE TypeFamilies #-}
|
|
{-# OPTIONS_GHC -fno-warn-orphans #-}
|
|
-- | Define the dispatch for a Wiki. You should probably start off by reading
|
|
-- WikiRoutes.
|
|
module Wiki
|
|
( module WikiRoutes
|
|
) where
|
|
|
|
import Control.Applicative ((<$>))
|
|
import Control.Monad (unless)
|
|
import Data.IORef.Lifted (readIORef, atomicModifyIORef)
|
|
import Data.Map (Map)
|
|
import qualified Data.Map as Map
|
|
import Data.Text (Text)
|
|
import WikiRoutes
|
|
import Yesod
|
|
|
|
-- | A subsite needs to be an instance of YesodSubDispatch, which states how to
|
|
-- dispatch. By using constraints, we can make requirements of our master site.
|
|
-- In this example, we're saying that the master site must be an instance of
|
|
-- YesodWiki.
|
|
instance YesodWiki master => YesodSubDispatch Wiki (HandlerT master IO) where
|
|
-- | This is all the TH magic for dispatch. WikiRoutes provides the
|
|
-- resourcesWiki value automatically, and mkYesodSubDispatch will generate
|
|
-- a dispatch function that will call out to the appropriate handler
|
|
-- functions.
|
|
yesodSubDispatch = $(mkYesodSubDispatch resourcesWiki)
|
|
|
|
-- | Helper type synonym to be used below.
|
|
type WikiHandler a = forall master. YesodWiki master
|
|
=> HandlerT Wiki (HandlerT master IO) a
|
|
|
|
------------- Helper functions
|
|
|
|
-- | Get all of the content in the Wiki.
|
|
getContent :: WikiHandler (Map Texts Textarea)
|
|
getContent = getYesod >>= readIORef . wikiContent
|
|
|
|
-- | Put a single new value into the Wiki.
|
|
putContent :: Texts -> Textarea -> WikiHandler ()
|
|
putContent k v = do
|
|
refMap <- wikiContent <$> getYesod
|
|
atomicModifyIORef refMap $ \m -> (Map.insert k v m, ())
|
|
|
|
-- | Gets the homepage, which lists all of the pages available.
|
|
getWikiHomeR :: WikiHandler TypedContent
|
|
getWikiHomeR = do
|
|
content <- getContent
|
|
-- We use the new selectRep/provideRep functionality to provide either an
|
|
-- HTML or JSON representation of the page. You could just as easily
|
|
-- provide YAML, plain text, RSS, or anything else.
|
|
selectRep $ do
|
|
provideRep $ do
|
|
-- We'll use toParent to convert Wiki routes into our master site
|
|
-- routes.
|
|
toParent <- getRouteToParent
|
|
|
|
-- Run the master site's defaultLayout to style the page.
|
|
lift $ defaultLayout
|
|
[whamlet|
|
|
<p>This wiki has the following pages:
|
|
<ul>
|
|
$forall page <- Map.keys content
|
|
<li>
|
|
-- Notice the usage of toParent!
|
|
<a href=@{toParent $ WikiReadR page}>#{show page}
|
|
|]
|
|
-- You provide a JSON representation just by returning a JSON value.
|
|
-- aeson's toJSON make it easy to convert a list of values into JSON.
|
|
provideRep $ return $ toJSON $ Map.keys content
|
|
|
|
getWikiReadR :: Texts -> WikiHandler TypedContent
|
|
getWikiReadR page = do
|
|
content <- getContent
|
|
selectRep $ do
|
|
provideRep $
|
|
case Map.lookup page content of
|
|
Nothing -> do
|
|
setMessage $ "Page does not exist, please create it."
|
|
|
|
-- We don't need to convert or lift here: we're using a
|
|
-- route from our subsite, and redirect lives in our
|
|
-- subsite.
|
|
redirect $ WikiEditR page
|
|
Just t -> do
|
|
toParent <- getRouteToParent
|
|
|
|
-- Notice that we lift the canEditPage function from the
|
|
-- master site.
|
|
canEdit <- lift $ canEditPage page
|
|
|
|
lift $ defaultLayout
|
|
[whamlet|
|
|
<article>#{t}
|
|
$if canEdit
|
|
<p>
|
|
<a href=@{toParent $ WikiEditR page}>Edit
|
|
|]
|
|
provideRep $ return $ toJSON $
|
|
case Map.lookup page content of
|
|
-- Our HTML representation sends a redirect if the page isn't
|
|
-- found, but our JSON representation just returns a JSON value
|
|
-- instead.
|
|
Nothing -> object ["error" .= ("Page not found" :: Text)]
|
|
Just (Textarea t) -> object ["content" .= t]
|
|
|
|
getWikiEditR :: Texts -> WikiHandler Html
|
|
getWikiEditR page = do
|
|
canEdit <- lift $ canEditPage page
|
|
unless canEdit $ permissionDenied "You do not have permissions to edit this page."
|
|
|
|
content <- getContent
|
|
let form = renderTable
|
|
$ areq textareaField "Content" (Map.lookup page content)
|
|
|
|
-- We need to use lift here since the widget will be used below.
|
|
-- Practically speaking, this means that we'll be rendering form messages
|
|
-- using the master site's translation functions.
|
|
((res, widget), enctype) <- lift $ runFormPost form
|
|
|
|
case res of
|
|
FormSuccess t -> do
|
|
putContent page t
|
|
setMessage "Content updated"
|
|
redirect $ WikiEditR page
|
|
_ -> do
|
|
toParent <- getRouteToParent
|
|
lift $ defaultLayout
|
|
[whamlet|
|
|
<p>
|
|
<a href=@{toParent $ WikiReadR page}>Read page
|
|
<form method=post action=@{toParent $ WikiEditR page} enctype=#{enctype}>
|
|
<table>
|
|
^{widget}
|
|
<tr>
|
|
<td colspan=2>
|
|
<button>Update page
|
|
|]
|
|
|
|
postWikiEditR :: Texts -> WikiHandler Html
|
|
postWikiEditR = getWikiEditR
|