Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions cmd/mxcli/lsp_completions_gen.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 3 additions & 3 deletions docs/01-project/MDL_QUICK_REFERENCE.md
Original file line number Diff line number Diff line change
Expand Up @@ -276,7 +276,7 @@ Nested folders use `/` separator: `'Parent/Child/Grandchild'`. Missing folders a
| Drop workflow | `DROP WORKFLOW Module.Name;` | |

**Workflow Activity Types:**
- `USER TASK <name> '<caption>' [PAGE Mod.Page] [TARGETING MICROFLOW Mod.MF] [OUTCOMES '<out>' { } ...];`
- `USER TASK <name> '<caption>' [PAGE Mod.Page] [TARGETING [USERS|GROUPS] MICROFLOW Mod.MF] [TARGETING [USERS|GROUPS] XPATH '<expr>'] [OUTCOMES '<out>' { } ...];`
- `CALL MICROFLOW Mod.MF [COMMENT '<text>'] [OUTCOMES '<out>' { } ...];`
- `CALL WORKFLOW Mod.WF [COMMENT '<text>'];`
- `DECISION ['<caption>'] OUTCOMES '<out>' { } ...;`
Expand Down Expand Up @@ -312,8 +312,8 @@ Modify an existing workflow's properties, activities, outcomes, paths, condition
| Set parameter | `SET PARAMETER $Var: Module.Entity` | Workflow context parameter |
| Set activity page | `SET ACTIVITY name PAGE Module.Page` | Change user task page |
| Set activity description | `SET ACTIVITY name DESCRIPTION 'text'` | Activity description |
| Set activity targeting | `SET ACTIVITY name TARGETING MICROFLOW Module.MF` | Target user assignment |
| Set activity XPath | `SET ACTIVITY name TARGETING XPATH '[expr]'` | XPath targeting |
| Set activity targeting | `SET ACTIVITY name TARGETING [USERS\|GROUPS] MICROFLOW Module.MF` | Target user/group assignment |
| Set activity XPath | `SET ACTIVITY name TARGETING [USERS\|GROUPS] XPATH '[expr]'` | XPath targeting |
| Set activity due date | `SET ACTIVITY name DUE DATE 'expr'` | Activity-level due date |
| Insert activity | `INSERT AFTER name CALL MICROFLOW Module.MF` | Insert after named activity |
| Drop activity | `DROP ACTIVITY name` | Remove activity by name |
Expand Down
108 changes: 108 additions & 0 deletions mdl-examples/doctype-tests/workflow-user-targeting.mdl
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
-- ============================================================================
-- Workflow User Targeting — all targeting variants
-- ============================================================================
--
-- Covers USER TASK and MULTI USER TASK with:
-- TARGETING USERS MICROFLOW (user targeting via microflow)
-- TARGETING USERS XPATH (user targeting via XPath)
-- TARGETING GROUPS MICROFLOW (group targeting via microflow)
-- TARGETING GROUPS XPATH (group targeting via XPath)
-- TARGETING MICROFLOW (legacy short form, still valid)
-- TARGETING XPATH (legacy short form, still valid)
--
-- ============================================================================

-- Prerequisites

CREATE PERSISTENT ENTITY WFTarget.Request (
Status: String(200)
);

-- Targeting microflows: (System.Workflow, context) -> List of System.User
CREATE MICROFLOW WFTarget.ACT_GetUsers (
$Workflow: System.Workflow,
$Context: WFTarget.Request
)
RETURNS List of System.User AS $Users
BEGIN
@position(200,200)
RETRIEVE $Users FROM System.User;
@position(400,200) RETURN $Users;
END;
/

CREATE MICROFLOW WFTarget.ACT_GetGroups (
$Workflow: System.Workflow,
$Context: WFTarget.Request
)
RETURNS List of System.UserGroup AS $Groups
BEGIN
@position(200,200)
RETRIEVE $Groups FROM System.UserGroup;
@position(400,200) RETURN $Groups;
END;
/

CREATE PAGE WFTarget.TaskPage (
Title: 'Task Page',
Layout: Atlas_Core.Atlas_Default,
Params: { $WorkflowUserTask: System.WorkflowUserTask }
) {
LAYOUTGRID g1 {
ROW r1 {
COLUMN c1 (DesktopWidth: 12) {
DYNAMICTEXT txt1 (Content: 'Task', RenderMode: H2)
}
}
}
}

-- Workflow with all targeting variants

CREATE WORKFLOW WFTarget.TargetingDemo
PARAMETER $WorkflowContext: WFTarget.Request
BEGIN
-- TARGETING USERS MICROFLOW (explicit user targeting)
USER TASK ByUserMF 'User microflow targeting'
PAGE WFTarget.TaskPage
TARGETING USERS MICROFLOW WFTarget.ACT_GetUsers
OUTCOMES 'Done' { };

