From bbf913a13ea8de7ea812533f0ba7e5859405b34d Mon Sep 17 00:00:00 2001 From: Thomas E Lackey Date: Fri, 10 Jan 2025 13:43:30 -0600 Subject: [PATCH 01/11] Add sorting of exlusive labels. --- models/issues/issue_search.go | 89 ++++++++++--------- models/issues/label.go | 3 +- modules/indexer/issues/db/options.go | 12 ++- modules/indexer/issues/dboptions.go | 8 +- options/locale/locale_en-US.ini | 2 + routers/web/repo/issue_label.go | 1 + routers/web/repo/issue_list.go | 25 ++++++ services/forms/repo_form.go | 13 +-- templates/repo/issue/filter_list.tmpl | 7 ++ .../repo/issue/labels/label_edit_modal.tmpl | 8 ++ templates/repo/issue/labels/label_list.tmpl | 1 + web_src/js/features/comp/LabelEdit.ts | 4 + 12 files changed, 120 insertions(+), 53 deletions(-) diff --git a/models/issues/issue_search.go b/models/issues/issue_search.go index f1cd125d49..382a994796 100644 --- a/models/issues/issue_search.go +++ b/models/issues/issue_search.go @@ -70,47 +70,54 @@ func (o *IssuesOptions) Copy(edit ...func(options *IssuesOptions)) *IssuesOption // applySorts sort an issues-related session based on the provided // sortType string func applySorts(sess *xorm.Session, sortType string, priorityRepoID int64) { - switch sortType { - case "oldest": - sess.Asc("issue.created_unix").Asc("issue.id") - case "recentupdate": - sess.Desc("issue.updated_unix").Desc("issue.created_unix").Desc("issue.id") - case "leastupdate": - sess.Asc("issue.updated_unix").Asc("issue.created_unix").Asc("issue.id") - case "mostcomment": - sess.Desc("issue.num_comments").Desc("issue.created_unix").Desc("issue.id") - case "leastcomment": - sess.Asc("issue.num_comments").Desc("issue.created_unix").Desc("issue.id") - case "priority": - sess.Desc("issue.priority").Desc("issue.created_unix").Desc("issue.id") - case "nearduedate": - // 253370764800 is 01/01/9999 @ 12:00am (UTC) - sess.Join("LEFT", "milestone", "issue.milestone_id = milestone.id"). - OrderBy("CASE " + - "WHEN issue.deadline_unix = 0 AND (milestone.deadline_unix = 0 OR milestone.deadline_unix IS NULL) THEN 253370764800 " + - "WHEN milestone.deadline_unix = 0 OR milestone.deadline_unix IS NULL THEN issue.deadline_unix " + - "WHEN milestone.deadline_unix < issue.deadline_unix OR issue.deadline_unix = 0 THEN milestone.deadline_unix " + - "ELSE issue.deadline_unix END ASC"). - Desc("issue.created_unix"). - Desc("issue.id") - case "farduedate": - sess.Join("LEFT", "milestone", "issue.milestone_id = milestone.id"). - OrderBy("CASE " + - "WHEN milestone.deadline_unix IS NULL THEN issue.deadline_unix " + - "WHEN milestone.deadline_unix < issue.deadline_unix OR issue.deadline_unix = 0 THEN milestone.deadline_unix " + - "ELSE issue.deadline_unix END DESC"). - Desc("issue.created_unix"). - Desc("issue.id") - case "priorityrepo": - sess.OrderBy("CASE "+ - "WHEN issue.repo_id = ? THEN 1 "+ - "ELSE 2 END ASC", priorityRepoID). - Desc("issue.created_unix"). - Desc("issue.id") - case "project-column-sorting": - sess.Asc("project_issue.sorting").Desc("issue.created_unix").Desc("issue.id") - default: - sess.Desc("issue.created_unix").Desc("issue.id") + if strings.HasPrefix(sortType, "scope-") { + scope := strings.TrimPrefix(sortType, "scope-") + sess.Join("LEFT", "issue_label", "issue.id = issue_label.issue_id") + sess.Join("LEFT", "label", "label.id = issue_label.label_id and label.name LIKE ?", fmt.Sprintf("%s/%%", scope)) + sess.Asc("label.exclusive_order").Desc("issue.id") + } else { + switch sortType { + case "oldest": + sess.Asc("issue.created_unix").Asc("issue.id") + case "recentupdate": + sess.Desc("issue.updated_unix").Desc("issue.created_unix").Desc("issue.id") + case "leastupdate": + sess.Asc("issue.updated_unix").Asc("issue.created_unix").Asc("issue.id") + case "mostcomment": + sess.Desc("issue.num_comments").Desc("issue.created_unix").Desc("issue.id") + case "leastcomment": + sess.Asc("issue.num_comments").Desc("issue.created_unix").Desc("issue.id") + case "priority": + sess.Desc("issue.priority").Desc("issue.created_unix").Desc("issue.id") + case "nearduedate": + // 253370764800 is 01/01/9999 @ 12:00am (UTC) + sess.Join("LEFT", "milestone", "issue.milestone_id = milestone.id"). + OrderBy("CASE " + + "WHEN issue.deadline_unix = 0 AND (milestone.deadline_unix = 0 OR milestone.deadline_unix IS NULL) THEN 253370764800 " + + "WHEN milestone.deadline_unix = 0 OR milestone.deadline_unix IS NULL THEN issue.deadline_unix " + + "WHEN milestone.deadline_unix < issue.deadline_unix OR issue.deadline_unix = 0 THEN milestone.deadline_unix " + + "ELSE issue.deadline_unix END ASC"). + Desc("issue.created_unix"). + Desc("issue.id") + case "farduedate": + sess.Join("LEFT", "milestone", "issue.milestone_id = milestone.id"). + OrderBy("CASE " + + "WHEN milestone.deadline_unix IS NULL THEN issue.deadline_unix " + + "WHEN milestone.deadline_unix < issue.deadline_unix OR issue.deadline_unix = 0 THEN milestone.deadline_unix " + + "ELSE issue.deadline_unix END DESC"). + Desc("issue.created_unix"). + Desc("issue.id") + case "priorityrepo": + sess.OrderBy("CASE "+ + "WHEN issue.repo_id = ? THEN 1 "+ + "ELSE 2 END ASC", priorityRepoID). + Desc("issue.created_unix"). + Desc("issue.id") + case "project-column-sorting": + sess.Asc("project_issue.sorting").Desc("issue.created_unix").Desc("issue.id") + default: + sess.Desc("issue.created_unix").Desc("issue.id") + } } } diff --git a/models/issues/label.go b/models/issues/label.go index b9d24bbe99..de1366f065 100644 --- a/models/issues/label.go +++ b/models/issues/label.go @@ -87,6 +87,7 @@ type Label struct { OrgID int64 `xorm:"INDEX"` Name string Exclusive bool + ExclusiveOrder int `xorm:"DEFAULT 0"` Description string Color string `xorm:"VARCHAR(7)"` NumIssues int @@ -236,7 +237,7 @@ func UpdateLabel(ctx context.Context, l *Label) error { } l.Color = color - return updateLabelCols(ctx, l, "name", "description", "color", "exclusive", "archived_unix") + return updateLabelCols(ctx, l, "name", "description", "color", "exclusive", "exclusive_order", "archived_unix") } // DeleteLabel delete a label diff --git a/modules/indexer/issues/db/options.go b/modules/indexer/issues/db/options.go index 42834f6e88..7e78d00c96 100644 --- a/modules/indexer/issues/db/options.go +++ b/modules/indexer/issues/db/options.go @@ -4,14 +4,14 @@ package db import ( - "context" - "fmt" - "code.gitea.io/gitea/models/db" issue_model "code.gitea.io/gitea/models/issues" "code.gitea.io/gitea/modules/container" "code.gitea.io/gitea/modules/indexer/issues/internal" "code.gitea.io/gitea/modules/optional" + "context" + "fmt" + "strings" ) func ToDBOptions(ctx context.Context, options *internal.SearchOptions) (*issue_model.IssuesOptions, error) { @@ -34,7 +34,11 @@ func ToDBOptions(ctx context.Context, options *internal.SearchOptions) (*issue_m case internal.SortByDeadlineAsc: sortType = "nearduedate" default: - sortType = "newest" + if strings.HasPrefix(string(options.SortBy), "scope-") { + sortType = string(options.SortBy) + } else { + sortType = "newest" + } } // See the comment of issues_model.SearchOptions for the reason why we need to convert diff --git a/modules/indexer/issues/dboptions.go b/modules/indexer/issues/dboptions.go index 4f6ad96d22..3921598689 100644 --- a/modules/indexer/issues/dboptions.go +++ b/modules/indexer/issues/dboptions.go @@ -6,7 +6,9 @@ package issues import ( "code.gitea.io/gitea/models/db" issues_model "code.gitea.io/gitea/models/issues" + "code.gitea.io/gitea/modules/indexer/issues/internal" "code.gitea.io/gitea/modules/optional" + "strings" ) func ToSearchOptions(keyword string, opts *issues_model.IssuesOptions) *SearchOptions { @@ -99,7 +101,11 @@ func ToSearchOptions(keyword string, opts *issues_model.IssuesOptions) *SearchOp // Unsupported sort type for search fallthrough default: - searchOpt.SortBy = SortByUpdatedDesc + if strings.HasPrefix(opts.SortType, "scope-") { + searchOpt.SortBy = internal.SortBy(opts.SortType) + } else { + searchOpt.SortBy = SortByUpdatedDesc + } } return searchOpt diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index 140e2efe57..e053198b9b 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -1635,6 +1635,8 @@ issues.label_archived_filter = Show archived labels issues.label_archive_tooltip = Archived labels are excluded by default from the suggestions when searching by label. issues.label_exclusive_desc = Name the label scope/item to make it mutually exclusive with other scope/ labels. issues.label_exclusive_warning = Any conflicting scoped labels will be removed when editing the labels of an issue or pull request. +issues.label_exclusive_order = Sort Order +issues.label_exclusive_order_tooltip = Exclusive labels in the same scope will be sorted according to this numeric order. issues.label_count = %d labels issues.label_open_issues = %d open issues/pull requests issues.label_edit = Edit diff --git a/routers/web/repo/issue_label.go b/routers/web/repo/issue_label.go index 5ef6b09faa..1172a474ea 100644 --- a/routers/web/repo/issue_label.go +++ b/routers/web/repo/issue_label.go @@ -139,6 +139,7 @@ func UpdateLabel(ctx *context.Context) { } l.Name = form.Title l.Exclusive = form.Exclusive + l.ExclusiveOrder = form.ExclusiveOrder l.Description = form.Description l.Color = form.Color diff --git a/routers/web/repo/issue_list.go b/routers/web/repo/issue_list.go index 2f615a100e..1c57320df1 100644 --- a/routers/web/repo/issue_list.go +++ b/routers/web/repo/issue_list.go @@ -6,7 +6,9 @@ package repo import ( "bytes" "fmt" + "maps" "net/http" + "slices" "strconv" "strings" @@ -459,6 +461,24 @@ func UpdateIssueStatus(ctx *context.Context) { ctx.JSONOK() } +func renderExclusiveLabelScopes(ctx *context.Context) { + labels, err := issues_model.GetLabelsByRepoID(ctx, ctx.Repo.Repository.ID, "", db.ListOptions{}) + if err != nil { + ctx.ServerError("GetAllRepoLabels", err) + return + } + scopeSet := make(map[string]bool, 0) + for _, label := range labels { + scope := label.ExclusiveScope() + if len(scope) > 0 { + scopeSet[scope] = true + } + } + + scopes := slices.Collect(maps.Keys(scopeSet)) + ctx.Data["ExclusiveLabelScopes"] = scopes +} + func renderMilestones(ctx *context.Context) { // Get milestones milestones, err := db.Find[issues_model.Milestone](ctx, issues_model.FindMilestoneOptions{ @@ -778,6 +798,11 @@ func Issues(ctx *context.Context) { return } + renderExclusiveLabelScopes(ctx) + if ctx.Written() { + return + } + ctx.Data["CanWriteIssuesOrPulls"] = ctx.Repo.CanWriteIssuesOrPulls(isPullList) ctx.HTML(http.StatusOK, tplIssues) diff --git a/services/forms/repo_form.go b/services/forms/repo_form.go index b14171787e..4b4398068b 100644 --- a/services/forms/repo_form.go +++ b/services/forms/repo_form.go @@ -524,12 +524,13 @@ func (f *CreateMilestoneForm) Validate(req *http.Request, errs binding.Errors) b // CreateLabelForm form for creating label type CreateLabelForm struct { - ID int64 - Title string `binding:"Required;MaxSize(50)" locale:"repo.issues.label_title"` - Exclusive bool `form:"exclusive"` - IsArchived bool `form:"is_archived"` - Description string `binding:"MaxSize(200)" locale:"repo.issues.label_description"` - Color string `binding:"Required;MaxSize(7)" locale:"repo.issues.label_color"` + ID int64 + Title string `binding:"Required;MaxSize(50)" locale:"repo.issues.label_title"` + Exclusive bool `form:"exclusive"` + ExclusiveOrder int `form:"exclusive_order"` + IsArchived bool `form:"is_archived"` + Description string `binding:"MaxSize(200)" locale:"repo.issues.label_description"` + Color string `binding:"Required;MaxSize(7)" locale:"repo.issues.label_color"` } // Validate validates the fields diff --git a/templates/repo/issue/filter_list.tmpl b/templates/repo/issue/filter_list.tmpl index 7612d93b21..3e1e422a35 100644 --- a/templates/repo/issue/filter_list.tmpl +++ b/templates/repo/issue/filter_list.tmpl @@ -133,5 +133,12 @@ {{ctx.Locale.Tr "repo.issues.filter_sort.leastcomment"}} {{ctx.Locale.Tr "repo.issues.filter_sort.nearduedate"}} {{ctx.Locale.Tr "repo.issues.filter_sort.farduedate"}} +
+
{{ctx.Locale.Tr "repo.issues.filter_label"}}
+ {{range .ExclusiveLabelScopes}} + {{ $scope := . }} + {{ $sortType := (printf "scope-%s" $scope) }} + {{$scope}} + {{end}} diff --git a/templates/repo/issue/labels/label_edit_modal.tmpl b/templates/repo/issue/labels/label_edit_modal.tmpl index 527b7ff900..7ee2cc7252 100644 --- a/templates/repo/issue/labels/label_edit_modal.tmpl +++ b/templates/repo/issue/labels/label_edit_modal.tmpl @@ -25,6 +25,14 @@ {{svg "octicon-alert"}} {{ctx.Locale.Tr "repo.issues.label_exclusive_warning"}}
+
+ + +
diff --git a/templates/repo/issue/labels/label_list.tmpl b/templates/repo/issue/labels/label_list.tmpl index 822567301e..a7c8016427 100644 --- a/templates/repo/issue/labels/label_list.tmpl +++ b/templates/repo/issue/labels/label_list.tmpl @@ -50,6 +50,7 @@ data-label-id="{{.ID}}" data-label-name="{{.Name}}" data-label-color="{{.Color}}" data-label-exclusive="{{.Exclusive}}" data-label-is-archived="{{gt .ArchivedUnix 0}}" data-label-num-issues="{{.NumIssues}}" data-label-description="{{.Description}}" + data-label-exclusive-order="{{.ExclusiveOrder}}" >{{svg "octicon-pencil"}} {{ctx.Locale.Tr "repo.issues.label_edit"}} ('.label-exclusive-input'); const elExclusiveWarning = elModal.querySelector('.label-exclusive-warning'); + const elExclusiveOrderField = elModal.querySelector('.label-exclusive-order-input-field'); + const elExclusiveOrderInput = elModal.querySelector('.label-exclusive-order-input'); const elIsArchivedField = elModal.querySelector('.label-is-archived-input-field'); const elIsArchivedInput = elModal.querySelector('.label-is-archived-input'); const elDescInput = elModal.querySelector('.label-desc-input'); @@ -29,6 +31,7 @@ export function initCompLabelEdit(pageSelector: string) { const showExclusiveWarning = hasScope && elExclusiveInput.checked && elModal.hasAttribute('data-need-warn-exclusive'); toggleElem(elExclusiveWarning, showExclusiveWarning); if (!hasScope) elExclusiveInput.checked = false; + toggleElem(elExclusiveOrderField, elExclusiveInput.checked) }; const showLabelEditModal = (btn:HTMLElement) => { @@ -36,6 +39,7 @@ export function initCompLabelEdit(pageSelector: string) { const form = elModal.querySelector('form'); elLabelId.value = btn.getAttribute('data-label-id') || ''; elNameInput.value = btn.getAttribute('data-label-name') || ''; + elExclusiveOrderInput.value = btn.getAttribute('data-label-exclusive-order') || '0'; elIsArchivedInput.checked = btn.getAttribute('data-label-is-archived') === 'true'; elExclusiveInput.checked = btn.getAttribute('data-label-exclusive') === 'true'; elDescInput.value = btn.getAttribute('data-label-description') || ''; From 104e4c517bbb34b1f9d7cd4fafbbc1b7d98b0390 Mon Sep 17 00:00:00 2001 From: Thomas E Lackey Date: Fri, 10 Jan 2025 13:47:52 -0600 Subject: [PATCH 02/11] Adjust format --- models/issues/issue_search.go | 88 ++++++++++++++++++----------------- 1 file changed, 45 insertions(+), 43 deletions(-) diff --git a/models/issues/issue_search.go b/models/issues/issue_search.go index 382a994796..691ae1331f 100644 --- a/models/issues/issue_search.go +++ b/models/issues/issue_search.go @@ -70,54 +70,56 @@ func (o *IssuesOptions) Copy(edit ...func(options *IssuesOptions)) *IssuesOption // applySorts sort an issues-related session based on the provided // sortType string func applySorts(sess *xorm.Session, sortType string, priorityRepoID int64) { + // Since this sortType is dynamically created, it has to be treated specially. if strings.HasPrefix(sortType, "scope-") { scope := strings.TrimPrefix(sortType, "scope-") sess.Join("LEFT", "issue_label", "issue.id = issue_label.issue_id") sess.Join("LEFT", "label", "label.id = issue_label.label_id and label.name LIKE ?", fmt.Sprintf("%s/%%", scope)) sess.Asc("label.exclusive_order").Desc("issue.id") - } else { - switch sortType { - case "oldest": - sess.Asc("issue.created_unix").Asc("issue.id") - case "recentupdate": - sess.Desc("issue.updated_unix").Desc("issue.created_unix").Desc("issue.id") - case "leastupdate": - sess.Asc("issue.updated_unix").Asc("issue.created_unix").Asc("issue.id") - case "mostcomment": - sess.Desc("issue.num_comments").Desc("issue.created_unix").Desc("issue.id") - case "leastcomment": - sess.Asc("issue.num_comments").Desc("issue.created_unix").Desc("issue.id") - case "priority": - sess.Desc("issue.priority").Desc("issue.created_unix").Desc("issue.id") - case "nearduedate": - // 253370764800 is 01/01/9999 @ 12:00am (UTC) - sess.Join("LEFT", "milestone", "issue.milestone_id = milestone.id"). - OrderBy("CASE " + - "WHEN issue.deadline_unix = 0 AND (milestone.deadline_unix = 0 OR milestone.deadline_unix IS NULL) THEN 253370764800 " + - "WHEN milestone.deadline_unix = 0 OR milestone.deadline_unix IS NULL THEN issue.deadline_unix " + - "WHEN milestone.deadline_unix < issue.deadline_unix OR issue.deadline_unix = 0 THEN milestone.deadline_unix " + - "ELSE issue.deadline_unix END ASC"). - Desc("issue.created_unix"). - Desc("issue.id") - case "farduedate": - sess.Join("LEFT", "milestone", "issue.milestone_id = milestone.id"). - OrderBy("CASE " + - "WHEN milestone.deadline_unix IS NULL THEN issue.deadline_unix " + - "WHEN milestone.deadline_unix < issue.deadline_unix OR issue.deadline_unix = 0 THEN milestone.deadline_unix " + - "ELSE issue.deadline_unix END DESC"). - Desc("issue.created_unix"). - Desc("issue.id") - case "priorityrepo": - sess.OrderBy("CASE "+ - "WHEN issue.repo_id = ? THEN 1 "+ - "ELSE 2 END ASC", priorityRepoID). - Desc("issue.created_unix"). - Desc("issue.id") - case "project-column-sorting": - sess.Asc("project_issue.sorting").Desc("issue.created_unix").Desc("issue.id") - default: - sess.Desc("issue.created_unix").Desc("issue.id") - } + return // EARLY RETURN + } + + switch sortType { + case "oldest": + sess.Asc("issue.created_unix").Asc("issue.id") + case "recentupdate": + sess.Desc("issue.updated_unix").Desc("issue.created_unix").Desc("issue.id") + case "leastupdate": + sess.Asc("issue.updated_unix").Asc("issue.created_unix").Asc("issue.id") + case "mostcomment": + sess.Desc("issue.num_comments").Desc("issue.created_unix").Desc("issue.id") + case "leastcomment": + sess.Asc("issue.num_comments").Desc("issue.created_unix").Desc("issue.id") + case "priority": + sess.Desc("issue.priority").Desc("issue.created_unix").Desc("issue.id") + case "nearduedate": + // 253370764800 is 01/01/9999 @ 12:00am (UTC) + sess.Join("LEFT", "milestone", "issue.milestone_id = milestone.id"). + OrderBy("CASE " + + "WHEN issue.deadline_unix = 0 AND (milestone.deadline_unix = 0 OR milestone.deadline_unix IS NULL) THEN 253370764800 " + + "WHEN milestone.deadline_unix = 0 OR milestone.deadline_unix IS NULL THEN issue.deadline_unix " + + "WHEN milestone.deadline_unix < issue.deadline_unix OR issue.deadline_unix = 0 THEN milestone.deadline_unix " + + "ELSE issue.deadline_unix END ASC"). + Desc("issue.created_unix"). + Desc("issue.id") + case "farduedate": + sess.Join("LEFT", "milestone", "issue.milestone_id = milestone.id"). + OrderBy("CASE " + + "WHEN milestone.deadline_unix IS NULL THEN issue.deadline_unix " + + "WHEN milestone.deadline_unix < issue.deadline_unix OR issue.deadline_unix = 0 THEN milestone.deadline_unix " + + "ELSE issue.deadline_unix END DESC"). + Desc("issue.created_unix"). + Desc("issue.id") + case "priorityrepo": + sess.OrderBy("CASE "+ + "WHEN issue.repo_id = ? THEN 1 "+ + "ELSE 2 END ASC", priorityRepoID). + Desc("issue.created_unix"). + Desc("issue.id") + case "project-column-sorting": + sess.Asc("project_issue.sorting").Desc("issue.created_unix").Desc("issue.id") + default: + sess.Desc("issue.created_unix").Desc("issue.id") } } From 182aed4bfbb63dc66f7ebed25dc8a137ae8ae633 Mon Sep 17 00:00:00 2001 From: Thomas E Lackey Date: Fri, 10 Jan 2025 14:33:49 -0600 Subject: [PATCH 03/11] yaml support --- modules/label/label.go | 9 +++++---- modules/repository/init.go | 9 +++++---- options/label/Advanced.yaml | 11 +++++++++++ 3 files changed, 21 insertions(+), 8 deletions(-) diff --git a/modules/label/label.go b/modules/label/label.go index d3ef0e1dc9..ce028aa9f3 100644 --- a/modules/label/label.go +++ b/modules/label/label.go @@ -14,10 +14,11 @@ var colorPattern = regexp.MustCompile("^#?(?:[0-9a-fA-F]{6}|[0-9a-fA-F]{3})$") // Label represents label information loaded from template type Label struct { - Name string `yaml:"name"` - Color string `yaml:"color"` - Description string `yaml:"description,omitempty"` - Exclusive bool `yaml:"exclusive,omitempty"` + Name string `yaml:"name"` + Color string `yaml:"color"` + Description string `yaml:"description,omitempty"` + Exclusive bool `yaml:"exclusive,omitempty"` + ExclusiveOrder int `yaml:"exclusive_order,omitempty"` } // NormalizeColor normalizes a color string to a 6-character hex code diff --git a/modules/repository/init.go b/modules/repository/init.go index 24602ae090..097dd596e5 100644 --- a/modules/repository/init.go +++ b/modules/repository/init.go @@ -154,10 +154,11 @@ func InitializeLabels(ctx context.Context, id int64, labelTemplate string, isOrg labels := make([]*issues_model.Label, len(list)) for i := 0; i < len(list); i++ { labels[i] = &issues_model.Label{ - Name: list[i].Name, - Exclusive: list[i].Exclusive, - Description: list[i].Description, - Color: list[i].Color, + Name: list[i].Name, + Exclusive: list[i].Exclusive, + ExclusiveOrder: list[i].ExclusiveOrder, + Description: list[i].Description, + Color: list[i].Color, } if isOrg { labels[i].OrgID = id diff --git a/options/label/Advanced.yaml b/options/label/Advanced.yaml index b1ecdd6d93..55ac91116e 100644 --- a/options/label/Advanced.yaml +++ b/options/label/Advanced.yaml @@ -22,49 +22,60 @@ labels: description: Breaking change that won't be backward compatible - name: "Reviewed/Duplicate" exclusive: true + exclusive_order: 50 color: 616161 description: This issue or pull request already exists - name: "Reviewed/Invalid" exclusive: true + exclusive_order: 100 color: 546e7a description: Invalid issue - name: "Reviewed/Confirmed" exclusive: true + exclusive_order: 0 color: 795548 description: Issue has been confirmed - name: "Reviewed/Won't Fix" exclusive: true + exclusive_order: 75 color: eeeeee description: This issue won't be fixed - name: "Status/Need More Info" exclusive: true + exclusive_order: 25 color: 424242 description: Feedback is required to reproduce issue or to continue work - name: "Status/Blocked" exclusive: true + exclusive_order: 0 color: 880e4f description: Something is blocking this issue or pull request - name: "Status/Abandoned" exclusive: true + exclusive_order: 100 color: "222222" description: Somebody has started to work on this but abandoned work - name: "Priority/Critical" exclusive: true + exclusive_order: 0 color: b71c1c description: The priority is critical priority: critical - name: "Priority/High" exclusive: true + exclusive_order: 1 color: d32f2f description: The priority is high priority: high - name: "Priority/Medium" exclusive: true + exclusive_order: 2 color: e64a19 description: The priority is medium priority: medium - name: "Priority/Low" exclusive: true + exclusive_order: 3 color: 4caf50 description: The priority is low priority: low From 2fa535df8402cfbb86373726877a470acd5f38d7 Mon Sep 17 00:00:00 2001 From: Thomas E Lackey Date: Fri, 10 Jan 2025 14:48:49 -0600 Subject: [PATCH 04/11] fmt --- modules/indexer/issues/db/options.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/modules/indexer/issues/db/options.go b/modules/indexer/issues/db/options.go index 7e78d00c96..690c77972f 100644 --- a/modules/indexer/issues/db/options.go +++ b/modules/indexer/issues/db/options.go @@ -4,14 +4,15 @@ package db import ( + "context" + "fmt" + "strings" + "code.gitea.io/gitea/models/db" issue_model "code.gitea.io/gitea/models/issues" "code.gitea.io/gitea/modules/container" "code.gitea.io/gitea/modules/indexer/issues/internal" "code.gitea.io/gitea/modules/optional" - "context" - "fmt" - "strings" ) func ToDBOptions(ctx context.Context, options *internal.SearchOptions) (*issue_model.IssuesOptions, error) { From 041b352f5cd6f500af9b526d5e7aecdd29e0b85e Mon Sep 17 00:00:00 2001 From: Thomas E Lackey Date: Fri, 10 Jan 2025 14:49:38 -0600 Subject: [PATCH 05/11] fmt --- modules/indexer/issues/dboptions.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/indexer/issues/dboptions.go b/modules/indexer/issues/dboptions.go index 3921598689..f987dd28b6 100644 --- a/modules/indexer/issues/dboptions.go +++ b/modules/indexer/issues/dboptions.go @@ -4,11 +4,12 @@ package issues import ( + "strings" + "code.gitea.io/gitea/models/db" issues_model "code.gitea.io/gitea/models/issues" "code.gitea.io/gitea/modules/indexer/issues/internal" "code.gitea.io/gitea/modules/optional" - "strings" ) func ToSearchOptions(keyword string, opts *issues_model.IssuesOptions) *SearchOptions { From 4e89634bd8f0035f08075a084225465a96143974 Mon Sep 17 00:00:00 2001 From: Thomas E Lackey Date: Fri, 10 Jan 2025 15:02:29 -0600 Subject: [PATCH 06/11] sirt --- routers/web/repo/issue_list.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/routers/web/repo/issue_list.go b/routers/web/repo/issue_list.go index 1c57320df1..40aaa9a248 100644 --- a/routers/web/repo/issue_list.go +++ b/routers/web/repo/issue_list.go @@ -9,6 +9,7 @@ import ( "maps" "net/http" "slices" + "sort" "strconv" "strings" @@ -476,6 +477,7 @@ func renderExclusiveLabelScopes(ctx *context.Context) { } scopes := slices.Collect(maps.Keys(scopeSet)) + sort.Strings(scopes) ctx.Data["ExclusiveLabelScopes"] = scopes } From 7b4563c88bdc476330c20d57db9c0690ea7fa2a1 Mon Sep 17 00:00:00 2001 From: Thomas E Lackey Date: Fri, 10 Jan 2025 15:22:38 -0600 Subject: [PATCH 07/11] refactor --- models/issues/issue_search.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/models/issues/issue_search.go b/models/issues/issue_search.go index 691ae1331f..a23098b4e6 100644 --- a/models/issues/issue_search.go +++ b/models/issues/issue_search.go @@ -74,7 +74,7 @@ func applySorts(sess *xorm.Session, sortType string, priorityRepoID int64) { if strings.HasPrefix(sortType, "scope-") { scope := strings.TrimPrefix(sortType, "scope-") sess.Join("LEFT", "issue_label", "issue.id = issue_label.issue_id") - sess.Join("LEFT", "label", "label.id = issue_label.label_id and label.name LIKE ?", fmt.Sprintf("%s/%%", scope)) + sess.Join("LEFT", "label", "label.id = issue_label.label_id and label.name LIKE ?", scope+"/%") sess.Asc("label.exclusive_order").Desc("issue.id") return // EARLY RETURN } From de58736eae96458caef878193f9c9a2e70e82a51 Mon Sep 17 00:00:00 2001 From: Thomas E Lackey Date: Mon, 17 Feb 2025 13:48:47 -0600 Subject: [PATCH 08/11] Tabs not spaces --- templates/repo/issue/filter_list.tmpl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/templates/repo/issue/filter_list.tmpl b/templates/repo/issue/filter_list.tmpl index 3e1e422a35..98a8d842b8 100644 --- a/templates/repo/issue/filter_list.tmpl +++ b/templates/repo/issue/filter_list.tmpl @@ -136,9 +136,9 @@
{{ctx.Locale.Tr "repo.issues.filter_label"}}
{{range .ExclusiveLabelScopes}} - {{ $scope := . }} - {{ $sortType := (printf "scope-%s" $scope) }} -
{{$scope}} + {{ $scope := . }} + {{ $sortType := (printf "scope-%s" $scope) }} + {{$scope}} {{end}}
From d1586e5ba963dddc1d2bcfc6ca79825471c9561e Mon Sep 17 00:00:00 2001 From: Thomas E Lackey Date: Mon, 17 Feb 2025 13:49:50 -0600 Subject: [PATCH 09/11] Lint --- web_src/js/features/comp/LabelEdit.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web_src/js/features/comp/LabelEdit.ts b/web_src/js/features/comp/LabelEdit.ts index 7de40eab09..a7f5d50772 100644 --- a/web_src/js/features/comp/LabelEdit.ts +++ b/web_src/js/features/comp/LabelEdit.ts @@ -31,7 +31,7 @@ export function initCompLabelEdit(pageSelector: string) { const showExclusiveWarning = hasScope && elExclusiveInput.checked && elModal.hasAttribute('data-need-warn-exclusive'); toggleElem(elExclusiveWarning, showExclusiveWarning); if (!hasScope) elExclusiveInput.checked = false; - toggleElem(elExclusiveOrderField, elExclusiveInput.checked) + toggleElem(elExclusiveOrderField, elExclusiveInput.checked); }; const showLabelEditModal = (btn:HTMLElement) => { From 591acd43003880275ed948e1e9c6c95acc7fe28e Mon Sep 17 00:00:00 2001 From: Thomas E Lackey Date: Mon, 17 Feb 2025 14:07:19 -0600 Subject: [PATCH 10/11] Lint --- templates/repo/issue/filter_list.tmpl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/templates/repo/issue/filter_list.tmpl b/templates/repo/issue/filter_list.tmpl index 98a8d842b8..680df67456 100644 --- a/templates/repo/issue/filter_list.tmpl +++ b/templates/repo/issue/filter_list.tmpl @@ -136,9 +136,9 @@
{{ctx.Locale.Tr "repo.issues.filter_label"}}
{{range .ExclusiveLabelScopes}} - {{ $scope := . }} - {{ $sortType := (printf "scope-%s" $scope) }} - {{$scope}} + {{$scope := .}} + {{$sortType := (printf "scope-%s" $scope)}} + {{$scope}} {{end}} From f3d2f67c28c7527ccba5cfbd1a5b1f8142392f65 Mon Sep 17 00:00:00 2001 From: Thomas E Lackey Date: Mon, 17 Feb 2025 15:18:28 -0600 Subject: [PATCH 11/11] Migration --- models/migrations/migrations.go | 1 + models/migrations/v1_24/v314.go | 16 ++++++++++++++++ 2 files changed, 17 insertions(+) create mode 100644 models/migrations/v1_24/v314.go diff --git a/models/migrations/migrations.go b/models/migrations/migrations.go index 87d674a440..a94a1a6605 100644 --- a/models/migrations/migrations.go +++ b/models/migrations/migrations.go @@ -374,6 +374,7 @@ func prepareMigrationTasks() []*migration { // Gitea 1.23.0-rc0 ends at migration ID number 311 (database version 312) newMigration(312, "Add DeleteBranchAfterMerge to AutoMerge", v1_24.AddDeleteBranchAfterMergeForAutoMerge), newMigration(313, "Move PinOrder from issue table to a new table issue_pin", v1_24.MovePinOrderToTableIssuePin), + newMigration(314, "Add ExclusiveOrder to Label table", v1_24.AddExclusiveOrderColumnToLabelTable), } return preparedMigrations } diff --git a/models/migrations/v1_24/v314.go b/models/migrations/v1_24/v314.go new file mode 100644 index 0000000000..cda6724586 --- /dev/null +++ b/models/migrations/v1_24/v314.go @@ -0,0 +1,16 @@ +// Copyright 2024 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package v1_24 //nolint + +import ( + "xorm.io/xorm" +) + +func AddExclusiveOrderColumnToLabelTable(x *xorm.Engine) error { + type Label struct { + ExclusiveOrder int64 `xorm:"DEFAULT 0"` + } + + return x.Sync(new(Label)) +}