UI: New ticket/MR creation form + deck/loom navigation links

This commit is contained in:
fr33domlover 2022-10-17 15:08:00 +00:00
parent d8c65930ca
commit 118b787416
33 changed files with 408 additions and 322 deletions

View file

@ -24,7 +24,7 @@ module Vervis.Client
--, followProject --, followProject
--, followTicket --, followTicket
--, followRepo --, followRepo
--, offerTicket , offerIssue
--, resolve --, resolve
--, undoFollowSharer --, undoFollowSharer
--, undoFollowProject --, undoFollowProject
@ -299,55 +299,65 @@ followRepo shrAuthor shrObject rpObject hide = do
let uObject = encodeRouteHome $ RepoR shrObject rpObject let uObject = encodeRouteHome $ RepoR shrObject rpObject
follow shrAuthor uObject uObject hide follow shrAuthor uObject uObject hide
-} -}
-}
offerIssue
:: KeyHashid Person -> Text -> PandocMarkdown -> FedURI
-> ExceptT Text Handler (Maybe HTML, [Aud URIMode], AP.Ticket URIMode)
offerIssue senderHash title desc uTracker = do
tracker <- do
tracker <- checkTracker uTracker
case tracker of
TrackerDeck deckID -> Left <$> encodeKeyHashid deckID
TrackerLoom _ -> throwE "Local patch tracker doesn't take issues"
TrackerRemote (ObjURI hTracker luTracker) -> Right <$> do
instanceID <- lift $ runDB $ either entityKey id <$> insertBy' (Instance hTracker)
result <- ExceptT $ first (T.pack . displayException) <$> fetchRemoteActor instanceID hTracker luTracker
case result of
Left Nothing -> throwE "Tracker @id mismatch"
Left (Just err) -> throwE $ T.pack $ displayException err
Right Nothing -> throwE "Tracker isn't an actor"
Right (Just actor) -> return (entityVal actor, uTracker)
offerTicket
:: (MonadHandler m, HandlerSite m ~ App, MonadSite m, SiteEnv m ~ App)
=> ShrIdent -> TextHtml -> TextPandocMarkdown -> ShrIdent -> PrjIdent -> m (Either Text (TextHtml, Audience URIMode, AP.Ticket URIMode, FedURI))
offerTicket shrAuthor (TextHtml title) (TextPandocMarkdown desc) shr prj = runExceptT $ do
error "Temporarily disabled"
{-
encodeRouteLocal <- getEncodeRouteLocal
encodeRouteHome <- getEncodeRouteHome
descHtml <- ExceptT . pure $ renderPandocMarkdown desc descHtml <- ExceptT . pure $ renderPandocMarkdown desc
summary <-
TextHtml . TL.toStrict . renderHtml <$> encodeRouteLocal <- getEncodeRouteLocal
withUrlRenderer hLocal <- asksSite siteInstanceHost
[hamlet|
<p> let audAuthor =
<a href=@{SharerR shrAuthor}> AudLocal [] [LocalStagePersonFollowers senderHash]
#{shr2text shrAuthor} audTracker =
\ offered a ticket to project # case tracker of
<a href=@{ProjectR shr prj}> Left deckHash ->
./s/#{shr2text shr}/p/#{prj2text prj} AudLocal
: #{preEscapedToHtml title}. [LocalActorDeck deckHash]
|] [LocalStageDeckFollowers deckHash]
let recipsA = [ProjectR shr prj] Right (remoteActor, ObjURI hTracker luTracker) ->
recipsC = [ProjectTeamR shr prj, ProjectFollowersR shr prj] AudRemote hTracker
[luTracker]
(maybeToList $ remoteActorFollowers remoteActor)
audience = [audAuthor, audTracker]
ticket = AP.Ticket ticket = AP.Ticket
{ AP.ticketLocal = Nothing { AP.ticketLocal = Nothing
, AP.ticketAttributedTo = encodeRouteLocal $ SharerR shrAuthor , AP.ticketAttributedTo = encodeRouteLocal $ PersonR senderHash
, AP.ticketPublished = Nothing , AP.ticketPublished = Nothing
, AP.ticketUpdated = Nothing , AP.ticketUpdated = Nothing
, AP.ticketContext = Nothing , AP.ticketContext = Nothing
-- , AP.ticketName = Nothing , AP.ticketSummary = encodeEntities title
, AP.ticketSummary = TextHtml title , AP.ticketContent = descHtml
, AP.ticketContent = TextHtml descHtml , AP.ticketSource = desc
, AP.ticketSource = TextPandocMarkdown desc
, AP.ticketAssignedTo = Nothing , AP.ticketAssignedTo = Nothing
, AP.ticketResolved = Nothing , AP.ticketResolved = Nothing
, AP.ticketAttachment = Nothing , AP.ticketAttachment = Nothing
} }
target = encodeRouteHome $ ProjectR shr prj
audience = Audience
{ audienceTo = map encodeRouteHome $ recipsA ++ recipsC
, audienceBto = []
, audienceCc = []
, audienceBcc = []
, audienceGeneral = []
, audienceNonActors = map encodeRouteHome recipsC
}
return (summary, audience, ticket, target)
return (Nothing, audience, ticket)
{-
{-
resolve resolve
:: (MonadHandler m, HandlerSite m ~ App, MonadSite m, SiteEnv m ~ App) :: (MonadHandler m, HandlerSite m ~ App, MonadSite m, SiteEnv m ~ App)
=> ShrIdent => ShrIdent

View file

@ -14,12 +14,16 @@
-} -}
module Vervis.Form.Ticket module Vervis.Form.Ticket
( --NewTicket (..) ( fedUriField
--, newTicketForm
, NewTicket (..)
, NewCloth (..)
, newTicketForm
, newClothForm
--, editTicketContentForm --, editTicketContentForm
--, assignTicketForm --, assignTicketForm
--, claimRequestForm --, claimRequestForm
ticketFilterForm , ticketFilterForm
--, ticketDepForm --, ticketDepForm
) )
where where
@ -32,13 +36,19 @@ import Data.Maybe
import Data.Text (Text) import Data.Text (Text)
import Data.Time.Calendar (Day (..)) import Data.Time.Calendar (Day (..))
import Data.Time.Clock (getCurrentTime, UTCTime (..)) import Data.Time.Clock (getCurrentTime, UTCTime (..))
import Data.Traversable
import Database.Persist import Database.Persist
import Text.HTML.SanitizeXSS import Yesod.Core
import Yesod.Form import Yesod.Form
import Yesod.Persist.Core (runDB) import Yesod.Persist.Core (runDB)
import qualified Data.Text as T import qualified Data.Text as T
import Development.PatchMediaType
import Network.FedURI
import Web.Text
import Vervis.FedURI
import Vervis.Foundation (App, Form, Handler) import Vervis.Foundation (App, Form, Handler)
import Vervis.Model import Vervis.Model
import Vervis.Model.Ticket import Vervis.Model.Ticket
@ -46,18 +56,36 @@ import Vervis.Model.Workflow
import Vervis.Ticket import Vervis.Ticket
import Vervis.TicketFilter (TicketFilter (..)) import Vervis.TicketFilter (TicketFilter (..))
fedUriField
:: (Monad m, RenderMessage (HandlerSite m) FormMessage) => Field m FedURI
fedUriField = Field
{ fieldParse = parseHelper $ \ t ->
case parseObjURI t of
Left e -> Left $ MsgInvalidUrl $ T.pack e <> ": " <> t
Right u -> Right u
, fieldView = \theId name attrs val isReq ->
[whamlet|<input ##{theId} name=#{name} *{attrs} type=url :isReq:required value=#{either id renderObjURI val}>|]
, fieldEnctype = UrlEncoded
}
--TODO use custom fields to ensure uniqueness or other constraints? --TODO use custom fields to ensure uniqueness or other constraints?
{-
data NewTicket = NewTicket data NewTicket = NewTicket
{ ntTitle :: Text { ntTitle :: Text
, ntDesc :: Text , ntDesc :: PandocMarkdown
, ntTParams :: [(WorkflowFieldId, Text)] --, ntTParams :: [(WorkflowFieldId, Text)]
, ntEParams :: [(WorkflowFieldId, WorkflowEnumCtorId)] --, ntEParams :: [(WorkflowFieldId, WorkflowEnumCtorId)]
, ntCParams :: [WorkflowFieldId] --, ntCParams :: [WorkflowFieldId]
, ntOffer :: Bool
} }
data NewCloth = NewCloth
{ ncTitle :: Text
, ncDesc :: PandocMarkdown
, ncTarget :: Maybe Text
, ncOrigin :: Maybe (FedURI, Maybe Text)
, ncPatch :: Maybe (PatchMediaType, FileInfo)
}
{-
fieldSettings :: Text -> Bool -> FieldSettings App fieldSettings :: Text -> Bool -> FieldSettings App
fieldSettings name req = fieldSettings name req =
fieldSettingsLabel $ fieldSettingsLabel $
@ -103,9 +131,11 @@ cfield (Entity fid f) =
in if workflowFieldRequired f in if workflowFieldRequired f
then mkval <$> areq checkBoxField sets Nothing then mkval <$> areq checkBoxField sets Nothing
else mkval . fromMaybe False <$> aopt checkBoxField sets Nothing else mkval . fromMaybe False <$> aopt checkBoxField sets Nothing
-}
newTicketForm :: WorkflowId -> Form NewTicket newTicketForm :: WorkflowId -> Form NewTicket
newTicketForm wid html = do newTicketForm wid html = do
{-
(tfs, efs, cfs) <- lift $ runDB $ do (tfs, efs, cfs) <- lift $ runDB $ do
tfs <- selectList tfs <- selectList
[ WorkflowFieldWorkflow ==. wid [ WorkflowFieldWorkflow ==. wid
@ -128,16 +158,37 @@ newTicketForm wid html = do
] ]
[] []
return (tfs, efs, cfs) return (tfs, efs, cfs)
-}
flip renderDivs html $ NewTicket flip renderDivs html $ NewTicket
<$> (sanitizeBalance <$> areq textField "Title*" Nothing) <$> (areq textField "Title*" Nothing)
<*> ( maybe "" (T.filter (/= '\r') . unTextarea) <$> <*> ( pandocMarkdownFromText . T.filter (/= '\r') . unTextarea <$>
aopt textareaField "Description (Markdown)" Nothing areq textareaField "Description (Markdown)*" Nothing
) )
<*> (catMaybes <$> traverse tfield tfs) -- <*> (catMaybes <$> traverse tfield tfs)
<*> (fmap catMaybes $ sequenceA $ mapMaybe efield efs) -- <*> (fmap catMaybes $ sequenceA $ mapMaybe efield efs)
<*> (catMaybes <$> traverse cfield cfs) -- <*> (catMaybes <$> traverse cfield cfs)
<*> areq checkBoxField "Offer" Nothing
-} newClothForm :: Form NewCloth
newClothForm = renderDivs $ mk
<$> (areq textField "Title*" Nothing)
<*> ( pandocMarkdownFromText . T.filter (/= '\r') . unTextarea <$>
areq textareaField "Description (Markdown)*" Nothing
)
<*> aopt textField "Target branch" Nothing
<*> aopt fedUriField "Origin repo" Nothing
<*> aopt textField "Origin branch" Nothing
<*> aopt (selectFieldList typeList) "Patch type" Nothing
<*> aopt fileField "Patch file" Nothing
where
typeList :: [(Text, PatchMediaType)]
typeList =
[ ("Darcs", PatchMediaTypeDarcs)
, ("Git" , PatchMediaTypeGit)
]
mk title desc targetBranch originRepo originBranch typ file =
NewCloth
title desc targetBranch
((,originBranch) <$> originRepo) ((,) <$> typ <*> file)
{- {-
editTicketContentAForm :: Ticket -> AForm Handler Ticket editTicketContentAForm :: Ticket -> AForm Handler Ticket

View file

@ -13,9 +13,9 @@
- <http://creativecommons.org/publicdomain/zero/1.0/>. - <http://creativecommons.org/publicdomain/zero/1.0/>.
-} -}
module Vervis.Form.Project module Vervis.Form.Tracker
( NewProject (..) ( NewDeck (..)
, newProjectForm , newDeckForm
, NewLoom (..) , NewLoom (..)
, newLoomForm , newLoomForm
--, NewProjectCollab (..) --, NewProjectCollab (..)
@ -41,13 +41,13 @@ import Yesod.Hashids
import Vervis.Foundation import Vervis.Foundation
import Vervis.Model import Vervis.Model
data NewProject = NewProject data NewDeck = NewDeck
{ npName :: Text { ndName :: Text
, npDesc :: Text , ndDesc :: Text
} }
newProjectForm :: Form NewProject newDeckForm :: Form NewDeck
newProjectForm = renderDivs $ NewProject newDeckForm = renderDivs $ NewDeck
<$> areq textField "Name*" Nothing <$> areq textField "Name*" Nothing
<*> areq textField "Description" Nothing <*> areq textField "Description" Nothing

View file

@ -908,6 +908,7 @@ instance YesodBreadcrumbs App where
TicketDepsR d t -> ("Dependencies", Just $ TicketR d t) TicketDepsR d t -> ("Dependencies", Just $ TicketR d t)
TicketReverseDepsR d t -> ("Dependants", Just $ TicketR d t) TicketReverseDepsR d t -> ("Dependants", Just $ TicketR d t)
TicketNewR d -> ("New Ticket", Just $ DeckR d)
TicketFollowR _ _ -> ("", Nothing) TicketFollowR _ _ -> ("", Nothing)
TicketUnfollowR _ _ -> ("", Nothing) TicketUnfollowR _ _ -> ("", Nothing)
TicketReplyR d t -> ("Reply", Just $ TicketR d t) TicketReplyR d t -> ("Reply", Just $ TicketR d t)
@ -940,6 +941,7 @@ instance YesodBreadcrumbs App where
BundleR l c b -> ("Bundle " <> keyHashidText b, Just $ ClothR l c) BundleR l c b -> ("Bundle " <> keyHashidText b, Just $ ClothR l c)
PatchR l c b p -> ("Patch " <> keyHashidText p, Just $ BundleR l c b) PatchR l c b p -> ("Patch " <> keyHashidText p, Just $ BundleR l c b)
ClothNewR l -> ("New Merge Request", Just $ LoomR l)
ClothApplyR _ _ -> ("", Nothing) ClothApplyR _ _ -> ("", Nothing)
ClothFollowR _ _ -> ("", Nothing) ClothFollowR _ _ -> ("", Nothing)
ClothUnfollowR _ _ -> ("", Nothing) ClothUnfollowR _ _ -> ("", Nothing)

View file

@ -79,6 +79,7 @@ import Vervis.API
import Vervis.Client import Vervis.Client
import Vervis.Data.Actor import Vervis.Data.Actor
import Vervis.FedURI import Vervis.FedURI
import Vervis.Form.Ticket
import Vervis.Foundation import Vervis.Foundation
import Vervis.Model import Vervis.Model
import Vervis.Model.Ident import Vervis.Model.Ident
@ -898,93 +899,6 @@ postRepoUnfollowR shrFollowee rpFollowee = do
setUnfollowMessage shrAuthor eid setUnfollowMessage shrAuthor eid
redirect $ RepoR shrFollowee rpFollowee redirect $ RepoR shrFollowee rpFollowee
postProjectTicketsR :: ShrIdent -> PrjIdent -> Handler Html
postProjectTicketsR shr prj = do
wid <- runDB $ do
sid <- getKeyBy404 $ UniqueSharer shr
j <- getValBy404 $ UniqueProject prj sid
return $ projectWorkflow j
((result, widget), enctype) <- runFormPost $ newTicketForm wid
(eperson, sharer) <- do
ep@(Entity _ p) <- requireVerifiedAuth
s <- runDB $ getJust $ personIdent p
return (ep, s)
let shrAuthor = sharerIdent sharer
eid <- runExceptT $ do
NewTicket title desc tparams eparams cparams offer <-
case result of
FormMissing -> throwE "Field(s) missing."
FormFailure _l ->
throwE "Ticket submission failed, see errors below."
FormSuccess nt -> return nt
unless (null tparams && null eparams && null cparams) $
throwE "Custom param support currently disabled"
{-
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
-}
if offer
then Right <$> do
(summary, audience, ticket, target) <-
ExceptT $ offerTicket shrAuthor (TextHtml title) (TextPandocMarkdown desc) shr prj
obiid <- offerTicketC eperson sharer (Just summary) audience ticket target
ExceptT $ runDB $ do
mtal <- getValBy $ UniqueTicketAuthorLocalOpen obiid
return $
case mtal of
Nothing ->
Left
"Offer processed successfully but no ticket \
\created"
Just tal -> Right $ ticketAuthorLocalTicket tal
else Left <$> do
(summary, audience, Create obj mtarget) <- do
encodeRouteHome <- getEncodeRouteHome
let project = encodeRouteHome $ ProjectR shr prj
ExceptT $ createTicket shrAuthor (TextHtml title) (TextPandocMarkdown desc) project project
let ticket =
case obj of
CreateTicket _ t -> t
_ -> error "Create object isn't a ticket"
obiid <- createTicketC eperson sharer (Just summary) audience ticket mtarget
ExceptT $ runDB $ do
mtalid <- getKeyBy $ UniqueTicketAuthorLocalOpen obiid
return $
case mtalid of
Nothing ->
Left
"Create processed successfully but no ticket \
\created"
Just v -> Right v
case eid of
Left e -> do
setMessage $ toHtml e
defaultLayout $(widgetFile "ticket/new")
Right (Left talid) -> do
talkhid <- encodeKeyHashid talid
redirect $ SharerTicketR shr talkhid
Right (Right ltid) -> do
ltkhid <- encodeKeyHashid ltid
eobiidFollow <- runExceptT $ do
(summary, audience, follow) <- followTicket shrAuthor shr prj ltkhid False
followC shrAuthor (Just summary) audience follow
case eobiidFollow of
Left e -> setMessage $ toHtml $ "Ticket created, but following it failed: " <> e
Right _ -> setMessage "Ticket created."
redirect $ ProjectTicketR shr prj ltkhid
postProjectTicketCloseR postProjectTicketCloseR
:: ShrIdent -> PrjIdent -> KeyHashid LocalTicket -> Handler Html :: ShrIdent -> PrjIdent -> KeyHashid LocalTicket -> Handler Html
postProjectTicketCloseR shr prj ltkhid = do postProjectTicketCloseR shr prj ltkhid = do
@ -1016,18 +930,6 @@ postProjectTicketOpenR shr prj ltkhid = do
redirect $ ProjectTicketR shr prj ltkhid redirect $ ProjectTicketR shr prj ltkhid
-} -}
fedUriField
:: (Monad m, RenderMessage (HandlerSite m) FormMessage) => Field m FedURI
fedUriField = Field
{ fieldParse = parseHelper $ \ t ->
case parseObjURI t of
Left e -> Left $ MsgInvalidUrl $ T.pack e <> ": " <> t
Right u -> Right u
, fieldView = \theId name attrs val isReq ->
[whamlet|<input ##{theId} name=#{name} *{attrs} type=url :isReq:required value=#{either id renderObjURI val}>|]
, fieldEnctype = UrlEncoded
}
capField capField
:: Field Handler :: Field Handler
( FedURI ( FedURI

View file

@ -26,6 +26,9 @@ module Vervis.Handler.Cloth
, getClothDepR , getClothDepR
, getClothNewR
, postClothNewR
, postClothApplyR , postClothApplyR
, postClothFollowR , postClothFollowR
, postClothUnfollowR , postClothUnfollowR
@ -66,6 +69,7 @@ module Vervis.Handler.Cloth
) )
where where
import Control.Exception.Base
import Control.Monad import Control.Monad
import Control.Monad.Trans.Except import Control.Monad.Trans.Except
import Data.Bifunctor import Data.Bifunctor
@ -83,10 +87,13 @@ import Network.HTTP.Types.Method
import Text.Blaze.Html (Html, preEscapedToHtml) import Text.Blaze.Html (Html, preEscapedToHtml)
import Yesod.Auth import Yesod.Auth
import Yesod.Core import Yesod.Core
import Yesod.Form
import Yesod.Persist.Core import Yesod.Persist.Core
import qualified Data.List.NonEmpty as NE import qualified Data.List.NonEmpty as NE
import qualified Data.List.Ordered as LO import qualified Data.List.Ordered as LO
import qualified Data.Text as T
import qualified Data.Text.Encoding as TE
import qualified Database.Esqueleto as E import qualified Database.Esqueleto as E
import Data.MediaType import Data.MediaType
@ -97,6 +104,7 @@ import Web.Text
import Yesod.ActivityPub import Yesod.ActivityPub
import Yesod.FedURI import Yesod.FedURI
import Yesod.Hashids import Yesod.Hashids
import Yesod.MonadSite
import Yesod.RenderSource import Yesod.RenderSource
import qualified Web.ActivityPub as AP import qualified Web.ActivityPub as AP
@ -104,6 +112,7 @@ import qualified Web.ActivityPub as AP
import Control.Monad.Trans.Except.Local import Control.Monad.Trans.Except.Local
import Data.Paginate.Local import Data.Paginate.Local
import Database.Persist.Local import Database.Persist.Local
import Yesod.Form.Local
import Yesod.Persist.Local import Yesod.Persist.Local
import Vervis.ActivityPub import Vervis.ActivityPub
@ -112,6 +121,7 @@ import Vervis.Cloth
import Vervis.Data.Actor import Vervis.Data.Actor
import Vervis.Persist.Discussion import Vervis.Persist.Discussion
import Vervis.FedURI import Vervis.FedURI
import Vervis.Form.Ticket
import Vervis.Foundation import Vervis.Foundation
import Vervis.Model import Vervis.Model
import Vervis.Model.Ident import Vervis.Model.Ident
@ -129,6 +139,7 @@ import Vervis.Web.Repo
import Vervis.Widget import Vervis.Widget
import Vervis.Widget.Discussion import Vervis.Widget.Discussion
import Vervis.Widget.Person import Vervis.Widget.Person
import Vervis.Widget.Tracker
import qualified Vervis.Client as C import qualified Vervis.Client as C
@ -284,10 +295,11 @@ getClothR loomHash clothHash = do
where where
getClothHtml = do getClothHtml = do
mpid <- maybeAuthId mpid <- maybeAuthId
(ticket, targetRepo, author, tparams, eparams, cparams, resolved, moriginRepo, mbundle) <- handlerToWidget $ runDB $ do (eloom, actor, ticket, targetRepo, author, tparams, eparams, cparams, resolved, moriginRepo, mbundle) <- handlerToWidget $ runDB $ do
(Entity _ loom, Entity _ cloth, Entity ticketID ticket, author, maybeResolve, proposal) <- (eloom@(Entity _ loom), Entity _ cloth, Entity ticketID ticket, author, maybeResolve, proposal) <-
getCloth404 loomHash clothHash getCloth404 loomHash clothHash
(ticket,,,,,,,,) actor <- getJust $ loomActor loom
(eloom,actor,ticket,,,,,,,,)
<$> getLocalRepo' (loomRepo loom) (ticketLoomBranch cloth) <$> getLocalRepo' (loomRepo loom) (ticketLoomBranch cloth)
<*> bitraverse <*> bitraverse
(\ (Entity _ (TicketAuthorLocal _ personID _)) -> do (\ (Entity _ (TicketAuthorLocal _ personID _)) -> do
@ -626,6 +638,69 @@ getClothDepR _ _ _ = do
tdc tdc
-} -}
getClothNewR :: KeyHashid Loom -> Handler Html
getClothNewR loomHash = do
loomID <- decodeKeyHashid404 loomHash
_ <- runDB $ get404 loomID
((_result, widget), enctype) <- runFormPost newClothForm
defaultLayout $(widgetFile "cloth/new")
postClothNewR :: KeyHashid Loom -> Handler Html
postClothNewR loomHash = do
loomID <- decodeKeyHashid404 loomHash
person@(Entity pid p) <- requireAuth
(loom, senderActor) <- runDB $ do
loom <- get404 loomID
a <- getJust $ personActor p
return (loom, a)
NewCloth title desc targetBranch origin patch <-
runFormPostRedirect (ClothNewR loomHash) newClothForm
encodeRouteHome <- getEncodeRouteHome
errorOrTicket <- runExceptT $ do
let uLoom = encodeRouteHome $ LoomR loomHash
senderHash <- encodeKeyHashid pid
(maybeSummary, audience, ticket) <- do
uTargetRepo <-
encodeRouteHome . RepoR <$> encodeKeyHashid (loomRepo loom)
case (origin, patch) of
(Nothing, Nothing) -> throwE "Neither origin no patch provided"
(Just _, Just _) -> throwE "Both origin and patch provided"
(Just (uRepo, mb), Nothing) ->
C.offerMerge
senderHash title desc uLoom uTargetRepo targetBranch
uRepo mb
(Nothing, Just (typ, fi)) -> do
diff <-
withExceptT (T.pack . displayException) $ ExceptT $
TE.decodeUtf8' <$> fileSourceByteString fi
C.offerPatches
senderHash title desc uLoom uTargetRepo targetBranch
typ (diff :| [])
(localRecips, remoteRecips, fwdHosts, action) <-
lift $ C.makeServerInput Nothing maybeSummary audience $
AP.OfferActivity $ AP.Offer (AP.OfferTicket ticket) uLoom
offerID <-
offerTicketC
person senderActor Nothing localRecips remoteRecips fwdHosts action
ticket uLoom
runDBExcept $ do
mtal <- lift $ getValBy $ UniqueTicketAuthorLocalOpen offerID
tal <- fromMaybeE mtal "Offer processed bu no ticket created"
return $ ticketAuthorLocalTicket tal
case errorOrTicket of
Left e -> do
setMessage $ toHtml e
redirect $ ClothNewR loomHash
Right ticketID -> do
clothID <- do
maybeClothID <- runDB $ getKeyBy $ UniqueTicketLoom ticketID
case maybeClothID of
Nothing -> error "No TicketLoom for the new Ticket"
Just c -> return c
clothHash <- encodeKeyHashid clothID
setMessage "MR created"
redirect $ ClothR loomHash clothHash
postClothApplyR :: KeyHashid Loom -> KeyHashid TicketLoom -> Handler () postClothApplyR :: KeyHashid Loom -> KeyHashid TicketLoom -> Handler ()
postClothApplyR loomHash clothHash = do postClothApplyR loomHash clothHash = do
ep@(Entity personID person) <- requireAuth ep@(Entity personID person) <- requireAuth

View file

@ -103,8 +103,8 @@ import Vervis.Federation.Collab
import Vervis.Federation.Discussion import Vervis.Federation.Discussion
import Vervis.Federation.Ticket import Vervis.Federation.Ticket
import Vervis.FedURI import Vervis.FedURI
import Vervis.Form.Project
import Vervis.Form.Ticket import Vervis.Form.Ticket
import Vervis.Form.Tracker
import Vervis.Foundation import Vervis.Foundation
import Vervis.Model import Vervis.Model
import Vervis.Paginate import Vervis.Paginate
@ -115,6 +115,7 @@ import Vervis.TicketFilter
import Vervis.Web.Actor import Vervis.Web.Actor
import Vervis.Widget.Person import Vervis.Widget.Person
import Vervis.Widget.Ticket import Vervis.Widget.Ticket
import Vervis.Widget.Tracker
import qualified Vervis.Client as C import qualified Vervis.Client as C
@ -226,16 +227,20 @@ getDeckFollowersR = getActorFollowersCollection DeckFollowersR deckActor
getDeckTicketsR :: KeyHashid Deck -> Handler TypedContent getDeckTicketsR :: KeyHashid Deck -> Handler TypedContent
getDeckTicketsR deckHash = selectRep $ do getDeckTicketsR deckHash = selectRep $ do
provideRep $ do provideRep $ do
((filtResult, filtWidget), filtEnctype) <- runFormGet ticketFilterForm let tf = def
{-
((filtResult, filtWidget), filtEnctype) <- runFormPost ticketFilterForm
let tf = let tf =
case filtResult of case filtResult of
FormSuccess filt -> filt FormSuccess filt -> filt
FormMissing -> def FormMissing -> def
FormFailure l -> FormFailure l ->
error $ "Ticket filter form failed: " ++ show l error $ "Ticket filter form failed: " ++ show l
-}
deckID <- decodeKeyHashid404 deckHash deckID <- decodeKeyHashid404 deckHash
(total, pages, mpage) <- runDB $ do (deck, actor, (total, pages, mpage)) <- runDB $ do
_ <- get404 deckID deck <- get404 deckID
actor <- getJust $ deckActor deck
let countAllTickets = count [TicketDeckDeck ==. deckID] let countAllTickets = count [TicketDeckDeck ==. deckID]
selectTickets off lim = selectTickets off lim =
getTicketSummaries getTicketSummaries
@ -243,7 +248,7 @@ getDeckTicketsR deckHash = selectRep $ do
(Just $ \ t -> [E.desc $ t E.^. TicketId]) (Just $ \ t -> [E.desc $ t E.^. TicketId])
(Just (off, lim)) (Just (off, lim))
deckID deckID
getPageAndNavCount countAllTickets selectTickets (deck,actor,) <$> getPageAndNavCount countAllTickets selectTickets
case mpage of case mpage of
Nothing -> redirectFirstPage here Nothing -> redirectFirstPage here
Just (rows, navModel) -> Just (rows, navModel) ->
@ -319,12 +324,12 @@ getDeckMessageR _ _ = notFound
getDeckNewR :: Handler Html getDeckNewR :: Handler Html
getDeckNewR = do getDeckNewR = do
((_result, widget), enctype) <- runFormPost newProjectForm ((_result, widget), enctype) <- runFormPost newDeckForm
defaultLayout $(widgetFile "project/new") defaultLayout $(widgetFile "deck/new")
postDeckNewR :: Handler Html postDeckNewR :: Handler Html
postDeckNewR = do postDeckNewR = do
NewProject name desc <- runFormPostRedirect DeckNewR newProjectForm NewDeck name desc <- runFormPostRedirect DeckNewR newDeckForm
personEntity@(Entity personID person) <- requireAuth personEntity@(Entity personID person) <- requireAuth
personHash <- encodeKeyHashid personID personHash <- encodeKeyHashid personID

View file

@ -80,8 +80,8 @@ import Vervis.Federation.Collab
import Vervis.Federation.Discussion import Vervis.Federation.Discussion
import Vervis.Federation.Ticket import Vervis.Federation.Ticket
import Vervis.FedURI import Vervis.FedURI
import Vervis.Form.Project
import Vervis.Form.Ticket import Vervis.Form.Ticket
import Vervis.Form.Tracker
import Vervis.Foundation import Vervis.Foundation
import Vervis.Model import Vervis.Model
import Vervis.Paginate import Vervis.Paginate
@ -91,6 +91,7 @@ import Vervis.Ticket
import Vervis.TicketFilter import Vervis.TicketFilter
import Vervis.Web.Actor import Vervis.Web.Actor
import Vervis.Widget.Ticket import Vervis.Widget.Ticket
import Vervis.Widget.Tracker
import qualified Vervis.Client as C import qualified Vervis.Client as C
@ -180,16 +181,20 @@ getLoomFollowersR = getActorFollowersCollection LoomFollowersR loomActor
getLoomClothsR :: KeyHashid Loom -> Handler TypedContent getLoomClothsR :: KeyHashid Loom -> Handler TypedContent
getLoomClothsR loomHash = selectRep $ do getLoomClothsR loomHash = selectRep $ do
provideRep $ do provideRep $ do
((filtResult, filtWidget), filtEnctype) <- runFormGet ticketFilterForm let tf = def
{-
((filtResult, filtWidget), filtEnctype) <- runFormPost ticketFilterForm
let tf = let tf =
case filtResult of case filtResult of
FormSuccess filt -> filt FormSuccess filt -> filt
FormMissing -> def FormMissing -> def
FormFailure l -> FormFailure l ->
error $ "Ticket filter form failed: " ++ show l error $ "Ticket filter form failed: " ++ show l
-}
loomID <- decodeKeyHashid404 loomHash loomID <- decodeKeyHashid404 loomHash
(total, pages, mpage) <- runDB $ do (loom, actor, (total, pages, mpage)) <- runDB $ do
_ <- get404 loomID loom <- get404 loomID
actor <- getJust $ loomActor loom
let countAllTickets = count [TicketLoomLoom ==. loomID] let countAllTickets = count [TicketLoomLoom ==. loomID]
selectTickets off lim = selectTickets off lim =
getClothSummaries getClothSummaries
@ -197,7 +202,7 @@ getLoomClothsR loomHash = selectRep $ do
(Just $ \ t -> [E.desc $ t E.^. TicketId]) (Just $ \ t -> [E.desc $ t E.^. TicketId])
(Just (off, lim)) (Just (off, lim))
loomID loomID
getPageAndNavCount countAllTickets selectTickets (loom,actor,) <$> getPageAndNavCount countAllTickets selectTickets
case mpage of case mpage of
Nothing -> redirectFirstPage here Nothing -> redirectFirstPage here
Just (rows, navModel) -> Just (rows, navModel) ->

View file

@ -24,6 +24,9 @@ module Vervis.Handler.Ticket
, getTicketDepR , getTicketDepR
, getTicketNewR
, postTicketNewR
, postTicketFollowR , postTicketFollowR
, postTicketUnfollowR , postTicketUnfollowR
@ -41,8 +44,6 @@ module Vervis.Handler.Ticket
{- {-
, getProjectTicketsR , getProjectTicketsR
, getProjectTicketTreeR , getProjectTicketTreeR
, getProjectTicketNewR
, putProjectTicketR
, deleteProjectTicketR , deleteProjectTicketR
, postProjectTicketR , postProjectTicketR
, getProjectTicketEditR , getProjectTicketEditR
@ -98,7 +99,7 @@ import Network.HTTP.Types (StdMethod (DELETE, POST))
import Text.Blaze.Html (Html, toHtml) import Text.Blaze.Html (Html, toHtml)
import Text.Blaze.Html.Renderer.Text import Text.Blaze.Html.Renderer.Text
import Text.HTML.SanitizeXSS import Text.HTML.SanitizeXSS
import Yesod.Auth (requireAuthId, maybeAuthId) import Yesod.Auth
import Yesod.Core hiding (logWarn) import Yesod.Core hiding (logWarn)
import Yesod.Core.Handler import Yesod.Core.Handler
import Yesod.Core.Widget import Yesod.Core.Widget
@ -128,17 +129,19 @@ import Yesod.RenderSource
import qualified Web.ActivityPub as AP import qualified Web.ActivityPub as AP
import Control.Monad.Trans.Except.Local
import Data.Either.Local import Data.Either.Local
import Data.Maybe.Local (partitionMaybePairs) import Data.Maybe.Local (partitionMaybePairs)
import Data.Paginate.Local import Data.Paginate.Local
import Database.Persist.Local import Database.Persist.Local
import Yesod.Form.Local
import Yesod.Persist.Local import Yesod.Persist.Local
import Vervis.ActivityPub import Vervis.ActivityPub
import Vervis.API import Vervis.API
import Vervis.Data.Actor import Vervis.Data.Actor
import Vervis.Persist.Discussion
import Vervis.FedURI import Vervis.FedURI
import Vervis.Form.Ticket
import Vervis.Foundation import Vervis.Foundation
--import Vervis.GraphProxy (ticketDepGraph) --import Vervis.GraphProxy (ticketDepGraph)
import Vervis.Model import Vervis.Model
@ -147,6 +150,7 @@ import Vervis.Model.Ticket
import Vervis.Model.Workflow import Vervis.Model.Workflow
import Vervis.Paginate import Vervis.Paginate
import Vervis.Persist.Actor import Vervis.Persist.Actor
import Vervis.Persist.Discussion
import Vervis.Recipient import Vervis.Recipient
import Vervis.Settings import Vervis.Settings
import Vervis.Style import Vervis.Style
@ -157,6 +161,9 @@ import Vervis.Web.Actor
import Vervis.Web.Discussion import Vervis.Web.Discussion
import Vervis.Widget.Discussion import Vervis.Widget.Discussion
import Vervis.Widget.Person import Vervis.Widget.Person
import Vervis.Widget.Tracker
import qualified Vervis.Client as C
selectDiscussionID deckHash taskHash = do selectDiscussionID deckHash taskHash = do
(_, _, Entity _ ticket, _, _) <- getTicket404 deckHash taskHash (_, _, Entity _ ticket, _, _) <- getTicket404 deckHash taskHash
@ -251,10 +258,11 @@ getTicketR deckHash ticketHash = do
where where
getTicketHtml = do getTicketHtml = do
mpid <- maybeAuthId mpid <- maybeAuthId
(ticket, author, tparams, eparams, cparams, resolved) <- handlerToWidget $ runDB $ do (edeck, actor, ticket, author, tparams, eparams, cparams, resolved) <- handlerToWidget $ runDB $ do
(_deck, _ticketdeck, Entity ticketID ticket, author, maybeResolve) <- (deck, _ticketdeck, Entity ticketID ticket, author, maybeResolve) <-
getTicket404 deckHash ticketHash getTicket404 deckHash ticketHash
(ticket,,,,,) actor <- getJust $ deckActor $ entityVal deck
(deck,actor,ticket,,,,,)
<$> bitraverse <$> bitraverse
(\ (Entity _ (TicketAuthorLocal _ personID _)) -> do (\ (Entity _ (TicketAuthorLocal _ personID _)) -> do
p <- getJust personID p <- getJust personID
@ -421,6 +429,54 @@ getTicketDepR _ _ _ = do
tdc tdc
-} -}
getTicketNewR :: KeyHashid Deck -> Handler Html
getTicketNewR deckHash = do
deckID <- decodeKeyHashid404 deckHash
wid <- runDB $ deckWorkflow <$> get404 deckID
((_result, widget), enctype) <- runFormPost $ newTicketForm wid
defaultLayout $(widgetFile "ticket/new")
postTicketNewR :: KeyHashid Deck -> Handler Html
postTicketNewR deckHash = do
deckID <- decodeKeyHashid404 deckHash
person@(Entity pid p) <- requireAuth
(wid, actor) <- runDB $ do
wid <- deckWorkflow <$> get404 deckID
a <- getJust $ personActor p
return (wid, a)
NewTicket title desc <-
runFormPostRedirect (TicketNewR deckHash) $ newTicketForm wid
errorOrTicket <- runExceptT $ do
encodeRouteHome <- getEncodeRouteHome
let uDeck = encodeRouteHome $ DeckR deckHash
senderHash <- encodeKeyHashid pid
(maybeSummary, audience, ticket) <-
C.offerIssue senderHash title desc uDeck
(localRecips, remoteRecips, fwdHosts, action) <-
lift $ C.makeServerInput Nothing maybeSummary audience $
AP.OfferActivity $ AP.Offer (AP.OfferTicket ticket) uDeck
offerID <-
offerTicketC
person actor Nothing localRecips remoteRecips fwdHosts action
ticket uDeck
runDBExcept $ do
mtal <- lift $ getValBy $ UniqueTicketAuthorLocalOpen offerID
tal <- fromMaybeE mtal "Offer processed bu no ticket created"
return $ ticketAuthorLocalTicket tal
case errorOrTicket of
Left e -> do
setMessage $ toHtml e
redirect $ TicketNewR deckHash
Right ticketID -> do
taskID <- do
maybeTaskID <- runDB $ getKeyBy $ UniqueTicketDeck ticketID
case maybeTaskID of
Nothing -> error "No TicketDeck for the new Ticket"
Just t -> return t
taskHash <- encodeKeyHashid taskID
setMessage "Ticket created"
redirect $ TicketR deckHash taskHash
postTicketFollowR :: KeyHashid Deck -> KeyHashid TicketDeck -> Handler () postTicketFollowR :: KeyHashid Deck -> KeyHashid TicketDeck -> Handler ()
postTicketFollowR _ = error "Temporarily disabled" postTicketFollowR _ = error "Temporarily disabled"
@ -500,75 +556,6 @@ postTicketReplyOnR deckHash taskHash msgHash = do
{- {-
getProjectTicketNewR :: ShrIdent -> PrjIdent -> Handler Html
getProjectTicketNewR shr prj = do
wid <- runDB $ do
Entity sid _ <- getBy404 $ UniqueSharer shr
Entity _ j <- getBy404 $ UniqueProject prj sid
return $ projectWorkflow j
((_result, widget), enctype) <- runFormPost $ newTicketForm wid
defaultLayout $(widgetFile "ticket/new")
putProjectTicketR :: ShrIdent -> PrjIdent -> KeyHashid LocalTicket -> Handler Html
putProjectTicketR shr prj ltkhid = do
(tid, ticket, wid) <- runDB $ do
(_es, Entity _ project, Entity tid ticket, _elt, _etcl, _etpl, _author, _) <- getProjectTicket404 shr prj ltkhid
return (tid, ticket, projectWorkflow project)
((result, widget), enctype) <-
runFormPost $ editTicketContentForm tid ticket wid
case result of
FormSuccess (ticket', tparams, eparams, cparams) -> do
newDescHtml <-
case renderPandocMarkdown $ ticketSource ticket' of
Left err -> do
setMessage $ toHtml err
redirect $ ProjectTicketEditR shr prj ltkhid
Right t -> return t
let ticket'' = ticket' { ticketDescription = newDescHtml }
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
let (cdel, cins, _ckeep) = partitionMaybePairs cparams
deleteWhere [TicketParamClassId <-. cdel]
let mkcparam fid = TicketParamClass
{ ticketParamClassTicket = tid
, ticketParamClassField = fid
}
insertMany_ $ map mkcparam cins
setMessage "Ticket updated."
redirect $ ProjectTicketR shr prj ltkhid
FormMissing -> do
setMessage "Field(s) missing."
defaultLayout $(widgetFile "ticket/edit")
FormFailure _l -> do
setMessage "Ticket update failed, see errors below."
defaultLayout $(widgetFile "ticket/edit")
deleteProjectTicketR :: ShrIdent -> PrjIdent -> KeyHashid LocalTicket -> Handler Html deleteProjectTicketR :: ShrIdent -> PrjIdent -> KeyHashid LocalTicket -> Handler Html
deleteProjectTicketR _shr _prj _ltkhid = deleteProjectTicketR _shr _prj _ltkhid =
--TODO: I can easily implement this, but should it even be possible to --TODO: I can easily implement this, but should it even be possible to

View file

@ -1,29 +0,0 @@
{- This file is part of Vervis.
-
- Written in 2019 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/>.
-}
module Vervis.Widget.Project
( projectNavW
)
where
import Vervis.Foundation
import Vervis.Model
import Vervis.Model.Ident
import Vervis.Settings
import Vervis.Widget.Workflow
projectNavW :: Project -> Workflow -> Sharer -> ShrIdent -> PrjIdent -> Widget
projectNavW project workflow wsharer shar proj =
$(widgetFile "project/widget/nav")

View file

@ -0,0 +1,40 @@
{- This file is part of Vervis.
-
- Written in 2019, 2022 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/>.
-}
module Vervis.Widget.Tracker
( deckNavW
, loomNavW
)
where
import Database.Persist.Types
import Yesod.Hashids
import Vervis.Foundation
import Vervis.Model
import Vervis.Settings
deckNavW :: Entity Deck -> Actor -> Widget
deckNavW (Entity deckID deck) actor = do
deckHash <- encodeKeyHashid deckID
hashRepo <- getEncodeKeyHashid
$(widgetFile "deck/widget/nav")
loomNavW :: Entity Loom -> Actor -> Widget
loomNavW (Entity loomID loom) actor = do
loomHash <- encodeKeyHashid loomID
hashRepo <- getEncodeKeyHashid
$(widgetFile "loom/widget/nav")

View file

@ -12,16 +12,18 @@ $# You should have received a copy of the CC0 Public Domain Dedication along
$# with this software. If not, see $# with this software. If not, see
$# <http://creativecommons.org/publicdomain/zero/1.0/>. $# <http://creativecommons.org/publicdomain/zero/1.0/>.
$# <p> ^{loomNavW (Entity loomID loom) actor}
$# <a href=@{ProjectTicketNewR shr prj}>Create new…
<p>
<a href=@{ClothNewR loomHash}>Create new…
$# <p> $# <p>
$# <a href=@{ProjectTicketTreeR shr prj}>View as tree… $# <a href=@{ProjectTicketTreeR shr prj}>View as tree…
<form method=GET action=@{LoomClothsR loomHash} enctype=#{filtEnctype}> $# <form method=GET action=@{LoomClothsR loomHash} enctype=#{filtEnctype}>
^{filtWidget} $# ^{filtWidget}
<div class="submit"> $# <div class="submit">
<input type="submit" value="Filter"> $# <input type="submit" value="Filter">
^{pageNav} ^{pageNav}

View file

@ -13,6 +13,8 @@ $# You should have received a copy of the CC0 Public Domain Dedication along
$# with this software. If not, see $# with this software. If not, see
$# <http://creativecommons.org/publicdomain/zero/1.0/>. $# <http://creativecommons.org/publicdomain/zero/1.0/>.
^{loomNavW eloom actor}
<h2>#{ticketTitle ticket} <h2>#{ticketTitle ticket}
<div> <div>

View file

@ -1,6 +1,6 @@
$# This file is part of Vervis. $# This file is part of Vervis.
$# $#
$# Written in 2019 by fr33domlover <fr33domlover@riseup.net>. $# Written in 2019, 2022 by fr33domlover <fr33domlover@riseup.net>.
$# $#
$# ♡ Copying is an act of love. Please copy, reuse and share. $# ♡ Copying is an act of love. Please copy, reuse and share.
$# $#
@ -15,36 +15,29 @@ $# <http://creativecommons.org/publicdomain/zero/1.0/>.
<div> <div>
<span> <span>
[[ 🏗 [[ 🏗
<a href=@{ProjectR shar proj}> <a href=@{DeckR deckHash}>
#{prj2text proj} =#{keyHashidText deckHash} #{actorName actor}
]] :: ]] ::
<span> <span>
<a href=@{ProjectInboxR shar proj}> <a href=@{DeckInboxR deckHash}>
[📥 Inbox] [📥 Inbox]
<span> <span>
<a href=@{ProjectOutboxR shar proj}> <a href=@{DeckOutboxR deckHash}>
[📤 Outbox] [📤 Outbox]
<span> <span>
<a href=@{ProjectFollowersR shar proj}> <a href=@{DeckFollowersR deckHash}>
[🐤 Followers] [🐤 Followers]
<span> <span>
<a href=@{ProjectDevsR shar proj}> [🤝 Collaborators]
[🤝 Collaborators]
<span> <span>
<a href=@{ProjectTicketsR shar proj}> <a href=@{DeckTicketsR deckHash}>
[🐛 Tickets] [🐛 Tickets]
<span> <span>
<a href=@{ClaimRequestsProjectR shar proj}> $maybe repoID <- deckWiki deck
[✋ Ticket claim requests] <a href=@{RepoR $ hashRepo repoID}>
<span>
[🔁 Ticket workflow:
^{workflowLinkW wsharer workflow}]
<span>
$maybe _wiki <- projectWiki project
<a href=@{WikiPageR shar proj []}>
[📖 Wiki] [📖 Wiki]
$nothing $nothing
[No wiki] [No wiki]
<span> <span>
<a href=@{ProjectEditR shar proj}> <a href=@{DeckEditR deckHash}>
[✏ Edit] [✏ Edit]

View file

@ -0,0 +1,39 @@
$# This file is part of Vervis.
$#
$# Written in 2019, 2022 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/>.
<div>
<span>
[[ 🏗
<a href=@{LoomR loomHash}>
+#{keyHashidText loomHash} #{actorName actor}
]] ::
<span>
<a href=@{LoomInboxR loomHash}>
[📥 Inbox]
<span>
<a href=@{LoomOutboxR loomHash}>
[📤 Outbox]
<span>
<a href=@{LoomFollowersR loomHash}>
[🐤 Followers]
<span>
[🤝 Collaborators]
<span>
<a href=@{LoomClothsR loomHash}>
[🧩 Merge Requests]
<span>
<a href=@{RepoR $ hashRepo $ loomRepo loom}>
[🗃 Repository]
<span>
[✏ Edit]

View file

@ -12,16 +12,18 @@ $# You should have received a copy of the CC0 Public Domain Dedication along
$# with this software. If not, see $# with this software. If not, see
$# <http://creativecommons.org/publicdomain/zero/1.0/>. $# <http://creativecommons.org/publicdomain/zero/1.0/>.
$# <p> ^{deckNavW (Entity deckID deck) actor}
$# <a href=@{ProjectTicketNewR shr prj}>Create new…
<p>
<a href=@{TicketNewR deckHash}>Create new…
$# <p> $# <p>
$# <a href=@{ProjectTicketTreeR shr prj}>View as tree… $# <a href=@{ProjectTicketTreeR shr prj}>View as tree…
<form method=GET action=@{DeckTicketsR deckHash} enctype=#{filtEnctype}> $# <form method=GET action=@{DeckTicketsR deckHash} enctype=#{filtEnctype}>
^{filtWidget} $# ^{filtWidget}
<div class="submit"> $# <div class="submit">
<input type="submit" value="Filter"> $# <input type="submit" value="Filter">
^{pageNav} ^{pageNav}

View file

@ -1,6 +1,6 @@
$# This file is part of Vervis. $# This file is part of Vervis.
$# $#
$# Written in 2016 by fr33domlover <fr33domlover@riseup.net>. $# Written in 2016, 2022 by fr33domlover <fr33domlover@riseup.net>.
$# $#
$# ♡ Copying is an act of love. Please copy, reuse and share. $# ♡ Copying is an act of love. Please copy, reuse and share.
$# $#
@ -14,7 +14,7 @@ $# <http://creativecommons.org/publicdomain/zero/1.0/>.
Enter the details and click "Submit" to create a new ticket. Enter the details and click "Submit" to create a new ticket.
<form method=POST action=@{ProjectTicketsR shr prj} enctype=#{enctype}> <form method=POST action=@{TicketNewR deckHash} enctype=#{enctype}>
^{widget} ^{widget}
<div class="submit"> <div class="submit">
<input type="submit"> <input type="submit">

View file

@ -13,6 +13,8 @@ $# You should have received a copy of the CC0 Public Domain Dedication along
$# with this software. If not, see $# with this software. If not, see
$# <http://creativecommons.org/publicdomain/zero/1.0/>. $# <http://creativecommons.org/publicdomain/zero/1.0/>.
^{deckNavW edeck actor}
<h2>#{ticketTitle ticket} <h2>#{ticketTitle ticket}
<div> <div>

View file

@ -224,10 +224,9 @@
/decks/#DeckKeyHashid/tickets/#TicketDeckKeyHashid/deps TicketDepsR GET /decks/#DeckKeyHashid/tickets/#TicketDeckKeyHashid/deps TicketDepsR GET
/decks/#DeckKeyHashid/tickets/#TicketDeckKeyHashid/rdeps TicketReverseDepsR GET /decks/#DeckKeyHashid/tickets/#TicketDeckKeyHashid/rdeps TicketReverseDepsR GET
-- /decks/#DeckKeyHashid/new-ticket TicketNewR GET POST /decks/#DeckKeyHashid/new-ticket TicketNewR GET POST
-- /decks/#DeckKeyHashid/tickets/#TicketDeckKeyHashid/edit TicketEditR GET POST -- /decks/#DeckKeyHashid/tickets/#TicketDeckKeyHashid/edit TicketEditR GET POST
-- /decks/#DeckKeyHashid/tickets/#TicketDeckKeyHashid/delete TicketDeleteR POST -- /decks/#DeckKeyHashid/tickets/#TicketDeckKeyHashid/delete TicketDeleteR POST
-- /decks/#DeckKeyHashid/tickets/#TicketDeckKeyHashid/accept TicketAcceptR POST
-- /decks/#DeckKeyHashid/tickets/#TicketDeckKeyHashid/close TicketCloseR POST -- /decks/#DeckKeyHashid/tickets/#TicketDeckKeyHashid/close TicketCloseR POST
-- /decks/#DeckKeyHashid/tickets/#TicketDeckKeyHashid/open TicketOpenR POST -- /decks/#DeckKeyHashid/tickets/#TicketDeckKeyHashid/open TicketOpenR POST
-- /decks/#DeckKeyHashid/tickets/#TicketDeckKeyHashid/claim TicketClaimR POST -- /decks/#DeckKeyHashid/tickets/#TicketDeckKeyHashid/claim TicketClaimR POST
@ -277,10 +276,9 @@
/looms/#LoomKeyHashid/cloths/#TicketLoomKeyHashid/bundles/#BundleKeyHashid BundleR GET /looms/#LoomKeyHashid/cloths/#TicketLoomKeyHashid/bundles/#BundleKeyHashid BundleR GET
/looms/#LoomKeyHashid/cloths/#TicketLoomKeyHashid/bundles/#BundleKeyHashid/patches/#PatchKeyHashid PatchR GET /looms/#LoomKeyHashid/cloths/#TicketLoomKeyHashid/bundles/#BundleKeyHashid/patches/#PatchKeyHashid PatchR GET
-- /looms/#LoomKeyHashid/new-cloth ClothNewR GET POST /looms/#LoomKeyHashid/new-cloth ClothNewR GET POST
-- /looms/#LoomKeyHashid/cloths/#TicketLoomKeyHashid/edit ClothEditR GET POST -- /looms/#LoomKeyHashid/cloths/#TicketLoomKeyHashid/edit ClothEditR GET POST
-- /looms/#LoomKeyHashid/cloths/#TicketLoomKeyHashid/delete ClothDeleteR POST -- /looms/#LoomKeyHashid/cloths/#TicketLoomKeyHashid/delete ClothDeleteR POST
-- /looms/#LoomKeyHashid/cloths/#TicketLoomKeyHashid/accept ClothAcceptR POST
-- /looms/#LoomKeyHashid/cloths/#TicketLoomKeyHashid/close ClothCloseR POST -- /looms/#LoomKeyHashid/cloths/#TicketLoomKeyHashid/close ClothCloseR POST
-- /looms/#LoomKeyHashid/cloths/#TicketLoomKeyHashid/open ClothOpenR POST -- /looms/#LoomKeyHashid/cloths/#TicketLoomKeyHashid/open ClothOpenR POST
-- /looms/#LoomKeyHashid/cloths/#TicketLoomKeyHashid/claim ClothClaimR POST -- /looms/#LoomKeyHashid/cloths/#TicketLoomKeyHashid/claim ClothClaimR POST

View file

@ -166,10 +166,10 @@ library
Vervis.Form.Discussion Vervis.Form.Discussion
--Vervis.Form.Group --Vervis.Form.Group
Vervis.Form.Key Vervis.Form.Key
Vervis.Form.Project
Vervis.Form.Repo Vervis.Form.Repo
--Vervis.Form.Role --Vervis.Form.Role
Vervis.Form.Ticket Vervis.Form.Ticket
Vervis.Form.Tracker
-- Vervis.Form.Workflow -- Vervis.Form.Workflow
Vervis.Formatting Vervis.Formatting
Vervis.Foundation Vervis.Foundation
@ -239,10 +239,10 @@ library
Vervis.Widget Vervis.Widget
Vervis.Widget.Discussion Vervis.Widget.Discussion
Vervis.Widget.Person Vervis.Widget.Person
--Vervis.Widget.Project
Vervis.Widget.Repo Vervis.Widget.Repo
--Vervis.Widget.Role --Vervis.Widget.Role
Vervis.Widget.Ticket Vervis.Widget.Ticket
Vervis.Widget.Tracker
-- Vervis.Widget.Workflow -- Vervis.Widget.Workflow
-- Vervis.Wiki -- Vervis.Wiki
Vervis.WorkItem Vervis.WorkItem