-- TARGETING USERS XPATH (explicit user xpath targeting)
USER TASK ByUserXP 'User xpath targeting'
PAGE WFTarget.TaskPage
TARGETING USERS XPATH '[Name = ''admin'']'
OUTCOMES 'Done' { };

-- TARGETING GROUPS MICROFLOW (group microflow targeting)
USER TASK ByGroupMF 'Group microflow targeting'
PAGE WFTarget.TaskPage
TARGETING GROUPS MICROFLOW WFTarget.ACT_GetGroups
OUTCOMES 'Done' { };

-- TARGETING GROUPS XPATH (group xpath targeting)
USER TASK ByGroupXP 'Group xpath targeting'
PAGE WFTarget.TaskPage
TARGETING GROUPS XPATH '[Name = ''Administrators'']'
OUTCOMES 'Done' { };

-- TARGETING MICROFLOW (legacy short form)
USER TASK LegacyMF 'Legacy microflow targeting'
PAGE WFTarget.TaskPage
TARGETING MICROFLOW WFTarget.ACT_GetUsers
OUTCOMES 'Done' { };

-- TARGETING XPATH (legacy short form)
USER TASK LegacyXP 'Legacy xpath targeting'
PAGE WFTarget.TaskPage
TARGETING XPATH '[Name = ''admin'']'
OUTCOMES 'Done' { };

-- MULTI USER TASK with group targeting
MULTI USER TASK MultiByGroup 'Multi group targeting'
PAGE WFTarget.TaskPage
TARGETING GROUPS MICROFLOW WFTarget.ACT_GetGroups
OUTCOMES 'Done' { };

END WORKFLOW;
/
7 changes: 4 additions & 3 deletions mdl/ast/ast_workflow.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,10 +56,11 @@ type WorkflowUserTaskNode struct {
func (n *WorkflowUserTaskNode) workflowActivityNode() {}

// WorkflowTargetingNode represents user targeting strategy.
// Kind: "microflow", "xpath", "group_microflow", "group_xpath", or ""
type WorkflowTargetingNode struct {
Kind string // "microflow", "xpath", or ""
Microflow QualifiedName // for microflow targeting
XPath string // for xpath targeting
Kind string // "microflow", "xpath", "group_microflow", "group_xpath", or ""
Microflow QualifiedName // for microflow targeting (user or group)
XPath string // for xpath targeting (user or group)
}

// WorkflowUserTaskOutcomeNode represents an outcome of a user task.
Expand Down
12 changes: 10 additions & 2 deletions mdl/executor/cmd_workflows.go
Original file line number Diff line number Diff line change
Expand Up @@ -425,11 +425,19 @@ func formatUserTask(a *workflows.UserTask, indent string) []string {
switch us := a.UserSource.(type) {
case *workflows.MicroflowBasedUserSource:
if us.Microflow != "" {
lines = append(lines, fmt.Sprintf("%s TARGETING MICROFLOW %s", indent, us.Microflow))
lines = append(lines, fmt.Sprintf("%s TARGETING USERS MICROFLOW %s", indent, us.Microflow))
}
case *workflows.XPathBasedUserSource:
if us.XPath != "" {
lines = append(lines, fmt.Sprintf("%s TARGETING XPATH '%s'", indent, us.XPath))
lines = append(lines, fmt.Sprintf("%s TARGETING USERS XPATH '%s'", indent, us.XPath))
}
case *workflows.MicroflowGroupSource:
if us.Microflow != "" {
lines = append(lines, fmt.Sprintf("%s TARGETING GROUPS MICROFLOW %s", indent, us.Microflow))
}
case *workflows.XPathGroupSource:
if us.XPath != "" {
lines = append(lines, fmt.Sprintf("%s TARGETING GROUPS XPATH '%s'", indent, us.XPath))
}
}
}
Expand Down
34 changes: 23 additions & 11 deletions mdl/executor/cmd_workflows_write.go
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,14 @@ func buildUserTask(n *ast.WorkflowUserTaskNode) *workflows.UserTask {
task.UserSource = &workflows.XPathBasedUserSource{
XPath: n.Targeting.XPath,
}
case "group_microflow":
task.UserSource = &workflows.MicroflowGroupSource{
Microflow: n.Targeting.Microflow.Module + "." + n.Targeting.Microflow.Name,
}
case "group_xpath":
task.UserSource = &workflows.XPathGroupSource{
XPath: n.Targeting.XPath,
}
}

// Outcomes
Expand Down Expand Up @@ -666,11 +674,25 @@ func (e *Executor) autoBindActivitiesInFlow(activities []workflows.WorkflowActiv
}

// autoBindCallMicroflow resolves microflow parameters and auto-generates ParameterMappings.
// Also ensures a default VoidConditionOutcome exists (required by Mendix runtime — CE6686).
func (e *Executor) autoBindCallMicroflow(task *workflows.CallMicroflowTask) {
// Sanitize name
task.Name = sanitizeActivityName(task.Name)

// Skip if already has parameter mappings
// Auto-generate Default outcome if no outcomes specified.
// This must run regardless of whether parameter mappings exist,
// because the Mendix runtime requires a VoidConditionOutcome on
// every CallMicroflowTask (CE6686: "outcomes do not match").
if len(task.Outcomes) == 0 {
outcome := &workflows.VoidConditionOutcome{
Flow: &workflows.Flow{},
}
outcome.BaseElement.ID = model.ID(mpr.GenerateID())
outcome.Flow.BaseElement.ID = model.ID(mpr.GenerateID())
task.Outcomes = append(task.Outcomes, outcome)
}

// Skip parameter auto-binding if already has explicit mappings
if len(task.ParameterMappings) > 0 {
return
}
Expand Down Expand Up @@ -704,16 +726,6 @@ func (e *Executor) autoBindCallMicroflow(task *workflows.CallMicroflowTask) {
mapping.BaseElement.ID = model.ID(mpr.GenerateID())
task.ParameterMappings = append(task.ParameterMappings, mapping)
}

// Auto-generate Default outcome if no outcomes specified
if len(task.Outcomes) == 0 {
outcome := &workflows.VoidConditionOutcome{
Flow: &workflows.Flow{},
}
outcome.BaseElement.ID = model.ID(mpr.GenerateID())
outcome.Flow.BaseElement.ID = model.ID(mpr.GenerateID())
task.Outcomes = append(task.Outcomes, outcome)
}
break
}
}
Expand Down
1 change: 1 addition & 0 deletions mdl/grammar/MDLLexer.g4
Original file line number Diff line number Diff line change
Expand Up @@ -652,6 +652,7 @@ ACTIVITY: A C T I V I T Y;
CONDITION: C O N D I T I O N;
OFF: O F F;
USERS: U S E R S;
GROUPS: G R O U P S;

