Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
162 commits
Select commit Hold shift + click to select a range
7bd027d
Added multi-project feature
tyroneyeh May 7, 2025
e80998b
Fix board column move issue
tyroneyeh May 7, 2025
840547b
Try fixing your unit tests
tyroneyeh May 7, 2025
eec10ef
Fix lint issue
tyroneyeh May 7, 2025
fa8aa1f
Fix add issue to multiple project issue
tyroneyeh May 7, 2025
f9b9764
Remove lessuse code
tyroneyeh May 7, 2025
84fea9e
Fix search empty issue
tyroneyeh May 7, 2025
2cb192a
Improject filter search method
tyroneyeh May 7, 2025
4354282
Try fix unit test fail
tyroneyeh May 7, 2025
ac596ca
Try fix unit test issue
tyroneyeh May 7, 2025
d5589b1
Fix no project filter list issue
tyroneyeh May 8, 2025
0da6922
Fix unit test on TestIssueList_LoadAttributes
tyroneyeh May 8, 2025
81bca6b
Fix unit test on Test_Projects fail
tyroneyeh May 8, 2025
0e3b695
Fix unit test on TestIssueLoadAttributes
tyroneyeh May 8, 2025
09db740
Adjuect from Cols to Select
tyroneyeh May 8, 2025
241cfdb
Improve adjust A to B project bug
tyroneyeh May 9, 2025
756c707
Improve string join code for projects id
tyroneyeh May 9, 2025
71b3d85
Fix on issue list clear project issue
tyroneyeh May 9, 2025
009a59d
Change db.Exec to Update method
tyroneyeh May 9, 2025
bdb89fe
Rename LoadProject to LoadProjects
tyroneyeh May 11, 2025
ed705b5
Rename variable projectIDs to ids
tyroneyeh May 12, 2025
90bedc8
Revert "Rename variable projectIDs to ids"
tyroneyeh May 12, 2025
17540f9
Fix creating new issue redirect link from project
tyroneyeh May 26, 2025
50daa7e
Fix LoadProjects function
tyroneyeh May 26, 2025
1d70bd6
Modify indexer json field name
tyroneyeh Jul 23, 2025
cd77e20
Fix unit test
tyroneyeh Jul 24, 2025
655d855
Fix lint check error
tyroneyeh Jul 24, 2025
4b2717f
Fix unit test
tyroneyeh Jul 24, 2025
c925709
[FIX] check to make sure SQL returns result, and without error
icyavocado Feb 14, 2026
de8c732
[FIX] Refactor projectIDs method to handle project retrieval and erro…
icyavocado Feb 14, 2026
faaaa7a
[FIX] Rename variable for clarity in IssueAssignOrRemoveProject function
icyavocado Feb 14, 2026
f531d1f
[FIX] Update project list styling to use 'relaxed' class for improved…
icyavocado Feb 16, 2026
bd2b3c8
[FIX] Handle error in LoadProjects when retrieving project IDs
icyavocado Feb 17, 2026
fb39b54
[FIX] Update projectIDs function to return error on database query fa…
icyavocado Feb 17, 2026
33756e2
[FIX] Log warnings for issues that fail to assign projects due to per…
icyavocado Feb 17, 2026
0f90f12
[FIX] Enhance LoadProjects to handle nil and empty project list cases
icyavocado Feb 17, 2026
9e69173
[FIX] Handle error when retrieving old project IDs in IssueAssignOrRe…
icyavocado Feb 17, 2026
2985744
Add comprehensive test suite for multiple projects per issue function…
icyavocado Feb 25, 2026
30c8ec1
[FIX] Update project_id to project_ids across indexers for consistency
Copilot Feb 18, 2026
8797597
[FIX] Update handling of ProjectIDs in ToDBOptions and related tests
Copilot Feb 18, 2026
3e2a760
[FIX] Update project ID handling in search queries and increment inde…
icyavocado Feb 25, 2026
557acff
[FIX] Rename project_id to project_ids for consistency in IndexerData…
icyavocado Feb 25, 2026
affe4ad
[FIX] Simplify ProjectIDs check using slices.Contains in indexer tests
icyavocado Feb 25, 2026
68c337e
[FIX] Replace RemoveValue with SliceRemoveAll for better project ID h…
icyavocado Feb 25, 2026
edb4bdc
[FIX] Refactor project ID handling to use strings.Join for improved c…
icyavocado Feb 25, 2026
7342f53
[FIX] find default column instead of default to uncategorized
icyavocado Feb 25, 2026
f62c9f3
[FIX] Refactor projectID handling to use explicit projectIDs slice li…
Copilot Feb 25, 2026
89956fd
[FIX] add isProjectsLoaded flag following milestone pattern
Copilot Feb 25, 2026
41be12c
[FIX] update queries in IssueAssignOrRemoveProject
Copilot Feb 25, 2026
87feeb5
[FIX] using a new isuesProject map to access Projects faster
Copilot Feb 25, 2026
de6d00b
[FIX] update documentation from LoadProject to LoadProjects
icyavocado Feb 25, 2026
fcec913
[DOCUMENTATION] update documentation to show mulitple projectIds can …
icyavocado Feb 25, 2026
5c794b3
[FIX] remove projectID passing in incorrectly
icyavocado Feb 25, 2026
9d95280
[DOCUMENTATION] update opts.ProjectID to opts.ProjectIDs
icyavocado Feb 25, 2026
3be8290
[FIX] rename variables to projectsToAdd and projectsToRemove
Copilot Feb 26, 2026
c6746d9
[FIX] make DiffSlice preserve input slice order
Copilot Feb 26, 2026
f23d682
[FIX] update ProjectIDs declaration
icyavocado Feb 27, 2026
40a2c46
[FIX] handle projectId NoConditionID
icyavocado Feb 27, 2026
36e3156
[FIX] convert projectID from optional.Option[int64] to []int64 for Pr…
Copilot Feb 27, 2026
b854a8f
[FIX] update to ProjectIDs
icyavocado Feb 27, 2026
052038e
[FIX] use projectIDs variable for IndexerData.ProjectIDs in test data…
Copilot Feb 27, 2026
2171a90
[FIX] compute issue-project sorting per (project_id, project_board_id)
Copilot Feb 27, 2026
12d9a01
[FIX] avoid mutating opts.ProjectIDs in applyProjectCondition
Copilot Feb 27, 2026
aac48dc
[FIX] streamline projectID handling in SearchRepoIssuesJSON
icyavocado Feb 27, 2026
e952c60
[FIX] update projectID handling in SearchIssues to support multiple p…
icyavocado Feb 27, 2026
71400fb
[FIX] format SearchRepoIssuesJSON parameters for consistency
icyavocado Feb 27, 2026
8508c3c
[E2E] add e2e test for projects
icyavocado Feb 28, 2026
b162c82
[FIX] update class name from milestone list to project-list
icyavocado Feb 28, 2026
0526f1f
[FIX] update class name from milestone list to project-list in e2e test
icyavocado Feb 28, 2026
4e7b04f
[FIX] reset isProjectsLoaded variable before re-LoadProjects
icyavocado Feb 28, 2026
10b54f4
[E2E] remove e2e tests since it will take about 14s to finish
icyavocado Feb 28, 2026
55d6fd6
Revert "[FIX] update class name from milestone list to project-list"
icyavocado Mar 10, 2026
d0242dd
Add test for utils DiffSlice
icyavocado Mar 10, 2026
75663d0
Update check to prevent index-out-of-bounds error
icyavocado Mar 10, 2026
41fad0d
Remove double assigning projects to issue
icyavocado Mar 10, 2026
e815ac8
Prevent project=0 indexer filter
icyavocado Mar 10, 2026
22bb7f5
Return only validated projectIDs
icyavocado Mar 10, 2026
2653c65
Remove duplicate issues when filtering by multiple projects
icyavocado Mar 10, 2026
6fac519
Fix DiffSlice to deduplicate output slices
icyavocado Mar 11, 2026
1832511
Return HTTP 400 for malformed projects parameter
icyavocado Mar 11, 2026
d36b98c
Reset project cache after IssueAssignOrRemoveProject
icyavocado Mar 11, 2026
a61b471
Add test case for filtering issues with no project
icyavocado Mar 11, 2026
ba310b4
Remove newColumnID parameter from IssueAssignOrRemoveProject
icyavocado Mar 11, 2026
419a25f
Fix double-filtering when ProjectIDs and ProjectColumnID are both set
icyavocado Mar 11, 2026
a36b2f2
Add multi-project support to API endpoints
icyavocado Mar 11, 2026
9e4c6be
Add e2e tests for multiple projects per issue feature
icyavocado Mar 14, 2026
3289f27
Add support for filtering issues by multiple projects in web UI
icyavocado Mar 14, 2026
19cf3ff
Change SelectedProjectID from string to []int64 for type safety
icyavocado Mar 11, 2026
cf25c10
Fix SearchIssues parameter precedence: projects takes precedence over…
icyavocado Apr 4, 2026
2279fc8
Return 400 error for invalid project IDs instead of silently dropping…
icyavocado Apr 4, 2026
dee5f1a
Optimize N+1 queries in IssueAssignOrRemoveProject using batch loading
icyavocado Apr 4, 2026
b4c7b30
Add NoProject field to issue indexers for proper "no project" filtering
icyavocado Apr 4, 2026
c9853e7
Add ProjectColumnMap to support deterministic column tracking for mul…
icyavocado Apr 4, 2026
8c760c4
Combine CreateIssue and EditIssue validation into validateProjectAcce…
icyavocado Apr 6, 2026
5acedcf
Add comments clarifying multi-project behavior
icyavocado Apr 6, 2026
e6e9cab
Fix e2e test calls to match new apiCreateIssue signature
icyavocado Apr 6, 2026
f42a097
Fix multi-project column selector in issue sidebar
icyavocado Apr 20, 2026
a7b054b
Improve multi-project UX with inline column dropdowns
icyavocado Apr 20, 2026
8a8d840
Update e2e tests to support column picker
icyavocado Apr 20, 2026
15d0b99
Revert to original project column picker design from 2f5b5a9e9c
icyavocado Apr 20, 2026
2bc7c47
Add multiple projects support to project column picker
icyavocado Apr 20, 2026
d364a4c
Update e2e tests to support column picker
icyavocado Apr 20, 2026
7c281e2
Update TestIssueSidebarProjectColumn with project column picker HTML …
icyavocado Apr 20, 2026
8e8beba
Revert JavaScript changes to use original IssueSidebarComboList design
icyavocado Apr 20, 2026
06867fe
Using only project ID param
icyavocado Apr 20, 2026
4e016a9
Remove unnecessary project_id param from column update URL
icyavocado Apr 20, 2026
a2e7052
Merge projects.test.ts into issue-project.test.ts
icyavocado Apr 20, 2026
5bb0e41
Fix project list not showing in sidebar for new issue
icyavocado Apr 20, 2026
008386b
Add issue project ordering
icyavocado Apr 20, 2026
269cf86
Validate issue list project params
icyavocado Apr 20, 2026
6ce5007
Reject non-numeric project_ids
icyavocado Apr 20, 2026
9610e6c
Use default column in sidebar
icyavocado Apr 20, 2026
e11ec3f
Align project column rows without margins
icyavocado Apr 20, 2026
5729888
Avoid ambiguous project column in indexer
icyavocado Apr 20, 2026
bca8a96
Batch project access validation
icyavocado Apr 20, 2026
97573fe
Tighten project list spacing
icyavocado Apr 20, 2026
0f7d0ce
Bump indexer version correctly
icyavocado Apr 21, 2026
c3b0ca2
Revert "Validate issue list project params"
icyavocado Apr 21, 2026
d697695
Revert "Align project column rows without margins"
icyavocado Apr 21, 2026
651976e
Bump indexer version correctly
icyavocado Apr 21, 2026
5158d8e
fix style
wxiaoguang Apr 22, 2026
666bdc9
fix tmpl
wxiaoguang Apr 22, 2026
813b20d
fix tmpl
wxiaoguang Apr 22, 2026
8dc215a
fix tmpl
wxiaoguang Apr 22, 2026
6b4fd2c
Remove duplicate code, replace with GetColumnIssueNextSorting
icyavocado Apr 22, 2026
c790839
Merge tests into one, reduce amount of db prepare
icyavocado Apr 22, 2026
d51c7aa
Use data-testid to increase e2e test stability
icyavocado Apr 23, 2026
34d70d9
Fix issue with adding/removing project with tmpl changes
icyavocado Apr 23, 2026
4ea21b4
Revert code back to the old logic of redirect
icyavocado Apr 23, 2026
21bfb35
Change projectColumnID to projectColumnIDs
icyavocado Apr 23, 2026
ea7d7e4
Switch to use base.StringsToInt64s
icyavocado Apr 23, 2026
b4ce5c1
Add support for multiple projects filter
icyavocado Apr 23, 2026
9109388
Rename project_board_ids to project_column_ids
icyavocado Apr 24, 2026
0d88c49
Add support for multiple projects id issue search
icyavocado Apr 25, 2026
38dfdd9
Return 400 error to match SearchIssues
icyavocado Apr 25, 2026
d21e0e4
Replace logic with helper GetProjectsMapByIDs
icyavocado Apr 25, 2026
69bcfad
Add guard for nil opts
icyavocado Apr 25, 2026
8ab3359
Reject malformed project IDs and drop unused ProjectColumnID
silverwind Apr 26, 2026
5228d6d
Simplify project assignment plumbing
silverwind Apr 26, 2026
a53fec6
Merge branch 'main' into issue-12974-multiple-projects-per-issue
silverwind Apr 26, 2026
d0a962d
Test UpdateIssueProject assign/remove and reject malformed id
silverwind Apr 26, 2026
0466bc6
Space the pre-selected project list on the new-issue page
silverwind Apr 26, 2026
f72b40b
Address wxiaoguang's review on the project sidebar
silverwind Apr 26, 2026
92b87ab
Match the labels sidebar pattern on the new-issue project list
silverwind Apr 26, 2026
5f8f3ac
Restore the gap between dropdown and first project card
silverwind Apr 26, 2026
0dc5a61
Move project-cards top margin into the stylesheet
silverwind Apr 26, 2026
f504417
Merge branch 'main' into issue-12974-multiple-projects-per-issue
wxiaoguang Apr 30, 2026
8caaea7
clean up
wxiaoguang Apr 30, 2026
a99e5da
clean up
wxiaoguang Apr 30, 2026
20b15cf
clean up
wxiaoguang Apr 30, 2026
360d39e
clean up
wxiaoguang Apr 30, 2026
5eb48e4
clean up
wxiaoguang Apr 30, 2026
b792033
clean up
wxiaoguang Apr 30, 2026
4a0c9a1
clean up
wxiaoguang Apr 30, 2026
1f7830e
clean up
wxiaoguang Apr 30, 2026
32a4996
clean up
wxiaoguang Apr 30, 2026
6d2fccc
clean up AI slop
wxiaoguang Apr 30, 2026
8ef4591
fix lint and test
wxiaoguang Apr 30, 2026
b700bc4
FIXME: ISSUE-MULTIPLE-PROJECTS-FILTER
wxiaoguang Apr 30, 2026
bf0578a
clean up
wxiaoguang Apr 30, 2026
38dae62
clean up AI slop
wxiaoguang Apr 30, 2026
187c376
fix
wxiaoguang Apr 30, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ cpu.out
/indexers
/log
/public/assets/img/avatar
/tests/e2e-output
/tests/integration/gitea-integration-*
/tests/integration/indexers-*
/tests/*.ini
Expand Down
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -478,7 +478,7 @@ playwright: deps-frontend
@pnpm exec playwright install $(if $(GITHUB_ACTIONS),,--with-deps) chromium firefox $(PLAYWRIGHT_FLAGS)

.PHONY: test-e2e
test-e2e: playwright backend
test-e2e: playwright frontend backend
@EXECUTABLE=$(EXECUTABLE) ./tools/test-e2e.sh $(GITEA_TEST_E2E_FLAGS)

.PHONY: build
Expand Down
26 changes: 14 additions & 12 deletions models/issues/issue.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,17 +59,18 @@ type Issue struct {
PosterID int64 `xorm:"INDEX"`
Poster *user_model.User `xorm:"-"`
OriginalAuthor string
OriginalAuthorID int64 `xorm:"index"`
Title string `xorm:"name"`
Content string `xorm:"LONGTEXT"`
RenderedContent template.HTML `xorm:"-"`
ContentVersion int `xorm:"NOT NULL DEFAULT 0"`
Labels []*Label `xorm:"-"`
isLabelsLoaded bool `xorm:"-"`
MilestoneID int64 `xorm:"INDEX"`
Milestone *Milestone `xorm:"-"`
isMilestoneLoaded bool `xorm:"-"`
Project *project_model.Project `xorm:"-"`
OriginalAuthorID int64 `xorm:"index"`
Title string `xorm:"name"`
Content string `xorm:"LONGTEXT"`
RenderedContent template.HTML `xorm:"-"`
ContentVersion int `xorm:"NOT NULL DEFAULT 0"`
Labels []*Label `xorm:"-"`
isLabelsLoaded bool `xorm:"-"`
MilestoneID int64 `xorm:"INDEX"`
Milestone *Milestone `xorm:"-"`
isMilestoneLoaded bool `xorm:"-"`
Projects []*project_model.Project `xorm:"-"`
isProjectsLoaded bool `xorm:"-"`
Priority int
AssigneeID int64 `xorm:"-"`
Assignee *user_model.User `xorm:"-"`
Expand Down Expand Up @@ -305,7 +306,7 @@ func (issue *Issue) LoadAttributes(ctx context.Context) (err error) {
return err
}

if err = issue.LoadProject(ctx); err != nil {
if err = issue.LoadProjects(ctx); err != nil {
return err
}

Expand Down Expand Up @@ -355,6 +356,7 @@ func (issue *Issue) ResetAttributesLoaded() {
issue.isMilestoneLoaded = false
issue.isAttachmentsLoaded = false
issue.isAssigneeLoaded = false
issue.isProjectsLoaded = false
}

// GetIsRead load the `IsRead` field of the issue
Expand Down
8 changes: 5 additions & 3 deletions models/issues/issue_list.go
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,7 @@ func (issues IssueList) LoadMilestones(ctx context.Context) error {

func (issues IssueList) LoadProjects(ctx context.Context) error {
issueIDs := issues.getIssueIDs()
projectMaps := make(map[int64]*project_model.Project, len(issues))
issueProjectMaps := make(map[int64][]*project_model.Project, len(issues))
left := len(issueIDs)

type projectWithIssueID struct {
Expand All @@ -202,19 +202,21 @@ func (issues IssueList) LoadProjects(ctx context.Context) error {
Select("project.*, project_issue.issue_id").
Join("INNER", "project_issue", "project.id = project_issue.project_id").
In("project_issue.issue_id", issueIDs[:limit]).
OrderBy("project_issue.issue_id ASC, project.id ASC").
Find(&projects)
if err != nil {
return err
}
for _, project := range projects {
projectMaps[project.IssueID] = project.Project
issueProjectMaps[project.IssueID] = append(issueProjectMaps[project.IssueID], project.Project)
}
left -= limit
issueIDs = issueIDs[limit:]
}

for _, issue := range issues {
issue.Project = projectMaps[issue.ID]
issue.Projects = issueProjectMaps[issue.ID]
issue.isProjectsLoaded = true
}
return nil
}
Expand Down
6 changes: 3 additions & 3 deletions models/issues/issue_list_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,10 +65,10 @@ func TestIssueList_LoadAttributes(t *testing.T) {
}
if issue.ID == int64(1) {
assert.Equal(t, int64(400), issue.TotalTrackedTime)
assert.NotNil(t, issue.Project)
assert.Equal(t, int64(1), issue.Project.ID)
assert.NotEmpty(t, issue.Projects)
assert.Equal(t, int64(1), issue.Projects[0].ID)
} else {
assert.Nil(t, issue.Project)
assert.Empty(t, issue.Projects)
}
}
}
164 changes: 93 additions & 71 deletions models/issues/issue_project.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,41 +12,38 @@ import (
"code.gitea.io/gitea/modules/util"
)

// LoadProject load the project the issue was assigned to
func (issue *Issue) LoadProject(ctx context.Context) (err error) {
if issue.Project == nil {
var p project_model.Project
has, err := db.GetEngine(ctx).Table("project").
// LoadProjects loads all projects the issue is assigned to
func (issue *Issue) LoadProjects(ctx context.Context) (err error) {
if !issue.isProjectsLoaded {
err = db.GetEngine(ctx).Table("project").
Join("INNER", "project_issue", "project.id=project_issue.project_id").
Where("project_issue.issue_id = ?", issue.ID).Get(&p)
if err != nil {
return err
} else if has {
issue.Project = &p
Where("project_issue.issue_id = ?", issue.ID).
OrderBy("project.id ASC").
Find(&issue.Projects)
if err == nil {
issue.isProjectsLoaded = true
}
}
return err
}

func (issue *Issue) projectID(ctx context.Context) int64 {
var ip project_model.ProjectIssue
has, err := db.GetEngine(ctx).Where("issue_id=?", issue.ID).Get(&ip)
if err != nil || !has {
return 0
}
return ip.ProjectID
func (issue *Issue) projectIDs(ctx context.Context) (projectIDs []int64, _ error) {
err := db.GetEngine(ctx).Table("project_issue").Where("issue_id = ?", issue.ID).Cols("project_id").Find(&projectIDs)
return projectIDs, err
}

// ProjectColumnID return project column id if issue was assigned to one
func (issue *Issue) ProjectColumnID(ctx context.Context) (int64, error) {
var ip project_model.ProjectIssue
has, err := db.GetEngine(ctx).Where("issue_id=?", issue.ID).Get(&ip)
if err != nil {
return 0, err
} else if !has {
return 0, nil
// ProjectColumnMap returns a map of project ID to column ID for this issue.
func (issue *Issue) ProjectColumnMap(ctx context.Context) (map[int64]int64, error) {
var projIssues []project_model.ProjectIssue
if err := db.GetEngine(ctx).Where("issue_id=?", issue.ID).Find(&projIssues); err != nil {
return nil, err
}

result := make(map[int64]int64, len(projIssues))
for _, projIssue := range projIssues {
result[projIssue.ProjectID] = projIssue.ProjectColumnID
}
return ip.ProjectColumnID, nil
return result, nil
}

func LoadProjectIssueColumnMap(ctx context.Context, projectID, defaultColumnID int64) (map[int64]int64, error) {
Expand All @@ -64,66 +61,91 @@ func LoadProjectIssueColumnMap(ctx context.Context, projectID, defaultColumnID i
return result, nil
}

// IssueAssignOrRemoveProject changes the project associated with an issue
// If newProjectID is 0, the issue is removed from the project
func IssueAssignOrRemoveProject(ctx context.Context, issue *Issue, doer *user_model.User, newProjectID, newColumnID int64) error {
// IssueAssignOrRemoveProject updates the projects associated with an issue.
// It adds projects that are in newProjectIDs but not currently assigned,
// and removes projects that are currently assigned but not in newProjectIDs.
// If newProjectIDs is empty, all projects are removed from the issue.
// When adding an issue to a project, it is placed in the project's default column.
func IssueAssignOrRemoveProject(ctx context.Context, issue *Issue, doer *user_model.User, newProjectIDs []int64) error {
return db.WithTx(ctx, func(ctx context.Context) error {
oldProjectID := issue.projectID(ctx)

if err := issue.LoadRepo(ctx); err != nil {
return err
}

// Only check if we add a new project and not remove it.
if newProjectID > 0 {
newProject, err := project_model.GetProjectByID(ctx, newProjectID)
if err != nil {
oldProjectIDs, err := issue.projectIDs(ctx)
if err != nil {
return err
}

projectsToAdd, projectsToRemove := util.DiffSlice(oldProjectIDs, newProjectIDs)
issue.isProjectsLoaded = false
issue.Projects = nil

if len(projectsToRemove) > 0 {
if _, err := db.GetEngine(ctx).Where("issue_id=?", issue.ID).In("project_id", projectsToRemove).Delete(&project_model.ProjectIssue{}); err != nil {
return err
}
if !newProject.CanBeAccessedByOwnerRepo(issue.Repo.OwnerID, issue.Repo) {
return util.NewPermissionDeniedErrorf("issue %d can't be accessed by project %d", issue.ID, newProject.ID)
}
if newColumnID == 0 {
newDefaultColumn, err := newProject.MustDefaultColumn(ctx)
if err != nil {
for _, projectID := range projectsToRemove {
if _, err := CreateComment(ctx, &CreateCommentOptions{
Type: CommentTypeProject,
Doer: doer,
Repo: issue.Repo,
Issue: issue,
OldProjectID: projectID,
ProjectID: 0,
}); err != nil {
return err
}
newColumnID = newDefaultColumn.ID
}
}

if _, err := db.GetEngine(ctx).Where("project_issue.issue_id=?", issue.ID).Delete(&project_model.ProjectIssue{}); err != nil {
return err
}

if oldProjectID > 0 || newProjectID > 0 {
if _, err := CreateComment(ctx, &CreateCommentOptions{
Type: CommentTypeProject,
Doer: doer,
Repo: issue.Repo,
Issue: issue,
OldProjectID: oldProjectID,
ProjectID: newProjectID,
}); err != nil {
if len(projectsToAdd) > 0 {
projectMap, err := project_model.GetProjectsMapByIDs(ctx, projectsToAdd)
if err != nil {
return err
}
}
if newProjectID == 0 {
return nil
}
if newColumnID == 0 {
panic("newColumnID must not be zero") // shouldn't happen
}

newSorting, err := project_model.GetColumnIssueNextSorting(ctx, newProjectID, newColumnID)
if err != nil {
return err
for _, projectID := range projectsToAdd {
newProject, ok := projectMap[projectID]
if !ok {
return util.NewNotExistErrorf("project %d not found", projectID)
}
if !newProject.CanBeAccessedByOwnerRepo(issue.Repo.OwnerID, issue.Repo) {
return util.NewPermissionDeniedErrorf("issue %d can't be accessed by project %d", issue.ID, newProject.ID)
}

defaultColumn, err := newProject.MustDefaultColumn(ctx)
if err != nil {
return err
}

newSorting, err := project_model.GetColumnIssueNextSorting(ctx, projectID, defaultColumn.ID)
if err != nil {
return err
}

err = db.Insert(ctx, &project_model.ProjectIssue{
IssueID: issue.ID,
ProjectID: projectID,
ProjectColumnID: defaultColumn.ID,
Sorting: newSorting,
})
if err != nil {
return err
}

if _, err := CreateComment(ctx, &CreateCommentOptions{
Type: CommentTypeProject,
Doer: doer,
Repo: issue.Repo,
Issue: issue,
OldProjectID: 0,
ProjectID: projectID,
}); err != nil {
return err
}
}
}
return db.Insert(ctx, &project_model.ProjectIssue{
IssueID: issue.ID,
ProjectID: newProjectID,
ProjectColumnID: newColumnID,
Sorting: newSorting,
})
return nil
})
}
Loading