1
0
Fork 0
mirror of https://code.sup39.dev/repos/Wqawg synced 2025-01-11 02:56:46 +09:00
vervis/src/Vervis/Handler/Ticket.hs

770 lines
31 KiB
Haskell
Raw Normal View History

2016-05-01 07:32:22 +09:00
{- This file is part of Vervis.
-
- Written in 2016, 2018, 2019 by fr33domlover <fr33domlover@riseup.net>.
2016-05-01 07:32:22 +09:00
-
- Copying is an act of love. Please copy, reuse and share.
-
- The author(s) have dedicated all copyright and related and neighboring
- rights to this software to the public domain worldwide. This software is
- distributed without any warranty.
-
- You should have received a copy of the CC0 Public Domain Dedication along
- with this software. If not, see
- <http://creativecommons.org/publicdomain/zero/1.0/>.
-}
module Vervis.Handler.Ticket
( getTicketsR
, postTicketsR
2016-08-04 16:36:24 +09:00
, getTicketTreeR
2016-05-01 07:32:22 +09:00
, getTicketNewR
2016-05-01 08:02:44 +09:00
, getTicketR
, putTicketR
, deleteTicketR
, postTicketR
, getTicketEditR
2016-08-11 16:58:51 +09:00
, postTicketAcceptR
, postTicketCloseR
, postTicketOpenR
, postTicketClaimR
, postTicketUnclaimR
, getTicketAssignR
, postTicketAssignR
, postTicketUnassignR
, getClaimRequestsPersonR
, getClaimRequestsProjectR
, getClaimRequestsTicketR
2016-06-08 10:28:18 +09:00
, postClaimRequestsTicketR
, getClaimRequestNewR
2016-05-20 01:58:23 +09:00
, getTicketDiscussionR
, postTicketDiscussionR
, getMessageR
2016-05-20 07:07:25 +09:00
, postTicketMessageR
, getTicketTopReplyR
, getTicketReplyR
, getTicketDepsR
, postTicketDepsR
, getTicketDepNewR
, postTicketDepR
, deleteTicketDepR
2016-07-27 17:35:50 +09:00
, getTicketReverseDepsR
2016-05-01 07:32:22 +09:00
)
where
import Prelude
2016-08-04 16:36:24 +09:00
import Control.Applicative (liftA2)
import Control.Monad.IO.Class (liftIO)
import Control.Monad.Logger (logWarn)
import Data.Bool (bool)
2016-05-22 23:31:56 +09:00
import Data.Default.Class (def)
import Data.Foldable (traverse_)
2016-05-01 18:58:55 +09:00
import Data.Maybe (fromMaybe)
import Data.Monoid ((<>))
2016-05-01 07:32:22 +09:00
import Data.Text (Text)
import Data.Time.Calendar (Day (..))
import Data.Time.Clock (UTCTime (..), getCurrentTime)
2016-05-01 18:58:55 +09:00
import Data.Time.Format (formatTime, defaultTimeLocale)
2016-06-02 01:20:19 +09:00
import Data.Traversable (for)
import Database.Esqueleto hiding ((=.), (+=.), update, delete)
import Database.Persist hiding ((==.))
import Network.HTTP.Types (StdMethod (DELETE, POST))
2016-05-01 07:32:22 +09:00
import Text.Blaze.Html (Html, toHtml)
2016-06-06 19:00:05 +09:00
import Yesod.Auth (requireAuthId, maybeAuthId)
import Yesod.Core
import Yesod.Core.Handler
2016-05-22 23:31:56 +09:00
import Yesod.Form.Functions (runFormGet, runFormPost)
2016-05-01 19:15:38 +09:00
import Yesod.Form.Types (FormResult (..))
2016-05-01 18:58:55 +09:00
import Yesod.Persist.Core (runDB, get404, getBy404)
2016-05-01 07:32:22 +09:00
import qualified Data.Text as T (filter, intercalate, pack)
2016-05-01 18:58:55 +09:00
import qualified Database.Esqueleto as E ((==.))
2016-05-01 07:32:22 +09:00
import Database.Persist.Sql.Graph.TransitiveReduction (trrFix)
import Yesod.Hashids
import Data.Maybe.Local (partitionMaybePairs)
2016-05-01 07:32:22 +09:00
import Vervis.Form.Ticket
import Vervis.Foundation
2016-05-20 01:58:23 +09:00
import Vervis.Handler.Discussion
import Vervis.GraphProxy (ticketDepGraph)
import Vervis.MediaType (MediaType (Markdown))
2016-05-01 07:32:22 +09:00
import Vervis.Model
import Vervis.Model.Ident
import Vervis.Model.Ticket
import Vervis.Model.Workflow
import Vervis.Render (renderSourceT)
2016-05-01 07:32:22 +09:00
import Vervis.Settings (widgetFile)
import Vervis.Style
2016-08-04 16:36:24 +09:00
import Vervis.Ticket
2016-05-22 23:31:56 +09:00
import Vervis.TicketFilter (filterTickets)
2016-06-06 16:26:58 +09:00
import Vervis.Time (showDate)
import Vervis.Widget (buttonW)
import Vervis.Widget.Discussion (discussionW)
2016-05-25 06:48:21 +09:00
import Vervis.Widget.Sharer (personLinkW)
import Vervis.Widget.Ticket
2016-05-01 07:32:22 +09:00
getTicketsR :: ShrIdent -> PrjIdent -> Handler Html
getTicketsR shr prj = do
2016-05-22 23:31:56 +09:00
((filtResult, filtWidget), filtEnctype) <- runFormGet ticketFilterForm
let tf =
case filtResult of
FormSuccess filt -> filt
FormMissing -> def
FormFailure l ->
error $ "Ticket filter form failed: " ++ show l
rows <- runDB $ do
Entity sid _ <- getBy404 $ UniqueSharer shr
Entity jid _ <- getBy404 $ UniqueProject prj sid
getTicketSummaries
(filterTickets tf)
(Just $ \ t -> [asc $ t ^. TicketNumber])
jid
defaultLayout $(widgetFile "ticket/list")
2016-05-01 07:32:22 +09:00
postTicketsR :: ShrIdent -> PrjIdent -> Handler Html
2016-05-01 19:15:38 +09:00
postTicketsR shar proj = do
Entity pid project <- runDB $ do
Entity sid _sharer <- getBy404 $ UniqueSharer shar
getBy404 $ UniqueProject proj sid
((result, widget), enctype) <-
runFormPost $ newTicketForm $ projectWorkflow project
2016-05-01 19:15:38 +09:00
case result of
FormSuccess nt -> do
author <- requireAuthId
now <- liftIO getCurrentTime
tnum <- runDB $ do
2016-05-18 18:44:32 +09:00
update pid [ProjectNextTicket +=. 1]
did <- insert Discussion
fsid <- insert FollowerSet
let ticket = Ticket
2016-06-02 01:20:19 +09:00
{ ticketProject = pid
, ticketNumber = projectNextTicket project
, ticketCreated = now
, ticketCreator = author
, ticketTitle = ntTitle nt
, ticketDesc = ntDesc nt
, ticketAssignee = Nothing
, ticketStatus = TSNew
2016-06-02 01:20:19 +09:00
, ticketClosed = UTCTime (ModifiedJulianDay 0) 0
, ticketCloser = author
, ticketDiscuss = did
, ticketFollowers = fsid
}
tid <- insert ticket
let mktparam (fid, v) = TicketParamText
{ ticketParamTextTicket = tid
, ticketParamTextField = fid
, ticketParamTextValue = v
}
insertMany_ $ map mktparam $ ntTParams nt
let mkeparam (fid, v) = TicketParamEnum
{ ticketParamEnumTicket = tid
, ticketParamEnumField = fid
, ticketParamEnumValue = v
}
insertMany_ $ map mkeparam $ ntEParams nt
return $ ticketNumber ticket
2016-05-01 19:15:38 +09:00
setMessage "Ticket created."
redirect $ TicketR shar proj tnum
2016-05-01 19:15:38 +09:00
FormMissing -> do
setMessage "Field(s) missing."
defaultLayout $(widgetFile "ticket/new")
FormFailure _l -> do
setMessage "Ticket creation failed, see errors below."
defaultLayout $(widgetFile "ticket/new")
2016-05-01 07:32:22 +09:00
2016-08-04 16:36:24 +09:00
getTicketTreeR :: ShrIdent -> PrjIdent -> Handler Html
getTicketTreeR shr prj = do
(summaries, deps) <- runDB $ do
Entity sid _ <- getBy404 $ UniqueSharer shr
Entity jid _ <- getBy404 $ UniqueProject prj sid
liftA2 (,)
(getTicketSummaries Nothing Nothing jid)
2016-08-04 16:36:24 +09:00
(getTicketDepEdges jid)
defaultLayout $ ticketTreeDW shr prj summaries deps
getTicketNewR :: ShrIdent -> PrjIdent -> Handler Html
2016-05-01 07:32:22 +09:00
getTicketNewR shar proj = do
wid <- runDB $ do
Entity sid _ <- getBy404 $ UniqueSharer shar
Entity _ j <- getBy404 $ UniqueProject proj sid
return $ projectWorkflow j
((_result, widget), enctype) <- runFormPost $ newTicketForm wid
defaultLayout $(widgetFile "ticket/new")
2016-05-01 08:02:44 +09:00
getTicketR :: ShrIdent -> PrjIdent -> Int -> Handler Html
2016-05-01 18:58:55 +09:00
getTicketR shar proj num = do
2016-06-06 19:00:05 +09:00
mpid <- maybeAuthId
( wshr, wfl,
author, massignee, closer, ticket, tparams, eparams, deps, rdeps) <-
runDB $ do
(jid, wshr, wid, wfl) <- do
Entity s sharer <- getBy404 $ UniqueSharer shar
Entity p project <- getBy404 $ UniqueProject proj s
w <- get404 $ projectWorkflow project
wsharer <-
if workflowSharer w == s
then return sharer
else get404 $ workflowSharer w
return
( p
, sharerIdent wsharer
, projectWorkflow project
, workflowIdent w
)
Entity tid ticket <- getBy404 $ UniqueTicket jid num
author <- do
person <- get404 $ ticketCreator ticket
get404 $ personIdent person
massignee <- for (ticketAssignee ticket) $ \ apid -> do
person <- get404 apid
sharer <- get404 $ personIdent person
return (sharer, fromMaybe False $ (== apid) <$> mpid)
closer <-
case ticketStatus ticket of
TSClosed -> do
person <- get404 $ ticketCloser ticket
get404 $ personIdent person
_ -> return author
tparams <- getTicketTextParams tid wid
eparams <- getTicketEnumParams tid wid
deps <- select $ from $ \ (dep `InnerJoin` t) -> do
on $ dep ^. TicketDependencyChild ==. t ^. TicketId
where_ $ dep ^. TicketDependencyParent ==. val tid
return t
rdeps <- select $ from $ \ (dep `InnerJoin` t) -> do
on $ dep ^. TicketDependencyParent ==. t ^. TicketId
where_ $ dep ^. TicketDependencyChild ==. val tid
return t
return
( wshr, wfl
, author, massignee, closer, ticket, tparams, eparams
, deps, rdeps
)
encodeHid <- getEncodeKeyHashid
let desc = renderSourceT Markdown $ T.filter (/= '\r') $ ticketDesc ticket
discuss =
discussionW
(return $ ticketDiscuss ticket)
(TicketTopReplyR shar proj num)
(TicketReplyR shar proj num . encodeHid)
cRelevant <- newIdent
cIrrelevant <- newIdent
let relevant filt =
bool cIrrelevant cRelevant $
case ticketStatus ticket of
TSNew -> wffNew filt
TSTodo -> wffTodo filt
TSClosed -> wffClosed filt
2016-05-20 01:58:23 +09:00
defaultLayout $(widgetFile "ticket/one")
putTicketR :: ShrIdent -> PrjIdent -> Int -> Handler Html
2016-05-02 18:15:10 +09:00
putTicketR shar proj num = do
(tid, ticket, wid) <- runDB $ do
Entity sid _sharer <- getBy404 $ UniqueSharer shar
Entity pid project <- getBy404 $ UniqueProject proj sid
Entity tid ticket <- getBy404 $ UniqueTicket pid num
return (tid, ticket, projectWorkflow project)
((result, widget), enctype) <-
runFormPost $ editTicketContentForm tid ticket wid
2016-05-02 18:15:10 +09:00
case result of
FormSuccess (ticket', tparams, eparams) -> do
runDB $ do
replace tid ticket'
let (tdel, tins, tupd) = partitionMaybePairs tparams
deleteWhere [TicketParamTextId <-. tdel]
let mktparam (fid, v) = TicketParamText
{ ticketParamTextTicket = tid
, ticketParamTextField = fid
, ticketParamTextValue = v
}
insertMany_ $ map mktparam tins
traverse_
(\ (aid, (_fid, v)) ->
update aid [TicketParamTextValue =. v]
)
tupd
let (edel, eins, eupd) = partitionMaybePairs eparams
deleteWhere [TicketParamEnumId <-. edel]
let mkeparam (fid, v) = TicketParamEnum
{ ticketParamEnumTicket = tid
, ticketParamEnumField = fid
, ticketParamEnumValue = v
}
insertMany_ $ map mkeparam eins
traverse_
(\ (aid, (_fid, v)) ->
update aid [TicketParamEnumValue =. v]
)
eupd
2016-05-02 20:33:30 +09:00
setMessage "Ticket updated."
2016-05-02 18:15:10 +09:00
redirect $ TicketR shar proj num
FormMissing -> do
setMessage "Field(s) missing."
defaultLayout $(widgetFile "ticket/edit")
FormFailure _l -> do
setMessage "Ticket update failed, see errors below."
defaultLayout $(widgetFile "ticket/edit")
deleteTicketR :: ShrIdent -> PrjIdent -> Int -> Handler Html
2016-05-02 18:15:10 +09:00
deleteTicketR shar proj num =
--TODO: I can easily implement this, but should it even be possible to
--delete tickets?
error "Not implemented"
postTicketR :: ShrIdent -> PrjIdent -> Int -> Handler Html
postTicketR shar proj num = do
mmethod <- lookupPostParam "_method"
case mmethod of
Just "PUT" -> putTicketR shar proj num
Just "DELETE" -> deleteTicketR shar proj num
_ -> notFound
getTicketEditR :: ShrIdent -> PrjIdent -> Int -> Handler Html
2016-05-02 18:15:10 +09:00
getTicketEditR shar proj num = do
(tid, ticket, wid) <- runDB $ do
Entity sid _sharer <- getBy404 $ UniqueSharer shar
Entity pid project <- getBy404 $ UniqueProject proj sid
Entity tid ticket <- getBy404 $ UniqueTicket pid num
return (tid, ticket, projectWorkflow project)
((_result, widget), enctype) <-
runFormPost $ editTicketContentForm tid ticket wid
2016-05-20 01:58:23 +09:00
defaultLayout $(widgetFile "ticket/edit")
2016-08-11 16:58:51 +09:00
postTicketAcceptR :: ShrIdent -> PrjIdent -> Int -> Handler Html
postTicketAcceptR shr prj num = do
succ <- runDB $ do
Entity tid ticket <- do
Entity s _ <- getBy404 $ UniqueSharer shr
Entity p _ <- getBy404 $ UniqueProject prj s
getBy404 $ UniqueTicket p num
case ticketStatus ticket of
TSNew -> do
update tid [TicketStatus =. TSTodo]
return True
_ -> return False
setMessage $
if succ
then "Ticket accepted."
else "Ticket is already accepted."
redirect $ TicketR shr prj num
postTicketCloseR :: ShrIdent -> PrjIdent -> Int -> Handler Html
postTicketCloseR shr prj num = do
pid <- requireAuthId
now <- liftIO getCurrentTime
succ <- runDB $ do
Entity tid ticket <- do
Entity s _ <- getBy404 $ UniqueSharer shr
Entity p _ <- getBy404 $ UniqueProject prj s
getBy404 $ UniqueTicket p num
case ticketStatus ticket of
TSClosed -> return False
_ -> do
update tid
[ TicketAssignee =. Nothing
, TicketStatus =. TSClosed
, TicketClosed =. now
, TicketCloser =. pid
]
return True
setMessage $
if succ
then "Ticket closed."
else "Ticket is already closed."
redirect $ TicketR shr prj num
postTicketOpenR :: ShrIdent -> PrjIdent -> Int -> Handler Html
postTicketOpenR shr prj num = do
pid <- requireAuthId
now <- liftIO getCurrentTime
succ <- runDB $ do
Entity tid ticket <- do
Entity s _ <- getBy404 $ UniqueSharer shr
Entity p _ <- getBy404 $ UniqueProject prj s
getBy404 $ UniqueTicket p num
case ticketStatus ticket of
TSClosed -> do
update tid
[ TicketStatus =. TSTodo
, TicketCloser =. ticketCreator ticket
]
return True
_ -> return False
setMessage $
if succ
then "Ticket reopened"
else "Ticket is already open."
redirect $ TicketR shr prj num
postTicketClaimR :: ShrIdent -> PrjIdent -> Int -> Handler Html
postTicketClaimR shr prj num = do
pid <- requireAuthId
mmsg <- runDB $ do
Entity tid ticket <- do
Entity s _ <- getBy404 $ UniqueSharer shr
Entity p _ <- getBy404 $ UniqueProject prj s
getBy404 $ UniqueTicket p num
case (ticketStatus ticket, ticketAssignee ticket) of
(TSNew, _) ->
return $
Just "The ticket isnt accepted yet. Cant claim it."
(TSClosed, _) ->
return $
Just "The ticket is closed. Cant claim closed tickets."
(TSTodo, Just _) ->
return $
Just "The ticket is already assigned to someone."
(TSTodo, Nothing) -> do
update tid [TicketAssignee =. Just pid]
return Nothing
setMessage $ fromMaybe "The ticket is now assigned to you." mmsg
redirect $ TicketR shr prj num
postTicketUnclaimR :: ShrIdent -> PrjIdent -> Int -> Handler Html
postTicketUnclaimR shr prj num = do
pid <- requireAuthId
mmsg <- runDB $ do
Entity tid ticket <- do
Entity s _ <- getBy404 $ UniqueSharer shr
Entity p _ <- getBy404 $ UniqueProject prj s
getBy404 $ UniqueTicket p num
case ((== pid) <$> ticketAssignee ticket, ticketStatus ticket) of
(Nothing, _) ->
return $ Just "The ticket is already unassigned."
(Just False, _) ->
return $ Just "The ticket is assigned to someone else."
(Just True, TSNew) -> do
$logWarn "Found a new claimed ticket, this is invalid"
return $
Just "The ticket isnt accepted yet. Cant unclaim it."
(Just True, TSClosed) -> do
$logWarn "Found a closed claimed ticket, this is invalid"
return $
Just "The ticket is closed. Cant unclaim closed tickets."
(Just True, TSTodo) -> do
update tid [TicketAssignee =. Nothing]
return Nothing
setMessage $ fromMaybe "The ticket is now unassigned." mmsg
redirect $ TicketR shr prj num
getTicketAssignR :: ShrIdent -> PrjIdent -> Int -> Handler Html
getTicketAssignR shr prj num = do
vpid <- requireAuthId
(jid, Entity tid ticket) <- runDB $ do
Entity s _ <- getBy404 $ UniqueSharer shr
Entity j _ <- getBy404 $ UniqueProject prj s
et <- getBy404 $ UniqueTicket j num
return (j, et)
let msg t = do
setMessage t
redirect $ TicketR shr prj num
case (ticketStatus ticket, ticketAssignee ticket) of
(TSNew, _) -> msg "The ticket isnt accepted yet. Cant assign it."
(TSClosed, _) -> msg "The ticket is closed. Cant assign it."
(TSTodo, Just _) -> msg "The ticket is already assigned to someone."
(TSTodo, Nothing) -> do
((_result, widget), enctype) <-
runFormPost $ assignTicketForm vpid jid
defaultLayout $(widgetFile "ticket/assign")
postTicketAssignR :: ShrIdent -> PrjIdent -> Int -> Handler Html
postTicketAssignR shr prj num = do
vpid <- requireAuthId
(jid, Entity tid ticket) <- runDB $ do
Entity s _ <- getBy404 $ UniqueSharer shr
Entity j _ <- getBy404 $ UniqueProject prj s
et <- getBy404 $ UniqueTicket j num
return (j, et)
let msg t = do
setMessage t
redirect $ TicketR shr prj num
case (ticketStatus ticket, ticketAssignee ticket) of
(TSNew, _) -> msg "The ticket isnt accepted yet. Cant assign it."
(TSClosed, _) -> msg "The ticket is closed. Cant assign it."
(TSTodo, Just _) -> msg "The ticket is already assigned to someone."
(TSTodo, Nothing) -> do
((result, widget), enctype) <-
runFormPost $ assignTicketForm vpid jid
case result of
FormSuccess pid -> do
sharer <- runDB $ do
update tid [TicketAssignee =. Just pid]
person <- getJust pid
getJust $ personIdent person
let si = sharerIdent sharer
msg $ toHtml $
"The ticket is now assigned to " <> shr2text si <> "."
FormMissing -> do
setMessage "Field(s) missing."
defaultLayout $(widgetFile "ticket/assign")
FormFailure _l -> do
setMessage "Ticket assignment failed, see errors below."
defaultLayout $(widgetFile "ticket/assign")
postTicketUnassignR :: ShrIdent -> PrjIdent -> Int -> Handler Html
postTicketUnassignR shr prj num = do
pid <- requireAuthId
mmsg <- runDB $ do
Entity tid ticket <- do
Entity s _ <- getBy404 $ UniqueSharer shr
Entity p _ <- getBy404 $ UniqueProject prj s
getBy404 $ UniqueTicket p num
case ((== pid) <$> ticketAssignee ticket, ticketStatus ticket) of
(Nothing, _) ->
return $ Just "The ticket is already unassigned."
(Just True, _) ->
return $ Just "The ticket is assigned to you, unclaim instead."
(Just False, TSNew) -> do
$logWarn "Found a new claimed ticket, this is invalid"
return $
Just "The ticket isnt accepted yet. Cant unclaim it."
(Just False, TSClosed) -> do
$logWarn "Found a closed claimed ticket, this is invalid"
return $
Just "The ticket is closed. Cant unclaim closed tickets."
(Just False, TSTodo) -> do
update tid [TicketAssignee =. Nothing]
return Nothing
setMessage $ fromMaybe "The ticket is now unassigned." mmsg
redirect $ TicketR shr prj num
-- | The logged-in user gets a list of the ticket claim requests they have
-- opened, in any project.
getClaimRequestsPersonR :: Handler Html
getClaimRequestsPersonR = do
pid <- requireAuthId
rqs <- runDB $ select $ from $
\ (tcr `InnerJoin` ticket `InnerJoin` project `InnerJoin` sharer) -> do
on $ project ^. ProjectSharer ==. sharer ^. SharerId
on $ ticket ^. TicketProject ==. project ^. ProjectId
on $ tcr ^. TicketClaimRequestTicket ==. ticket ^. TicketId
where_ $ tcr ^. TicketClaimRequestPerson ==. val pid
orderBy [desc $ tcr ^. TicketClaimRequestCreated]
return
( sharer ^. SharerIdent
, project ^. ProjectIdent
, ticket ^. TicketNumber
, ticket ^. TicketTitle
, tcr ^. TicketClaimRequestCreated
)
defaultLayout $(widgetFile "person/claim-requests")
-- | Get a list of ticket claim requests for a given project.
getClaimRequestsProjectR :: ShrIdent -> PrjIdent -> Handler Html
getClaimRequestsProjectR shr prj = do
rqs <- runDB $ do
Entity sid _ <- getBy404 $ UniqueSharer shr
Entity jid _ <- getBy404 $ UniqueProject prj sid
select $ from $
\ ( tcr `InnerJoin`
ticket `InnerJoin`
person `InnerJoin`
sharer
) -> do
on $ person ^. PersonIdent ==. sharer ^. SharerId
on $ tcr ^. TicketClaimRequestPerson ==. person ^. PersonId
on $ tcr ^. TicketClaimRequestTicket ==. ticket ^. TicketId
where_ $ ticket ^. TicketProject ==. val jid
orderBy [desc $ tcr ^. TicketClaimRequestCreated]
return
( sharer
, ticket ^. TicketNumber
, ticket ^. TicketTitle
, tcr ^. TicketClaimRequestCreated
)
defaultLayout $(widgetFile "project/claim-request/list")
-- | Get a list of ticket claim requests for a given ticket.
getClaimRequestsTicketR :: ShrIdent -> PrjIdent -> Int -> Handler Html
getClaimRequestsTicketR shr prj num = do
rqs <- runDB $ do
Entity sid _ <- getBy404 $ UniqueSharer shr
Entity jid _ <- getBy404 $ UniqueProject prj sid
Entity tid _ <- getBy404 $ UniqueTicket jid num
select $ from $ \ (tcr `InnerJoin` person `InnerJoin` sharer) -> do
on $ person ^. PersonIdent ==. sharer ^. SharerId
on $ tcr ^. TicketClaimRequestPerson ==. person ^. PersonId
where_ $ tcr ^. TicketClaimRequestTicket ==. val tid
orderBy [desc $ tcr ^. TicketClaimRequestCreated]
return (sharer, tcr)
defaultLayout $(widgetFile "ticket/claim-request/list")
2016-06-08 10:28:18 +09:00
getClaimRequestNewR :: ShrIdent -> PrjIdent -> Int -> Handler Html
getClaimRequestNewR shr prj num = do
((_result, widget), etype) <- runFormPost claimRequestForm
defaultLayout $(widgetFile "ticket/claim-request/new")
postClaimRequestsTicketR :: ShrIdent -> PrjIdent -> Int -> Handler Html
postClaimRequestsTicketR shr prj num = do
((result, widget), etype) <- runFormPost claimRequestForm
case result of
FormSuccess msg -> do
now <- liftIO getCurrentTime
pid <- requireAuthId
runDB $ do
tid <- do
Entity s _ <- getBy404 $ UniqueSharer shr
Entity j _ <- getBy404 $ UniqueProject prj s
Entity t _ <- getBy404 $ UniqueTicket j num
return t
let cr = TicketClaimRequest
{ ticketClaimRequestPerson = pid
, ticketClaimRequestTicket = tid
, ticketClaimRequestMessage = msg
, ticketClaimRequestCreated = now
}
insert_ cr
setMessage "Ticket claim request opened."
redirect $ TicketR shr prj num
FormMissing -> do
setMessage "Field(s) missing."
defaultLayout $(widgetFile "ticket/claim-request/new")
FormFailure _l -> do
setMessage "Submission failed, see errors below."
defaultLayout $(widgetFile "ticket/claim-request/new")
selectDiscussionId :: ShrIdent -> PrjIdent -> Int -> AppDB DiscussionId
2016-05-20 07:07:25 +09:00
selectDiscussionId shar proj tnum = do
Entity sid _sharer <- getBy404 $ UniqueSharer shar
2016-05-20 07:07:25 +09:00
Entity pid _project <- getBy404 $ UniqueProject proj sid
Entity _tid ticket <- getBy404 $ UniqueTicket pid tnum
return $ ticketDiscuss ticket
getTicketDiscussionR :: ShrIdent -> PrjIdent -> Int -> Handler Html
getTicketDiscussionR shar proj num = do
encodeHid <- getEncodeKeyHashid
getDiscussion
(TicketReplyR shar proj num . encodeHid)
(TicketTopReplyR shar proj num)
(selectDiscussionId shar proj num)
2016-05-20 01:58:23 +09:00
postTicketDiscussionR :: ShrIdent -> PrjIdent -> Int -> Handler Html
postTicketDiscussionR shar proj num =
postTopReply
(TicketDiscussionR shar proj num)
(const $ TicketR shar proj num)
(selectDiscussionId shar proj num)
getMessageR :: ShrIdent -> KeyHashid LocalMessage -> Handler TypedContent
getMessageR shr hid = do
lmid <- decodeKeyHashid404 hid
getDiscussionMessage shr lmid
postTicketMessageR :: ShrIdent -> PrjIdent -> Int -> KeyHashid Message -> Handler Html
postTicketMessageR shar proj tnum hid = do
encodeHid <- getEncodeKeyHashid
mid <- decodeKeyHashid404 hid
2016-05-20 07:07:25 +09:00
postReply
(TicketReplyR shar proj tnum . encodeHid)
(TicketMessageR shar proj tnum . encodeHid)
2016-05-20 07:07:25 +09:00
(const $ TicketR shar proj tnum)
(selectDiscussionId shar proj tnum)
mid
2016-05-20 07:07:25 +09:00
getTicketTopReplyR :: ShrIdent -> PrjIdent -> Int -> Handler Html
getTicketTopReplyR shar proj num =
getTopReply $ TicketDiscussionR shar proj num
2016-05-20 07:07:25 +09:00
getTicketReplyR :: ShrIdent -> PrjIdent -> Int -> KeyHashid Message -> Handler Html
getTicketReplyR shar proj tnum hid = do
encodeHid <- getEncodeKeyHashid
mid <- decodeKeyHashid404 hid
2016-05-20 07:07:25 +09:00
getReply
(TicketReplyR shar proj tnum . encodeHid)
(TicketMessageR shar proj tnum . encodeHid)
(selectDiscussionId shar proj tnum)
mid
2016-07-27 17:35:50 +09:00
getTicketDeps :: Bool -> ShrIdent -> PrjIdent -> Int -> Handler Html
getTicketDeps forward shr prj num = do
let from' =
if forward then TicketDependencyParent else TicketDependencyChild
to' =
if forward then TicketDependencyChild else TicketDependencyParent
rows <- runDB $ do
Entity sid _ <- getBy404 $ UniqueSharer shr
Entity jid _ <- getBy404 $ UniqueProject prj sid
Entity tid _ <- getBy404 $ UniqueTicket jid num
select $ from $
\ ( td `InnerJoin`
ticket `InnerJoin`
person `InnerJoin`
sharer
) -> do
on $ person ^. PersonIdent ==. sharer ^. SharerId
on $ ticket ^. TicketCreator ==. person ^. PersonId
on $ td ^. to' ==. ticket ^. TicketId
where_ $ td ^. from' ==. val tid
orderBy [asc $ ticket ^. TicketNumber]
return
( ticket ^. TicketNumber
, sharer
, ticket ^. TicketTitle
, ticket ^. TicketStatus
)
defaultLayout $(widgetFile "ticket/dep/list")
2016-07-27 17:35:50 +09:00
getTicketDepsR :: ShrIdent -> PrjIdent -> Int -> Handler Html
getTicketDepsR = getTicketDeps True
postTicketDepsR :: ShrIdent -> PrjIdent -> Int -> Handler Html
postTicketDepsR shr prj num = do
(jid, tid) <- runDB $ do
Entity sid _ <- getBy404 $ UniqueSharer shr
Entity jid _ <- getBy404 $ UniqueProject prj sid
Entity tid _ <- getBy404 $ UniqueTicket jid num
return (jid, tid)
((result, widget), enctype) <- runFormPost $ ticketDepForm jid tid
case result of
FormSuccess ctid -> do
runDB $ do
let td = TicketDependency
{ ticketDependencyParent = tid
, ticketDependencyChild = ctid
}
insert_ td
trrFix td ticketDepGraph
setMessage "Ticket dependency added."
redirect $ TicketR shr prj num
FormMissing -> do
setMessage "Field(s) missing."
defaultLayout $(widgetFile "ticket/dep/new")
FormFailure _l -> do
setMessage "Submission failed, see errors below."
defaultLayout $(widgetFile "ticket/dep/new")
getTicketDepNewR :: ShrIdent -> PrjIdent -> Int -> Handler Html
getTicketDepNewR shr prj num = do
(jid, tid) <- runDB $ do
Entity sid _ <- getBy404 $ UniqueSharer shr
Entity jid _ <- getBy404 $ UniqueProject prj sid
Entity tid _ <- getBy404 $ UniqueTicket jid num
return (jid, tid)
((_result, widget), enctype) <- runFormPost $ ticketDepForm jid tid
defaultLayout $(widgetFile "ticket/dep/new")
postTicketDepR :: ShrIdent -> PrjIdent -> Int -> Int -> Handler Html
postTicketDepR shr prj pnum cnum = do
mmethod <- lookupPostParam "_method"
case mmethod of
Just "DELETE" -> deleteTicketDepR shr prj pnum cnum
_ -> notFound
deleteTicketDepR :: ShrIdent -> PrjIdent -> Int -> Int -> Handler Html
deleteTicketDepR shr prj pnum cnum = do
runDB $ do
Entity sid _ <- getBy404 $ UniqueSharer shr
Entity jid _ <- getBy404 $ UniqueProject prj sid
Entity ptid _ <- getBy404 $ UniqueTicket jid pnum
Entity ctid _ <- getBy404 $ UniqueTicket jid cnum
Entity tdid _ <- getBy404 $ UniqueTicketDependency ptid ctid
delete tdid
setMessage "Ticket dependency removed."
redirect $ TicketDepsR shr prj pnum
2016-07-27 17:35:50 +09:00
getTicketReverseDepsR :: ShrIdent -> PrjIdent -> Int -> Handler Html
getTicketReverseDepsR = getTicketDeps False