forked from mirrors/forgejo
fix(web): org projects assignment in issue view (#7999)
Allows user to assign organization projects to their new issues, using the project sidebar selector, even when repository's projects are disabled. Moreover, the project sidebar selector is now hidden if no projects (repository-wide + organization-wide) are available. Fixes forgejo/forgejo#5666 Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/7999 Reviewed-by: Gusted <gusted@noreply.codeberg.org>
This commit is contained in:
parent
07a6b6ce82
commit
731334e973
33 changed files with 593 additions and 36 deletions
|
|
@ -604,7 +604,7 @@ func retrieveProjects(ctx *context.Context, repo *repo_model.Repository) {
|
||||||
repoOwnerType = project_model.TypeOrganization
|
repoOwnerType = project_model.TypeOrganization
|
||||||
}
|
}
|
||||||
var err error
|
var err error
|
||||||
projects, err := db.Find[project_model.Project](ctx, project_model.SearchOptions{
|
repositoryProjects, err := db.Find[project_model.Project](ctx, project_model.SearchOptions{
|
||||||
ListOptions: db.ListOptionsAll,
|
ListOptions: db.ListOptionsAll,
|
||||||
RepoID: repo.ID,
|
RepoID: repo.ID,
|
||||||
IsClosed: optional.Some(false),
|
IsClosed: optional.Some(false),
|
||||||
|
|
@ -614,7 +614,7 @@ func retrieveProjects(ctx *context.Context, repo *repo_model.Repository) {
|
||||||
ctx.ServerError("GetProjects", err)
|
ctx.ServerError("GetProjects", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
projects2, err := db.Find[project_model.Project](ctx, project_model.SearchOptions{
|
ownerProjects, err := db.Find[project_model.Project](ctx, project_model.SearchOptions{
|
||||||
ListOptions: db.ListOptionsAll,
|
ListOptions: db.ListOptionsAll,
|
||||||
OwnerID: repo.OwnerID,
|
OwnerID: repo.OwnerID,
|
||||||
IsClosed: optional.Some(false),
|
IsClosed: optional.Some(false),
|
||||||
|
|
@ -625,9 +625,10 @@ func retrieveProjects(ctx *context.Context, repo *repo_model.Repository) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.Data["OpenProjects"] = append(projects, projects2...)
|
ownerHasOpenProjects := len(ownerProjects) > 0
|
||||||
|
ctx.Data["OpenProjects"] = append(repositoryProjects, ownerProjects...)
|
||||||
|
|
||||||
projects, err = db.Find[project_model.Project](ctx, project_model.SearchOptions{
|
repositoryProjects, err = db.Find[project_model.Project](ctx, project_model.SearchOptions{
|
||||||
ListOptions: db.ListOptionsAll,
|
ListOptions: db.ListOptionsAll,
|
||||||
RepoID: repo.ID,
|
RepoID: repo.ID,
|
||||||
IsClosed: optional.Some(true),
|
IsClosed: optional.Some(true),
|
||||||
|
|
@ -637,7 +638,7 @@ func retrieveProjects(ctx *context.Context, repo *repo_model.Repository) {
|
||||||
ctx.ServerError("GetProjects", err)
|
ctx.ServerError("GetProjects", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
projects2, err = db.Find[project_model.Project](ctx, project_model.SearchOptions{
|
ownerProjects, err = db.Find[project_model.Project](ctx, project_model.SearchOptions{
|
||||||
ListOptions: db.ListOptionsAll,
|
ListOptions: db.ListOptionsAll,
|
||||||
OwnerID: repo.OwnerID,
|
OwnerID: repo.OwnerID,
|
||||||
IsClosed: optional.Some(true),
|
IsClosed: optional.Some(true),
|
||||||
|
|
@ -648,7 +649,8 @@ func retrieveProjects(ctx *context.Context, repo *repo_model.Repository) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.Data["ClosedProjects"] = append(projects, projects2...)
|
ctx.Data["OwnerHasProjects"] = ownerHasOpenProjects || len(ownerProjects) > 0
|
||||||
|
ctx.Data["ClosedProjects"] = append(repositoryProjects, ownerProjects...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// repoReviewerSelection items to bee shown
|
// repoReviewerSelection items to bee shown
|
||||||
|
|
@ -968,6 +970,14 @@ func NewIssue(ctx *context.Context) {
|
||||||
|
|
||||||
isProjectsEnabled := ctx.Repo.CanRead(unit.TypeProjects)
|
isProjectsEnabled := ctx.Repo.CanRead(unit.TypeProjects)
|
||||||
ctx.Data["IsProjectsEnabled"] = isProjectsEnabled
|
ctx.Data["IsProjectsEnabled"] = isProjectsEnabled
|
||||||
|
|
||||||
|
// Individuals always have projects unit enabled
|
||||||
|
isOwnerProjectsEnabled := true
|
||||||
|
if ctx.Repo.Owner.IsOrganization() {
|
||||||
|
isOwnerProjectsEnabled = ctx.Org.CanReadUnit(ctx, unit.TypeProjects)
|
||||||
|
}
|
||||||
|
ctx.Data["IsOwnerProjectsEnabled"] = isOwnerProjectsEnabled
|
||||||
|
|
||||||
ctx.Data["IsAttachmentEnabled"] = setting.Attachment.Enabled
|
ctx.Data["IsAttachmentEnabled"] = setting.Attachment.Enabled
|
||||||
upload.AddUploadContext(ctx, "comment")
|
upload.AddUploadContext(ctx, "comment")
|
||||||
|
|
||||||
|
|
@ -983,7 +993,7 @@ func NewIssue(ctx *context.Context) {
|
||||||
}
|
}
|
||||||
|
|
||||||
projectID := ctx.FormInt64("project")
|
projectID := ctx.FormInt64("project")
|
||||||
if projectID > 0 && isProjectsEnabled {
|
if projectID > 0 && (isProjectsEnabled || isOwnerProjectsEnabled) {
|
||||||
project, err := project_model.GetProjectByID(ctx, projectID)
|
project, err := project_model.GetProjectByID(ctx, projectID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("GetProjectByID: %d: %v", projectID, err)
|
log.Error("GetProjectByID: %d: %v", projectID, err)
|
||||||
|
|
@ -1276,9 +1286,9 @@ func NewIssuePost(ctx *context.Context) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if projectID > 0 {
|
if projectID > 0 {
|
||||||
if !ctx.Repo.CanRead(unit.TypeProjects) {
|
if !ctx.Repo.CanRead(unit.TypeProjects) || (ctx.ContextUser.IsOrganization() && !ctx.Org.CanReadUnit(ctx, unit.TypeProjects)) {
|
||||||
// User must also be able to see the project.
|
// User must also be able to see the project.
|
||||||
ctx.Error(http.StatusBadRequest, "user hasn't permissions to read projects")
|
ctx.Error(http.StatusForbidden, "user doesn't have permissions to read projects")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if err := issues_model.IssueAssignOrRemoveProject(ctx, issue, ctx.Doer, projectID, 0); err != nil {
|
if err := issues_model.IssueAssignOrRemoveProject(ctx, issue, ctx.Doer, projectID, 0); err != nil {
|
||||||
|
|
@ -1477,7 +1487,8 @@ func ViewIssue(ctx *context.Context) {
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.Data["IsModerationEnabled"] = setting.Moderation.Enabled
|
ctx.Data["IsModerationEnabled"] = setting.Moderation.Enabled
|
||||||
ctx.Data["IsProjectsEnabled"] = ctx.Repo.CanRead(unit.TypeProjects)
|
isProjectsEnabled := ctx.Repo.CanRead(unit.TypeProjects)
|
||||||
|
ctx.Data["IsProjectsEnabled"] = isProjectsEnabled
|
||||||
ctx.Data["IsAttachmentEnabled"] = setting.Attachment.Enabled
|
ctx.Data["IsAttachmentEnabled"] = setting.Attachment.Enabled
|
||||||
upload.AddUploadContext(ctx, "comment")
|
upload.AddUploadContext(ctx, "comment")
|
||||||
|
|
||||||
|
|
@ -1546,6 +1557,7 @@ func ViewIssue(ctx *context.Context) {
|
||||||
}
|
}
|
||||||
ctx.Data["Labels"] = labels
|
ctx.Data["Labels"] = labels
|
||||||
|
|
||||||
|
isOwnerProjectsEnabled := true
|
||||||
if repo.Owner.IsOrganization() {
|
if repo.Owner.IsOrganization() {
|
||||||
orgLabels, err := issues_model.GetLabelsByOrgID(ctx, repo.Owner.ID, ctx.FormString("sort"), db.ListOptions{})
|
orgLabels, err := issues_model.GetLabelsByOrgID(ctx, repo.Owner.ID, ctx.FormString("sort"), db.ListOptions{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -1553,9 +1565,11 @@ func ViewIssue(ctx *context.Context) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
ctx.Data["OrgLabels"] = orgLabels
|
ctx.Data["OrgLabels"] = orgLabels
|
||||||
|
|
||||||
labels = append(labels, orgLabels...)
|
labels = append(labels, orgLabels...)
|
||||||
|
|
||||||
|
isOwnerProjectsEnabled = ctx.Org.CanReadUnit(ctx, unit.TypeProjects)
|
||||||
}
|
}
|
||||||
|
ctx.Data["IsOwnerProjectsEnabled"] = isOwnerProjectsEnabled
|
||||||
|
|
||||||
hasSelected := false
|
hasSelected := false
|
||||||
for i := range labels {
|
for i := range labels {
|
||||||
|
|
@ -1569,7 +1583,9 @@ func ViewIssue(ctx *context.Context) {
|
||||||
// Check milestone and assignee.
|
// Check milestone and assignee.
|
||||||
if ctx.Repo.CanWriteIssuesOrPulls(issue.IsPull) {
|
if ctx.Repo.CanWriteIssuesOrPulls(issue.IsPull) {
|
||||||
RetrieveRepoMilestonesAndAssignees(ctx, repo)
|
RetrieveRepoMilestonesAndAssignees(ctx, repo)
|
||||||
retrieveProjects(ctx, repo)
|
if isProjectsEnabled || isOwnerProjectsEnabled {
|
||||||
|
retrieveProjects(ctx, repo)
|
||||||
|
}
|
||||||
|
|
||||||
if ctx.Written() {
|
if ctx.Written() {
|
||||||
return
|
return
|
||||||
|
|
|
||||||
|
|
@ -74,6 +74,9 @@ func TestNewIssueValidateProject(t *testing.T) {
|
||||||
contexttest.LoadUser(t, ctx, testCase.userID)
|
contexttest.LoadUser(t, ctx, testCase.userID)
|
||||||
contexttest.LoadRepo(t, ctx, testCase.repoID)
|
contexttest.LoadRepo(t, ctx, testCase.repoID)
|
||||||
contexttest.LoadGitRepo(t, ctx)
|
contexttest.LoadGitRepo(t, ctx)
|
||||||
|
if ctx.Repo.Owner.IsOrganization() {
|
||||||
|
contexttest.LoadOrganization(t, ctx, ctx.Repo.Owner.ID)
|
||||||
|
}
|
||||||
|
|
||||||
NewIssue(ctx)
|
NewIssue(ctx)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -400,7 +400,7 @@ func UpdateIssueProject(ctx *context.Context) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if _, err := issues.LoadRepositories(ctx); err != nil {
|
if _, err := issues.LoadRepositories(ctx); err != nil {
|
||||||
ctx.ServerError("LoadProjects", err)
|
ctx.ServerError("LoadRepositories", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -906,6 +906,29 @@ func registerRoutes(m *web.Route) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
reqRepoOrOwnerProjectReader := func(ctx *context.Context) {
|
||||||
|
unitType := unit.TypeProjects
|
||||||
|
if ctx.ContextUser == nil || ctx.Doer == nil {
|
||||||
|
ctx.NotFound(unitType.String(), nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case ctx.ContextUser.IsIndividual():
|
||||||
|
if ctx.Doer.ID == ctx.ContextUser.ID || ctx.Doer.IsAdmin {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
case ctx.ContextUser.IsOrganization():
|
||||||
|
if ctx.Org.Organization.UnitPermission(ctx, ctx.Doer, unitType) >= perm.AccessModeRead {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
ctx.NotFound(unitType.String(), nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
reqRepoProjectsReader(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
individualPermsChecker := func(ctx *context.Context) {
|
individualPermsChecker := func(ctx *context.Context) {
|
||||||
// org permissions have been checked in context.OrgAssignment(), but individual permissions haven't been checked.
|
// org permissions have been checked in context.OrgAssignment(), but individual permissions haven't been checked.
|
||||||
if ctx.ContextUser.IsIndividual() {
|
if ctx.ContextUser.IsIndividual() {
|
||||||
|
|
@ -1255,7 +1278,7 @@ func registerRoutes(m *web.Route) {
|
||||||
}
|
}
|
||||||
m.Group("/issues", func() {
|
m.Group("/issues", func() {
|
||||||
m.Group("/new", func() {
|
m.Group("/new", func() {
|
||||||
m.Combo("").Get(context.RepoRef(), repo.NewIssue).
|
m.Combo("", context.EnsureOrg()).Get(context.RepoRef(), repo.NewIssue).
|
||||||
Post(web.Bind(forms.CreateIssueForm{}), repo.NewIssuePost)
|
Post(web.Bind(forms.CreateIssueForm{}), repo.NewIssuePost)
|
||||||
m.Get("/choose", context.RepoRef(), repo.NewIssueChooseTemplate)
|
m.Get("/choose", context.RepoRef(), repo.NewIssueChooseTemplate)
|
||||||
})
|
})
|
||||||
|
|
@ -1301,7 +1324,7 @@ func registerRoutes(m *web.Route) {
|
||||||
|
|
||||||
m.Post("/labels", reqRepoIssuesOrPullsWriter, repo.UpdateIssueLabel)
|
m.Post("/labels", reqRepoIssuesOrPullsWriter, repo.UpdateIssueLabel)
|
||||||
m.Post("/milestone", reqRepoIssuesOrPullsWriter, repo.UpdateIssueMilestone)
|
m.Post("/milestone", reqRepoIssuesOrPullsWriter, repo.UpdateIssueMilestone)
|
||||||
m.Post("/projects", reqRepoIssuesOrPullsWriter, reqRepoProjectsReader, repo.UpdateIssueProject)
|
m.Post("/projects", reqRepoIssuesOrPullsWriter, context.EnsureOrg(), reqRepoOrOwnerProjectReader, repo.UpdateIssueProject)
|
||||||
m.Post("/assignee", reqRepoIssuesOrPullsWriter, repo.UpdateIssueAssignee)
|
m.Post("/assignee", reqRepoIssuesOrPullsWriter, repo.UpdateIssueAssignee)
|
||||||
m.Post("/request_review", reqRepoIssuesOrPullsReader, repo.UpdatePullReviewRequest)
|
m.Post("/request_review", reqRepoIssuesOrPullsReader, repo.UpdatePullReviewRequest)
|
||||||
m.Post("/dismiss_review", reqRepoAdmin, web.Bind(forms.DismissReviewForm{}), repo.DismissReview)
|
m.Post("/dismiss_review", reqRepoAdmin, web.Bind(forms.DismissReviewForm{}), repo.DismissReview)
|
||||||
|
|
@ -1427,7 +1450,7 @@ func registerRoutes(m *web.Route) {
|
||||||
m.Group("", func() {
|
m.Group("", func() {
|
||||||
m.Get("/issues/posters", repo.IssuePosters) // it can't use {type:issues|pulls} because other routes like "/pulls/{index}" has higher priority
|
m.Get("/issues/posters", repo.IssuePosters) // it can't use {type:issues|pulls} because other routes like "/pulls/{index}" has higher priority
|
||||||
m.Get("/{type:^(issues|pulls)$}", repo.Issues)
|
m.Get("/{type:^(issues|pulls)$}", repo.Issues)
|
||||||
m.Get("/{type:^(issues|pulls)$}/{index}", repo.ViewIssue)
|
m.Get("/{type:^(issues|pulls)$}/{index}", context.EnsureOrg(), repo.ViewIssue)
|
||||||
m.Group("/{type:^(issues|pulls)$}/{index}/content-history", func() {
|
m.Group("/{type:^(issues|pulls)$}/{index}/content-history", func() {
|
||||||
m.Get("/overview", repo.GetContentHistoryOverview)
|
m.Get("/overview", repo.GetContentHistoryOverview)
|
||||||
m.Get("/list", repo.GetContentHistoryList)
|
m.Get("/list", repo.GetContentHistoryList)
|
||||||
|
|
@ -1600,7 +1623,7 @@ func registerRoutes(m *web.Route) {
|
||||||
|
|
||||||
m.Get("/pulls/posters", repo.PullPosters)
|
m.Get("/pulls/posters", repo.PullPosters)
|
||||||
m.Group("/pulls/{index}", func() {
|
m.Group("/pulls/{index}", func() {
|
||||||
m.Get("", repo.SetWhitespaceBehavior, repo.GetPullDiffStats, repo.ViewIssue)
|
m.Get("", repo.SetWhitespaceBehavior, repo.GetPullDiffStats, context.EnsureOrg(), repo.ViewIssue)
|
||||||
m.Get(".diff", repo.DownloadPullDiff)
|
m.Get(".diff", repo.DownloadPullDiff)
|
||||||
m.Get(".patch", repo.DownloadPullPatch)
|
m.Get(".patch", repo.DownloadPullPatch)
|
||||||
m.Group("/commits", func() {
|
m.Group("/commits", func() {
|
||||||
|
|
|
||||||
|
|
@ -64,6 +64,32 @@ func GetOrganizationByParams(ctx *Context) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ensureIsOrg(ctx *Context) bool {
|
||||||
|
switch {
|
||||||
|
// Getting Organization from params
|
||||||
|
case ctx.ContextUser == nil:
|
||||||
|
if ctx.Org.Organization == nil {
|
||||||
|
GetOrganizationByParams(ctx)
|
||||||
|
}
|
||||||
|
return !ctx.Written()
|
||||||
|
// Getting Organization from ContextUser
|
||||||
|
case ctx.ContextUser.IsOrganization():
|
||||||
|
if ctx.Org == nil {
|
||||||
|
ctx.Org = &Organization{}
|
||||||
|
}
|
||||||
|
ctx.Org.Organization = (*organization.Organization)(ctx.ContextUser)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
// ContextUser is an individual User
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func EnsureOrg() func(*Context) {
|
||||||
|
return func(ctx *Context) {
|
||||||
|
ensureIsOrg(ctx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// HandleOrgAssignment handles organization assignment
|
// HandleOrgAssignment handles organization assignment
|
||||||
func HandleOrgAssignment(ctx *Context, args ...bool) {
|
func HandleOrgAssignment(ctx *Context, args ...bool) {
|
||||||
var (
|
var (
|
||||||
|
|
@ -87,24 +113,9 @@ func HandleOrgAssignment(ctx *Context, args ...bool) {
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
if ctx.ContextUser == nil {
|
if !ensureIsOrg(ctx) {
|
||||||
// if Organization is not defined, get it from params
|
|
||||||
if ctx.Org.Organization == nil {
|
|
||||||
GetOrganizationByParams(ctx)
|
|
||||||
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
org := ctx.Org.Organization
|
org := ctx.Org.Organization
|
||||||
|
|
||||||
// Handle Visibility
|
// Handle Visibility
|
||||||
|
|
|
||||||
|
|
@ -78,7 +78,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{{if .IsProjectsEnabled}}
|
{{if or .IsProjectsEnabled (and .OwnerHasProjects .IsOwnerProjectsEnabled)}}
|
||||||
<div class="divider"></div>
|
<div class="divider"></div>
|
||||||
|
|
||||||
<input id="project_id" name="project_id" type="hidden" value="{{.project_id}}">
|
<input id="project_id" name="project_id" type="hidden" value="{{.project_id}}">
|
||||||
|
|
|
||||||
|
|
@ -14,8 +14,10 @@
|
||||||
{{template "repo/issue/view_content/sidebar/milestones" .}}
|
{{template "repo/issue/view_content/sidebar/milestones" .}}
|
||||||
<div class="divider"></div>
|
<div class="divider"></div>
|
||||||
|
|
||||||
{{template "repo/issue/view_content/sidebar/projects" .}}
|
{{if or .IsProjectsEnabled (and .OwnerHasProjects .IsOwnerProjectsEnabled)}}
|
||||||
<div class="divider"></div>
|
{{template "repo/issue/view_content/sidebar/projects" .}}
|
||||||
|
<div class="divider"></div>
|
||||||
|
{{end}}
|
||||||
|
|
||||||
{{template "repo/issue/view_content/sidebar/assignees" dict "isExistingIssue" true "." .}}
|
{{template "repo/issue/view_content/sidebar/assignees" dict "isExistingIssue" true "." .}}
|
||||||
<div class="divider"></div>
|
<div class="divider"></div>
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
ref: refs/heads/master
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
[core]
|
||||||
|
repositoryformatversion = 0
|
||||||
|
filemode = true
|
||||||
|
bare = true
|
||||||
|
ignorecase = true
|
||||||
|
precomposeunicode = true
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
Unnamed repository; edit this file 'description' to name the repository.
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
# git ls-files --others --exclude-from=.git/info/exclude
|
||||||
|
# Lines that start with '#' are comments.
|
||||||
|
# For a project mostly in C, the following would be a good set of
|
||||||
|
# exclude patterns (uncomment them if you want to use them):
|
||||||
|
# *.[oa]
|
||||||
|
# *~
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -0,0 +1 @@
|
||||||
|
2a47ca4b614a9f5a43abbd5ad851a54a616ffee6
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
d22b4d4daa5be07329fcef6ed458f00cf3392da0
|
||||||
5
tests/integration/fixtures/TestAssignProject/access.yml
Normal file
5
tests/integration/fixtures/TestAssignProject/access.yml
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
-
|
||||||
|
id: 1001
|
||||||
|
user_id: 5
|
||||||
|
repo_id: 1001
|
||||||
|
mode: 2
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
-
|
||||||
|
id: 1001
|
||||||
|
repo_id: 1001
|
||||||
|
user_id: 5
|
||||||
|
mode: 2
|
||||||
51
tests/integration/fixtures/TestAssignProject/project.yml
Normal file
51
tests/integration/fixtures/TestAssignProject/project.yml
Normal file
|
|
@ -0,0 +1,51 @@
|
||||||
|
# team 1001 projects
|
||||||
|
-
|
||||||
|
id: 1001
|
||||||
|
title: project1001
|
||||||
|
owner_id: 3
|
||||||
|
repo_id: 0
|
||||||
|
is_closed: false
|
||||||
|
board_type: 0
|
||||||
|
type: 3
|
||||||
|
creator_id: 5
|
||||||
|
created_unix: 1688973030
|
||||||
|
updated_unix: 1688973030
|
||||||
|
|
||||||
|
# team 1002 projects
|
||||||
|
-
|
||||||
|
id: 1002
|
||||||
|
title: project1002
|
||||||
|
owner_id: 0
|
||||||
|
repo_id: 1001
|
||||||
|
is_closed: false
|
||||||
|
board_type: 0
|
||||||
|
type: 2
|
||||||
|
creator_id: 5
|
||||||
|
created_unix: 1688973030
|
||||||
|
updated_unix: 1688973030
|
||||||
|
|
||||||
|
# user 5 project
|
||||||
|
-
|
||||||
|
id: 1003
|
||||||
|
title: project1003
|
||||||
|
owner_id: 5
|
||||||
|
repo_id: 0
|
||||||
|
is_closed: false
|
||||||
|
board_type: 0
|
||||||
|
type: 1
|
||||||
|
creator_id: 5
|
||||||
|
created_unix: 1688973030
|
||||||
|
updated_unix: 1688973030
|
||||||
|
|
||||||
|
# org 1001 disabled project
|
||||||
|
-
|
||||||
|
id: 1004
|
||||||
|
title: project1004
|
||||||
|
owner_id: 1001
|
||||||
|
repo_id: 0
|
||||||
|
is_closed: false
|
||||||
|
board_type: 0
|
||||||
|
type: 3
|
||||||
|
creator_id: 5
|
||||||
|
created_unix: 1688973030
|
||||||
|
updated_unix: 1688973030
|
||||||
35
tests/integration/fixtures/TestAssignProject/repo_unit.yml
Normal file
35
tests/integration/fixtures/TestAssignProject/repo_unit.yml
Normal file
|
|
@ -0,0 +1,35 @@
|
||||||
|
-
|
||||||
|
id: 1001
|
||||||
|
repo_id: 3
|
||||||
|
type: 8
|
||||||
|
created_unix: 946684810
|
||||||
|
|
||||||
|
-
|
||||||
|
id: 1002
|
||||||
|
repo_id: 3
|
||||||
|
type: 2
|
||||||
|
created_unix: 946684810
|
||||||
|
|
||||||
|
-
|
||||||
|
id: 1003
|
||||||
|
repo_id: 3
|
||||||
|
type: 3
|
||||||
|
created_unix: 946684810
|
||||||
|
|
||||||
|
-
|
||||||
|
id: 1004
|
||||||
|
repo_id: 1001
|
||||||
|
type: 8
|
||||||
|
created_unix: 946684810
|
||||||
|
|
||||||
|
-
|
||||||
|
id: 1005
|
||||||
|
repo_id: 1001
|
||||||
|
type: 2
|
||||||
|
created_unix: 946684810
|
||||||
|
|
||||||
|
-
|
||||||
|
id: 1006
|
||||||
|
repo_id: 1001
|
||||||
|
type: 3
|
||||||
|
created_unix: 946684810
|
||||||
30
tests/integration/fixtures/TestAssignProject/repository.yml
Normal file
30
tests/integration/fixtures/TestAssignProject/repository.yml
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
-
|
||||||
|
id: 1001
|
||||||
|
owner_id: 1001
|
||||||
|
owner_name: test_assign_project_org
|
||||||
|
lower_name: test_assign_project_repo
|
||||||
|
name: test_assign_project_repo
|
||||||
|
default_branch: master
|
||||||
|
num_watches: 4
|
||||||
|
num_stars: 0
|
||||||
|
num_forks: 0
|
||||||
|
num_milestones: 3
|
||||||
|
num_closed_milestones: 1
|
||||||
|
num_projects: 1
|
||||||
|
num_closed_projects: 0
|
||||||
|
is_private: false
|
||||||
|
is_empty: false
|
||||||
|
is_archived: false
|
||||||
|
is_mirror: false
|
||||||
|
status: 0
|
||||||
|
is_fork: false
|
||||||
|
fork_id: 0
|
||||||
|
is_template: false
|
||||||
|
template_id: 0
|
||||||
|
size: 7597
|
||||||
|
is_fsck_enabled: true
|
||||||
|
close_issues_via_commit_in_any_branch: false
|
||||||
|
created_unix: 1731254961
|
||||||
|
updated_unix: 1731254961
|
||||||
|
topics: '[]'
|
||||||
|
|
||||||
32
tests/integration/fixtures/TestAssignProject/team.yml
Normal file
32
tests/integration/fixtures/TestAssignProject/team.yml
Normal file
|
|
@ -0,0 +1,32 @@
|
||||||
|
-
|
||||||
|
id: 1001
|
||||||
|
org_id: 3
|
||||||
|
lower_name: writers
|
||||||
|
name: Writers
|
||||||
|
authorize: 2 # writer
|
||||||
|
num_repos: 3
|
||||||
|
num_members: 1
|
||||||
|
includes_all_repositories: false
|
||||||
|
can_create_org_repo: true
|
||||||
|
|
||||||
|
-
|
||||||
|
id: 1002
|
||||||
|
org_id: 1001
|
||||||
|
lower_name: writers
|
||||||
|
name: Writers
|
||||||
|
authorize: 2 # writer
|
||||||
|
num_repos: 1
|
||||||
|
num_members: 1
|
||||||
|
includes_all_repositories: false
|
||||||
|
can_create_org_repo: true
|
||||||
|
|
||||||
|
-
|
||||||
|
id: 1003
|
||||||
|
org_id: 1001
|
||||||
|
lower_name: owners
|
||||||
|
name: Owners
|
||||||
|
authorize: 4
|
||||||
|
num_repos: 0
|
||||||
|
num_members: 1
|
||||||
|
includes_all_repositories: false
|
||||||
|
can_create_org_repo: true
|
||||||
11
tests/integration/fixtures/TestAssignProject/team_repo.yml
Normal file
11
tests/integration/fixtures/TestAssignProject/team_repo.yml
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
-
|
||||||
|
id: 1001
|
||||||
|
org_id: 3
|
||||||
|
team_id: 1001
|
||||||
|
repo_id: 3
|
||||||
|
|
||||||
|
-
|
||||||
|
id: 1002
|
||||||
|
org_id: 1001
|
||||||
|
team_id: 1002
|
||||||
|
repo_id: 1001
|
||||||
35
tests/integration/fixtures/TestAssignProject/team_unit.yml
Normal file
35
tests/integration/fixtures/TestAssignProject/team_unit.yml
Normal file
|
|
@ -0,0 +1,35 @@
|
||||||
|
-
|
||||||
|
id: 1001
|
||||||
|
team_id: 1001
|
||||||
|
type: 8
|
||||||
|
access_mode: 2
|
||||||
|
|
||||||
|
-
|
||||||
|
id: 1002
|
||||||
|
team_id: 1002
|
||||||
|
type: 8
|
||||||
|
access_mode: 1
|
||||||
|
|
||||||
|
-
|
||||||
|
id: 1003
|
||||||
|
team_id: 1001
|
||||||
|
type: 2
|
||||||
|
access_mode: 2
|
||||||
|
|
||||||
|
-
|
||||||
|
id: 1004
|
||||||
|
team_id: 1001
|
||||||
|
type: 3
|
||||||
|
access_mode: 2
|
||||||
|
|
||||||
|
-
|
||||||
|
id: 1005
|
||||||
|
team_id: 1002
|
||||||
|
type: 2
|
||||||
|
access_mode: 2
|
||||||
|
|
||||||
|
-
|
||||||
|
id: 1006
|
||||||
|
team_id: 1002
|
||||||
|
type: 3
|
||||||
|
access_mode: 2
|
||||||
11
tests/integration/fixtures/TestAssignProject/team_user.yml
Normal file
11
tests/integration/fixtures/TestAssignProject/team_user.yml
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
-
|
||||||
|
id: 1001
|
||||||
|
org_id: 3
|
||||||
|
team_id: 1001
|
||||||
|
uid: 5
|
||||||
|
|
||||||
|
-
|
||||||
|
id: 1002
|
||||||
|
org_id: 23
|
||||||
|
team_id: 1002
|
||||||
|
uid: 5
|
||||||
37
tests/integration/fixtures/TestAssignProject/user.yml
Normal file
37
tests/integration/fixtures/TestAssignProject/user.yml
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
-
|
||||||
|
id: 1001
|
||||||
|
lower_name: test_assign_project_org
|
||||||
|
name: test_assign_project_org
|
||||||
|
full_name: ' <<<< >> >> > >> > >>> >> '
|
||||||
|
email: org1001@example.com
|
||||||
|
keep_email_private: false
|
||||||
|
email_notifications_preference: onmention
|
||||||
|
passwd: ZogKvWdyEx:password
|
||||||
|
passwd_hash_algo: dummy
|
||||||
|
must_change_password: false
|
||||||
|
login_source: 0
|
||||||
|
login_name: test_assign_project_org
|
||||||
|
type: 1
|
||||||
|
salt: ZogKvWdyEx
|
||||||
|
max_repo_creation: 1000
|
||||||
|
is_active: false
|
||||||
|
is_admin: false
|
||||||
|
is_restricted: false
|
||||||
|
allow_git_hook: false
|
||||||
|
allow_import_local: false
|
||||||
|
allow_create_organization: true
|
||||||
|
prohibit_login: false
|
||||||
|
avatar: ""
|
||||||
|
avatar_email: org1001@example.com
|
||||||
|
use_custom_avatar: true
|
||||||
|
num_followers: 0
|
||||||
|
num_following: 0
|
||||||
|
num_stars: 0
|
||||||
|
num_repos: 1
|
||||||
|
num_teams: 1
|
||||||
|
num_members: 1
|
||||||
|
visibility: 0
|
||||||
|
repo_admin_change_team_access: false
|
||||||
|
theme: ""
|
||||||
|
keep_activity_private: false
|
||||||
|
created_unix: 1672578020
|
||||||
|
|
@ -19,6 +19,7 @@ import (
|
||||||
auth_model "forgejo.org/models/auth"
|
auth_model "forgejo.org/models/auth"
|
||||||
"forgejo.org/models/db"
|
"forgejo.org/models/db"
|
||||||
issues_model "forgejo.org/models/issues"
|
issues_model "forgejo.org/models/issues"
|
||||||
|
org_model "forgejo.org/models/organization"
|
||||||
project_model "forgejo.org/models/project"
|
project_model "forgejo.org/models/project"
|
||||||
repo_model "forgejo.org/models/repo"
|
repo_model "forgejo.org/models/repo"
|
||||||
unit_model "forgejo.org/models/unit"
|
unit_model "forgejo.org/models/unit"
|
||||||
|
|
@ -31,6 +32,7 @@ import (
|
||||||
api "forgejo.org/modules/structs"
|
api "forgejo.org/modules/structs"
|
||||||
"forgejo.org/modules/test"
|
"forgejo.org/modules/test"
|
||||||
"forgejo.org/modules/translation"
|
"forgejo.org/modules/translation"
|
||||||
|
repo_service "forgejo.org/services/repository"
|
||||||
files_service "forgejo.org/services/repository/files"
|
files_service "forgejo.org/services/repository/files"
|
||||||
user_service "forgejo.org/services/user"
|
user_service "forgejo.org/services/user"
|
||||||
"forgejo.org/tests"
|
"forgejo.org/tests"
|
||||||
|
|
@ -1661,3 +1663,113 @@ func TestIssueUrlHandling(t *testing.T) {
|
||||||
MakeRequest(t, req, http.StatusNotFound)
|
MakeRequest(t, req, http.StatusNotFound)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestIssueProjectSidebarMissing(t *testing.T) {
|
||||||
|
const (
|
||||||
|
repoID = 4
|
||||||
|
userID = 5
|
||||||
|
)
|
||||||
|
defer unittest.OverrideFixtures("tests/integration/fixtures/TestAssignProject/")()
|
||||||
|
defer tests.PrepareTestEnv(t)()
|
||||||
|
|
||||||
|
ctx := t.Context()
|
||||||
|
|
||||||
|
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: userID})
|
||||||
|
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: repoID})
|
||||||
|
session := loginUser(t, user.Name)
|
||||||
|
|
||||||
|
issueURL := testNewIssue(t, session, user.Name, repo.Name, "Hello", "World")
|
||||||
|
t.Run("Sidebar showing - user project available", func(tt *testing.T) {
|
||||||
|
defer tests.PrintCurrentTest(tt)()
|
||||||
|
req := NewRequest(t, "GET", issueURL)
|
||||||
|
resp := session.MakeRequest(t, req, http.StatusOK)
|
||||||
|
htmlDoc := NewHTMLParser(t, resp.Body)
|
||||||
|
htmlDoc.AssertElement(t, ".select-project.dropdown", true)
|
||||||
|
})
|
||||||
|
|
||||||
|
// Enable repository's project unit
|
||||||
|
projectUnit := repo_model.RepoUnit{
|
||||||
|
RepoID: repo.ID,
|
||||||
|
Type: unit_model.TypeProjects,
|
||||||
|
}
|
||||||
|
require.NoError(t, repo_service.UpdateRepositoryUnits(db.DefaultContext, repo, []repo_model.RepoUnit{projectUnit}, nil))
|
||||||
|
|
||||||
|
t.Run("Sidebar showing - repository project unit on", func(tt *testing.T) {
|
||||||
|
defer tests.PrintCurrentTest(tt)()
|
||||||
|
req := NewRequest(t, "GET", issueURL)
|
||||||
|
resp := session.MakeRequest(t, req, http.StatusOK)
|
||||||
|
htmlDoc := NewHTMLParser(t, resp.Body)
|
||||||
|
htmlDoc.AssertElement(t, ".select-project.dropdown", true)
|
||||||
|
})
|
||||||
|
|
||||||
|
project_model.DeleteProjectByID(ctx, 1003)
|
||||||
|
// Disable repository's project unit
|
||||||
|
require.NoError(t, repo_service.UpdateRepositoryUnits(db.DefaultContext, repo, nil, []unit_model.Type{unit_model.TypeProjects}))
|
||||||
|
t.Run("Sidebar missing", func(tt *testing.T) {
|
||||||
|
defer tests.PrintCurrentTest(tt)()
|
||||||
|
req := NewRequest(t, "GET", issueURL)
|
||||||
|
resp := session.MakeRequest(t, req, http.StatusOK)
|
||||||
|
htmlDoc := NewHTMLParser(t, resp.Body)
|
||||||
|
htmlDoc.AssertElement(t, ".select-project.dropdown", false)
|
||||||
|
})
|
||||||
|
|
||||||
|
// Team with project available
|
||||||
|
team := unittest.AssertExistsAndLoadBean(t, &org_model.Team{ID: 1001})
|
||||||
|
require.NoError(t, team.LoadMembers(ctx))
|
||||||
|
require.NoError(t, team.LoadRepositories(ctx))
|
||||||
|
|
||||||
|
user = team.Members[0]
|
||||||
|
repo = team.Repos[0]
|
||||||
|
org := team.GetOrg(ctx)
|
||||||
|
session = loginUser(t, user.Name)
|
||||||
|
|
||||||
|
issueURL = testNewIssue(t, session, org.Name, repo.Name, "Hello", "World")
|
||||||
|
t.Run("Sidebar showing - org on & repo on", func(tt *testing.T) {
|
||||||
|
defer tests.PrintCurrentTest(tt)()
|
||||||
|
req := NewRequest(t, "GET", issueURL)
|
||||||
|
resp := session.MakeRequest(t, req, http.StatusOK)
|
||||||
|
htmlDoc := NewHTMLParser(t, resp.Body)
|
||||||
|
htmlDoc.AssertElement(t, ".select-project.dropdown", true)
|
||||||
|
})
|
||||||
|
|
||||||
|
// Disable repository project unit
|
||||||
|
require.NoError(t, repo_service.UpdateRepositoryUnits(ctx, repo, nil, []unit_model.Type{unit_model.TypeProjects}))
|
||||||
|
t.Run("Sidebar showing - org on & repo off", func(tt *testing.T) {
|
||||||
|
defer tests.PrintCurrentTest(tt)()
|
||||||
|
req := NewRequest(t, "GET", issueURL)
|
||||||
|
resp := session.MakeRequest(t, req, http.StatusOK)
|
||||||
|
htmlDoc := NewHTMLParser(t, resp.Body)
|
||||||
|
htmlDoc.AssertElement(t, ".select-project.dropdown", true)
|
||||||
|
})
|
||||||
|
|
||||||
|
// Team with project disabled
|
||||||
|
team = unittest.AssertExistsAndLoadBean(t, &org_model.Team{ID: 1002})
|
||||||
|
require.NoError(t, team.LoadMembers(ctx))
|
||||||
|
require.NoError(t, team.LoadRepositories(ctx))
|
||||||
|
|
||||||
|
user = team.Members[0]
|
||||||
|
repo = team.Repos[0]
|
||||||
|
org = team.GetOrg(ctx)
|
||||||
|
session = loginUser(t, user.Name)
|
||||||
|
|
||||||
|
require.NoError(t, project_model.DeleteProjectByID(db.DefaultContext, 1004))
|
||||||
|
|
||||||
|
issueURL = testNewIssue(t, session, org.Name, repo.Name, "Hello", "World")
|
||||||
|
t.Run("Sidebar showing - org off & repo on", func(tt *testing.T) {
|
||||||
|
defer tests.PrintCurrentTest(tt)()
|
||||||
|
req := NewRequest(t, "GET", issueURL)
|
||||||
|
resp := session.MakeRequest(t, req, http.StatusOK)
|
||||||
|
htmlDoc := NewHTMLParser(t, resp.Body)
|
||||||
|
htmlDoc.AssertElement(t, ".select-project.dropdown", true)
|
||||||
|
})
|
||||||
|
|
||||||
|
// Disable repository project unit
|
||||||
|
require.NoError(t, repo_service.UpdateRepositoryUnits(ctx, repo, nil, []unit_model.Type{unit_model.TypeProjects}))
|
||||||
|
t.Run("Sidebar missing - org off & repo off", func(tt *testing.T) {
|
||||||
|
defer tests.PrintCurrentTest(tt)()
|
||||||
|
req := NewRequest(t, "GET", issueURL)
|
||||||
|
resp := session.MakeRequest(t, req, http.StatusOK)
|
||||||
|
htmlDoc := NewHTMLParser(t, resp.Body)
|
||||||
|
htmlDoc.AssertElement(t, ".select-project.dropdown", false)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,12 +7,20 @@ package integration
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"path"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"forgejo.org/models/db"
|
"forgejo.org/models/db"
|
||||||
|
issues_model "forgejo.org/models/issues"
|
||||||
|
org_model "forgejo.org/models/organization"
|
||||||
project_model "forgejo.org/models/project"
|
project_model "forgejo.org/models/project"
|
||||||
repo_model "forgejo.org/models/repo"
|
repo_model "forgejo.org/models/repo"
|
||||||
|
unit_model "forgejo.org/models/unit"
|
||||||
"forgejo.org/models/unittest"
|
"forgejo.org/models/unittest"
|
||||||
|
user_model "forgejo.org/models/user"
|
||||||
|
repo_service "forgejo.org/services/repository"
|
||||||
"forgejo.org/tests"
|
"forgejo.org/tests"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
|
@ -160,3 +168,117 @@ func TestChangeStatusProject(t *testing.T) {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestAssignProject(t *testing.T) {
|
||||||
|
defer unittest.OverrideFixtures("tests/integration/fixtures/TestAssignProject/")()
|
||||||
|
defer tests.PrepareTestEnv(t)()
|
||||||
|
|
||||||
|
ctx := t.Context()
|
||||||
|
|
||||||
|
newTestIssue := func(t *testing.T, session *TestSession, owner *user_model.User, repo *repo_model.Repository) (*issues_model.Issue, string, string) {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
issueURL := testNewIssue(t, session, owner.Name, repo.Name, "Hello", "World")
|
||||||
|
indexStr := issueURL[strings.LastIndexByte(issueURL, '/')+1:]
|
||||||
|
index, err := strconv.Atoi(indexStr)
|
||||||
|
require.NoError(t, err, "Invalid issue href: %s", issueURL)
|
||||||
|
|
||||||
|
issue := &issues_model.Issue{RepoID: repo.ID, Index: int64(index)}
|
||||||
|
unittest.AssertExistsAndLoadBean(t, issue)
|
||||||
|
|
||||||
|
issueID := strconv.FormatInt(issue.ID, 10)
|
||||||
|
return issue, indexStr, issueID
|
||||||
|
}
|
||||||
|
|
||||||
|
updateIssueProject := func(t *testing.T, session *TestSession, projectID, issueID, owner, repo string, expectedStatus int) {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
req := NewRequestWithValues(t, "POST", path.Join(owner, repo, "issues", "projects"), map[string]string{
|
||||||
|
"issue_ids": issueID,
|
||||||
|
"id": projectID,
|
||||||
|
})
|
||||||
|
session.MakeRequest(t, req, expectedStatus)
|
||||||
|
}
|
||||||
|
|
||||||
|
// User
|
||||||
|
t.Run("UserProjectOn+RepoProjectOff", func(tt *testing.T) {
|
||||||
|
defer tests.PrintCurrentTest(tt)()
|
||||||
|
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 4})
|
||||||
|
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 5})
|
||||||
|
|
||||||
|
session := loginUser(tt, user.Name)
|
||||||
|
issue, _, issueID := newTestIssue(tt, session, user, repo)
|
||||||
|
|
||||||
|
updateIssueProject(tt, session, "1003", issueID, user.Name, repo.Name, http.StatusOK)
|
||||||
|
require.NoError(tt, issue.LoadProject(db.DefaultContext))
|
||||||
|
require.NotNil(tt, issue.Project)
|
||||||
|
require.Equal(tt, int64(1003), issue.Project.ID)
|
||||||
|
})
|
||||||
|
|
||||||
|
// Team 1001 - enabled project unit
|
||||||
|
team := unittest.AssertExistsAndLoadBean(t, &org_model.Team{ID: 1001})
|
||||||
|
require.NoError(t, team.LoadMembers(ctx))
|
||||||
|
require.NoError(t, team.LoadRepositories(ctx))
|
||||||
|
|
||||||
|
user := team.Members[0]
|
||||||
|
repo := team.Repos[0]
|
||||||
|
org := team.GetOrg(ctx)
|
||||||
|
|
||||||
|
session := loginUser(t, user.Name)
|
||||||
|
|
||||||
|
t.Run("OrgProjectOn+RepoProjectOn", func(tt *testing.T) {
|
||||||
|
defer tests.PrintCurrentTest(tt)()
|
||||||
|
issue, _, issueID := newTestIssue(tt, session, org.AsUser(), repo)
|
||||||
|
|
||||||
|
updateIssueProject(tt, session, "1001", issueID, org.Name, repo.Name, http.StatusOK)
|
||||||
|
|
||||||
|
require.NoError(tt, issue.LoadProject(db.DefaultContext))
|
||||||
|
require.NotNil(tt, issue.Project)
|
||||||
|
require.Equal(tt, int64(1001), issue.Project.ID)
|
||||||
|
})
|
||||||
|
|
||||||
|
// Disable repository project unit
|
||||||
|
require.NoError(t, repo_service.UpdateRepositoryUnits(ctx, repo, nil, []unit_model.Type{unit_model.TypeProjects}))
|
||||||
|
t.Run("OrgProjectOn+RepoProjectOff", func(tt *testing.T) {
|
||||||
|
defer tests.PrintCurrentTest(tt)()
|
||||||
|
issue, _, issueID := newTestIssue(tt, session, org.AsUser(), repo)
|
||||||
|
|
||||||
|
updateIssueProject(tt, session, "1001", issueID, org.Name, repo.Name, http.StatusOK)
|
||||||
|
require.NoError(tt, issue.LoadProject(db.DefaultContext))
|
||||||
|
require.NotNil(tt, issue.Project)
|
||||||
|
require.Equal(tt, int64(1001), issue.Project.ID)
|
||||||
|
})
|
||||||
|
|
||||||
|
// Team 1002 - disabled project unit
|
||||||
|
team = unittest.AssertExistsAndLoadBean(t, &org_model.Team{ID: 1002})
|
||||||
|
require.NoError(t, team.LoadMembers(ctx))
|
||||||
|
require.NoError(t, team.LoadRepositories(ctx))
|
||||||
|
|
||||||
|
user = team.Members[0]
|
||||||
|
repo = team.Repos[0]
|
||||||
|
org = team.GetOrg(ctx)
|
||||||
|
|
||||||
|
session = loginUser(t, user.Name)
|
||||||
|
|
||||||
|
t.Run("OrgProjectOff+RepoProjectOn", func(tt *testing.T) {
|
||||||
|
defer tests.PrintCurrentTest(tt)()
|
||||||
|
issue, _, issueID := newTestIssue(tt, session, org.AsUser(), repo)
|
||||||
|
|
||||||
|
updateIssueProject(tt, session, "1002", issueID, org.Name, repo.Name, http.StatusOK)
|
||||||
|
require.NoError(tt, issue.LoadProject(db.DefaultContext))
|
||||||
|
require.NotNil(tt, issue.Project)
|
||||||
|
require.Equal(tt, int64(1002), issue.Project.ID)
|
||||||
|
})
|
||||||
|
|
||||||
|
// Disable repository project unit
|
||||||
|
require.NoError(t, repo_service.UpdateRepositoryUnits(ctx, repo, nil, []unit_model.Type{unit_model.TypeProjects}))
|
||||||
|
t.Run("OrgProjectOff+RepoProjectOff", func(tt *testing.T) {
|
||||||
|
defer tests.PrintCurrentTest(tt)()
|
||||||
|
issue, _, issueID := newTestIssue(tt, session, org.AsUser(), repo)
|
||||||
|
|
||||||
|
updateIssueProject(tt, session, "1002", issueID, org.Name, repo.Name, http.StatusOK)
|
||||||
|
require.NoError(tt, issue.LoadProject(db.DefaultContext))
|
||||||
|
require.NotNil(tt, issue.Project)
|
||||||
|
require.Equal(tt, int64(1002), issue.Project.ID)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue