From aa061b409f157b945de297591558777be5abc379 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim-Niclas=20Oelschl=C3=A4ger?= Date: Fri, 9 Feb 2024 17:30:38 +0100 Subject: [PATCH 01/33] enhancement: Show blocked_by and blocking in issue-list-view --- routers/web/repo/issue.go | 19 +++++++++++++++++++ templates/shared/issuelist.tmpl | 26 ++++++++++++++++++++++++++ 2 files changed, 45 insertions(+) diff --git a/routers/web/repo/issue.go b/routers/web/repo/issue.go index 83a5b76bf15bc..9c6eaa9f8cf64 100644 --- a/routers/web/repo/issue.go +++ b/routers/web/repo/issue.go @@ -324,6 +324,8 @@ func issues(ctx *context.Context, milestoneID, projectID int64, isPullOption opt return } + blockingDependenciesMap := make(map[int64][]*issues_model.DependencyInfo, len(issues)) + blockedByDependenciesMap := make(map[int64][]*issues_model.DependencyInfo, len(issues)) // Get posters. for i := range issues { // Check read status @@ -333,7 +335,24 @@ func issues(ctx *context.Context, milestoneID, projectID int64, isPullOption opt ctx.ServerError("GetIsRead", err) return } + blockingDependencies, err := issues[i].BlockingDependencies(ctx) + if err != nil { + ctx.ServerError("BlockingDependencies", err) + return + } + slices.Reverse(blockingDependencies) + blockingDependenciesMap[issues[i].ID] = blockingDependencies + + blockedByDependencies, err := issues[i].BlockedByDependencies(ctx, db.ListOptions{}) + if err != nil { + ctx.ServerError("BlockedByDependencies", err) + return + } + slices.Reverse(blockedByDependencies) + blockedByDependenciesMap[issues[i].ID] = blockedByDependencies } + ctx.Data["BlockingDependenciesMap"] = blockingDependenciesMap + ctx.Data["BlockedByDependenciesMap"] = blockedByDependenciesMap commitStatuses, lastStatus, err := pull_service.GetIssuesAllCommitStatus(ctx, issues) if err != nil { diff --git a/templates/shared/issuelist.tmpl b/templates/shared/issuelist.tmpl index e8a0079c1cdbe..332f7cbe0b3ec 100644 --- a/templates/shared/issuelist.tmpl +++ b/templates/shared/issuelist.tmpl @@ -118,6 +118,32 @@ {{end}} + {{if and $.BlockingDependenciesMap (index $.BlockingDependenciesMap .ID)}} +
+ {{ctx.Locale.Tr "repo.issues.dependency.blocks_short"}}: + {{ $first := true }} + {{range (index $.BlockingDependenciesMap .ID)}} + {{if not $first}}, {{end}} + {{ $first = false }} + + #{{.Issue.Index}} + + {{end}} +
+ {{end}} + {{if and $.BlockedByDependenciesMap (index $.BlockedByDependenciesMap .ID)}} +
+ {{ctx.Locale.Tr "repo.issues.dependency.blocked_by_short"}}: + {{ $first := true }} + {{range (index $.BlockedByDependenciesMap .ID)}} + {{if not $first}}, {{end}} + {{ $first = false }} + + #{{.Issue.Index}} + + {{end}} +
+ {{end}} {{if .IsPull}} {{$approveOfficial := call $approvalCounts .ID "approve"}} {{$rejectOfficial := call $approvalCounts .ID "reject"}} From 925ac870dcffb1a61e47110a6771ed1e09b46ee0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim-Niclas=20Oelschl=C3=A4ger?= Date: Fri, 9 Feb 2024 17:55:42 +0100 Subject: [PATCH 02/33] refactor: extract to subtemplate --- options/locale/locale_en-US.ini | 2 ++ templates/shared/issue_dependency.tmpl | 13 ++++++++++ templates/shared/issuelist.tmpl | 34 ++++++-------------------- 3 files changed, 23 insertions(+), 26 deletions(-) create mode 100644 templates/shared/issue_dependency.tmpl diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index e7ba7dd8c969b..9df6bf658ecc8 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -1685,7 +1685,9 @@ issues.dependency.issue_close_blocked = You need to close all issues blocking th issues.dependency.issue_batch_close_blocked = "Cannot batch close issues that you choose, because issue #%d still has open dependencies" issues.dependency.pr_close_blocked = You need to close all issues blocking this pull request before you can merge it. issues.dependency.blocks_short = Blocks +issues.dependency.blocks_following = Blocks: issues.dependency.blocked_by_short = Depends on +issues.dependency.blocked_by_following = Depends on: issues.dependency.remove_header = Remove Dependency issues.dependency.issue_remove_text = This will remove the dependency from this issue. Continue? issues.dependency.pr_remove_text = This will remove the dependency from this pull request. Continue? diff --git a/templates/shared/issue_dependency.tmpl b/templates/shared/issue_dependency.tmpl new file mode 100644 index 0000000000000..2c301d67b9ba8 --- /dev/null +++ b/templates/shared/issue_dependency.tmpl @@ -0,0 +1,13 @@ +{{if and .DependenciesMap (index .DependenciesMap .ID)}} +
+ {{.Title}} + {{$first := true}} + {{range (index .DependenciesMap .ID)}} + {{if not $first}},{{end}} + {{$first = false}} + + #{{.Issue.Index}} + + {{end}} +
+{{end}} \ No newline at end of file diff --git a/templates/shared/issuelist.tmpl b/templates/shared/issuelist.tmpl index 332f7cbe0b3ec..2a9418dfa3ee5 100644 --- a/templates/shared/issuelist.tmpl +++ b/templates/shared/issuelist.tmpl @@ -118,32 +118,14 @@ {{end}} - {{if and $.BlockingDependenciesMap (index $.BlockingDependenciesMap .ID)}} -
- {{ctx.Locale.Tr "repo.issues.dependency.blocks_short"}}: - {{ $first := true }} - {{range (index $.BlockingDependenciesMap .ID)}} - {{if not $first}}, {{end}} - {{ $first = false }} - - #{{.Issue.Index}} - - {{end}} -
- {{end}} - {{if and $.BlockedByDependenciesMap (index $.BlockedByDependenciesMap .ID)}} -
- {{ctx.Locale.Tr "repo.issues.dependency.blocked_by_short"}}: - {{ $first := true }} - {{range (index $.BlockedByDependenciesMap .ID)}} - {{if not $first}}, {{end}} - {{ $first = false }} - - #{{.Issue.Index}} - - {{end}} -
- {{end}} + {{template "shared/issue_dependency" (dict + "ID" .ID + "DependenciesMap" $.BlockingDependenciesMap + "Title" (ctx.Locale.Tr "repo.issues.dependency.blocks_following"))}} + {{template "shared/issue_dependency" (dict + "ID" .ID + "DependenciesMap" $.BlockedByDependenciesMap + "Title" (ctx.Locale.Tr "repo.issues.dependency.blocked_by_following"))}} {{if .IsPull}} {{$approveOfficial := call $approvalCounts .ID "approve"}} {{$rejectOfficial := call $approvalCounts .ID "reject"}} From 2b39981064f128ad625c6fb8aa5efae03cc2ea4c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim-Niclas=20Oelschl=C3=A4ger?= Date: Fri, 9 Feb 2024 18:03:46 +0100 Subject: [PATCH 03/33] chore: resolve lint errors --- templates/shared/issue_dependency.tmpl | 2 +- templates/shared/issuelist.tmpl | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/templates/shared/issue_dependency.tmpl b/templates/shared/issue_dependency.tmpl index 2c301d67b9ba8..09d3acd27ff83 100644 --- a/templates/shared/issue_dependency.tmpl +++ b/templates/shared/issue_dependency.tmpl @@ -10,4 +10,4 @@ {{end}} -{{end}} \ No newline at end of file +{{end}} diff --git a/templates/shared/issuelist.tmpl b/templates/shared/issuelist.tmpl index 2a9418dfa3ee5..7cec6822aeb47 100644 --- a/templates/shared/issuelist.tmpl +++ b/templates/shared/issuelist.tmpl @@ -118,11 +118,11 @@ {{end}} - {{template "shared/issue_dependency" (dict + {{template "shared/issue_dependency" (dict "ID" .ID "DependenciesMap" $.BlockingDependenciesMap "Title" (ctx.Locale.Tr "repo.issues.dependency.blocks_following"))}} - {{template "shared/issue_dependency" (dict + {{template "shared/issue_dependency" (dict "ID" .ID "DependenciesMap" $.BlockedByDependenciesMap "Title" (ctx.Locale.Tr "repo.issues.dependency.blocked_by_following"))}} From ad4d893ad57ea01b2eb0912248fa1a6309df8a35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim-Niclas=20Oelschl=C3=A4ger?= Date: Fri, 9 Feb 2024 20:13:10 +0100 Subject: [PATCH 04/33] refactor: use translations to support rtl --- modules/templates/helper.go | 1 + modules/templates/util_render.go | 18 ++++++++++++++++++ options/locale/locale_en-US.ini | 4 ++-- templates/shared/issue_dependency.tmpl | 16 ++++------------ templates/shared/issuelist.tmpl | 10 ++++------ 5 files changed, 29 insertions(+), 20 deletions(-) diff --git a/modules/templates/helper.go b/modules/templates/helper.go index 0997239a55add..532fc7989ec2a 100644 --- a/modules/templates/helper.go +++ b/modules/templates/helper.go @@ -165,6 +165,7 @@ func NewFuncMap() template.FuncMap { "RenderMarkdownToHtml": RenderMarkdownToHtml, "RenderLabel": RenderLabel, "RenderLabels": RenderLabels, + "RenderDependencies": RenderDependencies, // ----------------------------------------------------------------- // misc diff --git a/modules/templates/util_render.go b/modules/templates/util_render.go index cdff31698ca13..ead41faa32e25 100644 --- a/modules/templates/util_render.go +++ b/modules/templates/util_render.go @@ -224,3 +224,21 @@ func RenderLabels(ctx context.Context, labels []*issues_model.Label, repoLink st htmlCode += "" return template.HTML(htmlCode) } + +func RenderDependencies(ctx context.Context, dependencies []*issues_model.DependencyInfo) template.HTML { + if dependencies == nil || len(dependencies) < 1 { + return "" + } + + htmlCode := "" + + for index, dependency := range dependencies { + if index != 0 { + htmlCode += `,` + } + htmlCode += fmt.Sprintf(`#%d`, + dependency.Issue.Link(), dependency.Issue.Index, RenderEmoji(ctx, dependency.Issue.Title), dependency.Issue.Index) + } + + return template.HTML(htmlCode) +} diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index 9df6bf658ecc8..ad7e68bbc1c61 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -1685,9 +1685,9 @@ issues.dependency.issue_close_blocked = You need to close all issues blocking th issues.dependency.issue_batch_close_blocked = "Cannot batch close issues that you choose, because issue #%d still has open dependencies" issues.dependency.pr_close_blocked = You need to close all issues blocking this pull request before you can merge it. issues.dependency.blocks_short = Blocks -issues.dependency.blocks_following = Blocks: +issues.dependency.blocks_following = Blocks: %s issues.dependency.blocked_by_short = Depends on -issues.dependency.blocked_by_following = Depends on: +issues.dependency.blocked_by_following = Depends on: %s issues.dependency.remove_header = Remove Dependency issues.dependency.issue_remove_text = This will remove the dependency from this issue. Continue? issues.dependency.pr_remove_text = This will remove the dependency from this pull request. Continue? diff --git a/templates/shared/issue_dependency.tmpl b/templates/shared/issue_dependency.tmpl index 09d3acd27ff83..481cba8229b09 100644 --- a/templates/shared/issue_dependency.tmpl +++ b/templates/shared/issue_dependency.tmpl @@ -1,13 +1,5 @@ -{{if and .DependenciesMap (index .DependenciesMap .ID)}} -
- {{.Title}} - {{$first := true}} - {{range (index .DependenciesMap .ID)}} - {{if not $first}},{{end}} - {{$first = false}} - - #{{.Issue.Index}} - - {{end}} -
+{{if .Dependencies}} +
+ {{(ctx.Locale.Tr .TitleKey (RenderDependencies ctx .Dependencies)) | Safe}} +
{{end}} diff --git a/templates/shared/issuelist.tmpl b/templates/shared/issuelist.tmpl index 7cec6822aeb47..b040a9083d4fb 100644 --- a/templates/shared/issuelist.tmpl +++ b/templates/shared/issuelist.tmpl @@ -119,13 +119,11 @@ {{end}} {{template "shared/issue_dependency" (dict - "ID" .ID - "DependenciesMap" $.BlockingDependenciesMap - "Title" (ctx.Locale.Tr "repo.issues.dependency.blocks_following"))}} + "Dependencies" (index $.BlockingDependenciesMap .ID) + "TitleKey" "repo.issues.dependency.blocks_following")}} {{template "shared/issue_dependency" (dict - "ID" .ID - "DependenciesMap" $.BlockedByDependenciesMap - "Title" (ctx.Locale.Tr "repo.issues.dependency.blocked_by_following"))}} + "Dependencies" (index $.BlockedByDependenciesMap .ID) + "TitleKey" "repo.issues.dependency.blocked_by_following")}} {{if .IsPull}} {{$approveOfficial := call $approvalCounts .ID "approve"}} {{$rejectOfficial := call $approvalCounts .ID "reject"}} From 5c35d88110a364c980b7b3c6a67a404f80db8195 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim-Niclas=20Oelschl=C3=A4ger?= Date: Sat, 10 Feb 2024 00:34:42 +0100 Subject: [PATCH 05/33] enhancement: shows divider --- modules/templates/util_render.go | 6 +++--- templates/shared/issue_dependency.tmpl | 2 +- templates/shared/issuelist.tmpl | 6 +++--- web_src/css/repo/issue-list.css | 4 ++++ 4 files changed, 11 insertions(+), 7 deletions(-) diff --git a/modules/templates/util_render.go b/modules/templates/util_render.go index ead41faa32e25..d4d34418c5d6d 100644 --- a/modules/templates/util_render.go +++ b/modules/templates/util_render.go @@ -226,11 +226,11 @@ func RenderLabels(ctx context.Context, labels []*issues_model.Label, repoLink st } func RenderDependencies(ctx context.Context, dependencies []*issues_model.DependencyInfo) template.HTML { - if dependencies == nil || len(dependencies) < 1 { + if len(dependencies) == 0 { return "" } - htmlCode := "" + htmlCode := "" for index, dependency := range dependencies { if index != 0 { @@ -240,5 +240,5 @@ func RenderDependencies(ctx context.Context, dependencies []*issues_model.Depend dependency.Issue.Link(), dependency.Issue.Index, RenderEmoji(ctx, dependency.Issue.Title), dependency.Issue.Index) } - return template.HTML(htmlCode) + return template.HTML(htmlCode + "") } diff --git a/templates/shared/issue_dependency.tmpl b/templates/shared/issue_dependency.tmpl index 481cba8229b09..e97e85508494f 100644 --- a/templates/shared/issue_dependency.tmpl +++ b/templates/shared/issue_dependency.tmpl @@ -1,5 +1,5 @@ {{if .Dependencies}} -
+
{{(ctx.Locale.Tr .TitleKey (RenderDependencies ctx .Dependencies)) | Safe}}
{{end}} diff --git a/templates/shared/issuelist.tmpl b/templates/shared/issuelist.tmpl index b040a9083d4fb..00fe09adaf0aa 100644 --- a/templates/shared/issuelist.tmpl +++ b/templates/shared/issuelist.tmpl @@ -118,12 +118,12 @@ {{end}} - {{template "shared/issue_dependency" (dict - "Dependencies" (index $.BlockingDependenciesMap .ID) - "TitleKey" "repo.issues.dependency.blocks_following")}} {{template "shared/issue_dependency" (dict "Dependencies" (index $.BlockedByDependenciesMap .ID) "TitleKey" "repo.issues.dependency.blocked_by_following")}} + {{template "shared/issue_dependency" (dict + "Dependencies" (index $.BlockingDependenciesMap .ID) + "TitleKey" "repo.issues.dependency.blocks_following")}} {{if .IsPull}} {{$approveOfficial := call $approvalCounts .ID "approve"}} {{$rejectOfficial := call $approvalCounts .ID "reject"}} diff --git a/web_src/css/repo/issue-list.css b/web_src/css/repo/issue-list.css index 1421577af2a75..264ec27abcac1 100644 --- a/web_src/css/repo/issue-list.css +++ b/web_src/css/repo/issue-list.css @@ -82,6 +82,10 @@ background-color: var(--color-secondary-dark-4); } +#issue-list .flex-item-body > .flex-text-inline::before { + content: '|'; +} + .archived-label-filter { margin-left: 10px; font-size: 12px; From 67797f5ac0a30dcdbfca4a2963f7cab605f7627c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim-Niclas=20Oelschl=C3=A4ger?= Date: Sat, 10 Feb 2024 00:35:17 +0100 Subject: [PATCH 06/33] chore: lowercase --- options/locale/locale_en-US.ini | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index ad7e68bbc1c61..6543e85831085 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -1685,9 +1685,9 @@ issues.dependency.issue_close_blocked = You need to close all issues blocking th issues.dependency.issue_batch_close_blocked = "Cannot batch close issues that you choose, because issue #%d still has open dependencies" issues.dependency.pr_close_blocked = You need to close all issues blocking this pull request before you can merge it. issues.dependency.blocks_short = Blocks -issues.dependency.blocks_following = Blocks: %s +issues.dependency.blocks_following = blocks: %s issues.dependency.blocked_by_short = Depends on -issues.dependency.blocked_by_following = Depends on: %s +issues.dependency.blocked_by_following = depends on: %s issues.dependency.remove_header = Remove Dependency issues.dependency.issue_remove_text = This will remove the dependency from this issue. Continue? issues.dependency.pr_remove_text = This will remove the dependency from this pull request. Continue? From 586a42b640d4283dbb8303229a90fe784c4fd67b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim-Niclas=20Oelschl=C3=A4ger?= Date: Sat, 10 Feb 2024 00:36:16 +0100 Subject: [PATCH 07/33] chore: lint -> double-quotes --- web_src/css/repo/issue-list.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web_src/css/repo/issue-list.css b/web_src/css/repo/issue-list.css index 264ec27abcac1..f123cefd5d2b1 100644 --- a/web_src/css/repo/issue-list.css +++ b/web_src/css/repo/issue-list.css @@ -83,7 +83,7 @@ } #issue-list .flex-item-body > .flex-text-inline::before { - content: '|'; + content: "|"; } .archived-label-filter { From 5b8d4aa8650c923bebc0e9ab250e58f01f7f1b6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim-Niclas=20Oelschl=C3=A4ger?= Date: Sat, 10 Feb 2024 11:13:59 +0100 Subject: [PATCH 08/33] enhancement: line-through for closed issues --- modules/templates/util_render.go | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/modules/templates/util_render.go b/modules/templates/util_render.go index d4d34418c5d6d..37feb8001105b 100644 --- a/modules/templates/util_render.go +++ b/modules/templates/util_render.go @@ -236,8 +236,12 @@ func RenderDependencies(ctx context.Context, dependencies []*issues_model.Depend if index != 0 { htmlCode += `,` } - htmlCode += fmt.Sprintf(`#%d`, - dependency.Issue.Link(), dependency.Issue.Index, RenderEmoji(ctx, dependency.Issue.Title), dependency.Issue.Index) + anchorClasses := "gt-ml-2" + if dependency.Issue.IsClosed { + anchorClasses += " term-fg9" + } + htmlCode += fmt.Sprintf(`#%d`, + dependency.Issue.Link(), dependency.Issue.Index, RenderEmoji(ctx, dependency.Issue.Title), anchorClasses, dependency.Issue.Index) } return template.HTML(htmlCode + "") From 20dcbade9193e8962d3e8c8eef38626d1fa19595 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim-Niclas=20Oelschl=C3=A4ger?= Date: Sat, 10 Feb 2024 19:18:27 +0100 Subject: [PATCH 09/33] review: Dependencies as template.HTML --- modules/templates/helper.go | 1 - modules/templates/util_render.go | 22 ------------------- routers/web/repo/issue.go | 30 ++++++++++++++++++++++---- templates/shared/issue_dependency.tmpl | 2 +- web_src/css/helpers.css | 1 + 5 files changed, 28 insertions(+), 28 deletions(-) diff --git a/modules/templates/helper.go b/modules/templates/helper.go index 532fc7989ec2a..0997239a55add 100644 --- a/modules/templates/helper.go +++ b/modules/templates/helper.go @@ -165,7 +165,6 @@ func NewFuncMap() template.FuncMap { "RenderMarkdownToHtml": RenderMarkdownToHtml, "RenderLabel": RenderLabel, "RenderLabels": RenderLabels, - "RenderDependencies": RenderDependencies, // ----------------------------------------------------------------- // misc diff --git a/modules/templates/util_render.go b/modules/templates/util_render.go index 37feb8001105b..cdff31698ca13 100644 --- a/modules/templates/util_render.go +++ b/modules/templates/util_render.go @@ -224,25 +224,3 @@ func RenderLabels(ctx context.Context, labels []*issues_model.Label, repoLink st htmlCode += "" return template.HTML(htmlCode) } - -func RenderDependencies(ctx context.Context, dependencies []*issues_model.DependencyInfo) template.HTML { - if len(dependencies) == 0 { - return "" - } - - htmlCode := "" - - for index, dependency := range dependencies { - if index != 0 { - htmlCode += `,` - } - anchorClasses := "gt-ml-2" - if dependency.Issue.IsClosed { - anchorClasses += " term-fg9" - } - htmlCode += fmt.Sprintf(`#%d`, - dependency.Issue.Link(), dependency.Issue.Index, RenderEmoji(ctx, dependency.Issue.Title), anchorClasses, dependency.Issue.Index) - } - - return template.HTML(htmlCode + "") -} diff --git a/routers/web/repo/issue.go b/routers/web/repo/issue.go index 9c6eaa9f8cf64..d3f49425632cc 100644 --- a/routers/web/repo/issue.go +++ b/routers/web/repo/issue.go @@ -141,6 +141,28 @@ func MustAllowPulls(ctx *context.Context) { } } +func dependenciesToHTML(ctx *context.Context, dependencies []*issues_model.DependencyInfo) template.HTML { + if len(dependencies) == 0 { + return "" + } + + htmlCode := "" + + for index, dependency := range dependencies { + if index != 0 { + htmlCode += `,` + } + anchorClasses := "gt-ml-2" + if dependency.Issue.IsClosed { + anchorClasses += " gt-line-through" + } + htmlCode += fmt.Sprintf(`#%d`, + dependency.Issue.Link(), dependency.Issue.Index, templates.RenderEmoji(ctx, dependency.Issue.Title), anchorClasses, dependency.Issue.Index) + } + + return template.HTML(htmlCode + "") +} + func issues(ctx *context.Context, milestoneID, projectID int64, isPullOption optional.Option[bool]) { var err error viewType := ctx.FormString("type") @@ -324,8 +346,8 @@ func issues(ctx *context.Context, milestoneID, projectID int64, isPullOption opt return } - blockingDependenciesMap := make(map[int64][]*issues_model.DependencyInfo, len(issues)) - blockedByDependenciesMap := make(map[int64][]*issues_model.DependencyInfo, len(issues)) + blockingDependenciesMap := make(map[int64]template.HTML, len(issues)) + blockedByDependenciesMap := make(map[int64]template.HTML, len(issues)) // Get posters. for i := range issues { // Check read status @@ -341,7 +363,7 @@ func issues(ctx *context.Context, milestoneID, projectID int64, isPullOption opt return } slices.Reverse(blockingDependencies) - blockingDependenciesMap[issues[i].ID] = blockingDependencies + blockingDependenciesMap[issues[i].ID] = dependenciesToHTML(ctx, blockingDependencies) blockedByDependencies, err := issues[i].BlockedByDependencies(ctx, db.ListOptions{}) if err != nil { @@ -349,7 +371,7 @@ func issues(ctx *context.Context, milestoneID, projectID int64, isPullOption opt return } slices.Reverse(blockedByDependencies) - blockedByDependenciesMap[issues[i].ID] = blockedByDependencies + blockedByDependenciesMap[issues[i].ID] = dependenciesToHTML(ctx, blockedByDependencies) } ctx.Data["BlockingDependenciesMap"] = blockingDependenciesMap ctx.Data["BlockedByDependenciesMap"] = blockedByDependenciesMap diff --git a/templates/shared/issue_dependency.tmpl b/templates/shared/issue_dependency.tmpl index e97e85508494f..0587b9bcb1016 100644 --- a/templates/shared/issue_dependency.tmpl +++ b/templates/shared/issue_dependency.tmpl @@ -1,5 +1,5 @@ {{if .Dependencies}}
- {{(ctx.Locale.Tr .TitleKey (RenderDependencies ctx .Dependencies)) | Safe}} + {{(ctx.Locale.Tr .TitleKey .Dependencies) | Safe}}
{{end}} diff --git a/web_src/css/helpers.css b/web_src/css/helpers.css index dad0f9b127b18..76077718a4680 100644 --- a/web_src/css/helpers.css +++ b/web_src/css/helpers.css @@ -49,6 +49,7 @@ Gitea's private styles use `g-` prefix. /* below class names match Tailwind CSS */ .gt-object-contain { object-fit: contain !important; } .gt-no-underline { text-decoration-line: none !important; } +.gt-line-through { text-decoration-line: line-through !important; } .gt-normal-case { text-transform: none !important; } .gt-italic { font-style: italic !important; } From f9726ea8a4813994eb2c6d664df7e379c659b17a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim-Niclas=20Oelschl=C3=A4ger?= Date: Sun, 11 Feb 2024 16:59:49 +0100 Subject: [PATCH 10/33] style: middot as divider --- web_src/css/repo/issue-list.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web_src/css/repo/issue-list.css b/web_src/css/repo/issue-list.css index f123cefd5d2b1..e83c9e0a35279 100644 --- a/web_src/css/repo/issue-list.css +++ b/web_src/css/repo/issue-list.css @@ -83,7 +83,7 @@ } #issue-list .flex-item-body > .flex-text-inline::before { - content: "|"; + content: "•"; } .archived-label-filter { From d460a5cdc094e3edd16125ece65f4d11ce55edf9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim-Niclas=20Oelschl=C3=A4ger?= Date: Tue, 13 Feb 2024 23:43:18 +0100 Subject: [PATCH 11/33] performance: better sql --- models/issues/issue.go | 2 ++ models/issues/issue_list.go | 57 +++++++++++++++++++++++++++++++++ routers/web/repo/issue.go | 42 +++++++++++++----------- templates/shared/issuelist.tmpl | 4 +-- 4 files changed, 84 insertions(+), 21 deletions(-) diff --git a/models/issues/issue.go b/models/issues/issue.go index 563a780dcb134..6f2fd66a20c62 100644 --- a/models/issues/issue.go +++ b/models/issues/issue.go @@ -597,6 +597,8 @@ func IsUserParticipantsOfIssue(ctx context.Context, user *user_model.User, issue type DependencyInfo struct { Issue `xorm:"extends"` repo_model.Repository `xorm:"extends"` + IssueID int64 `xorm:"NOT NULL"` + DependencyID int64 `xorm:"NOT NULL"` } // GetParticipantIDsByIssue returns all userIDs who are participated in comments of an issue and issue author diff --git a/models/issues/issue_list.go b/models/issues/issue_list.go index a932ac2554369..8cea5f6c44156 100644 --- a/models/issues/issue_list.go +++ b/models/issues/issue_list.go @@ -6,6 +6,7 @@ package issues import ( "context" "fmt" + "strings" "code.gitea.io/gitea/models/db" project_model "code.gitea.io/gitea/models/project" @@ -599,3 +600,59 @@ func (issues IssueList) GetApprovalCounts(ctx context.Context) (map[int64][]*Rev return approvalCountMap, nil } + +func (issues IssueList) BlockingDependenciesMap(ctx context.Context) (issueDepsMap map[int64][]*DependencyInfo, err error) { + var issueDeps []*DependencyInfo + + issueIDsString := strings.Trim(strings.Join(strings.Fields(fmt.Sprint(issues.getIssueIDs())), ","), "[]") + repoIDsString := strings.Trim(strings.Join(strings.Fields(fmt.Sprint(issues.getRepoIDs())), ","), "[]") + + err = db.GetEngine(ctx). + Table("issue"). + Join("INNER", "repository", "repository.id = issue.repo_id"). + Join("INNER", "issue_dependency", "issue_dependency.issue_id = issue.id"). + Where(fmt.Sprintf("dependency_id IN (%s)", issueIDsString)). + // sort by repo id then created date, with the issues of the same repo at the beginning of the list + OrderBy(fmt.Sprintf("CASE WHEN issue.repo_id IN (%s) THEN 0 ELSE issue.repo_id END, issue.created_unix ASC", repoIDsString)). + Find(&issueDeps) + if err != nil { + return nil, err + } + + issueDepsMap = make(map[int64][]*DependencyInfo, len(issues)) + for _, depInfo := range issueDeps { + depInfo.Issue.Repo = &depInfo.Repository + + issueDepsMap[depInfo.DependencyID] = append(issueDepsMap[depInfo.DependencyID], depInfo) + } + + return issueDepsMap, nil +} + +func (issues IssueList) BlockedByDependenciesMap(ctx context.Context) (issueDepsMap map[int64][]*DependencyInfo, err error) { + var issueDeps []*DependencyInfo + + issueIDsString := strings.Trim(strings.Join(strings.Fields(fmt.Sprint(issues.getIssueIDs())), ","), "[]") + repoIDsString := strings.Trim(strings.Join(strings.Fields(fmt.Sprint(issues.getRepoIDs())), ","), "[]") + + err = db.GetEngine(ctx). + Table("issue"). + Join("INNER", "repository", "repository.id = issue.repo_id"). + Join("INNER", "issue_dependency", "issue_dependency.dependency_id = issue.id"). + Where(fmt.Sprintf("issue_id IN (%s)", issueIDsString)). + // sort by repo id then created date, with the issues of the same repo at the beginning of the list + OrderBy(fmt.Sprintf("CASE WHEN issue.repo_id IN (%s) THEN 0 ELSE issue.repo_id END, issue.created_unix ASC", repoIDsString)). + Find(&issueDeps) + if err != nil { + return nil, err + } + + issueDepsMap = make(map[int64][]*DependencyInfo, len(issues)) + for _, depInfo := range issueDeps { + depInfo.Issue.Repo = &depInfo.Repository + + issueDepsMap[depInfo.IssueID] = append(issueDepsMap[depInfo.IssueID], depInfo) + } + + return issueDepsMap, nil +} diff --git a/routers/web/repo/issue.go b/routers/web/repo/issue.go index d3f49425632cc..17367a4b1a0f5 100644 --- a/routers/web/repo/issue.go +++ b/routers/web/repo/issue.go @@ -346,8 +346,27 @@ func issues(ctx *context.Context, milestoneID, projectID int64, isPullOption opt return } - blockingDependenciesMap := make(map[int64]template.HTML, len(issues)) - blockedByDependenciesMap := make(map[int64]template.HTML, len(issues)) + blockingDependenciesTemplates := make(map[int64]template.HTML, len(issues)) + blockedByDependenciesTemplates := make(map[int64]template.HTML, len(issues)) + + blockingDependenciesMap, err := issues.BlockingDependenciesMap(ctx) + if err != nil { + ctx.ServerError("BlockingDependenciesMap", err) + return + } + for i, blockingDependencies := range blockingDependenciesMap { + blockingDependenciesTemplates[i] = dependenciesToHTML(ctx, blockingDependencies) + } + + blockedByDependenciesMap, err := issues.BlockedByDependenciesMap(ctx) + if err != nil { + ctx.ServerError("BlockedByDependenciesMap", err) + return + } + for i, blockedByDependencies := range blockedByDependenciesMap { + blockedByDependenciesTemplates[i] = dependenciesToHTML(ctx, blockedByDependencies) + } + // Get posters. for i := range issues { // Check read status @@ -357,24 +376,9 @@ func issues(ctx *context.Context, milestoneID, projectID int64, isPullOption opt ctx.ServerError("GetIsRead", err) return } - blockingDependencies, err := issues[i].BlockingDependencies(ctx) - if err != nil { - ctx.ServerError("BlockingDependencies", err) - return - } - slices.Reverse(blockingDependencies) - blockingDependenciesMap[issues[i].ID] = dependenciesToHTML(ctx, blockingDependencies) - - blockedByDependencies, err := issues[i].BlockedByDependencies(ctx, db.ListOptions{}) - if err != nil { - ctx.ServerError("BlockedByDependencies", err) - return - } - slices.Reverse(blockedByDependencies) - blockedByDependenciesMap[issues[i].ID] = dependenciesToHTML(ctx, blockedByDependencies) } - ctx.Data["BlockingDependenciesMap"] = blockingDependenciesMap - ctx.Data["BlockedByDependenciesMap"] = blockedByDependenciesMap + ctx.Data["BlockingDependenciesTemplates"] = blockingDependenciesTemplates + ctx.Data["BlockedByDependenciesTemplates"] = blockedByDependenciesTemplates commitStatuses, lastStatus, err := pull_service.GetIssuesAllCommitStatus(ctx, issues) if err != nil { diff --git a/templates/shared/issuelist.tmpl b/templates/shared/issuelist.tmpl index 00fe09adaf0aa..98ee6346e3dbf 100644 --- a/templates/shared/issuelist.tmpl +++ b/templates/shared/issuelist.tmpl @@ -119,10 +119,10 @@ {{end}} {{template "shared/issue_dependency" (dict - "Dependencies" (index $.BlockedByDependenciesMap .ID) + "Dependencies" (index $.BlockedByDependenciesTemplates .ID) "TitleKey" "repo.issues.dependency.blocked_by_following")}} {{template "shared/issue_dependency" (dict - "Dependencies" (index $.BlockingDependenciesMap .ID) + "Dependencies" (index $.BlockingDependenciesTemplates .ID) "TitleKey" "repo.issues.dependency.blocks_following")}} {{if .IsPull}} {{$approveOfficial := call $approvalCounts .ID "approve"}} From d6acba16e8b42d91efb30ff5cd8e03333b642944 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim-Niclas=20Oelschl=C3=A4ger?= Date: Wed, 14 Feb 2024 00:21:28 +0100 Subject: [PATCH 12/33] style: better popups --- routers/web/repo/issue.go | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/routers/web/repo/issue.go b/routers/web/repo/issue.go index 17367a4b1a0f5..0741af452ec5a 100644 --- a/routers/web/repo/issue.go +++ b/routers/web/repo/issue.go @@ -43,7 +43,6 @@ import ( repo_module "code.gitea.io/gitea/modules/repository" "code.gitea.io/gitea/modules/setting" api "code.gitea.io/gitea/modules/structs" - "code.gitea.io/gitea/modules/templates" "code.gitea.io/gitea/modules/templates/vars" "code.gitea.io/gitea/modules/timeutil" "code.gitea.io/gitea/modules/util" @@ -152,12 +151,11 @@ func dependenciesToHTML(ctx *context.Context, dependencies []*issues_model.Depen if index != 0 { htmlCode += `,` } - anchorClasses := "gt-ml-2" + anchorClasses := "gt-ml-2 ref-issue" if dependency.Issue.IsClosed { anchorClasses += " gt-line-through" } - htmlCode += fmt.Sprintf(`#%d`, - dependency.Issue.Link(), dependency.Issue.Index, templates.RenderEmoji(ctx, dependency.Issue.Title), anchorClasses, dependency.Issue.Index) + htmlCode += fmt.Sprintf(`#%d`, dependency.Issue.Link(), anchorClasses, dependency.Issue.Index) } return template.HTML(htmlCode + "") From 28bca98564b554d347d53b60cd2780411395e8c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim-Niclas=20Oelschl=C3=A4ger?= Date: Wed, 14 Feb 2024 00:21:52 +0100 Subject: [PATCH 13/33] style: better selector for middot --- templates/shared/issuelist.tmpl | 2 +- web_src/css/repo/issue-list.css | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/templates/shared/issuelist.tmpl b/templates/shared/issuelist.tmpl index 98ee6346e3dbf..d4135846d753c 100644 --- a/templates/shared/issuelist.tmpl +++ b/templates/shared/issuelist.tmpl @@ -52,7 +52,7 @@
{{end}} -
+
{{if eq $.listType "dashboard"}} {{.Repo.FullName}}#{{.Index}} diff --git a/web_src/css/repo/issue-list.css b/web_src/css/repo/issue-list.css index e83c9e0a35279..ef4eef0ad98bd 100644 --- a/web_src/css/repo/issue-list.css +++ b/web_src/css/repo/issue-list.css @@ -82,7 +82,7 @@ background-color: var(--color-secondary-dark-4); } -#issue-list .flex-item-body > .flex-text-inline::before { +#issue-list .separator-container > .flex-text-inline::before { content: "•"; } From eff4d3024c68858f5c8c9b365f1aa692dfb28fb6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim-Niclas=20Oelschl=C3=A4ger?= Date: Wed, 14 Feb 2024 00:33:01 +0100 Subject: [PATCH 14/33] chore: lint fix --- models/issues/issue_list.go | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/models/issues/issue_list.go b/models/issues/issue_list.go index 8cea5f6c44156..9e6172f9207cf 100644 --- a/models/issues/issue_list.go +++ b/models/issues/issue_list.go @@ -34,6 +34,11 @@ func (issues IssueList) getRepoIDs() []int64 { return repoIDs.Values() } +// get the repoIDs from getRepoIDs as a comma-separator string like "5,7,8" +func (issues IssueList) getRepoIDsAsString() string { + return strings.Trim(strings.Join(strings.Fields(fmt.Sprint(issues.getRepoIDs())), ","), "[]") +} + // LoadRepositories loads issues' all repositories func (issues IssueList) LoadRepositories(ctx context.Context) (repo_model.RepositoryList, error) { if len(issues) == 0 { @@ -140,6 +145,10 @@ func (issues IssueList) getIssueIDs() []int64 { return ids } +func (issues IssueList) getIssueIDsAsString() string { + return strings.Trim(strings.Join(strings.Fields(fmt.Sprint(issues.getIssueIDs())), ","), "[]") +} + func (issues IssueList) loadLabels(ctx context.Context) error { if len(issues) == 0 { return nil @@ -604,16 +613,13 @@ func (issues IssueList) GetApprovalCounts(ctx context.Context) (map[int64][]*Rev func (issues IssueList) BlockingDependenciesMap(ctx context.Context) (issueDepsMap map[int64][]*DependencyInfo, err error) { var issueDeps []*DependencyInfo - issueIDsString := strings.Trim(strings.Join(strings.Fields(fmt.Sprint(issues.getIssueIDs())), ","), "[]") - repoIDsString := strings.Trim(strings.Join(strings.Fields(fmt.Sprint(issues.getRepoIDs())), ","), "[]") - err = db.GetEngine(ctx). Table("issue"). Join("INNER", "repository", "repository.id = issue.repo_id"). Join("INNER", "issue_dependency", "issue_dependency.issue_id = issue.id"). - Where(fmt.Sprintf("dependency_id IN (%s)", issueIDsString)). + Where(fmt.Sprintf("dependency_id IN (%s)", issues.getIssueIDsAsString())). // sort by repo id then created date, with the issues of the same repo at the beginning of the list - OrderBy(fmt.Sprintf("CASE WHEN issue.repo_id IN (%s) THEN 0 ELSE issue.repo_id END, issue.created_unix ASC", repoIDsString)). + OrderBy(fmt.Sprintf("CASE WHEN issue.repo_id IN (%s) THEN 0 ELSE issue.repo_id END, issue.created_unix ASC", issues.getRepoIDsAsString())). Find(&issueDeps) if err != nil { return nil, err @@ -632,16 +638,13 @@ func (issues IssueList) BlockingDependenciesMap(ctx context.Context) (issueDepsM func (issues IssueList) BlockedByDependenciesMap(ctx context.Context) (issueDepsMap map[int64][]*DependencyInfo, err error) { var issueDeps []*DependencyInfo - issueIDsString := strings.Trim(strings.Join(strings.Fields(fmt.Sprint(issues.getIssueIDs())), ","), "[]") - repoIDsString := strings.Trim(strings.Join(strings.Fields(fmt.Sprint(issues.getRepoIDs())), ","), "[]") - err = db.GetEngine(ctx). Table("issue"). Join("INNER", "repository", "repository.id = issue.repo_id"). Join("INNER", "issue_dependency", "issue_dependency.dependency_id = issue.id"). - Where(fmt.Sprintf("issue_id IN (%s)", issueIDsString)). + Where(fmt.Sprintf("issue_id IN (%s)", issues.getIssueIDsAsString())). // sort by repo id then created date, with the issues of the same repo at the beginning of the list - OrderBy(fmt.Sprintf("CASE WHEN issue.repo_id IN (%s) THEN 0 ELSE issue.repo_id END, issue.created_unix ASC", repoIDsString)). + OrderBy(fmt.Sprintf("CASE WHEN issue.repo_id IN (%s) THEN 0 ELSE issue.repo_id END, issue.created_unix ASC", issues.getRepoIDsAsString())). Find(&issueDeps) if err != nil { return nil, err From f2ae3bdc0163a8e3c48fa89b981985eff6e020e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim-Niclas=20Oelschl=C3=A4ger?= Date: Thu, 15 Feb 2024 21:22:09 +0100 Subject: [PATCH 15/33] refactor: use xorm builder --- models/issues/issue_list.go | 24 ++++++++---------------- 1 file changed, 8 insertions(+), 16 deletions(-) diff --git a/models/issues/issue_list.go b/models/issues/issue_list.go index 9e6172f9207cf..5684d3a39822e 100644 --- a/models/issues/issue_list.go +++ b/models/issues/issue_list.go @@ -6,7 +6,6 @@ package issues import ( "context" "fmt" - "strings" "code.gitea.io/gitea/models/db" project_model "code.gitea.io/gitea/models/project" @@ -34,11 +33,6 @@ func (issues IssueList) getRepoIDs() []int64 { return repoIDs.Values() } -// get the repoIDs from getRepoIDs as a comma-separator string like "5,7,8" -func (issues IssueList) getRepoIDsAsString() string { - return strings.Trim(strings.Join(strings.Fields(fmt.Sprint(issues.getRepoIDs())), ","), "[]") -} - // LoadRepositories loads issues' all repositories func (issues IssueList) LoadRepositories(ctx context.Context) (repo_model.RepositoryList, error) { if len(issues) == 0 { @@ -145,10 +139,6 @@ func (issues IssueList) getIssueIDs() []int64 { return ids } -func (issues IssueList) getIssueIDsAsString() string { - return strings.Trim(strings.Join(strings.Fields(fmt.Sprint(issues.getIssueIDs())), ","), "[]") -} - func (issues IssueList) loadLabels(ctx context.Context) error { if len(issues) == 0 { return nil @@ -617,9 +607,10 @@ func (issues IssueList) BlockingDependenciesMap(ctx context.Context) (issueDepsM Table("issue"). Join("INNER", "repository", "repository.id = issue.repo_id"). Join("INNER", "issue_dependency", "issue_dependency.issue_id = issue.id"). - Where(fmt.Sprintf("dependency_id IN (%s)", issues.getIssueIDsAsString())). - // sort by repo id then created date, with the issues of the same repo at the beginning of the list - OrderBy(fmt.Sprintf("CASE WHEN issue.repo_id IN (%s) THEN 0 ELSE issue.repo_id END, issue.created_unix ASC", issues.getRepoIDsAsString())). + Where(builder.In("dependency_id", issues.getIssueIDs())). + // sort by repo id then created date + Asc("issue.repo_id"). + Asc("issue.created_unix"). Find(&issueDeps) if err != nil { return nil, err @@ -642,9 +633,10 @@ func (issues IssueList) BlockedByDependenciesMap(ctx context.Context) (issueDeps Table("issue"). Join("INNER", "repository", "repository.id = issue.repo_id"). Join("INNER", "issue_dependency", "issue_dependency.dependency_id = issue.id"). - Where(fmt.Sprintf("issue_id IN (%s)", issues.getIssueIDsAsString())). - // sort by repo id then created date, with the issues of the same repo at the beginning of the list - OrderBy(fmt.Sprintf("CASE WHEN issue.repo_id IN (%s) THEN 0 ELSE issue.repo_id END, issue.created_unix ASC", issues.getRepoIDsAsString())). + Where(builder.In("issue_id", issues.getIssueIDs())). + // sort by repo id then created date + Asc("issue.repo_id"). + Asc("issue.created_unix"). Find(&issueDeps) if err != nil { return nil, err From 0df1b3766a4c5b0f7bc8c25df937f744cff3bd85 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim-Niclas=20Oelschl=C3=A4ger?= Date: Sat, 17 Feb 2024 17:34:47 +0100 Subject: [PATCH 16/33] enhancement: show dependencies only with enabled setting --- models/repo/repo_unit.go | 1 + modules/structs/repo.go | 3 ++ options/locale/locale_en-US.ini | 1 + routers/api/v1/repo/repo.go | 4 +++ routers/web/repo/issue.go | 43 ++++++++++++++++------------ routers/web/repo/setting/setting.go | 1 + services/convert/repository.go | 3 ++ services/forms/repo_form.go | 1 + templates/repo/settings/options.tmpl | 6 ++++ templates/shared/issuelist.tmpl | 16 +++++++---- templates/swagger/v1_json.tmpl | 9 ++++++ 11 files changed, 63 insertions(+), 25 deletions(-) diff --git a/models/repo/repo_unit.go b/models/repo/repo_unit.go index 5a841f4d312e8..291bf3772b83b 100644 --- a/models/repo/repo_unit.go +++ b/models/repo/repo_unit.go @@ -118,6 +118,7 @@ func (cfg *IssuesConfig) ToDB() ([]byte, error) { // PullRequestsConfig describes pull requests config type PullRequestsConfig struct { IgnoreWhitespaceConflicts bool + ShowDependencies bool AllowMerge bool AllowRebase bool AllowRebaseMerge bool diff --git a/modules/structs/repo.go b/modules/structs/repo.go index bc8eb0b756973..6b4b133e49786 100644 --- a/modules/structs/repo.go +++ b/modules/structs/repo.go @@ -95,6 +95,7 @@ type Repository struct { HasPackages bool `json:"has_packages"` HasActions bool `json:"has_actions"` IgnoreWhitespaceConflicts bool `json:"ignore_whitespace_conflicts"` + ShowDependencies bool `json:"show_dependencies"` AllowMerge bool `json:"allow_merge_commits"` AllowRebase bool `json:"allow_rebase"` AllowRebaseMerge bool `json:"allow_rebase_explicit"` @@ -191,6 +192,8 @@ type EditRepoOption struct { HasActions *bool `json:"has_actions,omitempty"` // either `true` to ignore whitespace for conflicts, or `false` to not ignore whitespace. IgnoreWhitespaceConflicts *bool `json:"ignore_whitespace_conflicts,omitempty"` + // either `true` to show `depends on` and `blocks` in Pull Request list, or `false` to not show them. + ShowDependencies *bool `json:"show_dependencies,omitempty"` // either `true` to allow merging pull requests with a merge commit, or `false` to prevent merging pull requests with merge commits. AllowMerge *bool `json:"allow_merge_commits,omitempty"` // either `true` to allow rebase-merging pull requests, or `false` to prevent rebase-merging. diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index 6543e85831085..7e2b6ad379fc4 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -2121,6 +2121,7 @@ settings.enable_timetracker = Enable Time Tracking settings.allow_only_contributors_to_track_time = Let Only Contributors Track Time settings.pulls_desc = Enable Repository Pull Requests settings.pulls.ignore_whitespace = Ignore Whitespace for Conflicts +settings.pulls.show_dependencies = Show depends on and blocks in Pull Requests list settings.pulls.enable_autodetect_manual_merge = Enable autodetect manual merge (Note: In some special cases, misjudgments can occur) settings.pulls.allow_rebase_update = Enable updating pull request branch by rebase settings.pulls.default_delete_branch_after_merge = Delete pull request branch after merge by default diff --git a/routers/api/v1/repo/repo.go b/routers/api/v1/repo/repo.go index 80504b9c330d4..af47f69958121 100644 --- a/routers/api/v1/repo/repo.go +++ b/routers/api/v1/repo/repo.go @@ -881,6 +881,7 @@ func updateRepoUnits(ctx *context.APIContext, opts api.EditRepoOption) error { // Unit type doesn't exist so we make a new config file with default values config = &repo_model.PullRequestsConfig{ IgnoreWhitespaceConflicts: false, + ShowDependencies: false, AllowMerge: true, AllowRebase: true, AllowRebaseMerge: true, @@ -900,6 +901,9 @@ func updateRepoUnits(ctx *context.APIContext, opts api.EditRepoOption) error { if opts.IgnoreWhitespaceConflicts != nil { config.IgnoreWhitespaceConflicts = *opts.IgnoreWhitespaceConflicts } + if opts.ShowDependencies != nil { + config.ShowDependencies = *opts.ShowDependencies + } if opts.AllowMerge != nil { config.AllowMerge = *opts.AllowMerge } diff --git a/routers/web/repo/issue.go b/routers/web/repo/issue.go index 0741af452ec5a..5e36661753eda 100644 --- a/routers/web/repo/issue.go +++ b/routers/web/repo/issue.go @@ -344,25 +344,32 @@ func issues(ctx *context.Context, milestoneID, projectID int64, isPullOption opt return } - blockingDependenciesTemplates := make(map[int64]template.HTML, len(issues)) - blockedByDependenciesTemplates := make(map[int64]template.HTML, len(issues)) + if unit, err := repo.GetUnit(ctx, unit.TypePullRequests); err == nil { + if config := unit.PullRequestsConfig(); config.ShowDependencies { + blockingDependenciesTemplates := make(map[int64]template.HTML, len(issues)) + blockedByDependenciesTemplates := make(map[int64]template.HTML, len(issues)) - blockingDependenciesMap, err := issues.BlockingDependenciesMap(ctx) - if err != nil { - ctx.ServerError("BlockingDependenciesMap", err) - return - } - for i, blockingDependencies := range blockingDependenciesMap { - blockingDependenciesTemplates[i] = dependenciesToHTML(ctx, blockingDependencies) - } + blockingDependenciesMap, err := issues.BlockingDependenciesMap(ctx) + if err != nil { + ctx.ServerError("BlockingDependenciesMap", err) + return + } + for i, blockingDependencies := range blockingDependenciesMap { + blockingDependenciesTemplates[i] = dependenciesToHTML(ctx, blockingDependencies) + } - blockedByDependenciesMap, err := issues.BlockedByDependenciesMap(ctx) - if err != nil { - ctx.ServerError("BlockedByDependenciesMap", err) - return - } - for i, blockedByDependencies := range blockedByDependenciesMap { - blockedByDependenciesTemplates[i] = dependenciesToHTML(ctx, blockedByDependencies) + blockedByDependenciesMap, err := issues.BlockedByDependenciesMap(ctx) + if err != nil { + ctx.ServerError("BlockedByDependenciesMap", err) + return + } + for i, blockedByDependencies := range blockedByDependenciesMap { + blockedByDependenciesTemplates[i] = dependenciesToHTML(ctx, blockedByDependencies) + } + + ctx.Data["BlockingDependenciesTemplates"] = blockingDependenciesTemplates + ctx.Data["BlockedByDependenciesTemplates"] = blockedByDependenciesTemplates + } } // Get posters. @@ -375,8 +382,6 @@ func issues(ctx *context.Context, milestoneID, projectID int64, isPullOption opt return } } - ctx.Data["BlockingDependenciesTemplates"] = blockingDependenciesTemplates - ctx.Data["BlockedByDependenciesTemplates"] = blockedByDependenciesTemplates commitStatuses, lastStatus, err := pull_service.GetIssuesAllCommitStatus(ctx, issues) if err != nil { diff --git a/routers/web/repo/setting/setting.go b/routers/web/repo/setting/setting.go index e045e3b8dcc09..23f2acc799dbc 100644 --- a/routers/web/repo/setting/setting.go +++ b/routers/web/repo/setting/setting.go @@ -582,6 +582,7 @@ func SettingsPost(ctx *context.Context) { Type: unit_model.TypePullRequests, Config: &repo_model.PullRequestsConfig{ IgnoreWhitespaceConflicts: form.PullsIgnoreWhitespace, + ShowDependencies: form.PullsShowDependencies, AllowMerge: form.PullsAllowMerge, AllowRebase: form.PullsAllowRebase, AllowRebaseMerge: form.PullsAllowRebaseMerge, diff --git a/services/convert/repository.go b/services/convert/repository.go index 39efd304a96ad..622538ecd5540 100644 --- a/services/convert/repository.go +++ b/services/convert/repository.go @@ -89,6 +89,7 @@ func innerToRepo(ctx context.Context, repo *repo_model.Repository, permissionInR } hasPullRequests := false ignoreWhitespaceConflicts := false + showDependencies := false allowMerge := false allowRebase := false allowRebaseMerge := false @@ -102,6 +103,7 @@ func innerToRepo(ctx context.Context, repo *repo_model.Repository, permissionInR config := unit.PullRequestsConfig() hasPullRequests = true ignoreWhitespaceConflicts = config.IgnoreWhitespaceConflicts + showDependencies = config.ShowDependencies allowMerge = config.AllowMerge allowRebase = config.AllowRebase allowRebaseMerge = config.AllowRebaseMerge @@ -221,6 +223,7 @@ func innerToRepo(ctx context.Context, repo *repo_model.Repository, permissionInR ExternalWiki: externalWiki, HasPullRequests: hasPullRequests, IgnoreWhitespaceConflicts: ignoreWhitespaceConflicts, + ShowDependencies: showDependencies, AllowMerge: allowMerge, AllowRebase: allowRebase, AllowRebaseMerge: allowRebaseMerge, diff --git a/services/forms/repo_form.go b/services/forms/repo_form.go index e45a2a1695522..fd8497ff72b2a 100644 --- a/services/forms/repo_form.go +++ b/services/forms/repo_form.go @@ -149,6 +149,7 @@ type RepoSettingForm struct { EnablePulls bool EnableActions bool PullsIgnoreWhitespace bool + PullsShowDependencies bool PullsAllowMerge bool PullsAllowRebase bool PullsAllowRebaseMerge bool diff --git a/templates/repo/settings/options.tmpl b/templates/repo/settings/options.tmpl index a699396a84b32..9bd893261c0f6 100644 --- a/templates/repo/settings/options.tmpl +++ b/templates/repo/settings/options.tmpl @@ -645,6 +645,12 @@
+
+
+ + +
+
{{end}} diff --git a/templates/shared/issuelist.tmpl b/templates/shared/issuelist.tmpl index d4135846d753c..e8286d2f3e969 100644 --- a/templates/shared/issuelist.tmpl +++ b/templates/shared/issuelist.tmpl @@ -118,12 +118,16 @@ {{end}} - {{template "shared/issue_dependency" (dict - "Dependencies" (index $.BlockedByDependenciesTemplates .ID) - "TitleKey" "repo.issues.dependency.blocked_by_following")}} - {{template "shared/issue_dependency" (dict - "Dependencies" (index $.BlockingDependenciesTemplates .ID) - "TitleKey" "repo.issues.dependency.blocks_following")}} + {{if $.BlockedByDependenciesTemplates}} + {{template "shared/issue_dependency" (dict + "Dependencies" (index $.BlockedByDependenciesTemplates .ID) + "TitleKey" "repo.issues.dependency.blocked_by_following")}} + {{end}} + {{if $.BlockingDependenciesTemplates}} + {{template "shared/issue_dependency" (dict + "Dependencies" (index $.BlockingDependenciesTemplates .ID) + "TitleKey" "repo.issues.dependency.blocks_following")}} + {{end}} {{if .IsPull}} {{$approveOfficial := call $approvalCounts .ID "approve"}} {{$rejectOfficial := call $approvalCounts .ID "reject"}} diff --git a/templates/swagger/v1_json.tmpl b/templates/swagger/v1_json.tmpl index 98198696bc40c..963d0368c9d09 100644 --- a/templates/swagger/v1_json.tmpl +++ b/templates/swagger/v1_json.tmpl @@ -19834,6 +19834,11 @@ "type": "boolean", "x-go-name": "IgnoreWhitespaceConflicts" }, + "show_dependencies": { + "description": "either `true` to show `depends on` and `blocks` in Pull Request list, or `false` to not show them.", + "type": "boolean", + "x-go-name": "ShowDependencies" + }, "internal_tracker": { "$ref": "#/definitions/InternalTracker" }, @@ -22707,6 +22712,10 @@ "type": "boolean", "x-go-name": "IgnoreWhitespaceConflicts" }, + "show_dependencies": { + "type": "boolean", + "x-go-name": "ShowDependencies" + }, "internal": { "type": "boolean", "x-go-name": "Internal" From c68b79f90dc094370eebdb71f2060f554cb43707 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim-Niclas=20Oelschl=C3=A4ger?= Date: Sat, 17 Feb 2024 17:40:28 +0100 Subject: [PATCH 17/33] chore: update swagger --- templates/swagger/v1_json.tmpl | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/templates/swagger/v1_json.tmpl b/templates/swagger/v1_json.tmpl index 963d0368c9d09..d6a603f9cd130 100644 --- a/templates/swagger/v1_json.tmpl +++ b/templates/swagger/v1_json.tmpl @@ -19834,11 +19834,6 @@ "type": "boolean", "x-go-name": "IgnoreWhitespaceConflicts" }, - "show_dependencies": { - "description": "either `true` to show `depends on` and `blocks` in Pull Request list, or `false` to not show them.", - "type": "boolean", - "x-go-name": "ShowDependencies" - }, "internal_tracker": { "$ref": "#/definitions/InternalTracker" }, @@ -19863,6 +19858,11 @@ "type": "string", "x-go-name": "ProjectsMode" }, + "show_dependencies": { + "description": "either `true` to show `depends on` and `blocks` in Pull Request list, or `false` to not show them.", + "type": "boolean", + "x-go-name": "ShowDependencies" + }, "template": { "description": "either `true` to make this repository a template or `false` to make it a normal repository", "type": "boolean", @@ -22712,10 +22712,6 @@ "type": "boolean", "x-go-name": "IgnoreWhitespaceConflicts" }, - "show_dependencies": { - "type": "boolean", - "x-go-name": "ShowDependencies" - }, "internal": { "type": "boolean", "x-go-name": "Internal" @@ -22800,6 +22796,10 @@ "repo_transfer": { "$ref": "#/definitions/RepoTransfer" }, + "show_dependencies": { + "type": "boolean", + "x-go-name": "ShowDependencies" + }, "size": { "type": "integer", "format": "int64", From c0d5bc43b890115b33c8b22bb444d8f696904874 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim-Niclas=20Oelschl=C3=A4ger?= Date: Wed, 21 Feb 2024 21:07:18 +0100 Subject: [PATCH 18/33] chore: add documentation --- models/issues/issue.go | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/models/issues/issue.go b/models/issues/issue.go index 6f2fd66a20c62..7e43f2230a8b7 100644 --- a/models/issues/issue.go +++ b/models/issues/issue.go @@ -594,11 +594,14 @@ func IsUserParticipantsOfIssue(ctx context.Context, user *user_model.User, issue } // DependencyInfo represents high level information about an issue which is a dependency of another issue. +// this type is used in func `BlockingDependenciesMap` and `BlockedByDependenciesMap` as xorm intermediet type to retrive info from joined tables type DependencyInfo struct { - Issue `xorm:"extends"` - repo_model.Repository `xorm:"extends"` - IssueID int64 `xorm:"NOT NULL"` - DependencyID int64 `xorm:"NOT NULL"` + Issue `xorm:"extends"` // an issue/pull that depend on issue_id or is blocked by issue_id. the exact usage is determined by the function using this type + repo_model.Repository `xorm:"extends"` // the repo, that owns Issue + + // fields from `IssueDependency` + IssueID int64 `xorm:"NOT NULL"` // id of the issue/pull the that is used for the selection of dependent issues + DependencyID int64 `xorm:"NOT NULL"` // id of the issue/pull the that is used for the selection of blocked issues } // GetParticipantIDsByIssue returns all userIDs who are participated in comments of an issue and issue author From f41b8fb9f1618ff38abe225442f92ce66bd77d77 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim-Niclas=20Oelschl=C3=A4ger?= Date: Wed, 21 Feb 2024 21:13:49 +0100 Subject: [PATCH 19/33] chore: corrected 'retrieve' --- models/issues/issue.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/models/issues/issue.go b/models/issues/issue.go index 7e43f2230a8b7..a87970587aea4 100644 --- a/models/issues/issue.go +++ b/models/issues/issue.go @@ -594,7 +594,7 @@ func IsUserParticipantsOfIssue(ctx context.Context, user *user_model.User, issue } // DependencyInfo represents high level information about an issue which is a dependency of another issue. -// this type is used in func `BlockingDependenciesMap` and `BlockedByDependenciesMap` as xorm intermediet type to retrive info from joined tables +// this type is used in func `BlockingDependenciesMap` and `BlockedByDependenciesMap` as xorm intermediet type to retrieve info from joined tables type DependencyInfo struct { Issue `xorm:"extends"` // an issue/pull that depend on issue_id or is blocked by issue_id. the exact usage is determined by the function using this type repo_model.Repository `xorm:"extends"` // the repo, that owns Issue From 58d7e4f3182bfda5c4d05c98947e4dba3809942b Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Thu, 22 Feb 2024 17:13:06 +0100 Subject: [PATCH 20/33] Apply suggestions from code review Co-authored-by: Lunny Xiao Co-authored-by: wxiaoguang --- models/issues/issue_list.go | 4 ++-- templates/shared/issue_dependency.tmpl | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/models/issues/issue_list.go b/models/issues/issue_list.go index 5684d3a39822e..a354a5cb1f54f 100644 --- a/models/issues/issue_list.go +++ b/models/issues/issue_list.go @@ -607,7 +607,7 @@ func (issues IssueList) BlockingDependenciesMap(ctx context.Context) (issueDepsM Table("issue"). Join("INNER", "repository", "repository.id = issue.repo_id"). Join("INNER", "issue_dependency", "issue_dependency.issue_id = issue.id"). - Where(builder.In("dependency_id", issues.getIssueIDs())). + Where(builder.In("issue_dependency.dependency_id", issues.getIssueIDs())). // sort by repo id then created date Asc("issue.repo_id"). Asc("issue.created_unix"). @@ -633,7 +633,7 @@ func (issues IssueList) BlockedByDependenciesMap(ctx context.Context) (issueDeps Table("issue"). Join("INNER", "repository", "repository.id = issue.repo_id"). Join("INNER", "issue_dependency", "issue_dependency.dependency_id = issue.id"). - Where(builder.In("issue_id", issues.getIssueIDs())). + Where(builder.In("issue_dependency.issue_id", issues.getIssueIDs())). // sort by repo id then created date Asc("issue.repo_id"). Asc("issue.created_unix"). diff --git a/templates/shared/issue_dependency.tmpl b/templates/shared/issue_dependency.tmpl index 0587b9bcb1016..a1f4d144819cc 100644 --- a/templates/shared/issue_dependency.tmpl +++ b/templates/shared/issue_dependency.tmpl @@ -1,5 +1,5 @@ {{if .Dependencies}}
- {{(ctx.Locale.Tr .TitleKey .Dependencies) | Safe}} + {{ctx.Locale.Tr .TitleKey .Dependencies}}
{{end}} From e0a4164595cf4f37d1b563563ea84254f20454bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim-Niclas=20Oelschl=C3=A4ger?= Date: Thu, 22 Feb 2024 22:33:38 +0100 Subject: [PATCH 21/33] render issue-link in go-template --- options/locale/locale_en-US.ini | 4 +-- routers/web/repo/issue.go | 35 +++----------------------- templates/shared/issue_dependency.tmpl | 6 ++++- templates/shared/issue_link.tmpl | 1 + templates/shared/issuelist.tmpl | 8 +++--- web_src/css/helpers.css | 1 + 6 files changed, 16 insertions(+), 39 deletions(-) create mode 100644 templates/shared/issue_link.tmpl diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index 7e2b6ad379fc4..c672554c316d8 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -1685,9 +1685,9 @@ issues.dependency.issue_close_blocked = You need to close all issues blocking th issues.dependency.issue_batch_close_blocked = "Cannot batch close issues that you choose, because issue #%d still has open dependencies" issues.dependency.pr_close_blocked = You need to close all issues blocking this pull request before you can merge it. issues.dependency.blocks_short = Blocks -issues.dependency.blocks_following = blocks: %s +issues.dependency.blocks_following = blocks: issues.dependency.blocked_by_short = Depends on -issues.dependency.blocked_by_following = depends on: %s +issues.dependency.blocked_by_following = depends on: issues.dependency.remove_header = Remove Dependency issues.dependency.issue_remove_text = This will remove the dependency from this issue. Continue? issues.dependency.pr_remove_text = This will remove the dependency from this pull request. Continue? diff --git a/routers/web/repo/issue.go b/routers/web/repo/issue.go index 5e36661753eda..3925dcdc0a164 100644 --- a/routers/web/repo/issue.go +++ b/routers/web/repo/issue.go @@ -43,6 +43,7 @@ import ( repo_module "code.gitea.io/gitea/modules/repository" "code.gitea.io/gitea/modules/setting" api "code.gitea.io/gitea/modules/structs" + "code.gitea.io/gitea/modules/templates" "code.gitea.io/gitea/modules/templates/vars" "code.gitea.io/gitea/modules/timeutil" "code.gitea.io/gitea/modules/util" @@ -140,27 +141,6 @@ func MustAllowPulls(ctx *context.Context) { } } -func dependenciesToHTML(ctx *context.Context, dependencies []*issues_model.DependencyInfo) template.HTML { - if len(dependencies) == 0 { - return "" - } - - htmlCode := "" - - for index, dependency := range dependencies { - if index != 0 { - htmlCode += `,` - } - anchorClasses := "gt-ml-2 ref-issue" - if dependency.Issue.IsClosed { - anchorClasses += " gt-line-through" - } - htmlCode += fmt.Sprintf(`
#%d`, dependency.Issue.Link(), anchorClasses, dependency.Issue.Index) - } - - return template.HTML(htmlCode + "") -} - func issues(ctx *context.Context, milestoneID, projectID int64, isPullOption optional.Option[bool]) { var err error viewType := ctx.FormString("type") @@ -346,29 +326,20 @@ func issues(ctx *context.Context, milestoneID, projectID int64, isPullOption opt if unit, err := repo.GetUnit(ctx, unit.TypePullRequests); err == nil { if config := unit.PullRequestsConfig(); config.ShowDependencies { - blockingDependenciesTemplates := make(map[int64]template.HTML, len(issues)) - blockedByDependenciesTemplates := make(map[int64]template.HTML, len(issues)) - blockingDependenciesMap, err := issues.BlockingDependenciesMap(ctx) if err != nil { ctx.ServerError("BlockingDependenciesMap", err) return } - for i, blockingDependencies := range blockingDependenciesMap { - blockingDependenciesTemplates[i] = dependenciesToHTML(ctx, blockingDependencies) - } blockedByDependenciesMap, err := issues.BlockedByDependenciesMap(ctx) if err != nil { ctx.ServerError("BlockedByDependenciesMap", err) return } - for i, blockedByDependencies := range blockedByDependenciesMap { - blockedByDependenciesTemplates[i] = dependenciesToHTML(ctx, blockedByDependencies) - } - ctx.Data["BlockingDependenciesTemplates"] = blockingDependenciesTemplates - ctx.Data["BlockedByDependenciesTemplates"] = blockedByDependenciesTemplates + ctx.Data["BlockingDependenciesMap"] = blockingDependenciesMap + ctx.Data["BlockedByDependenciesMap"] = blockedByDependenciesMap } } diff --git a/templates/shared/issue_dependency.tmpl b/templates/shared/issue_dependency.tmpl index a1f4d144819cc..3eef63dd797a1 100644 --- a/templates/shared/issue_dependency.tmpl +++ b/templates/shared/issue_dependency.tmpl @@ -1,5 +1,9 @@ {{if .Dependencies}}
- {{ctx.Locale.Tr .TitleKey .Dependencies}} + {{ctx.Locale.Tr .TitleKey}} + {{range $i, $dependency := .Dependencies}} + {{if gt $i 0}}, {{end}} + {{template "shared/issue_link" $dependency.Issue}} + {{end}}
{{end}} diff --git a/templates/shared/issue_link.tmpl b/templates/shared/issue_link.tmpl new file mode 100644 index 0000000000000..db6de4fbea2ce --- /dev/null +++ b/templates/shared/issue_link.tmpl @@ -0,0 +1 @@ +#{{.Index}} \ No newline at end of file diff --git a/templates/shared/issuelist.tmpl b/templates/shared/issuelist.tmpl index e8286d2f3e969..fb8a4432ec3a2 100644 --- a/templates/shared/issuelist.tmpl +++ b/templates/shared/issuelist.tmpl @@ -118,14 +118,14 @@ {{end}} - {{if $.BlockedByDependenciesTemplates}} + {{if $.BlockedByDependenciesMap}} {{template "shared/issue_dependency" (dict - "Dependencies" (index $.BlockedByDependenciesTemplates .ID) + "Dependencies" (index $.BlockedByDependenciesMap .ID) "TitleKey" "repo.issues.dependency.blocked_by_following")}} {{end}} - {{if $.BlockingDependenciesTemplates}} + {{if $.BlockingDependenciesMap}} {{template "shared/issue_dependency" (dict - "Dependencies" (index $.BlockingDependenciesTemplates .ID) + "Dependencies" (index $.BlockingDependenciesMap .ID) "TitleKey" "repo.issues.dependency.blocks_following")}} {{end}} {{if .IsPull}} diff --git a/web_src/css/helpers.css b/web_src/css/helpers.css index 76077718a4680..ebe2e80b0ab49 100644 --- a/web_src/css/helpers.css +++ b/web_src/css/helpers.css @@ -103,6 +103,7 @@ Gitea's private styles use `g-` prefix. .gt-m-4 { margin: 1rem !important; } .gt-m-5 { margin: 2rem !important; } +.gt-ml--2 { margin-left: -.25rem !important; } .gt-ml-0 { margin-left: 0 !important; } .gt-ml-1 { margin-left: .125rem !important; } .gt-ml-2 { margin-left: .25rem !important; } From 9e331751ff72100a0cc583b6100b6fe94a4ac73e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim-Niclas=20Oelschl=C3=A4ger?= Date: Sat, 24 Feb 2024 00:25:00 +0100 Subject: [PATCH 22/33] add final new line --- templates/shared/issue_link.tmpl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/shared/issue_link.tmpl b/templates/shared/issue_link.tmpl index db6de4fbea2ce..01e333a8f16c6 100644 --- a/templates/shared/issue_link.tmpl +++ b/templates/shared/issue_link.tmpl @@ -1 +1 @@ -#{{.Index}} \ No newline at end of file +#{{.Index}} From c2d67ff0884972a2ee8094777087993952c74fa8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim-Niclas=20Oelschl=C3=A4ger?= Date: Sun, 25 Feb 2024 23:24:07 +0100 Subject: [PATCH 23/33] added test --- models/fixtures/issue_dependency.yml | 41 ++++++++++++++++++ models/issues/issue_list_test.go | 64 ++++++++++++++++++++++++++++ 2 files changed, 105 insertions(+) create mode 100644 models/fixtures/issue_dependency.yml diff --git a/models/fixtures/issue_dependency.yml b/models/fixtures/issue_dependency.yml new file mode 100644 index 0000000000000..aa610716f6c29 --- /dev/null +++ b/models/fixtures/issue_dependency.yml @@ -0,0 +1,41 @@ +- + id: 1 + user_id: 1 + issue_id: 1 + dependency_id: 2 + +- + id: 2 + user_id: 1 + issue_id: 1 + dependency_id: 3 + +- + id: 3 + user_id: 1 + issue_id: 1 + dependency_id: 4 + +- + id: 4 + user_id: 1 + issue_id: 3 + dependency_id: 1 + +- + id: 5 + user_id: 1 + issue_id: 3 + dependency_id: 2 + +- + id: 6 + user_id: 1 + issue_id: 4 + dependency_id: 1 + +- + id: 7 + user_id: 1 + issue_id: 4 + dependency_id: 2 diff --git a/models/issues/issue_list_test.go b/models/issues/issue_list_test.go index 9069e1012da53..1245fbc62346a 100644 --- a/models/issues/issue_list_test.go +++ b/models/issues/issue_list_test.go @@ -73,3 +73,67 @@ func TestIssueList_LoadAttributes(t *testing.T) { } } } + +func TestIssueList_BlockingDependenciesMap(t *testing.T) { + assert.NoError(t, unittest.PrepareTestDatabase()) + setting.Service.EnableTimetracking = true + issueList := issues_model.IssueList{ + unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 1}), + unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 4}), + } + + blockingDependenciesMap, err := issueList.BlockingDependenciesMap(db.DefaultContext) + assert.NoError(t, err) + assert.Len(t, blockingDependenciesMap, 2) + + issue1DepInfos := blockingDependenciesMap[1] + assert.Len(t, issue1DepInfos, 2) + issue2DepInfos := blockingDependenciesMap[2] + assert.Nil(t, issue2DepInfos) + issue3DepInfos := blockingDependenciesMap[3] + assert.Nil(t, issue3DepInfos) + issue4DepInfos := blockingDependenciesMap[4] + assert.Len(t, issue4DepInfos, 1) + + for _, depInfo := range issue1DepInfos { + assert.Equal(t, int64(1), depInfo.DependencyID) + assert.Contains(t, [3]int64{3, 4}, depInfo.Issue.ID) + } + + for _, depInfo := range issue4DepInfos { + assert.Equal(t, int64(4), depInfo.DependencyID) + assert.Equal(t, int64(1), depInfo.Issue.ID) + } +} + +func TestIssueList_BlockedByDependenciesMap(t *testing.T) { + assert.NoError(t, unittest.PrepareTestDatabase()) + setting.Service.EnableTimetracking = true + issueList := issues_model.IssueList{ + unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 1}), + unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 4}), + } + + blockedByDependenciesMap, err := issueList.BlockedByDependenciesMap(db.DefaultContext) + assert.NoError(t, err) + assert.Len(t, blockedByDependenciesMap, 2) + + issue1DepInfos := blockedByDependenciesMap[1] + assert.Len(t, issue1DepInfos, 3) + issue2DepInfos := blockedByDependenciesMap[2] + assert.Nil(t, issue2DepInfos) + issue3DepInfos := blockedByDependenciesMap[3] + assert.Nil(t, issue3DepInfos) + issue4DepInfos := blockedByDependenciesMap[4] + assert.Len(t, issue4DepInfos, 2) + + for _, depInfo := range issue1DepInfos { + assert.Equal(t, int64(1), depInfo.IssueID) + assert.Contains(t, [3]int64{2, 3, 4}, depInfo.Issue.ID) + } + + for _, depInfo := range issue4DepInfos { + assert.Equal(t, int64(4), depInfo.IssueID) + assert.Contains(t, [3]int64{1, 2}, depInfo.Issue.ID) + } +} From fa199e7e592e918c5bd83020395d2b3a0bb28440 Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Mon, 26 Feb 2024 03:07:11 +0100 Subject: [PATCH 24/33] move issues to be tested into higher range --- models/fixtures/issue_dependency.yml | 42 ++------ models/issues/issue_list_test.go | 152 ++++++++++++++++++++------- 2 files changed, 121 insertions(+), 73 deletions(-) diff --git a/models/fixtures/issue_dependency.yml b/models/fixtures/issue_dependency.yml index aa610716f6c29..1c43932745acc 100644 --- a/models/fixtures/issue_dependency.yml +++ b/models/fixtures/issue_dependency.yml @@ -1,41 +1,17 @@ - id: 1 - user_id: 1 - issue_id: 1 - dependency_id: 2 + user_id: 40 + issue_id: 21 + dependency_id: 20 - id: 2 - user_id: 1 - issue_id: 1 - dependency_id: 3 + user_id: 40 + issue_id: 21 + dependency_id: 22 - id: 3 - user_id: 1 - issue_id: 1 - dependency_id: 4 - -- - id: 4 - user_id: 1 - issue_id: 3 - dependency_id: 1 - -- - id: 5 - user_id: 1 - issue_id: 3 - dependency_id: 2 - -- - id: 6 - user_id: 1 - issue_id: 4 - dependency_id: 1 - -- - id: 7 - user_id: 1 - issue_id: 4 - dependency_id: 2 + user_id: 40 + issue_id: 20 + dependency_id: 22 diff --git a/models/issues/issue_list_test.go b/models/issues/issue_list_test.go index 1245fbc62346a..3dbd907794291 100644 --- a/models/issues/issue_list_test.go +++ b/models/issues/issue_list_test.go @@ -4,10 +4,13 @@ package issues_test import ( + "cmp" + "slices" "testing" "code.gitea.io/gitea/models/db" issues_model "code.gitea.io/gitea/models/issues" + repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/models/unittest" "code.gitea.io/gitea/modules/setting" @@ -76,64 +79,133 @@ func TestIssueList_LoadAttributes(t *testing.T) { func TestIssueList_BlockingDependenciesMap(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) - setting.Service.EnableTimetracking = true issueList := issues_model.IssueList{ - unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 1}), - unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 4}), + unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 20}), + unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 21}), + unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 22}), } blockingDependenciesMap, err := issueList.BlockingDependenciesMap(db.DefaultContext) assert.NoError(t, err) - assert.Len(t, blockingDependenciesMap, 2) - - issue1DepInfos := blockingDependenciesMap[1] - assert.Len(t, issue1DepInfos, 2) - issue2DepInfos := blockingDependenciesMap[2] - assert.Nil(t, issue2DepInfos) - issue3DepInfos := blockingDependenciesMap[3] - assert.Nil(t, issue3DepInfos) - issue4DepInfos := blockingDependenciesMap[4] - assert.Len(t, issue4DepInfos, 1) - - for _, depInfo := range issue1DepInfos { - assert.Equal(t, int64(1), depInfo.DependencyID) - assert.Contains(t, [3]int64{3, 4}, depInfo.Issue.ID) + if assert.Len(t, blockingDependenciesMap, 2) { + var keys []int64 + for k := range blockingDependenciesMap { + keys = append(keys, k) + } + slices.Sort(keys) + assert.EqualValues(t, []int64{20, 22}, keys) + + if assert.Len(t, blockingDependenciesMap[20], 1) { + expectIssuesDependencyInfo(t, + &issues_model.DependencyInfo{ + IssueID: 21, + DependencyID: 20, + Issue: issues_model.Issue{ID: 21}, + Repository: repo_model.Repository{ID: 60}, + }, + blockingDependenciesMap[20][0]) + } + if assert.Len(t, blockingDependenciesMap[22], 2) { + list := sortIssuesDependencyInfos(blockingDependenciesMap[22]) + expectIssuesDependencyInfo(t, &issues_model.DependencyInfo{ + IssueID: 20, + DependencyID: 22, + Issue: issues_model.Issue{ID: 20}, + Repository: repo_model.Repository{ID: 23}, + }, list[0]) + expectIssuesDependencyInfo(t, &issues_model.DependencyInfo{ + IssueID: 21, + DependencyID: 22, + Issue: issues_model.Issue{ID: 21}, + Repository: repo_model.Repository{ID: 60}, + }, list[1]) + } } - for _, depInfo := range issue4DepInfos { - assert.Equal(t, int64(4), depInfo.DependencyID) - assert.Equal(t, int64(1), depInfo.Issue.ID) + issueList = issues_model.IssueList{ + unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 21}), + unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 22}), } + + blockingDependenciesMap, err = issueList.BlockingDependenciesMap(db.DefaultContext) + assert.NoError(t, err) + assert.Len(t, blockingDependenciesMap, 1) + assert.Len(t, blockingDependenciesMap[22], 2) } func TestIssueList_BlockedByDependenciesMap(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) - setting.Service.EnableTimetracking = true issueList := issues_model.IssueList{ - unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 1}), - unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 4}), + unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 20}), + unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 21}), + unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 22}), } blockedByDependenciesMap, err := issueList.BlockedByDependenciesMap(db.DefaultContext) assert.NoError(t, err) - assert.Len(t, blockedByDependenciesMap, 2) - - issue1DepInfos := blockedByDependenciesMap[1] - assert.Len(t, issue1DepInfos, 3) - issue2DepInfos := blockedByDependenciesMap[2] - assert.Nil(t, issue2DepInfos) - issue3DepInfos := blockedByDependenciesMap[3] - assert.Nil(t, issue3DepInfos) - issue4DepInfos := blockedByDependenciesMap[4] - assert.Len(t, issue4DepInfos, 2) - - for _, depInfo := range issue1DepInfos { - assert.Equal(t, int64(1), depInfo.IssueID) - assert.Contains(t, [3]int64{2, 3, 4}, depInfo.Issue.ID) + if assert.Len(t, blockedByDependenciesMap, 2) { + var keys []int64 + for k := range blockedByDependenciesMap { + keys = append(keys, k) + } + slices.Sort(keys) + assert.EqualValues(t, []int64{20, 21}, keys) + + if assert.Len(t, blockedByDependenciesMap[20], 1) { + expectIssuesDependencyInfo(t, + &issues_model.DependencyInfo{ + IssueID: 20, + DependencyID: 22, + Issue: issues_model.Issue{ID: 22}, + Repository: repo_model.Repository{ID: 61}, + }, + blockedByDependenciesMap[20][0]) + } + if assert.Len(t, blockedByDependenciesMap[21], 2) { + list := sortIssuesDependencyInfos(blockedByDependenciesMap[21]) + expectIssuesDependencyInfo(t, &issues_model.DependencyInfo{ + IssueID: 21, + DependencyID: 20, + Issue: issues_model.Issue{ID: 20}, + Repository: repo_model.Repository{ID: 23}, + }, list[0]) + expectIssuesDependencyInfo(t, &issues_model.DependencyInfo{ + IssueID: 21, + DependencyID: 22, + Issue: issues_model.Issue{ID: 22}, + Repository: repo_model.Repository{ID: 61}, + }, list[1]) + } } - for _, depInfo := range issue4DepInfos { - assert.Equal(t, int64(4), depInfo.IssueID) - assert.Contains(t, [3]int64{1, 2}, depInfo.Issue.ID) + issueList = issues_model.IssueList{ + unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 21}), + unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 22}), } + + blockedByDependenciesMap, err = issueList.BlockedByDependenciesMap(db.DefaultContext) + assert.NoError(t, err) + assert.Len(t, blockedByDependenciesMap, 1) + assert.Len(t, blockedByDependenciesMap[21], 2) +} + +func expectIssuesDependencyInfo(t *testing.T, expect, got *issues_model.DependencyInfo) { + if expect == nil { + assert.Nil(t, got) + return + } + if !assert.NotNil(t, got) { + return + } + assert.EqualValues(t, expect.DependencyID, got.DependencyID, "DependencyID") + assert.EqualValues(t, expect.IssueID, got.IssueID, "IssueID") + assert.EqualValues(t, expect.Issue.ID, got.Issue.ID, "RelatedIssueID") + assert.EqualValues(t, expect.Repository.ID, got.Repository.ID, "RelatedIssueRepoID") +} + +func sortIssuesDependencyInfos(in []*issues_model.DependencyInfo) []*issues_model.DependencyInfo { + slices.SortFunc(in, func(a, b *issues_model.DependencyInfo) int { + return cmp.Compare(a.DependencyID, b.DependencyID) + }) + return in } From 2994945eb41c8a77c15349eead7b778abf9258b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim-Niclas=20Oelschl=C3=A4ger?= Date: Mon, 4 Mar 2024 22:48:16 +0100 Subject: [PATCH 25/33] use tailwind --- templates/shared/issue_dependency.tmpl | 2 +- templates/shared/issue_link.tmpl | 2 +- web_src/css/helpers.css | 2 -- 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/templates/shared/issue_dependency.tmpl b/templates/shared/issue_dependency.tmpl index 3eef63dd797a1..df57f83881d39 100644 --- a/templates/shared/issue_dependency.tmpl +++ b/templates/shared/issue_dependency.tmpl @@ -2,7 +2,7 @@
{{ctx.Locale.Tr .TitleKey}} {{range $i, $dependency := .Dependencies}} - {{if gt $i 0}}, {{end}} + {{if gt $i 0}}, {{end}} {{template "shared/issue_link" $dependency.Issue}} {{end}}
diff --git a/templates/shared/issue_link.tmpl b/templates/shared/issue_link.tmpl index 01e333a8f16c6..b3f7528a1f87b 100644 --- a/templates/shared/issue_link.tmpl +++ b/templates/shared/issue_link.tmpl @@ -1 +1 @@ -#{{.Index}} +#{{.Index}} diff --git a/web_src/css/helpers.css b/web_src/css/helpers.css index ebe2e80b0ab49..dad0f9b127b18 100644 --- a/web_src/css/helpers.css +++ b/web_src/css/helpers.css @@ -49,7 +49,6 @@ Gitea's private styles use `g-` prefix. /* below class names match Tailwind CSS */ .gt-object-contain { object-fit: contain !important; } .gt-no-underline { text-decoration-line: none !important; } -.gt-line-through { text-decoration-line: line-through !important; } .gt-normal-case { text-transform: none !important; } .gt-italic { font-style: italic !important; } @@ -103,7 +102,6 @@ Gitea's private styles use `g-` prefix. .gt-m-4 { margin: 1rem !important; } .gt-m-5 { margin: 2rem !important; } -.gt-ml--2 { margin-left: -.25rem !important; } .gt-ml-0 { margin-left: 0 !important; } .gt-ml-1 { margin-left: .125rem !important; } .gt-ml-2 { margin-left: .25rem !important; } From 86b10061f43f75de9928d9b37554dd6f708db8fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim-Niclas=20Oelschl=C3=A4ger?= Date: Mon, 4 Mar 2024 22:52:01 +0100 Subject: [PATCH 26/33] remove option from api --- modules/structs/repo.go | 3 --- routers/api/v1/repo/repo.go | 4 ---- services/convert/repository.go | 3 --- templates/swagger/v1_json.tmpl | 14 -------------- 4 files changed, 24 deletions(-) diff --git a/modules/structs/repo.go b/modules/structs/repo.go index 6b4b133e49786..bc8eb0b756973 100644 --- a/modules/structs/repo.go +++ b/modules/structs/repo.go @@ -95,7 +95,6 @@ type Repository struct { HasPackages bool `json:"has_packages"` HasActions bool `json:"has_actions"` IgnoreWhitespaceConflicts bool `json:"ignore_whitespace_conflicts"` - ShowDependencies bool `json:"show_dependencies"` AllowMerge bool `json:"allow_merge_commits"` AllowRebase bool `json:"allow_rebase"` AllowRebaseMerge bool `json:"allow_rebase_explicit"` @@ -192,8 +191,6 @@ type EditRepoOption struct { HasActions *bool `json:"has_actions,omitempty"` // either `true` to ignore whitespace for conflicts, or `false` to not ignore whitespace. IgnoreWhitespaceConflicts *bool `json:"ignore_whitespace_conflicts,omitempty"` - // either `true` to show `depends on` and `blocks` in Pull Request list, or `false` to not show them. - ShowDependencies *bool `json:"show_dependencies,omitempty"` // either `true` to allow merging pull requests with a merge commit, or `false` to prevent merging pull requests with merge commits. AllowMerge *bool `json:"allow_merge_commits,omitempty"` // either `true` to allow rebase-merging pull requests, or `false` to prevent rebase-merging. diff --git a/routers/api/v1/repo/repo.go b/routers/api/v1/repo/repo.go index af47f69958121..80504b9c330d4 100644 --- a/routers/api/v1/repo/repo.go +++ b/routers/api/v1/repo/repo.go @@ -881,7 +881,6 @@ func updateRepoUnits(ctx *context.APIContext, opts api.EditRepoOption) error { // Unit type doesn't exist so we make a new config file with default values config = &repo_model.PullRequestsConfig{ IgnoreWhitespaceConflicts: false, - ShowDependencies: false, AllowMerge: true, AllowRebase: true, AllowRebaseMerge: true, @@ -901,9 +900,6 @@ func updateRepoUnits(ctx *context.APIContext, opts api.EditRepoOption) error { if opts.IgnoreWhitespaceConflicts != nil { config.IgnoreWhitespaceConflicts = *opts.IgnoreWhitespaceConflicts } - if opts.ShowDependencies != nil { - config.ShowDependencies = *opts.ShowDependencies - } if opts.AllowMerge != nil { config.AllowMerge = *opts.AllowMerge } diff --git a/services/convert/repository.go b/services/convert/repository.go index 622538ecd5540..39efd304a96ad 100644 --- a/services/convert/repository.go +++ b/services/convert/repository.go @@ -89,7 +89,6 @@ func innerToRepo(ctx context.Context, repo *repo_model.Repository, permissionInR } hasPullRequests := false ignoreWhitespaceConflicts := false - showDependencies := false allowMerge := false allowRebase := false allowRebaseMerge := false @@ -103,7 +102,6 @@ func innerToRepo(ctx context.Context, repo *repo_model.Repository, permissionInR config := unit.PullRequestsConfig() hasPullRequests = true ignoreWhitespaceConflicts = config.IgnoreWhitespaceConflicts - showDependencies = config.ShowDependencies allowMerge = config.AllowMerge allowRebase = config.AllowRebase allowRebaseMerge = config.AllowRebaseMerge @@ -223,7 +221,6 @@ func innerToRepo(ctx context.Context, repo *repo_model.Repository, permissionInR ExternalWiki: externalWiki, HasPullRequests: hasPullRequests, IgnoreWhitespaceConflicts: ignoreWhitespaceConflicts, - ShowDependencies: showDependencies, AllowMerge: allowMerge, AllowRebase: allowRebase, AllowRebaseMerge: allowRebaseMerge, diff --git a/templates/swagger/v1_json.tmpl b/templates/swagger/v1_json.tmpl index d6a603f9cd130..6fe505c71a2b9 100644 --- a/templates/swagger/v1_json.tmpl +++ b/templates/swagger/v1_json.tmpl @@ -19853,16 +19853,6 @@ "type": "boolean", "x-go-name": "Private" }, - "projects_mode": { - "description": "`repo` to only allow repo-level projects, `owner` to only allow owner projects, `all` to allow both.", - "type": "string", - "x-go-name": "ProjectsMode" - }, - "show_dependencies": { - "description": "either `true` to show `depends on` and `blocks` in Pull Request list, or `false` to not show them.", - "type": "boolean", - "x-go-name": "ShowDependencies" - }, "template": { "description": "either `true` to make this repository a template or `false` to make it a normal repository", "type": "boolean", @@ -22796,10 +22786,6 @@ "repo_transfer": { "$ref": "#/definitions/RepoTransfer" }, - "show_dependencies": { - "type": "boolean", - "x-go-name": "ShowDependencies" - }, "size": { "type": "integer", "format": "int64", From 899f5adf5e1a472665c6bf58a41943d5f03b8a76 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim-Niclas=20Oelschl=C3=A4ger?= Date: Mon, 4 Mar 2024 23:05:45 +0100 Subject: [PATCH 27/33] chore: typo fix --- models/issues/issue.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/models/issues/issue.go b/models/issues/issue.go index a87970587aea4..faaa85def7737 100644 --- a/models/issues/issue.go +++ b/models/issues/issue.go @@ -594,7 +594,7 @@ func IsUserParticipantsOfIssue(ctx context.Context, user *user_model.User, issue } // DependencyInfo represents high level information about an issue which is a dependency of another issue. -// this type is used in func `BlockingDependenciesMap` and `BlockedByDependenciesMap` as xorm intermediet type to retrieve info from joined tables +// this type is used in func `BlockingDependenciesMap` and `BlockedByDependenciesMap` as xorm intermediate type to retrieve info from joined tables type DependencyInfo struct { Issue `xorm:"extends"` // an issue/pull that depend on issue_id or is blocked by issue_id. the exact usage is determined by the function using this type repo_model.Repository `xorm:"extends"` // the repo, that owns Issue From 40c33430163303039d7ab5b66af61756db2c8cf9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim-Niclas=20Oelschl=C3=A4ger?= Date: Mon, 4 Mar 2024 23:06:31 +0100 Subject: [PATCH 28/33] remove join --- models/issues/issue_list.go | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/models/issues/issue_list.go b/models/issues/issue_list.go index a354a5cb1f54f..c68235d24a3f2 100644 --- a/models/issues/issue_list.go +++ b/models/issues/issue_list.go @@ -605,12 +605,11 @@ func (issues IssueList) BlockingDependenciesMap(ctx context.Context) (issueDepsM err = db.GetEngine(ctx). Table("issue"). - Join("INNER", "repository", "repository.id = issue.repo_id"). Join("INNER", "issue_dependency", "issue_dependency.issue_id = issue.id"). Where(builder.In("issue_dependency.dependency_id", issues.getIssueIDs())). - // sort by repo id then created date - Asc("issue.repo_id"). - Asc("issue.created_unix"). + // sort by repo id then index + Asc("`issue`.`repo_id`"). + Asc("`issue`.`index`"). Find(&issueDeps) if err != nil { return nil, err @@ -631,12 +630,11 @@ func (issues IssueList) BlockedByDependenciesMap(ctx context.Context) (issueDeps err = db.GetEngine(ctx). Table("issue"). - Join("INNER", "repository", "repository.id = issue.repo_id"). Join("INNER", "issue_dependency", "issue_dependency.dependency_id = issue.id"). Where(builder.In("issue_dependency.issue_id", issues.getIssueIDs())). - // sort by repo id then created date - Asc("issue.repo_id"). - Asc("issue.created_unix"). + // sort by repo id then index + Asc("`issue`.`repo_id`"). + Asc("`issue`.`index`"). Find(&issueDeps) if err != nil { return nil, err From 611cccb471b83b39db73e32f39472c193d79740b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim-Niclas=20Oelschl=C3=A4ger?= Date: Mon, 4 Mar 2024 23:25:46 +0100 Subject: [PATCH 29/33] added index to IssueDependency --- models/issues/dependency.go | 4 ++-- models/migrations/migrations.go | 2 ++ models/migrations/v1_22/v291.go | 17 +++++++++++++++++ 3 files changed, 21 insertions(+), 2 deletions(-) create mode 100644 models/migrations/v1_22/v291.go diff --git a/models/issues/dependency.go b/models/issues/dependency.go index 146dd1887dae4..842556c2dc960 100644 --- a/models/issues/dependency.go +++ b/models/issues/dependency.go @@ -107,8 +107,8 @@ func (err ErrUnknownDependencyType) Unwrap() error { type IssueDependency struct { ID int64 `xorm:"pk autoincr"` UserID int64 `xorm:"NOT NULL"` - IssueID int64 `xorm:"UNIQUE(issue_dependency) NOT NULL"` - DependencyID int64 `xorm:"UNIQUE(issue_dependency) NOT NULL"` + IssueID int64 `xorm:"UNIQUE(issue_dependency) NOT NULL index"` + DependencyID int64 `xorm:"UNIQUE(issue_dependency) NOT NULL index"` CreatedUnix timeutil.TimeStamp `xorm:"created"` UpdatedUnix timeutil.TimeStamp `xorm:"updated"` } diff --git a/models/migrations/migrations.go b/models/migrations/migrations.go index ce77432db4810..0a21308f5fd75 100644 --- a/models/migrations/migrations.go +++ b/models/migrations/migrations.go @@ -566,6 +566,8 @@ var migrations = []Migration{ NewMigration("Add default_wiki_branch to repository table", v1_22.AddDefaultWikiBranch), // v290 -> v291 NewMigration("Add PayloadVersion to HookTask", v1_22.AddPayloadVersionToHookTaskTable), + // v291 -> v292 + NewMigration("Add indecies to IssueDependency", v1_22.AddIndeciesToIssueDepencencies), } // GetCurrentDBVersion returns the current db version diff --git a/models/migrations/v1_22/v291.go b/models/migrations/v1_22/v291.go new file mode 100644 index 0000000000000..69a74bea4c98b --- /dev/null +++ b/models/migrations/v1_22/v291.go @@ -0,0 +1,17 @@ +// Copyright 2024 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package v1_22 //nolint + +import ( + "xorm.io/xorm" +) + +func AddIndeciesToIssueDepencencies(x *xorm.Engine) error { + type IssueDependency struct { + IssueID int64 `xorm:"UNIQUE(issue_dependency) NOT NULL index"` + DependencyID int64 `xorm:"UNIQUE(issue_dependency) NOT NULL index"` + } + + return x.Sync(&IssueDependency{}) +} From 44cd71879d0ec67b22b35e9f6cecfef97f7f5744 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Fri, 8 Mar 2024 19:23:33 +0800 Subject: [PATCH 30/33] Fix swagger --- templates/swagger/v1_json.tmpl | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/templates/swagger/v1_json.tmpl b/templates/swagger/v1_json.tmpl index 6fe505c71a2b9..98198696bc40c 100644 --- a/templates/swagger/v1_json.tmpl +++ b/templates/swagger/v1_json.tmpl @@ -19853,6 +19853,11 @@ "type": "boolean", "x-go-name": "Private" }, + "projects_mode": { + "description": "`repo` to only allow repo-level projects, `owner` to only allow owner projects, `all` to allow both.", + "type": "string", + "x-go-name": "ProjectsMode" + }, "template": { "description": "either `true` to make this repository a template or `false` to make it a normal repository", "type": "boolean", From c897fe6a4a926850d46b044e818b5f452d2bc223 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim-Niclas=20Oelschl=C3=A4ger?= Date: Mon, 1 Apr 2024 16:34:34 +0200 Subject: [PATCH 31/33] improv: removed unused --- .../img/webpack/jquery.minicolors.0e614115.png | Bin 68627 -> 0 bytes templates/shared/issuelist.tmpl | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) delete mode 100644 public/assets/img/webpack/jquery.minicolors.0e614115.png diff --git a/public/assets/img/webpack/jquery.minicolors.0e614115.png b/public/assets/img/webpack/jquery.minicolors.0e614115.png deleted file mode 100644 index bccc2012af78a0358e893f004e0207cac8f9e642..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 68627 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 {{end}} -
+
{{if eq $.listType "dashboard"}} {{.Repo.FullName}}#{{.Index}} From 6ed8925b27f6db51090917e40f16798bdc524f1b Mon Sep 17 00:00:00 2001 From: silverwind Date: Thu, 4 Apr 2024 21:47:21 +0200 Subject: [PATCH 32/33] add svgs --- public/assets/img/svg/gitea-issue-dependency.svg | 1 + public/assets/img/svg/gitea-issue-dependent.svg | 1 + web_src/svg/gitea-issue-dependency.svg | 1 + web_src/svg/gitea-issue-dependent.svg | 1 + 4 files changed, 4 insertions(+) create mode 100644 public/assets/img/svg/gitea-issue-dependency.svg create mode 100644 public/assets/img/svg/gitea-issue-dependent.svg create mode 100644 web_src/svg/gitea-issue-dependency.svg create mode 100644 web_src/svg/gitea-issue-dependent.svg diff --git a/public/assets/img/svg/gitea-issue-dependency.svg b/public/assets/img/svg/gitea-issue-dependency.svg new file mode 100644 index 0000000000000..82864deb6443c --- /dev/null +++ b/public/assets/img/svg/gitea-issue-dependency.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/assets/img/svg/gitea-issue-dependent.svg b/public/assets/img/svg/gitea-issue-dependent.svg new file mode 100644 index 0000000000000..9a367acfbc707 --- /dev/null +++ b/public/assets/img/svg/gitea-issue-dependent.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/web_src/svg/gitea-issue-dependency.svg b/web_src/svg/gitea-issue-dependency.svg new file mode 100644 index 0000000000000..de88a40c7873d --- /dev/null +++ b/web_src/svg/gitea-issue-dependency.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/web_src/svg/gitea-issue-dependent.svg b/web_src/svg/gitea-issue-dependent.svg new file mode 100644 index 0000000000000..558d169f99ecb --- /dev/null +++ b/web_src/svg/gitea-issue-dependent.svg @@ -0,0 +1 @@ + \ No newline at end of file From f40747596a7439ae25b667a732da10e24a10defd Mon Sep 17 00:00:00 2001 From: silverwind Date: Sun, 14 Apr 2024 15:43:46 +0200 Subject: [PATCH 33/33] Update templates/shared/issue_link.tmpl --- templates/shared/issue_link.tmpl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/shared/issue_link.tmpl b/templates/shared/issue_link.tmpl index b3f7528a1f87b..c8af8fc50b205 100644 --- a/templates/shared/issue_link.tmpl +++ b/templates/shared/issue_link.tmpl @@ -1 +1 @@ -#{{.Index}} +#{{.Index}}