diff --git a/routers/web/projects/workflows.go b/routers/web/projects/workflows.go index 2dc09105d7..7e133df9d4 100644 --- a/routers/web/projects/workflows.go +++ b/routers/web/projects/workflows.go @@ -4,6 +4,7 @@ package projects import ( + stdCtx "context" "fmt" "io" "net/http" @@ -24,23 +25,40 @@ var ( ) // convertFormToFilters converts form filters to WorkflowFilter objects -func convertFormToFilters(formFilters map[string]any) []project_model.WorkflowFilter { +func convertFormToFilters(ctx stdCtx.Context, project *project_model.Project, formFilters map[string]any) []project_model.WorkflowFilter { filters := make([]project_model.WorkflowFilter, 0) for key, value := range formFilters { switch key { - case "labels": + case string(project_model.WorkflowFilterTypeLabels): // Handle labels array if labelInterfaces, ok := value.([]any); ok && len(labelInterfaces) > 0 { for _, labelInterface := range labelInterfaces { if label, ok := labelInterface.(string); ok && label != "" { - filters = append(filters, project_model.WorkflowFilter{ - Type: project_model.WorkflowFilterTypeLabels, - Value: label, - }) + labelID, _ := strconv.ParseInt(label, 10, 64) + if project_service.CanProjectAddLabel(ctx, project, labelID) { + filters = append(filters, project_model.WorkflowFilter{ + Type: project_model.WorkflowFilterTypeLabels, + Value: label, + }) + } } } } + case string(project_model.WorkflowFilterTypeSourceColumn), string(project_model.WorkflowFilterTypeTargetColumn): + if strValue, ok := value.(string); ok && strValue != "" { + strValueInt, _ := strconv.ParseInt(strValue, 10, 64) + if strValueInt > 0 { + col, _ := project_model.GetColumnByProjectIDAndColumnID(ctx, project.ID, strValueInt) + if col == nil { + continue + } + filters = append(filters, project_model.WorkflowFilter{ + Type: project_model.WorkflowFilterType(key), + Value: strconv.FormatInt(strValueInt, 10), + }) + } + } default: // Handle string values (issue_type, column) if strValue, ok := value.(string); ok && strValue != "" { @@ -56,18 +74,22 @@ func convertFormToFilters(formFilters map[string]any) []project_model.WorkflowFi } // convertFormToActions converts form actions to WorkflowAction objects -func convertFormToActions(formActions map[string]any) []project_model.WorkflowAction { +func convertFormToActions(ctx stdCtx.Context, project *project_model.Project, formActions map[string]any) []project_model.WorkflowAction { actions := make([]project_model.WorkflowAction, 0) for key, value := range formActions { switch key { case string(project_model.WorkflowActionTypeColumn): - if floatValue, ok := value.(string); ok { - floatValueInt, _ := strconv.ParseInt(floatValue, 10, 64) - if floatValueInt > 0 { + if colValue, ok := value.(string); ok { + colValueInt, _ := strconv.ParseInt(colValue, 10, 64) + if colValueInt > 0 { + col, _ := project_model.GetColumnByProjectIDAndColumnID(ctx, project.ID, colValueInt) + if col == nil { + continue + } actions = append(actions, project_model.WorkflowAction{ Type: project_model.WorkflowActionTypeColumn, - Value: strconv.FormatInt(floatValueInt, 10), + Value: strconv.FormatInt(colValueInt, 10), }) } } @@ -76,15 +98,22 @@ func convertFormToActions(formActions map[string]any) []project_model.WorkflowAc if labels, ok := value.([]string); ok && len(labels) > 0 { for _, label := range labels { if label != "" { - actions = append(actions, project_model.WorkflowAction{ - Type: project_model.WorkflowActionTypeAddLabels, - Value: label, - }) + labelID, _ := strconv.ParseInt(label, 10, 64) + if project_service.CanProjectAddLabel(ctx, project, labelID) { + actions = append(actions, project_model.WorkflowAction{ + Type: project_model.WorkflowActionTypeAddLabels, + Value: label, + }) + } } } } else if labelInterfaces, ok := value.([]any); ok && len(labelInterfaces) > 0 { for _, labelInterface := range labelInterfaces { if label, ok := labelInterface.(string); ok && label != "" { + labelID, _ := strconv.ParseInt(label, 10, 64) + if !project_service.CanProjectAddLabel(ctx, project, labelID) { + continue + } actions = append(actions, project_model.WorkflowAction{ Type: project_model.WorkflowActionTypeAddLabels, Value: label, @@ -97,6 +126,10 @@ func convertFormToActions(formActions map[string]any) []project_model.WorkflowAc if labels, ok := value.([]string); ok && len(labels) > 0 { for _, label := range labels { if label != "" { + labelID, _ := strconv.ParseInt(label, 10, 64) + if !project_service.CanProjectAddLabel(ctx, project, labelID) { + continue + } actions = append(actions, project_model.WorkflowAction{ Type: project_model.WorkflowActionTypeRemoveLabels, Value: label, @@ -106,6 +139,10 @@ func convertFormToActions(formActions map[string]any) []project_model.WorkflowAc } else if labelInterfaces, ok := value.([]any); ok && len(labelInterfaces) > 0 { for _, labelInterface := range labelInterfaces { if label, ok := labelInterface.(string); ok && label != "" { + labelID, _ := strconv.ParseInt(label, 10, 64) + if !project_service.CanProjectAddLabel(ctx, project, labelID) { + continue + } actions = append(actions, project_model.WorkflowAction{ Type: project_model.WorkflowActionTypeRemoveLabels, Value: label, @@ -376,8 +413,8 @@ func WorkflowsPost(ctx *context.Context) { } // Convert form data to filters and actions - filters := convertFormToFilters(form.Filters) - actions := convertFormToActions(form.Actions) + filters := convertFormToFilters(ctx, p, form.Filters) + actions := convertFormToActions(ctx, p, form.Actions) // Validate: at least one action must be configured if len(actions) == 0 { @@ -420,40 +457,41 @@ func WorkflowsPost(ctx *context.Context) { Enabled: wf.Enabled, }, }) - } else { - // Update an existing workflow - wf, err := project_model.GetWorkflowByID(ctx, eventID) - if err != nil { - ctx.ServerError("GetWorkflowByID", err) - return - } - if wf.ProjectID != projectID { - ctx.NotFound(nil) - return - } - - wf.WorkflowFilters = filters - wf.WorkflowActions = actions - if err := project_model.UpdateWorkflow(ctx, wf); err != nil { - ctx.ServerError("UpdateWorkflow", err) - return - } - - // Return the updated workflow with filter summary - workflowSummary := project_service.GetWorkflowSummary(ctx, wf) - ctx.JSON(http.StatusOK, map[string]any{ - "success": true, - "workflow": WorkflowConfig{ - ID: wf.ID, - EventID: strconv.FormatInt(wf.ID, 10), - DisplayName: string(ctx.Tr(wf.WorkflowEvent.LangKey())), - Filters: wf.WorkflowFilters, - Actions: wf.WorkflowActions, - Summary: workflowSummary, - Enabled: wf.Enabled, - }, - }) + return } + + // Update an existing workflow + wf, err := project_model.GetWorkflowByID(ctx, eventID) + if err != nil { + ctx.ServerError("GetWorkflowByID", err) + return + } + if wf.ProjectID != projectID { + ctx.NotFound(nil) + return + } + + wf.WorkflowFilters = filters + wf.WorkflowActions = actions + if err := project_model.UpdateWorkflow(ctx, wf); err != nil { + ctx.ServerError("UpdateWorkflow", err) + return + } + + // Return the updated workflow with filter summary + workflowSummary := project_service.GetWorkflowSummary(ctx, wf) + ctx.JSON(http.StatusOK, map[string]any{ + "success": true, + "workflow": WorkflowConfig{ + ID: wf.ID, + EventID: strconv.FormatInt(wf.ID, 10), + DisplayName: string(ctx.Tr(wf.WorkflowEvent.LangKey())), + Filters: wf.WorkflowFilters, + Actions: wf.WorkflowActions, + Summary: workflowSummary, + Enabled: wf.Enabled, + }, + }) } func WorkflowsStatus(ctx *context.Context) { diff --git a/services/projects/label.go b/services/projects/label.go index 725b2eb851..b6f77005eb 100644 --- a/services/projects/label.go +++ b/services/projects/label.go @@ -40,3 +40,24 @@ func GetProjectLabels(ctx context.Context, project *project_model.Project) ([]*i } return labels, nil } + +func CanProjectAddLabel(ctx context.Context, project *project_model.Project, labelID int64) bool { + switch project.Type { + case project_model.TypeOrganization, project_model.TypeIndividual: + label, _ := issues_model.GetLabelInOrgByID(ctx, project.OwnerID, labelID) + return label != nil + case project_model.TypeRepository: + label, _ := issues_model.GetLabelInRepoByID(ctx, project.RepoID, labelID) + if label != nil { + return true + } + + if err := project.LoadRepo(ctx); err != nil { + return false + } + + label, _ = issues_model.GetLabelInOrgByID(ctx, project.Repo.OwnerID, labelID) + return label != nil + } + return false +} diff --git a/services/projects/workflow_notifier.go b/services/projects/workflow_notifier.go index 19c7309e68..5c5bbb4f9e 100644 --- a/services/projects/workflow_notifier.go +++ b/services/projects/workflow_notifier.go @@ -345,7 +345,7 @@ func matchWorkflowsFilters(workflow *project_model.Workflow, issue *issues_model } func executeWorkflowActions(ctx context.Context, workflow *project_model.Workflow, issue *issues_model.Issue) { - var toAddedLables, toRemovedLables []*issues_model.Label + var toAddedLabels, toRemovedLabels []*issues_model.Label for _, action := range workflow.WorkflowActions { switch action.Type { @@ -375,7 +375,7 @@ func executeWorkflowActions(ctx context.Context, workflow *project_model.Workflo log.Error("GetLabelByID: %v", err) continue } - toAddedLables = append(toAddedLables, label) + toAddedLabels = append(toAddedLabels, label) case project_model.WorkflowActionTypeRemoveLabels: labelID, _ := strconv.ParseInt(action.Value, 10, 64) if labelID == 0 { @@ -387,7 +387,7 @@ func executeWorkflowActions(ctx context.Context, workflow *project_model.Workflo log.Error("GetLabelByID: %v", err) continue } - toRemovedLables = append(toRemovedLables, label) + toRemovedLabels = append(toRemovedLabels, label) case project_model.WorkflowActionTypeIssueState: if strings.EqualFold(action.Value, "reopen") { if issue.IsClosed { @@ -409,8 +409,8 @@ func executeWorkflowActions(ctx context.Context, workflow *project_model.Workflo } } - if len(toAddedLables)+len(toRemovedLables) > 0 { - if err := issue_service.AddRemoveLabels(ctx, issue, user_model.NewProjectWorkflowsUser(), toAddedLables, toRemovedLables); err != nil { + if len(toAddedLabels)+len(toRemovedLabels) > 0 { + if err := issue_service.AddRemoveLabels(ctx, issue, user_model.NewProjectWorkflowsUser(), toAddedLabels, toRemovedLabels); err != nil { log.Error("AddRemoveLabels: %v", err) } }