mirror of
https://code.sup39.dev/repos/Wqawg
synced 2024-12-27 16:54:53 +09:00
Ticket dependency add/remove and some fixes to recursive SQL
This commit is contained in:
parent
ddd4393825
commit
5c153b0294
16 changed files with 231 additions and 19 deletions
|
@ -109,7 +109,9 @@
|
||||||
/s/#ShrIdent/p/#PrjIdent/t/#Int/d/#Int TicketMessageR GET POST
|
/s/#ShrIdent/p/#PrjIdent/t/#Int/d/#Int TicketMessageR GET POST
|
||||||
/s/#ShrIdent/p/#PrjIdent/t/#Int/d/!reply TicketTopReplyR GET
|
/s/#ShrIdent/p/#PrjIdent/t/#Int/d/!reply TicketTopReplyR GET
|
||||||
/s/#ShrIdent/p/#PrjIdent/t/#Int/d/#Int/reply TicketReplyR GET
|
/s/#ShrIdent/p/#PrjIdent/t/#Int/d/#Int/reply TicketReplyR GET
|
||||||
/s/#ShrIdent/p/#PrjIdent/t/#Int/deps TicketDepsR GET
|
/s/#ShrIdent/p/#PrjIdent/t/#Int/deps TicketDepsR GET POST
|
||||||
|
/s/#ShrIdent/p/#PrjIdent/t/#Int/deps/!new TicketDepNewR GET
|
||||||
|
/s/#ShrIdent/p/#PrjIdent/t/#Int/deps/#Int TicketDepR POST DELETE
|
||||||
/s/#ShrIdent/p/#PrjIdent/t/#Int/rdeps TicketReverseDepsR GET
|
/s/#ShrIdent/p/#PrjIdent/t/#Int/rdeps TicketReverseDepsR GET
|
||||||
|
|
||||||
/s/#ShrIdent/p/#PrjIdent/w/+Texts WikiPageR GET
|
/s/#ShrIdent/p/#PrjIdent/w/+Texts WikiPageR GET
|
||||||
|
|
|
@ -23,5 +23,7 @@ import Prelude
|
||||||
import Database.Persist
|
import Database.Persist
|
||||||
|
|
||||||
class (PersistEntity n, PersistEntity e) => PersistEntityGraph n e where
|
class (PersistEntity n, PersistEntity e) => PersistEntityGraph n e where
|
||||||
|
sourceParam :: e -> Key n
|
||||||
sourceField :: EntityField e (Key n)
|
sourceField :: EntityField e (Key n)
|
||||||
|
destParam :: e -> Key n
|
||||||
destField :: EntityField e (Key n)
|
destField :: EntityField e (Key n)
|
||||||
|
|
|
@ -18,6 +18,8 @@ module Database.Persist.Local.Sql
|
||||||
, rawSqlWithGraph
|
, rawSqlWithGraph
|
||||||
, dummyFromFst
|
, dummyFromFst
|
||||||
, dummyFromSnd
|
, dummyFromSnd
|
||||||
|
, destParamFromProxy
|
||||||
|
, sourceParamFromProxy
|
||||||
, destFieldFromProxy
|
, destFieldFromProxy
|
||||||
, sourceFieldFromProxy
|
, sourceFieldFromProxy
|
||||||
, (?:)
|
, (?:)
|
||||||
|
@ -27,7 +29,7 @@ where
|
||||||
|
|
||||||
import Prelude
|
import Prelude
|
||||||
|
|
||||||
import Control.Monad.IO.Class (MonadIO)
|
import Control.Monad.IO.Class (MonadIO, liftIO)
|
||||||
import Control.Monad.Trans.Reader (ReaderT, ask)
|
import Control.Monad.Trans.Reader (ReaderT, ask)
|
||||||
import Data.Int (Int64)
|
import Data.Int (Int64)
|
||||||
import Data.Monoid ((<>))
|
import Data.Monoid ((<>))
|
||||||
|
@ -37,7 +39,7 @@ import Database.Persist
|
||||||
import Database.Persist.Sql
|
import Database.Persist.Sql
|
||||||
import Database.Persist.Sql.Util
|
import Database.Persist.Sql.Util
|
||||||
|
|
||||||
import qualified Data.Text as T (null, intercalate)
|
import qualified Data.Text as T (null, unpack, intercalate)
|
||||||
|
|
||||||
import Database.Persist.Local.Class.PersistEntityGraph
|
import Database.Persist.Local.Class.PersistEntityGraph
|
||||||
import Database.Persist.Local.Class.PersistQueryForest
|
import Database.Persist.Local.Class.PersistQueryForest
|
||||||
|
@ -55,6 +57,20 @@ dummyFromFst _ = Nothing
|
||||||
dummyFromSnd :: Proxy (a, b) -> Maybe b
|
dummyFromSnd :: Proxy (a, b) -> Maybe b
|
||||||
dummyFromSnd _ = Nothing
|
dummyFromSnd _ = Nothing
|
||||||
|
|
||||||
|
destParamFromProxy
|
||||||
|
:: PersistEntityGraph node edge
|
||||||
|
=> Proxy (node, edge)
|
||||||
|
-> edge
|
||||||
|
-> Key node
|
||||||
|
destParamFromProxy _ = destParam
|
||||||
|
|
||||||
|
sourceParamFromProxy
|
||||||
|
:: PersistEntityGraph node edge
|
||||||
|
=> Proxy (node, edge)
|
||||||
|
-> edge
|
||||||
|
-> Key node
|
||||||
|
sourceParamFromProxy _ = sourceParam
|
||||||
|
|
||||||
destFieldFromProxy
|
destFieldFromProxy
|
||||||
:: PersistEntityGraph node edge
|
:: PersistEntityGraph node edge
|
||||||
=> Proxy (node, edge)
|
=> Proxy (node, edge)
|
||||||
|
|
|
@ -157,7 +157,7 @@ xmconnectsm' follow filter msource mdest mlen proxy = do
|
||||||
Just _ -> mconcat
|
Just _ -> mconcat
|
||||||
[ " FROM ", dbname $ entityDB tNode
|
[ " FROM ", dbname $ entityDB tNode
|
||||||
, " WHERE ", entityDB tNode ^* fieldDB (entityId tNode)
|
, " WHERE ", entityDB tNode ^* fieldDB (entityId tNode)
|
||||||
, " IN ?"
|
, " = ANY(?)"
|
||||||
]
|
]
|
||||||
, " UNION ALL "
|
, " UNION ALL "
|
||||||
, case follow of
|
, case follow of
|
||||||
|
@ -174,10 +174,10 @@ xmconnectsm' follow filter msource mdest mlen proxy = do
|
||||||
, " FROM ", dbname temp
|
, " FROM ", dbname temp
|
||||||
, case mdest of
|
, case mdest of
|
||||||
Nothing -> ""
|
Nothing -> ""
|
||||||
Just _ -> " WHERE ", temp ^* tid, " IN ?"
|
Just _ -> " WHERE " <> temp ^* tid <> " = ANY(?)"
|
||||||
, case mlen of
|
, case mlen of
|
||||||
Nothing -> ""
|
Nothing -> ""
|
||||||
Just _ -> " AND array_length(", temp ^* tpath, ", 1) <= ?"
|
Just _ -> " AND array_length(" <> temp ^* tpath <> ", 1) <= ?"
|
||||||
, " )"
|
, " )"
|
||||||
]
|
]
|
||||||
toP = fmap toPersistValue
|
toP = fmap toPersistValue
|
||||||
|
|
|
@ -251,13 +251,14 @@ trrFix
|
||||||
, SqlBackend ~ PersistEntityBackend node
|
, SqlBackend ~ PersistEntityBackend node
|
||||||
, SqlBackend ~ PersistEntityBackend edge
|
, SqlBackend ~ PersistEntityBackend edge
|
||||||
)
|
)
|
||||||
=> Key edge
|
=> edge
|
||||||
-> Key edge
|
|
||||||
-> Proxy (node, edge)
|
-> Proxy (node, edge)
|
||||||
-> ReaderT SqlBackend m Int64
|
-> ReaderT SqlBackend m Int64
|
||||||
trrFix from to proxy = do
|
trrFix edge proxy = do
|
||||||
conn <- ask
|
conn <- ask
|
||||||
let tNode = entityDef $ dummyFromFst proxy
|
let from = sourceParamFromProxy proxy edge
|
||||||
|
to = destParamFromProxy proxy edge
|
||||||
|
tNode = entityDef $ dummyFromFst proxy
|
||||||
tEdge = entityDef $ dummyFromSnd proxy
|
tEdge = entityDef $ dummyFromSnd proxy
|
||||||
fwd = persistFieldDef $ destFieldFromProxy proxy
|
fwd = persistFieldDef $ destFieldFromProxy proxy
|
||||||
bwd = persistFieldDef $ sourceFieldFromProxy proxy
|
bwd = persistFieldDef $ sourceFieldFromProxy proxy
|
||||||
|
|
|
@ -15,18 +15,26 @@
|
||||||
|
|
||||||
module Vervis.Field.Ticket
|
module Vervis.Field.Ticket
|
||||||
( selectAssigneeFromProject
|
( selectAssigneeFromProject
|
||||||
|
, selectTicketDep
|
||||||
)
|
)
|
||||||
where
|
where
|
||||||
|
|
||||||
import Prelude
|
import Prelude
|
||||||
|
|
||||||
import Control.Arrow ((***))
|
import Control.Arrow ((***))
|
||||||
import Database.Esqueleto
|
import Data.Text (Text)
|
||||||
import Yesod.Form.Fields (selectField, optionsPairs)
|
import Database.Esqueleto hiding ((%))
|
||||||
|
import Formatting
|
||||||
|
import Yesod.Form.Fields (selectField, optionsPairs, optionsPersistKey)
|
||||||
|
import Yesod.Form.Functions (checkBool, checkM)
|
||||||
import Yesod.Form.Types (Field)
|
import Yesod.Form.Types (Field)
|
||||||
import Yesod.Persist.Core (runDB)
|
import Yesod.Persist.Core (runDB)
|
||||||
|
|
||||||
|
import qualified Database.Persist as P
|
||||||
|
|
||||||
|
import Database.Persist.Sql.Graph.Connects (uconnects)
|
||||||
import Vervis.Foundation (Handler)
|
import Vervis.Foundation (Handler)
|
||||||
|
import Vervis.GraphProxy (ticketDepGraph)
|
||||||
import Vervis.Model
|
import Vervis.Model
|
||||||
import Vervis.Model.Ident (shr2text)
|
import Vervis.Model.Ident (shr2text)
|
||||||
|
|
||||||
|
@ -44,3 +52,25 @@ selectAssigneeFromProject pid jid = selectField $ do
|
||||||
person ^. PersonId !=. val pid
|
person ^. PersonId !=. val pid
|
||||||
return (sharer ^. SharerIdent, person ^. PersonId)
|
return (sharer ^. SharerIdent, person ^. PersonId)
|
||||||
optionsPairs $ map (shr2text . unValue *** unValue) l
|
optionsPairs $ map (shr2text . unValue *** unValue) l
|
||||||
|
|
||||||
|
checkNotSelf :: TicketId -> Field Handler TicketId -> Field Handler TicketId
|
||||||
|
checkNotSelf tidP =
|
||||||
|
checkBool (/= tidP) ("A ticket can’t depend on itself" :: Text)
|
||||||
|
|
||||||
|
checkDep :: TicketId -> Field Handler TicketId -> Field Handler TicketId
|
||||||
|
checkDep tidP = checkM $ \ tidC -> do
|
||||||
|
uconn <- runDB $ uconnects tidP tidC Nothing ticketDepGraph
|
||||||
|
return $ if uconn
|
||||||
|
then Left ("There is already a dependency between the tickets" :: Text)
|
||||||
|
else Right tidC
|
||||||
|
|
||||||
|
-- | Select a ticket from a project, but exclude the given ticket ID. When
|
||||||
|
-- processing the form, verify there is no depedndency between the tickets
|
||||||
|
-- (i.e. neither is reachable from the other).
|
||||||
|
selectTicketDep :: ProjectId -> TicketId -> Field Handler TicketId
|
||||||
|
selectTicketDep jid tid =
|
||||||
|
checkDep tid $
|
||||||
|
checkNotSelf tid $
|
||||||
|
selectField $
|
||||||
|
optionsPersistKey [TicketProject P.==. jid, TicketId P.!=. tid] [] $
|
||||||
|
\ t -> sformat (int % " :: " % stext) (ticketNumber t) (ticketTitle t)
|
||||||
|
|
|
@ -20,6 +20,7 @@ module Vervis.Form.Ticket
|
||||||
, assignTicketForm
|
, assignTicketForm
|
||||||
, claimRequestForm
|
, claimRequestForm
|
||||||
, ticketFilterForm
|
, ticketFilterForm
|
||||||
|
, ticketDepForm
|
||||||
)
|
)
|
||||||
where
|
where
|
||||||
|
|
||||||
|
@ -104,3 +105,9 @@ ticketFilterAForm = TicketFilter
|
||||||
|
|
||||||
ticketFilterForm :: Form TicketFilter
|
ticketFilterForm :: Form TicketFilter
|
||||||
ticketFilterForm = renderDivs ticketFilterAForm
|
ticketFilterForm = renderDivs ticketFilterAForm
|
||||||
|
|
||||||
|
ticketDepAForm :: ProjectId -> TicketId -> AForm Handler TicketId
|
||||||
|
ticketDepAForm jid tid = areq (selectTicketDep jid tid) "Dependency" Nothing
|
||||||
|
|
||||||
|
ticketDepForm :: ProjectId -> TicketId -> Form TicketId
|
||||||
|
ticketDepForm jid tid = renderDivs $ ticketDepAForm jid tid
|
||||||
|
|
|
@ -178,6 +178,9 @@ instance Yesod App where
|
||||||
(TicketMessageR _ _ _ _ , True) -> personAny
|
(TicketMessageR _ _ _ _ , True) -> personAny
|
||||||
(TicketTopReplyR _ _ _ , _ ) -> personAny
|
(TicketTopReplyR _ _ _ , _ ) -> personAny
|
||||||
(TicketReplyR _ _ _ _ , _ ) -> personAny
|
(TicketReplyR _ _ _ _ , _ ) -> personAny
|
||||||
|
(TicketDepsR s j _ , True) -> projOp ProjOpAddTicketDep s j
|
||||||
|
(TicketDepNewR s j _ , _ ) -> projOp ProjOpAddTicketDep s j
|
||||||
|
(TicketDepR s j _ _ , True) -> projOp ProjOpRemoveTicketDep s j
|
||||||
_ -> return Authorized
|
_ -> return Authorized
|
||||||
where
|
where
|
||||||
nobody :: Handler AuthResult
|
nobody :: Handler AuthResult
|
||||||
|
@ -495,6 +498,12 @@ instance YesodBreadcrumbs App where
|
||||||
TicketDepsR shr prj num -> ( "Dependencies"
|
TicketDepsR shr prj num -> ( "Dependencies"
|
||||||
, Just $ TicketR shr prj num
|
, Just $ TicketR shr prj num
|
||||||
)
|
)
|
||||||
|
TicketDepNewR shr prj num -> ( "New dependency"
|
||||||
|
, Just $ TicketDepsR shr prj num
|
||||||
|
)
|
||||||
|
TicketDepR shr prj pnum cnum -> ( T.pack $ '#' : show cnum
|
||||||
|
, Just $ TicketDepsR shr prj pnum
|
||||||
|
)
|
||||||
TicketReverseDepsR shr prj num -> ( "Dependants"
|
TicketReverseDepsR shr prj num -> ( "Dependants"
|
||||||
, Just $ TicketR shr prj num
|
, Just $ TicketR shr prj num
|
||||||
)
|
)
|
||||||
|
|
45
src/Vervis/GraphProxy.hs
Normal file
45
src/Vervis/GraphProxy.hs
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
{- This file is part of Vervis.
|
||||||
|
-
|
||||||
|
- Written in 2016 by fr33domlover <fr33domlover@riseup.net>.
|
||||||
|
-
|
||||||
|
- ♡ 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/>.
|
||||||
|
-}
|
||||||
|
|
||||||
|
-- | Persistent graphs are specified using the 'PersistEntityGraph' typeclass,
|
||||||
|
-- using value functions which specify fields. But the DB schema is known at
|
||||||
|
-- development time, and a specific graph needs to be picked statically. Since
|
||||||
|
-- the 'persistent' package doesn't have compile-time (e.g. type-level)
|
||||||
|
-- representation of the schema (but instead converts from TH directly to
|
||||||
|
-- datatypes), the graph related functions use a 'Proxy' which specifies the
|
||||||
|
-- graph using the type.
|
||||||
|
--
|
||||||
|
-- I don't know enough about type systems and advanced type features and GHC
|
||||||
|
-- extensions, to tell whether a better solution is possible. For now, this is
|
||||||
|
-- how things work.
|
||||||
|
--
|
||||||
|
-- This module is a helper for easily specifying graphs instead of typing the
|
||||||
|
-- proxy type directly each time, which may be long and cumbersome.
|
||||||
|
module Vervis.GraphProxy
|
||||||
|
( GraphProxy
|
||||||
|
, ticketDepGraph
|
||||||
|
)
|
||||||
|
where
|
||||||
|
|
||||||
|
import Prelude
|
||||||
|
|
||||||
|
import Data.Proxy
|
||||||
|
|
||||||
|
import Vervis.Model
|
||||||
|
|
||||||
|
type GraphProxy n e = Proxy (n, e)
|
||||||
|
|
||||||
|
ticketDepGraph :: GraphProxy Ticket TicketDependency
|
||||||
|
ticketDepGraph = Proxy
|
|
@ -41,6 +41,10 @@ module Vervis.Handler.Ticket
|
||||||
, getTicketTopReplyR
|
, getTicketTopReplyR
|
||||||
, getTicketReplyR
|
, getTicketReplyR
|
||||||
, getTicketDepsR
|
, getTicketDepsR
|
||||||
|
, postTicketDepsR
|
||||||
|
, getTicketDepNewR
|
||||||
|
, postTicketDepR
|
||||||
|
, deleteTicketDepR
|
||||||
, getTicketReverseDepsR
|
, getTicketReverseDepsR
|
||||||
)
|
)
|
||||||
where
|
where
|
||||||
|
@ -57,7 +61,7 @@ import Data.Time.Calendar (Day (..))
|
||||||
import Data.Time.Clock (UTCTime (..), getCurrentTime)
|
import Data.Time.Clock (UTCTime (..), getCurrentTime)
|
||||||
import Data.Time.Format (formatTime, defaultTimeLocale)
|
import Data.Time.Format (formatTime, defaultTimeLocale)
|
||||||
import Data.Traversable (for)
|
import Data.Traversable (for)
|
||||||
import Database.Esqueleto hiding ((==.), (=.), (+=.), update)
|
import Database.Esqueleto hiding ((==.), (=.), (+=.), update, delete)
|
||||||
import Database.Persist
|
import Database.Persist
|
||||||
import Text.Blaze.Html (Html, toHtml)
|
import Text.Blaze.Html (Html, toHtml)
|
||||||
import Yesod.Auth (requireAuthId, maybeAuthId)
|
import Yesod.Auth (requireAuthId, maybeAuthId)
|
||||||
|
@ -70,9 +74,11 @@ import Yesod.Persist.Core (runDB, get404, getBy404)
|
||||||
import qualified Data.Text as T (filter, intercalate, pack)
|
import qualified Data.Text as T (filter, intercalate, pack)
|
||||||
import qualified Database.Esqueleto as E ((==.))
|
import qualified Database.Esqueleto as E ((==.))
|
||||||
|
|
||||||
|
import Database.Persist.Sql.Graph.TransitiveReduction (trrFix)
|
||||||
import Vervis.Form.Ticket
|
import Vervis.Form.Ticket
|
||||||
import Vervis.Foundation
|
import Vervis.Foundation
|
||||||
import Vervis.Handler.Discussion
|
import Vervis.Handler.Discussion
|
||||||
|
import Vervis.GraphProxy (ticketDepGraph)
|
||||||
import Vervis.MediaType (MediaType (Markdown))
|
import Vervis.MediaType (MediaType (Markdown))
|
||||||
import Vervis.Model
|
import Vervis.Model
|
||||||
import Vervis.Model.Ident
|
import Vervis.Model.Ident
|
||||||
|
@ -576,5 +582,60 @@ getTicketDeps forward shr prj num = do
|
||||||
getTicketDepsR :: ShrIdent -> PrjIdent -> Int -> Handler Html
|
getTicketDepsR :: ShrIdent -> PrjIdent -> Int -> Handler Html
|
||||||
getTicketDepsR = getTicketDeps True
|
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
|
||||||
|
|
||||||
getTicketReverseDepsR :: ShrIdent -> PrjIdent -> Int -> Handler Html
|
getTicketReverseDepsR :: ShrIdent -> PrjIdent -> Int -> Handler Html
|
||||||
getTicketReverseDepsR = getTicketDeps False
|
getTicketReverseDepsR = getTicketDeps False
|
||||||
|
|
|
@ -24,6 +24,7 @@ import Database.Persist.Quasi
|
||||||
import Database.Persist.Sql (fromSqlKey)
|
import Database.Persist.Sql (fromSqlKey)
|
||||||
import Yesod.Auth.HashDB (HashDBUser (..))
|
import Yesod.Auth.HashDB (HashDBUser (..))
|
||||||
|
|
||||||
|
import Database.Persist.Local.Class.PersistEntityGraph
|
||||||
import Vervis.Model.Group
|
import Vervis.Model.Group
|
||||||
import Vervis.Model.Ident
|
import Vervis.Model.Ident
|
||||||
import Vervis.Model.Repo
|
import Vervis.Model.Repo
|
||||||
|
@ -53,3 +54,9 @@ instance Hashable RepoRoleId where
|
||||||
instance Hashable ProjectRoleId where
|
instance Hashable ProjectRoleId where
|
||||||
hashWithSalt salt = hashWithSalt salt . fromSqlKey
|
hashWithSalt salt = hashWithSalt salt . fromSqlKey
|
||||||
hash = hash . fromSqlKey
|
hash = hash . fromSqlKey
|
||||||
|
|
||||||
|
instance PersistEntityGraph Ticket TicketDependency where
|
||||||
|
sourceParam = ticketDependencyParent
|
||||||
|
sourceField = TicketDependencyParent
|
||||||
|
destParam = ticketDependencyChild
|
||||||
|
destField = TicketDependencyChild
|
||||||
|
|
|
@ -33,6 +33,8 @@ data ProjectOperation
|
||||||
| ProjOpUnclaimTicket
|
| ProjOpUnclaimTicket
|
||||||
| ProjOpAssignTicket
|
| ProjOpAssignTicket
|
||||||
| ProjOpUnassignTicket
|
| ProjOpUnassignTicket
|
||||||
|
| ProjOpAddTicketDep
|
||||||
|
| ProjOpRemoveTicketDep
|
||||||
deriving (Eq, Show, Read, Enum, Bounded)
|
deriving (Eq, Show, Read, Enum, Bounded)
|
||||||
|
|
||||||
derivePersistField "ProjectOperation"
|
derivePersistField "ProjectOperation"
|
||||||
|
|
|
@ -71,16 +71,16 @@ getProjectRoleAncestorWithOpQ op role = do
|
||||||
ProjectRoleInheritParent
|
ProjectRoleInheritParent
|
||||||
ProjectRoleInheritChild
|
ProjectRoleInheritChild
|
||||||
(\ temp -> mconcat
|
(\ temp -> mconcat
|
||||||
[ "SELECT ", qcols
|
[ "SELECT ??"
|
||||||
, " FROM ", dbname temp, ", ", tAcc
|
, " FROM ", dbname temp, " INNER JOIN ", tAcc
|
||||||
, " WHERE "
|
, " ON "
|
||||||
, dbname temp, ".", field ProjectRoleInheritParent
|
, dbname temp, ".", field ProjectRoleInheritParent
|
||||||
, " = "
|
, " = "
|
||||||
, tAcc, ".", field ProjectAccessRole
|
, tAcc, ".", field ProjectAccessRole
|
||||||
, " AND "
|
, " WHERE "
|
||||||
, tAcc, ".", field ProjectAccessOp
|
, tAcc, ".", field ProjectAccessOp
|
||||||
, " = ? "
|
, " = ?"
|
||||||
, " LIMIT TO 1"
|
, " LIMIT 1"
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
[toPersistValue op]
|
[toPersistValue op]
|
||||||
|
|
|
@ -18,6 +18,8 @@ $# <http://creativecommons.org/publicdomain/zero/1.0/>.
|
||||||
<th>Author
|
<th>Author
|
||||||
<th>Title
|
<th>Title
|
||||||
<th>Done
|
<th>Done
|
||||||
|
$if forward
|
||||||
|
<th>Remove dependency
|
||||||
$forall (Value number, Entity _ author, Value title, Value done) <- rows
|
$forall (Value number, Entity _ author, Value title, Value done) <- rows
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
|
@ -28,3 +30,13 @@ $# <http://creativecommons.org/publicdomain/zero/1.0/>.
|
||||||
<a href=@{TicketR shr prj number}>#{title}
|
<a href=@{TicketR shr prj number}>#{title}
|
||||||
<td>
|
<td>
|
||||||
#{done}
|
#{done}
|
||||||
|
$if forward
|
||||||
|
<td>
|
||||||
|
<form method=POST action=@{TicketDepR shr prj num number}>
|
||||||
|
<input type=hidden name=_method value=DELETE>
|
||||||
|
<input type=submit value="Remove">
|
||||||
|
|
||||||
|
$if forward
|
||||||
|
<p>
|
||||||
|
<a href=@{TicketDepNewR shr prj num}>
|
||||||
|
Add new…
|
||||||
|
|
17
templates/ticket/dep/new.hamlet
Normal file
17
templates/ticket/dep/new.hamlet
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
$# This file is part of Vervis.
|
||||||
|
$#
|
||||||
|
$# Written in 2016 by fr33domlover <fr33domlover@riseup.net>.
|
||||||
|
$#
|
||||||
|
$# ♡ 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/>.
|
||||||
|
|
||||||
|
<form method=POST action=@{TicketDepsR shr prj num} enctype=#{enctype}>
|
||||||
|
^{widget}
|
||||||
|
<input type=submit>
|
|
@ -119,6 +119,7 @@ library
|
||||||
Vervis.Foundation
|
Vervis.Foundation
|
||||||
Vervis.Git
|
Vervis.Git
|
||||||
Vervis.GitOld
|
Vervis.GitOld
|
||||||
|
Vervis.GraphProxy
|
||||||
Vervis.Handler.Common
|
Vervis.Handler.Common
|
||||||
Vervis.Handler.Discussion
|
Vervis.Handler.Discussion
|
||||||
Vervis.Handler.Git
|
Vervis.Handler.Git
|
||||||
|
|
Loading…
Reference in a new issue