feat(workflows): placeholder handlers
This commit is contained in:
parent
0ced4094c9
commit
baea302e48
@ -1383,6 +1383,16 @@ MenuAdminCrontab: Crontab
|
|||||||
MenuAdminWorkflowDefinitionList: Workflows
|
MenuAdminWorkflowDefinitionList: Workflows
|
||||||
MenuAdminWorkflowDefinitionNew: Neue Workflow-Definition
|
MenuAdminWorkflowDefinitionNew: Neue Workflow-Definition
|
||||||
MenuAdminWorkflowDefinitionDelete: Löschen
|
MenuAdminWorkflowDefinitionDelete: Löschen
|
||||||
|
MenuAdminWorkflowInstanceList: Workflow-Instanzen
|
||||||
|
MenuAdminWorkflowInstanceNew: Neue Workflow-Instanz
|
||||||
|
MenuAdminWorkflowDefinitionInstantiate: Instanziieren
|
||||||
|
MenuWorkflowInstanceDelete: Löschen
|
||||||
|
MenuWorkflowInstanceWorkflows: Laufende Workflows
|
||||||
|
MenuWorkflowInstanceInitiate: Workflow starten
|
||||||
|
MenuWorkflowWorkflowEdit: Editieren
|
||||||
|
MenuWorkflowWorkflowDelete: Löschen
|
||||||
|
MenuGlobalWorkflowInstanceList: Workflows
|
||||||
|
MenuGlobalWorkflowWorkflowList: Laufende Workflows
|
||||||
|
|
||||||
BreadcrumbSubmissionFile: Datei
|
BreadcrumbSubmissionFile: Datei
|
||||||
BreadcrumbSubmissionUserInvite: Einladung zur Abgabe
|
BreadcrumbSubmissionUserInvite: Einladung zur Abgabe
|
||||||
@ -1462,6 +1472,22 @@ BreadcrumbAdminWorkflowDefinitionList: Workflow-Definitionen
|
|||||||
BreadcrumbAdminWorkflowDefinitionNew: Neue Workflow-Definition
|
BreadcrumbAdminWorkflowDefinitionNew: Neue Workflow-Definition
|
||||||
BreadcrumbAdminWorkflowDefinitionEdit renderedWorkflowScope'@Text wfdn@WorkflowDefinitionName: #{wfdn} (#{renderedWorkflowScope'})
|
BreadcrumbAdminWorkflowDefinitionEdit renderedWorkflowScope'@Text wfdn@WorkflowDefinitionName: #{wfdn} (#{renderedWorkflowScope'})
|
||||||
BreadcrumbAdminWorkflowDefinitionDelete: Löschen
|
BreadcrumbAdminWorkflowDefinitionDelete: Löschen
|
||||||
|
BreadcrumbAdminWorkflowDefinitionInstantiate: Instanziieren
|
||||||
|
BreadcrumbAdminWorkflowInstanceList: Workflow-Instanzen
|
||||||
|
BreadcrumbAdminWorkflowInstanceNew: Neue Workflow-Instanz
|
||||||
|
BreadcrumbAdminWorkflowWorkflowList: Initiierte Workflows
|
||||||
|
BreadcrumbAdminWorkflowWorkflowNew: Workflow initiieren
|
||||||
|
BreadcrumbWorkflowInstanceEdit win@WorkflowInstanceName: #{win}
|
||||||
|
BreadcrumbWorkflowInstanceDelete: Löschen
|
||||||
|
BreadcrumbWorkflowInstanceWorkflowList: Laufende Workflows
|
||||||
|
BreadcrumbWorkflowInstanceInitiate: Workflow starten
|
||||||
|
BreadcrumbWorkflowInstanceList: Workflows
|
||||||
|
BreadcrumbWorkflowInstanceNew: Neuer Workflow
|
||||||
|
BreadcrumbWorkflowWorkflow workflow@CryptoFileNameWorkflowWorkflow: #{toPathPiece workflow}
|
||||||
|
BreadcrumbWorkflowWorkflowEdit: Editieren
|
||||||
|
BreadcrumbWorkflowWorkflowDelete: Löschen
|
||||||
|
BreadcrumbGlobalWorkflowInstanceList: Workflows
|
||||||
|
BreadcrumbGlobalWorkflowWorkflowList: Laufende Workflows
|
||||||
|
|
||||||
ExternalExamEdit coursen@CourseName examn@ExamName: Bearbeiten: #{coursen}, #{examn}
|
ExternalExamEdit coursen@CourseName examn@ExamName: Bearbeiten: #{coursen}, #{examn}
|
||||||
ExternalExamGrades coursen@CourseName examn@ExamName: Prüfungsleistungen: #{coursen}, #{examn}
|
ExternalExamGrades coursen@CourseName examn@ExamName: Prüfungsleistungen: #{coursen}, #{examn}
|
||||||
@ -1517,6 +1543,7 @@ AuthTagAuthentication: Nutzer ist angemeldet, falls erforderlich
|
|||||||
AuthTagRead: Zugriff ist nur lesend
|
AuthTagRead: Zugriff ist nur lesend
|
||||||
AuthTagWrite: Zugriff ist i.A. schreibend
|
AuthTagWrite: Zugriff ist i.A. schreibend
|
||||||
AuthTagSubmissionGroup: Nutzer ist Mitglied in registrierter Abgabegruppe
|
AuthTagSubmissionGroup: Nutzer ist Mitglied in registrierter Abgabegruppe
|
||||||
|
AuthTagWorkflow: Nutzer hat passende Workflow-Rolle
|
||||||
|
|
||||||
DeleteCopyStringIfSure n@Int: Wenn Sie sich sicher sind, dass Sie #{pluralDE n "das obige Objekt" "obige Objekte"} unwiderbringlich löschen möchten, schreiben Sie bitte zunächst den angezeigten Text ab.
|
DeleteCopyStringIfSure n@Int: Wenn Sie sich sicher sind, dass Sie #{pluralDE n "das obige Objekt" "obige Objekte"} unwiderbringlich löschen möchten, schreiben Sie bitte zunächst den angezeigten Text ab.
|
||||||
DeletePressButtonIfSure n@Int: Wenn Sie sich sicher sind, dass Sie #{pluralDE n "das obige Objekt" "obige Objekte"} unwiderbringlich löschen möchten, bestätigen Sie dies bitte durch Drücken des untigen Knopfes.
|
DeletePressButtonIfSure n@Int: Wenn Sie sich sicher sind, dass Sie #{pluralDE n "das obige Objekt" "obige Objekte"} unwiderbringlich löschen möchten, bestätigen Sie dies bitte durch Drücken des untigen Knopfes.
|
||||||
@ -2842,30 +2869,32 @@ CronMatchNone: Nie
|
|||||||
SystemExamOffice: Prüfungsverwaltung
|
SystemExamOffice: Prüfungsverwaltung
|
||||||
SystemFaculty: Fakultätsmitglied
|
SystemFaculty: Fakultätsmitglied
|
||||||
|
|
||||||
WorkflowInstanceScopeKindGlobal: Systemweit
|
WorkflowScopeKindGlobal: Systemweit
|
||||||
WorkflowInstanceScopeKindTerm: Pro Semester
|
WorkflowScopeKindTerm: Pro Semester
|
||||||
WorkflowInstanceScopeKindSchool: Pro Institut
|
WorkflowScopeKindSchool: Pro Institut
|
||||||
WorkflowInstanceScopeKindTermSchool: Pro Institut & Semester
|
WorkflowScopeKindTermSchool: Pro Institut & Semester
|
||||||
WorkflowInstanceScopeKindCourse: Pro Veranstaltung
|
WorkflowScopeKindCourse: Pro Veranstaltung
|
||||||
|
WorkflowScopeGlobal: Systemweit
|
||||||
|
WorkflowScopeTermSchool tid@TermId ssh@SchoolId: #{tid} #{ssh}
|
||||||
|
WorkflowScopeCourse tid@TermId ssh@SchoolId csh@CourseShorthand: #{tid} #{ssh} #{csh}
|
||||||
WorkflowDefinitionScope: Bereich
|
WorkflowDefinitionScope: Bereich
|
||||||
WorkflowDefinitionName: Name
|
WorkflowDefinitionName: Name
|
||||||
WorkflowDefinitionDescriptions: Beschreibung
|
WorkflowDefinitionDescriptions: Beschreibung
|
||||||
WorkflowDefinitionDescriptionsLanguageExists: Eine Beschreibung in dieser Sprache existiert bereits
|
WorkflowDefinitionDescriptionsLanguageExists: Eine Beschreibung in dieser Sprache existiert bereits
|
||||||
WorkflowDefinitionDescriptionLanguage: Sprach-Code (RFC1766)
|
|
||||||
WorkflowDefinitionDescriptionTitle: Titel
|
|
||||||
WorkflowDefinitionDescription: Beschreibung
|
|
||||||
WorkflowDefinitionGraph: Spezifikation
|
WorkflowDefinitionGraph: Spezifikation
|
||||||
WorkflowDefinitionKeyDoesNotExist renderedCryptoID@Text: Referenziert ID existiert nicht: #{renderedCryptoID}
|
WorkflowDefinitionKeyDoesNotExist renderedCryptoID@Text: Referenziert ID existiert nicht: #{renderedCryptoID}
|
||||||
WorkflowDefinitionFiles: Dateien
|
WorkflowDefinitionFiles: Dateien
|
||||||
WorkflowDefinitionFileIdentDoesNotExist fileIdent@Text: Referenzierte Datei existiert nicht: #{fileIdent}
|
WorkflowFileIdentDoesNotExist fileIdent@Text: Referenzierte Datei existiert nicht: #{fileIdent}
|
||||||
|
WorkflowUserDoesNotExist userIdent@Text: Referenzierter Benutzer existiert nicht: #{userIdent}
|
||||||
WorkflowDefinitionFileIdentExists: Eine Datei mit dieser ID existiert bereits
|
WorkflowDefinitionFileIdentExists: Eine Datei mit dieser ID existiert bereits
|
||||||
WorkflowDefinitionFileIdent: Dateireferenz
|
WorkflowDefinitionFileIdent: Dateireferenz
|
||||||
WorkflowDefinitionFile: Datei
|
WorkflowDefinitionFile: Datei
|
||||||
WorkflowDefinitionCreated: Workflow-Definition angelegt
|
WorkflowDefinitionCreated: Workflow-Definition angelegt
|
||||||
WorkflowDefinitionEdited: Workflow-Definition ersetzt
|
WorkflowDefinitionEdited: Workflow-Definition ersetzt
|
||||||
WorkflowDefinitionCollision: Es existiert bereits eine Workflow-Definition mit diesem Namen
|
WorkflowDefinitionCollision: Es existiert bereits eine Workflow-Definition mit diesem Namen und Bereich
|
||||||
WorkflowDefinitionNewTitle: Workflow-Definition anlegen
|
WorkflowDefinitionNewTitle: Workflow-Definition anlegen
|
||||||
WorkflowDefinitionEditTitle: Workflow-Definition Bearbeiten
|
WorkflowDefinitionEditTitle: Workflow-Definition Bearbeiten
|
||||||
|
WorkflowDefinitionInstanceCategory: Kategorie
|
||||||
|
|
||||||
WorkflowDefinitionListTitle: Workflow-Definitionen
|
WorkflowDefinitionListTitle: Workflow-Definitionen
|
||||||
WorkflowDefinitionInstanceCount: Instanzen
|
WorkflowDefinitionInstanceCount: Instanzen
|
||||||
@ -2874,6 +2903,24 @@ WorkflowDefinitionConcreteInstanceCount num@Int64: #{num} Instanzen
|
|||||||
WorkflowDefinitionConcreteWorkflowCount num@Int64: #{num} Workflows
|
WorkflowDefinitionConcreteWorkflowCount num@Int64: #{num} Workflows
|
||||||
WorkflowDefinitionDeleteQuestion: Wollen Sie die unten aufgeführte Workflow-Definition wirklich löschen?
|
WorkflowDefinitionDeleteQuestion: Wollen Sie die unten aufgeführte Workflow-Definition wirklich löschen?
|
||||||
WorkflowDefinitionDeleted: Workflow-Definition gelöscht
|
WorkflowDefinitionDeleted: Workflow-Definition gelöscht
|
||||||
|
WorkflowDefinitionInstantiateTitle: Workflow-Definition instanziieren
|
||||||
|
WorkflowDefinitionInstantiated: Instanz angelegt
|
||||||
|
|
||||||
|
WorkflowScope: Bereich
|
||||||
|
WorkflowInstanceName: Name
|
||||||
|
WorkflowInstanceCategory: Kategorie
|
||||||
|
WorkflowInstanceCollision: Es existiert bereits eine Workflow-Instanz mit diesem Namen und Bereich
|
||||||
|
WorkflowInstanceListTitle: Workflow-Instanzen
|
||||||
|
WorkflowInstanceDescription: Instanz-Beschreibung
|
||||||
|
WorkflowInstanceDescriptions: Instanz-Beschreibung
|
||||||
|
WorkflowInstanceDescriptionsLanguageExists: Eine Instanz-Beschreibung in dieser Sprache existiert bereits
|
||||||
|
WorkflowInstanceCreated: Instanz angelegt
|
||||||
|
WorkflowInstanceDescriptionTitle: Instanz-Titel
|
||||||
|
WorkflowInstanceWorkflowCount: Workflows
|
||||||
|
|
||||||
|
WorkflowDescriptionLanguage: Sprach-Code (RFC1766)
|
||||||
|
WorkflowDescriptionTitle: Titel
|
||||||
|
WorkflowDescription: Beschreibung
|
||||||
|
|
||||||
ChangelogItemFeature: Feature
|
ChangelogItemFeature: Feature
|
||||||
ChangelogItemBugfix: Bugfix
|
ChangelogItemBugfix: Bugfix
|
||||||
@ -2843,22 +2843,20 @@ CronMatchNone: Never
|
|||||||
SystemExamOffice: Exam office
|
SystemExamOffice: Exam office
|
||||||
SystemFaculty: Faculty member
|
SystemFaculty: Faculty member
|
||||||
|
|
||||||
WorkflowInstanceScopeKindGlobal: Global
|
WorkflowScopeKindGlobal: Global
|
||||||
WorkflowInstanceScopeKindTerm: Per term
|
WorkflowScopeKindTerm: Per term
|
||||||
WorkflowInstanceScopeKindSchool: Per school
|
WorkflowScopeKindSchool: Per school
|
||||||
WorkflowInstanceScopeKindTermSchool: Per school & term
|
WorkflowScopeKindTermSchool: Per school & term
|
||||||
WorkflowInstanceScopeKindCourse: Per course
|
WorkflowScopeKindCourse: Per course
|
||||||
WorkflowDefinitionScope: Scope
|
WorkflowDefinitionScope: Scope
|
||||||
WorkflowDefinitionName: Name
|
WorkflowDefinitionName: Name
|
||||||
WorkflowDefinitionDescriptions: Description
|
WorkflowDescriptionLanguage: Language code (RFC1766)
|
||||||
WorkflowDefinitionDescriptionsLanguageExists: A description in this language already exists
|
WorkflowDescriptionTitle: Title
|
||||||
WorkflowDefinitionDescriptionLanguage: Language code (RFC1766)
|
WorkflowDescription: Description
|
||||||
WorkflowDefinitionDescriptionTitle: Title
|
|
||||||
WorkflowDefinitionDescription: Description
|
|
||||||
WorkflowDefinitionGraph: Specification
|
WorkflowDefinitionGraph: Specification
|
||||||
WorkflowDefinitionKeyDoesNotExist renderedCryptoID: Referenced id does not exist: #{renderedCryptoID}
|
WorkflowDefinitionKeyDoesNotExist renderedCryptoID: Referenced id does not exist: #{renderedCryptoID}
|
||||||
WorkflowDefinitionFiles: Files
|
WorkflowDefinitionFiles: Files
|
||||||
WorkflowDefinitionFileIdentDoesNotExist fileIdent: Referenced file does not exist: #{fileIdent}
|
WorkflowFileIdentDoesNotExist fileIdent: Referenced file does not exist: #{fileIdent}
|
||||||
WorkflowDefinitionFileIdentExists: A file with the given reference id already exists
|
WorkflowDefinitionFileIdentExists: A file with the given reference id already exists
|
||||||
WorkflowDefinitionFileIdent: File reference id
|
WorkflowDefinitionFileIdent: File reference id
|
||||||
WorkflowDefinitionFile: File
|
WorkflowDefinitionFile: File
|
||||||
|
|||||||
@ -1,7 +1,8 @@
|
|||||||
WorkflowDefinition
|
WorkflowDefinition
|
||||||
graph (WorkflowGraph FileReference SqlBackendKey) -- UserId
|
graph (WorkflowGraph FileReference SqlBackendKey) -- UserId
|
||||||
scope WorkflowInstanceScope'
|
scope WorkflowScope'
|
||||||
name WorkflowDefinitionName
|
name WorkflowDefinitionName
|
||||||
|
instanceCategory WorkflowInstanceCategory Maybe
|
||||||
UniqueWorkflowDefinition name scope
|
UniqueWorkflowDefinition name scope
|
||||||
|
|
||||||
WorkflowDefinitionDescription
|
WorkflowDefinitionDescription
|
||||||
@ -11,16 +12,31 @@ WorkflowDefinitionDescription
|
|||||||
description Html Maybe
|
description Html Maybe
|
||||||
UniqueWorkflowDefinitionDescription definition language
|
UniqueWorkflowDefinitionDescription definition language
|
||||||
|
|
||||||
|
WorkflowDefinitionInstanceDescription
|
||||||
|
definition WorkflowDefinitionId
|
||||||
|
language Lang
|
||||||
|
title Text
|
||||||
|
description Html Maybe
|
||||||
|
UniqueWorkflowDefinitionInstanceDescription definition language
|
||||||
|
|
||||||
WorkflowInstance
|
WorkflowInstance
|
||||||
definition WorkflowDefinitionId Maybe
|
definition WorkflowDefinitionId Maybe
|
||||||
graph (WorkflowGraph FileReference SqlBackendKey) -- UserId
|
graph (WorkflowGraph FileReference SqlBackendKey) -- UserId
|
||||||
scope (WorkflowInstanceScope SqlBackendKey SqlBackendKey SqlBackendKey) -- TermId, SchoolId, CourseId
|
scope (WorkflowScope TermIdentifier SchoolShorthand SqlBackendKey) -- TermId, SchoolId, CourseId
|
||||||
name WorkflowInstanceName
|
name WorkflowInstanceName
|
||||||
category WorkflowInstanceCategory Maybe
|
category WorkflowInstanceCategory Maybe
|
||||||
UniqueWorkflowInstance name scope
|
UniqueWorkflowInstance name scope
|
||||||
|
|
||||||
|
WorkflowInstanceDescription
|
||||||
|
instance WorkflowInstanceId
|
||||||
|
language Lang
|
||||||
|
title Text
|
||||||
|
description Html Maybe
|
||||||
|
UniqueWorkflowInstanceDescription instance language
|
||||||
|
|
||||||
WorkflowWorkflow
|
WorkflowWorkflow
|
||||||
instance WorkflowInstanceId Maybe
|
instance WorkflowInstanceId Maybe
|
||||||
|
scope (WorkflowScope TermIdentifier SchoolShorthand SqlBackendKey) -- TermId, SchoolId, CourseId
|
||||||
graph (WorkflowGraph FileReference SqlBackendKey) -- UserId
|
graph (WorkflowGraph FileReference SqlBackendKey) -- UserId
|
||||||
initUser UserId Maybe
|
initUser UserId Maybe
|
||||||
initTime UTCTime
|
initTime UTCTime
|
||||||
|
|||||||
29
routes
29
routes
@ -57,11 +57,30 @@
|
|||||||
/admin/errMsg AdminErrMsgR GET POST
|
/admin/errMsg AdminErrMsgR GET POST
|
||||||
/admin/tokens AdminTokensR GET POST
|
/admin/tokens AdminTokensR GET POST
|
||||||
/admin/crontab AdminCrontabR GET
|
/admin/crontab AdminCrontabR GET
|
||||||
/admin/workflow-definitions AdminWorkflowDefinitionListR GET
|
|
||||||
/admin/workflow-definitions/new AdminWorkflowDefinitionNewR GET POST
|
/admin/workflows/definitions AdminWorkflowDefinitionListR GET
|
||||||
/admin/workflow-definitions/#WorkflowInstanceScope'/#WorkflowDefinitionName AdminWorkflowDefinitionR:
|
/admin/workflows/definitions/new AdminWorkflowDefinitionNewR GET POST
|
||||||
/edit AWDEditR GET POST
|
/admin/workflows/definitions/#WorkflowScope'/#WorkflowDefinitionName AdminWorkflowDefinitionR:
|
||||||
/delete AWDDeleteR GET POST
|
/edit AWDEditR GET POST
|
||||||
|
/delete AWDDeleteR GET POST
|
||||||
|
/instantiate AWDInstantiateR GET POST
|
||||||
|
/admin/workflows/instances AdminWorkflowInstanceListR GET
|
||||||
|
/admin/workflows/instances/new AdminWorkflowInstanceNewR GET POST
|
||||||
|
/admin/workflows/workflows AdminWorkflowWorkflowListR GET
|
||||||
|
/admin/workflows/workflows/new AdminWorkflowWorkflowNewR GET POST
|
||||||
|
|
||||||
|
/workflow-instances GlobalWorkflowInstanceListR GET !¬empty
|
||||||
|
/workflow-instances/new GlobalWorkflowInstanceNewR GET POST
|
||||||
|
/workflow-instances/#WorkflowInstanceName GlobalWorkflowInstanceR:
|
||||||
|
/edit GWIEditR GET POST
|
||||||
|
/delete GWIDeleteR GET POST
|
||||||
|
/workflows GWIWorkflowsR GET
|
||||||
|
/initiate GWIInitiateR GET POST !workflow
|
||||||
|
/workflows GlobalWorkflowWorkflowListR GET !¬empty
|
||||||
|
/workflows/#CryptoFileNameWorkflowWorkflow GlobalWorkflowWorkflowR:
|
||||||
|
/ GWWWorkflowR GET POST !workflow
|
||||||
|
/edit GWWEditR GET POST
|
||||||
|
/delete GWWDeleteR GET POST
|
||||||
|
|
||||||
/health HealthR GET !free
|
/health HealthR GET !free
|
||||||
/instance InstanceR GET !free
|
/instance InstanceR GET !free
|
||||||
|
|||||||
@ -73,6 +73,7 @@ decCryptoIDs [ ''SubmissionId
|
|||||||
, ''CourseEventId
|
, ''CourseEventId
|
||||||
, ''TutorialId
|
, ''TutorialId
|
||||||
, ''ExternalExamId
|
, ''ExternalExamId
|
||||||
|
, ''WorkflowInstanceId
|
||||||
, ''WorkflowWorkflowId
|
, ''WorkflowWorkflowId
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -113,3 +114,22 @@ instance {-# OVERLAPS #-} FromJSONKey (E.CryptoID "User" (CI FilePath)) where
|
|||||||
fromJSONKey = FromJSONKeyTextParser $ maybe (fail "Could not parse CryptoFileNameUser") return . fromPathPiece
|
fromJSONKey = FromJSONKeyTextParser $ maybe (fail "Could not parse CryptoFileNameUser") return . fromPathPiece
|
||||||
instance {-# OVERLAPS #-} ToMarkup (E.CryptoID "User" (CI FilePath)) where
|
instance {-# OVERLAPS #-} ToMarkup (E.CryptoID "User" (CI FilePath)) where
|
||||||
toMarkup = toMarkup . toPathPiece
|
toMarkup = toMarkup . toPathPiece
|
||||||
|
|
||||||
|
|
||||||
|
-- CryptoIDNamespace (CI FilePath) WorkflowWorkflowId ~ "WorkflowWorkflow"
|
||||||
|
instance {-# OVERLAPS #-} PathPiece (E.CryptoID "WorkflowWorkflow" (CI FilePath)) where
|
||||||
|
fromPathPiece (Text.unpack -> piece) = do
|
||||||
|
piece' <- (stripPrefix `on` map CI.mk) "uww" piece
|
||||||
|
return . CryptoID . CI.mk $ map CI.original piece'
|
||||||
|
toPathPiece = Text.pack . ("uww" <>) . CI.foldedCase . ciphertext
|
||||||
|
|
||||||
|
instance {-# OVERLAPS #-} ToJSON (E.CryptoID "WorkflowWorkflow" (CI FilePath)) where
|
||||||
|
toJSON = String . toPathPiece
|
||||||
|
instance {-# OVERLAPS #-} ToJSONKey (E.CryptoID "WorkflowWorkflow" (CI FilePath)) where
|
||||||
|
toJSONKey = ToJSONKeyText toPathPiece (text . toPathPiece)
|
||||||
|
instance {-# OVERLAPS #-} FromJSON (E.CryptoID "WorkflowWorkflow" (CI FilePath)) where
|
||||||
|
parseJSON = withText "CryptoFileNameWorkflowWorkflow" $ maybe (fail "Could not parse CryptoFileNameWorkflowWorkflow") return . fromPathPiece
|
||||||
|
instance {-# OVERLAPS #-} FromJSONKey (E.CryptoID "WorkflowWorkflow" (CI FilePath)) where
|
||||||
|
fromJSONKey = FromJSONKeyTextParser $ maybe (fail "Could not parse CryptoFileNameWorkflowWorkflow") return . fromPathPiece
|
||||||
|
instance {-# OVERLAPS #-} ToMarkup (E.CryptoID "WorkflowWorkflow" (CI FilePath)) where
|
||||||
|
toMarkup = toMarkup . toPathPiece
|
||||||
|
|||||||
@ -1323,6 +1323,7 @@ tagAccessPredicate AuthAuthentication = APDB $ \mAuthId route _ -> case route of
|
|||||||
guard $ not systemMessageAuthenticatedOnly || isAuthenticated
|
guard $ not systemMessageAuthenticatedOnly || isAuthenticated
|
||||||
return Authorized
|
return Authorized
|
||||||
r -> $unsupportedAuthPredicate AuthAuthentication r
|
r -> $unsupportedAuthPredicate AuthAuthentication r
|
||||||
|
tagAccessPredicate AuthWorkflow = APHandler $ \_ route _ -> $unsupportedAuthPredicate AuthWorkflow route
|
||||||
tagAccessPredicate AuthRead = APPure $ \_ _ isWrite -> do
|
tagAccessPredicate AuthRead = APPure $ \_ _ isWrite -> do
|
||||||
MsgRenderer mr <- ask
|
MsgRenderer mr <- ask
|
||||||
return $ bool Authorized (Unauthorized $ mr MsgUnauthorizedWrite) isWrite
|
return $ bool Authorized (Unauthorized $ mr MsgUnauthorizedWrite) isWrite
|
||||||
|
|||||||
@ -226,7 +226,7 @@ embedRenderMessage ''UniWorX ''ExamOnlinePreset id
|
|||||||
embedRenderMessage ''UniWorX ''ExamSynchronicityPreset id
|
embedRenderMessage ''UniWorX ''ExamSynchronicityPreset id
|
||||||
embedRenderMessage ''UniWorX ''ExamRequiredEquipmentPreset id
|
embedRenderMessage ''UniWorX ''ExamRequiredEquipmentPreset id
|
||||||
embedRenderMessage ''UniWorX ''ChangelogItemKind id
|
embedRenderMessage ''UniWorX ''ChangelogItemKind id
|
||||||
embedRenderMessage ''UniWorX ''WorkflowInstanceScope' $ ("WorkflowInstanceScopeKind" <>) . concat . drop 1 . splitCamel . fromMaybe (error "Expected WorkflowInstanceScope' to have '") . stripSuffix "'"
|
embedRenderMessage ''UniWorX ''WorkflowScope' $ ("WorkflowScopeKind" <>) . concat . drop 1 . splitCamel . fromMaybe (error "Expected WorkflowScope' to have '") . stripSuffix "'"
|
||||||
|
|
||||||
embedRenderMessage ''UniWorX ''AuthenticationMode id
|
embedRenderMessage ''UniWorX ''AuthenticationMode id
|
||||||
|
|
||||||
@ -400,6 +400,17 @@ instance RenderMessage UniWorX ShortWeekDay where
|
|||||||
|
|
||||||
embedRenderMessage ''UniWorX ''ButtonSubmit id
|
embedRenderMessage ''UniWorX ''ButtonSubmit id
|
||||||
|
|
||||||
|
instance RenderMessage UniWorX (WorkflowScope TermIdentifier SchoolShorthand (TermId, SchoolId, CourseShorthand)) where
|
||||||
|
renderMessage foundation ls = \case
|
||||||
|
WSGlobal -> mr MsgWorkflowScopeGlobal
|
||||||
|
WSTerm{..} -> mr $ ShortTermIdentifier wisTerm
|
||||||
|
WSSchool{..} -> mr wisSchool
|
||||||
|
WSTermSchool{..} -> mr $ MsgWorkflowScopeTermSchool (TermKey wisTerm) (SchoolKey wisSchool)
|
||||||
|
WSCourse{ wisCourse = (tid, ssh, csh) } -> mr $ MsgWorkflowScopeCourse tid ssh csh
|
||||||
|
where
|
||||||
|
mr :: forall msg. RenderMessage UniWorX msg => msg -> Text
|
||||||
|
mr = renderMessage foundation ls
|
||||||
|
|
||||||
|
|
||||||
unRenderMessage' :: (Eq a, Finite a, RenderMessage master a) => (Text -> Text -> Bool) -> master -> Text -> [a]
|
unRenderMessage' :: (Eq a, Finite a, RenderMessage master a) => (Text -> Text -> Bool) -> master -> Text -> [a]
|
||||||
unRenderMessage' cmp foundation inp = nub $ do
|
unRenderMessage' cmp foundation inp = nub $ do
|
||||||
|
|||||||
@ -338,6 +338,28 @@ instance BearerAuthSite UniWorX => YesodBreadcrumbs UniWorX where
|
|||||||
MsgRenderer mr <- getMsgRenderer
|
MsgRenderer mr <- getMsgRenderer
|
||||||
i18nCrumb (MsgBreadcrumbAdminWorkflowDefinitionEdit (mr wfdScope) wfdName) $ Just AdminWorkflowDefinitionListR
|
i18nCrumb (MsgBreadcrumbAdminWorkflowDefinitionEdit (mr wfdScope) wfdName) $ Just AdminWorkflowDefinitionListR
|
||||||
AWDDeleteR -> i18nCrumb MsgBreadcrumbAdminWorkflowDefinitionDelete . Just $ AdminWorkflowDefinitionR wfdScope wfdName AWDEditR
|
AWDDeleteR -> i18nCrumb MsgBreadcrumbAdminWorkflowDefinitionDelete . Just $ AdminWorkflowDefinitionR wfdScope wfdName AWDEditR
|
||||||
|
AWDInstantiateR -> i18nCrumb MsgBreadcrumbAdminWorkflowDefinitionInstantiate . Just $ AdminWorkflowDefinitionR wfdScope wfdName AWDEditR
|
||||||
|
breadcrumb AdminWorkflowInstanceListR = i18nCrumb MsgBreadcrumbAdminWorkflowInstanceList $ Just AdminWorkflowDefinitionListR
|
||||||
|
breadcrumb AdminWorkflowInstanceNewR = i18nCrumb MsgBreadcrumbAdminWorkflowInstanceNew $ Just AdminWorkflowInstanceListR
|
||||||
|
breadcrumb AdminWorkflowWorkflowListR = i18nCrumb MsgBreadcrumbAdminWorkflowWorkflowList $ Just AdminWorkflowInstanceListR
|
||||||
|
breadcrumb AdminWorkflowWorkflowNewR = i18nCrumb MsgBreadcrumbAdminWorkflowWorkflowNew $ Just AdminWorkflowWorkflowListR
|
||||||
|
|
||||||
|
breadcrumb GlobalWorkflowInstanceListR = i18nCrumb MsgBreadcrumbWorkflowInstanceList Nothing
|
||||||
|
breadcrumb GlobalWorkflowInstanceNewR = i18nCrumb MsgBreadcrumbWorkflowInstanceNew $ Just GlobalWorkflowInstanceListR
|
||||||
|
breadcrumb (GlobalWorkflowInstanceR win sRoute) = case sRoute of
|
||||||
|
GWIEditR -> i18nCrumb (MsgBreadcrumbWorkflowInstanceEdit win) $ Just GlobalWorkflowInstanceListR
|
||||||
|
GWIDeleteR -> i18nCrumb MsgBreadcrumbWorkflowInstanceDelete . Just $ GlobalWorkflowInstanceR win GWIEditR
|
||||||
|
GWIWorkflowsR -> i18nCrumb MsgBreadcrumbWorkflowInstanceWorkflowList . Just $ GlobalWorkflowInstanceR win GWIEditR
|
||||||
|
GWIInitiateR -> do
|
||||||
|
mayEdit <- hasReadAccessTo $ GlobalWorkflowInstanceR win GWIEditR
|
||||||
|
i18nCrumb MsgBreadcrumbWorkflowInstanceInitiate . Just $ if
|
||||||
|
| mayEdit -> GlobalWorkflowInstanceR win GWIEditR
|
||||||
|
| otherwise -> GlobalWorkflowInstanceListR
|
||||||
|
breadcrumb GlobalWorkflowWorkflowListR = i18nCrumb MsgBreadcrumbGlobalWorkflowWorkflowList $ Just GlobalWorkflowInstanceListR
|
||||||
|
breadcrumb (GlobalWorkflowWorkflowR cID sRoute) = case sRoute of
|
||||||
|
GWWWorkflowR -> i18nCrumb (MsgBreadcrumbWorkflowWorkflow cID) $ Just GlobalWorkflowInstanceListR
|
||||||
|
GWWEditR -> i18nCrumb MsgBreadcrumbWorkflowWorkflowEdit . Just $ GlobalWorkflowWorkflowR cID GWWWorkflowR
|
||||||
|
GWWDeleteR -> i18nCrumb MsgBreadcrumbWorkflowWorkflowDelete . Just $ GlobalWorkflowWorkflowR cID GWWWorkflowR
|
||||||
|
|
||||||
|
|
||||||
data NavQuickView
|
data NavQuickView
|
||||||
@ -748,6 +770,18 @@ defaultLinks = fmap catMaybes . mapM runMaybeT $ -- Define the menu items of the
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
, return NavHeader
|
||||||
|
{ navHeaderRole = NavHeaderPrimary
|
||||||
|
, navIcon = IconMenuWorkflows
|
||||||
|
, navLink = NavLink
|
||||||
|
{ navLabel = MsgMenuGlobalWorkflowInstanceList
|
||||||
|
, navRoute = GlobalWorkflowInstanceListR
|
||||||
|
, navAccess' = return True
|
||||||
|
, navType = NavTypeLink { navModal = False }
|
||||||
|
, navQuick' = mempty
|
||||||
|
, navForceActive = False
|
||||||
|
}
|
||||||
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
pageActions :: ( MonadHandler m
|
pageActions :: ( MonadHandler m
|
||||||
@ -2299,6 +2333,17 @@ pageActions AdminWorkflowDefinitionListR = return
|
|||||||
}
|
}
|
||||||
, navChildren = []
|
, navChildren = []
|
||||||
}
|
}
|
||||||
|
, NavPageActionPrimary
|
||||||
|
{ navLink = NavLink
|
||||||
|
{ navLabel = MsgMenuAdminWorkflowInstanceList
|
||||||
|
, navRoute = AdminWorkflowInstanceListR
|
||||||
|
, navAccess' = return True
|
||||||
|
, navType = NavTypeLink { navModal = False }
|
||||||
|
, navQuick' = mempty
|
||||||
|
, navForceActive = False
|
||||||
|
}
|
||||||
|
, navChildren = []
|
||||||
|
}
|
||||||
]
|
]
|
||||||
pageActions (AdminWorkflowDefinitionR wds wdn AWDEditR) = return
|
pageActions (AdminWorkflowDefinitionR wds wdn AWDEditR) = return
|
||||||
[ NavPageActionSecondary
|
[ NavPageActionSecondary
|
||||||
@ -2311,6 +2356,99 @@ pageActions (AdminWorkflowDefinitionR wds wdn AWDEditR) = return
|
|||||||
, navForceActive = False
|
, navForceActive = False
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
, NavPageActionPrimary
|
||||||
|
{ navLink = NavLink
|
||||||
|
{ navLabel = MsgMenuAdminWorkflowDefinitionInstantiate
|
||||||
|
, navRoute = AdminWorkflowDefinitionR wds wdn AWDInstantiateR
|
||||||
|
, navAccess' = return True
|
||||||
|
, navType = NavTypeLink { navModal = True }
|
||||||
|
, navQuick' = mempty
|
||||||
|
, navForceActive = False
|
||||||
|
}
|
||||||
|
, navChildren = []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
pageActions AdminWorkflowInstanceListR = return
|
||||||
|
[ NavPageActionPrimary
|
||||||
|
{ navLink = NavLink
|
||||||
|
{ navLabel = MsgMenuAdminWorkflowInstanceNew
|
||||||
|
, navRoute = AdminWorkflowInstanceNewR
|
||||||
|
, navAccess' = return True
|
||||||
|
, navType = NavTypeLink { navModal = False }
|
||||||
|
, navQuick' = mempty
|
||||||
|
, navForceActive = False
|
||||||
|
}
|
||||||
|
, navChildren = []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
pageActions GlobalWorkflowInstanceListR = return
|
||||||
|
[ NavPageActionPrimary
|
||||||
|
{ navLink = NavLink
|
||||||
|
{ navLabel = MsgMenuGlobalWorkflowWorkflowList
|
||||||
|
, navRoute = GlobalWorkflowWorkflowListR
|
||||||
|
, navAccess' = return True
|
||||||
|
, navType = NavTypeLink { navModal = False }
|
||||||
|
, navQuick' = mempty
|
||||||
|
, navForceActive = False
|
||||||
|
}
|
||||||
|
, navChildren = []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
pageActions (GlobalWorkflowInstanceR win GWIEditR) = return
|
||||||
|
[ NavPageActionSecondary
|
||||||
|
{ navLink = NavLink
|
||||||
|
{ navLabel = MsgMenuWorkflowInstanceDelete
|
||||||
|
, navRoute = GlobalWorkflowInstanceR win GWIDeleteR
|
||||||
|
, navAccess' = return True
|
||||||
|
, navType = NavTypeLink { navModal = False }
|
||||||
|
, navQuick' = mempty
|
||||||
|
, navForceActive = False
|
||||||
|
}
|
||||||
|
}
|
||||||
|
, NavPageActionPrimary
|
||||||
|
{ navLink = NavLink
|
||||||
|
{ navLabel = MsgMenuWorkflowInstanceWorkflows
|
||||||
|
, navRoute = GlobalWorkflowInstanceR win GWIWorkflowsR
|
||||||
|
, navAccess' = return True
|
||||||
|
, navType = NavTypeLink { navModal = False }
|
||||||
|
, navQuick' = mempty
|
||||||
|
, navForceActive = False
|
||||||
|
}
|
||||||
|
, navChildren = []
|
||||||
|
}
|
||||||
|
, NavPageActionPrimary
|
||||||
|
{ navLink = NavLink
|
||||||
|
{ navLabel = MsgMenuWorkflowInstanceInitiate
|
||||||
|
, navRoute = GlobalWorkflowInstanceR win GWIInitiateR
|
||||||
|
, navAccess' = return True
|
||||||
|
, navType = NavTypeLink { navModal = False }
|
||||||
|
, navQuick' = mempty
|
||||||
|
, navForceActive = False
|
||||||
|
}
|
||||||
|
, navChildren = []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
pageActions (GlobalWorkflowWorkflowR cID GWWWorkflowR) = return
|
||||||
|
[ NavPageActionSecondary
|
||||||
|
{ navLink = NavLink
|
||||||
|
{ navLabel = MsgMenuWorkflowWorkflowEdit
|
||||||
|
, navRoute = GlobalWorkflowWorkflowR cID GWWEditR
|
||||||
|
, navAccess' = return True
|
||||||
|
, navType = NavTypeLink { navModal = False }
|
||||||
|
, navQuick' = mempty
|
||||||
|
, navForceActive = False
|
||||||
|
}
|
||||||
|
}
|
||||||
|
, NavPageActionSecondary
|
||||||
|
{ navLink = NavLink
|
||||||
|
{ navLabel = MsgMenuWorkflowWorkflowDelete
|
||||||
|
, navRoute = GlobalWorkflowWorkflowR cID GWWDeleteR
|
||||||
|
, navAccess' = return True
|
||||||
|
, navType = NavTypeLink { navModal = False }
|
||||||
|
, navQuick' = mempty
|
||||||
|
, navForceActive = False
|
||||||
|
}
|
||||||
|
}
|
||||||
]
|
]
|
||||||
pageActions _ = return []
|
pageActions _ = return []
|
||||||
|
|
||||||
|
|||||||
@ -39,6 +39,8 @@ deriving instance Generic ExamOfficeR
|
|||||||
deriving instance Generic CourseNewsR
|
deriving instance Generic CourseNewsR
|
||||||
deriving instance Generic CourseEventR
|
deriving instance Generic CourseEventR
|
||||||
deriving instance Generic AdminWorkflowDefinitionR
|
deriving instance Generic AdminWorkflowDefinitionR
|
||||||
|
deriving instance Generic GlobalWorkflowInstanceR
|
||||||
|
deriving instance Generic GlobalWorkflowWorkflowR
|
||||||
deriving instance Generic (Route UniWorX)
|
deriving instance Generic (Route UniWorX)
|
||||||
|
|
||||||
data RouteChildren
|
data RouteChildren
|
||||||
|
|||||||
@ -147,6 +147,8 @@ routeNormalizers = map (hoist (hoist liftHandler . withReaderT projectBackend) .
|
|||||||
, ncTutorial
|
, ncTutorial
|
||||||
, ncExam
|
, ncExam
|
||||||
, ncExternalExam
|
, ncExternalExam
|
||||||
|
, ncAdminWorkflowDefinition
|
||||||
|
, ncGlobalWorkflowInstance
|
||||||
, verifySubmission
|
, verifySubmission
|
||||||
, verifyCourseApplication
|
, verifyCourseApplication
|
||||||
, verifyCourseNews
|
, verifyCourseNews
|
||||||
@ -223,6 +225,18 @@ routeNormalizers = map (hoist (hoist liftHandler . withReaderT projectBackend) .
|
|||||||
return $ route
|
return $ route
|
||||||
& typesUsing @RouteChildren @CourseName . filtered (== coursen) .~ externalExamCourseName
|
& typesUsing @RouteChildren @CourseName . filtered (== coursen) .~ externalExamCourseName
|
||||||
& typesUsing @RouteChildren @ExamName . filtered (== examn) .~ externalExamExamName
|
& typesUsing @RouteChildren @ExamName . filtered (== examn) .~ externalExamExamName
|
||||||
|
ncAdminWorkflowDefinition = maybeOrig $ \route -> do
|
||||||
|
AdminWorkflowDefinitionR wds wdn _ <- return route
|
||||||
|
Entity _ WorkflowDefinition{..} <- MaybeT . $cachedHereBinary (wds, wdn) . lift . getBy $ UniqueWorkflowDefinition wdn wds
|
||||||
|
caseChanged wdn workflowDefinitionName
|
||||||
|
return $ route
|
||||||
|
& typesUsing @RouteChildren @WorkflowDefinitionName . filtered (== wdn) .~ workflowDefinitionName
|
||||||
|
ncGlobalWorkflowInstance = maybeOrig $ \route -> do
|
||||||
|
GlobalWorkflowInstanceR wdn _ <- return route
|
||||||
|
Entity _ WorkflowInstance{..} <- MaybeT . $cachedHereBinary wdn . lift . getBy $ UniqueWorkflowInstance wdn WSGlobal
|
||||||
|
caseChanged wdn workflowInstanceName
|
||||||
|
return $ route
|
||||||
|
& typesUsing @RouteChildren @WorkflowInstanceName . filtered (== wdn) .~ workflowInstanceName
|
||||||
verifySubmission = maybeOrig $ \route -> do
|
verifySubmission = maybeOrig $ \route -> do
|
||||||
CSubmissionR _tid _ssh _csh _shn cID sr <- return route
|
CSubmissionR _tid _ssh _csh _shn cID sr <- return route
|
||||||
sId <- $cachedHereBinary cID $ decrypt cID
|
sId <- $cachedHereBinary cID $ decrypt cID
|
||||||
@ -248,3 +262,5 @@ routeNormalizers = map (hoist (hoist liftHandler . withReaderT projectBackend) .
|
|||||||
let newRoute = CNewsR courseTerm courseSchool courseShorthand cID sr
|
let newRoute = CNewsR courseTerm courseSchool courseShorthand cID sr
|
||||||
tell . Any $ route /= newRoute
|
tell . Any $ route /= newRoute
|
||||||
return newRoute
|
return newRoute
|
||||||
|
|
||||||
|
-- TODO: verify*WorkflowWorkflow
|
||||||
|
|||||||
143
src/Handler/Utils/Workflow/Form.hs
Normal file
143
src/Handler/Utils/Workflow/Form.hs
Normal file
@ -0,0 +1,143 @@
|
|||||||
|
module Handler.Utils.Workflow.Form
|
||||||
|
( FileIdent
|
||||||
|
, WorkflowGraphForm(..)
|
||||||
|
, workflowGraphForm
|
||||||
|
, toWorkflowGraphForm, fromWorkflowGraphForm
|
||||||
|
, WorkflowDescriptionsFormScope(..)
|
||||||
|
, workflowDescriptionsForm
|
||||||
|
) where
|
||||||
|
|
||||||
|
import Import
|
||||||
|
import Utils.Form
|
||||||
|
|
||||||
|
import Handler.Utils.Form
|
||||||
|
|
||||||
|
import qualified Data.Conduit.Combinators as C
|
||||||
|
|
||||||
|
import qualified Data.Map as Map
|
||||||
|
import Data.Map ((!))
|
||||||
|
import qualified Data.Set as Set
|
||||||
|
import qualified Data.CaseInsensitive as CI
|
||||||
|
|
||||||
|
import Data.Bimap (Bimap)
|
||||||
|
import qualified Data.Bimap as Bimap
|
||||||
|
|
||||||
|
import qualified Control.Monad.State.Class as State
|
||||||
|
|
||||||
|
import qualified Data.List.NonEmpty as NonEmpty
|
||||||
|
|
||||||
|
|
||||||
|
newtype FileIdent = FileIdent (CI Text)
|
||||||
|
deriving (Eq, Ord, Read, Show, Generic, Typeable)
|
||||||
|
deriving newtype (ToMessage, ToJSON, FromJSON)
|
||||||
|
|
||||||
|
makeWrapped ''FileIdent
|
||||||
|
|
||||||
|
data WorkflowGraphForm = WorkflowGraphForm
|
||||||
|
{ wgfGraph :: WorkflowGraph FileIdent CryptoUUIDUser
|
||||||
|
, wgfFiles :: Map FileIdent FileReference
|
||||||
|
} deriving (Generic, Typeable)
|
||||||
|
|
||||||
|
makeLenses_ ''WorkflowGraphForm
|
||||||
|
|
||||||
|
|
||||||
|
workflowGraphForm :: Maybe WorkflowGraphForm -> AForm DB WorkflowGraphForm
|
||||||
|
workflowGraphForm template = validateAForm validateWorkflowGraphForm . hoistAForm lift $ WorkflowGraphForm
|
||||||
|
<$> areq yamlField (fslI MsgWorkflowDefinitionGraph) (wgfGraph <$> template)
|
||||||
|
<*> filesForm
|
||||||
|
where
|
||||||
|
filesForm = Map.fromList <$> massInputAccumEditA fileAdd fileEdit (const Nothing) fileLayout ("workflow-definition-files" :: Text) (fslI MsgWorkflowDefinitionFiles) False (Map.toList . wgfFiles <$> template)
|
||||||
|
where fileAdd nudge submitView csrf = do
|
||||||
|
(formRes, formView) <- fileForm nudge Nothing csrf
|
||||||
|
MsgRenderer mr <- getMsgRenderer
|
||||||
|
let res' = formRes <&> \newFile@(newFileIdent, _) oldFiles -> if
|
||||||
|
| any (\(oldFileIdent, _) -> newFileIdent == oldFileIdent) oldFiles
|
||||||
|
-> FormFailure [mr MsgWorkflowDefinitionFileIdentExists]
|
||||||
|
| otherwise
|
||||||
|
-> FormSuccess $ pure newFile
|
||||||
|
return (res', $(widgetFile "widgets/massinput/workflowDefinitionFiles/add"))
|
||||||
|
fileEdit nudge = fileForm nudge . Just
|
||||||
|
fileForm :: (Text -> Text) -> Maybe (FileIdent, FileReference) -> Form (FileIdent, FileReference)
|
||||||
|
fileForm nudge fileTemplate csrf = do
|
||||||
|
(fileIdentRes, fileIdentView) <- mpreq (isoField _Unwrapped ciField) (fslI MsgWorkflowDefinitionFileIdent & addName (nudge "ident")) (view _1 <$> fileTemplate)
|
||||||
|
(fileRes, fileView) <- mpreq (singleFileField . fromMaybe (return ()) $ views _2 yield <$> fileTemplate) (fslI MsgWorkflowDefinitionFile & addName (nudge "file")) (views _2 yield <$> fileTemplate)
|
||||||
|
fileRes' <- liftHandler . runDB $ case fileRes of
|
||||||
|
FormSuccess uploads -> maybe FormMissing FormSuccess <$> runConduit (transPipe liftHandler uploads .| C.head)
|
||||||
|
FormFailure errs -> return $ FormFailure errs
|
||||||
|
FormMissing -> return FormMissing
|
||||||
|
return ((,) <$> fileIdentRes <*> fileRes', $(widgetFile "widgets/massinput/workflowDefinitionFiles/form"))
|
||||||
|
fileLayout :: MassInputLayout ListLength (FileIdent, FileReference) (FileIdent, FileReference)
|
||||||
|
fileLayout lLength _ cellWdgts delButtons addWdgts = $(widgetFile "widgets/massinput/workflowDefinitionFiles/layout")
|
||||||
|
|
||||||
|
validateWorkflowGraphForm :: FormValidator WorkflowGraphForm DB ()
|
||||||
|
validateWorkflowGraphForm = do
|
||||||
|
fIdentsReferenced <- uses _wgfGraph . setOf $ typesCustom @WorkflowChildren
|
||||||
|
fIdentsAvailable <- uses _wgfFiles Map.keysSet
|
||||||
|
forM_ (fIdentsReferenced `Set.difference` fIdentsAvailable) $ tellValidationError . MsgWorkflowFileIdentDoesNotExist . views _Wrapped CI.original
|
||||||
|
|
||||||
|
toWorkflowGraphForm :: ( MonadHandler m, HandlerSite m ~ UniWorX
|
||||||
|
)
|
||||||
|
=> WorkflowGraph FileReference SqlBackendKey
|
||||||
|
-> m WorkflowGraphForm
|
||||||
|
toWorkflowGraphForm g = liftHandler . fmap (uncurry WorkflowGraphForm . over _2 Bimap.toMap) . (runStateT ?? Bimap.empty) . ($ g)
|
||||||
|
$ traverseOf (typesCustom @WorkflowChildren) recordFile
|
||||||
|
>=> traverseOf (typesCustom @WorkflowChildren @(WorkflowGraph FileIdent SqlBackendKey) @_ @_ @CryptoUUIDUser) (encrypt . review (_SqlKey @User))
|
||||||
|
where
|
||||||
|
recordFile :: forall m. Monad m => FileReference -> StateT (Bimap FileIdent FileReference) m FileIdent
|
||||||
|
recordFile fRef@FileReference{..} = do
|
||||||
|
prev <- State.gets $ Bimap.lookupR fRef
|
||||||
|
case prev of
|
||||||
|
Just fIdent -> return fIdent
|
||||||
|
Nothing -> do
|
||||||
|
cMap <- State.get
|
||||||
|
let candidateIdents = map (review _Wrapped . CI.mk) $
|
||||||
|
map pack $ fileReferenceTitle : [ base <.> show n <.> ext | n <- [1..] :: [Natural], let (base, ext) = splitExtension fileReferenceTitle ]
|
||||||
|
fIdent = case filter (`Bimap.notMember` cMap) candidateIdents of
|
||||||
|
fIdent' : _ -> fIdent'
|
||||||
|
[] -> error "candidateIdents should be infinite; cMap should be finite"
|
||||||
|
State.modify $ Bimap.insert fIdent fRef
|
||||||
|
return fIdent
|
||||||
|
|
||||||
|
fromWorkflowGraphForm :: (MonadHandler m, HandlerSite m ~ UniWorX)
|
||||||
|
=> WorkflowGraphForm
|
||||||
|
-> m (WorkflowGraph FileReference SqlBackendKey)
|
||||||
|
fromWorkflowGraphForm WorkflowGraphForm{..}
|
||||||
|
= liftHandler $ wgfGraph
|
||||||
|
& over (typesCustom @WorkflowChildren) (wgfFiles !)
|
||||||
|
& traverseOf (typesCustom @WorkflowChildren @(WorkflowGraph FileReference CryptoUUIDUser) @_ @CryptoUUIDUser) (fmap (view $ _SqlKey @User) . decrypt)
|
||||||
|
|
||||||
|
|
||||||
|
data WorkflowDescriptionsFormScope
|
||||||
|
= WorkflowDescriptionsFormDefinition
|
||||||
|
| WorkflowDescriptionsFormInstance
|
||||||
|
deriving (Eq, Ord, Bounded, Enum, Read, Show, Generic, Typeable)
|
||||||
|
deriving (Universe, Finite)
|
||||||
|
|
||||||
|
workflowDescriptionsForm :: WorkflowDescriptionsFormScope -> Maybe (Map Lang (Text, Maybe Html)) -> AForm Handler (Map Lang (Text, Maybe Html))
|
||||||
|
workflowDescriptionsForm scope template = Map.fromList <$> massInputAccumEditA descrAdd descrEdit (const Nothing) descrLayout ("workflow-descriptions" :: Text) (fslI msgWorkflowDescriptions) False (Map.toList <$> template)
|
||||||
|
where
|
||||||
|
descrAdd nudge submitView csrf = do
|
||||||
|
(formRes, formView) <- descrForm nudge Nothing csrf
|
||||||
|
MsgRenderer mr <- getMsgRenderer
|
||||||
|
let res' = formRes <&> \newDescr@(newLang, _) oldDescrs -> if
|
||||||
|
| any (\(oldLang, _) -> newLang == oldLang) oldDescrs
|
||||||
|
-> FormFailure [mr msgWorkflowDescriptionsLanguageExists]
|
||||||
|
| otherwise
|
||||||
|
-> FormSuccess $ pure newDescr
|
||||||
|
return (res', $(widgetFile "widgets/massinput/workflowDescriptions/add"))
|
||||||
|
descrEdit nudge = descrForm nudge . Just
|
||||||
|
descrForm :: (Text -> Text) -> Maybe (Lang, (Text, Maybe Html)) -> Form (Lang, (Text, Maybe Html))
|
||||||
|
descrForm nudge descrTemplate csrf = do
|
||||||
|
(langRes, langView) <- mpreq (langField False) (fslI MsgWorkflowDescriptionLanguage & addName (nudge "lang")) (fmap (view _1) descrTemplate <|> Just (NonEmpty.head appLanguages))
|
||||||
|
(titleRes, titleView) <- mpreq textField (fslI MsgWorkflowDescriptionTitle & addName (nudge "title")) (view (_2 . _1) <$> descrTemplate)
|
||||||
|
(descrRes, descrView) <- mopt htmlField (fslI MsgWorkflowDescription & addName (nudge "descr")) (view (_2 . _2) <$> descrTemplate)
|
||||||
|
return ((,) <$> langRes <*> ((,) <$> titleRes <*> descrRes), $(widgetFile "widgets/massinput/workflowDescriptions/form"))
|
||||||
|
descrLayout :: MassInputLayout ListLength (Lang, (Text, Maybe Html)) (Lang, (Text, Maybe Html))
|
||||||
|
descrLayout lLength _ cellWdgts delButtons addWdgts = $(widgetFile "widgets/massinput/workflowDescriptions/layout")
|
||||||
|
|
||||||
|
msgWorkflowDescriptions = case scope of
|
||||||
|
WorkflowDescriptionsFormDefinition -> MsgWorkflowDefinitionDescriptions
|
||||||
|
WorkflowDescriptionsFormInstance -> MsgWorkflowInstanceDescriptions
|
||||||
|
msgWorkflowDescriptionsLanguageExists = case scope of
|
||||||
|
WorkflowDescriptionsFormDefinition -> MsgWorkflowDefinitionDescriptionsLanguageExists
|
||||||
|
WorkflowDescriptionsFormInstance -> MsgWorkflowInstanceDescriptionsLanguageExists
|
||||||
17
src/Handler/Utils/Workflow/Workflow.hs
Normal file
17
src/Handler/Utils/Workflow/Workflow.hs
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
module Handler.Utils.Workflow.Workflow
|
||||||
|
( ensureScope
|
||||||
|
) where
|
||||||
|
|
||||||
|
import Import
|
||||||
|
|
||||||
|
|
||||||
|
ensureScope :: WorkflowScope TermId SchoolId CourseId -> CryptoFileNameWorkflowWorkflow -> MaybeT DB WorkflowWorkflowId
|
||||||
|
ensureScope wiScope cID = do
|
||||||
|
wId <- catchMaybeT (Proxy @CryptoIDError) $ decrypt cID
|
||||||
|
WorkflowWorkflow{..} <- MaybeT $ get wId
|
||||||
|
let wiScope' = wiScope
|
||||||
|
& _wisTerm %~ unTermKey
|
||||||
|
& _wisSchool %~ unSchoolKey
|
||||||
|
& _wisCourse %~ view _SqlKey
|
||||||
|
guard $ workflowWorkflowScope == wiScope'
|
||||||
|
return wId
|
||||||
@ -3,3 +3,5 @@ module Handler.Workflow
|
|||||||
) where
|
) where
|
||||||
|
|
||||||
import Handler.Workflow.Definition as Handler.Workflow
|
import Handler.Workflow.Definition as Handler.Workflow
|
||||||
|
import Handler.Workflow.Instance as Handler.Workflow
|
||||||
|
import Handler.Workflow.Workflow as Handler.Workflow
|
||||||
|
|||||||
@ -6,3 +6,4 @@ import Handler.Workflow.Definition.List as Handler.Workflow.Definition
|
|||||||
import Handler.Workflow.Definition.New as Handler.Workflow.Definition
|
import Handler.Workflow.Definition.New as Handler.Workflow.Definition
|
||||||
import Handler.Workflow.Definition.Edit as Handler.Workflow.Definition
|
import Handler.Workflow.Definition.Edit as Handler.Workflow.Definition
|
||||||
import Handler.Workflow.Definition.Delete as Handler.Workflow.Definition
|
import Handler.Workflow.Definition.Delete as Handler.Workflow.Definition
|
||||||
|
import Handler.Workflow.Definition.Instantiate as Handler.Workflow.Definition
|
||||||
|
|||||||
@ -11,7 +11,7 @@ import qualified Database.Esqueleto as E
|
|||||||
import qualified Data.Set as Set
|
import qualified Data.Set as Set
|
||||||
|
|
||||||
|
|
||||||
getAWDDeleteR, postAWDDeleteR :: WorkflowInstanceScope' -> WorkflowDefinitionName -> Handler Html
|
getAWDDeleteR, postAWDDeleteR :: WorkflowScope' -> WorkflowDefinitionName -> Handler Html
|
||||||
getAWDDeleteR = postAWDDeleteR
|
getAWDDeleteR = postAWDDeleteR
|
||||||
postAWDDeleteR wds' wdn = do
|
postAWDDeleteR wds' wdn = do
|
||||||
wdId <- runDB . getKeyBy404 $ UniqueWorkflowDefinition wdn wds'
|
wdId <- runDB . getKeyBy404 $ UniqueWorkflowDefinition wdn wds'
|
||||||
|
|||||||
@ -9,17 +9,9 @@ import Handler.Utils
|
|||||||
import Handler.Workflow.Definition.Form
|
import Handler.Workflow.Definition.Form
|
||||||
|
|
||||||
import qualified Data.Map as Map
|
import qualified Data.Map as Map
|
||||||
import Data.Map.Strict ((!))
|
|
||||||
|
|
||||||
import Data.Bimap (Bimap)
|
|
||||||
import qualified Data.Bimap as Bimap
|
|
||||||
|
|
||||||
import qualified Control.Monad.State.Class as State
|
|
||||||
|
|
||||||
import qualified Data.CaseInsensitive as CI
|
|
||||||
|
|
||||||
|
|
||||||
getAWDEditR, postAWDEditR :: WorkflowInstanceScope' -> WorkflowDefinitionName -> Handler Html
|
getAWDEditR, postAWDEditR :: WorkflowScope' -> WorkflowDefinitionName -> Handler Html
|
||||||
getAWDEditR = postAWDEditR
|
getAWDEditR = postAWDEditR
|
||||||
postAWDEditR wds' wdn = do
|
postAWDEditR wds' wdn = do
|
||||||
(((_, editForm), editEncoding), act) <- runDB $ do
|
(((_, editForm), editEncoding), act) <- runDB $ do
|
||||||
@ -31,44 +23,33 @@ postAWDEditR wds' wdn = do
|
|||||||
| Entity _ WorkflowDefinitionDescription{..} <- descs
|
| Entity _ WorkflowDefinitionDescription{..} <- descs
|
||||||
]
|
]
|
||||||
|
|
||||||
let recordFile :: forall m. Monad m => FileReference -> StateT (Bimap FileIdent FileReference) m FileIdent
|
iDescs <- selectList [WorkflowDefinitionInstanceDescriptionDefinition ==. wdId] []
|
||||||
recordFile fRef@FileReference{..} = do
|
let wdfInstanceDescriptions = Map.fromList
|
||||||
prev <- State.gets $ Bimap.lookupR fRef
|
[ (workflowDefinitionInstanceDescriptionLanguage, (workflowDefinitionInstanceDescriptionTitle, workflowDefinitionInstanceDescriptionDescription))
|
||||||
case prev of
|
| Entity _ WorkflowDefinitionInstanceDescription{..} <- iDescs
|
||||||
Just fIdent -> return fIdent
|
]
|
||||||
Nothing -> do
|
|
||||||
cMap <- State.get
|
|
||||||
let candidateIdents = map (review _Wrapped . CI.mk) $
|
|
||||||
map pack $ fileReferenceTitle : [ base <.> show n <.> ext | n <- [1..] :: [Natural], let (base, ext) = splitExtension fileReferenceTitle ]
|
|
||||||
fIdent = case filter (`Bimap.notMember` cMap) candidateIdents of
|
|
||||||
fIdent' : _ -> fIdent'
|
|
||||||
[] -> error "candidateIdents should be infinite; cMap should be finite"
|
|
||||||
State.modify $ Bimap.insert fIdent fRef
|
|
||||||
return fIdent
|
|
||||||
|
|
||||||
(wdfGraph, Bimap.toMap -> wdfFiles) <- (runStateT ?? Bimap.empty) . ($ workflowDefinitionGraph)
|
wdfGraph <- toWorkflowGraphForm workflowDefinitionGraph
|
||||||
$ traverseOf (typesCustom @WorkflowChildren) recordFile
|
|
||||||
>=> traverseOf (typesCustom @WorkflowChildren @(WorkflowGraph FileIdent SqlBackendKey) @_ @_ @CryptoUUIDUser) (encrypt . review (_SqlKey @User))
|
|
||||||
|
|
||||||
return WorkflowDefinitionForm
|
return WorkflowDefinitionForm
|
||||||
{ wdfScope = workflowDefinitionScope
|
{ wdfScope = workflowDefinitionScope
|
||||||
, wdfName = workflowDefinitionName
|
, wdfName = workflowDefinitionName
|
||||||
|
, wdfInstanceCategory = workflowDefinitionInstanceCategory
|
||||||
, wdfDescriptions
|
, wdfDescriptions
|
||||||
|
, wdfInstanceDescriptions
|
||||||
, wdfGraph
|
, wdfGraph
|
||||||
, wdfFiles
|
|
||||||
}
|
}
|
||||||
|
|
||||||
form@((editRes, _), _) <- runFormPost . workflowDefinitionForm $ Just template
|
form@((editRes, _), _) <- runFormPost . workflowDefinitionForm $ Just template
|
||||||
|
|
||||||
act <- formResultMaybe editRes $ \WorkflowDefinitionForm{..} -> do
|
act <- formResultMaybe editRes $ \WorkflowDefinitionForm{..} -> do
|
||||||
wdfGraph' <- wdfGraph
|
wdfGraph' <- fromWorkflowGraphForm wdfGraph
|
||||||
& over (typesCustom @WorkflowChildren) (wdfFiles !)
|
|
||||||
& traverseOf (typesCustom @WorkflowChildren @(WorkflowGraph FileReference CryptoUUIDUser) @_ @CryptoUUIDUser) (fmap (view $ _SqlKey @User) . decrypt)
|
|
||||||
|
|
||||||
insConflict <- replaceUnique wdId WorkflowDefinition
|
insConflict <- replaceUnique wdId WorkflowDefinition
|
||||||
{ workflowDefinitionGraph = wdfGraph'
|
{ workflowDefinitionGraph = wdfGraph'
|
||||||
, workflowDefinitionScope = wdfScope
|
, workflowDefinitionScope = wdfScope
|
||||||
, workflowDefinitionName = wdfName
|
, workflowDefinitionName = wdfName
|
||||||
|
, workflowDefinitionInstanceCategory = wdfInstanceCategory
|
||||||
}
|
}
|
||||||
|
|
||||||
when (is _Nothing insConflict) . iforM_ wdfDescriptions $ \wddLang (wddTitle, wddDesc) -> do
|
when (is _Nothing insConflict) . iforM_ wdfDescriptions $ \wddLang (wddTitle, wddDesc) -> do
|
||||||
@ -79,6 +60,14 @@ postAWDEditR wds' wdn = do
|
|||||||
, workflowDefinitionDescriptionTitle = wddTitle
|
, workflowDefinitionDescriptionTitle = wddTitle
|
||||||
, workflowDefinitionDescriptionDescription = wddDesc
|
, workflowDefinitionDescriptionDescription = wddDesc
|
||||||
}
|
}
|
||||||
|
when (is _Nothing insConflict) . iforM_ wdfInstanceDescriptions $ \wddLang (wddTitle, wddDesc) -> do
|
||||||
|
deleteWhere [WorkflowDefinitionInstanceDescriptionDefinition ==. wdId]
|
||||||
|
insert WorkflowDefinitionInstanceDescription
|
||||||
|
{ workflowDefinitionInstanceDescriptionDefinition = wdId
|
||||||
|
, workflowDefinitionInstanceDescriptionLanguage = wddLang
|
||||||
|
, workflowDefinitionInstanceDescriptionTitle = wddTitle
|
||||||
|
, workflowDefinitionInstanceDescriptionDescription = wddDesc
|
||||||
|
}
|
||||||
|
|
||||||
case insConflict of
|
case insConflict of
|
||||||
Just (UniqueWorkflowDefinition wdn' wds'') -> return . Just $
|
Just (UniqueWorkflowDefinition wdn' wds'') -> return . Just $
|
||||||
|
|||||||
@ -1,103 +1,43 @@
|
|||||||
module Handler.Workflow.Definition.Form
|
module Handler.Workflow.Definition.Form
|
||||||
( WorkflowDefinitionForm(..), FileIdent
|
( WorkflowDefinitionForm(..)
|
||||||
, workflowDefinitionForm
|
, workflowDefinitionForm
|
||||||
|
, module Handler.Utils.Workflow.Form
|
||||||
) where
|
) where
|
||||||
|
|
||||||
import Import
|
import Import
|
||||||
|
|
||||||
import Handler.Utils
|
import Handler.Utils
|
||||||
import qualified Data.List.NonEmpty as NonEmpty
|
import Handler.Utils.Workflow.Form
|
||||||
|
|
||||||
import qualified Data.Set as Set
|
|
||||||
import qualified Data.Map as Map
|
|
||||||
import Data.Map ((!))
|
|
||||||
|
|
||||||
import qualified Data.CryptoID as C
|
import qualified Data.CryptoID as C
|
||||||
|
|
||||||
import qualified Data.CaseInsensitive as CI
|
|
||||||
|
|
||||||
import qualified Data.Conduit.Combinators as C
|
|
||||||
|
|
||||||
|
|
||||||
newtype FileIdent = FileIdent (CI Text)
|
|
||||||
deriving (Eq, Ord, Read, Show, Generic, Typeable)
|
|
||||||
deriving newtype (ToMessage, ToJSON, FromJSON)
|
|
||||||
|
|
||||||
makeWrapped ''FileIdent
|
|
||||||
|
|
||||||
|
|
||||||
data WorkflowDefinitionForm = WorkflowDefinitionForm
|
data WorkflowDefinitionForm = WorkflowDefinitionForm
|
||||||
{ wdfScope :: WorkflowInstanceScope'
|
{ wdfScope :: WorkflowScope'
|
||||||
, wdfName :: CI Text
|
, wdfName :: WorkflowDefinitionName
|
||||||
|
, wdfInstanceCategory :: Maybe WorkflowInstanceCategory
|
||||||
, wdfDescriptions :: Map Lang (Text, Maybe Html)
|
, wdfDescriptions :: Map Lang (Text, Maybe Html)
|
||||||
, wdfGraph :: WorkflowGraph FileIdent CryptoUUIDUser
|
, wdfInstanceDescriptions :: Map Lang (Text, Maybe Html)
|
||||||
, wdfFiles :: Map FileIdent FileReference
|
, wdfGraph :: WorkflowGraphForm
|
||||||
} deriving (Generic, Typeable)
|
} deriving (Generic, Typeable)
|
||||||
|
|
||||||
makeLenses_ ''WorkflowDefinitionForm
|
makeLenses_ ''WorkflowDefinitionForm
|
||||||
|
|
||||||
workflowDefinitionForm :: Maybe WorkflowDefinitionForm -> Html -> MForm DB (FormResult WorkflowDefinitionForm, Widget)
|
workflowDefinitionForm :: Maybe WorkflowDefinitionForm -> Html -> MForm DB (FormResult WorkflowDefinitionForm, Widget)
|
||||||
workflowDefinitionForm template = validateForm validateWorkflowDefinitionForm . (hoist lift .) . renderAForm FormStandard $ WorkflowDefinitionForm
|
workflowDefinitionForm template = validateForm validateWorkflowDefinitionForm . renderAForm FormStandard $ WorkflowDefinitionForm
|
||||||
<$> apopt (selectField optionsFinite) (fslI MsgWorkflowDefinitionScope) (wdfScope <$> template)
|
<$> apopt (hoistField lift $ selectField optionsFinite) (fslI MsgWorkflowDefinitionScope) (wdfScope <$> template)
|
||||||
<*> areq ciField (fslI MsgWorkflowDefinitionName) (wdfName <$> template)
|
<*> areq ciField (fslI MsgWorkflowDefinitionName) (wdfName <$> template)
|
||||||
<*> descriptionsForm
|
<*> aopt ciField (fslI MsgWorkflowDefinitionInstanceCategory) (wdfInstanceCategory <$> template)
|
||||||
<*> areq yamlField (fslI MsgWorkflowDefinitionGraph) (wdfGraph <$> template)
|
<*> hoistAForm lift (workflowDescriptionsForm WorkflowDescriptionsFormDefinition $ wdfDescriptions <$> template)
|
||||||
<*> filesForm
|
<*> hoistAForm lift (workflowDescriptionsForm WorkflowDescriptionsFormInstance $ wdfInstanceDescriptions <$> template)
|
||||||
where
|
<*> workflowGraphForm (wdfGraph <$> template)
|
||||||
descriptionsForm = Map.fromList <$> massInputAccumEditA descrAdd descrEdit (const Nothing) descrLayout ("workflow-definition-descriptions" :: Text) (fslI MsgWorkflowDefinitionDescriptions) False (Map.toList . wdfDescriptions <$> template)
|
|
||||||
where descrAdd nudge submitView csrf = do
|
|
||||||
(formRes, formView) <- descrForm nudge Nothing csrf
|
|
||||||
MsgRenderer mr <- getMsgRenderer
|
|
||||||
let res' = formRes <&> \newDescr@(newLang, _) oldDescrs -> if
|
|
||||||
| any (\(oldLang, _) -> newLang == oldLang) oldDescrs
|
|
||||||
-> FormFailure [mr MsgWorkflowDefinitionDescriptionsLanguageExists]
|
|
||||||
| otherwise
|
|
||||||
-> FormSuccess $ pure newDescr
|
|
||||||
return (res', $(widgetFile "widgets/massinput/workflowDefinitionDescriptions/add"))
|
|
||||||
descrEdit nudge = descrForm nudge . Just
|
|
||||||
descrForm :: (Text -> Text) -> Maybe (Lang, (Text, Maybe Html)) -> Form (Lang, (Text, Maybe Html))
|
|
||||||
descrForm nudge descrTemplate csrf = do
|
|
||||||
(langRes, langView) <- mpreq (langField False) (fslI MsgWorkflowDefinitionDescriptionLanguage & addName (nudge "lang")) (fmap (view _1) descrTemplate <|> Just (NonEmpty.head appLanguages))
|
|
||||||
(titleRes, titleView) <- mpreq textField (fslI MsgWorkflowDefinitionDescriptionTitle & addName (nudge "title")) (view (_2 . _1) <$> descrTemplate)
|
|
||||||
(descrRes, descrView) <- mopt htmlField (fslI MsgWorkflowDefinitionDescription & addName (nudge "descr")) (view (_2 . _2) <$> descrTemplate)
|
|
||||||
return ((,) <$> langRes <*> ((,) <$> titleRes <*> descrRes), $(widgetFile "widgets/massinput/workflowDefinitionDescriptions/form"))
|
|
||||||
descrLayout :: MassInputLayout ListLength (Lang, (Text, Maybe Html)) (Lang, (Text, Maybe Html))
|
|
||||||
descrLayout lLength _ cellWdgts delButtons addWdgts = $(widgetFile "widgets/massinput/workflowDefinitionDescriptions/layout")
|
|
||||||
|
|
||||||
filesForm = Map.fromList <$> massInputAccumEditA fileAdd fileEdit (const Nothing) fileLayout ("workflow-definition-files" :: Text) (fslI MsgWorkflowDefinitionFiles) False (Map.toList . wdfFiles <$> template)
|
|
||||||
where fileAdd nudge submitView csrf = do
|
|
||||||
(formRes, formView) <- fileForm nudge Nothing csrf
|
|
||||||
MsgRenderer mr <- getMsgRenderer
|
|
||||||
let res' = formRes <&> \newFile@(newFileIdent, _) oldFiles -> if
|
|
||||||
| any (\(oldFileIdent, _) -> newFileIdent == oldFileIdent) oldFiles
|
|
||||||
-> FormFailure [mr MsgWorkflowDefinitionFileIdentExists]
|
|
||||||
| otherwise
|
|
||||||
-> FormSuccess $ pure newFile
|
|
||||||
return (res', $(widgetFile "widgets/massinput/workflowDefinitionFiles/add"))
|
|
||||||
fileEdit nudge = fileForm nudge . Just
|
|
||||||
fileForm :: (Text -> Text) -> Maybe (FileIdent, FileReference) -> Form (FileIdent, FileReference)
|
|
||||||
fileForm nudge fileTemplate csrf = do
|
|
||||||
(fileIdentRes, fileIdentView) <- mpreq (isoField _Unwrapped ciField) (fslI MsgWorkflowDefinitionFileIdent & addName (nudge "ident")) (view _1 <$> fileTemplate)
|
|
||||||
(fileRes, fileView) <- mpreq (singleFileField . fromMaybe (return ()) $ views _2 yield <$> fileTemplate) (fslI MsgWorkflowDefinitionFile & addName (nudge "file")) (views _2 yield <$> fileTemplate)
|
|
||||||
fileRes' <- liftHandler . runDB $ case fileRes of
|
|
||||||
FormSuccess uploads -> maybe FormMissing FormSuccess <$> runConduit (transPipe liftHandler uploads .| C.head)
|
|
||||||
FormFailure errs -> return $ FormFailure errs
|
|
||||||
FormMissing -> return FormMissing
|
|
||||||
return ((,) <$> fileIdentRes <*> fileRes', $(widgetFile "widgets/massinput/workflowDefinitionFiles/form"))
|
|
||||||
fileLayout :: MassInputLayout ListLength (FileIdent, FileReference) (FileIdent, FileReference)
|
|
||||||
fileLayout lLength _ cellWdgts delButtons addWdgts = $(widgetFile "widgets/massinput/workflowDefinitionFiles/layout")
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
validateWorkflowDefinitionForm :: FormValidator WorkflowDefinitionForm DB ()
|
validateWorkflowDefinitionForm :: FormValidator WorkflowDefinitionForm DB ()
|
||||||
validateWorkflowDefinitionForm = do
|
validateWorkflowDefinitionForm = do
|
||||||
join . uses _wdfGraph . mapMOf_ (typesCustom @WorkflowChildren) . ensureExists $ Proxy @User
|
join . uses _wdfGraph . mapMOf_ (typesCustom @WorkflowChildren) . ensureExists $ Proxy @User
|
||||||
fIdentsReferenced <- uses _wdfGraph . setOf $ typesCustom @WorkflowChildren
|
|
||||||
fIdentsAvailable <- uses _wdfFiles Map.keysSet
|
|
||||||
forM_ (fIdentsReferenced `Set.difference` fIdentsAvailable) $ tellValidationError . MsgWorkflowDefinitionFileIdentDoesNotExist . views _Wrapped CI.original
|
|
||||||
where
|
where
|
||||||
ensureExists :: forall record ns p r. _ => p record -> C.CryptoID ns UUID -> FormValidator r DB ()
|
ensureExists :: forall record ns p r. _ => p record -> C.CryptoID ns UUID -> FormValidator r DB ()
|
||||||
ensureExists _ cID = maybeT (tellValidationError . MsgWorkflowDefinitionFileIdentDoesNotExist $ toPathPiece cID) . catchMPlus (Proxy @CryptoIDError) $ do
|
ensureExists _ cID = maybeT (tellValidationError . MsgWorkflowUserDoesNotExist $ toPathPiece cID) . catchMPlus (Proxy @CryptoIDError) $ do
|
||||||
$logDebugS "validateWorkflowDefinitionForm" $ "Checking key for existence: " <> toPathPiece cID
|
$logDebugS "validateWorkflowDefinitionForm" $ "Checking key for existence: " <> toPathPiece cID
|
||||||
key <- decrypt cID
|
key <- decrypt cID
|
||||||
guardM . lift . lift $ existsKey (key :: Key record)
|
guardM . lift . lift $ existsKey (key :: Key record)
|
||||||
|
|||||||
59
src/Handler/Workflow/Definition/Instantiate.hs
Normal file
59
src/Handler/Workflow/Definition/Instantiate.hs
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
module Handler.Workflow.Definition.Instantiate
|
||||||
|
( getAWDInstantiateR, postAWDInstantiateR
|
||||||
|
) where
|
||||||
|
|
||||||
|
import Import
|
||||||
|
import Handler.Utils
|
||||||
|
import Handler.Utils.Workflow.Form
|
||||||
|
|
||||||
|
import Handler.Workflow.Instance.Form
|
||||||
|
|
||||||
|
|
||||||
|
getAWDInstantiateR, postAWDInstantiateR :: WorkflowScope' -> WorkflowDefinitionName -> Handler Html
|
||||||
|
getAWDInstantiateR = postAWDInstantiateR
|
||||||
|
postAWDInstantiateR wds' wdn = do
|
||||||
|
(((_, instForm), instEncoding), act) <- runDB $ do
|
||||||
|
wdId <- getKeyBy404 $ UniqueWorkflowDefinition wdn wds'
|
||||||
|
form@((instRes, _), _) <- runFormPost $ workflowInstanceForm (Just wdId) Nothing
|
||||||
|
|
||||||
|
act <- formResultMaybe instRes $ \WorkflowInstanceForm{..} -> do
|
||||||
|
wifGraph' <- fromWorkflowGraphForm wifGraph
|
||||||
|
let wifScope' = wifScope
|
||||||
|
& over _wisTerm unTermKey
|
||||||
|
& over _wisSchool unSchoolKey
|
||||||
|
& over _wisCourse (view _SqlKey)
|
||||||
|
instId <- insertUnique WorkflowInstance
|
||||||
|
{ workflowInstanceDefinition = Just wdId
|
||||||
|
, workflowInstanceGraph = wifGraph'
|
||||||
|
, workflowInstanceScope = wifScope'
|
||||||
|
, workflowInstanceName = wifName
|
||||||
|
, workflowInstanceCategory = wifCategory
|
||||||
|
}
|
||||||
|
|
||||||
|
for_ instId $ \instId' -> iforM_ wifDescriptions $ \widLang (widTitle, widDesc) ->
|
||||||
|
insert WorkflowInstanceDescription
|
||||||
|
{ workflowInstanceDescriptionInstance = instId'
|
||||||
|
, workflowInstanceDescriptionLanguage = widLang
|
||||||
|
, workflowInstanceDescriptionTitle = widTitle
|
||||||
|
, workflowInstanceDescriptionDescription = widDesc
|
||||||
|
}
|
||||||
|
|
||||||
|
return . Just $ case instId of
|
||||||
|
Nothing -> addMessageI Error MsgWorkflowInstanceCollision
|
||||||
|
Just _ -> do
|
||||||
|
addMessageI Success MsgWorkflowDefinitionInstantiated
|
||||||
|
redirect AdminWorkflowInstanceListR
|
||||||
|
|
||||||
|
return (form, act)
|
||||||
|
|
||||||
|
forM_ act id
|
||||||
|
|
||||||
|
let instWidget = wrapForm instForm def
|
||||||
|
{ formAction = Just . SomeRoute $ AdminWorkflowDefinitionR wds' wdn AWDInstantiateR
|
||||||
|
, formEncoding = instEncoding
|
||||||
|
}
|
||||||
|
|
||||||
|
siteLayoutMsg MsgWorkflowDefinitionInstantiateTitle $ do
|
||||||
|
setTitleI MsgWorkflowDefinitionInstantiateTitle
|
||||||
|
|
||||||
|
instWidget
|
||||||
@ -1,7 +1,7 @@
|
|||||||
{-# OPTIONS_GHC -fno-warn-redundant-constraints #-}
|
{-# OPTIONS_GHC -fno-warn-redundant-constraints #-}
|
||||||
|
|
||||||
module Handler.Workflow.Definition.List
|
module Handler.Workflow.Definition.List
|
||||||
( getAdminWorkflowDefinitionListR, postAdminWorkflowDefinitionListR
|
( getAdminWorkflowDefinitionListR
|
||||||
) where
|
) where
|
||||||
|
|
||||||
import Import
|
import Import
|
||||||
@ -35,6 +35,7 @@ queryWorkflowCount = to $ \(view queryWorkflowDefinition -> workflowDefinition)
|
|||||||
type WorkflowDefinitionData = DBRow
|
type WorkflowDefinitionData = DBRow
|
||||||
( Entity WorkflowDefinition
|
( Entity WorkflowDefinition
|
||||||
, Maybe (Entity WorkflowDefinitionDescription)
|
, Maybe (Entity WorkflowDefinitionDescription)
|
||||||
|
, Maybe (Entity WorkflowDefinitionInstanceDescription)
|
||||||
, Int64, Int64
|
, Int64, Int64
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -44,14 +45,16 @@ resultDefinition = _dbrOutput . _1
|
|||||||
resultDescription :: Traversal' WorkflowDefinitionData (Entity WorkflowDefinitionDescription)
|
resultDescription :: Traversal' WorkflowDefinitionData (Entity WorkflowDefinitionDescription)
|
||||||
resultDescription = _dbrOutput . _2 . _Just
|
resultDescription = _dbrOutput . _2 . _Just
|
||||||
|
|
||||||
|
resultInstanceDescription :: Traversal' WorkflowDefinitionData (Entity WorkflowDefinitionInstanceDescription)
|
||||||
|
resultInstanceDescription = _dbrOutput . _3 . _Just
|
||||||
|
|
||||||
resultWorkflowInstanceCount, resultWorkflowCount :: Lens' WorkflowDefinitionData Int64
|
resultWorkflowInstanceCount, resultWorkflowCount :: Lens' WorkflowDefinitionData Int64
|
||||||
resultWorkflowInstanceCount = _dbrOutput . _3
|
resultWorkflowInstanceCount = _dbrOutput . _4
|
||||||
resultWorkflowCount = _dbrOutput . _4
|
resultWorkflowCount = _dbrOutput . _5
|
||||||
|
|
||||||
|
|
||||||
getAdminWorkflowDefinitionListR, postAdminWorkflowDefinitionListR :: Handler Html
|
getAdminWorkflowDefinitionListR :: Handler Html
|
||||||
getAdminWorkflowDefinitionListR = postAdminWorkflowDefinitionListR
|
getAdminWorkflowDefinitionListR = do
|
||||||
postAdminWorkflowDefinitionListR = do
|
|
||||||
definitionsTable <- runDB $
|
definitionsTable <- runDB $
|
||||||
let
|
let
|
||||||
workflowDefinitionsDBTable = DBTable{..}
|
workflowDefinitionsDBTable = DBTable{..}
|
||||||
@ -70,17 +73,24 @@ postAdminWorkflowDefinitionListR = do
|
|||||||
return $ workflowDefinitionDescription E.^. WorkflowDefinitionDescriptionLanguage
|
return $ workflowDefinitionDescription E.^. WorkflowDefinitionDescriptionLanguage
|
||||||
descLang <- traverse selectLanguage . nonEmpty $ E.unValue <$> descLangs
|
descLang <- traverse selectLanguage . nonEmpty $ E.unValue <$> descLangs
|
||||||
desc <- lift . fmap join . for descLang $ \descLang' -> getBy $ UniqueWorkflowDefinitionDescription wdId descLang'
|
desc <- lift . fmap join . for descLang $ \descLang' -> getBy $ UniqueWorkflowDefinitionDescription wdId descLang'
|
||||||
(wd, desc,,)
|
idescLangs <- lift . E.select . E.from $ \workflowDefinitionInstanceDescription -> do
|
||||||
|
E.where_ $ workflowDefinitionInstanceDescription E.^. WorkflowDefinitionInstanceDescriptionDefinition E.==. E.val wdId
|
||||||
|
return $ workflowDefinitionInstanceDescription E.^. WorkflowDefinitionInstanceDescriptionLanguage
|
||||||
|
idescLang <- traverse selectLanguage . nonEmpty $ E.unValue <$> idescLangs
|
||||||
|
idesc <- lift . fmap join . for idescLang $ \idescLang' -> getBy $ UniqueWorkflowDefinitionInstanceDescription wdId idescLang'
|
||||||
|
(wd, desc, idesc,,)
|
||||||
<$> view (_2 . _Value)
|
<$> view (_2 . _Value)
|
||||||
<*> view (_3 . _Value)
|
<*> view (_3 . _Value)
|
||||||
dbtColonnade :: Colonnade Sortable WorkflowDefinitionData _
|
dbtColonnade :: Colonnade Sortable WorkflowDefinitionData _
|
||||||
dbtColonnade = mconcat
|
dbtColonnade = mconcat
|
||||||
[ sortable (Just "name") (i18nCell MsgWorkflowDefinitionName) . anchorEdit $ views (resultDefinition . _entityVal . _workflowDefinitionName) i18n
|
[ sortable (Just "name") (i18nCell MsgWorkflowDefinitionName) . anchorEdit $ views (resultDefinition . _entityVal . _workflowDefinitionName) i18n
|
||||||
, sortable (Just "scope") (i18nCell MsgWorkflowDefinitionScope) $ views (resultDefinition . _entityVal . _workflowDefinitionScope) i18nCell
|
, sortable (Just "scope") (i18nCell MsgWorkflowDefinitionScope) $ views (resultDefinition . _entityVal . _workflowDefinitionScope) i18nCell
|
||||||
, sortable (Just "title") (i18nCell MsgWorkflowDefinitionDescriptionTitle) $ maybe mempty (anchorEdit . const . i18n) =<< preview (resultDescription . _entityVal . _workflowDefinitionDescriptionTitle)
|
, sortable (Just "title") (i18nCell MsgWorkflowDescriptionTitle) $ maybe mempty (anchorEdit . const . i18n) =<< preview (resultDescription . _entityVal . _workflowDefinitionDescriptionTitle)
|
||||||
, sortable (Just "instances") (i18nCell MsgWorkflowDefinitionInstanceCount) $ maybe mempty i18nCell . views resultWorkflowInstanceCount (assertM' (> 0))
|
, sortable (Just "instances") (i18nCell MsgWorkflowDefinitionInstanceCount) $ maybe mempty i18nCell . views resultWorkflowInstanceCount (assertM' (> 0))
|
||||||
, sortable (Just "workflows") (i18nCell MsgWorkflowDefinitionWorkflowCount) $ maybe mempty i18nCell . views resultWorkflowCount (assertM' (> 0))
|
, sortable (Just "workflows") (i18nCell MsgWorkflowDefinitionWorkflowCount) $ maybe mempty i18nCell . views resultWorkflowCount (assertM' (> 0))
|
||||||
, sortable (Just "description") (i18nCell MsgWorkflowDefinitionDescription) $ maybe mempty modalCell . preview (resultDescription . _entityVal . _workflowDefinitionDescriptionDescription . _Just)
|
, sortable (Just "description") (i18nCell MsgWorkflowDescription) $ maybe mempty modalCell . preview (resultDescription . _entityVal . _workflowDefinitionDescriptionDescription . _Just)
|
||||||
|
, sortable (Just "instance-title") (i18nCell MsgWorkflowInstanceDescriptionTitle) $ maybe mempty (anchorEdit . const . i18n) =<< preview (resultInstanceDescription . _entityVal . _workflowDefinitionInstanceDescriptionTitle)
|
||||||
|
, sortable (Just "instance-description") (i18nCell MsgWorkflowInstanceDescription) $ maybe mempty modalCell . preview (resultInstanceDescription . _entityVal . _workflowDefinitionInstanceDescriptionDescription . _Just)
|
||||||
, sortable Nothing (i18nCell MsgWorkflowDefinitionGraph) $ views (resultDefinition . _entityVal . _workflowDefinitionGraph) (modalCell . displayGraph)
|
, sortable Nothing (i18nCell MsgWorkflowDefinitionGraph) $ views (resultDefinition . _entityVal . _workflowDefinitionGraph) (modalCell . displayGraph)
|
||||||
]
|
]
|
||||||
where
|
where
|
||||||
@ -98,6 +108,8 @@ postAdminWorkflowDefinitionListR = do
|
|||||||
, singletonMap "scope" . SortColumn . views queryWorkflowDefinition $ E.orderByEnum . (E.^. WorkflowDefinitionScope)
|
, singletonMap "scope" . SortColumn . views queryWorkflowDefinition $ E.orderByEnum . (E.^. WorkflowDefinitionScope)
|
||||||
, singletonMap "title" . SortProjected . comparing . view $ resultDescription . _entityVal . _workflowDefinitionDescriptionTitle
|
, singletonMap "title" . SortProjected . comparing . view $ resultDescription . _entityVal . _workflowDefinitionDescriptionTitle
|
||||||
, singletonMap "description" . SortProjected . comparing . view $ resultDescription . _entityVal . _workflowDefinitionDescriptionDescription
|
, singletonMap "description" . SortProjected . comparing . view $ resultDescription . _entityVal . _workflowDefinitionDescriptionDescription
|
||||||
|
, singletonMap "instance-title" . SortProjected . comparing . view $ resultInstanceDescription . _entityVal . _workflowDefinitionInstanceDescriptionTitle
|
||||||
|
, singletonMap "instance-description" . SortProjected . comparing . view $ resultInstanceDescription . _entityVal . _workflowDefinitionInstanceDescriptionDescription
|
||||||
, singletonMap "instances" . SortColumn $ view queryWorkflowInstanceCount
|
, singletonMap "instances" . SortColumn $ view queryWorkflowInstanceCount
|
||||||
, singletonMap "workflows" . SortColumn $ view queryWorkflowCount
|
, singletonMap "workflows" . SortColumn $ view queryWorkflowCount
|
||||||
]
|
]
|
||||||
@ -105,11 +117,13 @@ postAdminWorkflowDefinitionListR = do
|
|||||||
[ singletonMap "name" . FilterColumn $ E.mkContainsFilter (E.^. WorkflowDefinitionName)
|
[ singletonMap "name" . FilterColumn $ E.mkContainsFilter (E.^. WorkflowDefinitionName)
|
||||||
, singletonMap "scope" . FilterColumn $ E.mkExactFilter (E.^. WorkflowDefinitionScope)
|
, singletonMap "scope" . FilterColumn $ E.mkExactFilter (E.^. WorkflowDefinitionScope)
|
||||||
, singletonMap "title" . FilterProjected $ \(ts :: Set Text) (view $ resultDescription . _entityVal . _workflowDefinitionDescriptionTitle -> t) -> oany ((flip isInfixOf `on` CI.foldCase) t) ts
|
, singletonMap "title" . FilterProjected $ \(ts :: Set Text) (view $ resultDescription . _entityVal . _workflowDefinitionDescriptionTitle -> t) -> oany ((flip isInfixOf `on` CI.foldCase) t) ts
|
||||||
|
, singletonMap "instance-title" . FilterProjected $ \(ts :: Set Text) (view $ resultInstanceDescription . _entityVal . _workflowDefinitionInstanceDescriptionTitle -> t) -> oany ((flip isInfixOf `on` CI.foldCase) t) ts
|
||||||
]
|
]
|
||||||
dbtFilterUI mPrev = mconcat
|
dbtFilterUI mPrev = mconcat
|
||||||
[ prismAForm (singletonFilter "name") mPrev $ aopt textField (fslI MsgWorkflowDefinitionName)
|
[ prismAForm (singletonFilter "name") mPrev $ aopt textField (fslI MsgWorkflowDefinitionName)
|
||||||
, prismAForm (singletonFilter "scope" . maybePrism _PathPiece) mPrev $ aopt (selectField' (Just $ SomeMessage MsgNoFilter) optionsFinite :: Field _ WorkflowInstanceScope') (fslI MsgWorkflowDefinitionScope)
|
, prismAForm (singletonFilter "scope" . maybePrism _PathPiece) mPrev $ aopt (selectField' (Just $ SomeMessage MsgNoFilter) optionsFinite :: Field _ WorkflowScope') (fslI MsgWorkflowDefinitionScope)
|
||||||
, prismAForm (singletonFilter "title") mPrev $ aopt textField (fslI MsgWorkflowDefinitionDescriptionTitle)
|
, prismAForm (singletonFilter "title") mPrev $ aopt textField (fslI MsgWorkflowDescriptionTitle)
|
||||||
|
, prismAForm (singletonFilter "instance-title") mPrev $ aopt textField (fslI MsgWorkflowInstanceDescriptionTitle)
|
||||||
]
|
]
|
||||||
dbtStyle = def { dbsFilterLayout = defaultDBSFilterLayout }
|
dbtStyle = def { dbsFilterLayout = defaultDBSFilterLayout }
|
||||||
dbtParams = def
|
dbtParams = def
|
||||||
|
|||||||
@ -6,8 +6,6 @@ import Import
|
|||||||
import Handler.Utils
|
import Handler.Utils
|
||||||
import Handler.Workflow.Definition.Form
|
import Handler.Workflow.Definition.Form
|
||||||
|
|
||||||
import Data.Map.Strict ((!))
|
|
||||||
|
|
||||||
|
|
||||||
getAdminWorkflowDefinitionNewR, postAdminWorkflowDefinitionNewR :: Handler Html
|
getAdminWorkflowDefinitionNewR, postAdminWorkflowDefinitionNewR :: Handler Html
|
||||||
getAdminWorkflowDefinitionNewR = postAdminWorkflowDefinitionNewR
|
getAdminWorkflowDefinitionNewR = postAdminWorkflowDefinitionNewR
|
||||||
@ -15,15 +13,14 @@ postAdminWorkflowDefinitionNewR = do
|
|||||||
(((_, newForm), newEncoding), act) <- runDB $ do
|
(((_, newForm), newEncoding), act) <- runDB $ do
|
||||||
form@((newRes, _), _) <- runFormPost $ workflowDefinitionForm Nothing
|
form@((newRes, _), _) <- runFormPost $ workflowDefinitionForm Nothing
|
||||||
|
|
||||||
act <- formResultMaybe newRes $ \WorkflowDefinitionForm{..} -> do
|
act <- formResultMaybe newRes $ \WorkflowDefinitionForm{ .. } -> do
|
||||||
wdfGraph' <- wdfGraph
|
wdfGraph' <- fromWorkflowGraphForm wdfGraph
|
||||||
& over (typesCustom @WorkflowChildren) (wdfFiles !)
|
|
||||||
& traverseOf (typesCustom @WorkflowChildren @(WorkflowGraph FileReference CryptoUUIDUser) @_ @CryptoUUIDUser) (fmap (view $ _SqlKey @User) . decrypt)
|
|
||||||
|
|
||||||
insRes <- insertUnique WorkflowDefinition
|
insRes <- insertUnique WorkflowDefinition
|
||||||
{ workflowDefinitionGraph = wdfGraph'
|
{ workflowDefinitionGraph = wdfGraph'
|
||||||
, workflowDefinitionScope = wdfScope
|
, workflowDefinitionScope = wdfScope
|
||||||
, workflowDefinitionName = wdfName
|
, workflowDefinitionName = wdfName
|
||||||
|
, workflowDefinitionInstanceCategory = wdfInstanceCategory
|
||||||
}
|
}
|
||||||
|
|
||||||
for_ insRes $ \wdId -> iforM_ wdfDescriptions $ \wddLang (wddTitle, wddDesc) ->
|
for_ insRes $ \wdId -> iforM_ wdfDescriptions $ \wddLang (wddTitle, wddDesc) ->
|
||||||
@ -33,6 +30,13 @@ postAdminWorkflowDefinitionNewR = do
|
|||||||
, workflowDefinitionDescriptionTitle = wddTitle
|
, workflowDefinitionDescriptionTitle = wddTitle
|
||||||
, workflowDefinitionDescriptionDescription = wddDesc
|
, workflowDefinitionDescriptionDescription = wddDesc
|
||||||
}
|
}
|
||||||
|
for_ insRes $ \wdId -> iforM_ wdfInstanceDescriptions $ \wddLang (wddTitle, wddDesc) ->
|
||||||
|
insert WorkflowDefinitionInstanceDescription
|
||||||
|
{ workflowDefinitionInstanceDescriptionDefinition = wdId
|
||||||
|
, workflowDefinitionInstanceDescriptionLanguage = wddLang
|
||||||
|
, workflowDefinitionInstanceDescriptionTitle = wddTitle
|
||||||
|
, workflowDefinitionInstanceDescriptionDescription = wddDesc
|
||||||
|
}
|
||||||
|
|
||||||
case insRes of
|
case insRes of
|
||||||
Just _ -> return . Just $ do
|
Just _ -> return . Just $ do
|
||||||
|
|||||||
9
src/Handler/Workflow/Instance.hs
Normal file
9
src/Handler/Workflow/Instance.hs
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
module Handler.Workflow.Instance
|
||||||
|
( module Handler.Workflow.Instance
|
||||||
|
) where
|
||||||
|
|
||||||
|
import Handler.Workflow.Instance.List as Handler.Workflow.Instance
|
||||||
|
import Handler.Workflow.Instance.New as Handler.Workflow.Instance
|
||||||
|
import Handler.Workflow.Instance.Edit as Handler.Workflow.Instance
|
||||||
|
import Handler.Workflow.Instance.Delete as Handler.Workflow.Instance
|
||||||
|
import Handler.Workflow.Instance.Initiate as Handler.Workflow.Instance
|
||||||
15
src/Handler/Workflow/Instance/Delete.hs
Normal file
15
src/Handler/Workflow/Instance/Delete.hs
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
module Handler.Workflow.Instance.Delete
|
||||||
|
( getGWIDeleteR, postGWIDeleteR
|
||||||
|
, workflowInstanceDeleteR
|
||||||
|
) where
|
||||||
|
|
||||||
|
import Import
|
||||||
|
|
||||||
|
|
||||||
|
getGWIDeleteR, postGWIDeleteR :: WorkflowInstanceName -> Handler Html
|
||||||
|
getGWIDeleteR = postGWIDeleteR
|
||||||
|
postGWIDeleteR win
|
||||||
|
= workflowInstanceDeleteR <=< runDB . getKeyBy404 $ UniqueWorkflowInstance win WSGlobal
|
||||||
|
|
||||||
|
workflowInstanceDeleteR :: WorkflowInstanceId -> Handler Html
|
||||||
|
workflowInstanceDeleteR = error "not implemented"
|
||||||
15
src/Handler/Workflow/Instance/Edit.hs
Normal file
15
src/Handler/Workflow/Instance/Edit.hs
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
module Handler.Workflow.Instance.Edit
|
||||||
|
( getGWIEditR, postGWIEditR
|
||||||
|
, workflowInstanceEditR
|
||||||
|
) where
|
||||||
|
|
||||||
|
import Import
|
||||||
|
|
||||||
|
|
||||||
|
getGWIEditR, postGWIEditR :: WorkflowInstanceName -> Handler Html
|
||||||
|
getGWIEditR = postGWIEditR
|
||||||
|
postGWIEditR win
|
||||||
|
= workflowInstanceEditR <=< runDB . getKeyBy404 $ UniqueWorkflowInstance win WSGlobal
|
||||||
|
|
||||||
|
workflowInstanceEditR :: WorkflowInstanceId -> Handler Html
|
||||||
|
workflowInstanceEditR = error "not implemented"
|
||||||
79
src/Handler/Workflow/Instance/Form.hs
Normal file
79
src/Handler/Workflow/Instance/Form.hs
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
module Handler.Workflow.Instance.Form
|
||||||
|
( WorkflowInstanceForm(..), FileIdent
|
||||||
|
, workflowInstanceForm
|
||||||
|
) where
|
||||||
|
|
||||||
|
import Import
|
||||||
|
|
||||||
|
import Handler.Utils
|
||||||
|
|
||||||
|
import Handler.Utils.Workflow.Form
|
||||||
|
|
||||||
|
import qualified Data.Map as Map
|
||||||
|
import qualified Data.Set as Set
|
||||||
|
|
||||||
|
|
||||||
|
workflowInstanceScopeForm :: Maybe WorkflowScope'
|
||||||
|
-> FieldSettings UniWorX
|
||||||
|
-> Maybe (WorkflowScope TermId SchoolId CourseId)
|
||||||
|
-> AForm Handler (WorkflowScope TermId SchoolId CourseId)
|
||||||
|
workflowInstanceScopeForm scopeRestr fs mPrev = multiActionA scopeOptions' fs $ classifyWorkflowScope <$> mPrev
|
||||||
|
where
|
||||||
|
scopeOptions' = maybe id (flip Map.restrictKeys . Set.singleton) scopeRestr scopeOptions
|
||||||
|
scopeOptions = Map.fromList
|
||||||
|
[ ( WSGlobal'
|
||||||
|
, pure WSGlobal
|
||||||
|
)
|
||||||
|
, ( WSTerm'
|
||||||
|
, WSTerm <$> apopt termField (fslI MsgTerm) (mPrev ^? _Just . _wisTerm)
|
||||||
|
)
|
||||||
|
, ( WSSchool'
|
||||||
|
, WSSchool <$> apopt schoolField (fslI MsgSchool) (mPrev ^? _Just . _wisSchool)
|
||||||
|
)
|
||||||
|
, ( WSTermSchool'
|
||||||
|
, WSTermSchool <$> apopt termField (fslI MsgTerm) (mPrev ^? _Just . _wisTerm)
|
||||||
|
<*> apopt schoolField (fslI MsgSchool) (mPrev ^? _Just . _wisSchool)
|
||||||
|
)
|
||||||
|
, ( WSCourse'
|
||||||
|
, WSCourse <$> apopt (selectField' Nothing courseOptions) (fslI MsgCourse) (mPrev ^? _Just . _wisCourse)
|
||||||
|
)
|
||||||
|
]
|
||||||
|
where courseOptions = fmap (fmap entityKey) . optionsPersistCryptoId [] [ Desc CourseTerm, Asc CourseSchool, Asc CourseName ] $ \Course{..} -> MsgCourseOption courseTerm courseSchool courseName
|
||||||
|
|
||||||
|
|
||||||
|
data WorkflowInstanceForm = WorkflowInstanceForm
|
||||||
|
{ wifScope :: WorkflowScope TermId SchoolId CourseId
|
||||||
|
, wifName :: WorkflowInstanceName
|
||||||
|
, wifCategory :: Maybe WorkflowInstanceCategory
|
||||||
|
, wifDescriptions :: Map Lang (Text, Maybe Html)
|
||||||
|
, wifGraph :: WorkflowGraphForm
|
||||||
|
} deriving (Generic, Typeable)
|
||||||
|
|
||||||
|
makeLenses_ ''WorkflowInstanceForm
|
||||||
|
|
||||||
|
workflowInstanceForm :: Maybe WorkflowDefinitionId
|
||||||
|
-> Maybe WorkflowInstanceForm
|
||||||
|
-> Html
|
||||||
|
-> MForm DB (FormResult WorkflowInstanceForm, Widget)
|
||||||
|
workflowInstanceForm forcedDefId template = renderWForm FormStandard $ do
|
||||||
|
defEnt <- for forcedDefId $ lift . lift . getJustEntity
|
||||||
|
defDescs <- for defEnt $ \(Entity dId _) -> do
|
||||||
|
descs <- lift . lift $ selectList [WorkflowDefinitionInstanceDescriptionDefinition ==. dId] []
|
||||||
|
return $ Map.fromList
|
||||||
|
[ (workflowDefinitionInstanceDescriptionLanguage, (workflowDefinitionInstanceDescriptionTitle, workflowDefinitionInstanceDescriptionDescription))
|
||||||
|
| Entity _ WorkflowDefinitionInstanceDescription{..} <- descs
|
||||||
|
]
|
||||||
|
defGraph <- for defEnt $ toWorkflowGraphForm . workflowDefinitionGraph . entityVal
|
||||||
|
|
||||||
|
wifScopeRes <- aFormToWForm . hoistAForm lift $ workflowInstanceScopeForm (workflowDefinitionScope . entityVal <$> defEnt) (fslI MsgWorkflowScope) (wifScope <$> template)
|
||||||
|
wifNameRes <- wreq ciField (fslI MsgWorkflowInstanceName) (fmap wifName template <|> fmap (workflowDefinitionName . entityVal) defEnt)
|
||||||
|
wifCategoryRes <- wopt ciField (fslI MsgWorkflowInstanceCategory) (fmap wifCategory template <|> fmap (workflowDefinitionInstanceCategory . entityVal) defEnt)
|
||||||
|
wifDescriptions <- aFormToWForm . hoistAForm lift $ workflowDescriptionsForm WorkflowDescriptionsFormDefinition (fmap wifDescriptions template <|> defDescs)
|
||||||
|
wifGraphRes <- aFormToWForm $ workflowGraphForm ((template ^? _Just . _wifGraph) <|> defGraph)
|
||||||
|
|
||||||
|
return $ WorkflowInstanceForm
|
||||||
|
<$> wifScopeRes
|
||||||
|
<*> wifNameRes
|
||||||
|
<*> wifCategoryRes
|
||||||
|
<*> wifDescriptions
|
||||||
|
<*> wifGraphRes
|
||||||
15
src/Handler/Workflow/Instance/Initiate.hs
Normal file
15
src/Handler/Workflow/Instance/Initiate.hs
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
module Handler.Workflow.Instance.Initiate
|
||||||
|
( getGWIInitiateR, postGWIInitiateR
|
||||||
|
, workflowInstanceInitiateR
|
||||||
|
) where
|
||||||
|
|
||||||
|
import Import
|
||||||
|
|
||||||
|
|
||||||
|
getGWIInitiateR, postGWIInitiateR :: WorkflowInstanceName -> Handler Html
|
||||||
|
getGWIInitiateR = postGWIInitiateR
|
||||||
|
postGWIInitiateR win
|
||||||
|
= workflowInstanceInitiateR <=< runDB . getKeyBy404 $ UniqueWorkflowInstance win WSGlobal
|
||||||
|
|
||||||
|
workflowInstanceInitiateR :: WorkflowInstanceId -> Handler Html
|
||||||
|
workflowInstanceInitiateR = error "not implemented"
|
||||||
125
src/Handler/Workflow/Instance/List.hs
Normal file
125
src/Handler/Workflow/Instance/List.hs
Normal file
@ -0,0 +1,125 @@
|
|||||||
|
{-# OPTIONS_GHC -fno-warn-redundant-constraints #-}
|
||||||
|
|
||||||
|
module Handler.Workflow.Instance.List
|
||||||
|
( getAdminWorkflowInstanceListR
|
||||||
|
, getGlobalWorkflowInstanceListR
|
||||||
|
, workflowInstanceListR
|
||||||
|
) where
|
||||||
|
|
||||||
|
import Import
|
||||||
|
|
||||||
|
import Handler.Utils
|
||||||
|
|
||||||
|
import qualified Database.Esqueleto as E
|
||||||
|
import qualified Database.Esqueleto.Utils as E
|
||||||
|
|
||||||
|
import qualified Data.CaseInsensitive as CI
|
||||||
|
|
||||||
|
|
||||||
|
type WorkflowInstanceTableExpr = E.SqlExpr (Entity WorkflowInstance)
|
||||||
|
|
||||||
|
queryWorkflowInstance :: Equality' WorkflowInstanceTableExpr (E.SqlExpr (Entity WorkflowInstance))
|
||||||
|
queryWorkflowInstance = id
|
||||||
|
|
||||||
|
queryWorkflowCount :: Getter WorkflowInstanceTableExpr (E.SqlExpr (E.Value Int64))
|
||||||
|
queryWorkflowCount = to $ \(view queryWorkflowInstance -> workflowInstance) ->
|
||||||
|
E.subSelectCount . E.from $ \workflow ->
|
||||||
|
E.where_ $ workflow E.^. WorkflowWorkflowInstance E.==. E.just (workflowInstance E.^. WorkflowInstanceId)
|
||||||
|
|
||||||
|
|
||||||
|
type WorkflowInstanceData = DBRow
|
||||||
|
( Entity WorkflowInstance
|
||||||
|
, Maybe (Entity WorkflowInstanceDescription)
|
||||||
|
, Int64
|
||||||
|
)
|
||||||
|
|
||||||
|
resultWorkflowInstance :: Lens' WorkflowInstanceData (Entity WorkflowInstance)
|
||||||
|
resultWorkflowInstance = _dbrOutput . _1
|
||||||
|
|
||||||
|
resultDescription :: Traversal' WorkflowInstanceData (Entity WorkflowInstanceDescription)
|
||||||
|
resultDescription = _dbrOutput . _2 . _Just
|
||||||
|
|
||||||
|
resultWorkflowCount :: Lens' WorkflowInstanceData Int64
|
||||||
|
resultWorkflowCount = _dbrOutput . _3
|
||||||
|
|
||||||
|
|
||||||
|
getAdminWorkflowInstanceListR :: Handler Html
|
||||||
|
getAdminWorkflowInstanceListR = do
|
||||||
|
instancesTable <- runDB $ do
|
||||||
|
scopeOptions <- do
|
||||||
|
scopes <- fmap (map E.unValue) . E.select . E.from $ \workflowInstance ->
|
||||||
|
return $ workflowInstance E.^. WorkflowInstanceScope
|
||||||
|
fmap mkOptionList . for scopes $ \scope -> do
|
||||||
|
eScope <- traverseOf _wisCourse (encrypt . (review _SqlKey :: SqlBackendKey -> CourseId)) scope :: DB (WorkflowScope TermIdentifier SchoolShorthand CryptoUUIDCourse)
|
||||||
|
wScope <- forOf _wisCourse scope $ fmap ((,,) <$> courseTerm <*> courseSchool <*> courseShorthand) . getJust . review _SqlKey
|
||||||
|
MsgRenderer mr <- getMsgRenderer
|
||||||
|
return Option
|
||||||
|
{ optionDisplay = mr wScope
|
||||||
|
, optionInternalValue = scope
|
||||||
|
, optionExternalValue = toPathPiece eScope
|
||||||
|
}
|
||||||
|
|
||||||
|
let workflowInstancesDBTable = DBTable{..}
|
||||||
|
where
|
||||||
|
dbtSQLQuery = runReaderT $ do
|
||||||
|
workflowInstance <- view queryWorkflowInstance
|
||||||
|
workflowCount <- view queryWorkflowCount
|
||||||
|
|
||||||
|
return (workflowInstance, workflowCount)
|
||||||
|
dbtRowKey = (E.^. WorkflowInstanceId)
|
||||||
|
dbtProj = runReaderT $ (asks . set _dbrOutput) <=< magnify _dbrOutput $ do
|
||||||
|
wi@(Entity wiId _) <- view _1
|
||||||
|
descLangs <- lift . E.select . E.from $ \workflowInstanceDescription -> do
|
||||||
|
E.where_ $ workflowInstanceDescription E.^. WorkflowInstanceDescriptionInstance E.==. E.val wiId
|
||||||
|
return $ workflowInstanceDescription E.^. WorkflowInstanceDescriptionLanguage
|
||||||
|
descLang <- traverse selectLanguage . nonEmpty $ E.unValue <$> descLangs
|
||||||
|
desc <- lift . fmap join . for descLang $ \descLang' -> getBy $ UniqueWorkflowInstanceDescription wiId descLang'
|
||||||
|
(wi, desc,)
|
||||||
|
<$> view (_2 . _Value)
|
||||||
|
dbtColonnade :: Colonnade Sortable WorkflowInstanceData _
|
||||||
|
dbtColonnade = mconcat
|
||||||
|
[ sortable (Just "name") (i18nCell MsgWorkflowInstanceName) $ views (resultWorkflowInstance . _entityVal . _workflowInstanceName) i18nCell
|
||||||
|
, sortable (Just "scope") (i18nCell MsgWorkflowScope) . views (resultWorkflowInstance . _entityVal . _workflowInstanceScope) $ \scope ->
|
||||||
|
sqlCell . fmap i18n . forOf _wisCourse scope $ fmap ((,,) <$> courseTerm <*> courseSchool <*> courseShorthand) . getJust . review _SqlKey
|
||||||
|
, sortable (Just "title") (i18nCell MsgWorkflowInstanceDescriptionTitle) $ maybe mempty i18nCell . preview (resultDescription . _entityVal . _workflowInstanceDescriptionTitle)
|
||||||
|
, sortable (Just "workflows") (i18nCell MsgWorkflowInstanceWorkflowCount) $ maybe mempty i18nCell . views resultWorkflowCount (assertM' (> 0))
|
||||||
|
, sortable (Just "description") (i18nCell MsgWorkflowInstanceDescription) $ maybe mempty modalCell . preview (resultDescription . _entityVal . _workflowInstanceDescriptionDescription . _Just)
|
||||||
|
]
|
||||||
|
dbtSorting = mconcat
|
||||||
|
[ singletonMap "name" . SortColumn $ views queryWorkflowInstance (E.^. WorkflowInstanceName)
|
||||||
|
, singletonMap "scope" . SortColumn $ views queryWorkflowInstance (E.^. WorkflowInstanceScope)
|
||||||
|
, singletonMap "title" . SortProjected . comparing . view $ resultDescription . _entityVal . _workflowInstanceDescriptionTitle
|
||||||
|
, singletonMap "description" . SortProjected . comparing . view $ resultDescription . _entityVal . _workflowInstanceDescriptionDescription
|
||||||
|
, singletonMap "workflows" . SortColumn $ view queryWorkflowCount
|
||||||
|
]
|
||||||
|
dbtFilter = mconcat
|
||||||
|
[ singletonMap "name" . FilterColumn $ E.mkContainsFilter (E.^. WorkflowInstanceName)
|
||||||
|
, singletonMap "scope" . FilterColumn $ E.mkExactFilter (E.^. WorkflowInstanceScope)
|
||||||
|
, singletonMap "title" . FilterProjected $ \(ts :: Set Text) (view $ resultDescription . _entityVal . _workflowInstanceDescriptionTitle -> t) -> oany ((flip isInfixOf `on` CI.foldCase) t) ts
|
||||||
|
]
|
||||||
|
dbtFilterUI mPrev = mconcat
|
||||||
|
[ prismAForm (singletonFilter "name") mPrev $ aopt textField (fslI MsgWorkflowInstanceName)
|
||||||
|
, prismAForm (singletonFilter "scope" . maybePrism _PathPiece) mPrev $ aopt (selectField' (Just $ SomeMessage MsgNoFilter) $ return scopeOptions) (fslI MsgWorkflowScope)
|
||||||
|
, prismAForm (singletonFilter "title") mPrev $ aopt textField (fslI MsgWorkflowInstanceDescriptionTitle)
|
||||||
|
]
|
||||||
|
dbtStyle = def { dbsFilterLayout = defaultDBSFilterLayout }
|
||||||
|
dbtParams = def
|
||||||
|
dbtIdent :: Text
|
||||||
|
dbtIdent = "workflow-instances"
|
||||||
|
dbtCsvEncode = noCsvEncode
|
||||||
|
dbtCsvDecode = Nothing
|
||||||
|
workflowInstancesDBTableValidator = def
|
||||||
|
& defaultSorting [SortAscBy "scope", SortAscBy "name"]
|
||||||
|
in dbTableDB' workflowInstancesDBTableValidator workflowInstancesDBTable
|
||||||
|
|
||||||
|
siteLayoutMsg MsgWorkflowInstanceListTitle $ do
|
||||||
|
setTitleI MsgWorkflowInstanceListTitle
|
||||||
|
|
||||||
|
instancesTable
|
||||||
|
|
||||||
|
|
||||||
|
getGlobalWorkflowInstanceListR :: Handler Html
|
||||||
|
getGlobalWorkflowInstanceListR = workflowInstanceListR WSGlobal
|
||||||
|
|
||||||
|
workflowInstanceListR :: WorkflowScope TermId SchoolId CourseId -> Handler Html
|
||||||
|
workflowInstanceListR = error "not implemented"
|
||||||
76
src/Handler/Workflow/Instance/New.hs
Normal file
76
src/Handler/Workflow/Instance/New.hs
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
module Handler.Workflow.Instance.New
|
||||||
|
( getAdminWorkflowInstanceNewR, postAdminWorkflowInstanceNewR
|
||||||
|
, adminWorkflowInstanceNewR
|
||||||
|
, getGlobalWorkflowInstanceNewR, postGlobalWorkflowInstanceNewR
|
||||||
|
, workflowInstanceNewR
|
||||||
|
) where
|
||||||
|
|
||||||
|
import Import
|
||||||
|
import Handler.Utils
|
||||||
|
import Handler.Utils.Workflow.Form
|
||||||
|
|
||||||
|
import Handler.Workflow.Instance.Form
|
||||||
|
|
||||||
|
getAdminWorkflowInstanceNewR, postAdminWorkflowInstanceNewR :: Handler Html
|
||||||
|
getAdminWorkflowInstanceNewR = postAdminWorkflowInstanceNewR
|
||||||
|
postAdminWorkflowInstanceNewR = adminWorkflowInstanceNewR Nothing
|
||||||
|
|
||||||
|
adminWorkflowInstanceNewR :: Maybe WorkflowDefinitionId -> Handler Html
|
||||||
|
adminWorkflowInstanceNewR wdId = do
|
||||||
|
cRoute <- getCurrentRoute
|
||||||
|
(((_, instForm), instEncoding), act) <- runDB $ do
|
||||||
|
form@((instRes, _), _) <- runFormPost $ workflowInstanceForm wdId Nothing
|
||||||
|
|
||||||
|
act <- formResultMaybe instRes $ \WorkflowInstanceForm{..} -> do
|
||||||
|
wifGraph' <- fromWorkflowGraphForm wifGraph
|
||||||
|
let wifScope' = wifScope
|
||||||
|
& over _wisTerm unTermKey
|
||||||
|
& over _wisSchool unSchoolKey
|
||||||
|
& over _wisCourse (view _SqlKey)
|
||||||
|
instId <- insertUnique WorkflowInstance
|
||||||
|
{ workflowInstanceDefinition = wdId
|
||||||
|
, workflowInstanceGraph = wifGraph'
|
||||||
|
, workflowInstanceScope = wifScope'
|
||||||
|
, workflowInstanceName = wifName
|
||||||
|
, workflowInstanceCategory = wifCategory
|
||||||
|
}
|
||||||
|
|
||||||
|
for_ instId $ \instId' -> iforM_ wifDescriptions $ \widLang (widTitle, widDesc) ->
|
||||||
|
insert WorkflowInstanceDescription
|
||||||
|
{ workflowInstanceDescriptionInstance = instId'
|
||||||
|
, workflowInstanceDescriptionLanguage = widLang
|
||||||
|
, workflowInstanceDescriptionTitle = widTitle
|
||||||
|
, workflowInstanceDescriptionDescription = widDesc
|
||||||
|
}
|
||||||
|
|
||||||
|
return . Just $ case instId of
|
||||||
|
Nothing -> addMessageI Error MsgWorkflowInstanceCollision
|
||||||
|
Just _
|
||||||
|
| is _Just wdId -> do
|
||||||
|
addMessageI Success MsgWorkflowDefinitionInstantiated
|
||||||
|
redirect AdminWorkflowInstanceListR
|
||||||
|
| otherwise -> do
|
||||||
|
addMessageI Success MsgWorkflowInstanceCreated
|
||||||
|
redirect AdminWorkflowInstanceListR
|
||||||
|
|
||||||
|
return (form, act)
|
||||||
|
|
||||||
|
forM_ act id
|
||||||
|
|
||||||
|
let instWidget = wrapForm instForm def
|
||||||
|
{ formAction = SomeRoute <$> cRoute
|
||||||
|
, formEncoding = instEncoding
|
||||||
|
}
|
||||||
|
|
||||||
|
siteLayoutMsg MsgWorkflowDefinitionInstantiateTitle $ do
|
||||||
|
setTitleI MsgWorkflowDefinitionInstantiateTitle
|
||||||
|
|
||||||
|
instWidget
|
||||||
|
|
||||||
|
|
||||||
|
getGlobalWorkflowInstanceNewR, postGlobalWorkflowInstanceNewR :: Handler Html
|
||||||
|
getGlobalWorkflowInstanceNewR = postGlobalWorkflowInstanceNewR
|
||||||
|
postGlobalWorkflowInstanceNewR = workflowInstanceNewR WSGlobal
|
||||||
|
|
||||||
|
workflowInstanceNewR :: WorkflowScope TermId SchoolId CourseId -> Handler Html
|
||||||
|
workflowInstanceNewR = error "not implemented"
|
||||||
9
src/Handler/Workflow/Workflow.hs
Normal file
9
src/Handler/Workflow/Workflow.hs
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
module Handler.Workflow.Workflow
|
||||||
|
( module Handler.Workflow.Workflow
|
||||||
|
) where
|
||||||
|
|
||||||
|
import Handler.Workflow.Workflow.List as Handler.Workflow.Workflow
|
||||||
|
import Handler.Workflow.Workflow.Workflow as Handler.Workflow.Workflow
|
||||||
|
import Handler.Workflow.Workflow.Edit as Handler.Workflow.Workflow
|
||||||
|
import Handler.Workflow.Workflow.Delete as Handler.Workflow.Workflow
|
||||||
|
import Handler.Workflow.Workflow.New as Handler.Workflow.Workflow
|
||||||
17
src/Handler/Workflow/Workflow/Delete.hs
Normal file
17
src/Handler/Workflow/Workflow/Delete.hs
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
module Handler.Workflow.Workflow.Delete
|
||||||
|
( getGWWDeleteR, postGWWDeleteR
|
||||||
|
, workflowDeleteR
|
||||||
|
) where
|
||||||
|
|
||||||
|
import Import
|
||||||
|
|
||||||
|
import Handler.Utils.Workflow.Workflow
|
||||||
|
|
||||||
|
|
||||||
|
getGWWDeleteR, postGWWDeleteR :: CryptoFileNameWorkflowWorkflow -> Handler Html
|
||||||
|
getGWWDeleteR = postGWWDeleteR
|
||||||
|
postGWWDeleteR cID
|
||||||
|
= workflowDeleteR <=< runDB . maybeT notFound $ ensureScope WSGlobal cID
|
||||||
|
|
||||||
|
workflowDeleteR :: WorkflowWorkflowId -> Handler Html
|
||||||
|
workflowDeleteR = error "not implemented"
|
||||||
17
src/Handler/Workflow/Workflow/Edit.hs
Normal file
17
src/Handler/Workflow/Workflow/Edit.hs
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
module Handler.Workflow.Workflow.Edit
|
||||||
|
( getGWWEditR, postGWWEditR
|
||||||
|
, workflowEditR
|
||||||
|
) where
|
||||||
|
|
||||||
|
import Import
|
||||||
|
|
||||||
|
import Handler.Utils.Workflow.Workflow
|
||||||
|
|
||||||
|
|
||||||
|
getGWWEditR, postGWWEditR :: CryptoFileNameWorkflowWorkflow -> Handler Html
|
||||||
|
getGWWEditR = postGWWEditR
|
||||||
|
postGWWEditR cID
|
||||||
|
= workflowEditR <=< runDB . maybeT notFound $ ensureScope WSGlobal cID
|
||||||
|
|
||||||
|
workflowEditR :: WorkflowWorkflowId -> Handler Html
|
||||||
|
workflowEditR = error "not implemented"
|
||||||
28
src/Handler/Workflow/Workflow/List.hs
Normal file
28
src/Handler/Workflow/Workflow/List.hs
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
module Handler.Workflow.Workflow.List
|
||||||
|
( getGlobalWorkflowWorkflowListR
|
||||||
|
, workflowWorkflowListR
|
||||||
|
, getGWIWorkflowsR
|
||||||
|
, workflowInstanceWorkflowsR
|
||||||
|
, getAdminWorkflowWorkflowListR
|
||||||
|
) where
|
||||||
|
|
||||||
|
import Import
|
||||||
|
|
||||||
|
|
||||||
|
getGlobalWorkflowWorkflowListR :: Handler Html
|
||||||
|
getGlobalWorkflowWorkflowListR = workflowWorkflowListR WSGlobal
|
||||||
|
|
||||||
|
workflowWorkflowListR :: WorkflowScope TermId SchoolId CourseId -> Handler Html
|
||||||
|
workflowWorkflowListR = error "not implemented"
|
||||||
|
|
||||||
|
|
||||||
|
getGWIWorkflowsR :: WorkflowInstanceName -> Handler Html
|
||||||
|
getGWIWorkflowsR win
|
||||||
|
= workflowInstanceWorkflowsR <=< runDB . getKeyBy404 $ UniqueWorkflowInstance win WSGlobal
|
||||||
|
|
||||||
|
workflowInstanceWorkflowsR :: WorkflowInstanceId -> Handler Html
|
||||||
|
workflowInstanceWorkflowsR = error "not implemented"
|
||||||
|
|
||||||
|
|
||||||
|
getAdminWorkflowWorkflowListR :: Handler Html
|
||||||
|
getAdminWorkflowWorkflowListR = error "not implemented"
|
||||||
10
src/Handler/Workflow/Workflow/New.hs
Normal file
10
src/Handler/Workflow/Workflow/New.hs
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
module Handler.Workflow.Workflow.New
|
||||||
|
( getAdminWorkflowWorkflowNewR, postAdminWorkflowWorkflowNewR
|
||||||
|
) where
|
||||||
|
|
||||||
|
import Import
|
||||||
|
|
||||||
|
|
||||||
|
getAdminWorkflowWorkflowNewR, postAdminWorkflowWorkflowNewR :: Handler Html
|
||||||
|
getAdminWorkflowWorkflowNewR = postAdminWorkflowWorkflowNewR
|
||||||
|
postAdminWorkflowWorkflowNewR = error "not implemented"
|
||||||
19
src/Handler/Workflow/Workflow/Workflow.hs
Normal file
19
src/Handler/Workflow/Workflow/Workflow.hs
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
module Handler.Workflow.Workflow.Workflow
|
||||||
|
( getGWWWorkflowR, postGWWWorkflowR
|
||||||
|
, workflowR
|
||||||
|
) where
|
||||||
|
|
||||||
|
import Import
|
||||||
|
|
||||||
|
import Handler.Utils.Workflow.Workflow
|
||||||
|
|
||||||
|
|
||||||
|
getGWWWorkflowR, postGWWWorkflowR :: CryptoFileNameWorkflowWorkflow -> Handler Html
|
||||||
|
getGWWWorkflowR = postGWWWorkflowR
|
||||||
|
postGWWWorkflowR cID = do
|
||||||
|
wId <- runDB . maybeT notFound $ ensureScope WSGlobal cID
|
||||||
|
|
||||||
|
workflowR wId
|
||||||
|
|
||||||
|
workflowR :: WorkflowWorkflowId -> Handler Html
|
||||||
|
workflowR = error "not implemented"
|
||||||
@ -55,6 +55,7 @@ data AuthTag -- sortiert nach gewünschter Reihenfolge auf /authpreds, d.h. Prä
|
|||||||
| AuthSystemExamOffice
|
| AuthSystemExamOffice
|
||||||
| AuthEvaluation
|
| AuthEvaluation
|
||||||
| AuthAllocationAdmin
|
| AuthAllocationAdmin
|
||||||
|
| AuthWorkflow
|
||||||
| AuthAllocationRegistered
|
| AuthAllocationRegistered
|
||||||
| AuthCourseRegistered
|
| AuthCourseRegistered
|
||||||
| AuthTutorialRegistered
|
| AuthTutorialRegistered
|
||||||
|
|||||||
@ -3,8 +3,8 @@
|
|||||||
module Model.Types.Workflow
|
module Model.Types.Workflow
|
||||||
( WorkflowGraph(..)
|
( WorkflowGraph(..)
|
||||||
, WorkflowGraphNodeLabel
|
, WorkflowGraphNodeLabel
|
||||||
, WorkflowInstanceScope(..)
|
, WorkflowScope(..)
|
||||||
, WorkflowInstanceScope'(..)
|
, WorkflowScope'(..), classifyWorkflowScope
|
||||||
, WorkflowState
|
, WorkflowState
|
||||||
, WorkflowAction(..)
|
, WorkflowAction(..)
|
||||||
, WorkflowChildren
|
, WorkflowChildren
|
||||||
@ -48,13 +48,11 @@ data WorkflowGraph fileid userid = WorkflowGraph
|
|||||||
newtype WorkflowGraphNodeLabel = WorkflowGraphNodeLabel { unWorkflowGraphNodeLabel :: CI Text }
|
newtype WorkflowGraphNodeLabel = WorkflowGraphNodeLabel { unWorkflowGraphNodeLabel :: CI Text }
|
||||||
deriving stock (Eq, Ord, Read, Show, Data, Generic, Typeable)
|
deriving stock (Eq, Ord, Read, Show, Data, Generic, Typeable)
|
||||||
deriving newtype (IsString, ToJSON, ToJSONKey, FromJSON, FromJSONKey, PersistField, PersistFieldSql)
|
deriving newtype (IsString, ToJSON, ToJSONKey, FromJSON, FromJSONKey, PersistField, PersistFieldSql)
|
||||||
newtype WorkflowGraphEdgeLabel = WorkflowGraphEdgeLabel { unWorkflowGraphEdgeLabel :: CI Text }
|
|
||||||
deriving stock (Eq, Ord, Read, Show, Data, Generic, Typeable)
|
|
||||||
deriving newtype (IsString, ToJSON, ToJSONKey, FromJSON, FromJSONKey, PersistField, PersistFieldSql)
|
|
||||||
|
|
||||||
data WorkflowGraphNode fileid userid = WGN
|
data WorkflowGraphNode fileid userid = WGN
|
||||||
{ wgnDisplayLabel :: Maybe I18nText
|
{ wgnDisplayLabel :: Maybe I18nText
|
||||||
, wgnInitial, wgnFinished :: Bool
|
, wgnInitial :: Maybe (NonNull (Set (WorkflowRole userid)))
|
||||||
|
, wgnFinal :: Bool
|
||||||
, wgnViewers :: Set (WorkflowRole userid)
|
, wgnViewers :: Set (WorkflowRole userid)
|
||||||
, wgnEdges :: Map WorkflowGraphEdgeLabel (WorkflowGraphEdge fileid userid)
|
, wgnEdges :: Map WorkflowGraphEdgeLabel (WorkflowGraphEdge fileid userid)
|
||||||
}
|
}
|
||||||
@ -63,6 +61,10 @@ data WorkflowGraphNode fileid userid = WGN
|
|||||||
|
|
||||||
----- WORKFLOW GRAPH: EDGES -----
|
----- WORKFLOW GRAPH: EDGES -----
|
||||||
|
|
||||||
|
newtype WorkflowGraphEdgeLabel = WorkflowGraphEdgeLabel { unWorkflowGraphEdgeLabel :: CI Text }
|
||||||
|
deriving stock (Eq, Ord, Read, Show, Data, Generic, Typeable)
|
||||||
|
deriving newtype (IsString, ToJSON, ToJSONKey, FromJSON, FromJSONKey, PersistField, PersistFieldSql)
|
||||||
|
|
||||||
data WorkflowGraphEdge fileid userid
|
data WorkflowGraphEdge fileid userid
|
||||||
= WorkflowGraphEdgeManual
|
= WorkflowGraphEdgeManual
|
||||||
{ wgeTarget :: WorkflowGraphNodeLabel
|
{ wgeTarget :: WorkflowGraphNodeLabel
|
||||||
@ -122,7 +124,7 @@ data WorkflowPayloadField fileid userid (payload :: Type) where
|
|||||||
WorkflowPayloadFieldBool :: { wpfbLabel :: I18nText
|
WorkflowPayloadFieldBool :: { wpfbLabel :: I18nText
|
||||||
, wpfbTooltip :: Maybe I18nHtml
|
, wpfbTooltip :: Maybe I18nHtml
|
||||||
, wpfbDefault :: Maybe Bool
|
, wpfbDefault :: Maybe Bool
|
||||||
, wpfbOptional :: Maybe Text -- ^ Optional if `Just`; encodes label of `Nothing`-Option
|
, wpfbOptional :: Maybe I18nText -- ^ Optional if `Just`; encodes label of `Nothing`-Option
|
||||||
} -> WorkflowPayloadField fileid userid Bool
|
} -> WorkflowPayloadField fileid userid Bool
|
||||||
WorkflowPayloadFieldFile :: { wpffLabel :: I18nText
|
WorkflowPayloadFieldFile :: { wpffLabel :: I18nText
|
||||||
, wpffTooltip :: Maybe I18nHtml
|
, wpffTooltip :: Maybe I18nHtml
|
||||||
@ -173,19 +175,26 @@ instance (Ord fileid, Ord userid, Typeable fileid, Typeable userid) => Ord (Work
|
|||||||
|
|
||||||
----- WORKFLOW INSTANCE -----
|
----- WORKFLOW INSTANCE -----
|
||||||
|
|
||||||
data WorkflowInstanceScope termid schoolid courseid
|
data WorkflowScope termid schoolid courseid
|
||||||
= WISGlobal
|
= WSGlobal
|
||||||
| WISTerm { wisTerm :: termid }
|
| WSTerm { wisTerm :: termid }
|
||||||
| WISSchool { wisSchool :: schoolid }
|
| WSSchool { wisSchool :: schoolid }
|
||||||
| WISTermSchool { wisTerm :: termid, wisSchool :: schoolid }
|
| WSTermSchool { wisTerm :: termid, wisSchool :: schoolid }
|
||||||
| WISCourse { wisCourse :: courseid }
|
| WSCourse { wisCourse :: courseid }
|
||||||
deriving (Eq, Ord, Show, Read, Data, Generic, Typeable)
|
deriving (Eq, Ord, Show, Read, Data, Generic, Typeable)
|
||||||
|
|
||||||
data WorkflowInstanceScope'
|
data WorkflowScope'
|
||||||
= WISGlobal' | WISTerm' | WISSchool' | WISTermSchool' | WISCourse'
|
= WSGlobal' | WSTerm' | WSSchool' | WSTermSchool' | WSCourse'
|
||||||
deriving (Eq, Ord, Enum, Bounded, Read, Show, Data, Generic, Typeable)
|
deriving (Eq, Ord, Enum, Bounded, Read, Show, Data, Generic, Typeable)
|
||||||
deriving anyclass (Universe, Finite)
|
deriving anyclass (Universe, Finite)
|
||||||
|
|
||||||
|
classifyWorkflowScope :: WorkflowScope termid schoolid courseid -> WorkflowScope'
|
||||||
|
classifyWorkflowScope = \case
|
||||||
|
WSGlobal -> WSGlobal'
|
||||||
|
WSTerm{} -> WSTerm'
|
||||||
|
WSSchool{} -> WSSchool'
|
||||||
|
WSTermSchool{} -> WSTermSchool'
|
||||||
|
WSCourse{} -> WSCourse'
|
||||||
|
|
||||||
----- WORKFLOW: PAYLOAD -----
|
----- WORKFLOW: PAYLOAD -----
|
||||||
|
|
||||||
@ -252,9 +261,10 @@ data WorkflowFieldPayload'' = WFPText' | WFPNumber' | WFPBool' | WFPFile' | WFPU
|
|||||||
|
|
||||||
----- PathPiece instances -----
|
----- PathPiece instances -----
|
||||||
|
|
||||||
nullaryPathPiece ''WorkflowInstanceScope' $ camelToPathPiece' 1 . fromJust . stripSuffix "'"
|
nullaryPathPiece ''WorkflowScope' $ camelToPathPiece' 1 . fromJust . stripSuffix "'"
|
||||||
nullaryPathPiece ''WorkflowFieldPayload'' $ camelToPathPiece' 1 . fromJust . stripSuffix "'"
|
nullaryPathPiece ''WorkflowFieldPayload'' $ camelToPathPiece' 1 . fromJust . stripSuffix "'"
|
||||||
|
|
||||||
|
derivePathPiece ''WorkflowScope (camelToPathPiece' 1) "--"
|
||||||
|
|
||||||
----- Generic traversal -----
|
----- Generic traversal -----
|
||||||
|
|
||||||
@ -482,9 +492,9 @@ instance ( FromJSON fileid, FromJSON userid
|
|||||||
deriveJSON defaultOptions
|
deriveJSON defaultOptions
|
||||||
{ constructorTagModifier = camelToPathPiece' 1
|
{ constructorTagModifier = camelToPathPiece' 1
|
||||||
, fieldLabelModifier = camelToPathPiece' 1
|
, fieldLabelModifier = camelToPathPiece' 1
|
||||||
} ''WorkflowInstanceScope
|
} ''WorkflowScope
|
||||||
|
|
||||||
pathPieceJSON ''WorkflowInstanceScope'
|
pathPieceJSON ''WorkflowScope'
|
||||||
|
|
||||||
deriveToJSON workflowActionAesonOptions ''WorkflowAction
|
deriveToJSON workflowActionAesonOptions ''WorkflowAction
|
||||||
|
|
||||||
@ -556,15 +566,15 @@ instance ( ToJSON fileid, ToJSON userid
|
|||||||
|
|
||||||
instance ( ToJSON termid, ToJSON schoolid, ToJSON courseid
|
instance ( ToJSON termid, ToJSON schoolid, ToJSON courseid
|
||||||
, FromJSON termid, FromJSON schoolid, FromJSON courseid
|
, FromJSON termid, FromJSON schoolid, FromJSON courseid
|
||||||
) => PersistField (WorkflowInstanceScope termid schoolid courseid) where
|
) => PersistField (WorkflowScope termid schoolid courseid) where
|
||||||
toPersistValue = toPersistValueJSON
|
toPersistValue = toPersistValueJSON
|
||||||
fromPersistValue = fromPersistValueJSON
|
fromPersistValue = fromPersistValueJSON
|
||||||
instance ( ToJSON termid, ToJSON schoolid, ToJSON courseid
|
instance ( ToJSON termid, ToJSON schoolid, ToJSON courseid
|
||||||
, FromJSON termid, FromJSON schoolid, FromJSON courseid
|
, FromJSON termid, FromJSON schoolid, FromJSON courseid
|
||||||
) => PersistFieldSql (WorkflowInstanceScope termid schoolid courseid) where
|
) => PersistFieldSql (WorkflowScope termid schoolid courseid) where
|
||||||
sqlType _ = sqlTypeJSON
|
sqlType _ = sqlTypeJSON
|
||||||
|
|
||||||
derivePersistFieldJSON ''WorkflowInstanceScope'
|
derivePersistFieldJSON ''WorkflowScope'
|
||||||
|
|
||||||
instance ( ToJSON fileid, ToJSON userid
|
instance ( ToJSON fileid, ToJSON userid
|
||||||
, FromJSON fileid, FromJSON userid
|
, FromJSON fileid, FromJSON userid
|
||||||
@ -579,3 +589,9 @@ instance ( ToJSON fileid, ToJSON userid
|
|||||||
, Typeable fileid, Typeable userid
|
, Typeable fileid, Typeable userid
|
||||||
) => PersistFieldSql (WorkflowState fileid userid) where
|
) => PersistFieldSql (WorkflowState fileid userid) where
|
||||||
sqlType _ = sqlTypeJSON
|
sqlType _ = sqlTypeJSON
|
||||||
|
|
||||||
|
|
||||||
|
----- Binary instances -----
|
||||||
|
|
||||||
|
instance Binary WorkflowScope'
|
||||||
|
instance (Binary termid, Binary schoolid, Binary courseid) => Binary (WorkflowScope termid schoolid courseid)
|
||||||
|
|||||||
@ -1110,6 +1110,12 @@ validateFormDB :: ( MonadHandler m
|
|||||||
-> (Markup -> MForm m (FormResult a, xml))
|
-> (Markup -> MForm m (FormResult a, xml))
|
||||||
validateFormDB (FormValidator valF) = validateForm . FormValidator $ hoist (liftHandler . runDB) valF
|
validateFormDB (FormValidator valF) = validateForm . FormValidator $ hoist (liftHandler . runDB) valF
|
||||||
|
|
||||||
|
validateAForm :: MonadHandler m
|
||||||
|
=> FormValidator a m ()
|
||||||
|
-> AForm m a
|
||||||
|
-> AForm m a
|
||||||
|
validateAForm valF form = formToAForm . over (mapped . _2) ($ []) $ validateForm valF (const $ aFormToForm form) mempty
|
||||||
|
|
||||||
tellValidationError :: ( MonadHandler m
|
tellValidationError :: ( MonadHandler m
|
||||||
, RenderMessage (HandlerSite m) msg
|
, RenderMessage (HandlerSite m) msg
|
||||||
)
|
)
|
||||||
|
|||||||
@ -90,6 +90,7 @@ data Icon
|
|||||||
| IconAllocationRegister | IconAllocationRegistrationEdit
|
| IconAllocationRegister | IconAllocationRegistrationEdit
|
||||||
| IconAllocationApplicationEdit
|
| IconAllocationApplicationEdit
|
||||||
| IconPersonalIdentification
|
| IconPersonalIdentification
|
||||||
|
| IconMenuWorkflows
|
||||||
deriving (Eq, Ord, Enum, Bounded, Show, Read, Generic, Typeable)
|
deriving (Eq, Ord, Enum, Bounded, Show, Read, Generic, Typeable)
|
||||||
|
|
||||||
iconText :: Icon -> Text
|
iconText :: Icon -> Text
|
||||||
@ -160,6 +161,7 @@ iconText = \case
|
|||||||
IconAllocationRegistrationEdit -> "pencil-alt"
|
IconAllocationRegistrationEdit -> "pencil-alt"
|
||||||
IconAllocationApplicationEdit -> "pencil-alt"
|
IconAllocationApplicationEdit -> "pencil-alt"
|
||||||
IconPersonalIdentification -> "id-card"
|
IconPersonalIdentification -> "id-card"
|
||||||
|
IconMenuWorkflows -> "folders"
|
||||||
|
|
||||||
instance Universe Icon
|
instance Universe Icon
|
||||||
instance Finite Icon
|
instance Finite Icon
|
||||||
|
|||||||
@ -247,6 +247,10 @@ makeLenses_ ''FallbackPersonalisedSheetFilesKey
|
|||||||
|
|
||||||
makeLenses_ ''WorkflowDefinition
|
makeLenses_ ''WorkflowDefinition
|
||||||
makeLenses_ ''WorkflowDefinitionDescription
|
makeLenses_ ''WorkflowDefinitionDescription
|
||||||
|
makeLenses_ ''WorkflowDefinitionInstanceDescription
|
||||||
|
makeLenses_ ''WorkflowScope
|
||||||
|
makeLenses_ ''WorkflowInstance
|
||||||
|
makeLenses_ ''WorkflowInstanceDescription
|
||||||
|
|
||||||
makeWrapped ''Textarea
|
makeWrapped ''Textarea
|
||||||
|
|
||||||
|
|||||||
@ -3,13 +3,13 @@ $newline never
|
|||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>
|
<th>
|
||||||
_{MsgWorkflowDefinitionDescriptionLanguage} #
|
_{MsgWorkflowDescriptionLanguage} #
|
||||||
<span .form-group__required-marker>
|
<span .form-group__required-marker>
|
||||||
<th>
|
<th>
|
||||||
_{MsgWorkflowDefinitionDescriptionTitle} #
|
_{MsgWorkflowDescriptionTitle} #
|
||||||
<span .form-group__required-marker>
|
<span .form-group__required-marker>
|
||||||
<th>
|
<th>
|
||||||
_{MsgWorkflowDefinitionDescription}
|
_{MsgWorkflowDescription}
|
||||||
<td>
|
<td>
|
||||||
<tbody>
|
<tbody>
|
||||||
$forall coord <- review liveCoords lLength
|
$forall coord <- review liveCoords lLength
|
||||||
Reference in New Issue
Block a user