feat(workflows): definition route stubs & i18n
This commit is contained in:
parent
712714c903
commit
e3b5b93c71
@ -1368,6 +1368,9 @@ MenuFaq: FAQ
|
|||||||
MenuSheetPersonalisedFiles: Personalisierte Dateien herunterladen
|
MenuSheetPersonalisedFiles: Personalisierte Dateien herunterladen
|
||||||
MenuCourseSheetPersonalisedFiles: Vorlage für personalisierte Übungsblatt-Dateien herunterladen
|
MenuCourseSheetPersonalisedFiles: Vorlage für personalisierte Übungsblatt-Dateien herunterladen
|
||||||
MenuAdminCrontab: Crontab
|
MenuAdminCrontab: Crontab
|
||||||
|
MenuAdminWorkflowDefinitionList: Workflows
|
||||||
|
MenuAdminWorkflowDefinitionNew: Neue Workflow-Definition
|
||||||
|
MenuAdminWorkflowDefinitionDelete: Löschen
|
||||||
|
|
||||||
BreadcrumbSubmissionFile: Datei
|
BreadcrumbSubmissionFile: Datei
|
||||||
BreadcrumbSubmissionUserInvite: Einladung zur Abgabe
|
BreadcrumbSubmissionUserInvite: Einladung zur Abgabe
|
||||||
@ -1443,6 +1446,10 @@ BreadcrumbFaq: FAQ
|
|||||||
BreadcrumbSheetPersonalisedFiles: Personalisierte Dateien herunterladen
|
BreadcrumbSheetPersonalisedFiles: Personalisierte Dateien herunterladen
|
||||||
BreadcrumbCourseSheetPersonalisedFiles: Vorlage für personalisierte Übungsblatt-Dateien herunterladen
|
BreadcrumbCourseSheetPersonalisedFiles: Vorlage für personalisierte Übungsblatt-Dateien herunterladen
|
||||||
BreadcrumbAdminCrontab: Crontab
|
BreadcrumbAdminCrontab: Crontab
|
||||||
|
BreadcrumbAdminWorkflowDefinitionList: Workflow-Definitionen
|
||||||
|
BreadcrumbAdminWorkflowDefinitionNew: Neue Workflow-Definition
|
||||||
|
BreadcrumbAdminWorkflowDefinitionEdit renderedWorkflowScope'@Text wfdn@WorkflowDefinitionName: #{wfdn} (#{renderedWorkflowScope'}
|
||||||
|
BreadcrumbAdminWorkflowDefinitionDelete: Löschen
|
||||||
|
|
||||||
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}
|
||||||
@ -2766,3 +2773,9 @@ CronMatchNone: Nie
|
|||||||
|
|
||||||
SystemExamOffice: Prüfungsverwaltung
|
SystemExamOffice: Prüfungsverwaltung
|
||||||
SystemFaculty: Fakultätsmitglied
|
SystemFaculty: Fakultätsmitglied
|
||||||
|
|
||||||
|
WorkflowInstanceScopeKindGlobal: Systemweit
|
||||||
|
WorkflowInstanceScopeKindTerm: Pro Semester
|
||||||
|
WorkflowInstanceScopeKindSchool: Pro Institut
|
||||||
|
WorkflowInstanceScopeKindTermSchool: Pro Institut & Semester
|
||||||
|
WorkflowInstanceScopeKindCourse: Pro Veranstaltung
|
||||||
|
|||||||
@ -1,19 +1,26 @@
|
|||||||
WorkflowDefinition
|
WorkflowDefinition
|
||||||
graph (WorkflowGraph SqlBackendKey SqlBackendKey)
|
graph (WorkflowGraph SqlBackendKey SqlBackendKey) -- FileId, UserId
|
||||||
scope WorkflowInstanceScope'
|
scope WorkflowInstanceScope'
|
||||||
name (CI Text)
|
name WorkflowDefinitionName
|
||||||
UniqueWorkflowDefinition name scope
|
UniqueWorkflowDefinition name scope
|
||||||
|
|
||||||
WorkflowInstance
|
WorkflowDefinitionDescription
|
||||||
definition WorkflowDefinitionId
|
definition WorkflowDefinitionId
|
||||||
|
language Lang
|
||||||
|
title Text
|
||||||
|
description Html Maybe
|
||||||
|
UniqueWorkflowDefinitionDescription definition language
|
||||||
|
|
||||||
|
WorkflowInstance
|
||||||
|
definition WorkflowDefinitionId Maybe
|
||||||
graph (WorkflowGraph SqlBackendKey SqlBackendKey) -- FileId, UserId
|
graph (WorkflowGraph SqlBackendKey SqlBackendKey) -- FileId, UserId
|
||||||
scope (WorkflowInstanceScope SqlBackendKey SqlBackendKey SqlBackendKey) -- TermId, SchoolId, CourseId
|
scope (WorkflowInstanceScope SqlBackendKey SqlBackendKey SqlBackendKey) -- TermId, SchoolId, CourseId
|
||||||
name (CI Text)
|
name WorkflowInstanceName
|
||||||
category (CI Text) Maybe
|
category WorkflowInstanceCategory Maybe
|
||||||
UniqueWorkflowInstance name scope
|
UniqueWorkflowInstance name scope
|
||||||
|
|
||||||
WorkflowWorkflow
|
WorkflowWorkflow
|
||||||
instance WorkflowInstanceId
|
instance WorkflowInstanceId Maybe
|
||||||
graph (WorkflowGraph SqlBackendKey SqlBackendKey) -- FileId, UserId
|
graph (WorkflowGraph SqlBackendKey SqlBackendKey) -- FileId, UserId
|
||||||
initUser UserId Maybe
|
initUser UserId Maybe
|
||||||
initTime UTCTime
|
initTime UTCTime
|
||||||
|
|||||||
5
routes
5
routes
@ -57,6 +57,11 @@
|
|||||||
/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/workflow-definitions/#WorkflowInstanceScope'/#WorkflowDefinitionName AdminWorkflowDefinitionR:
|
||||||
|
/edit AWDEditR GET POST
|
||||||
|
/delete AWDDeleteR GET POST
|
||||||
|
|
||||||
/health HealthR GET !free
|
/health HealthR GET !free
|
||||||
/instance InstanceR GET !free
|
/instance InstanceR GET !free
|
||||||
|
|||||||
@ -128,6 +128,7 @@ import Handler.Metrics
|
|||||||
import Handler.ExternalExam
|
import Handler.ExternalExam
|
||||||
import Handler.Participants
|
import Handler.Participants
|
||||||
import Handler.StorageKey
|
import Handler.StorageKey
|
||||||
|
import Handler.Workflow
|
||||||
|
|
||||||
|
|
||||||
-- This line actually creates our YesodDispatch instance. It is the second half
|
-- This line actually creates our YesodDispatch instance. It is the second half
|
||||||
|
|||||||
@ -73,6 +73,7 @@ decCryptoIDs [ ''SubmissionId
|
|||||||
, ''CourseEventId
|
, ''CourseEventId
|
||||||
, ''TutorialId
|
, ''TutorialId
|
||||||
, ''ExternalExamId
|
, ''ExternalExamId
|
||||||
|
, ''WorkflowWorkflowId
|
||||||
]
|
]
|
||||||
|
|
||||||
decCryptoIDKeySize
|
decCryptoIDKeySize
|
||||||
|
|||||||
@ -225,6 +225,7 @@ embedRenderMessage ''UniWorX ''Quoting ("Csv" <>)
|
|||||||
embedRenderMessage ''UniWorX ''FavouriteReason id
|
embedRenderMessage ''UniWorX ''FavouriteReason id
|
||||||
embedRenderMessage ''UniWorX ''Sex id
|
embedRenderMessage ''UniWorX ''Sex id
|
||||||
embedRenderMessage ''UniWorX ''ExamGradingMode id
|
embedRenderMessage ''UniWorX ''ExamGradingMode id
|
||||||
|
embedRenderMessage ''UniWorX ''WorkflowInstanceScope' $ ("WorkflowInstanceScopeKind" <>) . concat . drop 1 . splitCamel . fromMaybe (error "Expected WorkflowInstanceScope' to have '") . stripSuffix "'"
|
||||||
|
|
||||||
embedRenderMessage ''UniWorX ''AuthenticationMode id
|
embedRenderMessage ''UniWorX ''AuthenticationMode id
|
||||||
|
|
||||||
|
|||||||
@ -331,6 +331,14 @@ instance BearerAuthSite UniWorX => YesodBreadcrumbs UniWorX where
|
|||||||
EEStaffInviteR -> i18nCrumb MsgBreadcrumbExternalExamStaffInvite . Just $ EExamR tid ssh coursen examn EEShowR
|
EEStaffInviteR -> i18nCrumb MsgBreadcrumbExternalExamStaffInvite . Just $ EExamR tid ssh coursen examn EEShowR
|
||||||
EECorrectR -> i18nCrumb MsgBreadcrumbExternalExamCorrect . Just $ EExamR tid ssh coursen examn EEShowR
|
EECorrectR -> i18nCrumb MsgBreadcrumbExternalExamCorrect . Just $ EExamR tid ssh coursen examn EEShowR
|
||||||
|
|
||||||
|
breadcrumb AdminWorkflowDefinitionListR = i18nCrumb MsgBreadcrumbAdminWorkflowDefinitionList $ Just AdminR
|
||||||
|
breadcrumb AdminWorkflowDefinitionNewR = i18nCrumb MsgBreadcrumbAdminWorkflowDefinitionNew $ Just AdminWorkflowDefinitionListR
|
||||||
|
breadcrumb (AdminWorkflowDefinitionR wfdScope wfdName sRoute) = case sRoute of
|
||||||
|
AWDEditR -> do
|
||||||
|
MsgRenderer mr <- getMsgRenderer
|
||||||
|
i18nCrumb (MsgBreadcrumbAdminWorkflowDefinitionEdit (mr wfdScope) wfdName) $ Just AdminWorkflowDefinitionListR
|
||||||
|
AWDDeleteR -> i18nCrumb MsgBreadcrumbAdminWorkflowDefinitionDelete . Just $ AdminWorkflowDefinitionR wfdScope wfdName AWDEditR
|
||||||
|
|
||||||
|
|
||||||
data NavQuickView
|
data NavQuickView
|
||||||
= NavQuickViewFavourite
|
= NavQuickViewFavourite
|
||||||
@ -667,6 +675,14 @@ defaultLinks = fmap catMaybes . mapM runMaybeT $ -- Define the menu items of the
|
|||||||
, navQuick' = mempty
|
, navQuick' = mempty
|
||||||
, navForceActive = False
|
, navForceActive = False
|
||||||
}
|
}
|
||||||
|
, NavLink
|
||||||
|
{ navLabel = MsgMenuAdminWorkflowDefinitionList
|
||||||
|
, navRoute = AdminWorkflowDefinitionListR
|
||||||
|
, navAccess' = return True
|
||||||
|
, navType = NavTypeLink { navModal = False }
|
||||||
|
, navQuick' = mempty
|
||||||
|
, navForceActive = False
|
||||||
|
}
|
||||||
, NavLink
|
, NavLink
|
||||||
{ navLabel = MsgMenuAdminCrontab
|
{ navLabel = MsgMenuAdminCrontab
|
||||||
, navRoute = AdminCrontabR
|
, navRoute = AdminCrontabR
|
||||||
@ -2271,6 +2287,31 @@ pageActions ParticipantsListR = return
|
|||||||
, navChildren = []
|
, navChildren = []
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
pageActions AdminWorkflowDefinitionListR = return
|
||||||
|
[ NavPageActionPrimary
|
||||||
|
{ navLink = NavLink
|
||||||
|
{ navLabel = MsgMenuAdminWorkflowDefinitionNew
|
||||||
|
, navRoute = AdminWorkflowDefinitionNewR
|
||||||
|
, navAccess' = return True
|
||||||
|
, navType = NavTypeLink { navModal = False }
|
||||||
|
, navQuick' = mempty
|
||||||
|
, navForceActive = False
|
||||||
|
}
|
||||||
|
, navChildren = []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
pageActions (AdminWorkflowDefinitionR wds wdn AWDEditR) = return
|
||||||
|
[ NavPageActionSecondary
|
||||||
|
{ navLink = NavLink
|
||||||
|
{ navLabel = MsgMenuAdminWorkflowDefinitionDelete
|
||||||
|
, navRoute = AdminWorkflowDefinitionR wds wdn AWDDeleteR
|
||||||
|
, navAccess' = return True
|
||||||
|
, navType = NavTypeLink { navModal = True }
|
||||||
|
, navQuick' = mempty
|
||||||
|
, navForceActive = False
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
pageActions _ = return []
|
pageActions _ = return []
|
||||||
|
|
||||||
submissionList :: ( MonadIO m
|
submissionList :: ( MonadIO m
|
||||||
|
|||||||
@ -38,6 +38,7 @@ deriving instance Generic SchoolR
|
|||||||
deriving instance Generic ExamOfficeR
|
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 (Route UniWorX)
|
deriving instance Generic (Route UniWorX)
|
||||||
|
|
||||||
data RouteChildren
|
data RouteChildren
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
module Handler.Utils.I18n
|
module Handler.Utils.I18n
|
||||||
( i18nWidgetFile
|
( i18nWidgetFile
|
||||||
, i18nWidgetFilesAvailable, i18nWidgetFilesAvailable', i18nWidgetFiles
|
, i18nWidgetFilesAvailable, i18nWidgetFilesAvailable', i18nWidgetFiles
|
||||||
|
, i18nMessage
|
||||||
) where
|
) where
|
||||||
|
|
||||||
import Import.NoFoundation
|
import Import.NoFoundation
|
||||||
@ -77,3 +78,11 @@ i18nWidgetFiles basename = do
|
|||||||
, l <- unpack <$> NonEmpty.toList ls
|
, l <- unpack <$> NonEmpty.toList ls
|
||||||
] ++ [ clause [wildP, wildP] (normalB [e| error "selectLanguage returned an invalid translation" |]) [] ] -- Fallback mostly there so compiler does not complain about non-exhaustive pattern match
|
] ++ [ clause [wildP, wildP] (normalB [e| error "selectLanguage returned an invalid translation" |]) [] ] -- Fallback mostly there so compiler does not complain about non-exhaustive pattern match
|
||||||
] [e|imap (\kind ls -> selectLanguage ls >>= $(varE ws) kind) availableTranslations'|]
|
] [e|imap (\kind ls -> selectLanguage ls >>= $(varE ws) kind) availableTranslations'|]
|
||||||
|
|
||||||
|
|
||||||
|
i18nMessage :: ( MonadHandler m
|
||||||
|
, HandlerSite m ~ UniWorX
|
||||||
|
, RenderMessage UniWorX msg
|
||||||
|
)
|
||||||
|
=> msg -> m I18nText
|
||||||
|
i18nMessage = i18nMessageFor $ toList appLanguages
|
||||||
|
|||||||
5
src/Handler/Workflow.hs
Normal file
5
src/Handler/Workflow.hs
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
module Handler.Workflow
|
||||||
|
( module Handler.Workflow
|
||||||
|
) where
|
||||||
|
|
||||||
|
import Handler.Workflow.Definition as Handler.Workflow
|
||||||
8
src/Handler/Workflow/Definition.hs
Normal file
8
src/Handler/Workflow/Definition.hs
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
module Handler.Workflow.Definition
|
||||||
|
( module Handler.Workflow.Definition
|
||||||
|
) where
|
||||||
|
|
||||||
|
import Handler.Workflow.Definition.List 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.Delete as Handler.Workflow.Definition
|
||||||
9
src/Handler/Workflow/Definition/Delete.hs
Normal file
9
src/Handler/Workflow/Definition/Delete.hs
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
module Handler.Workflow.Definition.Delete
|
||||||
|
( getAWDDeleteR, postAWDDeleteR
|
||||||
|
) where
|
||||||
|
|
||||||
|
import Import
|
||||||
|
|
||||||
|
getAWDDeleteR, postAWDDeleteR :: WorkflowInstanceScope' -> WorkflowDefinitionName -> Handler Html
|
||||||
|
getAWDDeleteR = postAWDDeleteR
|
||||||
|
postAWDDeleteR = error "not implemented"
|
||||||
9
src/Handler/Workflow/Definition/Edit.hs
Normal file
9
src/Handler/Workflow/Definition/Edit.hs
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
module Handler.Workflow.Definition.Edit
|
||||||
|
( getAWDEditR, postAWDEditR
|
||||||
|
) where
|
||||||
|
|
||||||
|
import Import
|
||||||
|
|
||||||
|
getAWDEditR, postAWDEditR :: WorkflowInstanceScope' -> WorkflowDefinitionName -> Handler Html
|
||||||
|
getAWDEditR = postAWDEditR
|
||||||
|
postAWDEditR = error "not implemented"
|
||||||
22
src/Handler/Workflow/Definition/Form.hs
Normal file
22
src/Handler/Workflow/Definition/Form.hs
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
module Handler.Workflow.Definition.Form
|
||||||
|
( workflowDefinitionForm
|
||||||
|
) where
|
||||||
|
|
||||||
|
import Import
|
||||||
|
|
||||||
|
import Handler.Utils
|
||||||
|
|
||||||
|
data WorkflowDefinitionForm = WorkflowDefinitionForm
|
||||||
|
{ wdfScope :: WorkflowInstanceScope'
|
||||||
|
, wdfName :: CI Text
|
||||||
|
, wdfDescriptions :: Map Lang (Text, Maybe Html)
|
||||||
|
, wdfGraph :: WorkflowGraph CryptoUUIDFile CryptoUUIDUser
|
||||||
|
} deriving (Generic, Typeable)
|
||||||
|
|
||||||
|
workflowDefinitionForm :: Html -> MForm DB (FormResult WorkflowDefinitionForm, Widget)
|
||||||
|
workflowDefinitionForm = validateForm validateWorkflowDefinitionForm . renderAForm FormStandard $
|
||||||
|
error "not implemented"
|
||||||
|
|
||||||
|
|
||||||
|
validateWorkflowDefinitionForm :: FormValidator WorkflowDefinitionForm DB ()
|
||||||
|
validateWorkflowDefinitionForm = error "not implemented"
|
||||||
9
src/Handler/Workflow/Definition/List.hs
Normal file
9
src/Handler/Workflow/Definition/List.hs
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
module Handler.Workflow.Definition.List
|
||||||
|
( getAdminWorkflowDefinitionListR, postAdminWorkflowDefinitionListR
|
||||||
|
) where
|
||||||
|
|
||||||
|
import Import
|
||||||
|
|
||||||
|
getAdminWorkflowDefinitionListR, postAdminWorkflowDefinitionListR :: Handler Html
|
||||||
|
getAdminWorkflowDefinitionListR = postAdminWorkflowDefinitionListR
|
||||||
|
postAdminWorkflowDefinitionListR = error "not implemented"
|
||||||
9
src/Handler/Workflow/Definition/New.hs
Normal file
9
src/Handler/Workflow/Definition/New.hs
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
module Handler.Workflow.Definition.New
|
||||||
|
( getAdminWorkflowDefinitionNewR, postAdminWorkflowDefinitionNewR
|
||||||
|
) where
|
||||||
|
|
||||||
|
import Import
|
||||||
|
|
||||||
|
getAdminWorkflowDefinitionNewR, postAdminWorkflowDefinitionNewR :: Handler Html
|
||||||
|
getAdminWorkflowDefinitionNewR = postAdminWorkflowDefinitionNewR
|
||||||
|
postAdminWorkflowDefinitionNewR = error "not implemented"
|
||||||
@ -54,3 +54,9 @@ type InstanceId = UUID
|
|||||||
type ClusterId = UUID
|
type ClusterId = UUID
|
||||||
type TokenId = UUID
|
type TokenId = UUID
|
||||||
type TermCandidateIncidence = UUID
|
type TermCandidateIncidence = UUID
|
||||||
|
|
||||||
|
type SessionFileReference = Digest SHA3_256
|
||||||
|
|
||||||
|
type WorkflowDefinitionName = CI Text
|
||||||
|
type WorkflowInstanceName = CI Text
|
||||||
|
type WorkflowInstanceCategory = CI Text
|
||||||
|
|||||||
@ -69,7 +69,7 @@ predNFAesonOptions = defaultOptions
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
workflowGraphAesonOptions, workflowGraphEdgeAesonOptions, workflowGraphNodeAesonOptions, workflowActionAesonOptions :: Options
|
workflowGraphAesonOptions, workflowGraphEdgeAesonOptions, workflowGraphNodeAesonOptions, workflowActionAesonOptions, workflowPayloadViewAesonOptions :: Options
|
||||||
workflowGraphAesonOptions = defaultOptions
|
workflowGraphAesonOptions = defaultOptions
|
||||||
{ fieldLabelModifier = camelToPathPiece' 1
|
{ fieldLabelModifier = camelToPathPiece' 1
|
||||||
}
|
}
|
||||||
@ -84,3 +84,6 @@ workflowGraphNodeAesonOptions = defaultOptions
|
|||||||
workflowActionAesonOptions = defaultOptions
|
workflowActionAesonOptions = defaultOptions
|
||||||
{ fieldLabelModifier = camelToPathPiece' 1
|
{ fieldLabelModifier = camelToPathPiece' 1
|
||||||
}
|
}
|
||||||
|
workflowPayloadViewAesonOptions = defaultOptions
|
||||||
|
{ fieldLabelModifier = camelToPathPiece' 1
|
||||||
|
}
|
||||||
|
|||||||
@ -28,8 +28,8 @@ import Type.Reflection (eqTypeRep, typeOf, (:~~:)(..))
|
|||||||
----- WORKFLOW GRAPH -----
|
----- WORKFLOW GRAPH -----
|
||||||
|
|
||||||
data WorkflowGraph fileid userid = WorkflowGraph
|
data WorkflowGraph fileid userid = WorkflowGraph
|
||||||
{ wgNodes :: Map WorkflowGraphNodeLabel (WorkflowGraphNode fileid userid)
|
{ wgNodes :: Map WorkflowGraphNodeLabel (WorkflowGraphNode fileid userid)
|
||||||
, wgPayloadViewers :: Map WorkflowPayloadLabel (NonNull (Set (WorkflowRole userid)))
|
, wgPayloadView :: Map WorkflowPayloadLabel (WorkflowPayloadView userid)
|
||||||
}
|
}
|
||||||
deriving (Eq, Ord, Show, Generic, Typeable)
|
deriving (Eq, Ord, Show, Generic, Typeable)
|
||||||
|
|
||||||
@ -44,9 +44,8 @@ newtype WorkflowGraphEdgeLabel = WorkflowGraphEdgeLabel { unWorkflowGraphEdgeLab
|
|||||||
deriving newtype (IsString, ToJSON, ToJSONKey, FromJSON, FromJSONKey, PersistField, PersistFieldSql)
|
deriving newtype (IsString, ToJSON, ToJSONKey, FromJSON, FromJSONKey, PersistField, PersistFieldSql)
|
||||||
|
|
||||||
data WorkflowGraphNode fileid userid = WGN
|
data WorkflowGraphNode fileid userid = WGN
|
||||||
{ wgnDisplayLabel :: Maybe Text
|
{ wgnDisplayLabel :: Maybe I18nText
|
||||||
, wgnInitial :: Bool
|
, wgnInitial, wgnFinished :: Bool
|
||||||
, wgnFinished :: Bool
|
|
||||||
, wgnViewers :: Set (WorkflowRole userid)
|
, wgnViewers :: Set (WorkflowRole userid)
|
||||||
, wgnEdges :: Map WorkflowGraphEdgeLabel (WorkflowGraphEdge fileid userid)
|
, wgnEdges :: Map WorkflowGraphEdgeLabel (WorkflowGraphEdge fileid userid)
|
||||||
}
|
}
|
||||||
@ -85,39 +84,44 @@ data WorkflowRole userid
|
|||||||
|
|
||||||
----- WORKFLOW GRAPH: PAYLOAD SPECIFICATION -----
|
----- WORKFLOW GRAPH: PAYLOAD SPECIFICATION -----
|
||||||
|
|
||||||
|
data WorkflowPayloadView userid = WorkflowPayloadView
|
||||||
|
{ wpvViewers :: NonNull (Set (WorkflowRole userid))
|
||||||
|
, wpvDisplayLabel :: I18nText
|
||||||
|
} deriving (Eq, Ord, Read, Show, Data, Generic, Typeable)
|
||||||
|
|
||||||
data WorkflowPayloadSpec fileid userid = forall payload. Typeable payload => WorkflowPayloadSpec (WorkflowPayloadField fileid userid payload)
|
data WorkflowPayloadSpec fileid userid = forall payload. Typeable payload => WorkflowPayloadSpec (WorkflowPayloadField fileid userid payload)
|
||||||
deriving (Typeable)
|
deriving (Typeable)
|
||||||
|
|
||||||
deriving instance (Show fileid, Show userid) => Show (WorkflowPayloadSpec fileid userid)
|
deriving instance (Show fileid, Show userid) => Show (WorkflowPayloadSpec fileid userid)
|
||||||
|
|
||||||
data WorkflowPayloadField fileid userid (payload :: Type) where
|
data WorkflowPayloadField fileid userid (payload :: Type) where
|
||||||
WorkflowPayloadFieldText :: { wpftLabel :: Text
|
WorkflowPayloadFieldText :: { wpftLabel :: I18nText
|
||||||
, wpftPlaceholder :: Maybe Text
|
, wpftPlaceholder :: Maybe I18nText
|
||||||
, wpftTooltip :: Maybe Text
|
, wpftTooltip :: Maybe I18nHtml
|
||||||
, wpftDefault :: Maybe Text
|
, wpftDefault :: Maybe Text
|
||||||
, wpftOptional :: Bool
|
, wpftOptional :: Bool
|
||||||
} -> WorkflowPayloadField fileid userid Text
|
} -> WorkflowPayloadField fileid userid Text
|
||||||
WorkflowPayloadFieldNumber :: { wpfnLabel :: Text
|
WorkflowPayloadFieldNumber :: { wpfnLabel :: I18nText
|
||||||
, wpfnPlaceholder :: Maybe Text
|
, wpfnPlaceholder :: Maybe I18nText
|
||||||
, wpfnTooltip :: Maybe Text
|
, wpfnTooltip :: Maybe I18nHtml
|
||||||
, wpfnDefault
|
, wpfnDefault
|
||||||
, wpfnMin
|
, wpfnMin
|
||||||
, wpfnMax
|
, wpfnMax
|
||||||
, wpfnStep :: Maybe Scientific
|
, wpfnStep :: Maybe Scientific
|
||||||
, wpfnOptional :: Bool
|
, wpfnOptional :: Bool
|
||||||
} -> WorkflowPayloadField fileid userid Scientific
|
} -> WorkflowPayloadField fileid userid Scientific
|
||||||
WorkflowPayloadFieldBool :: { wpfbLabel :: Text
|
WorkflowPayloadFieldBool :: { wpfbLabel :: I18nText
|
||||||
, wpfbTooltip :: Maybe Text
|
, wpfbTooltip :: Maybe I18nHtml
|
||||||
, wpfbDefault :: Maybe Bool
|
, wpfbDefault :: Maybe Bool
|
||||||
, wpfbOptional :: Maybe Text -- ^ Optional if `Just`; encodes label of `Nothing`-Option
|
, wpfbOptional :: Maybe Text -- ^ Optional if `Just`; encodes label of `Nothing`-Option
|
||||||
} -> WorkflowPayloadField fileid userid Bool
|
} -> WorkflowPayloadField fileid userid Bool
|
||||||
WorkflowPayloadFieldFile :: { wpffLabel :: Text
|
WorkflowPayloadFieldFile :: { wpffLabel :: I18nText
|
||||||
, wpffTooltip :: Maybe Text
|
, wpffTooltip :: Maybe I18nHtml
|
||||||
, wpffDefault :: Maybe fileid
|
, wpffDefault :: Maybe fileid
|
||||||
, wpffOptional :: Bool
|
, wpffOptional :: Bool
|
||||||
} -> WorkflowPayloadField fileid userid FileInfo
|
} -> WorkflowPayloadField fileid userid FileInfo
|
||||||
WorkflowPayloadFieldUser :: { wpfuLabel :: Text
|
WorkflowPayloadFieldUser :: { wpfuLabel :: I18nText
|
||||||
, wpfuTooltip :: Maybe Text
|
, wpfuTooltip :: Maybe I18nHtml
|
||||||
, wpfuDefault :: Maybe userid
|
, wpfuDefault :: Maybe userid
|
||||||
, wpfuOptional :: Bool
|
, wpfuOptional :: Bool
|
||||||
} -> WorkflowPayloadField fileid userid userid
|
} -> WorkflowPayloadField fileid userid userid
|
||||||
@ -164,11 +168,12 @@ data WorkflowInstanceScope termid schoolid courseid
|
|||||||
= WISGlobal
|
= WISGlobal
|
||||||
| WISTerm { wisTerm :: termid }
|
| WISTerm { wisTerm :: termid }
|
||||||
| WISSchool { wisSchool :: schoolid }
|
| WISSchool { wisSchool :: schoolid }
|
||||||
|
| WISTermSchool { wisTerm :: termid, wisSchool :: schoolid }
|
||||||
| WISCourse { wisCourse :: courseid }
|
| WISCourse { wisCourse :: courseid }
|
||||||
deriving (Eq, Ord, Show, Read, Data, Generic, Typeable)
|
deriving (Eq, Ord, Show, Read, Data, Generic, Typeable)
|
||||||
|
|
||||||
data WorkflowInstanceScope'
|
data WorkflowInstanceScope'
|
||||||
= WISGlobal' | WISTerm' | WISSchool' | WISCourse'
|
= WISGlobal' | WISTerm' | WISSchool' | WISTermSchool' | WISCourse'
|
||||||
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)
|
||||||
|
|
||||||
@ -236,6 +241,12 @@ data WorkflowFieldPayload'' = WFPText' | WFPNumber' | WFPBool' | WFPFile' | WFPU
|
|||||||
deriving anyclass (Universe, Finite)
|
deriving anyclass (Universe, Finite)
|
||||||
|
|
||||||
|
|
||||||
|
----- PathPiece instances -----
|
||||||
|
|
||||||
|
nullaryPathPiece ''WorkflowInstanceScope' $ camelToPathPiece' 1
|
||||||
|
nullaryPathPiece ''WorkflowFieldPayload'' $ camelToPathPiece' 1 . fromJust . stripSuffix "'"
|
||||||
|
|
||||||
|
|
||||||
----- ToJSON / FromJSON instances -----
|
----- ToJSON / FromJSON instances -----
|
||||||
|
|
||||||
omitNothing :: [JSON.Pair] -> [JSON.Pair]
|
omitNothing :: [JSON.Pair] -> [JSON.Pair]
|
||||||
@ -246,6 +257,11 @@ deriveJSON defaultOptions
|
|||||||
, constructorTagModifier = camelToPathPiece' 2
|
, constructorTagModifier = camelToPathPiece' 2
|
||||||
} ''WorkflowRole
|
} ''WorkflowRole
|
||||||
|
|
||||||
|
deriveToJSON workflowPayloadViewAesonOptions ''WorkflowPayloadView
|
||||||
|
|
||||||
|
instance (FromJSON userid, Ord userid) => FromJSON (WorkflowPayloadView userid) where
|
||||||
|
parseJSON = genericParseJSON workflowPayloadViewAesonOptions
|
||||||
|
|
||||||
instance (ToJSON fileid, ToJSON userid) => ToJSON (WorkflowGraph fileid userid) where
|
instance (ToJSON fileid, ToJSON userid) => ToJSON (WorkflowGraph fileid userid) where
|
||||||
toJSON = genericToJSON workflowGraphAesonOptions
|
toJSON = genericToJSON workflowGraphAesonOptions
|
||||||
instance ( FromJSON fileid, FromJSON userid
|
instance ( FromJSON fileid, FromJSON userid
|
||||||
@ -367,9 +383,7 @@ deriveJSON defaultOptions
|
|||||||
, fieldLabelModifier = camelToPathPiece' 1
|
, fieldLabelModifier = camelToPathPiece' 1
|
||||||
} ''WorkflowInstanceScope
|
} ''WorkflowInstanceScope
|
||||||
|
|
||||||
deriveJSON defaultOptions
|
pathPieceJSON ''WorkflowInstanceScope'
|
||||||
{ constructorTagModifier = camelToPathPiece' 1 . fromJust . stripSuffix "'"
|
|
||||||
} ''WorkflowInstanceScope'
|
|
||||||
|
|
||||||
deriveToJSON workflowActionAesonOptions ''WorkflowAction
|
deriveToJSON workflowActionAesonOptions ''WorkflowAction
|
||||||
|
|
||||||
|
|||||||
@ -31,6 +31,7 @@ import Utils.Cookies as Utils
|
|||||||
import Utils.Cookies.Registered as Utils
|
import Utils.Cookies.Registered as Utils
|
||||||
import Utils.Session as Utils
|
import Utils.Session as Utils
|
||||||
import Utils.Csv as Utils
|
import Utils.Csv as Utils
|
||||||
|
import Utils.I18n as Utils
|
||||||
|
|
||||||
import Text.Blaze (Markup, ToMarkup)
|
import Text.Blaze (Markup, ToMarkup)
|
||||||
|
|
||||||
|
|||||||
81
src/Utils/I18n.hs
Normal file
81
src/Utils/I18n.hs
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
{-# LANGUAGE UndecidableInstances #-}
|
||||||
|
|
||||||
|
module Utils.I18n
|
||||||
|
( I18n(..)
|
||||||
|
, I18nText, I18nHtml
|
||||||
|
, renderMessageI18n
|
||||||
|
, i18nMessageFor
|
||||||
|
, Element
|
||||||
|
) where
|
||||||
|
|
||||||
|
import ClassyPrelude.Yesod hiding (derivePersistFieldJSON)
|
||||||
|
|
||||||
|
import qualified Data.Aeson as JSON
|
||||||
|
import qualified Data.HashMap.Lazy as HashMap
|
||||||
|
|
||||||
|
import qualified Data.Map as Map
|
||||||
|
|
||||||
|
import Model.Types.TH.JSON
|
||||||
|
|
||||||
|
import Data.Data (Data)
|
||||||
|
|
||||||
|
|
||||||
|
data I18n a = I18n
|
||||||
|
{ i18nFallback :: a
|
||||||
|
, i18nTranslations :: Map Lang a
|
||||||
|
} deriving (Eq, Ord, Read, Show, Functor, Foldable, Traversable, Data, Generic, Typeable)
|
||||||
|
deriving anyclass (MonoFunctor, MonoFoldable, MonoTraversable)
|
||||||
|
type instance Element (I18n a) = a
|
||||||
|
|
||||||
|
type I18nText = I18n Text
|
||||||
|
type I18nHtml = I18n Html
|
||||||
|
|
||||||
|
|
||||||
|
instance MonoPointed (I18n a) where
|
||||||
|
opoint = flip I18n Map.empty
|
||||||
|
|
||||||
|
instance IsString a => IsString (I18n a) where
|
||||||
|
fromString = opoint . fromString
|
||||||
|
|
||||||
|
instance ToJSON a => ToJSON (I18n a) where
|
||||||
|
toJSON I18n{..}
|
||||||
|
| Map.null i18nTranslations
|
||||||
|
, fallbackUnambiguous
|
||||||
|
= toJSON i18nFallback
|
||||||
|
| Map.null i18nTranslations
|
||||||
|
= JSON.object [ "fallback" JSON..= i18nFallback ]
|
||||||
|
| otherwise
|
||||||
|
= JSON.object [ "fallback" JSON..= i18nFallback
|
||||||
|
, "translations" JSON..= i18nTranslations
|
||||||
|
]
|
||||||
|
where
|
||||||
|
fallbackUnambiguous = case toJSON i18nFallback of
|
||||||
|
JSON.Object hm -> not $ HashMap.member "fallback" hm
|
||||||
|
_other -> True
|
||||||
|
|
||||||
|
instance FromJSON a => FromJSON (I18n a) where
|
||||||
|
parseJSON (JSON.Object o)
|
||||||
|
| HashMap.member "fallback" o = do
|
||||||
|
i18nFallback <- o JSON..: "fallback"
|
||||||
|
i18nTranslations <- o JSON..:? "translations" JSON..!= Map.empty
|
||||||
|
return I18n{..}
|
||||||
|
parseJSON val = do
|
||||||
|
i18nFallback <- JSON.parseJSON val
|
||||||
|
let i18nTranslations = Map.empty
|
||||||
|
return I18n{..}
|
||||||
|
|
||||||
|
derivePersistFieldJSON ''I18n
|
||||||
|
|
||||||
|
|
||||||
|
renderMessageI18n :: RenderMessage site msg
|
||||||
|
=> [Lang] -> site -> msg -> I18nText
|
||||||
|
renderMessageI18n ls app msg = I18n
|
||||||
|
{ i18nFallback = renderMessage app ls msg
|
||||||
|
, i18nTranslations = Map.fromList . flip map ls $ \l -> (l, ) $ renderMessage app (l : filter (/= l) ls) msg
|
||||||
|
}
|
||||||
|
|
||||||
|
i18nMessageFor :: ( MonadHandler m
|
||||||
|
, RenderMessage (HandlerSite m) msg
|
||||||
|
)
|
||||||
|
=> [Lang] -> msg -> m I18nText
|
||||||
|
i18nMessageFor ls msg = getsYesod $ flip (renderMessageI18n ls) msg
|
||||||
Reference in New Issue
Block a user