From 16836c2a9a0fadca6d44d4c026c91c8a6c2046ab Mon Sep 17 00:00:00 2001 From: yp05327 <576951401@qq.com> Date: Sun, 12 Feb 2023 02:11:52 +0900 Subject: [PATCH 01/69] fix edit/close/delete projects in org --- modules/context/org.go | 47 +++++++++------ routers/web/org/projects.go | 107 +++++++++++------------------------ routers/web/repo/projects.go | 2 +- services/context/user.go | 3 +- templates/projects/list.tmpl | 8 +-- templates/projects/view.tmpl | 4 +- 6 files changed, 74 insertions(+), 97 deletions(-) diff --git a/modules/context/org.go b/modules/context/org.go index 0add7f2c0c3d..982032acea8e 100644 --- a/modules/context/org.go +++ b/modules/context/org.go @@ -56,6 +56,29 @@ func (org *Organization) UnitPermission(ctx *Context, doerID int64, unitType uni return perm.AccessModeNone } +func GetOrganizationByParams(ctx *Context) { + orgName := ctx.Params(":org") + + var err error + + ctx.Org.Organization, err = organization.GetOrgByName(ctx, orgName) + if err != nil { + if organization.IsErrOrgNotExist(err) { + redirectUserID, err := user_model.LookupUserRedirect(orgName) + if err == nil { + RedirectToUser(ctx, orgName, redirectUserID) + } else if user_model.IsErrUserRedirectNotExist(err) { + ctx.NotFound("GetUserByName", err) + } else { + ctx.ServerError("LookupUserRedirect", err) + } + } else { + ctx.ServerError("GetUserByName", err) + } + return + } +} + // HandleOrgAssignment handles organization assignment func HandleOrgAssignment(ctx *Context, args ...bool) { var ( @@ -77,25 +100,17 @@ func HandleOrgAssignment(ctx *Context, args ...bool) { requireTeamAdmin = args[3] } - orgName := ctx.Params(":org") - var err error - ctx.Org.Organization, err = organization.GetOrgByName(ctx, orgName) - if err != nil { - if organization.IsErrOrgNotExist(err) { - redirectUserID, err := user_model.LookupUserRedirect(orgName) - if err == nil { - RedirectToUser(ctx, orgName, redirectUserID) - } else if user_model.IsErrUserRedirectNotExist(err) { - ctx.NotFound("GetUserByName", err) - } else { - ctx.ServerError("LookupUserRedirect", err) - } - } else { - ctx.ServerError("GetUserByName", err) + + // if Organization is not defined, get it from params + if ctx.Org.Organization == nil { + GetOrganizationByParams(ctx) + + if ctx.Written() { + return } - return } + org := ctx.Org.Organization // Handle Visibility diff --git a/routers/web/org/projects.go b/routers/web/org/projects.go index 6449d12de105..4675d9a56caa 100644 --- a/routers/web/org/projects.go +++ b/routers/web/org/projects.go @@ -8,12 +8,12 @@ import ( "fmt" "net/http" "net/url" - "strconv" "strings" issues_model "code.gitea.io/gitea/models/issues" project_model "code.gitea.io/gitea/models/project" "code.gitea.io/gitea/models/unit" + unit_model "code.gitea.io/gitea/models/unit" "code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/json" @@ -33,7 +33,7 @@ const ( // MustEnableProjects check if projects are enabled in settings func MustEnableProjects(ctx *context.Context) { - if unit.TypeProjects.UnitGlobalDisabled() { + if unit_model.TypeProjects.UnitGlobalDisabled() { ctx.NotFound("EnableKanbanBoard", nil) return } @@ -108,6 +108,17 @@ func Projects(ctx *context.Context) { ctx.Data["PageIsViewProjects"] = true ctx.Data["SortType"] = sortType + if ctx.ContextUser.IsOrganization() { + ctx.Data["IsOwner"] = ctx.Org.IsOwner + } else { + if ctx.ContextUser.IsAdmin { + ctx.Data["IsOwner"] = true + } else { + ctx.Data["IsOwner"] = ctx.ContextUser.ID == ctx.Doer.ID + } + } + ctx.Data["DoerID"] = ctx.Doer.ID + ctx.HTML(http.StatusOK, tplProjects) } @@ -167,19 +178,19 @@ func ChangeProjectStatus(ctx *context.Context) { case "close": toClose = true default: - ctx.Redirect(ctx.Repo.RepoLink + "/projects") + ctx.Redirect(ctx.ContextUser.HomeLink() + "/-/projects") } id := ctx.ParamsInt64(":id") - if err := project_model.ChangeProjectStatusByRepoIDAndID(ctx.Repo.Repository.ID, id, toClose); err != nil { + if err := project_model.ChangeProjectStatusByRepoIDAndID(int64(0), id, toClose); err != nil { if project_model.IsErrProjectNotExist(err) { ctx.NotFound("", err) } else { - ctx.ServerError("ChangeProjectStatusByIDAndRepoID", err) + ctx.ServerError("ChangeProjectStatusByRepoIDAndID", err) } return } - ctx.Redirect(ctx.Repo.RepoLink + "/projects?state=" + url.QueryEscape(ctx.Params(":action"))) + ctx.Redirect(ctx.ContextUser.HomeLink() + "/-/projects?state=" + url.QueryEscape(ctx.Params(":action"))) } // DeleteProject delete a project @@ -193,7 +204,7 @@ func DeleteProject(ctx *context.Context) { } return } - if p.RepoID != ctx.Repo.Repository.ID { + if p.OwnerID != ctx.ContextUser.ID { ctx.NotFound("", nil) return } @@ -205,7 +216,7 @@ func DeleteProject(ctx *context.Context) { } ctx.JSON(http.StatusOK, map[string]interface{}{ - "redirect": ctx.Repo.RepoLink + "/projects", + "redirect": ctx.ContextUser.HomeLink() + "/-/projects", }) } @@ -226,7 +237,8 @@ func EditProject(ctx *context.Context) { } return } - if p.RepoID != ctx.Repo.Repository.ID { + + if p.OwnerID != ctx.ContextUser.ID { ctx.NotFound("", nil) return } @@ -260,7 +272,8 @@ func EditProjectPost(ctx *context.Context) { } return } - if p.RepoID != ctx.Repo.Repository.ID { + + if p.OwnerID != ctx.ContextUser.ID { ctx.NotFound("", nil) return } @@ -273,7 +286,7 @@ func EditProjectPost(ctx *context.Context) { } ctx.Flash.Success(ctx.Tr("repo.projects.edit_success", p.Title)) - ctx.Redirect(ctx.Repo.RepoLink + "/projects") + ctx.Redirect(ctx.ContextUser.HomeLink() + "/-/projects") } // ViewProject renders the project board for a project @@ -336,73 +349,21 @@ func ViewProject(ctx *context.Context) { ctx.Data["Project"] = project ctx.Data["IssuesMap"] = issuesMap ctx.Data["Boards"] = boards - shared_user.RenderUserHeader(ctx) - ctx.HTML(http.StatusOK, tplProjectsView) -} - -func getActionIssues(ctx *context.Context) []*issues_model.Issue { - commaSeparatedIssueIDs := ctx.FormString("issue_ids") - if len(commaSeparatedIssueIDs) == 0 { - return nil - } - issueIDs := make([]int64, 0, 10) - for _, stringIssueID := range strings.Split(commaSeparatedIssueIDs, ",") { - issueID, err := strconv.ParseInt(stringIssueID, 10, 64) - if err != nil { - ctx.ServerError("ParseInt", err) - return nil - } - issueIDs = append(issueIDs, issueID) - } - issues, err := issues_model.GetIssuesByIDs(ctx, issueIDs) - if err != nil { - ctx.ServerError("GetIssuesByIDs", err) - return nil - } - // Check access rights for all issues - issueUnitEnabled := ctx.Repo.CanRead(unit.TypeIssues) - prUnitEnabled := ctx.Repo.CanRead(unit.TypePullRequests) - for _, issue := range issues { - if issue.RepoID != ctx.Repo.Repository.ID { - ctx.NotFound("some issue's RepoID is incorrect", errors.New("some issue's RepoID is incorrect")) - return nil - } - if issue.IsPull && !prUnitEnabled || !issue.IsPull && !issueUnitEnabled { - ctx.NotFound("IssueOrPullRequestUnitNotAllowed", nil) - return nil - } - if err = issue.LoadAttributes(ctx); err != nil { - ctx.ServerError("LoadAttributes", err) - return nil + if ctx.ContextUser.IsOrganization() { + ctx.Data["IsOwner"] = ctx.Org.IsOwner + } else { + if ctx.ContextUser.IsAdmin { + ctx.Data["IsOwner"] = true + } else { + ctx.Data["IsOwner"] = ctx.ContextUser.ID == ctx.Doer.ID } } - return issues -} - -// UpdateIssueProject change an issue's project -func UpdateIssueProject(ctx *context.Context) { - issues := getActionIssues(ctx) - if ctx.Written() { - return - } + ctx.Data["IsProjectCreator"] = project.CreatorID == ctx.Doer.ID - projectID := ctx.FormInt64("id") - for _, issue := range issues { - oldProjectID := issue.ProjectID() - if oldProjectID == projectID { - continue - } - - if err := issues_model.ChangeProjectAssign(issue, ctx.Doer, projectID); err != nil { - ctx.ServerError("ChangeProjectAssign", err) - return - } - } + shared_user.RenderUserHeader(ctx) - ctx.JSON(http.StatusOK, map[string]interface{}{ - "ok": true, - }) + ctx.HTML(http.StatusOK, tplProjectsView) } // DeleteProjectBoard allows for the deletion of a project board diff --git a/routers/web/repo/projects.go b/routers/web/repo/projects.go index 967b81c60851..fe21a65dd972 100644 --- a/routers/web/repo/projects.go +++ b/routers/web/repo/projects.go @@ -177,7 +177,7 @@ func ChangeProjectStatus(ctx *context.Context) { if project_model.IsErrProjectNotExist(err) { ctx.NotFound("", err) } else { - ctx.ServerError("ChangeProjectStatusByIDAndRepoID", err) + ctx.ServerError("ChangeProjectStatusByRepoIDAndID", err) } return } diff --git a/services/context/user.go b/services/context/user.go index 7642cba4e1f0..29a02a4aa563 100644 --- a/services/context/user.go +++ b/services/context/user.go @@ -63,7 +63,8 @@ func userAssignment(ctx *context.Context, errCb func(int, string, interface{})) ctx.Org = &context.Organization{} } ctx.Org.Organization = (*org_model.Organization)(ctx.ContextUser) - ctx.Data["Org"] = ctx.Org.Organization + + context.HandleOrgAssignment(ctx) } } } diff --git a/templates/projects/list.tmpl b/templates/projects/list.tmpl index 21a3350a75db..fb76c01f36d9 100644 --- a/templates/projects/list.tmpl +++ b/templates/projects/list.tmpl @@ -50,8 +50,8 @@ {{svg "octicon-check" 16 "mr-3"}} {{JsPrettyNumber .NumClosedIssues}} {{$.locale.Tr "repo.issues.closed_title"}} - - {{if and (or $.CanWriteIssues $.CanWritePulls) (not $.Repository.IsArchived)}} + + {{if or $.IsOwner (eq .CreatorID $.DoerID)}}
{{svg "octicon-pencil"}} {{$.locale.Tr "repo.issues.label_edit"}} {{if .IsClosed}} @@ -59,7 +59,7 @@ {{else}} {{svg "octicon-skip"}} {{$.locale.Tr "repo.projects.close"}} {{end}} - {{svg "octicon-trash"}} {{$.locale.Tr "repo.issues.label_delete"}} + {{svg "octicon-trash"}} {{$.locale.Tr "repo.issues.label_delete"}}
{{end}} {{if .Description}} @@ -75,7 +75,7 @@ -{{if or .CanWriteIssues .CanWritePulls}} +{{if .IsOwner}} + {{if or $.IsOwner (eq .CreatorID $.DoerID)}}
{{svg "octicon-pencil"}} {{$.locale.Tr "repo.issues.label_edit"}} From 192a6d4355b6d832023180fce11baedde1c95559 Mon Sep 17 00:00:00 2001 From: yp05327 <576951401@qq.com> Date: Sun, 12 Feb 2023 11:38:55 +0900 Subject: [PATCH 04/69] improve edit/close/delete permission --- routers/web/org/projects.go | 22 ---------------------- templates/projects/list.tmpl | 4 +++- templates/projects/view.tmpl | 4 ++-- 3 files changed, 5 insertions(+), 25 deletions(-) diff --git a/routers/web/org/projects.go b/routers/web/org/projects.go index 775580c4c50a..e6c6d559d73b 100644 --- a/routers/web/org/projects.go +++ b/routers/web/org/projects.go @@ -107,17 +107,6 @@ func Projects(ctx *context.Context) { ctx.Data["PageIsViewProjects"] = true ctx.Data["SortType"] = sortType - if ctx.ContextUser.IsOrganization() { - ctx.Data["IsOwner"] = ctx.Org.IsOwner - } else { - if ctx.Doer.IsAdmin { - ctx.Data["IsOwner"] = true - } else { - ctx.Data["IsOwner"] = ctx.ContextUser.ID == ctx.Doer.ID - } - } - ctx.Data["DoerID"] = ctx.Doer.ID - ctx.HTML(http.StatusOK, tplProjects) } @@ -349,17 +338,6 @@ func ViewProject(ctx *context.Context) { ctx.Data["IssuesMap"] = issuesMap ctx.Data["Boards"] = boards - if ctx.ContextUser.IsOrganization() { - ctx.Data["IsOwner"] = ctx.Org.IsOwner - } else { - if ctx.Doer.IsAdmin { - ctx.Data["IsOwner"] = true - } else { - ctx.Data["IsOwner"] = ctx.ContextUser.ID == ctx.Doer.ID - } - } - ctx.Data["IsProjectCreator"] = project.CreatorID == ctx.Doer.ID - shared_user.RenderUserHeader(ctx) ctx.HTML(http.StatusOK, tplProjectsView) diff --git a/templates/projects/list.tmpl b/templates/projects/list.tmpl index e6288723588d..bfaecd30acc5 100644 --- a/templates/projects/list.tmpl +++ b/templates/projects/list.tmpl @@ -51,7 +51,7 @@ {{JsPrettyNumber .NumClosedIssues}} {{$.locale.Tr "repo.issues.closed_title"}}
- {{if or $.IsOwner (eq .CreatorID $.DoerID)}} + {{if $.CanWriteProjects}}
{{svg "octicon-pencil"}} {{$.locale.Tr "repo.issues.label_edit"}} {{if .IsClosed}} @@ -75,6 +75,7 @@
+{{if .CanWriteProjects}} +{{end}} diff --git a/templates/projects/view.tmpl b/templates/projects/view.tmpl index 4cfb017daf5e..eb93079d5213 100644 --- a/templates/projects/view.tmpl +++ b/templates/projects/view.tmpl @@ -43,7 +43,7 @@

{{$.Project.Title}}

{{$.Project.RenderedContent|Str2html}}
- {{if or .IsOwner .IsProjectCreator}} + {{if .CanWriteProjects}}
-{{if or .IsOwner .IsProjectCreator}} +{{if .CanWriteProjects}} {{range .ClosedProjects}} + {{if .RepoID}} {{svg "octicon-project" 18 "mr-3"}} {{.Title}} + {{else}} + + {{svg "octicon-project" 18 "mr-3"}} + {{.Owner.Name}}/-/{{.Title}} + + {{end}} {{end}} {{end}} {{end}} From 5242c79b68e5ce90bc6960a86c5f9950dc742d37 Mon Sep 17 00:00:00 2001 From: yp05327 <576951401@qq.com> Date: Tue, 14 Feb 2023 20:54:16 +0900 Subject: [PATCH 11/69] test add porject.TypeUser --- models/project/project.go | 18 +++++++++++++++++- models/project/project_test.go | 1 + routers/web/org/projects.go | 2 +- 3 files changed, 19 insertions(+), 2 deletions(-) diff --git a/models/project/project.go b/models/project/project.go index 931ef44675af..df5bf7083437 100644 --- a/models/project/project.go +++ b/models/project/project.go @@ -44,6 +44,9 @@ const ( // TypeOrganization is a project that is tied to an organisation TypeOrganization + + // TypeUser is a project that is tied to a user + TypeUser ) // ErrProjectNotExist represents a "ProjectNotExist" kind of error. @@ -148,6 +151,19 @@ func (p *Project) IsOrganizationProject() bool { return p.Type == TypeOrganization } +func (p *Project) IsUserProject() bool { + return p.Type == TypeUser +} + +// GetProjectTypeByContextUser retrieves the types of configurations project by user +func GetProjectTypeByUser(user *user_model.User) Type { + if user.IsOrganization() { + return TypeOrganization + } else { + return TypeUser + } +} + func init() { db.RegisterModel(new(Project)) } @@ -172,7 +188,7 @@ func GetCardConfig() []CardConfig { // IsTypeValid checks if a project type is valid func IsTypeValid(p Type) bool { switch p { - case TypeRepository, TypeOrganization: + case TypeRepository, TypeOrganization, TypeUser: return true default: return false diff --git a/models/project/project_test.go b/models/project/project_test.go index 6caa244f540a..e668f12a0a90 100644 --- a/models/project/project_test.go +++ b/models/project/project_test.go @@ -23,6 +23,7 @@ func TestIsProjectTypeValid(t *testing.T) { {TypeIndividual, false}, {TypeRepository, true}, {TypeOrganization, true}, + {TypeUser, true}, {UnknownType, false}, } diff --git a/routers/web/org/projects.go b/routers/web/org/projects.go index 7e5e17df1a07..602362246408 100644 --- a/routers/web/org/projects.go +++ b/routers/web/org/projects.go @@ -147,7 +147,7 @@ func NewProjectPost(ctx *context.Context) { Description: form.Content, CreatorID: ctx.Doer.ID, BoardType: form.BoardType, - Type: project_model.TypeOrganization, + Type: project_model.GetProjectTypeByUser(ctx.ContextUser), }); err != nil { ctx.ServerError("NewProject", err) return From f92c5e038c8311969e1f404d2b35cbbeb332252d Mon Sep 17 00:00:00 2001 From: yp05327 <576951401@qq.com> Date: Wed, 15 Feb 2023 16:24:48 +0900 Subject: [PATCH 12/69] remove TypeUser improve project names and icons in create/view/list issue page improve project names in issue badge redirect to origin page after edit an project --- models/project/project.go | 100 ++++++++++++++---- models/project/project_test.go | 1 - models/user/user.go | 5 + routers/web/org/projects.go | 21 +++- routers/web/repo/projects.go | 7 +- templates/projects/new.tmpl | 1 + templates/projects/view.tmpl | 2 +- templates/repo/issue/list.tmpl | 44 ++++++-- templates/repo/issue/new_form.tmpl | 50 +++++---- .../repo/issue/view_content/comments.tmpl | 6 +- .../repo/issue/view_content/sidebar.tmpl | 30 ++++-- templates/repo/projects/list.tmpl | 12 +-- templates/repo/projects/new.tmpl | 2 +- templates/repo/projects/view.tmpl | 2 +- templates/shared/issuelist.tmpl | 11 +- 15 files changed, 218 insertions(+), 76 deletions(-) diff --git a/models/project/project.go b/models/project/project.go index df5bf7083437..00b7206e916e 100644 --- a/models/project/project.go +++ b/models/project/project.go @@ -33,6 +33,9 @@ type ( // Type is used to identify the type of project in question and ownership Type uint8 + + // ProjectList is used to identify a list of projects + ProjectList []*Project ) const ( @@ -44,9 +47,6 @@ const ( // TypeOrganization is a project that is tied to an organisation TypeOrganization - - // TypeUser is a project that is tied to a user - TypeUser ) // ErrProjectNotExist represents a "ProjectNotExist" kind of error. @@ -88,6 +88,25 @@ func (err ErrProjectBoardNotExist) Unwrap() error { return util.ErrNotExist } +// ErrUserType represents a "ErrUserType" kind of error. +type ErrUserType struct { + UserType user_model.UserType +} + +// IsErrUserType checks if an error is a ErrUserType +func IsErrUserType(err error) bool { + _, ok := err.(ErrUserType) + return ok +} + +func (err ErrUserType) Error() string { + return fmt.Sprintf("projects does not support user type: %d", err.UserType) +} + +func (err ErrUserType) Unwrap() error { + return util.ErrNotExist +} + // Project represents a project board type Project struct { ID int64 `xorm:"pk autoincr"` @@ -118,6 +137,18 @@ func (p *Project) LoadOwner(ctx context.Context) (err error) { return err } +func (pl ProjectList) LoadOwners(ctx context.Context) (err error) { + for _, p := range pl { + if p.Owner != nil { + continue + } + if p.Owner, err = user_model.GetUserByID(ctx, p.OwnerID); err != nil { + return err + } + } + return nil +} + func (p *Project) LoadRepo(ctx context.Context) (err error) { if p.RepoID == 0 || p.Repo != nil { return nil @@ -126,19 +157,29 @@ func (p *Project) LoadRepo(ctx context.Context) (err error) { return err } +func (pl ProjectList) LoadRepos(ctx context.Context) (err error) { + for _, p := range pl { + if p.RepoID == 0 || p.Repo != nil { + continue + } + if p.Repo, err = repo_model.GetRepositoryByID(ctx, p.RepoID); err != nil { + return err + } + } + return nil +} + // Link returns the project's relative URL. func (p *Project) Link() string { if p.OwnerID > 0 { - err := p.LoadOwner(db.DefaultContext) - if err != nil { + if err := p.LoadOwner(db.DefaultContext); err != nil { log.Error("LoadOwner: %v", err) return "" } return fmt.Sprintf("%s/-/projects/%d", p.Owner.HomeLink(), p.ID) } if p.RepoID > 0 { - err := p.LoadRepo(db.DefaultContext) - if err != nil { + if err := p.LoadRepo(db.DefaultContext); err != nil { log.Error("LoadRepo: %v", err) return "" } @@ -147,20 +188,43 @@ func (p *Project) Link() string { return "" } +// Name return the project's name which is combined with owner/repo/project's name +func (p *Project) Name() string { + if p.OwnerID > 0 { + if err := p.LoadOwner(db.DefaultContext); err != nil { + log.Error("LoadOwner: %v", err) + return "" + } + return fmt.Sprintf("%s/-/%s", p.Owner.Name, p.Title) + } + if p.RepoID > 0 { + if err := p.LoadRepo(db.DefaultContext); err != nil { + log.Error("LoadRepo: %v", err) + return "" + } + + return fmt.Sprintf("%s/%s/%s", p.Repo.OwnerName, p.Repo.Name, p.Title) + } + return "" +} + func (p *Project) IsOrganizationProject() bool { return p.Type == TypeOrganization } -func (p *Project) IsUserProject() bool { - return p.Type == TypeUser +func (p *Project) IsIndividualProject() bool { + return p.Type == TypeIndividual } -// GetProjectTypeByContextUser retrieves the types of configurations project by user -func GetProjectTypeByUser(user *user_model.User) Type { - if user.IsOrganization() { - return TypeOrganization - } else { - return TypeUser +// GetProjectTypeByUser retrieves the type of a project by user's type +func GetProjectTypeByUser(user *user_model.User) (Type, error) { + switch user.Type { + case user_model.UserTypeIndividual: + return TypeIndividual, nil + case user_model.UserTypeOrganization: + return TypeOrganization, nil + default: + return 0, ErrUserType{UserType: user.Type} } } @@ -188,7 +252,7 @@ func GetCardConfig() []CardConfig { // IsTypeValid checks if a project type is valid func IsTypeValid(p Type) bool { switch p { - case TypeRepository, TypeOrganization, TypeUser: + case TypeIndividual, TypeRepository, TypeOrganization: return true default: return false @@ -232,9 +296,9 @@ func CountProjects(ctx context.Context, opts SearchOptions) (int64, error) { } // FindProjects returns a list of all projects that have been created in the repository -func FindProjects(ctx context.Context, opts SearchOptions) ([]*Project, int64, error) { +func FindProjects(ctx context.Context, opts SearchOptions) (ProjectList, int64, error) { e := db.GetEngine(ctx) - projects := make([]*Project, 0, setting.UI.IssuePagingNum) + projects := make(ProjectList, 0, setting.UI.IssuePagingNum) cond := opts.toConds() count, err := e.Where(cond).Count(new(Project)) diff --git a/models/project/project_test.go b/models/project/project_test.go index e668f12a0a90..6caa244f540a 100644 --- a/models/project/project_test.go +++ b/models/project/project_test.go @@ -23,7 +23,6 @@ func TestIsProjectTypeValid(t *testing.T) { {TypeIndividual, false}, {TypeRepository, true}, {TypeOrganization, true}, - {TypeUser, true}, {UnknownType, false}, } diff --git a/models/user/user.go b/models/user/user.go index 0917bea7540d..1f52936303a7 100644 --- a/models/user/user.go +++ b/models/user/user.go @@ -460,6 +460,11 @@ func (u *User) IsOrganization() bool { return u.Type == UserTypeOrganization } +// IsIndividual returns true if user is actually a individual user. +func (u *User) IsIndividual() bool { + return u.Type == UserTypeIndividual +} + // DisplayName returns full name if it's not empty, // returns username otherwise. func (u *User) DisplayName() string { diff --git a/routers/web/org/projects.go b/routers/web/org/projects.go index 602362246408..f63621bf8673 100644 --- a/routers/web/org/projects.go +++ b/routers/web/org/projects.go @@ -57,12 +57,17 @@ func Projects(ctx *context.Context) { page = 1 } + projectType, err := project_model.GetProjectTypeByUser(ctx.ContextUser) + if err != nil { + ctx.ServerError("GetProjectTypeByUser", err) + return + } projects, total, err := project_model.FindProjects(ctx, project_model.SearchOptions{ OwnerID: ctx.ContextUser.ID, Page: page, IsClosed: util.OptionalBoolOf(isShowClosed), SortType: sortType, - Type: project_model.TypeOrganization, + Type: projectType, }) if err != nil { ctx.ServerError("FindProjects", err) @@ -141,13 +146,18 @@ func NewProjectPost(ctx *context.Context) { return } + projectType, err := project_model.GetProjectTypeByUser(ctx.ContextUser) + if err != nil { + ctx.ServerError("GetProjectTypeByUser", err) + return + } if err := project_model.NewProject(&project_model.Project{ OwnerID: ctx.ContextUser.ID, Title: form.Title, Description: form.Content, CreatorID: ctx.Doer.ID, BoardType: form.BoardType, - Type: project_model.GetProjectTypeByUser(ctx.ContextUser), + Type: projectType, }); err != nil { ctx.ServerError("NewProject", err) return @@ -233,6 +243,7 @@ func EditProject(ctx *context.Context) { ctx.Data["title"] = p.Title ctx.Data["content"] = p.Description + ctx.Data["redirect"] = ctx.FormString("redirect") ctx.HTML(http.StatusOK, tplProjectsNew) } @@ -274,7 +285,11 @@ func EditProjectPost(ctx *context.Context) { } ctx.Flash.Success(ctx.Tr("repo.projects.edit_success", p.Title)) - ctx.Redirect(ctx.ContextUser.HomeLink() + "/-/projects") + if ctx.FormString("redirect") == "project" { + ctx.Redirect(p.Link()) + } else { + ctx.Redirect(ctx.ContextUser.HomeLink() + "/-/projects") + } } // ViewProject renders the project board for a project diff --git a/routers/web/repo/projects.go b/routers/web/repo/projects.go index fe21a65dd972..e188501f3307 100644 --- a/routers/web/repo/projects.go +++ b/routers/web/repo/projects.go @@ -235,6 +235,7 @@ func EditProject(ctx *context.Context) { ctx.Data["title"] = p.Title ctx.Data["content"] = p.Description ctx.Data["card_type"] = p.CardType + ctx.Data["redirect"] = ctx.FormString("redirect") ctx.HTML(http.StatusOK, tplProjectsNew) } @@ -275,7 +276,11 @@ func EditProjectPost(ctx *context.Context) { } ctx.Flash.Success(ctx.Tr("repo.projects.edit_success", p.Title)) - ctx.Redirect(ctx.Repo.RepoLink + "/projects") + if ctx.FormString("redirect") == "project" { + ctx.Redirect(p.Link()) + } else { + ctx.Redirect(ctx.Repo.RepoLink + "/projects") + } } // ViewProject renders the project board for a project diff --git a/templates/projects/new.tmpl b/templates/projects/new.tmpl index 19bf50369238..85ceddec6055 100644 --- a/templates/projects/new.tmpl +++ b/templates/projects/new.tmpl @@ -21,6 +21,7 @@
{{.CsrfTokenHtml}}
+
diff --git a/templates/projects/view.tmpl b/templates/projects/view.tmpl index eb93079d5213..da5ac45d8aed 100644 --- a/templates/projects/view.tmpl +++ b/templates/projects/view.tmpl @@ -46,7 +46,7 @@ {{if .CanWriteProjects}} {{range .ClosedProjects}} - {{if .IsOrganizationProject}}{{svg "octicon-project-symlink" 18 "mr-3"}}{{else}}{{svg "octicon-project" 18 "mr-3"}}{{end}} - {{.Title}} + {{if .IsOrganizationProject}} + {{svg "octicon-project-symlink" 18 "mr-3"}} + {{else if .IsIndividualProject}} + {{svg "octicon-project-symlink" 18 "mr-3"}}{{/*TODO: Change Individual Project Icon*/}} + {{else}} + {{svg "octicon-project" 18 "mr-3"}} + {{end}} + {{.Name}} {{end}} {{end}} @@ -259,9 +271,15 @@ {{.locale.Tr "repo.issues.new.open_projects"}}
{{range .OpenProjects}} -
- {{if .IsOrganizationProject}}{{svg "octicon-project-symlink" 18 "mr-3"}}{{else}}{{svg "octicon-project" 18 "mr-3"}}{{end}} - {{.Title}} +
+ {{if .IsOrganizationProject}} + {{svg "octicon-project-symlink" 18 "mr-3"}} + {{else if .IsIndividualProject}} + {{svg "octicon-project-symlink" 18 "mr-3"}}{{/*TODO: Change Individual Project Icon*/}} + {{else}} + {{svg "octicon-project" 18 "mr-3"}} + {{end}} + {{.Name}}
{{end}} {{end}} @@ -271,9 +289,15 @@ {{.locale.Tr "repo.issues.new.closed_projects"}}
{{range .ClosedProjects}} -
- {{if .IsOrganizationProject}}{{svg "octicon-project-symlink" 18 "mr-3"}}{{else}}{{svg "octicon-project" 18 "mr-3"}}{{end}} - {{.Title}} +
+ {{if .IsOrganizationProject}} + {{svg "octicon-project-symlink" 18 "mr-3"}} + {{else if .IsIndividualProject}} + {{svg "octicon-project-symlink" 18 "mr-3"}}{{/*TODO: Change Individual Project Icon*/}} + {{else}} + {{svg "octicon-project" 18 "mr-3"}} + {{end}} + {{.Name}}
{{end}} {{end}} diff --git a/templates/repo/issue/new_form.tmpl b/templates/repo/issue/new_form.tmpl index b08863237a6e..d99d0da52e54 100644 --- a/templates/repo/issue/new_form.tmpl +++ b/templates/repo/issue/new_form.tmpl @@ -164,17 +164,16 @@ {{.locale.Tr "repo.issues.new.open_projects"}}
{{range .OpenProjects}} - {{if .RepoID}} - - {{svg "octicon-project" 18 "mr-3"}} - {{.Owner.Name}}/{{.Repo.Name}}/{{.Title}} - - {{else}} - - {{svg "octicon-project" 18 "mr-3"}} - {{.Owner.Name}}/-/{{.Title}} + + {{if .IsOrganizationProject}} + {{svg "octicon-project-symlink" 18 "mr-3"}} + {{else if .IsIndividualProject}} + {{svg "octicon-project-symlink" 18 "mr-3"}}{{/*TODO: Change Individual Project Icon*/}} + {{else}} + {{svg "octicon-project" 18 "mr-3"}} + {{end}} + {{.Name}} - {{end}} {{end}} {{end}} {{if .ClosedProjects}} @@ -183,17 +182,16 @@ {{.locale.Tr "repo.issues.new.closed_projects"}}
{{range .ClosedProjects}} - {{if .RepoID}} - - {{svg "octicon-project" 18 "mr-3"}} - {{.Title}} - - {{else}} - - {{svg "octicon-project" 18 "mr-3"}} - {{.Owner.Name}}/-/{{.Title}} + + {{if .IsOrganizationProject}} + {{svg "octicon-project-symlink" 18 "mr-3"}} + {{else if .IsIndividualProject}} + {{svg "octicon-project-symlink" 18 "mr-3"}}{{/*TODO: Change Individual Project Icon*/}} + {{else}} + {{svg "octicon-project" 18 "mr-3"}} + {{end}} + {{.Name}} - {{end}} {{end}} {{end}} {{end}} @@ -203,9 +201,15 @@ {{.locale.Tr "repo.issues.new.no_projects"}} diff --git a/templates/repo/issue/view_content/comments.tmpl b/templates/repo/issue/view_content/comments.tmpl index 37aa82345fa5..158e7e336d1b 100644 --- a/templates/repo/issue/view_content/comments.tmpl +++ b/templates/repo/issue/view_content/comments.tmpl @@ -720,12 +720,12 @@ {{template "shared/user/authorlink" .Poster}} {{if gt .OldProjectID 0}} {{if gt .ProjectID 0}} - {{$.locale.Tr "repo.issues.change_project_at" (.OldProject.Title|Escape) (.Project.Title|Escape) $createdStr | Safe}} + {{$.locale.Tr "repo.issues.change_project_at" (.OldProject.Name|Escape) (.Project.Name|Escape) $createdStr | Safe}} {{else}} - {{$.locale.Tr "repo.issues.remove_project_at" (.OldProject.Title|Escape) $createdStr | Safe}} + {{$.locale.Tr "repo.issues.remove_project_at" (.OldProject.Name|Escape) $createdStr | Safe}} {{end}} {{else if gt .ProjectID 0}} - {{$.locale.Tr "repo.issues.add_project_at" (.Project.Title|Escape) $createdStr | Safe}} + {{$.locale.Tr "repo.issues.add_project_at" (.Project.Name|Escape) $createdStr | Safe}} {{end}}
diff --git a/templates/repo/issue/view_content/sidebar.tmpl b/templates/repo/issue/view_content/sidebar.tmpl index 10bb6a07f971..0be67834d831 100644 --- a/templates/repo/issue/view_content/sidebar.tmpl +++ b/templates/repo/issue/view_content/sidebar.tmpl @@ -227,8 +227,14 @@ {{range .OpenProjects}} - {{if .IsOrganizationProject}}{{svg "octicon-project-symlink" 18 "mr-3"}}{{else}}{{svg "octicon-project" 18 "mr-3"}}{{end}} - {{.Title}} + {{if .IsOrganizationProject}} + {{svg "octicon-project-symlink" 18 "mr-3"}} + {{else if .IsIndividualProject}} + {{svg "octicon-project-symlink" 18 "mr-3"}}{{/*TODO: Change Individual Project Icon*/}} + {{else}} + {{svg "octicon-project" 18 "mr-3"}} + {{end}} + {{.Name}} {{end}} {{end}} @@ -239,8 +245,14 @@ {{range .ClosedProjects}} - {{if .IsOrganizationProject}}{{svg "octicon-project-symlink" 18 "mr-3"}}{{else}}{{svg "octicon-project" 18 "mr-3"}}{{end}} - {{.Title}} + {{if .IsOrganizationProject}} + {{svg "octicon-project-symlink" 18 "mr-3"}} + {{else if .IsIndividualProject}} + {{svg "octicon-project-symlink" 18 "mr-3"}}{{/*TODO: Change Individual Project Icon*/}} + {{else}} + {{svg "octicon-project" 18 "mr-3"}} + {{end}} + {{.Name}} {{end}} {{end}} @@ -251,8 +263,14 @@
{{if .Issue.ProjectID}} - {{if .IsOrganizationProject}}{{svg "octicon-project-symlink" 18 "mr-3"}}{{else}}{{svg "octicon-project" 18 "mr-3"}}{{end}} - {{.Issue.Project.Title}} + {{if .Issue.Project.IsOrganizationProject}} + {{svg "octicon-project-symlink" 18 "mr-3"}} + {{else if .Issue.Project.IsIndividualProject}} + {{svg "octicon-project-symlink" 18 "mr-3"}}{{/*TODO: Change Individual Project Icon*/}} + {{else}} + {{svg "octicon-project" 18 "mr-3"}} + {{end}} + {{.Issue.Project.Name}} {{end}}
diff --git a/templates/repo/projects/list.tmpl b/templates/repo/projects/list.tmpl index f717934b3373..b080acd6e7c8 100644 --- a/templates/repo/projects/list.tmpl +++ b/templates/repo/projects/list.tmpl @@ -32,7 +32,7 @@ @@ -40,7 +40,7 @@
{{range .Projects}}
  • - {{svg "octicon-project"}} {{.Title}} + {{svg "octicon-project"}} {{.Title}}
    {{$closedDate:= TimeSinceUnix .ClosedDateUnix $.locale}} {{if .IsClosed}} @@ -55,13 +55,13 @@
    {{if and (or $.CanWriteIssues $.CanWritePulls) (not $.Repository.IsArchived)}} {{end}} {{if .Description}} diff --git a/templates/repo/projects/new.tmpl b/templates/repo/projects/new.tmpl index c90fa4369c40..13273e7757ba 100644 --- a/templates/repo/projects/new.tmpl +++ b/templates/repo/projects/new.tmpl @@ -24,6 +24,7 @@ {{.CsrfTokenHtml}}
    +
    @@ -65,7 +66,6 @@
  • -
    diff --git a/templates/repo/projects/view.tmpl b/templates/repo/projects/view.tmpl index 711b48818034..628d78321af3 100644 --- a/templates/repo/projects/view.tmpl +++ b/templates/repo/projects/view.tmpl @@ -50,7 +50,7 @@ {{if and (or $.CanWriteIssues $.CanWritePulls) (not $.Repository.IsArchived)}}
    {{range .OpenProjects}} -
    +
    {{if .IsOrganizationProject}} {{svg "octicon-project-symlink" 18 "mr-3"}} {{else if .IsIndividualProject}} @@ -289,7 +289,7 @@ {{.locale.Tr "repo.issues.new.closed_projects"}}
    {{range .ClosedProjects}} -
    +
    {{if .IsOrganizationProject}} {{svg "octicon-project-symlink" 18 "mr-3"}} {{else if .IsIndividualProject}} From 87d98fc87955cc5f898f07de64a6ea8882f3813c Mon Sep 17 00:00:00 2001 From: yp05327 <576951401@qq.com> Date: Thu, 16 Feb 2023 18:06:39 +0900 Subject: [PATCH 20/69] add TODO --- models/issues/issue_project.go | 2 +- routers/web/repo/issue.go | 7 ++++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/models/issues/issue_project.go b/models/issues/issue_project.go index ed3dbca413bd..dd97c72f3e23 100644 --- a/models/issues/issue_project.go +++ b/models/issues/issue_project.go @@ -63,6 +63,7 @@ func (issue *Issue) projectBoardID(ctx context.Context) int64 { func LoadIssuesFromBoard(ctx context.Context, b *project_model.Board) (IssueList, error) { issueList := make([]*Issue, 0, 10) + // FIXME: add a opts arg to check org/team/user permission if b.ID != 0 { issues, err := Issues(ctx, &IssuesOptions{ ProjectBoardID: b.ID, @@ -142,7 +143,6 @@ func addUpdateIssueProject(ctx context.Context, issue *Issue, doer *user_model.U return fmt.Errorf("issue's repository is not the same as project's repository") } case project_model.TypeOrganization: - // TODO: org team permission check if newProject.OwnerID != issue.Repo.OwnerID { return fmt.Errorf("issue's repository's owner is not the same as project's repository's owner") } diff --git a/routers/web/repo/issue.go b/routers/web/repo/issue.go index b40032cbef3b..8b5f3d811efe 100644 --- a/routers/web/repo/issue.go +++ b/routers/web/repo/issue.go @@ -872,6 +872,7 @@ func NewIssue(ctx *context.Context) { } } + // TODO: add org/user project support projectID := ctx.FormInt64("project") if projectID > 0 && isProjectsEnabled { project, err := project_model.GetProjectByID(ctx, projectID) @@ -1030,6 +1031,7 @@ func ValidateRepoMetas(ctx *context.Context, form forms.CreateIssueForm, isPull } projectLink := "" + projectId := int64(0) if form.ProjectID > 0 { p, err := project_model.GetProjectByID(ctx, form.ProjectID) if err != nil { @@ -1041,9 +1043,12 @@ func ValidateRepoMetas(ctx *context.Context, form forms.CreateIssueForm, isPull return nil, nil, 0, 0, "" } + // TODO: check project accessibility, if it is not accessable, return 0 + ctx.Data["Project"] = p ctx.Data["project_id"] = form.ProjectID + projectId = form.ProjectID projectLink = p.Link() } @@ -1081,7 +1086,7 @@ func ValidateRepoMetas(ctx *context.Context, form forms.CreateIssueForm, isPull assigneeIDs = append(assigneeIDs, form.AssigneeID) } - return labelIDs, assigneeIDs, milestoneID, form.ProjectID, projectLink + return labelIDs, assigneeIDs, milestoneID, projectId, projectLink } // NewIssuePost response for creating new issue From ebb6e83dffb8bedee2ce268c06f6c6d5b36fb914 Mon Sep 17 00:00:00 2001 From: yp05327 <576951401@qq.com> Date: Fri, 17 Feb 2023 13:10:44 +0900 Subject: [PATCH 21/69] fix retrieve projects in an issue --- models/organization/org.go | 25 ++++++ models/project/project.go | 35 ++++++++ modules/context/org.go | 30 +------ routers/web/repo/issue.go | 177 ++++++++++++++++++++++++------------- 4 files changed, 177 insertions(+), 90 deletions(-) diff --git a/models/organization/org.go b/models/organization/org.go index f05027be729d..f98b9378204f 100644 --- a/models/organization/org.go +++ b/models/organization/org.go @@ -239,6 +239,31 @@ func (org *Organization) CustomAvatarRelativePath() string { return org.Avatar } +func (org *Organization) UnitPermission(ctx context.Context, doerID int64, unitType unit.Type) perm.AccessMode { + if doerID > 0 { + teams, err := GetUserOrgTeams(ctx, org.ID, doerID) + if err != nil { + log.Error("GetUserOrgTeams: %v", err) + return perm.AccessModeNone + } + + if err := teams.LoadUnits(ctx); err != nil { + log.Error("LoadUnits: %v", err) + return perm.AccessModeNone + } + + if len(teams) > 0 { + return teams.UnitMaxAccess(unitType) + } + } + + if org.Visibility == structs.VisibleTypePublic { + return perm.AccessModeRead + } + + return perm.AccessModeNone +} + // CreateOrganization creates record of a new organization. func CreateOrganization(org *Organization, owner *user_model.User) (err error) { if !owner.CanCreateOrganization() { diff --git a/models/project/project.go b/models/project/project.go index 1715bcd6471a..c1e2a77785f6 100644 --- a/models/project/project.go +++ b/models/project/project.go @@ -8,7 +8,10 @@ import ( "fmt" "code.gitea.io/gitea/models/db" + org_model "code.gitea.io/gitea/models/organization" + "code.gitea.io/gitea/models/perm" repo_model "code.gitea.io/gitea/models/repo" + "code.gitea.io/gitea/models/unit" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" @@ -210,6 +213,38 @@ func (p *Project) IsIndividualProject() bool { return p.Type == TypeIndividual } +func (p *Project) IsRepositoryProject() bool { + return p.Type == TypeRepository +} + +// CanRetrievedByDoer return whether project can retrieved by a doer in a repo +func (p *Project) CanRetrievedByDoer(ctx context.Context, repo *repo_model.Repository, doerID int64) (bool, error) { + if err := repo.GetOwner(ctx); err != nil { + return false, fmt.Errorf("GetOwner: %w", err) + } + + if p.RepoID > 0 { + // repo's project + if p.RepoID != repo.ID { + return false, nil + } + } else { + // individual/org's project + if p.OwnerID != repo.OwnerID { + return false, nil + } + + if repo.Owner.IsOrganization() { + // check doer read permission + if (*org_model.Organization)(repo.Owner).UnitPermission(ctx, doerID, unit.TypeProjects) < perm.AccessModeRead { + return false, nil + } + } + } + + return true, nil +} + // GetProjectTypeByUser retrieves the type of a project by user's type func GetProjectTypeByUser(user *user_model.User) (Type, error) { switch user.Type { diff --git a/modules/context/org.go b/modules/context/org.go index cdcff9c1b7aa..9fbf099f0e6e 100644 --- a/modules/context/org.go +++ b/modules/context/org.go @@ -11,7 +11,6 @@ import ( "code.gitea.io/gitea/models/perm" "code.gitea.io/gitea/models/unit" user_model "code.gitea.io/gitea/models/user" - "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/structs" ) @@ -34,39 +33,14 @@ func (org *Organization) CanWriteUnit(ctx *Context, unitType unit.Type) bool { if ctx.Doer == nil { return false } - return org.UnitPermission(ctx, ctx.Doer.ID, unitType) >= perm.AccessModeWrite + return org.Organization.UnitPermission(ctx, ctx.Doer.ID, unitType) >= perm.AccessModeWrite } func (org *Organization) CanAccessUnit(ctx *Context, unitType unit.Type) bool { if ctx.Doer == nil { return false } - return org.UnitPermission(ctx, ctx.Doer.ID, unitType) >= perm.AccessModeRead -} - -func (org *Organization) UnitPermission(ctx *Context, doerID int64, unitType unit.Type) perm.AccessMode { - if doerID > 0 { - teams, err := organization.GetUserOrgTeams(ctx, org.Organization.ID, doerID) - if err != nil { - log.Error("GetUserOrgTeams: %v", err) - return perm.AccessModeNone - } - - if err := teams.LoadUnits(ctx); err != nil { - log.Error("LoadUnits: %v", err) - return perm.AccessModeNone - } - - if len(teams) > 0 { - return teams.UnitMaxAccess(unitType) - } - } - - if org.Organization.Visibility == structs.VisibleTypePublic { - return perm.AccessModeRead - } - - return perm.AccessModeNone + return org.Organization.UnitPermission(ctx, ctx.Doer.ID, unitType) >= perm.AccessModeRead } func GetOrganizationByParams(ctx *Context) { diff --git a/routers/web/repo/issue.go b/routers/web/repo/issue.go index 8b5f3d811efe..f2d643f0b114 100644 --- a/routers/web/repo/issue.go +++ b/routers/web/repo/issue.go @@ -468,39 +468,63 @@ func RetrieveRepoMilestonesAndAssignees(ctx *context.Context, repo *repo_model.R } func retrieveProjects(ctx *context.Context, repo *repo_model.Repository) { + var projects project_model.List var err error - projects, _, err := project_model.FindProjects(ctx, project_model.SearchOptions{ - RepoID: repo.ID, - Page: -1, - IsClosed: util.OptionalBoolFalse, - Type: project_model.TypeRepository, - }) - if err != nil { - ctx.ServerError("GetProjects", err) + + if err := repo.GetOwner(ctx); err != nil { + ctx.ServerError("GetOwner", err) return } - projectsOrg, _, err := project_model.FindProjects(ctx, project_model.SearchOptions{ - OwnerID: repo.OwnerID, - Page: -1, - IsClosed: util.OptionalBoolFalse, - Type: project_model.TypeOrganization, - }) - if err != nil { - ctx.ServerError("GetProjects", err) - return + + // retrieve this repo's projects + if ctx.Repo.CanRead(unit.TypeProjects) { + projects, _, err = project_model.FindProjects(ctx, project_model.SearchOptions{ + RepoID: repo.ID, + Page: -1, + IsClosed: util.OptionalBoolFalse, + Type: project_model.TypeRepository, + }) + if err != nil { + ctx.ServerError("GetProjects", err) + return + } } - projects = append(projects, projectsOrg...) - projectsUser, _, err := project_model.FindProjects(ctx, project_model.SearchOptions{ - OwnerID: ctx.Doer.ID, - Page: -1, - IsClosed: util.OptionalBoolFalse, - Type: project_model.TypeIndividual, - }) - if err != nil { - ctx.ServerError("GetProjects", err) - return + // repo's owner can retrieve his individual projects + if ctx.Doer.ID == repo.OwnerID { + projectsRepoOwner, _, err := project_model.FindProjects(ctx, project_model.SearchOptions{ + OwnerID: repo.OwnerID, + Page: -1, + IsClosed: util.OptionalBoolFalse, + Type: project_model.TypeIndividual, + }) + if err != nil { + ctx.ServerError("GetProjects", err) + return + } + projects = append(projects, projectsRepoOwner...) + } + // org repo's projects + if ctx.ContextUser.IsOrganization() { + projectsOrg, _, err := project_model.FindProjects(ctx, project_model.SearchOptions{ + OwnerID: repo.OwnerID, + Page: -1, + IsClosed: util.OptionalBoolFalse, + Type: project_model.TypeOrganization, + }) + if err != nil { + ctx.ServerError("GetProjects", err) + return + } + for _, p := range projectsOrg { + if canRetrievedByDoer, err := p.CanRetrievedByDoer(ctx, repo, ctx.Doer.ID); err != nil { + ctx.ServerError("CanRetrievedByDoer", err) + return + } else if canRetrievedByDoer { + projects = append(projects, p) + } + } } - ctx.Data["OpenProjects"] = append(projects, projectsUser...) + ctx.Data["OpenProjects"] = projects projects, _, err = project_model.FindProjects(ctx, project_model.SearchOptions{ RepoID: repo.ID, @@ -512,29 +536,41 @@ func retrieveProjects(ctx *context.Context, repo *repo_model.Repository) { ctx.ServerError("GetProjects", err) return } - projectsOrg, _, err = project_model.FindProjects(ctx, project_model.SearchOptions{ - OwnerID: repo.OwnerID, - Page: -1, - IsClosed: util.OptionalBoolTrue, - Type: project_model.TypeOrganization, - }) - if err != nil { - ctx.ServerError("GetProjects", err) - return + // repo's owner can retrieve his individual projects + if ctx.Doer.ID == repo.OwnerID { + projectsRepoOwner, _, err := project_model.FindProjects(ctx, project_model.SearchOptions{ + OwnerID: repo.OwnerID, + Page: -1, + IsClosed: util.OptionalBoolTrue, + Type: project_model.TypeIndividual, + }) + if err != nil { + ctx.ServerError("GetProjects", err) + return + } + projects = append(projects, projectsRepoOwner...) } - projects = append(projects, projectsOrg...) - projectsUser, _, err = project_model.FindProjects(ctx, project_model.SearchOptions{ - OwnerID: ctx.Doer.ID, - Page: -1, - IsClosed: util.OptionalBoolTrue, - Type: project_model.TypeIndividual, - }) - if err != nil { - ctx.ServerError("GetProjects", err) - return + if ctx.ContextUser.IsOrganization() { + projectsOrg, _, err := project_model.FindProjects(ctx, project_model.SearchOptions{ + OwnerID: repo.OwnerID, + Page: -1, + IsClosed: util.OptionalBoolTrue, + Type: project_model.TypeOrganization, + }) + if err != nil { + ctx.ServerError("GetProjects", err) + return + } + for _, p := range projectsOrg { + if canRetrievedByDoer, err := p.CanRetrievedByDoer(ctx, repo, ctx.Doer.ID); err != nil { + ctx.ServerError("CanRetrievedByDoer", err) + return + } else if canRetrievedByDoer { + projects = append(projects, p) + } + } } - - ctx.Data["ClosedProjects"] = append(projects, projectsUser...) + ctx.Data["ClosedProjects"] = projects } // repoReviewerSelection items to bee shown @@ -872,17 +908,23 @@ func NewIssue(ctx *context.Context) { } } - // TODO: add org/user project support projectID := ctx.FormInt64("project") - if projectID > 0 && isProjectsEnabled { + if projectID > 0 { project, err := project_model.GetProjectByID(ctx, projectID) if err != nil { log.Error("GetProjectByID: %d: %v", projectID, err) - } else if project.RepoID != ctx.Repo.Repository.ID { - log.Error("GetProjectByID: %d: %v", projectID, fmt.Errorf("project[%d] not found", project.ID)) } else { - ctx.Data["project_id"] = projectID - ctx.Data["Project"] = project + canRetrievedByDoer, err := project.CanRetrievedByDoer(ctx, ctx.Repo.Repository, ctx.Doer.ID) + if err != nil { + log.Error("CanRetrievedByDoer: %d: %v", projectID, err) + } else if !canRetrievedByDoer { + log.Error("CanRetrievedByDoer: %d: %v", projectID, fmt.Errorf("project[%d] not found", project.ID)) + } else if project.IsRepositoryProject() && !isProjectsEnabled { + log.Error("CanRetrievedByDoer: %d: %v", projectID, fmt.Errorf("projects is not enabled in repo[%d]", ctx.Repo.Repository.ID)) + } else { + ctx.Data["project_id"] = projectID + ctx.Data["Project"] = project + } } if len(ctx.Req.URL.Query().Get("project")) > 0 { @@ -954,6 +996,7 @@ func NewIssueChooseTemplate(ctx *context.Context) { } ctx.Data["milestone"] = ctx.FormInt64("milestone") + // TODO: invaild projectid check? ctx.Data["project"] = ctx.FormInt64("project") ctx.HTML(http.StatusOK, tplIssueChoose) @@ -1043,13 +1086,21 @@ func ValidateRepoMetas(ctx *context.Context, form forms.CreateIssueForm, isPull return nil, nil, 0, 0, "" } - // TODO: check project accessibility, if it is not accessable, return 0 - - ctx.Data["Project"] = p - ctx.Data["project_id"] = form.ProjectID + // if projects unit of this repo is disabled in the repo, it will redirect to a 404 page + // if user have no access permission to the project, it will alse redirect to a 404 page + // so we need to return empty projectLink here + if canRetrievedByDoer, err := p.CanRetrievedByDoer(ctx, ctx.Repo.Repository, ctx.Doer.ID); err != nil { + ctx.ServerError("CanRetrievedByDoer", err) + return nil, nil, 0, 0, "" + } else if canRetrievedByDoer { + if !(p.IsRepositoryProject() && !ctx.Repo.CanRead(unit.TypeProjects)) { + ctx.Data["Project"] = p + ctx.Data["project_id"] = form.ProjectID - projectId = form.ProjectID - projectLink = p.Link() + projectId = form.ProjectID + projectLink = p.Link() + } + } } // Check assignees @@ -1163,7 +1214,7 @@ func NewIssuePost(ctx *context.Context) { } log.Trace("Issue created: %d/%d", repo.ID, issue.ID) - if ctx.FormString("redirect_after_creation") == "project" { + if ctx.FormString("redirect_after_creation") == "project" && projectLink != "" { ctx.Redirect(projectLink) } else { ctx.Redirect(issue.Link()) @@ -2388,6 +2439,7 @@ func SearchIssues(ctx *context.Context) { includedMilestones = strings.Split(milestones, ",") } + // TODO:invaild projectid check? projectID := ctx.FormInt64("project") // this api is also used in UI, @@ -2550,6 +2602,7 @@ func ListIssues(ctx *context.Context) { } } + // TODO: invaild projectid check? projectID := ctx.FormInt64("project") listOptions := db.ListOptions{ From 762cfa63a8f52743f6742d9ab07c91025d79df82 Mon Sep 17 00:00:00 2001 From: yp05327 <576951401@qq.com> Date: Fri, 17 Feb 2023 13:46:46 +0900 Subject: [PATCH 22/69] fix 'new project' button in repo projects page --- routers/web/repo/projects.go | 2 +- templates/repo/projects/list.tmpl | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/routers/web/repo/projects.go b/routers/web/repo/projects.go index e188501f3307..f51a8512038b 100644 --- a/routers/web/repo/projects.go +++ b/routers/web/repo/projects.go @@ -113,7 +113,7 @@ func Projects(ctx *context.Context) { pager.AddParam(ctx, "state", "State") ctx.Data["Page"] = pager - ctx.Data["CanWriteProjects"] = true + ctx.Data["CanWriteProjects"] = ctx.Repo.Permission.CanWrite(unit.TypeProjects) ctx.Data["IsShowClosed"] = isShowClosed ctx.Data["IsProjectsPage"] = true ctx.Data["SortType"] = sortType diff --git a/templates/repo/projects/list.tmpl b/templates/repo/projects/list.tmpl index 7432298a3b78..5e8e00c6957c 100644 --- a/templates/repo/projects/list.tmpl +++ b/templates/repo/projects/list.tmpl @@ -53,7 +53,7 @@ {{JsPrettyNumber .NumClosedIssues}} {{$.locale.Tr "repo.issues.closed_title"}}
    - {{if and (or $.CanWriteIssues $.CanWritePulls) (not $.Repository.IsArchived)}} + {{if and $.CanWriteProjects (not $.Repository.IsArchived)}}
    -{{if or .CanWriteIssues .CanWritePulls}} +{{if .CanWriteProjects}} {{range .ClosedProjects}} - {{if .IsOrganizationProject}} + {{if .IsUserProject}} {{svg "octicon-project-symlink" 18 "mr-3"}} - {{else if .IsIndividualProject}} - {{svg "octicon-project-symlink" 18 "mr-3"}}{{/*TODO: Change Individual Project Icon*/}} {{else}} {{svg "octicon-project" 18 "mr-3"}} {{end}} @@ -272,10 +268,8 @@
    {{range .OpenProjects}}
    - {{if .IsOrganizationProject}} + {{if .IsUserProject}} {{svg "octicon-project-symlink" 18 "mr-3"}} - {{else if .IsIndividualProject}} - {{svg "octicon-project-symlink" 18 "mr-3"}}{{/*TODO: Change Individual Project Icon*/}} {{else}} {{svg "octicon-project" 18 "mr-3"}} {{end}} @@ -290,10 +284,8 @@
    {{range .ClosedProjects}}
    - {{if .IsOrganizationProject}} + {{if .IsUserProject}} {{svg "octicon-project-symlink" 18 "mr-3"}} - {{else if .IsIndividualProject}} - {{svg "octicon-project-symlink" 18 "mr-3"}}{{/*TODO: Change Individual Project Icon*/}} {{else}} {{svg "octicon-project" 18 "mr-3"}} {{end}} diff --git a/templates/repo/issue/new_form.tmpl b/templates/repo/issue/new_form.tmpl index 37561b645afe..3274c6f28d46 100644 --- a/templates/repo/issue/new_form.tmpl +++ b/templates/repo/issue/new_form.tmpl @@ -165,10 +165,8 @@
    {{range .OpenProjects}}
    - {{if .IsOrganizationProject}} + {{if .IsUserProject}} {{svg "octicon-project-symlink" 18 "mr-3"}} - {{else if .IsIndividualProject}} - {{svg "octicon-project-symlink" 18 "mr-3"}}{{/*TODO: Change Individual Project Icon*/}} {{else}} {{svg "octicon-project" 18 "mr-3"}} {{end}} @@ -183,10 +181,8 @@
    {{range .ClosedProjects}} - {{if .IsOrganizationProject}} + {{if .IsUserProject}} {{svg "octicon-project-symlink" 18 "mr-3"}} - {{else if .IsIndividualProject}} - {{svg "octicon-project-symlink" 18 "mr-3"}}{{/*TODO: Change Individual Project Icon*/}} {{else}} {{svg "octicon-project" 18 "mr-3"}} {{end}} @@ -202,10 +198,8 @@
    {{if .Project}} - {{if .Project.IsOrganizationProject}} + {{if .Project.IsUserProject}} {{svg "octicon-project-symlink" 18 "mr-3"}} - {{else if .Project.IsIndividualProject}} - {{svg "octicon-project-symlink" 18 "mr-3"}}{{/*TODO: Change Individual Project Icon*/}} {{else}} {{svg "octicon-project" 18 "mr-3"}} {{end}} diff --git a/templates/repo/issue/view_content/sidebar.tmpl b/templates/repo/issue/view_content/sidebar.tmpl index 37a54327bf4e..4f3cdfc40519 100644 --- a/templates/repo/issue/view_content/sidebar.tmpl +++ b/templates/repo/issue/view_content/sidebar.tmpl @@ -227,10 +227,8 @@
    {{range .OpenProjects}} - {{if .IsOrganizationProject}} + {{if .IsUserProject}} {{svg "octicon-project-symlink" 18 "mr-3"}} - {{else if .IsIndividualProject}} - {{svg "octicon-project-symlink" 18 "mr-3"}}{{/*TODO: Change Individual Project Icon*/}} {{else}} {{svg "octicon-project" 18 "mr-3"}} {{end}} @@ -245,10 +243,8 @@
    {{range .ClosedProjects}} - {{if .IsOrganizationProject}} + {{if .IsUserProject}} {{svg "octicon-project-symlink" 18 "mr-3"}} - {{else if .IsIndividualProject}} - {{svg "octicon-project-symlink" 18 "mr-3"}}{{/*TODO: Change Individual Project Icon*/}} {{else}} {{svg "octicon-project" 18 "mr-3"}} {{end}} @@ -263,10 +259,8 @@
    {{if .Issue.ProjectID}} - {{if .Issue.Project.IsOrganizationProject}} + {{if .Issue.Project.IsUserProject}} {{svg "octicon-project-symlink" 18 "mr-3"}} - {{else if .Issue.Project.IsIndividualProject}} - {{svg "octicon-project-symlink" 18 "mr-3"}}{{/*TODO: Change Individual Project Icon*/}} {{else}} {{svg "octicon-project" 18 "mr-3"}} {{end}} diff --git a/templates/shared/issuelist.tmpl b/templates/shared/issuelist.tmpl index 299bcb5d8e43..2a43bad6e041 100644 --- a/templates/shared/issuelist.tmpl +++ b/templates/shared/issuelist.tmpl @@ -88,10 +88,8 @@ {{end}} {{if .Project}} - {{if .Project.IsOrganizationProject}} + {{if .Project.IsUserProject}} {{svg "octicon-project-symlink" 14 "mr-2"}} - {{else if .Project.IsIndividualProject}} - {{svg "octicon-project-symlink" 14 "mr-2"}}{{/*TODO: Change Individual Project Icon*/}} {{else}} {{svg "octicon-project" 14 "mr-2"}} {{end}} From c0677c37c7c7ebb7908a729f84682faab3a46d10 Mon Sep 17 00:00:00 2001 From: yp05327 <576951401@qq.com> Date: Fri, 17 Feb 2023 17:24:49 +0900 Subject: [PATCH 24/69] fix wrong project link --- templates/shared/issuelist.tmpl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/shared/issuelist.tmpl b/templates/shared/issuelist.tmpl index 2a43bad6e041..d4f010864706 100644 --- a/templates/shared/issuelist.tmpl +++ b/templates/shared/issuelist.tmpl @@ -87,7 +87,7 @@ {{end}} {{if .Project}} - + {{if .Project.IsUserProject}} {{svg "octicon-project-symlink" 14 "mr-2"}} {{else}} From f9c3351d797d903b634370e2e8af0341c693bcb5 Mon Sep 17 00:00:00 2001 From: yp05327 <576951401@qq.com> Date: Sat, 18 Feb 2023 13:32:38 +0900 Subject: [PATCH 25/69] fix permission check in projectboard --- models/issues/issue.go | 43 ++++++++++++++++++++++++++++++++++ models/issues/issue_project.go | 23 +++++++++++++----- models/project/project.go | 13 +++++++--- routers/web/org/projects.go | 2 +- routers/web/repo/projects.go | 2 +- 5 files changed, 72 insertions(+), 11 deletions(-) diff --git a/models/issues/issue.go b/models/issues/issue.go index e0dcf3d269ee..d8c649089803 100644 --- a/models/issues/issue.go +++ b/models/issues/issue.go @@ -476,6 +476,49 @@ func (issue *Issue) getLabels(ctx context.Context) (err error) { return nil } +// CanRetrievedByDoer returns whether doer can retrieve the issue +func (issue *Issue) CanRetrievedByDoer(ctx context.Context, p *project_model.Project, doerID int64) (bool, error) { + if err := issue.LoadRepo(ctx); err != nil { + return false, err + } + + if unit.TypeIssues.UnitGlobalDisabled() { + return false, nil + } + + if err := issue.Repo.GetOwner(ctx); err != nil { + return false, err + } + + if !issue.Repo.UnitEnabled(ctx, unit.TypeIssues) { + return false, nil + } + + if p.RepoID > 0 { + if p.RepoID != issue.RepoID { + return false, nil + } + } else { + // individual/org's project + if p.OwnerID != issue.Repo.OwnerID { + return false, nil + } + + if issue.Repo.Owner.IsIndividual() && issue.Repo.Owner.ID != doerID { + return false, nil + } + + if issue.Repo.Owner.IsOrganization() { + // check doer read permission + if (*organization.Organization)(issue.Repo.Owner).UnitPermission(ctx, doerID, unit.TypeIssues) < perm.AccessModeRead { + return false, nil + } + } + } + + return true, nil +} + func clearIssueLabels(ctx context.Context, issue *Issue, doer *user_model.User) (err error) { if err = issue.getLabels(ctx); err != nil { return fmt.Errorf("getLabels: %w", err) diff --git a/models/issues/issue_project.go b/models/issues/issue_project.go index c17fed96a231..4bdb6c9f83c8 100644 --- a/models/issues/issue_project.go +++ b/models/issues/issue_project.go @@ -60,10 +60,9 @@ func (issue *Issue) projectBoardID(ctx context.Context) int64 { } // LoadIssuesFromBoard load issues assigned to this board -func LoadIssuesFromBoard(ctx context.Context, b *project_model.Board) (IssueList, error) { +func LoadIssuesFromBoard(ctx context.Context, b *project_model.Board, p *project_model.Project, doerID int64) (IssueList, error) { issueList := make([]*Issue, 0, 10) - // FIXME: add a opts arg to check org/team/user permission if b.ID != 0 { issues, err := Issues(ctx, &IssuesOptions{ ProjectBoardID: b.ID, @@ -73,7 +72,13 @@ func LoadIssuesFromBoard(ctx context.Context, b *project_model.Board) (IssueList if err != nil { return nil, err } - issueList = issues + for _, issue := range issues { + if canRetrievedByDoer, err := issue.CanRetrievedByDoer(ctx, p, doerID); err != nil { + return nil, err + } else if canRetrievedByDoer { + issueList = append(issueList, issue) + } + } } if b.Default { @@ -85,7 +90,13 @@ func LoadIssuesFromBoard(ctx context.Context, b *project_model.Board) (IssueList if err != nil { return nil, err } - issueList = append(issueList, issues...) + for _, issue := range issues { + if canRetrievedByDoer, err := issue.CanRetrievedByDoer(ctx, p, doerID); err != nil { + return nil, err + } else if canRetrievedByDoer { + issueList = append(issueList, issue) + } + } } if err := IssueList(issueList).LoadComments(ctx); err != nil { @@ -96,10 +107,10 @@ func LoadIssuesFromBoard(ctx context.Context, b *project_model.Board) (IssueList } // LoadIssuesFromBoardList load issues assigned to the boards -func LoadIssuesFromBoardList(ctx context.Context, bs project_model.BoardList) (map[int64]IssueList, error) { +func LoadIssuesFromBoardList(ctx context.Context, bs project_model.BoardList, p *project_model.Project, doerID int64) (map[int64]IssueList, error) { issuesMap := make(map[int64]IssueList, len(bs)) for i := range bs { - il, err := LoadIssuesFromBoard(ctx, bs[i]) + il, err := LoadIssuesFromBoard(ctx, bs[i], p, doerID) if err != nil { return nil, err } diff --git a/models/project/project.go b/models/project/project.go index 3819a9adb2d2..dbd0e4c7725d 100644 --- a/models/project/project.go +++ b/models/project/project.go @@ -8,7 +8,7 @@ import ( "fmt" "code.gitea.io/gitea/models/db" - org_model "code.gitea.io/gitea/models/organization" + "code.gitea.io/gitea/models/organization" "code.gitea.io/gitea/models/perm" repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/models/unit" @@ -210,16 +210,23 @@ func (p *Project) IsRepositoryProject() bool { return p.Type == TypeRepository } -// CanRetrievedByDoer return whether project can retrieved by a doer in a repo +// CanRetrievedByDoer return whether project can be retrieved by a doer in a repo func (p *Project) CanRetrievedByDoer(ctx context.Context, repo *repo_model.Repository, doerID int64) (bool, error) { if err := repo.GetOwner(ctx); err != nil { return false, fmt.Errorf("GetOwner: %w", err) } + if unit.TypeProjects.UnitGlobalDisabled() { + return false, nil + } + if p.RepoID > 0 { if p.RepoID != repo.ID { return false, nil } + if !repo.UnitEnabled(ctx, unit.TypeProjects) { + return false, nil + } } else { // individual/org's project if p.OwnerID != repo.OwnerID { @@ -232,7 +239,7 @@ func (p *Project) CanRetrievedByDoer(ctx context.Context, repo *repo_model.Repos if repo.Owner.IsOrganization() { // check doer read permission - if (*org_model.Organization)(repo.Owner).UnitPermission(ctx, doerID, unit.TypeProjects) < perm.AccessModeRead { + if (*organization.Organization)(repo.Owner).UnitPermission(ctx, doerID, unit.TypeProjects) < perm.AccessModeRead { return false, nil } } diff --git a/routers/web/org/projects.go b/routers/web/org/projects.go index d9231594fcb0..900a74c56cec 100644 --- a/routers/web/org/projects.go +++ b/routers/web/org/projects.go @@ -308,7 +308,7 @@ func ViewProject(ctx *context.Context) { boards[0].Title = ctx.Tr("repo.projects.type.uncategorized") } - issuesMap, err := issues_model.LoadIssuesFromBoardList(ctx, boards) + issuesMap, err := issues_model.LoadIssuesFromBoardList(ctx, boards, project, ctx.Doer.ID) if err != nil { ctx.ServerError("LoadIssuesOfBoards", err) return diff --git a/routers/web/repo/projects.go b/routers/web/repo/projects.go index f51a8512038b..b07094538069 100644 --- a/routers/web/repo/projects.go +++ b/routers/web/repo/projects.go @@ -309,7 +309,7 @@ func ViewProject(ctx *context.Context) { boards[0].Title = ctx.Tr("repo.projects.type.uncategorized") } - issuesMap, err := issues_model.LoadIssuesFromBoardList(ctx, boards) + issuesMap, err := issues_model.LoadIssuesFromBoardList(ctx, boards, project, ctx.Doer.ID) if err != nil { ctx.ServerError("LoadIssuesOfBoards", err) return From 6e668ef37323ef1206116da8dce35ec348886d7c Mon Sep 17 00:00:00 2001 From: yp05327 <576951401@qq.com> Date: Sat, 18 Feb 2023 13:41:23 +0900 Subject: [PATCH 26/69] remove used code --- models/project/project.go | 37 ------------------------------------- 1 file changed, 37 deletions(-) diff --git a/models/project/project.go b/models/project/project.go index dbd0e4c7725d..fa94a51218fe 100644 --- a/models/project/project.go +++ b/models/project/project.go @@ -88,25 +88,6 @@ func (err ErrProjectBoardNotExist) Unwrap() error { return util.ErrNotExist } -// ErrUserType represents a "ErrUserType" kind of error. -type ErrUserType struct { - UserType user_model.UserType -} - -// IsErrUserType checks if an error is a ErrUserType -func IsErrUserType(err error) bool { - _, ok := err.(ErrUserType) - return ok -} - -func (err ErrUserType) Error() string { - return fmt.Sprintf("projects does not support user type: %d", err.UserType) -} - -func (err ErrUserType) Unwrap() error { - return util.ErrNotExist -} - // Project represents a project board type Project struct { ID int64 `xorm:"pk autoincr"` @@ -137,15 +118,6 @@ func (p *Project) LoadOwner(ctx context.Context) (err error) { return err } -func (ps List) LoadOwners(ctx context.Context) (err error) { - for _, p := range ps { - if err := p.LoadOwner(ctx); err != nil { - return err - } - } - return nil -} - func (p *Project) LoadRepo(ctx context.Context) (err error) { if p.RepoID == 0 || p.Repo != nil { return nil @@ -154,15 +126,6 @@ func (p *Project) LoadRepo(ctx context.Context) (err error) { return err } -func (ps List) LoadRepos(ctx context.Context) (err error) { - for _, p := range ps { - if err := p.LoadRepo(ctx); err != nil { - return err - } - } - return nil -} - // Link returns the project's relative URL. func (p *Project) Link() string { if p.OwnerID > 0 { From a4a75a30b9798c186796caf2454dfb59447e95c4 Mon Sep 17 00:00:00 2001 From: yp05327 <576951401@qq.com> Date: Sat, 18 Feb 2023 13:43:44 +0900 Subject: [PATCH 27/69] fix var-naming --- routers/web/repo/issue.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/routers/web/repo/issue.go b/routers/web/repo/issue.go index 13db8e2091b0..6b045e7fd3d9 100644 --- a/routers/web/repo/issue.go +++ b/routers/web/repo/issue.go @@ -1047,7 +1047,7 @@ func ValidateRepoMetas(ctx *context.Context, form forms.CreateIssueForm, isPull } projectLink := "" - projectId := int64(0) + projectID := int64(0) if form.ProjectID > 0 { p, err := project_model.GetProjectByID(ctx, form.ProjectID) if err != nil { @@ -1070,7 +1070,7 @@ func ValidateRepoMetas(ctx *context.Context, form forms.CreateIssueForm, isPull ctx.Data["Project"] = p ctx.Data["project_id"] = form.ProjectID - projectId = form.ProjectID + projectID = form.ProjectID projectLink = p.Link() } } @@ -1110,7 +1110,7 @@ func ValidateRepoMetas(ctx *context.Context, form forms.CreateIssueForm, isPull assigneeIDs = append(assigneeIDs, form.AssigneeID) } - return labelIDs, assigneeIDs, milestoneID, projectId, projectLink + return labelIDs, assigneeIDs, milestoneID, projectID, projectLink } // NewIssuePost response for creating new issue From 6235923f9c206384c6bfb8a2b441c1bde00b7f69 Mon Sep 17 00:00:00 2001 From: yp05327 <576951401@qq.com> Date: Sat, 18 Feb 2023 13:49:05 +0900 Subject: [PATCH 28/69] fix misspelling --- routers/web/repo/issue.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/routers/web/repo/issue.go b/routers/web/repo/issue.go index 6b045e7fd3d9..2864f3d9897c 100644 --- a/routers/web/repo/issue.go +++ b/routers/web/repo/issue.go @@ -1060,7 +1060,7 @@ func ValidateRepoMetas(ctx *context.Context, form forms.CreateIssueForm, isPull } // if projects unit of this repo is disabled in the repo, it will redirect to a 404 page - // if user have no access permission to the project, it will alse redirect to a 404 page + // if user have no access permission to the project, it will also redirect to a 404 page // so we need to return empty projectLink here if canRetrievedByDoer, err := p.CanRetrievedByDoer(ctx, ctx.Repo.Repository, ctx.Doer.ID); err != nil { ctx.ServerError("CanRetrievedByDoer", err) From a75dedbe0297cb095b3940fc910ed2788e21f911 Mon Sep 17 00:00:00 2001 From: yp05327 <576951401@qq.com> Date: Sat, 18 Feb 2023 14:50:12 +0900 Subject: [PATCH 29/69] add db migration --- models/migrations/migrations.go | 2 ++ models/migrations/v1_19/v243.go | 14 ++++++++++++++ 2 files changed, 16 insertions(+) create mode 100644 models/migrations/v1_19/v243.go diff --git a/models/migrations/migrations.go b/models/migrations/migrations.go index 73c44f008a6c..b900d66dafc6 100644 --- a/models/migrations/migrations.go +++ b/models/migrations/migrations.go @@ -459,6 +459,8 @@ var migrations = []Migration{ NewMigration("Add card_type column to project table", v1_19.AddCardTypeToProjectTable), // v242 -> v243 NewMigration("Alter gpg_key_import content TEXT field to MEDIUMTEXT", v1_19.AlterPublicGPGKeyImportContentFieldToMediumText), + // v243 -> v244 + NewMigration("Change Project Type", v1_19.ChangeProjectType), } // GetCurrentDBVersion returns the current db version diff --git a/models/migrations/v1_19/v243.go b/models/migrations/v1_19/v243.go new file mode 100644 index 000000000000..ac2efc7965ca --- /dev/null +++ b/models/migrations/v1_19/v243.go @@ -0,0 +1,14 @@ +// Copyright 2023 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package v1_19 //nolint + +import ( + "xorm.io/xorm" +) + +// ChangeProjectType: set ProjectType from 3(TypeOrganization/TypeIndividual) to 1(TypeUser) +func ChangeProjectType(x *xorm.Engine) error { + _, err := x.Exec("UPDATE project SET type = ? WHERE type = ?", 1, 3) + return err +} From 0d2869db56ee25c026d50e9e497106176c37fdaa Mon Sep 17 00:00:00 2001 From: yp05327 <576951401@qq.com> Date: Sat, 18 Feb 2023 22:07:24 +0900 Subject: [PATCH 30/69] fix permission check in issue.CanRetrievedByDoer --- models/issues/issue.go | 30 +++++++++++++++++++----------- models/issues/issue_project.go | 10 +++++----- modules/context/org.go | 2 +- routers/web/org/projects.go | 2 +- routers/web/repo/projects.go | 2 +- 5 files changed, 27 insertions(+), 19 deletions(-) diff --git a/models/issues/issue.go b/models/issues/issue.go index d8c649089803..0e548b7fd5d3 100644 --- a/models/issues/issue.go +++ b/models/issues/issue.go @@ -477,24 +477,34 @@ func (issue *Issue) getLabels(ctx context.Context) (err error) { } // CanRetrievedByDoer returns whether doer can retrieve the issue -func (issue *Issue) CanRetrievedByDoer(ctx context.Context, p *project_model.Project, doerID int64) (bool, error) { +func (issue *Issue) CanRetrievedByDoer(ctx context.Context, p *project_model.Project, doer *user_model.User) (bool, error) { if err := issue.LoadRepo(ctx); err != nil { return false, err } - if unit.TypeIssues.UnitGlobalDisabled() { + if perm, err := access_model.GetUserRepoPermission(ctx, issue.Repo, doer); err != nil { + return false, err + } else if !perm.HasAccess() { return false, nil } - if err := issue.Repo.GetOwner(ctx); err != nil { - return false, err + if unit.TypeIssues.UnitGlobalDisabled() { + return false, nil } - if !issue.Repo.UnitEnabled(ctx, unit.TypeIssues) { return false, nil } + // TODO: how about Mirror repo + if issue.Repo.IsArchived { + return false, nil + } + + if err := issue.Repo.GetOwner(ctx); err != nil { + return false, err + } if p.RepoID > 0 { + // repo project if p.RepoID != issue.RepoID { return false, nil } @@ -504,15 +514,13 @@ func (issue *Issue) CanRetrievedByDoer(ctx context.Context, p *project_model.Pro return false, nil } - if issue.Repo.Owner.IsIndividual() && issue.Repo.Owner.ID != doerID { + if issue.Repo.Owner.IsIndividual() && issue.Repo.Owner.ID != doer.ID { return false, nil } - if issue.Repo.Owner.IsOrganization() { - // check doer read permission - if (*organization.Organization)(issue.Repo.Owner).UnitPermission(ctx, doerID, unit.TypeIssues) < perm.AccessModeRead { - return false, nil - } + // check doer read permission + if issue.Repo.Owner.IsOrganization() && (*organization.Organization)(issue.Repo.Owner).UnitPermission(ctx, doer.ID, unit.TypeIssues) < perm.AccessModeRead { + return false, nil } } diff --git a/models/issues/issue_project.go b/models/issues/issue_project.go index 4bdb6c9f83c8..e94ac4112291 100644 --- a/models/issues/issue_project.go +++ b/models/issues/issue_project.go @@ -60,7 +60,7 @@ func (issue *Issue) projectBoardID(ctx context.Context) int64 { } // LoadIssuesFromBoard load issues assigned to this board -func LoadIssuesFromBoard(ctx context.Context, b *project_model.Board, p *project_model.Project, doerID int64) (IssueList, error) { +func LoadIssuesFromBoard(ctx context.Context, b *project_model.Board, p *project_model.Project, doer *user_model.User) (IssueList, error) { issueList := make([]*Issue, 0, 10) if b.ID != 0 { @@ -73,7 +73,7 @@ func LoadIssuesFromBoard(ctx context.Context, b *project_model.Board, p *project return nil, err } for _, issue := range issues { - if canRetrievedByDoer, err := issue.CanRetrievedByDoer(ctx, p, doerID); err != nil { + if canRetrievedByDoer, err := issue.CanRetrievedByDoer(ctx, p, doer); err != nil { return nil, err } else if canRetrievedByDoer { issueList = append(issueList, issue) @@ -91,7 +91,7 @@ func LoadIssuesFromBoard(ctx context.Context, b *project_model.Board, p *project return nil, err } for _, issue := range issues { - if canRetrievedByDoer, err := issue.CanRetrievedByDoer(ctx, p, doerID); err != nil { + if canRetrievedByDoer, err := issue.CanRetrievedByDoer(ctx, p, doer); err != nil { return nil, err } else if canRetrievedByDoer { issueList = append(issueList, issue) @@ -107,10 +107,10 @@ func LoadIssuesFromBoard(ctx context.Context, b *project_model.Board, p *project } // LoadIssuesFromBoardList load issues assigned to the boards -func LoadIssuesFromBoardList(ctx context.Context, bs project_model.BoardList, p *project_model.Project, doerID int64) (map[int64]IssueList, error) { +func LoadIssuesFromBoardList(ctx context.Context, bs project_model.BoardList, p *project_model.Project, doer *user_model.User) (map[int64]IssueList, error) { issuesMap := make(map[int64]IssueList, len(bs)) for i := range bs { - il, err := LoadIssuesFromBoard(ctx, bs[i], p, doerID) + il, err := LoadIssuesFromBoard(ctx, bs[i], p, doer) if err != nil { return nil, err } diff --git a/modules/context/org.go b/modules/context/org.go index 9fbf099f0e6e..a01ca603111e 100644 --- a/modules/context/org.go +++ b/modules/context/org.go @@ -38,7 +38,7 @@ func (org *Organization) CanWriteUnit(ctx *Context, unitType unit.Type) bool { func (org *Organization) CanAccessUnit(ctx *Context, unitType unit.Type) bool { if ctx.Doer == nil { - return false + return org.Organization.Visibility.IsPublic() } return org.Organization.UnitPermission(ctx, ctx.Doer.ID, unitType) >= perm.AccessModeRead } diff --git a/routers/web/org/projects.go b/routers/web/org/projects.go index 900a74c56cec..17210db14047 100644 --- a/routers/web/org/projects.go +++ b/routers/web/org/projects.go @@ -308,7 +308,7 @@ func ViewProject(ctx *context.Context) { boards[0].Title = ctx.Tr("repo.projects.type.uncategorized") } - issuesMap, err := issues_model.LoadIssuesFromBoardList(ctx, boards, project, ctx.Doer.ID) + issuesMap, err := issues_model.LoadIssuesFromBoardList(ctx, boards, project, ctx.Doer) if err != nil { ctx.ServerError("LoadIssuesOfBoards", err) return diff --git a/routers/web/repo/projects.go b/routers/web/repo/projects.go index b07094538069..e779e5d9edbc 100644 --- a/routers/web/repo/projects.go +++ b/routers/web/repo/projects.go @@ -309,7 +309,7 @@ func ViewProject(ctx *context.Context) { boards[0].Title = ctx.Tr("repo.projects.type.uncategorized") } - issuesMap, err := issues_model.LoadIssuesFromBoardList(ctx, boards, project, ctx.Doer.ID) + issuesMap, err := issues_model.LoadIssuesFromBoardList(ctx, boards, project, ctx.Doer) if err != nil { ctx.ServerError("LoadIssuesOfBoards", err) return From e022704cfc14cbdbed8f02e2c16788bda767573a Mon Sep 17 00:00:00 2001 From: yp05327 <576951401@qq.com> Date: Sat, 18 Feb 2023 22:19:55 +0900 Subject: [PATCH 31/69] rename GetOwner to LoadOwner --- models/issues/issue.go | 2 +- routers/web/repo/issue.go | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/models/issues/issue.go b/models/issues/issue.go index 19a3ec3981f2..25844d9df2d5 100644 --- a/models/issues/issue.go +++ b/models/issues/issue.go @@ -499,7 +499,7 @@ func (issue *Issue) CanRetrievedByDoer(ctx context.Context, p *project_model.Pro return false, nil } - if err := issue.Repo.GetOwner(ctx); err != nil { + if err := issue.Repo.LoadOwner(ctx); err != nil { return false, err } diff --git a/routers/web/repo/issue.go b/routers/web/repo/issue.go index c3966eed39e2..1bb869e30680 100644 --- a/routers/web/repo/issue.go +++ b/routers/web/repo/issue.go @@ -472,8 +472,8 @@ func retrieveProjects(ctx *context.Context, repo *repo_model.Repository) { var closedProjects project_model.List var err error - if err := repo.GetOwner(ctx); err != nil { - ctx.ServerError("GetOwner", err) + if err := repo.LoadOwner(ctx); err != nil { + ctx.ServerError("LoadOwner", err) return } From 5850d85ddb87ba8af4ce4fe0c0641d787686fd10 Mon Sep 17 00:00:00 2001 From: yp05327 <576951401@qq.com> Date: Sun, 19 Feb 2023 00:57:22 +0900 Subject: [PATCH 32/69] rename GetOwner to LoadOwner --- models/project/project.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/models/project/project.go b/models/project/project.go index fa94a51218fe..2ac795bf0d81 100644 --- a/models/project/project.go +++ b/models/project/project.go @@ -175,8 +175,8 @@ func (p *Project) IsRepositoryProject() bool { // CanRetrievedByDoer return whether project can be retrieved by a doer in a repo func (p *Project) CanRetrievedByDoer(ctx context.Context, repo *repo_model.Repository, doerID int64) (bool, error) { - if err := repo.GetOwner(ctx); err != nil { - return false, fmt.Errorf("GetOwner: %w", err) + if err := repo.LoadOwner(ctx); err != nil { + return false, fmt.Errorf("LoadOwner: %w", err) } if unit.TypeProjects.UnitGlobalDisabled() { From 2b398fd71a658fb1b713d2626f48e374a6aaf64c Mon Sep 17 00:00:00 2001 From: yp05327 <576951401@qq.com> Date: Sun, 19 Feb 2023 17:31:20 +0900 Subject: [PATCH 33/69] fix incorrect issue num in org/individual project board. --- models/issues/issue.go | 6 +---- models/issues/issue_project.go | 49 +++++++++++++++++++++++++++++----- models/project/board.go | 41 ++++++++++++++++++++++++++++ routers/web/org/projects.go | 12 ++++++++- routers/web/repo/projects.go | 12 ++++++++- templates/projects/list.tmpl | 2 +- templates/projects/view.tmpl | 2 +- 7 files changed, 108 insertions(+), 16 deletions(-) diff --git a/models/issues/issue.go b/models/issues/issue.go index 25844d9df2d5..7f9ccfd625a2 100644 --- a/models/issues/issue.go +++ b/models/issues/issue.go @@ -494,7 +494,7 @@ func (issue *Issue) CanRetrievedByDoer(ctx context.Context, p *project_model.Pro if !issue.Repo.UnitEnabled(ctx, unit.TypeIssues) { return false, nil } - // TODO: how about Mirror repo + // TODO: what about Mirror repo if issue.Repo.IsArchived { return false, nil } @@ -514,10 +514,6 @@ func (issue *Issue) CanRetrievedByDoer(ctx context.Context, p *project_model.Pro return false, nil } - if issue.Repo.Owner.IsIndividual() && issue.Repo.Owner.ID != doer.ID { - return false, nil - } - // check doer read permission if issue.Repo.Owner.IsOrganization() && (*organization.Organization)(issue.Repo.Owner).UnitPermission(ctx, doer.ID, unit.TypeIssues) < perm.AccessModeRead { return false, nil diff --git a/models/issues/issue_project.go b/models/issues/issue_project.go index e94ac4112291..597e10da7538 100644 --- a/models/issues/issue_project.go +++ b/models/issues/issue_project.go @@ -60,9 +60,13 @@ func (issue *Issue) projectBoardID(ctx context.Context) int64 { } // LoadIssuesFromBoard load issues assigned to this board -func LoadIssuesFromBoard(ctx context.Context, b *project_model.Board, p *project_model.Project, doer *user_model.User) (IssueList, error) { +func LoadIssuesFromBoard(ctx context.Context, b *project_model.Board, doer *user_model.User) (IssueList, error) { issueList := make([]*Issue, 0, 10) + if err := b.LoadProject(ctx); err != nil { + return nil, err + } + if b.ID != 0 { issues, err := Issues(ctx, &IssuesOptions{ ProjectBoardID: b.ID, @@ -73,7 +77,7 @@ func LoadIssuesFromBoard(ctx context.Context, b *project_model.Board, p *project return nil, err } for _, issue := range issues { - if canRetrievedByDoer, err := issue.CanRetrievedByDoer(ctx, p, doer); err != nil { + if canRetrievedByDoer, err := issue.CanRetrievedByDoer(ctx, b.Project, doer); err != nil { return nil, err } else if canRetrievedByDoer { issueList = append(issueList, issue) @@ -91,7 +95,7 @@ func LoadIssuesFromBoard(ctx context.Context, b *project_model.Board, p *project return nil, err } for _, issue := range issues { - if canRetrievedByDoer, err := issue.CanRetrievedByDoer(ctx, p, doer); err != nil { + if canRetrievedByDoer, err := issue.CanRetrievedByDoer(ctx, b.Project, doer); err != nil { return nil, err } else if canRetrievedByDoer { issueList = append(issueList, issue) @@ -107,18 +111,49 @@ func LoadIssuesFromBoard(ctx context.Context, b *project_model.Board, p *project } // LoadIssuesFromBoardList load issues assigned to the boards -func LoadIssuesFromBoardList(ctx context.Context, bs project_model.BoardList, p *project_model.Project, doer *user_model.User) (map[int64]IssueList, error) { +func LoadIssuesFromBoardList(ctx context.Context, bs project_model.BoardList, doer *user_model.User) (map[int64]IssueList, error) { issuesMap := make(map[int64]IssueList, len(bs)) - for i := range bs { - il, err := LoadIssuesFromBoard(ctx, bs[i], p, doer) + for _, b := range bs { + il, err := LoadIssuesFromBoard(ctx, b, doer) if err != nil { return nil, err } - issuesMap[bs[i].ID] = il + issuesMap[b.ID] = il } return issuesMap, nil } +// NumIssuesInProjects returns counter of all issues assigned to a project list which doer can access +func NumIssuesInProjects(ctx context.Context, pl project_model.List, doer *user_model.User) (int, error) { + numIssuesInProjects := int(0) + for _, p := range pl { + numIssuesInProject, err := NumIssuesInProject(ctx, p, doer) + if err != nil { + return 0, err + } + numIssuesInProjects += numIssuesInProject + } + + return numIssuesInProjects, nil +} + +// NumIssuesInProject returns counter of all issues assigned to a project which doer can access +func NumIssuesInProject(ctx context.Context, p *project_model.Project, doer *user_model.User) (int, error) { + numIssuesInProject := int(0) + bs, err := project_model.GetBoards(ctx, p.ID) + if err != nil { + return 0, err + } + im, err := LoadIssuesFromBoardList(ctx, bs, doer) + if err != nil { + return 0, err + } + for _, il := range im { + numIssuesInProject += len(il) + } + return numIssuesInProject, nil +} + // ChangeProjectAssign changes the project associated with an issue func ChangeProjectAssign(issue *Issue, doer *user_model.User, newProjectID int64) error { ctx, committer, err := db.TxContext(db.DefaultContext) diff --git a/models/project/board.go b/models/project/board.go index dc4e2e6882a5..ce7a8e67f09b 100644 --- a/models/project/board.go +++ b/models/project/board.go @@ -59,6 +59,8 @@ type Board struct { ProjectID int64 `xorm:"INDEX NOT NULL"` CreatorID int64 `xorm:"NOT NULL"` + Project *Project `xorm:"-"` + CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"` UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"` } @@ -68,6 +70,45 @@ func (Board) TableName() string { return "project_board" } +// LoadProject loads project +func (b *Board) LoadProject(ctx context.Context) error { + if b.Project != nil { + return nil + } + + var project Project + has, err := db.GetEngine(ctx).ID(b.ProjectID).Get(&project) + if err != nil { + return err + } else if !has { + return ErrProjectNotExist{ + ID: b.ProjectID, + } + } + b.Project = &project + return nil +} + +// SetProject set an exist project object to the board +func (b *Board) SetProject(p *Project) error { + if b.ProjectID != p.ID { + return fmt.Errorf("board[id:%d] doesn't belong to project[id:%d]", b.ID, p.ID) + } + + b.Project = p + return nil +} + +// SetProject set an exist project object to the board +func (bl BoardList) SetProject(p *Project) error { + for _, b := range bl { + if err := b.SetProject(p); err != nil { + return err + } + } + return nil +} + // NumIssues return counter of all issues assigned to the board func (b *Board) NumIssues() int { c, err := db.GetEngine(db.DefaultContext).Table("project_issue"). diff --git a/routers/web/org/projects.go b/routers/web/org/projects.go index 17210db14047..d2adf69f2f98 100644 --- a/routers/web/org/projects.go +++ b/routers/web/org/projects.go @@ -114,6 +114,13 @@ func Projects(ctx *context.Context) { ctx.Data["PageIsViewProjects"] = true ctx.Data["SortType"] = sortType + numIssuesInProjects, err := issues_model.NumIssuesInProjects(ctx, projects, ctx.Doer) + if err != nil { + ctx.ServerError("NumIssuesInProjects", err) + return + } + ctx.Data["NumIssuesInProjects"] = numIssuesInProjects + ctx.HTML(http.StatusOK, tplProjects) } @@ -308,7 +315,10 @@ func ViewProject(ctx *context.Context) { boards[0].Title = ctx.Tr("repo.projects.type.uncategorized") } - issuesMap, err := issues_model.LoadIssuesFromBoardList(ctx, boards, project, ctx.Doer) + // avild multiple loading of project in LoadIssuesFromBoardList + boards.SetProject(project) + + issuesMap, err := issues_model.LoadIssuesFromBoardList(ctx, boards, ctx.Doer) if err != nil { ctx.ServerError("LoadIssuesOfBoards", err) return diff --git a/routers/web/repo/projects.go b/routers/web/repo/projects.go index e779e5d9edbc..dd02e9f30e85 100644 --- a/routers/web/repo/projects.go +++ b/routers/web/repo/projects.go @@ -118,6 +118,13 @@ func Projects(ctx *context.Context) { ctx.Data["IsProjectsPage"] = true ctx.Data["SortType"] = sortType + numIssuesInProjects, err := issues_model.NumIssuesInProjects(ctx, projects, ctx.Doer) + if err != nil { + ctx.ServerError("NumIssuesInProjects", err) + return + } + ctx.Data["NumIssuesInProjects"] = numIssuesInProjects + ctx.HTML(http.StatusOK, tplProjects) } @@ -309,7 +316,10 @@ func ViewProject(ctx *context.Context) { boards[0].Title = ctx.Tr("repo.projects.type.uncategorized") } - issuesMap, err := issues_model.LoadIssuesFromBoardList(ctx, boards, project, ctx.Doer) + // avild multiple loading of project in LoadIssuesFromBoardList + boards.SetProject(project) + + issuesMap, err := issues_model.LoadIssuesFromBoardList(ctx, boards, ctx.Doer) if err != nil { ctx.ServerError("LoadIssuesOfBoards", err) return diff --git a/templates/projects/list.tmpl b/templates/projects/list.tmpl index 179800d5e096..73bd5a90affa 100644 --- a/templates/projects/list.tmpl +++ b/templates/projects/list.tmpl @@ -46,7 +46,7 @@ {{end}} {{svg "octicon-issue-opened" 16 "gt-mr-3"}} - {{JsPrettyNumber .NumOpenIssues}} {{$.locale.Tr "repo.issues.open_title"}} + {{JsPrettyNumber $.NumIssuesInProjects}} {{$.locale.Tr "repo.issues.open_title"}} {{svg "octicon-check" 16 "gt-mr-3"}} {{JsPrettyNumber .NumClosedIssues}} {{$.locale.Tr "repo.issues.closed_title"}} diff --git a/templates/projects/view.tmpl b/templates/projects/view.tmpl index b07c8a6e3b8a..a0d0c4f9c040 100644 --- a/templates/projects/view.tmpl +++ b/templates/projects/view.tmpl @@ -80,7 +80,7 @@
    - {{.NumIssues}} + {{len (index $.IssuesMap .ID)}}
    {{.Title}}
    From 87668f74d37fcf6e4ee015a8b4932c6c8bddfda1 Mon Sep 17 00:00:00 2001 From: yp05327 <576951401@qq.com> Date: Sun, 19 Feb 2023 17:42:08 +0900 Subject: [PATCH 34/69] fix incorrect permission check in repo project --- models/issues/issue.go | 9 ++++----- templates/repo/projects/view.tmpl | 2 +- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/models/issues/issue.go b/models/issues/issue.go index 7f9ccfd625a2..09b4988a2ab1 100644 --- a/models/issues/issue.go +++ b/models/issues/issue.go @@ -503,6 +503,10 @@ func (issue *Issue) CanRetrievedByDoer(ctx context.Context, p *project_model.Pro return false, err } + if issue.Repo.Owner.IsOrganization() && (*organization.Organization)(issue.Repo.Owner).UnitPermission(ctx, doer.ID, unit.TypeIssues) < perm.AccessModeRead { + return false, nil + } + if p.RepoID > 0 { // repo project if p.RepoID != issue.RepoID { @@ -513,11 +517,6 @@ func (issue *Issue) CanRetrievedByDoer(ctx context.Context, p *project_model.Pro if p.OwnerID != issue.Repo.OwnerID { return false, nil } - - // check doer read permission - if issue.Repo.Owner.IsOrganization() && (*organization.Organization)(issue.Repo.Owner).UnitPermission(ctx, doer.ID, unit.TypeIssues) < perm.AccessModeRead { - return false, nil - } } return true, nil diff --git a/templates/repo/projects/view.tmpl b/templates/repo/projects/view.tmpl index 333d29f6f709..a1ae3cd8869f 100644 --- a/templates/repo/projects/view.tmpl +++ b/templates/repo/projects/view.tmpl @@ -84,7 +84,7 @@
    - {{.NumIssues}} + {{len (index $.IssuesMap .ID)}}
    {{.Title}}
    From 4c1c419522e9754ebf21d9152629d5321f8bc832 Mon Sep 17 00:00:00 2001 From: yp05327 <576951401@qq.com> Date: Sun, 19 Feb 2023 18:22:26 +0900 Subject: [PATCH 35/69] fix incorrect open/closed issuenum in project list --- models/issues/issue_project.go | 17 ++++++++++------- routers/web/org/projects.go | 12 +++++++++--- routers/web/repo/projects.go | 12 +++++++++--- templates/projects/list.tmpl | 4 ++-- templates/repo/projects/list.tmpl | 4 ++-- 5 files changed, 32 insertions(+), 17 deletions(-) diff --git a/models/issues/issue_project.go b/models/issues/issue_project.go index 597e10da7538..515c3ca81bc8 100644 --- a/models/issues/issue_project.go +++ b/models/issues/issue_project.go @@ -10,6 +10,7 @@ import ( "code.gitea.io/gitea/models/db" project_model "code.gitea.io/gitea/models/project" user_model "code.gitea.io/gitea/models/user" + "code.gitea.io/gitea/modules/util" ) // LoadProject load the project the issue was assigned to @@ -60,7 +61,7 @@ func (issue *Issue) projectBoardID(ctx context.Context) int64 { } // LoadIssuesFromBoard load issues assigned to this board -func LoadIssuesFromBoard(ctx context.Context, b *project_model.Board, doer *user_model.User) (IssueList, error) { +func LoadIssuesFromBoard(ctx context.Context, b *project_model.Board, doer *user_model.User, isClosed util.OptionalBool) (IssueList, error) { issueList := make([]*Issue, 0, 10) if err := b.LoadProject(ctx); err != nil { @@ -72,6 +73,7 @@ func LoadIssuesFromBoard(ctx context.Context, b *project_model.Board, doer *user ProjectBoardID: b.ID, ProjectID: b.ProjectID, SortType: "project-column-sorting", + IsClosed: isClosed, }) if err != nil { return nil, err @@ -90,6 +92,7 @@ func LoadIssuesFromBoard(ctx context.Context, b *project_model.Board, doer *user ProjectBoardID: -1, // Issues without ProjectBoardID ProjectID: b.ProjectID, SortType: "project-column-sorting", + IsClosed: isClosed, }) if err != nil { return nil, err @@ -111,10 +114,10 @@ func LoadIssuesFromBoard(ctx context.Context, b *project_model.Board, doer *user } // LoadIssuesFromBoardList load issues assigned to the boards -func LoadIssuesFromBoardList(ctx context.Context, bs project_model.BoardList, doer *user_model.User) (map[int64]IssueList, error) { +func LoadIssuesFromBoardList(ctx context.Context, bs project_model.BoardList, doer *user_model.User, isClosed util.OptionalBool) (map[int64]IssueList, error) { issuesMap := make(map[int64]IssueList, len(bs)) for _, b := range bs { - il, err := LoadIssuesFromBoard(ctx, b, doer) + il, err := LoadIssuesFromBoard(ctx, b, doer, isClosed) if err != nil { return nil, err } @@ -124,10 +127,10 @@ func LoadIssuesFromBoardList(ctx context.Context, bs project_model.BoardList, do } // NumIssuesInProjects returns counter of all issues assigned to a project list which doer can access -func NumIssuesInProjects(ctx context.Context, pl project_model.List, doer *user_model.User) (int, error) { +func NumIssuesInProjects(ctx context.Context, pl project_model.List, doer *user_model.User, isClosed util.OptionalBool) (int, error) { numIssuesInProjects := int(0) for _, p := range pl { - numIssuesInProject, err := NumIssuesInProject(ctx, p, doer) + numIssuesInProject, err := NumIssuesInProject(ctx, p, doer, isClosed) if err != nil { return 0, err } @@ -138,13 +141,13 @@ func NumIssuesInProjects(ctx context.Context, pl project_model.List, doer *user_ } // NumIssuesInProject returns counter of all issues assigned to a project which doer can access -func NumIssuesInProject(ctx context.Context, p *project_model.Project, doer *user_model.User) (int, error) { +func NumIssuesInProject(ctx context.Context, p *project_model.Project, doer *user_model.User, isClosed util.OptionalBool) (int, error) { numIssuesInProject := int(0) bs, err := project_model.GetBoards(ctx, p.ID) if err != nil { return 0, err } - im, err := LoadIssuesFromBoardList(ctx, bs, doer) + im, err := LoadIssuesFromBoardList(ctx, bs, doer, isClosed) if err != nil { return 0, err } diff --git a/routers/web/org/projects.go b/routers/web/org/projects.go index d2adf69f2f98..fd748016eee2 100644 --- a/routers/web/org/projects.go +++ b/routers/web/org/projects.go @@ -114,12 +114,18 @@ func Projects(ctx *context.Context) { ctx.Data["PageIsViewProjects"] = true ctx.Data["SortType"] = sortType - numIssuesInProjects, err := issues_model.NumIssuesInProjects(ctx, projects, ctx.Doer) + numOpenIssues, err := issues_model.NumIssuesInProjects(ctx, projects, ctx.Doer, util.OptionalBoolTrue) if err != nil { ctx.ServerError("NumIssuesInProjects", err) return } - ctx.Data["NumIssuesInProjects"] = numIssuesInProjects + numClosedIssues, err := issues_model.NumIssuesInProjects(ctx, projects, ctx.Doer, util.OptionalBoolFalse) + if err != nil { + ctx.ServerError("NumIssuesInProjects", err) + return + } + ctx.Data["NumOpenIssuesInProjects"] = numOpenIssues + ctx.Data["NumClosedIssuesInProjects"] = numClosedIssues ctx.HTML(http.StatusOK, tplProjects) } @@ -318,7 +324,7 @@ func ViewProject(ctx *context.Context) { // avild multiple loading of project in LoadIssuesFromBoardList boards.SetProject(project) - issuesMap, err := issues_model.LoadIssuesFromBoardList(ctx, boards, ctx.Doer) + issuesMap, err := issues_model.LoadIssuesFromBoardList(ctx, boards, ctx.Doer, util.OptionalBoolNone) if err != nil { ctx.ServerError("LoadIssuesOfBoards", err) return diff --git a/routers/web/repo/projects.go b/routers/web/repo/projects.go index dd02e9f30e85..17ca60e51ea4 100644 --- a/routers/web/repo/projects.go +++ b/routers/web/repo/projects.go @@ -118,12 +118,18 @@ func Projects(ctx *context.Context) { ctx.Data["IsProjectsPage"] = true ctx.Data["SortType"] = sortType - numIssuesInProjects, err := issues_model.NumIssuesInProjects(ctx, projects, ctx.Doer) + numOpenIssues, err := issues_model.NumIssuesInProjects(ctx, projects, ctx.Doer, util.OptionalBoolTrue) if err != nil { ctx.ServerError("NumIssuesInProjects", err) return } - ctx.Data["NumIssuesInProjects"] = numIssuesInProjects + numClosedIssues, err := issues_model.NumIssuesInProjects(ctx, projects, ctx.Doer, util.OptionalBoolFalse) + if err != nil { + ctx.ServerError("NumIssuesInProjects", err) + return + } + ctx.Data["NumOpenIssuesInProjects"] = numOpenIssues + ctx.Data["NumClosedIssuesInProjects"] = numClosedIssues ctx.HTML(http.StatusOK, tplProjects) } @@ -319,7 +325,7 @@ func ViewProject(ctx *context.Context) { // avild multiple loading of project in LoadIssuesFromBoardList boards.SetProject(project) - issuesMap, err := issues_model.LoadIssuesFromBoardList(ctx, boards, ctx.Doer) + issuesMap, err := issues_model.LoadIssuesFromBoardList(ctx, boards, ctx.Doer, util.OptionalBoolNone) if err != nil { ctx.ServerError("LoadIssuesOfBoards", err) return diff --git a/templates/projects/list.tmpl b/templates/projects/list.tmpl index 73bd5a90affa..57b526bdfacc 100644 --- a/templates/projects/list.tmpl +++ b/templates/projects/list.tmpl @@ -46,9 +46,9 @@ {{end}} {{svg "octicon-issue-opened" 16 "gt-mr-3"}} - {{JsPrettyNumber $.NumIssuesInProjects}} {{$.locale.Tr "repo.issues.open_title"}} + {{JsPrettyNumber $.NumOpenIssuesInProjects}} {{$.locale.Tr "repo.issues.open_title"}} {{svg "octicon-check" 16 "gt-mr-3"}} - {{JsPrettyNumber .NumClosedIssues}} {{$.locale.Tr "repo.issues.closed_title"}} + {{JsPrettyNumber $.NumClosedIssuesInProjects}} {{$.locale.Tr "repo.issues.closed_title"}}
    {{if $.CanWriteProjects}} diff --git a/templates/repo/projects/list.tmpl b/templates/repo/projects/list.tmpl index 5e8e00c6957c..3c8231a91174 100644 --- a/templates/repo/projects/list.tmpl +++ b/templates/repo/projects/list.tmpl @@ -48,9 +48,9 @@ {{end}} {{svg "octicon-issue-opened" 16 "gt-mr-3"}} - {{JsPrettyNumber .NumOpenIssues}} {{$.locale.Tr "repo.issues.open_title"}} + {{JsPrettyNumber $.NumOpenIssuesInProjects}} {{$.locale.Tr "repo.issues.open_title"}} {{svg "octicon-check" 16 "gt-mr-3"}} - {{JsPrettyNumber .NumClosedIssues}} {{$.locale.Tr "repo.issues.closed_title"}} + {{JsPrettyNumber $.NumClosedIssuesInProjects}} {{$.locale.Tr "repo.issues.closed_title"}}
    {{if and $.CanWriteProjects (not $.Repository.IsArchived)}} From 72d059d7de41250de8ca8b332b6a0561dd08b2a8 Mon Sep 17 00:00:00 2001 From: yp05327 <576951401@qq.com> Date: Sun, 19 Feb 2023 18:45:24 +0900 Subject: [PATCH 36/69] binding a issue to a project needs Write perm --- models/issues/issue.go | 3 ++- models/issues/issue_project.go | 4 ++-- models/project/project.go | 31 +++++++++++++++++++++++-------- routers/web/repo/issue.go | 28 ++++++++++++++-------------- 4 files changed, 41 insertions(+), 25 deletions(-) diff --git a/models/issues/issue.go b/models/issues/issue.go index 09b4988a2ab1..2a18db4cc9b9 100644 --- a/models/issues/issue.go +++ b/models/issues/issue.go @@ -503,7 +503,8 @@ func (issue *Issue) CanRetrievedByDoer(ctx context.Context, p *project_model.Pro return false, err } - if issue.Repo.Owner.IsOrganization() && (*organization.Organization)(issue.Repo.Owner).UnitPermission(ctx, doer.ID, unit.TypeIssues) < perm.AccessModeRead { + if issue.Repo.Owner.IsOrganization() && issue.Repo.IsPrivate && + (*organization.Organization)(issue.Repo.Owner).UnitPermission(ctx, doer.ID, unit.TypeIssues) < perm.AccessModeRead { return false, nil } diff --git a/models/issues/issue_project.go b/models/issues/issue_project.go index 515c3ca81bc8..502e6bce256f 100644 --- a/models/issues/issue_project.go +++ b/models/issues/issue_project.go @@ -186,9 +186,9 @@ func addUpdateIssueProject(ctx context.Context, issue *Issue, doer *user_model.U return err } - if canRetrievedByDoer, err := newProject.CanRetrievedByDoer(ctx, issue.Repo, doer.ID); err != nil { + if canWriteByDoer, err := newProject.CanWriteByDoer(ctx, issue.Repo, doer); err != nil { return err - } else if !canRetrievedByDoer { + } else if !canWriteByDoer { return fmt.Errorf("issue's repository can't be retrieved by doer") } } diff --git a/models/project/project.go b/models/project/project.go index 2ac795bf0d81..5739fe32bd94 100644 --- a/models/project/project.go +++ b/models/project/project.go @@ -10,6 +10,7 @@ import ( "code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/organization" "code.gitea.io/gitea/models/perm" + access_model "code.gitea.io/gitea/models/perm/access" repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/models/unit" user_model "code.gitea.io/gitea/models/user" @@ -174,19 +175,30 @@ func (p *Project) IsRepositoryProject() bool { } // CanRetrievedByDoer return whether project can be retrieved by a doer in a repo -func (p *Project) CanRetrievedByDoer(ctx context.Context, repo *repo_model.Repository, doerID int64) (bool, error) { - if err := repo.LoadOwner(ctx); err != nil { - return false, fmt.Errorf("LoadOwner: %w", err) - } - +func (p *Project) CanWriteByDoer(ctx context.Context, repo *repo_model.Repository, doer *user_model.User) (bool, error) { if unit.TypeProjects.UnitGlobalDisabled() { return false, nil } + if err := repo.LoadOwner(ctx); err != nil { + return false, fmt.Errorf("LoadOwner: %w", err) + } + if p.RepoID > 0 { + // repo projects if p.RepoID != repo.ID { return false, nil } + + if err := p.LoadRepo(ctx); err != nil { + return false, fmt.Errorf("LoadOwner: %w", err) + } + if perm, err := access_model.GetUserRepoPermission(ctx, p.Repo, doer); err != nil { + return false, err + } else if !perm.HasAccess() { + return false, nil + } + if !repo.UnitEnabled(ctx, unit.TypeProjects) { return false, nil } @@ -196,13 +208,16 @@ func (p *Project) CanRetrievedByDoer(ctx context.Context, repo *repo_model.Repos return false, nil } - if repo.Owner.IsIndividual() && repo.Owner.ID != doerID { + if doer == nil { + return false, nil + } + + if repo.Owner.IsIndividual() && repo.Owner.ID != doer.ID { return false, nil } if repo.Owner.IsOrganization() { - // check doer read permission - if (*organization.Organization)(repo.Owner).UnitPermission(ctx, doerID, unit.TypeProjects) < perm.AccessModeRead { + if (*organization.Organization)(repo.Owner).UnitPermission(ctx, doer.ID, unit.TypeProjects) < perm.AccessModeWrite { return false, nil } } diff --git a/routers/web/repo/issue.go b/routers/web/repo/issue.go index 1bb869e30680..d77e95841f00 100644 --- a/routers/web/repo/issue.go +++ b/routers/web/repo/issue.go @@ -502,10 +502,10 @@ func retrieveProjects(ctx *context.Context, repo *repo_model.Repository) { return } for _, p := range openProjectsUser { - if canRetrievedByDoer, err := p.CanRetrievedByDoer(ctx, repo, ctx.Doer.ID); err != nil { - ctx.ServerError("CanRetrievedByDoer", err) + if canWriteByDoer, err := p.CanWriteByDoer(ctx, repo, ctx.Doer); err != nil { + ctx.ServerError("CanWriteByDoer", err) return - } else if canRetrievedByDoer { + } else if canWriteByDoer { openProjects = append(openProjects, p) } } @@ -536,10 +536,10 @@ func retrieveProjects(ctx *context.Context, repo *repo_model.Repository) { return } for _, p := range closedProjectsUser { - if canRetrievedByDoer, err := p.CanRetrievedByDoer(ctx, repo, ctx.Doer.ID); err != nil { - ctx.ServerError("CanRetrievedByDoer", err) + if canWriteByDoer, err := p.CanWriteByDoer(ctx, repo, ctx.Doer); err != nil { + ctx.ServerError("CanWriteByDoer", err) return - } else if canRetrievedByDoer { + } else if canWriteByDoer { closedProjects = append(closedProjects, p) } } @@ -887,13 +887,13 @@ func NewIssue(ctx *context.Context) { if err != nil { log.Error("GetProjectByID: %d: %v", projectID, err) } else { - canRetrievedByDoer, err := project.CanRetrievedByDoer(ctx, ctx.Repo.Repository, ctx.Doer.ID) + canWriteByDoer, err := project.CanWriteByDoer(ctx, ctx.Repo.Repository, ctx.Doer) if err != nil { - log.Error("CanRetrievedByDoer: %d: %v", projectID, err) - } else if !canRetrievedByDoer { - log.Error("CanRetrievedByDoer: %d: %v", projectID, fmt.Errorf("project[%d] not found", project.ID)) + log.Error("CanWriteByDoer: %d: %v", projectID, err) + } else if !canWriteByDoer { + log.Error("CanWriteByDoer: %d: %v", projectID, fmt.Errorf("project[%d] not found", project.ID)) } else if project.IsRepositoryProject() && !isProjectsEnabled { - log.Error("CanRetrievedByDoer: %d: %v", projectID, fmt.Errorf("projects is not enabled in repo[%d]", ctx.Repo.Repository.ID)) + log.Error("CanWriteByDoer: %d: %v", projectID, fmt.Errorf("projects is not enabled in repo[%d]", ctx.Repo.Repository.ID)) } else { ctx.Data["project_id"] = projectID ctx.Data["Project"] = project @@ -1062,10 +1062,10 @@ func ValidateRepoMetas(ctx *context.Context, form forms.CreateIssueForm, isPull // if projects unit of this repo is disabled in the repo, it will redirect to a 404 page // if user have no access permission to the project, it will also redirect to a 404 page // so we need to return empty projectLink here - if canRetrievedByDoer, err := p.CanRetrievedByDoer(ctx, ctx.Repo.Repository, ctx.Doer.ID); err != nil { - ctx.ServerError("CanRetrievedByDoer", err) + if canWriteByDoer, err := p.CanWriteByDoer(ctx, ctx.Repo.Repository, ctx.Doer); err != nil { + ctx.ServerError("CanWriteByDoer", err) return nil, nil, 0, 0, "" - } else if canRetrievedByDoer { + } else if canWriteByDoer { if !(p.IsRepositoryProject() && !ctx.Repo.CanRead(unit.TypeProjects)) { ctx.Data["Project"] = p ctx.Data["project_id"] = form.ProjectID From 7be7b02dcdba9314ceea68cbc7d02b4c5f345d99 Mon Sep 17 00:00:00 2001 From: yp05327 <576951401@qq.com> Date: Sun, 19 Feb 2023 18:47:41 +0900 Subject: [PATCH 37/69] fix incorrect open/close issue num in project list --- routers/web/org/projects.go | 4 ++-- routers/web/repo/projects.go | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/routers/web/org/projects.go b/routers/web/org/projects.go index fd748016eee2..a16da7256655 100644 --- a/routers/web/org/projects.go +++ b/routers/web/org/projects.go @@ -114,12 +114,12 @@ func Projects(ctx *context.Context) { ctx.Data["PageIsViewProjects"] = true ctx.Data["SortType"] = sortType - numOpenIssues, err := issues_model.NumIssuesInProjects(ctx, projects, ctx.Doer, util.OptionalBoolTrue) + numOpenIssues, err := issues_model.NumIssuesInProjects(ctx, projects, ctx.Doer, util.OptionalBoolFalse) if err != nil { ctx.ServerError("NumIssuesInProjects", err) return } - numClosedIssues, err := issues_model.NumIssuesInProjects(ctx, projects, ctx.Doer, util.OptionalBoolFalse) + numClosedIssues, err := issues_model.NumIssuesInProjects(ctx, projects, ctx.Doer, util.OptionalBoolTrue) if err != nil { ctx.ServerError("NumIssuesInProjects", err) return diff --git a/routers/web/repo/projects.go b/routers/web/repo/projects.go index 17ca60e51ea4..2f9a8ddbd492 100644 --- a/routers/web/repo/projects.go +++ b/routers/web/repo/projects.go @@ -118,12 +118,12 @@ func Projects(ctx *context.Context) { ctx.Data["IsProjectsPage"] = true ctx.Data["SortType"] = sortType - numOpenIssues, err := issues_model.NumIssuesInProjects(ctx, projects, ctx.Doer, util.OptionalBoolTrue) + numOpenIssues, err := issues_model.NumIssuesInProjects(ctx, projects, ctx.Doer, util.OptionalBoolFalse) if err != nil { ctx.ServerError("NumIssuesInProjects", err) return } - numClosedIssues, err := issues_model.NumIssuesInProjects(ctx, projects, ctx.Doer, util.OptionalBoolFalse) + numClosedIssues, err := issues_model.NumIssuesInProjects(ctx, projects, ctx.Doer, util.OptionalBoolTrue) if err != nil { ctx.ServerError("NumIssuesInProjects", err) return From 0a2272940d7db27b891377c815caf1dd0a09c107 Mon Sep 17 00:00:00 2001 From: yp05327 <576951401@qq.com> Date: Tue, 7 Mar 2023 06:49:45 +0000 Subject: [PATCH 38/69] remove projects.Name --- models/project/project.go | 20 ------------ templates/repo/issue/list.tmpl | 32 +++++-------------- .../repo/issue/view_content/comments.tmpl | 6 ++-- .../repo/issue/view_content/sidebar.tmpl | 24 ++++---------- 4 files changed, 17 insertions(+), 65 deletions(-) diff --git a/models/project/project.go b/models/project/project.go index b3ef076b65ad..da13f9938aa0 100644 --- a/models/project/project.go +++ b/models/project/project.go @@ -146,26 +146,6 @@ func (p *Project) Link() string { return "" } -// Name return the project's name which is combined with owner/repo/project's name -func (p *Project) Name() string { - if p.OwnerID > 0 { - if err := p.LoadOwner(db.DefaultContext); err != nil { - log.Error("LoadOwner: %v", err) - return "" - } - return fmt.Sprintf("%s/-/%s", p.Owner.Name, p.Title) - } - if p.RepoID > 0 { - if err := p.LoadRepo(db.DefaultContext); err != nil { - log.Error("LoadRepo: %v", err) - return "" - } - - return fmt.Sprintf("%s/%s/%s", p.Repo.OwnerName, p.Repo.Name, p.Title) - } - return "" -} - func (p *Project) IsUserProject() bool { return p.Type == TypeUser } diff --git a/templates/repo/issue/list.tmpl b/templates/repo/issue/list.tmpl index 239e40087118..ca05264e77b6 100644 --- a/templates/repo/issue/list.tmpl +++ b/templates/repo/issue/list.tmpl @@ -100,12 +100,8 @@
    {{range .OpenProjects}} - {{if .IsUserProject}} - {{svg "octicon-project-symlink" 18 "mr-3"}} - {{else}} - {{svg "octicon-project" 18 "mr-3"}} - {{end}} - {{.Name}} + {{if .IsOrganizationProject}}{{svg "octicon-project-symlink" 18 "gt-mr-3"}}{{else}}{{svg "octicon-project" 18 "gt-mr-3"}}{{end}} + {{.Title}} {{end}} {{end}} @@ -116,12 +112,8 @@ {{range .ClosedProjects}} - {{if .IsUserProject}} - {{svg "octicon-project-symlink" 18 "mr-3"}} - {{else}} - {{svg "octicon-project" 18 "mr-3"}} - {{end}} - {{.Name}} + {{if .IsOrganizationProject}}{{svg "octicon-project-symlink" 18 "gt-mr-3"}}{{else}}{{svg "octicon-project" 18 "gt-mr-3"}}{{end}} + {{.Title}} {{end}} {{end}} @@ -281,12 +273,8 @@ {{range .OpenProjects}}
    - {{if .IsUserProject}} - {{svg "octicon-project-symlink" 18 "mr-3"}} - {{else}} - {{svg "octicon-project" 18 "mr-3"}} - {{end}} - {{.Name}} + {{if .IsOrganizationProject}}{{svg "octicon-project-symlink" 18 "gt-mr-3"}}{{else}}{{svg "octicon-project" 18 "gt-mr-3"}}{{end}} + {{.Title}}
    {{end}} {{end}} @@ -297,12 +285,8 @@ {{range .ClosedProjects}}
    - {{if .IsUserProject}} - {{svg "octicon-project-symlink" 18 "mr-3"}} - {{else}} - {{svg "octicon-project" 18 "mr-3"}} - {{end}} - {{.Name}} + {{if .IsOrganizationProject}}{{svg "octicon-project-symlink" 18 "gt-mr-3"}}{{else}}{{svg "octicon-project" 18 "gt-mr-3"}}{{end}} + {{.Title}}
    {{end}} {{end}} diff --git a/templates/repo/issue/view_content/comments.tmpl b/templates/repo/issue/view_content/comments.tmpl index a4b5dc58723e..9f2b7ec2d0cd 100644 --- a/templates/repo/issue/view_content/comments.tmpl +++ b/templates/repo/issue/view_content/comments.tmpl @@ -729,12 +729,12 @@ {{template "shared/user/authorlink" .Poster}} {{if gt .OldProjectID 0}} {{if gt .ProjectID 0}} - {{$.locale.Tr "repo.issues.change_project_at" (.OldProject.Name|Escape) (.Project.Name|Escape) $createdStr | Safe}} + {{$.locale.Tr "repo.issues.change_project_at" (.OldProject.Title|Escape) (.Project.Title|Escape) $createdStr | Safe}} {{else}} - {{$.locale.Tr "repo.issues.remove_project_at" (.OldProject.Name|Escape) $createdStr | Safe}} + {{$.locale.Tr "repo.issues.remove_project_at" (.OldProject.Title|Escape) $createdStr | Safe}} {{end}} {{else if gt .ProjectID 0}} - {{$.locale.Tr "repo.issues.add_project_at" (.Project.Name|Escape) $createdStr | Safe}} + {{$.locale.Tr "repo.issues.add_project_at" (.Project.Title|Escape) $createdStr | Safe}} {{end}} diff --git a/templates/repo/issue/view_content/sidebar.tmpl b/templates/repo/issue/view_content/sidebar.tmpl index b07bc77a7ddc..29bfac7c27ea 100644 --- a/templates/repo/issue/view_content/sidebar.tmpl +++ b/templates/repo/issue/view_content/sidebar.tmpl @@ -239,12 +239,8 @@ {{range .OpenProjects}} - {{if .IsUserProject}} - {{svg "octicon-project-symlink" 18 "mr-3"}} - {{else}} - {{svg "octicon-project" 18 "mr-3"}} - {{end}} - {{.Name}} + {{if .IsOrganizationProject}}{{svg "octicon-project-symlink" 18 "gt-mr-3"}}{{else}}{{svg "octicon-project" 18 "gt-mr-3"}}{{end}} + {{.Title}} {{end}} {{end}} @@ -255,12 +251,8 @@ {{range .ClosedProjects}} - {{if .IsUserProject}} - {{svg "octicon-project-symlink" 18 "mr-3"}} - {{else}} - {{svg "octicon-project" 18 "mr-3"}} - {{end}} - {{.Name}} + {{if .IsOrganizationProject}}{{svg "octicon-project-symlink" 18 "gt-mr-3"}}{{else}}{{svg "octicon-project" 18 "gt-mr-3"}}{{end}} + {{.Title}} {{end}} {{end}} @@ -271,12 +263,8 @@
    {{if .Issue.ProjectID}} - {{if .Issue.Project.IsUserProject}} - {{svg "octicon-project-symlink" 18 "mr-3"}} - {{else}} - {{svg "octicon-project" 18 "mr-3"}} - {{end}} - {{.Issue.Project.Name}} + {{if .IsOrganizationProject}}{{svg "octicon-project-symlink" 18 "gt-mr-3"}}{{else}}{{svg "octicon-project" 18 "gt-mr-3"}}{{end}} + {{.Issue.Project.Title}} {{end}}
    From 05f64152718d968ae6d1d79df612be06bdbb699e Mon Sep 17 00:00:00 2001 From: yp05327 <576951401@qq.com> Date: Thu, 9 Mar 2023 04:33:53 +0000 Subject: [PATCH 39/69] remove migration --- models/migrations/migrations.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/models/migrations/migrations.go b/models/migrations/migrations.go index 72fee11db48b..585457e474f0 100644 --- a/models/migrations/migrations.go +++ b/models/migrations/migrations.go @@ -467,8 +467,6 @@ var migrations = []Migration{ // v244 -> v245 NewMigration("Add NeedApproval to actions tables", v1_20.AddNeedApprovalToActionRun), - // v245 -> v246 - NewMigration("Change Project Type", v1_20.ChangeProjectType), } // GetCurrentDBVersion returns the current db version From e0475b78762159ac3a0796ed7c3ece49afdf17c6 Mon Sep 17 00:00:00 2001 From: yp05327 <576951401@qq.com> Date: Thu, 9 Mar 2023 04:54:02 +0000 Subject: [PATCH 40/69] revert project type --- models/project/project.go | 19 +++++++++++----- models/project/project_test.go | 3 ++- routers/web/org/projects.go | 24 +++++++++++++++----- routers/web/repo/issue.go | 40 ++++++++++++++++++++++++++++++++-- routers/web/user/profile.go | 2 +- 5 files changed, 73 insertions(+), 15 deletions(-) diff --git a/models/project/project.go b/models/project/project.go index 24343c2e2f44..831dffc0de77 100644 --- a/models/project/project.go +++ b/models/project/project.go @@ -43,11 +43,14 @@ type ( ) const ( - // TypeUser is a type of project board that is owned by a User (individual and organization) - TypeUser Type = iota + 1 + // TypeIndividual is a type of project board that is owned by an individual + TypeIndividual Type = iota + 1 - // TypeRepository is a project that is tied to a Repository + // TypeRepository is a project that is tied to a repository TypeRepository + + // TypeOrganization is a project that is tied to an organisation + TypeOrganization ) // ErrProjectNotExist represents a "ProjectNotExist" kind of error. @@ -146,14 +149,18 @@ func (p *Project) Link() string { return "" } -func (p *Project) IsUserProject() bool { - return p.Type == TypeUser +func (p *Project) IsIndividualProject() bool { + return p.Type == TypeIndividual } func (p *Project) IsRepositoryProject() bool { return p.Type == TypeRepository } +func (p *Project) IsOrganizationProject() bool { + return p.Type == TypeOrganization +} + // CanRetrievedByDoer return whether project can be retrieved by a doer in a repo func (p *Project) CanWriteByDoer(ctx context.Context, repo *repo_model.Repository, doer *user_model.User) (bool, error) { if unit.TypeProjects.UnitGlobalDisabled() { @@ -230,7 +237,7 @@ func GetCardConfig() []CardConfig { // IsTypeValid checks if a project type is valid func IsTypeValid(p Type) bool { switch p { - case TypeUser, TypeRepository: + case TypeIndividual, TypeRepository, TypeOrganization: return true default: return false diff --git a/models/project/project_test.go b/models/project/project_test.go index 582f246e961a..71ceda7aa5ad 100644 --- a/models/project/project_test.go +++ b/models/project/project_test.go @@ -20,8 +20,9 @@ func TestIsProjectTypeValid(t *testing.T) { typ Type valid bool }{ - {TypeUser, true}, + {TypeIndividual, true}, {TypeRepository, true}, + {TypeOrganization, true}, {UnknownType, false}, } diff --git a/routers/web/org/projects.go b/routers/web/org/projects.go index a16da7256655..4c15d25ed446 100644 --- a/routers/web/org/projects.go +++ b/routers/web/org/projects.go @@ -57,12 +57,19 @@ func Projects(ctx *context.Context) { page = 1 } + var projectType project_model.Type + if ctx.ContextUser.IsOrganization() { + projectType = project_model.TypeOrganization + } else { + projectType = project_model.TypeIndividual + } + projects, total, err := project_model.FindProjects(ctx, project_model.SearchOptions{ OwnerID: ctx.ContextUser.ID, Page: page, IsClosed: util.OptionalBoolOf(isShowClosed), SortType: sortType, - Type: project_model.TypeUser, + Type: projectType, }) if err != nil { ctx.ServerError("FindProjects", err) @@ -72,7 +79,7 @@ func Projects(ctx *context.Context) { opTotal, err := project_model.CountProjects(ctx, project_model.SearchOptions{ OwnerID: ctx.ContextUser.ID, IsClosed: util.OptionalBoolOf(!isShowClosed), - Type: project_model.TypeUser, + Type: projectType, }) if err != nil { ctx.ServerError("CountProjects", err) @@ -154,14 +161,21 @@ func NewProjectPost(ctx *context.Context) { return } - if err := project_model.NewProject(&project_model.Project{ + newProject := project_model.Project{ OwnerID: ctx.ContextUser.ID, Title: form.Title, Description: form.Content, CreatorID: ctx.Doer.ID, BoardType: form.BoardType, - Type: project_model.TypeUser, - }); err != nil { + } + + if ctx.ContextUser.IsOrganization() { + newProject.Type = project_model.TypeOrganization + } else { + newProject.Type = project_model.TypeIndividual + } + + if err := project_model.NewProject(&newProject); err != nil { ctx.ServerError("NewProject", err) return } diff --git a/routers/web/repo/issue.go b/routers/web/repo/issue.go index f296d36d6d5b..5752734f7f1f 100644 --- a/routers/web/repo/issue.go +++ b/routers/web/repo/issue.go @@ -516,7 +516,25 @@ func retrieveProjects(ctx *context.Context, repo *repo_model.Repository) { OwnerID: repo.OwnerID, Page: -1, IsClosed: util.OptionalBoolFalse, - Type: project_model.TypeUser, + Type: project_model.TypeIndividual, + }) + if err != nil { + ctx.ServerError("GetProjects", err) + return + } + for _, p := range openProjectsUser { + if canWriteByDoer, err := p.CanWriteByDoer(ctx, repo, ctx.Doer); err != nil { + ctx.ServerError("CanWriteByDoer", err) + return + } else if canWriteByDoer { + openProjects = append(openProjects, p) + } + } + openProjectsUser, _, err = project_model.FindProjects(ctx, project_model.SearchOptions{ + OwnerID: repo.OwnerID, + Page: -1, + IsClosed: util.OptionalBoolFalse, + Type: project_model.TypeOrganization, }) if err != nil { ctx.ServerError("GetProjects", err) @@ -550,7 +568,25 @@ func retrieveProjects(ctx *context.Context, repo *repo_model.Repository) { OwnerID: repo.OwnerID, Page: -1, IsClosed: util.OptionalBoolTrue, - Type: project_model.TypeUser, + Type: project_model.TypeIndividual, + }) + if err != nil { + ctx.ServerError("GetProjects", err) + return + } + for _, p := range closedProjectsUser { + if canWriteByDoer, err := p.CanWriteByDoer(ctx, repo, ctx.Doer); err != nil { + ctx.ServerError("CanWriteByDoer", err) + return + } else if canWriteByDoer { + closedProjects = append(closedProjects, p) + } + } + closedProjectsUser, _, err = project_model.FindProjects(ctx, project_model.SearchOptions{ + OwnerID: repo.OwnerID, + Page: -1, + IsClosed: util.OptionalBoolTrue, + Type: project_model.TypeOrganization, }) if err != nil { ctx.ServerError("GetProjects", err) diff --git a/routers/web/user/profile.go b/routers/web/user/profile.go index ca0f86478a53..af14ab7a7d1c 100644 --- a/routers/web/user/profile.go +++ b/routers/web/user/profile.go @@ -241,7 +241,7 @@ func Profile(ctx *context.Context) { OwnerID: ctx.ContextUser.ID, Page: -1, IsClosed: util.OptionalBoolFalse, - Type: project_model.TypeUser, + Type: project_model.TypeIndividual, }) if err != nil { ctx.ServerError("GetProjects", err) From d1d5f301a28b8568b4fb1e96411a8bb90abbaff0 Mon Sep 17 00:00:00 2001 From: yp05327 <576951401@qq.com> Date: Fri, 10 Mar 2023 00:12:24 +0000 Subject: [PATCH 41/69] move canWriteProjects --- routers/web/org/projects.go | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/routers/web/org/projects.go b/routers/web/org/projects.go index a2b8d07040cf..737739cd489d 100644 --- a/routers/web/org/projects.go +++ b/routers/web/org/projects.go @@ -38,13 +38,6 @@ func MustEnableProjects(ctx *context.Context) { } } -func canWriteProjects(ctx *context.Context) bool { - if ctx.ContextUser.IsOrganization() { - return ctx.Org.CanWriteUnit(ctx, unit_model.TypeProjects) - } - return ctx.Doer != nil && ctx.ContextUser.ID == ctx.Doer.ID -} - // Projects renders the home page of projects func Projects(ctx *context.Context) { ctx.Data["Title"] = ctx.Tr("repo.project_board") @@ -137,6 +130,13 @@ func Projects(ctx *context.Context) { ctx.HTML(http.StatusOK, tplProjects) } +func canWriteProjects(ctx *context.Context) bool { + if ctx.ContextUser.IsOrganization() { + return ctx.Org.CanWriteUnit(ctx, unit_model.TypeProjects) + } + return ctx.Doer != nil && ctx.ContextUser.ID == ctx.Doer.ID +} + // NewProject render creating a project page func NewProject(ctx *context.Context) { ctx.Data["Title"] = ctx.Tr("repo.projects.new") From 17b2eb2fd1bc31a8e674093c99bfb18c2101436a Mon Sep 17 00:00:00 2001 From: yp05327 <576951401@qq.com> Date: Sat, 11 Mar 2023 08:37:43 +0900 Subject: [PATCH 42/69] update new design --- models/issues/issue.go | 2 +- models/organization/org.go | 8 ++--- models/project/project.go | 2 +- modules/context/org.go | 39 +++++++++++--------- routers/web/org/home.go | 1 + routers/web/org/projects.go | 1 + routers/web/shared/user/header.go | 2 ++ routers/web/user/code.go | 1 + routers/web/user/profile.go | 1 + routers/web/web.go | 56 +++++++++++------------------ services/context/user.go | 10 ------ templates/org/menu.tmpl | 8 ++--- templates/user/overview/header.tmpl | 6 ++-- 13 files changed, 61 insertions(+), 76 deletions(-) diff --git a/models/issues/issue.go b/models/issues/issue.go index c205d7e508af..10668e3f08af 100644 --- a/models/issues/issue.go +++ b/models/issues/issue.go @@ -506,7 +506,7 @@ func (issue *Issue) CanRetrievedByDoer(ctx context.Context, p *project_model.Pro } if issue.Repo.Owner.IsOrganization() && issue.Repo.IsPrivate && - (*organization.Organization)(issue.Repo.Owner).UnitPermission(ctx, doer.ID, unit.TypeIssues) < perm.AccessModeRead { + (*organization.Organization)(issue.Repo.Owner).UnitPermission(ctx, doer, unit.TypeIssues) < perm.AccessModeRead { return false, nil } diff --git a/models/organization/org.go b/models/organization/org.go index f98b9378204f..96a669004677 100644 --- a/models/organization/org.go +++ b/models/organization/org.go @@ -239,9 +239,9 @@ func (org *Organization) CustomAvatarRelativePath() string { return org.Avatar } -func (org *Organization) UnitPermission(ctx context.Context, doerID int64, unitType unit.Type) perm.AccessMode { - if doerID > 0 { - teams, err := GetUserOrgTeams(ctx, org.ID, doerID) +func (org *Organization) UnitPermission(ctx context.Context, doer *user_model.User, unitType unit.Type) perm.AccessMode { + if doer != nil { + teams, err := GetUserOrgTeams(ctx, org.ID, doer.ID) if err != nil { log.Error("GetUserOrgTeams: %v", err) return perm.AccessModeNone @@ -257,7 +257,7 @@ func (org *Organization) UnitPermission(ctx context.Context, doerID int64, unitT } } - if org.Visibility == structs.VisibleTypePublic { + if org.Visibility.IsPublic() { return perm.AccessModeRead } diff --git a/models/project/project.go b/models/project/project.go index 831dffc0de77..6626ca81058b 100644 --- a/models/project/project.go +++ b/models/project/project.go @@ -204,7 +204,7 @@ func (p *Project) CanWriteByDoer(ctx context.Context, repo *repo_model.Repositor } if repo.Owner.IsOrganization() { - if (*organization.Organization)(repo.Owner).UnitPermission(ctx, doer.ID, unit.TypeProjects) < perm.AccessModeWrite { + if (*organization.Organization)(repo.Owner).UnitPermission(ctx, doer, unit.TypeProjects) < perm.AccessModeWrite { return false, nil } } diff --git a/modules/context/org.go b/modules/context/org.go index a01ca603111e..b6f4dc04749e 100644 --- a/modules/context/org.go +++ b/modules/context/org.go @@ -30,17 +30,11 @@ type Organization struct { } func (org *Organization) CanWriteUnit(ctx *Context, unitType unit.Type) bool { - if ctx.Doer == nil { - return false - } - return org.Organization.UnitPermission(ctx, ctx.Doer.ID, unitType) >= perm.AccessModeWrite + return org.Organization.UnitPermission(ctx, ctx.Doer, unitType) >= perm.AccessModeWrite } -func (org *Organization) CanAccessUnit(ctx *Context, unitType unit.Type) bool { - if ctx.Doer == nil { - return org.Organization.Visibility.IsPublic() - } - return org.Organization.UnitPermission(ctx, ctx.Doer.ID, unitType) >= perm.AccessModeRead +func (org *Organization) CanReadUnit(ctx *Context, unitType unit.Type) bool { + return org.Organization.UnitPermission(ctx, ctx.Doer, unitType) >= perm.AccessModeRead } func GetOrganizationByParams(ctx *Context) { @@ -89,13 +83,23 @@ func HandleOrgAssignment(ctx *Context, args ...bool) { var err error - // if Organization is not defined, get it from params - if ctx.Org.Organization == nil { - GetOrganizationByParams(ctx) + if ctx.ContextUser == nil { + // if Organization is not defined, get it from params + if ctx.Org.Organization == nil { + GetOrganizationByParams(ctx) - if ctx.Written() { - return + if ctx.Written() { + return + } + } + } else if ctx.ContextUser.IsOrganization() { + if ctx.Org == nil { + ctx.Org = &Organization{} } + ctx.Org.Organization = (*organization.Organization)(ctx.ContextUser) + } else { + // ContextUser is an individual User + return } org := ctx.Org.Organization @@ -158,6 +162,7 @@ func HandleOrgAssignment(ctx *Context, args ...bool) { } ctx.Data["IsOrganizationOwner"] = ctx.Org.IsOwner ctx.Data["IsOrganizationMember"] = ctx.Org.IsMember + ctx.Data["IsProjectEnabled"] = true ctx.Data["IsPackageEnabled"] = setting.Packages.Enabled ctx.Data["IsRepoIndexerEnabled"] = setting.Indexer.RepoIndexerEnabled ctx.Data["IsPublicMember"] = func(uid int64) bool { @@ -234,9 +239,9 @@ func HandleOrgAssignment(ctx *Context, args ...bool) { } } - ctx.Data["CanAccessProjects"] = ctx.Org.CanAccessUnit(ctx, unit.TypeProjects) - ctx.Data["CanAccessPackages"] = ctx.Org.CanAccessUnit(ctx, unit.TypePackages) - ctx.Data["CanAccessCode"] = ctx.Org.CanAccessUnit(ctx, unit.TypeCode) + ctx.Data["CanReadProjects"] = ctx.Org.CanReadUnit(ctx, unit.TypeProjects) + ctx.Data["CanReadPackages"] = ctx.Org.CanReadUnit(ctx, unit.TypePackages) + ctx.Data["CanReadCode"] = ctx.Org.CanReadUnit(ctx, unit.TypeCode) } // OrgAssignment returns a middleware to handle organization assignment diff --git a/routers/web/org/home.go b/routers/web/org/home.go index 4cc364acd3a0..8c9cc8a9d86b 100644 --- a/routers/web/org/home.go +++ b/routers/web/org/home.go @@ -156,6 +156,7 @@ func Home(ctx *context.Context) { pager.SetDefaultParams(ctx) pager.AddParam(ctx, "language", "Language") ctx.Data["Page"] = pager + ctx.Data["ContextUser"] = ctx.ContextUser ctx.HTML(http.StatusOK, tplOrgHome) } diff --git a/routers/web/org/projects.go b/routers/web/org/projects.go index 737739cd489d..f009a51282bf 100644 --- a/routers/web/org/projects.go +++ b/routers/web/org/projects.go @@ -142,6 +142,7 @@ func NewProject(ctx *context.Context) { ctx.Data["Title"] = ctx.Tr("repo.projects.new") ctx.Data["BoardTypes"] = project_model.GetBoardConfig() ctx.Data["CanWriteProjects"] = canWriteProjects(ctx) + ctx.Data["PageIsViewProjects"] = true ctx.Data["HomeLink"] = ctx.ContextUser.HomeLink() shared_user.RenderUserHeader(ctx) ctx.HTML(http.StatusOK, tplProjectsNew) diff --git a/routers/web/shared/user/header.go b/routers/web/shared/user/header.go index 94e59e2a490f..05e45f999eed 100644 --- a/routers/web/shared/user/header.go +++ b/routers/web/shared/user/header.go @@ -9,6 +9,8 @@ import ( ) func RenderUserHeader(ctx *context.Context) { + ctx.Data["IsProjectEnabled"] = true + ctx.Data["IsPackageEnabled"] = setting.Packages.Enabled ctx.Data["IsRepoIndexerEnabled"] = setting.Indexer.RepoIndexerEnabled ctx.Data["ContextUser"] = ctx.ContextUser } diff --git a/routers/web/user/code.go b/routers/web/user/code.go index 81e3e65b4b67..b3adbcb8d3a8 100644 --- a/routers/web/user/code.go +++ b/routers/web/user/code.go @@ -24,6 +24,7 @@ func CodeSearch(ctx *context.Context) { return } + ctx.Data["IsProjectEnabled"] = true ctx.Data["IsPackageEnabled"] = setting.Packages.Enabled ctx.Data["IsRepoIndexerEnabled"] = setting.Indexer.RepoIndexerEnabled ctx.Data["Title"] = ctx.Tr("explore.code") diff --git a/routers/web/user/profile.go b/routers/web/user/profile.go index af14ab7a7d1c..044dbd40f79a 100644 --- a/routers/web/user/profile.go +++ b/routers/web/user/profile.go @@ -305,6 +305,7 @@ func Profile(ctx *context.Context) { pager.AddParam(ctx, "date", "Date") } ctx.Data["Page"] = pager + ctx.Data["IsProjectEnabled"] = true ctx.Data["IsPackageEnabled"] = setting.Packages.Enabled ctx.Data["IsRepoIndexerEnabled"] = setting.Indexer.RepoIndexerEnabled diff --git a/routers/web/web.go b/routers/web/web.go index 9ba1ca006387..cf3a742244ce 100644 --- a/routers/web/web.go +++ b/routers/web/web.go @@ -690,6 +690,21 @@ func RegisterRoutes(m *web.Route) { } } + reqUnitAccess := func(unitType unit.Type, accessMode perm.AccessMode) func(ctx *context.Context) { + return func(ctx *context.Context) { + if ctx.ContextUser == nil { + ctx.NotFound(unitType.String(), nil) + return + } + if ctx.ContextUser.IsOrganization() { + if ctx.Org.Organization.UnitPermission(ctx, ctx.Doer, unitType) < accessMode { + ctx.NotFound(unitType.String(), nil) + return + } + } + } + } + // ***** START: Organization ***** m.Group("/org", func() { m.Group("/{org}", func() { @@ -872,18 +887,7 @@ func RegisterRoutes(m *web.Route) { m.Group("", func() { m.Get("", org.Projects) m.Get("/{id}", org.ViewProject) - }, func(ctx *context.Context) { - if ctx.ContextUser == nil { - ctx.NotFound("Project", nil) - return - } - if ctx.ContextUser.IsOrganization() { - if !ctx.Org.CanAccessUnit(ctx, unit.TypeProjects) { - ctx.NotFound("Project", nil) - return - } - } - }) + }, reqUnitAccess(unit.TypeProjects, perm.AccessModeRead)) m.Group("", func() { //nolint:dupl m.Get("/new", org.NewProject) m.Post("/new", web.Bind(forms.CreateProjectForm{}), org.NewProjectPost) @@ -903,17 +907,8 @@ func RegisterRoutes(m *web.Route) { m.Post("/move", org.MoveIssues) }) }) - }, reqSignIn, func(ctx *context.Context) { - if ctx.ContextUser == nil { - ctx.NotFound("NewProject", nil) - return - } - if ctx.ContextUser.IsOrganization() { - if !ctx.Org.CanWriteUnit(ctx, unit.TypeProjects) { - ctx.NotFound("NewProject", nil) - return - } - } else if ctx.ContextUser.ID != ctx.Doer.ID { + }, reqSignIn, reqUnitAccess(unit.TypeProjects, perm.AccessModeWrite), func(ctx *context.Context) { + if ctx.ContextUser.IsIndividual() && ctx.ContextUser.ID != ctx.Doer.ID { ctx.NotFound("NewProject", nil) return } @@ -921,19 +916,8 @@ func RegisterRoutes(m *web.Route) { }, repo.MustEnableProjects) m.Group("", func() { m.Get("/code", user.CodeSearch) - }, func(ctx *context.Context) { - if ctx.ContextUser == nil { - ctx.NotFound("Code", nil) - return - } - if ctx.ContextUser.IsOrganization() { - if !ctx.Org.CanAccessUnit(ctx, unit.TypeCode) { - ctx.NotFound("Code", nil) - return - } - } - }) - }, context_service.UserAssignmentWeb()) + }, reqUnitAccess(unit.TypeCode, perm.AccessModeRead)) + }, context_service.UserAssignmentWeb(), context.OrgAssignment()) // ***** Release Attachment Download without Signin m.Get("/{username}/{reponame}/releases/download/{vTag}/{fileName}", ignSignIn, context.RepoAssignment, repo.MustBeNotEmpty, repo.RedirectDownload) diff --git a/services/context/user.go b/services/context/user.go index 29a02a4aa563..9dc84c3ac15e 100644 --- a/services/context/user.go +++ b/services/context/user.go @@ -8,7 +8,6 @@ import ( "net/http" "strings" - org_model "code.gitea.io/gitea/models/organization" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/context" ) @@ -57,15 +56,6 @@ func userAssignment(ctx *context.Context, errCb func(int, string, interface{})) } else { errCb(http.StatusInternalServerError, "GetUserByName", err) } - } else { - if ctx.ContextUser.IsOrganization() { - if ctx.Org == nil { - ctx.Org = &context.Organization{} - } - ctx.Org.Organization = (*org_model.Organization)(ctx.ContextUser) - - context.HandleOrgAssignment(ctx) - } } } } diff --git a/templates/org/menu.tmpl b/templates/org/menu.tmpl index 7f894f3c5b93..2a359d811134 100644 --- a/templates/org/menu.tmpl +++ b/templates/org/menu.tmpl @@ -3,18 +3,18 @@ {{svg "octicon-repo"}} {{.locale.Tr "user.repositories"}} - {{if .CanAccessProjects}} + {{if and .IsProjectEnabled .CanReadProjects}} {{svg "octicon-project-symlink"}} {{.locale.Tr "user.projects"}} {{end}} - {{if and .IsPackageEnabled .CanAccessPackages}} + {{if and .IsPackageEnabled .CanReadPackages}} {{svg "octicon-package"}} {{.locale.Tr "packages.title"}} {{end}} - {{if and .IsRepoIndexerEnabled .CanAccessCode}} - + {{if and .IsRepoIndexerEnabled .CanReadCode}} + {{svg "octicon-code"}} {{$.locale.Tr "org.code"}} {{end}} diff --git a/templates/user/overview/header.tmpl b/templates/user/overview/header.tmpl index 92aa26a758d8..b4f7d6f90091 100644 --- a/templates/user/overview/header.tmpl +++ b/templates/user/overview/header.tmpl @@ -22,17 +22,17 @@ {{svg "octicon-repo"}} {{.locale.Tr "user.repositories"}} - {{if or (not .ContextUser.IsOrganization) (and .ContextUser.IsOrganization .CanAccessProjects)}} + {{if and .IsProjectEnabled (or .ContextUser.IsIndividual (and .ContextUser.IsOrganization .CanReadProjects))}} {{svg "octicon-project-symlink"}} {{.locale.Tr "user.projects"}} {{end}} - {{if and (not .UnitPackagesGlobalDisabled) (or (not .ContextUser.IsOrganization) (and .ContextUser.IsOrganization .CanAccessPackages))}} + {{if and .IsPackageEnabled (or .ContextUser.IsIndividual (and .ContextUser.IsOrganization .CanReadPackages))}} {{svg "octicon-package"}} {{.locale.Tr "packages.title"}} {{end}} - {{if and .IsRepoIndexerEnabled (or (not .ContextUser.IsOrganization) (and .ContextUser.IsOrganization .CanAccessCode))}} + {{if and .IsRepoIndexerEnabled (or .ContextUser.IsIndividual (and .ContextUser.IsOrganization .CanReadCode))}} {{svg "octicon-code"}} {{.locale.Tr "user.code"}} From aaf9ef831d476fd8b8e0f025385618be7a3e48b0 Mon Sep 17 00:00:00 2001 From: yp05327 <576951401@qq.com> Date: Mon, 13 Mar 2023 01:35:39 +0000 Subject: [PATCH 43/69] remove --- templates/repo/projects/list.tmpl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/repo/projects/list.tmpl b/templates/repo/projects/list.tmpl index 3c8231a91174..39bd66db33e0 100644 --- a/templates/repo/projects/list.tmpl +++ b/templates/repo/projects/list.tmpl @@ -32,7 +32,7 @@ From 5adbb7d683142c6eebc670b8a157f86aec701b70 Mon Sep 17 00:00:00 2001 From: yp05327 <576951401@qq.com> Date: Mon, 13 Mar 2023 02:15:27 +0000 Subject: [PATCH 44/69] remove unnecessary code --- models/issues/issue.go | 14 +----------- models/issues/issue_project.go | 8 ++----- models/project/board.go | 39 ---------------------------------- routers/web/org/projects.go | 3 --- routers/web/repo/projects.go | 3 --- 5 files changed, 3 insertions(+), 64 deletions(-) diff --git a/models/issues/issue.go b/models/issues/issue.go index 10668e3f08af..56b332a84dec 100644 --- a/models/issues/issue.go +++ b/models/issues/issue.go @@ -479,7 +479,7 @@ func (issue *Issue) getLabels(ctx context.Context) (err error) { } // CanRetrievedByDoer returns whether doer can retrieve the issue -func (issue *Issue) CanRetrievedByDoer(ctx context.Context, p *project_model.Project, doer *user_model.User) (bool, error) { +func (issue *Issue) CanRetrievedByDoer(ctx context.Context, doer *user_model.User) (bool, error) { if err := issue.LoadRepo(ctx); err != nil { return false, err } @@ -510,18 +510,6 @@ func (issue *Issue) CanRetrievedByDoer(ctx context.Context, p *project_model.Pro return false, nil } - if p.RepoID > 0 { - // repo project - if p.RepoID != issue.RepoID { - return false, nil - } - } else { - // individual/org's project - if p.OwnerID != issue.Repo.OwnerID { - return false, nil - } - } - return true, nil } diff --git a/models/issues/issue_project.go b/models/issues/issue_project.go index 59c6b600fccd..5b74e4f63326 100644 --- a/models/issues/issue_project.go +++ b/models/issues/issue_project.go @@ -60,10 +60,6 @@ func (issue *Issue) projectBoardID(ctx context.Context) int64 { func LoadIssuesFromBoard(ctx context.Context, b *project_model.Board, doer *user_model.User, isClosed util.OptionalBool) (IssueList, error) { issueList := make([]*Issue, 0, 10) - if err := b.LoadProject(ctx); err != nil { - return nil, err - } - if b.ID != 0 { issues, err := Issues(ctx, &IssuesOptions{ ProjectBoardID: b.ID, @@ -75,7 +71,7 @@ func LoadIssuesFromBoard(ctx context.Context, b *project_model.Board, doer *user return nil, err } for _, issue := range issues { - if canRetrievedByDoer, err := issue.CanRetrievedByDoer(ctx, b.Project, doer); err != nil { + if canRetrievedByDoer, err := issue.CanRetrievedByDoer(ctx, doer); err != nil { return nil, err } else if canRetrievedByDoer { issueList = append(issueList, issue) @@ -94,7 +90,7 @@ func LoadIssuesFromBoard(ctx context.Context, b *project_model.Board, doer *user return nil, err } for _, issue := range issues { - if canRetrievedByDoer, err := issue.CanRetrievedByDoer(ctx, b.Project, doer); err != nil { + if canRetrievedByDoer, err := issue.CanRetrievedByDoer(ctx, doer); err != nil { return nil, err } else if canRetrievedByDoer { issueList = append(issueList, issue) diff --git a/models/project/board.go b/models/project/board.go index ce7a8e67f09b..97a9e38d84b4 100644 --- a/models/project/board.go +++ b/models/project/board.go @@ -70,45 +70,6 @@ func (Board) TableName() string { return "project_board" } -// LoadProject loads project -func (b *Board) LoadProject(ctx context.Context) error { - if b.Project != nil { - return nil - } - - var project Project - has, err := db.GetEngine(ctx).ID(b.ProjectID).Get(&project) - if err != nil { - return err - } else if !has { - return ErrProjectNotExist{ - ID: b.ProjectID, - } - } - b.Project = &project - return nil -} - -// SetProject set an exist project object to the board -func (b *Board) SetProject(p *Project) error { - if b.ProjectID != p.ID { - return fmt.Errorf("board[id:%d] doesn't belong to project[id:%d]", b.ID, p.ID) - } - - b.Project = p - return nil -} - -// SetProject set an exist project object to the board -func (bl BoardList) SetProject(p *Project) error { - for _, b := range bl { - if err := b.SetProject(p); err != nil { - return err - } - } - return nil -} - // NumIssues return counter of all issues assigned to the board func (b *Board) NumIssues() int { c, err := db.GetEngine(db.DefaultContext).Table("project_issue"). diff --git a/routers/web/org/projects.go b/routers/web/org/projects.go index f009a51282bf..d3f09568191b 100644 --- a/routers/web/org/projects.go +++ b/routers/web/org/projects.go @@ -334,9 +334,6 @@ func ViewProject(ctx *context.Context) { boards[0].Title = ctx.Tr("repo.projects.type.uncategorized") } - // avild multiple loading of project in LoadIssuesFromBoardList - boards.SetProject(project) - issuesMap, err := issues_model.LoadIssuesFromBoardList(ctx, boards, ctx.Doer, util.OptionalBoolNone) if err != nil { ctx.ServerError("LoadIssuesOfBoards", err) diff --git a/routers/web/repo/projects.go b/routers/web/repo/projects.go index 2f9a8ddbd492..e8271ade0ff5 100644 --- a/routers/web/repo/projects.go +++ b/routers/web/repo/projects.go @@ -322,9 +322,6 @@ func ViewProject(ctx *context.Context) { boards[0].Title = ctx.Tr("repo.projects.type.uncategorized") } - // avild multiple loading of project in LoadIssuesFromBoardList - boards.SetProject(project) - issuesMap, err := issues_model.LoadIssuesFromBoardList(ctx, boards, ctx.Doer, util.OptionalBoolNone) if err != nil { ctx.ServerError("LoadIssuesOfBoards", err) From 894f96009483a89e3c8fa604520fe22e69f2423e Mon Sep 17 00:00:00 2001 From: yp05327 <576951401@qq.com> Date: Mon, 13 Mar 2023 02:49:05 +0000 Subject: [PATCH 45/69] add permission check for collaborations --- models/issues/issue.go | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/models/issues/issue.go b/models/issues/issue.go index 56b332a84dec..10c3106f6e07 100644 --- a/models/issues/issue.go +++ b/models/issues/issue.go @@ -505,9 +505,20 @@ func (issue *Issue) CanRetrievedByDoer(ctx context.Context, doer *user_model.Use return false, err } - if issue.Repo.Owner.IsOrganization() && issue.Repo.IsPrivate && - (*organization.Organization)(issue.Repo.Owner).UnitPermission(ctx, doer, unit.TypeIssues) < perm.AccessModeRead { - return false, nil + if issue.Repo.Owner.IsOrganization() && issue.Repo.IsPrivate { + collaboration, err := repo_model.GetCollaboration(ctx, issue.Repo.ID, doer.ID) + if err != nil { + return false, fmt.Errorf("getCollaborators: %w", err) + } + if collaboration == nil { + if (*organization.Organization)(issue.Repo.Owner).UnitPermission(ctx, doer, unit.TypeIssues) < perm.AccessModeRead { + return false, nil + } + } else { + if collaboration.Mode < perm.AccessModeRead { + return false, nil + } + } } return true, nil From cd10524ee53459cd12e5ec028b4d1933bf339cbe Mon Sep 17 00:00:00 2001 From: yp05327 <576951401@qq.com> Date: Mon, 13 Mar 2023 04:36:19 +0000 Subject: [PATCH 46/69] improve performance --- models/issues/issue.go | 26 ++++++++++++++++------- models/issues/issue_project.go | 24 +++++++++------------ models/repo/repo_list.go | 39 +++++++++++++++++++++++++--------- 3 files changed, 57 insertions(+), 32 deletions(-) diff --git a/models/issues/issue.go b/models/issues/issue.go index 10c3106f6e07..9ac0b9b132e4 100644 --- a/models/issues/issue.go +++ b/models/issues/issue.go @@ -478,12 +478,26 @@ func (issue *Issue) getLabels(ctx context.Context) (err error) { return nil } -// CanRetrievedByDoer returns whether doer can retrieve the issue -func (issue *Issue) CanRetrievedByDoer(ctx context.Context, doer *user_model.User) (bool, error) { - if err := issue.LoadRepo(ctx); err != nil { - return false, err +func (issues IssueList) FliterVaildByDoer(ctx context.Context, doer *user_model.User) (IssueList, error) { + repos, err := issues.LoadRepositories(ctx) + if err != nil { + return nil, err } + (repo_model.RepositoryList)(repos).LoadOwners(ctx) + issueList := issues[:0] + for _, issue := range issues { + if canRetrievedByDoer, err := issue.canRetrievedByDoer(ctx, doer); err != nil { + return nil, err + } else if canRetrievedByDoer { + issueList = append(issueList, issue) + } + } + return issueList, nil +} + +// canRetrievedByDoer returns whether doer can retrieve the issue +func (issue *Issue) canRetrievedByDoer(ctx context.Context, doer *user_model.User) (bool, error) { if perm, err := access_model.GetUserRepoPermission(ctx, issue.Repo, doer); err != nil { return false, err } else if !perm.HasAccess() { @@ -501,10 +515,6 @@ func (issue *Issue) CanRetrievedByDoer(ctx context.Context, doer *user_model.Use return false, nil } - if err := issue.Repo.LoadOwner(ctx); err != nil { - return false, err - } - if issue.Repo.Owner.IsOrganization() && issue.Repo.IsPrivate { collaboration, err := repo_model.GetCollaboration(ctx, issue.Repo.ID, doer.ID) if err != nil { diff --git a/models/issues/issue_project.go b/models/issues/issue_project.go index 5b74e4f63326..db739f6c5b09 100644 --- a/models/issues/issue_project.go +++ b/models/issues/issue_project.go @@ -58,7 +58,7 @@ func (issue *Issue) projectBoardID(ctx context.Context) int64 { // LoadIssuesFromBoard load issues assigned to this board func LoadIssuesFromBoard(ctx context.Context, b *project_model.Board, doer *user_model.User, isClosed util.OptionalBool) (IssueList, error) { - issueList := make([]*Issue, 0, 10) + var issueList IssueList if b.ID != 0 { issues, err := Issues(ctx, &IssuesOptions{ @@ -70,13 +70,11 @@ func LoadIssuesFromBoard(ctx context.Context, b *project_model.Board, doer *user if err != nil { return nil, err } - for _, issue := range issues { - if canRetrievedByDoer, err := issue.CanRetrievedByDoer(ctx, doer); err != nil { - return nil, err - } else if canRetrievedByDoer { - issueList = append(issueList, issue) - } + issues, err = IssueList(issues).FliterVaildByDoer(ctx, doer) + if err != nil { + return nil, err } + issueList = append(issueList, issues...) } if b.Default { @@ -89,16 +87,14 @@ func LoadIssuesFromBoard(ctx context.Context, b *project_model.Board, doer *user if err != nil { return nil, err } - for _, issue := range issues { - if canRetrievedByDoer, err := issue.CanRetrievedByDoer(ctx, doer); err != nil { - return nil, err - } else if canRetrievedByDoer { - issueList = append(issueList, issue) - } + issues, err = IssueList(issues).FliterVaildByDoer(ctx, doer) + if err != nil { + return nil, err } + issueList = append(issueList, issues...) } - if err := IssueList(issueList).LoadComments(ctx); err != nil { + if err := issueList.LoadComments(ctx); err != nil { return nil, err } diff --git a/models/repo/repo_list.go b/models/repo/repo_list.go index d9cd905a1936..09e755f2dd38 100644 --- a/models/repo/repo_list.go +++ b/models/repo/repo_list.go @@ -62,6 +62,34 @@ func RepositoryListOfMap(repoMap map[int64]*Repository) RepositoryList { return RepositoryList(ValuesRepository(repoMap)) } +func (repos RepositoryList) GetOwnerIDs() []int64 { + ids := make(container.Set[int64], len(repos)) + for _, repo := range repos { + if repo.OwnerID == 0 { + continue + } + ids.Add(repo.OwnerID) + } + return ids.Values() +} + +func (repos RepositoryList) LoadOwners(ctx context.Context) error { + userIDs := repos.GetOwnerIDs() + users := make(map[int64]*user_model.User, len(userIDs)) + if err := db.GetEngine(ctx). + Where("id > 0"). + In("id", userIDs). + Find(&users); err != nil { + return fmt.Errorf("find users: %w", err) + } + for _, repo := range repos { + if repo.OwnerID > 0 && repo.Owner == nil { + repo.Owner = users[repo.OwnerID] + } + } + return nil +} + func (repos RepositoryList) loadAttributes(ctx context.Context) error { if len(repos) == 0 { return nil @@ -75,16 +103,7 @@ func (repos RepositoryList) loadAttributes(ctx context.Context) error { } // Load owners. - users := make(map[int64]*user_model.User, len(set)) - if err := db.GetEngine(ctx). - Where("id > 0"). - In("id", set.Values()). - Find(&users); err != nil { - return fmt.Errorf("find users: %w", err) - } - for i := range repos { - repos[i].Owner = users[repos[i].OwnerID] - } + repos.LoadOwners(ctx) // Load primary language. stats := make(LanguageStatList, 0, len(repos)) From 3fba5cc261b772fa5c3d37cefddfe716e265e630 Mon Sep 17 00:00:00 2001 From: yp05327 <576951401@qq.com> Date: Mon, 13 Mar 2023 04:48:27 +0000 Subject: [PATCH 47/69] fix incorrect issue num in project list page --- models/issues/issue_project.go | 12 ++++++------ templates/projects/list.tmpl | 4 ++-- templates/repo/projects/list.tmpl | 4 ++-- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/models/issues/issue_project.go b/models/issues/issue_project.go index db739f6c5b09..1e44df8c12fd 100644 --- a/models/issues/issue_project.go +++ b/models/issues/issue_project.go @@ -115,17 +115,17 @@ func LoadIssuesFromBoardList(ctx context.Context, bs project_model.BoardList, do } // NumIssuesInProjects returns counter of all issues assigned to a project list which doer can access -func NumIssuesInProjects(ctx context.Context, pl project_model.List, doer *user_model.User, isClosed util.OptionalBool) (int, error) { - numIssuesInProjects := int(0) +func NumIssuesInProjects(ctx context.Context, pl project_model.List, doer *user_model.User, isClosed util.OptionalBool) (map[int64]int, error) { + numMap := make(map[int64]int, len(pl)) for _, p := range pl { - numIssuesInProject, err := NumIssuesInProject(ctx, p, doer, isClosed) + num, err := NumIssuesInProject(ctx, p, doer, isClosed) if err != nil { - return 0, err + return nil, err } - numIssuesInProjects += numIssuesInProject + numMap[p.ID] = num } - return numIssuesInProjects, nil + return numMap, nil } // NumIssuesInProject returns counter of all issues assigned to a project which doer can access diff --git a/templates/projects/list.tmpl b/templates/projects/list.tmpl index 19cec473f618..8f86f7651b5b 100644 --- a/templates/projects/list.tmpl +++ b/templates/projects/list.tmpl @@ -46,9 +46,9 @@ {{end}} {{svg "octicon-issue-opened" 16 "gt-mr-3"}} - {{JsPrettyNumber $.NumOpenIssuesInProjects}} {{$.locale.Tr "repo.issues.open_title"}} + {{JsPrettyNumber (index $.NumOpenIssuesInProjects .ID)}} {{$.locale.Tr "repo.issues.open_title"}} {{svg "octicon-check" 16 "gt-mr-3"}} - {{JsPrettyNumber $.NumClosedIssuesInProjects}} {{$.locale.Tr "repo.issues.closed_title"}} + {{JsPrettyNumber (index $.NumClosedIssuesInProjects .ID)}} {{$.locale.Tr "repo.issues.closed_title"}} {{if and $.CanWriteProjects (not $.Repository.IsArchived)}} diff --git a/templates/repo/projects/list.tmpl b/templates/repo/projects/list.tmpl index 39bd66db33e0..66f5e8122e5e 100644 --- a/templates/repo/projects/list.tmpl +++ b/templates/repo/projects/list.tmpl @@ -48,9 +48,9 @@ {{end}} {{svg "octicon-issue-opened" 16 "gt-mr-3"}} - {{JsPrettyNumber $.NumOpenIssuesInProjects}} {{$.locale.Tr "repo.issues.open_title"}} + {{JsPrettyNumber (index $.NumOpenIssuesInProjects .ID)}} {{$.locale.Tr "repo.issues.open_title"}} {{svg "octicon-check" 16 "gt-mr-3"}} - {{JsPrettyNumber $.NumClosedIssuesInProjects}} {{$.locale.Tr "repo.issues.closed_title"}} + {{JsPrettyNumber (index $.NumClosedIssuesInProjects .ID)}} {{$.locale.Tr "repo.issues.closed_title"}} {{if and $.CanWriteProjects (not $.Repository.IsArchived)}} From f16fc72642f31377337d931cb561865220d566de Mon Sep 17 00:00:00 2001 From: yp05327 <576951401@qq.com> Date: Mon, 13 Mar 2023 05:20:52 +0000 Subject: [PATCH 48/69] update --- models/issues/issue.go | 2 +- models/issues/issue_project.go | 2 +- models/project/project.go | 75 ++++++++++++++++++++++++++++++---- models/repo/repo_list.go | 4 +- 4 files changed, 71 insertions(+), 12 deletions(-) diff --git a/models/issues/issue.go b/models/issues/issue.go index 9ac0b9b132e4..5a32e3d3622f 100644 --- a/models/issues/issue.go +++ b/models/issues/issue.go @@ -518,7 +518,7 @@ func (issue *Issue) canRetrievedByDoer(ctx context.Context, doer *user_model.Use if issue.Repo.Owner.IsOrganization() && issue.Repo.IsPrivate { collaboration, err := repo_model.GetCollaboration(ctx, issue.Repo.ID, doer.ID) if err != nil { - return false, fmt.Errorf("getCollaborators: %w", err) + return false, fmt.Errorf("GetCollaboration: %w", err) } if collaboration == nil { if (*organization.Organization)(issue.Repo.Owner).UnitPermission(ctx, doer, unit.TypeIssues) < perm.AccessModeRead { diff --git a/models/issues/issue_project.go b/models/issues/issue_project.go index 1e44df8c12fd..17f1d2831a54 100644 --- a/models/issues/issue_project.go +++ b/models/issues/issue_project.go @@ -177,7 +177,7 @@ func addUpdateIssueProject(ctx context.Context, issue *Issue, doer *user_model.U if canWriteByDoer, err := newProject.CanWriteByDoer(ctx, issue.Repo, doer); err != nil { return err } else if !canWriteByDoer { - return fmt.Errorf("issue's repository can't be retrieved by doer") + return fmt.Errorf("doer have no write permission to project [id:%d]", newProjectID) } } diff --git a/models/project/project.go b/models/project/project.go index 6626ca81058b..2f783b35e437 100644 --- a/models/project/project.go +++ b/models/project/project.go @@ -14,6 +14,7 @@ import ( repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/models/unit" user_model "code.gitea.io/gitea/models/user" + "code.gitea.io/gitea/modules/container" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/timeutil" @@ -161,12 +162,62 @@ func (p *Project) IsOrganizationProject() bool { return p.Type == TypeOrganization } -// CanRetrievedByDoer return whether project can be retrieved by a doer in a repo +func (pl List) GetOwnerIDs() []int64 { + ids := make(container.Set[int64], len(pl)) + for _, p := range pl { + if p.OwnerID == 0 { + continue + } + ids.Add(p.OwnerID) + } + return ids.Values() +} + +func (pl List) LoadOwners(ctx context.Context) error { + userIDs := pl.GetOwnerIDs() + users := make(map[int64]*user_model.User, len(userIDs)) + if err := db.GetEngine(ctx). + Where("id > 0"). + In("id", userIDs). + Find(&users); err != nil { + return fmt.Errorf("find users: %w", err) + } + for _, p := range pl { + if p.OwnerID > 0 && p.Owner == nil { + p.Owner = users[p.OwnerID] + } + } + return nil +} + +// CanWriteByDoer return whether doer have write permission to the project in a repo +func (pl List) FliterWritableByDoer(ctx context.Context, repo *repo_model.Repository, doer *user_model.User) (List, error) { + if err := pl.LoadOwners(ctx); err != nil { + return nil, err + } + + projectList := pl[:0] + for _, p := range pl { + if canWriteByDoer, err := p.CanWriteByDoer(ctx, repo, doer); err != nil { + return nil, err + } else if canWriteByDoer { + projectList = append(projectList, p) + } + } + return projectList, nil +} + +// CanWriteByDoer return whether doer have write permission to the project in a repo func (p *Project) CanWriteByDoer(ctx context.Context, repo *repo_model.Repository, doer *user_model.User) (bool, error) { if unit.TypeProjects.UnitGlobalDisabled() { return false, nil } + // non-login user have no write permission + if doer == nil { + return false, nil + } + if err := repo.LoadOwner(ctx); err != nil { return false, fmt.Errorf("LoadOwner: %w", err) } @@ -178,7 +229,7 @@ func (p *Project) CanWriteByDoer(ctx context.Context, repo *repo_model.Repositor } if err := p.LoadRepo(ctx); err != nil { - return false, fmt.Errorf("LoadOwner: %w", err) + return false, fmt.Errorf("LoadRepo: %w", err) } if perm, err := access_model.GetUserRepoPermission(ctx, p.Repo, doer); err != nil { return false, err @@ -195,17 +246,23 @@ func (p *Project) CanWriteByDoer(ctx context.Context, repo *repo_model.Repositor return false, nil } - if doer == nil { - return false, nil - } - if repo.Owner.IsIndividual() && repo.Owner.ID != doer.ID { return false, nil } - if repo.Owner.IsOrganization() { - if (*organization.Organization)(repo.Owner).UnitPermission(ctx, doer, unit.TypeProjects) < perm.AccessModeWrite { - return false, nil + if repo.Owner.IsOrganization() && repo.IsPrivate { + collaboration, err := repo_model.GetCollaboration(ctx, repo.ID, doer.ID) + if err != nil { + return false, fmt.Errorf("GetCollaboration: %w", err) + } + if collaboration == nil { + if (*organization.Organization)(repo.Owner).UnitPermission(ctx, doer, unit.TypeIssues) < perm.AccessModeWrite { + return false, nil + } + } else { + if collaboration.Mode < perm.AccessModeWrite { + return false, nil + } } } } diff --git a/models/repo/repo_list.go b/models/repo/repo_list.go index 09e755f2dd38..6c5446a464ce 100644 --- a/models/repo/repo_list.go +++ b/models/repo/repo_list.go @@ -103,7 +103,9 @@ func (repos RepositoryList) loadAttributes(ctx context.Context) error { } // Load owners. - repos.LoadOwners(ctx) + if err := repos.LoadOwners(ctx); err != nil { + return err + } // Load primary language. stats := make(LanguageStatList, 0, len(repos)) From f0cd211e6fd299be4c2b70eb84e3d97a04a41d2d Mon Sep 17 00:00:00 2001 From: yp05327 <576951401@qq.com> Date: Mon, 13 Mar 2023 05:31:48 +0000 Subject: [PATCH 49/69] add err check --- models/issues/issue.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/models/issues/issue.go b/models/issues/issue.go index 5a32e3d3622f..51a46f2e26dd 100644 --- a/models/issues/issue.go +++ b/models/issues/issue.go @@ -484,7 +484,10 @@ func (issues IssueList) FliterVaildByDoer(ctx context.Context, doer *user_model. return nil, err } - (repo_model.RepositoryList)(repos).LoadOwners(ctx) + if err := repo_model.RepositoryList(repos).LoadOwners(ctx); err != nil { + return nil, err + } + issueList := issues[:0] for _, issue := range issues { if canRetrievedByDoer, err := issue.canRetrievedByDoer(ctx, doer); err != nil { From 396f11ed50fe0c17c1d951581719c74b02358617 Mon Sep 17 00:00:00 2001 From: yp05327 <576951401@qq.com> Date: Wed, 15 Mar 2023 00:46:58 +0000 Subject: [PATCH 50/69] fix conflicts --- models/repo/repo_list.go | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/models/repo/repo_list.go b/models/repo/repo_list.go index 6c5446a464ce..e05cb504ff6b 100644 --- a/models/repo/repo_list.go +++ b/models/repo/repo_list.go @@ -90,7 +90,8 @@ func (repos RepositoryList) LoadOwners(ctx context.Context) error { return nil } -func (repos RepositoryList) loadAttributes(ctx context.Context) error { +// LoadAttributes loads the attributes for the given RepositoryList +func (repos RepositoryList) LoadAttributes(ctx context.Context) error { if len(repos) == 0 { return nil } @@ -128,11 +129,6 @@ func (repos RepositoryList) loadAttributes(ctx context.Context) error { return nil } -// LoadAttributes loads the attributes for the given RepositoryList -func (repos RepositoryList) LoadAttributes() error { - return repos.loadAttributes(db.DefaultContext) -} - // SearchRepoOptions holds the search options type SearchRepoOptions struct { db.ListOptions From cad44d93a384d4fa9fc9f6c4cc8a932722d84afc Mon Sep 17 00:00:00 2001 From: yp05327 <576951401@qq.com> Date: Fri, 17 Mar 2023 05:38:25 +0000 Subject: [PATCH 51/69] update --- models/issues/issue.go | 2 +- models/issues/issue_project.go | 6 ++-- models/project/project.go | 13 ++++---- routers/web/repo/issue.go | 59 ++++++++++++++-------------------- 4 files changed, 35 insertions(+), 45 deletions(-) diff --git a/models/issues/issue.go b/models/issues/issue.go index 51a46f2e26dd..f6cd2995134b 100644 --- a/models/issues/issue.go +++ b/models/issues/issue.go @@ -478,7 +478,7 @@ func (issue *Issue) getLabels(ctx context.Context) (err error) { return nil } -func (issues IssueList) FliterVaildByDoer(ctx context.Context, doer *user_model.User) (IssueList, error) { +func (issues IssueList) FilterVaildByDoer(ctx context.Context, doer *user_model.User) (IssueList, error) { repos, err := issues.LoadRepositories(ctx) if err != nil { return nil, err diff --git a/models/issues/issue_project.go b/models/issues/issue_project.go index 17f1d2831a54..ce3268fbe5df 100644 --- a/models/issues/issue_project.go +++ b/models/issues/issue_project.go @@ -70,7 +70,7 @@ func LoadIssuesFromBoard(ctx context.Context, b *project_model.Board, doer *user if err != nil { return nil, err } - issues, err = IssueList(issues).FliterVaildByDoer(ctx, doer) + issues, err = IssueList(issues).FilterVaildByDoer(ctx, doer) if err != nil { return nil, err } @@ -87,7 +87,7 @@ func LoadIssuesFromBoard(ctx context.Context, b *project_model.Board, doer *user if err != nil { return nil, err } - issues, err = IssueList(issues).FliterVaildByDoer(ctx, doer) + issues, err = IssueList(issues).FilterVaildByDoer(ctx, doer) if err != nil { return nil, err } @@ -115,7 +115,7 @@ func LoadIssuesFromBoardList(ctx context.Context, bs project_model.BoardList, do } // NumIssuesInProjects returns counter of all issues assigned to a project list which doer can access -func NumIssuesInProjects(ctx context.Context, pl project_model.List, doer *user_model.User, isClosed util.OptionalBool) (map[int64]int, error) { +func NumIssuesInProjects(ctx context.Context, pl project_model.ProjectList, doer *user_model.User, isClosed util.OptionalBool) (map[int64]int, error) { numMap := make(map[int64]int, len(pl)) for _, p := range pl { num, err := NumIssuesInProject(ctx, p, doer, isClosed) diff --git a/models/project/project.go b/models/project/project.go index 2f783b35e437..9312cd460d63 100644 --- a/models/project/project.go +++ b/models/project/project.go @@ -40,7 +40,7 @@ type ( Type uint8 // List is used to identify a list of projects - List []*Project + ProjectList []*Project ) const ( @@ -162,7 +162,7 @@ func (p *Project) IsOrganizationProject() bool { return p.Type == TypeOrganization } -func (pl List) GetOwnerIDs() []int64 { +func (pl ProjectList) GetOwnerIDs() []int64 { ids := make(container.Set[int64], len(pl)) for _, p := range pl { if p.OwnerID == 0 { @@ -173,7 +173,7 @@ func (pl List) GetOwnerIDs() []int64 { return ids.Values() } -func (pl List) LoadOwners(ctx context.Context) error { +func (pl ProjectList) LoadOwners(ctx context.Context) error { userIDs := pl.GetOwnerIDs() users := make(map[int64]*user_model.User, len(userIDs)) if err := db.GetEngine(ctx). @@ -190,8 +190,7 @@ func (pl List) LoadOwners(ctx context.Context) error { return nil } -// CanWriteByDoer return whether doer have write permission to the project in a repo -func (pl List) FliterWritableByDoer(ctx context.Context, repo *repo_model.Repository, doer *user_model.User) (List, error) { +func (pl ProjectList) FilterWritableByDoer(ctx context.Context, repo *repo_model.Repository, doer *user_model.User) (ProjectList, error) { if err := pl.LoadOwners(ctx); err != nil { return nil, err } @@ -338,9 +337,9 @@ func CountProjects(ctx context.Context, opts SearchOptions) (int64, error) { } // FindProjects returns a list of all projects that have been created in the repository -func FindProjects(ctx context.Context, opts SearchOptions) ([]*Project, int64, error) { +func FindProjects(ctx context.Context, opts SearchOptions) (ProjectList, int64, error) { e := db.GetEngine(ctx).Where(opts.toConds()) - projects := make([]*Project, 0, setting.UI.IssuePagingNum) + projects := make(ProjectList, 0, setting.UI.IssuePagingNum) if opts.Page > 0 { e = e.Limit(setting.UI.IssuePagingNum, (opts.Page-1)*setting.UI.IssuePagingNum) diff --git a/routers/web/repo/issue.go b/routers/web/repo/issue.go index 5752734f7f1f..a1acf764ea7f 100644 --- a/routers/web/repo/issue.go +++ b/routers/web/repo/issue.go @@ -489,8 +489,8 @@ func RetrieveRepoMilestonesAndAssignees(ctx *context.Context, repo *repo_model.R } func retrieveProjects(ctx *context.Context, repo *repo_model.Repository) { - var openProjects project_model.List - var closedProjects project_model.List + var openProjects project_model.ProjectList + var closedProjects project_model.ProjectList var err error if err := repo.LoadOwner(ctx); err != nil { @@ -511,7 +511,7 @@ func retrieveProjects(ctx *context.Context, repo *repo_model.Repository) { return } } - // individual/org's project + // individual projects openProjectsUser, _, err := project_model.FindProjects(ctx, project_model.SearchOptions{ OwnerID: repo.OwnerID, Page: -1, @@ -522,14 +522,13 @@ func retrieveProjects(ctx *context.Context, repo *repo_model.Repository) { ctx.ServerError("GetProjects", err) return } - for _, p := range openProjectsUser { - if canWriteByDoer, err := p.CanWriteByDoer(ctx, repo, ctx.Doer); err != nil { - ctx.ServerError("CanWriteByDoer", err) - return - } else if canWriteByDoer { - openProjects = append(openProjects, p) - } + openProjectsUser, err = openProjectsUser.FilterWritableByDoer(ctx, repo, ctx.Doer) + if err != nil { + ctx.ServerError("FilterWritableByDoer", err) + return } + openProjects = append(openProjects, openProjectsUser...) + // org projects openProjectsUser, _, err = project_model.FindProjects(ctx, project_model.SearchOptions{ OwnerID: repo.OwnerID, Page: -1, @@ -540,15 +539,12 @@ func retrieveProjects(ctx *context.Context, repo *repo_model.Repository) { ctx.ServerError("GetProjects", err) return } - for _, p := range openProjectsUser { - if canWriteByDoer, err := p.CanWriteByDoer(ctx, repo, ctx.Doer); err != nil { - ctx.ServerError("CanWriteByDoer", err) - return - } else if canWriteByDoer { - openProjects = append(openProjects, p) - } + openProjectsUser, err = openProjectsUser.FilterWritableByDoer(ctx, repo, ctx.Doer) + if err != nil { + ctx.ServerError("FilterWritableByDoer", err) + return } - ctx.Data["OpenProjects"] = openProjects + ctx.Data["OpenProjects"] = append(openProjects, openProjectsUser...) // retrieve this repo's projects if ctx.Repo.CanRead(unit.TypeProjects) { @@ -563,7 +559,7 @@ func retrieveProjects(ctx *context.Context, repo *repo_model.Repository) { return } } - // individual/org's project + // individual project closedProjectsUser, _, err := project_model.FindProjects(ctx, project_model.SearchOptions{ OwnerID: repo.OwnerID, Page: -1, @@ -574,14 +570,12 @@ func retrieveProjects(ctx *context.Context, repo *repo_model.Repository) { ctx.ServerError("GetProjects", err) return } - for _, p := range closedProjectsUser { - if canWriteByDoer, err := p.CanWriteByDoer(ctx, repo, ctx.Doer); err != nil { - ctx.ServerError("CanWriteByDoer", err) - return - } else if canWriteByDoer { - closedProjects = append(closedProjects, p) - } + closedProjectsUser, err = closedProjectsUser.FilterWritableByDoer(ctx, repo, ctx.Doer) + if err != nil { + ctx.ServerError("FilterWritableByDoer", err) + return } + closedProjects = append(closedProjects, closedProjectsUser...) closedProjectsUser, _, err = project_model.FindProjects(ctx, project_model.SearchOptions{ OwnerID: repo.OwnerID, Page: -1, @@ -592,15 +586,12 @@ func retrieveProjects(ctx *context.Context, repo *repo_model.Repository) { ctx.ServerError("GetProjects", err) return } - for _, p := range closedProjectsUser { - if canWriteByDoer, err := p.CanWriteByDoer(ctx, repo, ctx.Doer); err != nil { - ctx.ServerError("CanWriteByDoer", err) - return - } else if canWriteByDoer { - closedProjects = append(closedProjects, p) - } + closedProjectsUser, err = closedProjectsUser.FilterWritableByDoer(ctx, repo, ctx.Doer) + if err != nil { + ctx.ServerError("FilterWritableByDoer", err) + return } - ctx.Data["ClosedProjects"] = closedProjects + ctx.Data["ClosedProjects"] = append(closedProjects, closedProjectsUser...) } // repoReviewerSelection items to bee shown From 77a2c6786ce4d72f0ab25d0032de0ec1e3b85cb3 Mon Sep 17 00:00:00 2001 From: yp05327 <576951401@qq.com> Date: Fri, 17 Mar 2023 05:43:23 +0000 Subject: [PATCH 52/69] add nolint --- models/project/project.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/models/project/project.go b/models/project/project.go index 9312cd460d63..0b6660522b64 100644 --- a/models/project/project.go +++ b/models/project/project.go @@ -40,7 +40,7 @@ type ( Type uint8 // List is used to identify a list of projects - ProjectList []*Project + ProjectList []*Project //nolint ) const ( From c92cb2e7912bc4ff4bd33df03c7462da250bb506 Mon Sep 17 00:00:00 2001 From: yp05327 <576951401@qq.com> Date: Fri, 17 Mar 2023 06:09:37 +0000 Subject: [PATCH 53/69] improve search logic and performance --- routers/web/repo/issue.go | 86 ++++++++++++--------------------------- 1 file changed, 25 insertions(+), 61 deletions(-) diff --git a/routers/web/repo/issue.go b/routers/web/repo/issue.go index a1acf764ea7f..9d5e8074ebdf 100644 --- a/routers/web/repo/issue.go +++ b/routers/web/repo/issue.go @@ -498,43 +498,41 @@ func retrieveProjects(ctx *context.Context, repo *repo_model.Repository) { return } + searchOptions := project_model.SearchOptions{ + RepoID: repo.ID, + Page: -1, + IsClosed: util.OptionalBoolFalse, + Type: project_model.TypeRepository, + } + // retrieve this repo's projects if ctx.Repo.CanRead(unit.TypeProjects) { - openProjects, _, err = project_model.FindProjects(ctx, project_model.SearchOptions{ - RepoID: repo.ID, - Page: -1, - IsClosed: util.OptionalBoolFalse, - Type: project_model.TypeRepository, - }) + openProjects, _, err = project_model.FindProjects(ctx, searchOptions) + if err != nil { + ctx.ServerError("GetProjects", err) + return + } + + searchOptions.IsClosed = util.OptionalBoolTrue + closedProjects, _, err = project_model.FindProjects(ctx, searchOptions) if err != nil { ctx.ServerError("GetProjects", err) return } } - // individual projects - openProjectsUser, _, err := project_model.FindProjects(ctx, project_model.SearchOptions{ + + searchOptions = project_model.SearchOptions{ OwnerID: repo.OwnerID, Page: -1, IsClosed: util.OptionalBoolFalse, - Type: project_model.TypeIndividual, - }) - if err != nil { - ctx.ServerError("GetProjects", err) - return } - openProjectsUser, err = openProjectsUser.FilterWritableByDoer(ctx, repo, ctx.Doer) - if err != nil { - ctx.ServerError("FilterWritableByDoer", err) - return + if repo.Owner.IsOrganization() { + searchOptions.Type = project_model.TypeOrganization + } else { + searchOptions.Type = project_model.TypeIndividual } - openProjects = append(openProjects, openProjectsUser...) - // org projects - openProjectsUser, _, err = project_model.FindProjects(ctx, project_model.SearchOptions{ - OwnerID: repo.OwnerID, - Page: -1, - IsClosed: util.OptionalBoolFalse, - Type: project_model.TypeOrganization, - }) + // individual/org projects + openProjectsUser, _, err := project_model.FindProjects(ctx, searchOptions) if err != nil { ctx.ServerError("GetProjects", err) return @@ -546,42 +544,8 @@ func retrieveProjects(ctx *context.Context, repo *repo_model.Repository) { } ctx.Data["OpenProjects"] = append(openProjects, openProjectsUser...) - // retrieve this repo's projects - if ctx.Repo.CanRead(unit.TypeProjects) { - closedProjects, _, err = project_model.FindProjects(ctx, project_model.SearchOptions{ - RepoID: repo.ID, - Page: -1, - IsClosed: util.OptionalBoolTrue, - Type: project_model.TypeRepository, - }) - if err != nil { - ctx.ServerError("GetProjects", err) - return - } - } - // individual project - closedProjectsUser, _, err := project_model.FindProjects(ctx, project_model.SearchOptions{ - OwnerID: repo.OwnerID, - Page: -1, - IsClosed: util.OptionalBoolTrue, - Type: project_model.TypeIndividual, - }) - if err != nil { - ctx.ServerError("GetProjects", err) - return - } - closedProjectsUser, err = closedProjectsUser.FilterWritableByDoer(ctx, repo, ctx.Doer) - if err != nil { - ctx.ServerError("FilterWritableByDoer", err) - return - } - closedProjects = append(closedProjects, closedProjectsUser...) - closedProjectsUser, _, err = project_model.FindProjects(ctx, project_model.SearchOptions{ - OwnerID: repo.OwnerID, - Page: -1, - IsClosed: util.OptionalBoolTrue, - Type: project_model.TypeOrganization, - }) + searchOptions.IsClosed = util.OptionalBoolTrue + closedProjectsUser, _, err := project_model.FindProjects(ctx, searchOptions) if err != nil { ctx.ServerError("GetProjects", err) return From 1a6da573ff83d3d4bfac1d64578d82d7d220993e Mon Sep 17 00:00:00 2001 From: yp05327 <576951401@qq.com> Date: Fri, 14 Apr 2023 01:46:42 +0000 Subject: [PATCH 54/69] fix --- models/issues/issue_project.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/models/issues/issue_project.go b/models/issues/issue_project.go index ce3268fbe5df..b14114000721 100644 --- a/models/issues/issue_project.go +++ b/models/issues/issue_project.go @@ -131,7 +131,7 @@ func NumIssuesInProjects(ctx context.Context, pl project_model.ProjectList, doer // NumIssuesInProject returns counter of all issues assigned to a project which doer can access func NumIssuesInProject(ctx context.Context, p *project_model.Project, doer *user_model.User, isClosed util.OptionalBool) (int, error) { numIssuesInProject := int(0) - bs, err := project_model.GetBoards(ctx, p.ID) + bs, err := p.GetBoards(ctx) if err != nil { return 0, err } From 5dc2aa010b421814c579d40f1686871f27343589 Mon Sep 17 00:00:00 2001 From: yp05327 <576951401@qq.com> Date: Wed, 26 Apr 2023 06:37:56 +0000 Subject: [PATCH 55/69] fix typo --- models/issues/issue.go | 2 +- models/issues/issue_project.go | 4 ++-- routers/web/repo/issue.go | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/models/issues/issue.go b/models/issues/issue.go index 0ceab20a50fd..bb2f648117a3 100644 --- a/models/issues/issue.go +++ b/models/issues/issue.go @@ -480,7 +480,7 @@ func (issue *Issue) getLabels(ctx context.Context) (err error) { return nil } -func (issues IssueList) FilterVaildByDoer(ctx context.Context, doer *user_model.User) (IssueList, error) { +func (issues IssueList) FilterValidByDoer(ctx context.Context, doer *user_model.User) (IssueList, error) { repos, err := issues.LoadRepositories(ctx) if err != nil { return nil, err diff --git a/models/issues/issue_project.go b/models/issues/issue_project.go index b14114000721..47c99849d6d0 100644 --- a/models/issues/issue_project.go +++ b/models/issues/issue_project.go @@ -70,7 +70,7 @@ func LoadIssuesFromBoard(ctx context.Context, b *project_model.Board, doer *user if err != nil { return nil, err } - issues, err = IssueList(issues).FilterVaildByDoer(ctx, doer) + issues, err = IssueList(issues).FilterValidByDoer(ctx, doer) if err != nil { return nil, err } @@ -87,7 +87,7 @@ func LoadIssuesFromBoard(ctx context.Context, b *project_model.Board, doer *user if err != nil { return nil, err } - issues, err = IssueList(issues).FilterVaildByDoer(ctx, doer) + issues, err = IssueList(issues).FilterValidByDoer(ctx, doer) if err != nil { return nil, err } diff --git a/routers/web/repo/issue.go b/routers/web/repo/issue.go index e5ba7452f573..4f78c298af7c 100644 --- a/routers/web/repo/issue.go +++ b/routers/web/repo/issue.go @@ -980,7 +980,7 @@ func NewIssueChooseTemplate(ctx *context.Context) { ctx.Data["IssueConfigError"] = err // ctx.Flash.Err makes problems here ctx.Data["milestone"] = ctx.FormInt64("milestone") - // TODO: invaild projectid check? + // TODO: invalid projectid check? ctx.Data["project"] = ctx.FormInt64("project") ctx.HTML(http.StatusOK, tplIssueChoose) @@ -2474,7 +2474,7 @@ func SearchIssues(ctx *context.Context) { includedMilestones = strings.Split(milestones, ",") } - // TODO:invaild projectid check? + // TODO:invalid projectid check? projectID := ctx.FormInt64("project") // this api is also used in UI, @@ -2640,7 +2640,7 @@ func ListIssues(ctx *context.Context) { } } - // TODO: invaild projectid check? + // TODO: invalid projectid check? projectID := ctx.FormInt64("project") listOptions := db.ListOptions{ From d76148790c573d89d4dea555b1599067beb5f929 Mon Sep 17 00:00:00 2001 From: yp05327 <576951401@qq.com> Date: Wed, 26 Apr 2023 06:42:30 +0000 Subject: [PATCH 56/69] rename --- models/issues/issue.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/models/issues/issue.go b/models/issues/issue.go index bb2f648117a3..f6b82c85e4a8 100644 --- a/models/issues/issue.go +++ b/models/issues/issue.go @@ -492,17 +492,17 @@ func (issues IssueList) FilterValidByDoer(ctx context.Context, doer *user_model. issueList := issues[:0] for _, issue := range issues { - if canRetrievedByDoer, err := issue.canRetrievedByDoer(ctx, doer); err != nil { + if isIssueVisibleToDoer, err := issue.isIssueVisibleToDoer(ctx, doer); err != nil { return nil, err - } else if canRetrievedByDoer { + } else if isIssueVisibleToDoer { issueList = append(issueList, issue) } } return issueList, nil } -// canRetrievedByDoer returns whether doer can retrieve the issue -func (issue *Issue) canRetrievedByDoer(ctx context.Context, doer *user_model.User) (bool, error) { +// isIssueVisibleToDoer returns whether doer can view the issue +func (issue *Issue) isIssueVisibleToDoer(ctx context.Context, doer *user_model.User) (bool, error) { if perm, err := access_model.GetUserRepoPermission(ctx, issue.Repo, doer); err != nil { return false, err } else if !perm.HasAccess() { From 0d6757dedc56d805c5ce68379b17658753f3d658 Mon Sep 17 00:00:00 2001 From: yp05327 <576951401@qq.com> Date: Mon, 15 May 2023 01:37:02 +0000 Subject: [PATCH 57/69] remove archived repo check --- models/issues/issue.go | 3 --- 1 file changed, 3 deletions(-) diff --git a/models/issues/issue.go b/models/issues/issue.go index b9604c630680..30cb37a65174 100644 --- a/models/issues/issue.go +++ b/models/issues/issue.go @@ -516,9 +516,6 @@ func (issue *Issue) isIssueVisibleToDoer(ctx context.Context, doer *user_model.U return false, nil } // TODO: what about Mirror repo - if issue.Repo.IsArchived { - return false, nil - } if issue.Repo.Owner.IsOrganization() && issue.Repo.IsPrivate { collaboration, err := repo_model.GetCollaboration(ctx, issue.Repo.ID, doer.ID) From 8f3d51df1154bf13839027e3bfeda8d37fda43b1 Mon Sep 17 00:00:00 2001 From: yp05327 <576951401@qq.com> Date: Thu, 18 May 2023 05:09:39 +0000 Subject: [PATCH 58/69] move UnitGlobalDisabled from isIssueVisibleToDoer to LoadIssuesFromBoard --- models/issues/issue.go | 3 --- models/issues/issue_project.go | 6 +++++- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/models/issues/issue.go b/models/issues/issue.go index 4f8700820d20..6fc631db3d1a 100644 --- a/models/issues/issue.go +++ b/models/issues/issue.go @@ -509,9 +509,6 @@ func (issue *Issue) isIssueVisibleToDoer(ctx context.Context, doer *user_model.U return false, nil } - if unit.TypeIssues.UnitGlobalDisabled() { - return false, nil - } if !issue.Repo.UnitEnabled(ctx, unit.TypeIssues) { return false, nil } diff --git a/models/issues/issue_project.go b/models/issues/issue_project.go index 47c99849d6d0..7ac3cc26d5c8 100644 --- a/models/issues/issue_project.go +++ b/models/issues/issue_project.go @@ -9,6 +9,7 @@ import ( "code.gitea.io/gitea/models/db" project_model "code.gitea.io/gitea/models/project" + "code.gitea.io/gitea/models/unit" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/util" ) @@ -58,8 +59,11 @@ func (issue *Issue) projectBoardID(ctx context.Context) int64 { // LoadIssuesFromBoard load issues assigned to this board func LoadIssuesFromBoard(ctx context.Context, b *project_model.Board, doer *user_model.User, isClosed util.OptionalBool) (IssueList, error) { - var issueList IssueList + if unit.TypeIssues.UnitGlobalDisabled() { + return nil, nil + } + var issueList IssueList if b.ID != 0 { issues, err := Issues(ctx, &IssuesOptions{ ProjectBoardID: b.ID, From 77ac2bc88cfe139e9c76ecfc7d720ec6f52adaae Mon Sep 17 00:00:00 2001 From: yp05327 <576951401@qq.com> Date: Thu, 25 May 2023 10:04:36 +0000 Subject: [PATCH 59/69] fix --- models/issues/issue.go | 57 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/models/issues/issue.go b/models/issues/issue.go index fc046d273c98..83c15afca204 100644 --- a/models/issues/issue.go +++ b/models/issues/issue.go @@ -10,8 +10,12 @@ import ( "regexp" "code.gitea.io/gitea/models/db" + "code.gitea.io/gitea/models/organization" + "code.gitea.io/gitea/models/perm" + access_model "code.gitea.io/gitea/models/perm/access" project_model "code.gitea.io/gitea/models/project" repo_model "code.gitea.io/gitea/models/repo" + "code.gitea.io/gitea/models/unit" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/log" api "code.gitea.io/gitea/modules/structs" @@ -446,6 +450,59 @@ func (issue *Issue) IsPoster(uid int64) bool { return issue.OriginalAuthorID == 0 && issue.PosterID == uid } +func (issues IssueList) FilterValidByDoer(ctx context.Context, doer *user_model.User) (IssueList, error) { + repos, err := issues.LoadRepositories(ctx) + if err != nil { + return nil, err + } + + if err := repo_model.RepositoryList(repos).LoadOwners(ctx); err != nil { + return nil, err + } + + issueList := issues[:0] + for _, issue := range issues { + if isIssueVisibleToDoer, err := issue.isIssueVisibleToDoer(ctx, doer); err != nil { + return nil, err + } else if isIssueVisibleToDoer { + issueList = append(issueList, issue) + } + } + return issueList, nil +} + +// isIssueVisibleToDoer returns whether doer can view the issue +func (issue *Issue) isIssueVisibleToDoer(ctx context.Context, doer *user_model.User) (bool, error) { + if perm, err := access_model.GetUserRepoPermission(ctx, issue.Repo, doer); err != nil { + return false, err + } else if !perm.HasAccess() { + return false, nil + } + + if !issue.Repo.UnitEnabled(ctx, unit.TypeIssues) { + return false, nil + } + // TODO: what about Mirror repo + + if issue.Repo.Owner.IsOrganization() && issue.Repo.IsPrivate { + collaboration, err := repo_model.GetCollaboration(ctx, issue.Repo.ID, doer.ID) + if err != nil { + return false, fmt.Errorf("GetCollaboration: %w", err) + } + if collaboration == nil { + if (*organization.Organization)(issue.Repo.Owner).UnitPermission(ctx, doer, unit.TypeIssues) < perm.AccessModeRead { + return false, nil + } + } else { + if collaboration.Mode < perm.AccessModeRead { + return false, nil + } + } + } + + return true, nil +} + // GetTasks returns the amount of tasks in the issues content func (issue *Issue) GetTasks() int { return len(issueTasksPat.FindAllStringIndex(issue.Content, -1)) From cdb454ae49604e12e91d72cc631fec1d939467d3 Mon Sep 17 00:00:00 2001 From: yp05327 <576951401@qq.com> Date: Tue, 6 Jun 2023 08:25:24 +0000 Subject: [PATCH 60/69] only check repo unit permission --- models/issues/issue.go | 46 +++++++++--------------------------------- 1 file changed, 9 insertions(+), 37 deletions(-) diff --git a/models/issues/issue.go b/models/issues/issue.go index 5c2b2cba394a..a08a8f66bb61 100644 --- a/models/issues/issue.go +++ b/models/issues/issue.go @@ -10,13 +10,12 @@ import ( "regexp" "code.gitea.io/gitea/models/db" - "code.gitea.io/gitea/models/organization" - "code.gitea.io/gitea/models/perm" access_model "code.gitea.io/gitea/models/perm/access" project_model "code.gitea.io/gitea/models/project" repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/models/unit" user_model "code.gitea.io/gitea/models/user" + "code.gitea.io/gitea/modules/container" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" api "code.gitea.io/gitea/modules/structs" @@ -462,49 +461,22 @@ func (issues IssueList) FilterValidByDoer(ctx context.Context, doer *user_model. return nil, err } + validRepoIDs := make(container.Set[int64], len(repos)) + for _, repo := range repos { + if access_model.CheckRepoUnitUser(ctx, repo, doer, unit.TypeIssues) { + validRepoIDs.Add(repo.ID) + } + } + issueList := issues[:0] for _, issue := range issues { - if isIssueVisibleToDoer, err := issue.isIssueVisibleToDoer(ctx, doer); err != nil { - return nil, err - } else if isIssueVisibleToDoer { + if validRepoIDs.Contains(issue.Repo.ID) { issueList = append(issueList, issue) } } return issueList, nil } -// isIssueVisibleToDoer returns whether doer can view the issue -func (issue *Issue) isIssueVisibleToDoer(ctx context.Context, doer *user_model.User) (bool, error) { - if perm, err := access_model.GetUserRepoPermission(ctx, issue.Repo, doer); err != nil { - return false, err - } else if !perm.HasAccess() { - return false, nil - } - - if !issue.Repo.UnitEnabled(ctx, unit.TypeIssues) { - return false, nil - } - // TODO: what about Mirror repo - - if issue.Repo.Owner.IsOrganization() && issue.Repo.IsPrivate { - collaboration, err := repo_model.GetCollaboration(ctx, issue.Repo.ID, doer.ID) - if err != nil { - return false, fmt.Errorf("GetCollaboration: %w", err) - } - if collaboration == nil { - if (*organization.Organization)(issue.Repo.Owner).UnitPermission(ctx, doer, unit.TypeIssues) < perm.AccessModeRead { - return false, nil - } - } else { - if collaboration.Mode < perm.AccessModeRead { - return false, nil - } - } - } - - return true, nil -} - // GetTasks returns the amount of tasks in the issues content func (issue *Issue) GetTasks() int { return len(issueTasksPat.FindAllStringIndex(issue.Content, -1)) From 79a6d4e7d5ca560f0581c25fccef50203e08c3d4 Mon Sep 17 00:00:00 2001 From: yp05327 <576951401@qq.com> Date: Wed, 7 Jun 2023 02:32:44 +0000 Subject: [PATCH 61/69] improve FilterWritableByDoer --- models/issues/issue.go | 4 - models/perm/access/repo_permission.go | 14 +++ models/project/project.go | 158 +++++++++++++++----------- 3 files changed, 108 insertions(+), 68 deletions(-) diff --git a/models/issues/issue.go b/models/issues/issue.go index a08a8f66bb61..d9cb08967819 100644 --- a/models/issues/issue.go +++ b/models/issues/issue.go @@ -457,10 +457,6 @@ func (issues IssueList) FilterValidByDoer(ctx context.Context, doer *user_model. return nil, err } - if err := repo_model.RepositoryList(repos).LoadOwners(ctx); err != nil { - return nil, err - } - validRepoIDs := make(container.Set[int64], len(repos)) for _, repo := range repos { if access_model.CheckRepoUnitUser(ctx, repo, doer, unit.TypeIssues) { diff --git a/models/perm/access/repo_permission.go b/models/perm/access/repo_permission.go index 64df5355bba6..92465405309a 100644 --- a/models/perm/access/repo_permission.go +++ b/models/perm/access/repo_permission.go @@ -422,3 +422,17 @@ func CheckRepoUnitUser(ctx context.Context, repo *repo_model.Repository, user *u return perm.CanRead(unitType) } + +// CheckRepoUnitWriteUser check whether user could write the unit of this repository +func CheckRepoUnitWriteUser(ctx context.Context, repo *repo_model.Repository, user *user_model.User, unitType unit.Type) bool { + if user != nil && user.IsAdmin { + return true + } + perm, err := GetUserRepoPermission(ctx, repo, user) + if err != nil { + log.Error("GetUserRepoPermission: %w", err) + return false + } + + return perm.CanWrite(unitType) +} diff --git a/models/project/project.go b/models/project/project.go index 7f982a4bacb2..afd85dc3593a 100644 --- a/models/project/project.go +++ b/models/project/project.go @@ -169,7 +169,7 @@ func (p *Project) IsOrganizationProject() bool { return p.Type == TypeOrganization } -func (pl ProjectList) GetOwnerIDs() []int64 { +func (pl ProjectList) getOwnerIDs() []int64 { ids := make(container.Set[int64], len(pl)) for _, p := range pl { if p.OwnerID == 0 { @@ -180,99 +180,129 @@ func (pl ProjectList) GetOwnerIDs() []int64 { return ids.Values() } -func (pl ProjectList) LoadOwners(ctx context.Context) error { - userIDs := pl.GetOwnerIDs() - users := make(map[int64]*user_model.User, len(userIDs)) +func (pl ProjectList) getRepoIDs() []int64 { + ids := make(container.Set[int64], len(pl)) + for _, p := range pl { + if p.RepoID == 0 { + continue + } + ids.Add(p.RepoID) + } + return ids.Values() +} + +func (pl ProjectList) LoadOwners(ctx context.Context) ([]*user_model.User, error) { + if len(pl) == 0 { + return nil, nil + } + + userIDs := pl.getOwnerIDs() + usersMap := make(map[int64]*user_model.User, len(userIDs)) if err := db.GetEngine(ctx). Where("id > 0"). In("id", userIDs). - Find(&users); err != nil { - return fmt.Errorf("find users: %w", err) + Find(&usersMap); err != nil { + return nil, fmt.Errorf("find users: %w", err) } for _, p := range pl { if p.OwnerID > 0 && p.Owner == nil { - p.Owner = users[p.OwnerID] + p.Owner = usersMap[p.OwnerID] } } - return nil + users := make([]*user_model.User, 0, len(usersMap)) + for _, u := range usersMap { + if u != nil { + users = append(users, u) + } + } + return users, nil } -func (pl ProjectList) FilterWritableByDoer(ctx context.Context, repo *repo_model.Repository, doer *user_model.User) (ProjectList, error) { - if err := pl.LoadOwners(ctx); err != nil { - return nil, err +func (pl ProjectList) LoadRepositories(ctx context.Context) (repo_model.RepositoryList, error) { + if len(pl) == 0 { + return nil, nil } - projectList := pl[:0] + repoIDs := pl.getRepoIDs() + repos := make(map[int64]*repo_model.Repository, len(repoIDs)) + if err := db.GetEngine(ctx). + In("id", repoIDs). + Find(&repos); err != nil { + return nil, fmt.Errorf("find repository: %w", err) + } for _, p := range pl { - if canWriteByDoer, err := p.CanWriteByDoer(ctx, repo, doer); err != nil { - return nil, err - } else if canWriteByDoer { - projectList = append(projectList, p) + if p.RepoID > 0 && p.Repo == nil { + p.Repo = repos[p.RepoID] } } - return projectList, nil + return repo_model.ValuesRepository(repos), nil } -// CanWriteByDoer return whether doer have write permission to the project in a repo -func (p *Project) CanWriteByDoer(ctx context.Context, repo *repo_model.Repository, doer *user_model.User) (bool, error) { - if unit.TypeProjects.UnitGlobalDisabled() { - return false, nil - } - +func (pl ProjectList) FilterWritableByDoer(ctx context.Context, repo *repo_model.Repository, doer *user_model.User) (ProjectList, error) { // non-login user have no write permission if doer == nil { - return false, nil + return nil, nil } - if err := repo.LoadOwner(ctx); err != nil { - return false, fmt.Errorf("LoadOwner: %w", err) + // Check valid individual/organization projects + owners, err := pl.LoadOwners(ctx) + if err != nil { + return nil, err } - - if p.RepoID > 0 { - // repo projects - if p.RepoID != repo.ID { - return false, nil - } - - if err := p.LoadRepo(ctx); err != nil { - return false, fmt.Errorf("LoadRepo: %w", err) - } - if perm, err := access_model.GetUserRepoPermission(ctx, p.Repo, doer); err != nil { - return false, err - } else if !perm.HasAccess() { - return false, nil + validOwnerIDs := make(container.Set[int64], len(owners)) + for _, owner := range owners { + if CheckUserUnitWriteDoer(ctx, owner, doer, unit.TypeProjects) { + validOwnerIDs.Add(owner.ID) } + } - if !repo.UnitEnabled(ctx, unit.TypeProjects) { - return false, nil - } - } else { - // individual/org's project - if p.OwnerID != repo.OwnerID { - return false, nil + // Check valid repo projects + repos, err := pl.LoadRepositories(ctx) + if err != nil { + return nil, err + } + validRepoIDs := make(container.Set[int64], len(repos)) + for _, repo := range repos { + if access_model.CheckRepoUnitWriteUser(ctx, repo, doer, unit.TypeProjects) { + validRepoIDs.Add(repo.ID) } + } - if repo.Owner.IsIndividual() && repo.Owner.ID != doer.ID { - return false, nil + projectList := pl[:0] + for _, p := range pl { + if (p.RepoID > 0 && p.RepoID == repo.ID && validRepoIDs.Contains(p.RepoID)) || + (p.OwnerID > 0 && p.OwnerID == repo.OwnerID && validOwnerIDs.Contains(p.OwnerID)) { + projectList = append(projectList, p) } + } + return projectList, nil +} - if repo.Owner.IsOrganization() && repo.IsPrivate { - collaboration, err := repo_model.GetCollaboration(ctx, repo.ID, doer.ID) - if err != nil { - return false, fmt.Errorf("GetCollaboration: %w", err) - } - if collaboration == nil { - if (*organization.Organization)(repo.Owner).UnitPermission(ctx, doer, unit.TypeIssues) < perm.AccessModeWrite { - return false, nil - } - } else { - if collaboration.Mode < perm.AccessModeWrite { - return false, nil - } - } - } +// CheckUserUnitWriteDoer return whether doer have write permission to the individual/organization project +func CheckUserUnitWriteDoer(ctx context.Context, user, doer *user_model.User, unitType unit.Type) bool { + if user == nil { + return false + } + if user.IsIndividual() && user.ID != doer.ID { + return false } + if user.IsOrganization() && (*organization.Organization)(user).UnitPermission(ctx, doer, unitType) < perm.AccessModeWrite { + return false + } + return true +} +// CanWriteByDoer return whether doer have write permission to the project +func (p Project) CanWriteByDoer(ctx context.Context, repo *repo_model.Repository, doer *user_model.User) (bool, error) { + var err error + projectList := ProjectList{&p} + projectList, err = projectList.FilterWritableByDoer(ctx, repo, doer) + if err != nil { + return false, err + } + if len(projectList) == 0 { + return false, nil + } return true, nil } From 821fce203309939adf4f7fa4cda1f976ee9d3e26 Mon Sep 17 00:00:00 2001 From: yp05327 <576951401@qq.com> Date: Wed, 7 Jun 2023 02:34:33 +0000 Subject: [PATCH 62/69] improve UnitGlobalDisabled check --- models/issues/issue_project.go | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/models/issues/issue_project.go b/models/issues/issue_project.go index 7ac3cc26d5c8..2e8cf5320b2b 100644 --- a/models/issues/issue_project.go +++ b/models/issues/issue_project.go @@ -59,10 +59,6 @@ func (issue *Issue) projectBoardID(ctx context.Context) int64 { // LoadIssuesFromBoard load issues assigned to this board func LoadIssuesFromBoard(ctx context.Context, b *project_model.Board, doer *user_model.User, isClosed util.OptionalBool) (IssueList, error) { - if unit.TypeIssues.UnitGlobalDisabled() { - return nil, nil - } - var issueList IssueList if b.ID != 0 { issues, err := Issues(ctx, &IssuesOptions{ @@ -107,6 +103,9 @@ func LoadIssuesFromBoard(ctx context.Context, b *project_model.Board, doer *user // LoadIssuesFromBoardList load issues assigned to the boards func LoadIssuesFromBoardList(ctx context.Context, bs project_model.BoardList, doer *user_model.User, isClosed util.OptionalBool) (map[int64]IssueList, error) { + if unit.TypeIssues.UnitGlobalDisabled() { + return nil, nil + } issuesMap := make(map[int64]IssueList, len(bs)) for _, b := range bs { il, err := LoadIssuesFromBoard(ctx, b, doer, isClosed) From e22dcfa9026e05951423a43733e1384e864b5835 Mon Sep 17 00:00:00 2001 From: yp05327 <576951401@qq.com> Date: Wed, 7 Jun 2023 02:45:25 +0000 Subject: [PATCH 63/69] 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 d9cb08967819..14b4ddfcdf04 100644 --- a/models/issues/issue.go +++ b/models/issues/issue.go @@ -466,7 +466,7 @@ func (issues IssueList) FilterValidByDoer(ctx context.Context, doer *user_model. issueList := issues[:0] for _, issue := range issues { - if validRepoIDs.Contains(issue.Repo.ID) { + if validRepoIDs.Contains(issue.RepoID) { issueList = append(issueList, issue) } } From cedeb3869646f1fa6dddd5ed81e754d20625a7ab Mon Sep 17 00:00:00 2001 From: yp05327 <576951401@qq.com> Date: Mon, 10 Jul 2023 04:54:28 +0000 Subject: [PATCH 64/69] fix --- routers/web/org/projects.go | 1 + 1 file changed, 1 insertion(+) diff --git a/routers/web/org/projects.go b/routers/web/org/projects.go index 3b8fb8e635c8..74fac3936766 100644 --- a/routers/web/org/projects.go +++ b/routers/web/org/projects.go @@ -8,6 +8,7 @@ import ( "fmt" "net/http" "net/url" + "strconv" "strings" issues_model "code.gitea.io/gitea/models/issues" From 0621ed5f7518571b2e5df25d0cf118901e4be160 Mon Sep 17 00:00:00 2001 From: yp05327 <576951401@qq.com> Date: Wed, 6 Mar 2024 07:43:28 +0000 Subject: [PATCH 65/69] fix --- models/issues/issue_project.go | 10 +++++----- routers/web/org/projects.go | 6 +++--- routers/web/repo/issue.go | 31 +++++++++++++------------------ routers/web/repo/projects.go | 6 +++--- 4 files changed, 24 insertions(+), 29 deletions(-) diff --git a/models/issues/issue_project.go b/models/issues/issue_project.go index 396d008f7d02..0cb0c8a922dc 100644 --- a/models/issues/issue_project.go +++ b/models/issues/issue_project.go @@ -11,7 +11,7 @@ import ( project_model "code.gitea.io/gitea/models/project" "code.gitea.io/gitea/models/unit" user_model "code.gitea.io/gitea/models/user" - "code.gitea.io/gitea/modules/util" + "code.gitea.io/gitea/modules/optional" ) // LoadProject load the project the issue was assigned to @@ -50,7 +50,7 @@ func (issue *Issue) ProjectBoardID(ctx context.Context) int64 { } // LoadIssuesFromBoard load issues assigned to this board -func LoadIssuesFromBoard(ctx context.Context, b *project_model.Board, doer *user_model.User, isClosed util.OptionalBool) (IssueList, error) { +func LoadIssuesFromBoard(ctx context.Context, b *project_model.Board, doer *user_model.User, isClosed optional.Option[bool]) (IssueList, error) { issueList := make(IssueList, 0, 10) if b.ID > 0 { @@ -95,7 +95,7 @@ func LoadIssuesFromBoard(ctx context.Context, b *project_model.Board, doer *user } // LoadIssuesFromBoardList load issues assigned to the boards -func LoadIssuesFromBoardList(ctx context.Context, bs project_model.BoardList, doer *user_model.User, isClosed util.OptionalBool) (map[int64]IssueList, error) { +func LoadIssuesFromBoardList(ctx context.Context, bs project_model.BoardList, doer *user_model.User, isClosed optional.Option[bool]) (map[int64]IssueList, error) { if unit.TypeIssues.UnitGlobalDisabled() { return nil, nil } @@ -111,7 +111,7 @@ func LoadIssuesFromBoardList(ctx context.Context, bs project_model.BoardList, do } // NumIssuesInProjects returns counter of all issues assigned to a project list which doer can access -func NumIssuesInProjects(ctx context.Context, pl project_model.ProjectList, doer *user_model.User, isClosed util.OptionalBool) (map[int64]int, error) { +func NumIssuesInProjects(ctx context.Context, pl project_model.ProjectList, doer *user_model.User, isClosed optional.Option[bool]) (map[int64]int, error) { numMap := make(map[int64]int, len(pl)) for _, p := range pl { num, err := NumIssuesInProject(ctx, p, doer, isClosed) @@ -125,7 +125,7 @@ func NumIssuesInProjects(ctx context.Context, pl project_model.ProjectList, doer } // NumIssuesInProject returns counter of all issues assigned to a project which doer can access -func NumIssuesInProject(ctx context.Context, p *project_model.Project, doer *user_model.User, isClosed util.OptionalBool) (int, error) { +func NumIssuesInProject(ctx context.Context, p *project_model.Project, doer *user_model.User, isClosed optional.Option[bool]) (int, error) { numIssuesInProject := int(0) bs, err := p.GetBoards(ctx) if err != nil { diff --git a/routers/web/org/projects.go b/routers/web/org/projects.go index 99b42ee1d222..79c97c711aaa 100644 --- a/routers/web/org/projects.go +++ b/routers/web/org/projects.go @@ -128,12 +128,12 @@ func Projects(ctx *context.Context) { ctx.Data["PageIsViewProjects"] = true ctx.Data["SortType"] = sortType - numOpenIssues, err := issues_model.NumIssuesInProjects(ctx, projects, ctx.Doer, util.OptionalBoolFalse) + numOpenIssues, err := issues_model.NumIssuesInProjects(ctx, projects, ctx.Doer, optional.Some(false)) if err != nil { ctx.ServerError("NumIssuesInProjects", err) return } - numClosedIssues, err := issues_model.NumIssuesInProjects(ctx, projects, ctx.Doer, util.OptionalBoolTrue) + numClosedIssues, err := issues_model.NumIssuesInProjects(ctx, projects, ctx.Doer, optional.Some(true)) if err != nil { ctx.ServerError("NumIssuesInProjects", err) return @@ -370,7 +370,7 @@ func ViewProject(ctx *context.Context) { boards[0].Title = ctx.Locale.TrString("repo.projects.type.uncategorized") } - issuesMap, err := issues_model.LoadIssuesFromBoardList(ctx, boards, ctx.Doer, util.OptionalBoolNone) + issuesMap, err := issues_model.LoadIssuesFromBoardList(ctx, boards, ctx.Doer, optional.None[bool]()) if err != nil { ctx.ServerError("LoadIssuesOfBoards", err) return diff --git a/routers/web/repo/issue.go b/routers/web/repo/issue.go index b79f388c6ddf..818e0379191b 100644 --- a/routers/web/repo/issue.go +++ b/routers/web/repo/issue.go @@ -591,8 +591,8 @@ func retrieveProjects(ctx *context.Context, repo *repo_model.Repository) { projectsUnit := repo.MustGetUnit(ctx, unit.TypeProjects) - var openProjects []*project_model.Project - var closedProjects []*project_model.Project + var openProjects project_model.ProjectList + var closedProjects project_model.ProjectList var err error if projectsUnit.ProjectsConfig().IsProjectsAllowed(repo_model.ProjectsModeRepo) { @@ -629,11 +629,11 @@ func retrieveProjects(ctx *context.Context, repo *repo_model.Repository) { ctx.ServerError("GetProjects", err) return } - openProjects2, err = openProjects2.FilterWritableByDoer(ctx, repo, ctx.Doer) - if err != nil { - ctx.ServerError("FilterWritableByDoer", err) - return - } + openProjects2, err = project_model.ProjectList(openProjects2).FilterWritableByDoer(ctx, repo, ctx.Doer) + if err != nil { + ctx.ServerError("FilterWritableByDoer", err) + return + } openProjects = append(openProjects, openProjects2...) closedProjects2, err := db.Find[project_model.Project](ctx, project_model.SearchOptions{ ListOptions: db.ListOptionsAll, @@ -645,11 +645,11 @@ func retrieveProjects(ctx *context.Context, repo *repo_model.Repository) { ctx.ServerError("GetProjects", err) return } - closedProjects2, err = closedProjects2.FilterWritableByDoer(ctx, repo, ctx.Doer) - if err != nil { - ctx.ServerError("FilterWritableByDoer", err) - return - } + closedProjects2, err = project_model.ProjectList(closedProjects2).FilterWritableByDoer(ctx, repo, ctx.Doer) + if err != nil { + ctx.ServerError("FilterWritableByDoer", err) + return + } closedProjects = append(closedProjects, closedProjects2...) } @@ -1195,7 +1195,7 @@ func ValidateRepoMetas(ctx *context.Context, form forms.CreateIssueForm, isPull ctx.Data["project_id"] = form.ProjectID projectID = form.ProjectID - projectLink = p.Link() + projectLink = p.Link(ctx) } } } @@ -2834,11 +2834,6 @@ func ListIssues(ctx *context.Context) { var projectID *int64 if v := ctx.FormInt64("project"); v > 0 { projectID = &v - } - - listOptions := db.ListOptions{ - Page: ctx.FormInt("page"), - PageSize: convert.ToCorrectPageSize(ctx.FormInt("limit")), } isPull := optional.None[bool]() diff --git a/routers/web/repo/projects.go b/routers/web/repo/projects.go index 7f4750b745ec..8c115307cee5 100644 --- a/routers/web/repo/projects.go +++ b/routers/web/repo/projects.go @@ -126,12 +126,12 @@ func Projects(ctx *context.Context) { ctx.Data["IsProjectsPage"] = true ctx.Data["SortType"] = sortType - numOpenIssues, err := issues_model.NumIssuesInProjects(ctx, projects, ctx.Doer, util.OptionalBoolFalse) + numOpenIssues, err := issues_model.NumIssuesInProjects(ctx, projects, ctx.Doer, optional.Some(false)) if err != nil { ctx.ServerError("NumIssuesInProjects", err) return } - numClosedIssues, err := issues_model.NumIssuesInProjects(ctx, projects, ctx.Doer, util.OptionalBoolTrue) + numClosedIssues, err := issues_model.NumIssuesInProjects(ctx, projects, ctx.Doer, optional.Some(true)) if err != nil { ctx.ServerError("NumIssuesInProjects", err) return @@ -332,7 +332,7 @@ func ViewProject(ctx *context.Context) { boards[0].Title = ctx.Locale.TrString("repo.projects.type.uncategorized") } - issuesMap, err := issues_model.LoadIssuesFromBoardList(ctx, boards, ctx.Doer, util.OptionalBoolNone) + issuesMap, err := issues_model.LoadIssuesFromBoardList(ctx, boards, ctx.Doer, optional.None[bool]()) if err != nil { ctx.ServerError("LoadIssuesOfBoards", err) return From a48cdbad845e9b4f10230814893fe14da1c4efab Mon Sep 17 00:00:00 2001 From: yp05327 <576951401@qq.com> Date: Wed, 6 Mar 2024 07:49:33 +0000 Subject: [PATCH 66/69] fix lint --- models/issues/issue_project.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/models/issues/issue_project.go b/models/issues/issue_project.go index 0cb0c8a922dc..ef26d3e2b1c7 100644 --- a/models/issues/issue_project.go +++ b/models/issues/issue_project.go @@ -63,7 +63,7 @@ func LoadIssuesFromBoard(ctx context.Context, b *project_model.Board, doer *user if err != nil { return nil, err } - issues, err = IssueList(issues).FilterValidByDoer(ctx, doer) + issues, err = issues.FilterValidByDoer(ctx, doer) if err != nil { return nil, err } @@ -80,7 +80,7 @@ func LoadIssuesFromBoard(ctx context.Context, b *project_model.Board, doer *user if err != nil { return nil, err } - issues, err = IssueList(issues).FilterValidByDoer(ctx, doer) + issues, err = issues.FilterValidByDoer(ctx, doer) if err != nil { return nil, err } From b2294673e83530809b958eadaa10ade4b2916bf5 Mon Sep 17 00:00:00 2001 From: yp05327 <576951401@qq.com> Date: Wed, 6 Mar 2024 08:42:46 +0000 Subject: [PATCH 67/69] remove issueList.FilterValidByDoer --- models/issues/issue.go | 24 ---------------------- models/issues/issue_project.go | 37 ++++++++++++++++------------------ 2 files changed, 17 insertions(+), 44 deletions(-) diff --git a/models/issues/issue.go b/models/issues/issue.go index d1b22107b044..563a780dcb13 100644 --- a/models/issues/issue.go +++ b/models/issues/issue.go @@ -12,10 +12,8 @@ import ( "slices" "code.gitea.io/gitea/models/db" - access_model "code.gitea.io/gitea/models/perm/access" project_model "code.gitea.io/gitea/models/project" repo_model "code.gitea.io/gitea/models/repo" - "code.gitea.io/gitea/models/unit" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/container" "code.gitea.io/gitea/modules/log" @@ -444,28 +442,6 @@ func (issue *Issue) IsPoster(uid int64) bool { return issue.OriginalAuthorID == 0 && issue.PosterID == uid } -func (issues IssueList) FilterValidByDoer(ctx context.Context, doer *user_model.User) (IssueList, error) { - repos, err := issues.LoadRepositories(ctx) - if err != nil { - return nil, err - } - - validRepoIDs := make(container.Set[int64], len(repos)) - for _, repo := range repos { - if access_model.CheckRepoUnitUser(ctx, repo, doer, unit.TypeIssues) { - validRepoIDs.Add(repo.ID) - } - } - - issueList := issues[:0] - for _, issue := range issues { - if validRepoIDs.Contains(issue.RepoID) { - issueList = append(issueList, issue) - } - } - return issueList, nil -} - // GetTasks returns the amount of tasks in the issues content func (issue *Issue) GetTasks() int { return len(issueTasksPat.FindAllStringIndex(issue.Content, -1)) diff --git a/models/issues/issue_project.go b/models/issues/issue_project.go index ef26d3e2b1c7..c9dc2c87f531 100644 --- a/models/issues/issue_project.go +++ b/models/issues/issue_project.go @@ -53,17 +53,22 @@ func (issue *Issue) ProjectBoardID(ctx context.Context) int64 { func LoadIssuesFromBoard(ctx context.Context, b *project_model.Board, doer *user_model.User, isClosed optional.Option[bool]) (IssueList, error) { issueList := make(IssueList, 0, 10) + opts := &IssuesOptions{ + ProjectID: b.ProjectID, + SortType: "project-column-sorting", + IsClosed: isClosed, + } + + if doer != nil { + opts.User = doer + } else { + // non-login user can only access public repos + opts.AllPublic = true + } + if b.ID > 0 { - issues, err := Issues(ctx, &IssuesOptions{ - ProjectBoardID: b.ID, - ProjectID: b.ProjectID, - SortType: "project-column-sorting", - IsClosed: isClosed, - }) - if err != nil { - return nil, err - } - issues, err = issues.FilterValidByDoer(ctx, doer) + opts.ProjectBoardID = b.ID + issues, err := Issues(ctx, opts) if err != nil { return nil, err } @@ -71,16 +76,8 @@ func LoadIssuesFromBoard(ctx context.Context, b *project_model.Board, doer *user } if b.Default { - issues, err := Issues(ctx, &IssuesOptions{ - ProjectBoardID: db.NoConditionID, - ProjectID: b.ProjectID, - SortType: "project-column-sorting", - IsClosed: isClosed, - }) - if err != nil { - return nil, err - } - issues, err = issues.FilterValidByDoer(ctx, doer) + opts.ProjectBoardID = db.NoConditionID + issues, err := Issues(ctx, opts) if err != nil { return nil, err } From 004f4d242d7662220e76449e13777fa236264a95 Mon Sep 17 00:00:00 2001 From: yp05327 <576951401@qq.com> Date: Thu, 7 Mar 2024 00:34:26 +0000 Subject: [PATCH 68/69] improve(bug public individual project cannot display the issues in public indifidual repo) --- models/issues/issue_project.go | 36 ++++++++++++++++------------------ routers/web/org/projects.go | 6 +++--- routers/web/repo/projects.go | 6 +++--- 3 files changed, 23 insertions(+), 25 deletions(-) diff --git a/models/issues/issue_project.go b/models/issues/issue_project.go index c9dc2c87f531..891e23210827 100644 --- a/models/issues/issue_project.go +++ b/models/issues/issue_project.go @@ -8,6 +8,7 @@ import ( "fmt" "code.gitea.io/gitea/models/db" + "code.gitea.io/gitea/models/organization" project_model "code.gitea.io/gitea/models/project" "code.gitea.io/gitea/models/unit" user_model "code.gitea.io/gitea/models/user" @@ -50,7 +51,7 @@ func (issue *Issue) ProjectBoardID(ctx context.Context) int64 { } // LoadIssuesFromBoard load issues assigned to this board -func LoadIssuesFromBoard(ctx context.Context, b *project_model.Board, doer *user_model.User, isClosed optional.Option[bool]) (IssueList, error) { +func LoadIssuesFromBoard(ctx context.Context, b *project_model.Board, doer *user_model.User, org *organization.Organization, isClosed optional.Option[bool]) (IssueList, error) { issueList := make(IssueList, 0, 10) opts := &IssuesOptions{ @@ -61,6 +62,7 @@ func LoadIssuesFromBoard(ctx context.Context, b *project_model.Board, doer *user if doer != nil { opts.User = doer + opts.Org = org } else { // non-login user can only access public repos opts.AllPublic = true @@ -68,21 +70,17 @@ func LoadIssuesFromBoard(ctx context.Context, b *project_model.Board, doer *user if b.ID > 0 { opts.ProjectBoardID = b.ID - issues, err := Issues(ctx, opts) - if err != nil { - return nil, err - } - issueList = append(issueList, issues...) + } else if b.Default { + opts.ProjectBoardID = db.NoConditionID + } else { + return issueList, nil } - if b.Default { - opts.ProjectBoardID = db.NoConditionID - issues, err := Issues(ctx, opts) - if err != nil { - return nil, err - } - issueList = append(issueList, issues...) + issues, err := Issues(ctx, opts) + if err != nil { + return nil, err } + issueList = append(issueList, issues...) if err := issueList.LoadComments(ctx); err != nil { return nil, err @@ -92,13 +90,13 @@ func LoadIssuesFromBoard(ctx context.Context, b *project_model.Board, doer *user } // LoadIssuesFromBoardList load issues assigned to the boards -func LoadIssuesFromBoardList(ctx context.Context, bs project_model.BoardList, doer *user_model.User, isClosed optional.Option[bool]) (map[int64]IssueList, error) { +func LoadIssuesFromBoardList(ctx context.Context, bs project_model.BoardList, doer *user_model.User, org *organization.Organization, isClosed optional.Option[bool]) (map[int64]IssueList, error) { if unit.TypeIssues.UnitGlobalDisabled() { return nil, nil } issuesMap := make(map[int64]IssueList, len(bs)) for _, b := range bs { - il, err := LoadIssuesFromBoard(ctx, b, doer, isClosed) + il, err := LoadIssuesFromBoard(ctx, b, doer, org, isClosed) if err != nil { return nil, err } @@ -108,10 +106,10 @@ func LoadIssuesFromBoardList(ctx context.Context, bs project_model.BoardList, do } // NumIssuesInProjects returns counter of all issues assigned to a project list which doer can access -func NumIssuesInProjects(ctx context.Context, pl project_model.ProjectList, doer *user_model.User, isClosed optional.Option[bool]) (map[int64]int, error) { +func NumIssuesInProjects(ctx context.Context, pl project_model.ProjectList, doer *user_model.User, org *organization.Organization, isClosed optional.Option[bool]) (map[int64]int, error) { numMap := make(map[int64]int, len(pl)) for _, p := range pl { - num, err := NumIssuesInProject(ctx, p, doer, isClosed) + num, err := NumIssuesInProject(ctx, p, doer, org, isClosed) if err != nil { return nil, err } @@ -122,13 +120,13 @@ func NumIssuesInProjects(ctx context.Context, pl project_model.ProjectList, doer } // NumIssuesInProject returns counter of all issues assigned to a project which doer can access -func NumIssuesInProject(ctx context.Context, p *project_model.Project, doer *user_model.User, isClosed optional.Option[bool]) (int, error) { +func NumIssuesInProject(ctx context.Context, p *project_model.Project, doer *user_model.User, org *organization.Organization, isClosed optional.Option[bool]) (int, error) { numIssuesInProject := int(0) bs, err := p.GetBoards(ctx) if err != nil { return 0, err } - im, err := LoadIssuesFromBoardList(ctx, bs, doer, isClosed) + im, err := LoadIssuesFromBoardList(ctx, bs, doer, org, isClosed) if err != nil { return 0, err } diff --git a/routers/web/org/projects.go b/routers/web/org/projects.go index 79c97c711aaa..1351aa890483 100644 --- a/routers/web/org/projects.go +++ b/routers/web/org/projects.go @@ -128,12 +128,12 @@ func Projects(ctx *context.Context) { ctx.Data["PageIsViewProjects"] = true ctx.Data["SortType"] = sortType - numOpenIssues, err := issues_model.NumIssuesInProjects(ctx, projects, ctx.Doer, optional.Some(false)) + numOpenIssues, err := issues_model.NumIssuesInProjects(ctx, projects, ctx.Doer, ctx.Org.Organization, optional.Some(false)) if err != nil { ctx.ServerError("NumIssuesInProjects", err) return } - numClosedIssues, err := issues_model.NumIssuesInProjects(ctx, projects, ctx.Doer, optional.Some(true)) + numClosedIssues, err := issues_model.NumIssuesInProjects(ctx, projects, ctx.Doer, ctx.Org.Organization, optional.Some(true)) if err != nil { ctx.ServerError("NumIssuesInProjects", err) return @@ -370,7 +370,7 @@ func ViewProject(ctx *context.Context) { boards[0].Title = ctx.Locale.TrString("repo.projects.type.uncategorized") } - issuesMap, err := issues_model.LoadIssuesFromBoardList(ctx, boards, ctx.Doer, optional.None[bool]()) + issuesMap, err := issues_model.LoadIssuesFromBoardList(ctx, boards, ctx.Doer, ctx.Org.Organization, optional.None[bool]()) if err != nil { ctx.ServerError("LoadIssuesOfBoards", err) return diff --git a/routers/web/repo/projects.go b/routers/web/repo/projects.go index 8c115307cee5..430fba0fabf9 100644 --- a/routers/web/repo/projects.go +++ b/routers/web/repo/projects.go @@ -126,12 +126,12 @@ func Projects(ctx *context.Context) { ctx.Data["IsProjectsPage"] = true ctx.Data["SortType"] = sortType - numOpenIssues, err := issues_model.NumIssuesInProjects(ctx, projects, ctx.Doer, optional.Some(false)) + numOpenIssues, err := issues_model.NumIssuesInProjects(ctx, projects, ctx.Doer, ctx.Org.Organization, optional.Some(false)) if err != nil { ctx.ServerError("NumIssuesInProjects", err) return } - numClosedIssues, err := issues_model.NumIssuesInProjects(ctx, projects, ctx.Doer, optional.Some(true)) + numClosedIssues, err := issues_model.NumIssuesInProjects(ctx, projects, ctx.Doer, ctx.Org.Organization, optional.Some(true)) if err != nil { ctx.ServerError("NumIssuesInProjects", err) return @@ -332,7 +332,7 @@ func ViewProject(ctx *context.Context) { boards[0].Title = ctx.Locale.TrString("repo.projects.type.uncategorized") } - issuesMap, err := issues_model.LoadIssuesFromBoardList(ctx, boards, ctx.Doer, optional.None[bool]()) + issuesMap, err := issues_model.LoadIssuesFromBoardList(ctx, boards, ctx.Doer, ctx.Org.Organization, optional.None[bool]()) if err != nil { ctx.ServerError("LoadIssuesOfBoards", err) return From 80922b1f2c04b1f04723b4f4b854de8345d456f9 Mon Sep 17 00:00:00 2001 From: yp05327 <576951401@qq.com> Date: Thu, 7 Mar 2024 00:59:55 +0000 Subject: [PATCH 69/69] fix issue search bug --- models/issues/issue_search.go | 3 ++- models/repo/repo_list.go | 4 ++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/models/issues/issue_search.go b/models/issues/issue_search.go index c5c9cecdb92d..836fba037156 100644 --- a/models/issues/issue_search.go +++ b/models/issues/issue_search.go @@ -202,7 +202,7 @@ func applyRepoConditions(sess *xorm.Session, opts *IssuesOptions) *xorm.Session if opts.RepoCond == nil { opts.RepoCond = builder.NewCond() } - opts.RepoCond = opts.RepoCond.Or(builder.In("issue.repo_id", builder.Select("id").From("repository").Where(builder.Eq{"is_private": false}))) + opts.RepoCond = opts.RepoCond.Or(repo_model.PublicRepoCond("issue.repo_id")) } if opts.RepoCond != nil { sess.And(opts.RepoCond) @@ -340,6 +340,7 @@ func issuePullAccessibleRepoCond(repoIDstr string, userID int64, org *organizati } else { cond = cond.And( builder.Or( + repo_model.PublicRepoCond(repoIDstr), // all public repos repo_model.UserOwnedRepoCond(userID), // owned repos repo_model.UserAccessRepoCond(repoIDstr, userID), // user can access repo in a unit independent way repo_model.UserAssignedRepoCond(repoIDstr, userID), // user has been assigned accessible public repos diff --git a/models/repo/repo_list.go b/models/repo/repo_list.go index 7552a455810f..405c028647b8 100644 --- a/models/repo/repo_list.go +++ b/models/repo/repo_list.go @@ -215,6 +215,10 @@ const ( SearchOrderByForksReverse SearchOrderBy = "num_forks DESC" ) +func PublicRepoCond(id string) builder.Cond { + return builder.In(id, builder.Select("id").From("repository").Where(builder.Eq{"is_private": false})) +} + // UserOwnedRepoCond returns user ownered repositories func UserOwnedRepoCond(userID int64) builder.Cond { return builder.Eq{