// Data transformer tokens
DATA: D A T A;
Expand Down
10 changes: 5 additions & 5 deletions mdl/grammar/MDLParser.g4
Original file line number Diff line number Diff line change
Expand Up @@ -2718,17 +2718,17 @@ workflowActivityStmt
workflowUserTaskStmt
: USER TASK IDENTIFIER STRING_LITERAL
(PAGE qualifiedName)?
(TARGETING MICROFLOW qualifiedName)?
(TARGETING XPATH STRING_LITERAL)?
(TARGETING (USERS | GROUPS)? MICROFLOW qualifiedName)?
(TARGETING (USERS | GROUPS)? XPATH STRING_LITERAL)?
(ENTITY qualifiedName)?
(DUE DATE_TYPE STRING_LITERAL)?
(DESCRIPTION STRING_LITERAL)?
(OUTCOMES workflowUserTaskOutcome+)?
(BOUNDARY EVENT workflowBoundaryEventClause+)?
| MULTI USER TASK IDENTIFIER STRING_LITERAL
(PAGE qualifiedName)?
(TARGETING MICROFLOW qualifiedName)?
(TARGETING XPATH STRING_LITERAL)?
(TARGETING (USERS | GROUPS)? MICROFLOW qualifiedName)?
(TARGETING (USERS | GROUPS)? XPATH STRING_LITERAL)?
(ENTITY qualifiedName)?
(DUE DATE_TYPE STRING_LITERAL)?
(DESCRIPTION STRING_LITERAL)?
Expand Down Expand Up @@ -3748,7 +3748,7 @@ keyword

// Workflow
| ABORT | ACTIVITY | ANNOTATION | BOUNDARY | BY | COMPLETE_TASK
| CONDITION | DATE | DECISION | DUE | INTERRUPTING | JUMP
| CONDITION | DATE | DECISION | DUE | GROUPS | INTERRUPTING | JUMP
| LOCK | MULTI | NODE | NON | NOTIFICATION | NOTIFY
| OPEN | OUTCOME | OUTCOMES | OVERVIEW | PARALLEL | PAUSE
| REASON | RESTART | RETRY | SPLIT | TARGETING | TASK | TIMER
Expand Down
5 changes: 4 additions & 1 deletion mdl/grammar/parser/MDLLexer.interp

Large diffs are not rendered by default.

Loading
Loading