packages feed

darcsden-1.1: src/DarcsDen/Handler.hs

{-# LANGUAGE OverloadedStrings #-}
module DarcsDen.Handler where

import Control.Applicative ((<|>))
import Control.Arrow (second)
import Control.Concurrent (threadDelay)
import Control.Monad.Trans
import Data.Monoid (mconcat)
import qualified Darcs.Repository as R
import qualified Darcs.Patch.V1.Core as P
import qualified Darcs.Patch.Prim.V1.Core as Pr
import Snap.Core
import Snap.Util.FileServe
import System.FilePath ((</>))
import qualified Data.Text as T
import qualified Data.ByteString as BS

import DarcsDen.Handler.Repository
import DarcsDen.Handler.Repository.Util (getRepo)
import DarcsDen.Handler.User
import DarcsDen.Settings
import DarcsDen.State.Repository
import DarcsDen.State.Session
import DarcsDen.State.User
import DarcsDen.State.Util
import DarcsDen.Util
import DarcsDen.WebUtils
import qualified DarcsDen.Pages.Base as Base

-- URL handling
handler :: FilePath -> Snap ()
handler cwd =
    route
        [ ("public", serveDirectory $ cwd </> "public")
        , ("robots.txt", serveFile $ cwd </> "public/robots.txt")
        , ("favicon.ico", serveFile $ cwd </> "public/images/favicon.ico")
        , (":user/:repo/_darcs", repoServe "_darcs")
        , (":user/:repo/raw", repoServe "")
        , ("delay/:seconds", dbgDelay)
        ] <|>
    withSession (\s -> ifTop (frontpage s) <|> route (routes s))

routes :: Session -> [(BS.ByteString, Snap ())]
routes s =
    [ -- Main
      ("explore", explore s)
    , ("explore/page/:page", explore s)
    , ("init", method GET (initialize s) <|> method POST (doInitialize s))

    -- Users
    , (":user", user s)
    ]
    ++ (if multiuser
        then [ ("register", method GET (register s) <|> method POST (doRegister s))
             , ("register/github", githubRegister s)
             , ("register/github/response", githubRegisterResponse s)
             , ("register/google", googleRegister s)
             , ("register/google/response", googleRegisterResponse s)
             ] 
        else [])
    ++ [
      ("login/github", githubLogin s)
    , ("login/github/response", githubLoginResponse s)
    , ("login/google", googleLogin s)
    , ("login/google/response", googleLoginResponse s)
    , ("login", method GET (login s) <|> method POST (doLogin s))
    , ("logout", logout s)
    , ("settings", method GET (settings s) <|> method POST (doSettings s))
    , ("forgotpassword", method GET (forgotPassword s) <|> method POST (forgotPasswordResponse s))
    , ("recovery", method GET (resetPassword s) <|> method POST (resetPasswordResponse s))
    , ("sync/github", syncWithGithub s)
    , ("sync/github/response", syncWithGithubResponse s)
    , ("sync/google", syncWithGoogle s)
    , ("sync/google/response", syncWithGoogleResponse s)
    ] ++

    -- Repositories
    map (second (validateRepo s)) (
        [ (":user/:repo", browseRepo)
        , (":user/:repo/browse", browseRepo)
        , (":user/:repo/edit-file", \u r s' ->
            method GET (editFile u r s') <|>
            method POST (editFilePost u r s'))
	, (":user/:repo/add-file", \u r s' ->
            method GET (addFile u r s') <|>
            method POST (addFilePost u r s'))
        , (":user/:repo/changes", repoChanges)
        , (":user/:repo/changes/atom", repoChangesAtom)
        , (":user/:repo/changes/page/:page", repoChanges)
        , (":user/:repo/delete", \u r s' ->
            method GET (deleteRepo u r s') <|>
            method POST (doDeleteRepo u r s'))
        , (":user/:repo/edit", \u r s' ->
            method GET (editRepo u r s') <|>
            method POST (doEditRepo u r s'))
        , (":user/:repo/packs", buildPacks)
        , (":user/:repo/fork", forkRepo)
        , (":user/:repo/fork-as", forkRepoAs)
        , (":user/:repo/patches", repoPatches)
        , (":user/:repo/merge", repoMerge)
        -- , (":user/:repo/patch1/:id", repoPatch1)
        , (":user/:repo/patch/:id", repoPatch)
        , (":user/:repo/renderedpatch/:id", renderedpatch)
        ]
        ++ if issuetrackers then
        [ (":user/:repo/issues", repoIssuesOpen)
        , (":user/:repo/issues/closed", repoIssuesClosed)
        , (":user/:repo/issues/all", repoIssuesAll)
        , (":user/:repo/issues/tag/:tag", repoIssuesTag)
        , (":user/:repo/issue/:number", repoIssue)
        , (":user/:repo/issue/:number/comment", repoComment)
        , (":user/:repo/new-issue", \u r s' ->
            method GET (newIssue u r s') <|>
            method POST (doNewIssue u r s'))
        ]
        else []
        )

dbgDelay :: Snap ()
dbgDelay = do
    Just delay <- getParam "seconds"
    let s = read $ fromBS delay
        s' = max s 300
    liftIO $ threadDelay $ s' * 1000000
    writeText $ mconcat [T.pack $ show s', "s delay complete"]

frontpage :: Page
frontpage s = do
    us <- getUsers
    doPage (Base.frontpage us) s

validateRepo :: Session -> (User -> Repository -> Page) -> Snap ()
validateRepo s p = do
    mo <- getParam "user"
    mn <- getParam "repo"

    case (mo, mn) of
        (Just o, Just n) -> do
            let (owner, name) = (fromBS o, fromBS n)
            mu <- getUser owner
            mr <- getValidRepo owner name
            darcsRepo <- liftIO (getRepo (repoDir owner name)) :: Snap (Either String (R.Repository (P.Patch Pr.Prim) wR wU wT))
            case (mu, mr, darcsRepo) of
                (Just u , Just r , Right _) -> p u r s
                (Nothing, _      , _      ) -> notFoundPage "user does not exist"
                (_      , Just _ , Left _ ) -> notFoundPage "repository invalid"
                (Just _ , Nothing, _      ) -> notFoundPage "repository does not exist"
        _ -> warn "no repository specified" s >> redirectTo baseUrl
  where
    getValidRepo owner name = do
        public <- getRepository (owner, name)

        -- repository is public
        if public /= Nothing
            then return public
            else do

        -- repository is private
        case sUser s of
            -- owner viewing their own repository
            Just un | un == owner ->
                getOwnerRepository (owner, name)

            -- some other user, check if they're a member
            Just mn -> do
                mm <- getUser mn
                case mm of
                    Just (User { uName = un }) -> do
                        ism <- isMember un (owner, name)
                        if ism
                            then getOwnerRepository (owner, name)
                            else return Nothing

                    -- invalid user
                    _ -> return Nothing

            -- not logged in
            _ -> return Nothing