improvements

pull/30205/head
Lunny Xiao 2025-10-23 22:24:52 -07:00
parent 623388cef9
commit c2419f8c5b
No known key found for this signature in database
GPG Key ID: C3B7C91B632F738A
7 changed files with 61 additions and 39 deletions

View File

@ -70,7 +70,6 @@ func (err ErrProjectNotExist) Unwrap() error {
type ErrProjectColumnNotExist struct {
ColumnID int64
ProjectID int64
Name string
}
// IsErrProjectColumnNotExist checks if an error is a ErrProjectColumnNotExist
@ -80,8 +79,8 @@ func IsErrProjectColumnNotExist(err error) bool {
}
func (err ErrProjectColumnNotExist) Error() string {
if err.ProjectID > 0 && len(err.Name) > 0 {
return fmt.Sprintf("project column does not exist [project_id: %d, name: %s]", err.ProjectID, err.Name)
if err.ProjectID > 0 {
return fmt.Sprintf("project column does not exist [project_id: %d, column_id: %d]", err.ProjectID, err.ColumnID)
}
return fmt.Sprintf("project column does not exist [id: %d]", err.ColumnID)
}

View File

@ -106,7 +106,7 @@ const (
WorkflowActionTypeColumn WorkflowActionType = "column" // add the item to the project's column
WorkflowActionTypeAddLabels WorkflowActionType = "add_labels" // choose one or more labels
WorkflowActionTypeRemoveLabels WorkflowActionType = "remove_labels" // choose one or more labels
WorkflowActionTypeClose WorkflowActionType = "close" // close the issue
WorkflowActionTypeIssueState WorkflowActionType = "issue_state" // change the issue state (reopen/close)
)
type WorkflowAction struct {
@ -141,7 +141,7 @@ func GetWorkflowEventCapabilities() map[WorkflowEvent]WorkflowEventCapabilities
},
WorkflowEventItemColumnChanged: {
AvailableFilters: []WorkflowFilterType{WorkflowFilterTypeIssueType, WorkflowFilterTypeColumn, WorkflowFilterTypeLabels},
AvailableActions: []WorkflowActionType{WorkflowActionTypeAddLabels, WorkflowActionTypeRemoveLabels, WorkflowActionTypeClose},
AvailableActions: []WorkflowActionType{WorkflowActionTypeAddLabels, WorkflowActionTypeRemoveLabels, WorkflowActionTypeIssueState},
},
WorkflowEventCodeChangesRequested: {
AvailableFilters: []WorkflowFilterType{WorkflowFilterTypeLabels}, // only applies to pull requests

View File

@ -4,6 +4,7 @@
package projects
import (
stdCtx "context"
"errors"
"io"
"net/http"
@ -25,22 +26,56 @@ var (
)
// getFilterSummary returns a human-readable summary of the filters
func getFilterSummary(filters []project_model.WorkflowFilter) string {
func getFilterSummary(ctx stdCtx.Context, filters []project_model.WorkflowFilter) string {
if len(filters) == 0 {
return ""
}
var summary strings.Builder
labelIDs := make([]int64, 0)
for _, filter := range filters {
if filter.Type == "scope" {
switch filter.Type {
case project_model.WorkflowFilterTypeIssueType:
switch filter.Value {
case "issue":
return " (Issues only)"
summary.WriteString(" (Issues only)")
case "pull_request":
return " (Pull requests only)"
summary.WriteString(" (Pull requests only)")
}
case project_model.WorkflowFilterTypeColumn:
columnID, _ := strconv.ParseInt(filter.Value, 10, 64)
if columnID <= 0 {
continue
}
col, err := project_model.GetColumn(ctx, columnID)
if err != nil {
log.Error("GetColumn: %v", err)
continue
}
summary.WriteString(" (Column: " + col.Title + ")")
case project_model.WorkflowFilterTypeLabels:
labelID, _ := strconv.ParseInt(filter.Value, 10, 64)
if labelID > 0 {
labelIDs = append(labelIDs, labelID)
}
}
}
return ""
if len(labelIDs) > 0 {
labels, err := issues_model.GetLabelsByIDs(ctx, labelIDs)
if err != nil {
log.Error("GetLabelsByIDs: %v", err)
} else {
summary.WriteString(" (Labels: ")
for i, label := range labels {
summary.WriteString(label.Name)
if i < len(labels)-1 {
summary.WriteString(", ")
}
}
summary.WriteString(")")
}
}
return summary.String()
}
// convertFormToFilters converts form filters to WorkflowFilter objects
@ -133,28 +168,16 @@ func convertFormToActions(formActions map[string]any) []project_model.WorkflowAc
}
}
}
case "issueState":
case "issue_state":
if strValue, ok := value.(string); ok {
switch strings.ToLower(strValue) {
case "close", "closed", "true":
v := strings.ToLower(strValue)
if v == "close" || v == "reopen" {
actions = append(actions, project_model.WorkflowAction{
Type: project_model.WorkflowActionTypeClose,
Value: "close",
})
case "reopen", "open", "false":
actions = append(actions, project_model.WorkflowAction{
Type: project_model.WorkflowActionTypeClose,
Value: "reopen",
Type: project_model.WorkflowActionTypeIssueState,
Value: v,
})
}
}
case "closeIssue":
if boolValue, ok := value.(bool); ok && boolValue {
actions = append(actions, project_model.WorkflowAction{
Type: project_model.WorkflowActionTypeClose,
Value: "close",
})
}
}
}
@ -216,7 +239,7 @@ func WorkflowsEvents(ctx *context.Context) {
if len(existingWorkflows) > 0 {
// Add all existing workflows for this event
for _, wf := range existingWorkflows {
filterSummary := getFilterSummary(wf.WorkflowFilters)
filterSummary := getFilterSummary(ctx, wf.WorkflowFilters)
outputWorkflows = append(outputWorkflows, &WorkflowConfig{
ID: wf.ID,
EventID: strconv.FormatInt(wf.ID, 10),
@ -485,7 +508,7 @@ func WorkflowsPost(ctx *context.Context) {
}
// Return the newly created workflow with filter summary
filterSummary := getFilterSummary(wf.WorkflowFilters)
filterSummary := getFilterSummary(ctx, wf.WorkflowFilters)
ctx.JSON(http.StatusOK, map[string]any{
"success": true,
"workflow": map[string]any{
@ -518,7 +541,7 @@ func WorkflowsPost(ctx *context.Context) {
}
// Return the updated workflow with filter summary
filterSummary := getFilterSummary(wf.WorkflowFilters)
filterSummary := getFilterSummary(ctx, wf.WorkflowFilters)
ctx.JSON(http.StatusOK, map[string]any{
"success": true,
"workflow": map[string]any{

View File

@ -447,7 +447,7 @@ func UpdateIssueProject(ctx *context.Context) {
if issue.Project != nil && issue.Project.ID == projectID {
continue
}
if err := issues_servie.IssueAssignOrRemoveProject(ctx, issue, ctx.Doer, projectID, 0); err != nil {
if err := issues_servie.AssignOrRemoveProject(ctx, issue, ctx.Doer, projectID, 0); err != nil {
if errors.Is(err, util.ErrPermissionDenied) {
continue
}

View File

@ -12,7 +12,7 @@ import (
"code.gitea.io/gitea/services/notify"
)
func IssueAssignOrRemoveProject(ctx context.Context, issue *issues_model.Issue, doer *user_model.User, projectID int64, position int) error {
func AssignOrRemoveProject(ctx context.Context, issue *issues_model.Issue, doer *user_model.User, projectID int64, position int) error {
if err := issues_model.IssueAssignOrRemoveProject(ctx, issue, doer, projectID, 0); err != nil {
return err
}

View File

@ -424,8 +424,8 @@ func executeWorkflowActions(ctx context.Context, workflow *project_model.Workflo
}
continue
}
case project_model.WorkflowActionTypeClose:
if strings.EqualFold(action.Value, "reopen") || strings.EqualFold(action.Value, "false") {
case project_model.WorkflowActionTypeIssueState:
if strings.EqualFold(action.Value, "reopen") {
if issue.IsClosed {
if err := issue_service.ReopenIssue(ctx, issue, user_model.NewProjectWorkflowsUser(), ""); err != nil {
log.Error("ReopenIssue: %v", err)
@ -433,7 +433,7 @@ func executeWorkflowActions(ctx context.Context, workflow *project_model.Workflo
}
issue.IsClosed = false
}
} else {
} else if strings.EqualFold(action.Value, "close") {
if !issue.IsClosed {
if err := issue_service.CloseIssue(ctx, issue, user_model.NewProjectWorkflowsUser(), ""); err != nil {
log.Error("CloseIssue: %v", err)

View File

@ -977,21 +977,21 @@ onUnmounted(() => {
</div>
</div>
<div class="field" v-if="hasAction('close')">
<div class="field" v-if="hasAction('issue_state')">
<label for="issue-state-action">Issue state</label>
<select
v-if="isInEditMode"
id="issue-state-action"
class="column-select"
v-model="store.workflowActions.issueState"
v-model="store.workflowActions.issue_state"
>
<option value="">No change</option>
<option value="close">Close issue</option>
<option value="reopen">Reopen issue</option>
</select>
<div v-else class="readonly-value">
{{ store.workflowActions.issueState === 'close' ? 'Close issue' :
store.workflowActions.issueState === 'reopen' ? 'Reopen issue' : 'No change' }}
{{ store.workflowActions.issue_state === 'close' ? 'Close issue' :
store.workflowActions.issue_state === 'reopen' ? 'Reopen issue' : 'No change' }}
</div>
</div>
</div>