mirror of https://github.com/go-gitea/gitea.git
Merge 3d1e9167fc
into 982b20d259
This commit is contained in:
commit
9b5cdbde2a
|
@ -30,6 +30,7 @@ const (
|
|||
SearchOrderByStarsReverse SearchOrderBy = "num_stars DESC"
|
||||
SearchOrderByForks SearchOrderBy = "num_forks ASC"
|
||||
SearchOrderByForksReverse SearchOrderBy = "num_forks DESC"
|
||||
SearchOrderByTitle SearchOrderBy = "title ASC"
|
||||
)
|
||||
|
||||
const (
|
||||
|
|
|
@ -103,14 +103,14 @@ type Issue struct {
|
|||
PosterID int64 `xorm:"INDEX"`
|
||||
Poster *user_model.User `xorm:"-"`
|
||||
OriginalAuthor string
|
||||
OriginalAuthorID int64 `xorm:"index"`
|
||||
Title string `xorm:"name"`
|
||||
Content string `xorm:"LONGTEXT"`
|
||||
RenderedContent template.HTML `xorm:"-"`
|
||||
Labels []*Label `xorm:"-"`
|
||||
MilestoneID int64 `xorm:"INDEX"`
|
||||
Milestone *Milestone `xorm:"-"`
|
||||
Project *project_model.Project `xorm:"-"`
|
||||
OriginalAuthorID int64 `xorm:"index"`
|
||||
Title string `xorm:"name"`
|
||||
Content string `xorm:"LONGTEXT"`
|
||||
RenderedContent template.HTML `xorm:"-"`
|
||||
Labels []*Label `xorm:"-"`
|
||||
MilestoneID int64 `xorm:"INDEX"`
|
||||
Milestone *Milestone `xorm:"-"`
|
||||
Projects []*project_model.Project `xorm:"-"`
|
||||
Priority int
|
||||
AssigneeID int64 `xorm:"-"`
|
||||
Assignee *user_model.User `xorm:"-"`
|
||||
|
@ -311,7 +311,7 @@ func (issue *Issue) LoadAttributes(ctx context.Context) (err error) {
|
|||
return err
|
||||
}
|
||||
|
||||
if err = issue.LoadProject(ctx); err != nil {
|
||||
if err = issue.LoadProjects(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
|
|
@ -251,14 +251,19 @@ func (issues IssueList) LoadProjects(ctx context.Context) error {
|
|||
return err
|
||||
}
|
||||
for _, project := range projects {
|
||||
projectMaps[project.IssueID] = project.Project
|
||||
projectMaps[project.ID] = project.Project
|
||||
}
|
||||
left -= limit
|
||||
issueIDs = issueIDs[limit:]
|
||||
}
|
||||
|
||||
for _, issue := range issues {
|
||||
issue.Project = projectMaps[issue.ID]
|
||||
projectIDs := issue.projectIDs(ctx)
|
||||
for _, i := range projectIDs {
|
||||
if projectMaps[i] != nil {
|
||||
issue.Projects = append(issue.Projects, projectMaps[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -66,10 +66,10 @@ func TestIssueList_LoadAttributes(t *testing.T) {
|
|||
}
|
||||
if issue.ID == int64(1) {
|
||||
assert.Equal(t, int64(400), issue.TotalTrackedTime)
|
||||
assert.NotNil(t, issue.Project)
|
||||
assert.Equal(t, int64(1), issue.Project.ID)
|
||||
assert.NotNil(t, issue.Projects)
|
||||
assert.Equal(t, int64(1), issue.Projects[0].ID)
|
||||
} else {
|
||||
assert.Nil(t, issue.Project)
|
||||
assert.Nil(t, issue.Projects)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,28 +13,21 @@ import (
|
|||
)
|
||||
|
||||
// LoadProject load the project the issue was assigned to
|
||||
func (issue *Issue) LoadProject(ctx context.Context) (err error) {
|
||||
if issue.Project == nil {
|
||||
var p project_model.Project
|
||||
has, err := db.GetEngine(ctx).Table("project").
|
||||
func (issue *Issue) LoadProjects(ctx context.Context) (err error) {
|
||||
if issue.Projects == nil {
|
||||
err = db.GetEngine(ctx).Table("project").
|
||||
Join("INNER", "project_issue", "project.id=project_issue.project_id").
|
||||
Where("project_issue.issue_id = ?", issue.ID).Get(&p)
|
||||
if err != nil {
|
||||
return err
|
||||
} else if has {
|
||||
issue.Project = &p
|
||||
}
|
||||
Where("project_issue.issue_id = ?", issue.ID).Find(&issue.Projects)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (issue *Issue) projectID(ctx context.Context) int64 {
|
||||
var ip project_model.ProjectIssue
|
||||
has, err := db.GetEngine(ctx).Where("issue_id=?", issue.ID).Get(&ip)
|
||||
if err != nil || !has {
|
||||
return 0
|
||||
func (issue *Issue) projectIDs(ctx context.Context) []int64 {
|
||||
var ips []int64
|
||||
if err := db.GetEngine(ctx).Table("project_issue").Select("project_id").Where("issue_id=?", issue.ID).Find(&ips); err != nil {
|
||||
return nil
|
||||
}
|
||||
return ip.ProjectID
|
||||
return ips
|
||||
}
|
||||
|
||||
// ProjectBoardID return project board id if issue was assigned to one
|
||||
|
@ -91,24 +84,25 @@ func LoadIssuesFromBoardList(ctx context.Context, bs project_model.BoardList) (m
|
|||
}
|
||||
|
||||
// ChangeProjectAssign changes the project associated with an issue
|
||||
func ChangeProjectAssign(ctx context.Context, issue *Issue, doer *user_model.User, newProjectID int64) error {
|
||||
func ChangeProjectAssign(ctx context.Context, issue *Issue, doer *user_model.User, newProjectID int64, action string) error {
|
||||
ctx, committer, err := db.TxContext(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer committer.Close()
|
||||
|
||||
if err := addUpdateIssueProject(ctx, issue, doer, newProjectID); err != nil {
|
||||
if err := addUpdateIssueProject(ctx, issue, doer, newProjectID, action); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return committer.Commit()
|
||||
}
|
||||
|
||||
func addUpdateIssueProject(ctx context.Context, issue *Issue, doer *user_model.User, newProjectID int64) error {
|
||||
oldProjectID := issue.projectID(ctx)
|
||||
func addUpdateIssueProject(ctx context.Context, issue *Issue, doer *user_model.User, newProjectID int64, action string) error {
|
||||
var oldProjectIDs []int64
|
||||
var err error
|
||||
|
||||
if err := issue.LoadRepo(ctx); err != nil {
|
||||
if err = issue.LoadRepo(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -123,25 +117,51 @@ func addUpdateIssueProject(ctx context.Context, issue *Issue, doer *user_model.U
|
|||
}
|
||||
}
|
||||
|
||||
if _, err := db.GetEngine(ctx).Where("project_issue.issue_id=?", issue.ID).Delete(&project_model.ProjectIssue{}); err != nil {
|
||||
return err
|
||||
if action == "null" {
|
||||
if newProjectID == 0 {
|
||||
action = "clear"
|
||||
} else {
|
||||
action = "attach"
|
||||
count, err := db.GetEngine(ctx).Table("project_issue").Where("issue_id=? AND project_id=?", issue.ID, newProjectID).Count()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if count > 0 {
|
||||
action = "detach"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if oldProjectID > 0 || newProjectID > 0 {
|
||||
if action == "attach" {
|
||||
err = db.Insert(ctx, &project_model.ProjectIssue{
|
||||
IssueID: issue.ID,
|
||||
ProjectID: newProjectID,
|
||||
})
|
||||
oldProjectIDs = append(oldProjectIDs, 0)
|
||||
} else if action == "detach" {
|
||||
_, err = db.GetEngine(ctx).Where("issue_id=? AND project_id=?", issue.ID, newProjectID).Delete(&project_model.ProjectIssue{})
|
||||
oldProjectIDs = append(oldProjectIDs, newProjectID)
|
||||
newProjectID = 0
|
||||
} else if action == "clear" {
|
||||
if err = db.GetEngine(ctx).Table("project_issue").Select("project_id").Where("issue_id=?", issue.ID).Find(&oldProjectIDs); err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = db.GetEngine(ctx).Where("issue_id=?", issue.ID).Delete(&project_model.ProjectIssue{})
|
||||
newProjectID = 0
|
||||
}
|
||||
|
||||
for i := range oldProjectIDs {
|
||||
if _, err := CreateComment(ctx, &CreateCommentOptions{
|
||||
Type: CommentTypeProject,
|
||||
Doer: doer,
|
||||
Repo: issue.Repo,
|
||||
Issue: issue,
|
||||
OldProjectID: oldProjectID,
|
||||
OldProjectID: oldProjectIDs[i],
|
||||
ProjectID: newProjectID,
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return db.Insert(ctx, &project_model.ProjectIssue{
|
||||
IssueID: issue.ID,
|
||||
ProjectID: newProjectID,
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -174,6 +174,8 @@ func applyProjectBoardCondition(sess *xorm.Session, opts *IssuesOptions) *xorm.S
|
|||
// do not need to apply any condition
|
||||
if opts.ProjectBoardID > 0 {
|
||||
sess.In("issue.id", builder.Select("issue_id").From("project_issue").Where(builder.Eq{"project_board_id": opts.ProjectBoardID}))
|
||||
} else if opts.ProjectID > 0 {
|
||||
sess.In("issue.id", builder.Select("issue_id").From("project_issue").Where(builder.Eq{"project_board_id": 0, "project_id": opts.ProjectID}))
|
||||
} else if opts.ProjectBoardID == db.NoConditionID {
|
||||
sess.In("issue.id", builder.Select("issue_id").From("project_issue").Where(builder.Eq{"project_board_id": 0}))
|
||||
}
|
||||
|
|
|
@ -418,10 +418,10 @@ func TestIssueLoadAttributes(t *testing.T) {
|
|||
}
|
||||
if issue.ID == int64(1) {
|
||||
assert.Equal(t, int64(400), issue.TotalTrackedTime)
|
||||
assert.NotNil(t, issue.Project)
|
||||
assert.Equal(t, int64(1), issue.Project.ID)
|
||||
assert.NotNil(t, issue.Projects)
|
||||
assert.Equal(t, int64(1), issue.Projects[0].ID)
|
||||
} else {
|
||||
assert.Nil(t, issue.Project)
|
||||
assert.Nil(t, issue.Projects)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -76,7 +76,7 @@ func (p *Project) NumOpenIssues(ctx context.Context) int {
|
|||
}
|
||||
|
||||
// MoveIssuesOnProjectBoard moves or keeps issues in a column and sorts them inside that column
|
||||
func MoveIssuesOnProjectBoard(ctx context.Context, board *Board, sortedIssueIDs map[int64]int64) error {
|
||||
func MoveIssuesOnProjectBoard(ctx context.Context, board *Board, sortedIssueIDs map[int64]int64, projectID int64) error {
|
||||
return db.WithTx(ctx, func(ctx context.Context) error {
|
||||
sess := db.GetEngine(ctx)
|
||||
|
||||
|
@ -93,7 +93,7 @@ func MoveIssuesOnProjectBoard(ctx context.Context, board *Board, sortedIssueIDs
|
|||
}
|
||||
|
||||
for sorting, issueID := range sortedIssueIDs {
|
||||
_, err = sess.Exec("UPDATE `project_issue` SET project_board_id=?, sorting=? WHERE issue_id=?", board.ID, sorting, issueID)
|
||||
_, err = sess.Exec("UPDATE `project_issue` SET project_board_id=?, sorting=? WHERE issue_id=? AND project_id=?", board.ID, sorting, issueID, projectID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -237,6 +237,8 @@ func GetSearchOrderByBySortType(sortType string) db.SearchOrderBy {
|
|||
return db.SearchOrderByRecentUpdated
|
||||
case "leastupdate":
|
||||
return db.SearchOrderByLeastUpdated
|
||||
case "title":
|
||||
return db.SearchOrderByTitle
|
||||
default:
|
||||
return db.SearchOrderByNewest
|
||||
}
|
||||
|
|
|
@ -23,7 +23,7 @@ import (
|
|||
const (
|
||||
issueIndexerAnalyzer = "issueIndexer"
|
||||
issueIndexerDocType = "issueIndexerDocType"
|
||||
issueIndexerLatestVersion = 4
|
||||
issueIndexerLatestVersion = 5
|
||||
)
|
||||
|
||||
const unicodeNormalizeName = "unicodeNormalize"
|
||||
|
@ -78,7 +78,8 @@ func generateIssueIndexMapping() (mapping.IndexMapping, error) {
|
|||
docMapping.AddFieldMappingsAt("label_ids", numberFieldMapping)
|
||||
docMapping.AddFieldMappingsAt("no_label", boolFieldMapping)
|
||||
docMapping.AddFieldMappingsAt("milestone_id", numberFieldMapping)
|
||||
docMapping.AddFieldMappingsAt("project_id", numberFieldMapping)
|
||||
docMapping.AddFieldMappingsAt("project_ids", numberFieldMapping)
|
||||
docMapping.AddFieldMappingsAt("no_project", boolFieldMapping)
|
||||
docMapping.AddFieldMappingsAt("project_board_id", numberFieldMapping)
|
||||
docMapping.AddFieldMappingsAt("poster_id", numberFieldMapping)
|
||||
docMapping.AddFieldMappingsAt("assignee_id", numberFieldMapping)
|
||||
|
@ -222,7 +223,11 @@ func (b *Indexer) Search(ctx context.Context, options *internal.SearchOptions) (
|
|||
}
|
||||
|
||||
if options.ProjectID.Has() {
|
||||
queries = append(queries, inner_bleve.NumericEqualityQuery(options.ProjectID.Value(), "project_id"))
|
||||
if v := options.ProjectID.Value(); v != 0 {
|
||||
queries = append(queries, inner_bleve.NumericEqualityQuery(v, "project_ids"))
|
||||
} else {
|
||||
queries = append(queries, inner_bleve.BoolFieldQuery(true, "no_project"))
|
||||
}
|
||||
}
|
||||
if options.ProjectBoardID.Has() {
|
||||
queries = append(queries, inner_bleve.NumericEqualityQuery(options.ProjectBoardID.Value(), "project_board_id"))
|
||||
|
|
|
@ -18,7 +18,7 @@ import (
|
|||
)
|
||||
|
||||
const (
|
||||
issueIndexerLatestVersion = 1
|
||||
issueIndexerLatestVersion = 2
|
||||
// multi-match-types, currently only 2 types are used
|
||||
// Reference: https://www.elastic.co/guide/en/elasticsearch/reference/7.0/query-dsl-multi-match-query.html#multi-match-types
|
||||
esMultiMatchTypeBestFields = "best_fields"
|
||||
|
@ -61,7 +61,8 @@ const (
|
|||
"label_ids": { "type": "integer", "index": true },
|
||||
"no_label": { "type": "boolean", "index": true },
|
||||
"milestone_id": { "type": "integer", "index": true },
|
||||
"project_id": { "type": "integer", "index": true },
|
||||
"project_ids": { "type": "integer", "index": true },
|
||||
"no_project": { "type": "boolean", "index": true },
|
||||
"project_board_id": { "type": "integer", "index": true },
|
||||
"poster_id": { "type": "integer", "index": true },
|
||||
"assignee_id": { "type": "integer", "index": true },
|
||||
|
@ -195,7 +196,11 @@ func (b *Indexer) Search(ctx context.Context, options *internal.SearchOptions) (
|
|||
}
|
||||
|
||||
if options.ProjectID.Has() {
|
||||
query.Must(elastic.NewTermQuery("project_id", options.ProjectID.Value()))
|
||||
if v := options.ProjectID.Value(); v != 0 {
|
||||
query.Must(elastic.NewTermQuery("project_ids", v))
|
||||
} else {
|
||||
query.Must(elastic.NewTermQuery("no_project", true))
|
||||
}
|
||||
}
|
||||
if options.ProjectBoardID.Has() {
|
||||
query.Must(elastic.NewTermQuery("project_board_id", options.ProjectBoardID.Value()))
|
||||
|
|
|
@ -361,12 +361,6 @@ func searchIssueInProject(t *testing.T) {
|
|||
opts SearchOptions
|
||||
expectedIDs []int64
|
||||
}{
|
||||
{
|
||||
SearchOptions{
|
||||
ProjectID: optional.Some(int64(1)),
|
||||
},
|
||||
[]int64{5, 3, 2, 1},
|
||||
},
|
||||
{
|
||||
SearchOptions{
|
||||
ProjectBoardID: optional.Some(int64(1)),
|
||||
|
|
|
@ -26,7 +26,8 @@ type IndexerData struct {
|
|||
LabelIDs []int64 `json:"label_ids"`
|
||||
NoLabel bool `json:"no_label"` // True if LabelIDs is empty
|
||||
MilestoneID int64 `json:"milestone_id"`
|
||||
ProjectID int64 `json:"project_id"`
|
||||
ProjectIDs []int64 `json:"project_ids"`
|
||||
NoProject bool `json:"no_project"` // True if ProjectIDs is empty
|
||||
ProjectBoardID int64 `json:"project_board_id"`
|
||||
PosterID int64 `json:"poster_id"`
|
||||
AssigneeID int64 `json:"assignee_id"`
|
||||
|
@ -89,7 +90,7 @@ type SearchOptions struct {
|
|||
|
||||
MilestoneIDs []int64 // milestones the issues have
|
||||
|
||||
ProjectID optional.Option[int64] // project the issues belong to
|
||||
ProjectID optional.Option[int64] // project the issues belong to, zero means no project
|
||||
ProjectBoardID optional.Option[int64] // project board the issues belong to
|
||||
|
||||
PosterID optional.Option[int64] // poster of the issues
|
||||
|
|
|
@ -312,10 +312,10 @@ var cases = []*testIndexerCase{
|
|||
Expected: func(t *testing.T, data map[int64]*internal.IndexerData, result *internal.SearchResult) {
|
||||
assert.Equal(t, 5, len(result.Hits))
|
||||
for _, v := range result.Hits {
|
||||
assert.Equal(t, int64(1), data[v.ID].ProjectID)
|
||||
assert.Contains(t, data[v.ID].ProjectIDs, int64(1))
|
||||
}
|
||||
assert.Equal(t, countIndexerData(data, func(v *internal.IndexerData) bool {
|
||||
return v.ProjectID == 1
|
||||
return slices.Contains(v.ProjectIDs, 1)
|
||||
}), result.Total)
|
||||
},
|
||||
},
|
||||
|
@ -330,10 +330,10 @@ var cases = []*testIndexerCase{
|
|||
Expected: func(t *testing.T, data map[int64]*internal.IndexerData, result *internal.SearchResult) {
|
||||
assert.Equal(t, 5, len(result.Hits))
|
||||
for _, v := range result.Hits {
|
||||
assert.Equal(t, int64(0), data[v.ID].ProjectID)
|
||||
assert.Empty(t, data[v.ID].ProjectIDs)
|
||||
}
|
||||
assert.Equal(t, countIndexerData(data, func(v *internal.IndexerData) bool {
|
||||
return v.ProjectID == 0
|
||||
return len(v.ProjectIDs) == 0
|
||||
}), result.Total)
|
||||
},
|
||||
},
|
||||
|
@ -692,6 +692,10 @@ func generateDefaultIndexerData() []*internal.IndexerData {
|
|||
for i := range subscriberIDs {
|
||||
subscriberIDs[i] = int64(i) + 1 // SubscriberID should not be 0
|
||||
}
|
||||
projectIDs := make([]int64, id%5)
|
||||
for i := range projectIDs {
|
||||
projectIDs[i] = int64(i) + 1 // ProjectID should not be 0
|
||||
}
|
||||
|
||||
data = append(data, &internal.IndexerData{
|
||||
ID: id,
|
||||
|
@ -705,7 +709,8 @@ func generateDefaultIndexerData() []*internal.IndexerData {
|
|||
LabelIDs: labelIDs,
|
||||
NoLabel: len(labelIDs) == 0,
|
||||
MilestoneID: issueIndex % 4,
|
||||
ProjectID: issueIndex % 5,
|
||||
ProjectIDs: projectIDs,
|
||||
NoProject: len(projectIDs) == 0,
|
||||
ProjectBoardID: issueIndex % 6,
|
||||
PosterID: id%10 + 1, // PosterID should not be 0
|
||||
AssigneeID: issueIndex % 10,
|
||||
|
|
|
@ -18,7 +18,7 @@ import (
|
|||
)
|
||||
|
||||
const (
|
||||
issueIndexerLatestVersion = 3
|
||||
issueIndexerLatestVersion = 4
|
||||
|
||||
// TODO: make this configurable if necessary
|
||||
maxTotalHits = 10000
|
||||
|
@ -64,7 +64,8 @@ func NewIndexer(url, apiKey, indexerName string) *Indexer {
|
|||
"label_ids",
|
||||
"no_label",
|
||||
"milestone_id",
|
||||
"project_id",
|
||||
"project_ids",
|
||||
"no_project",
|
||||
"project_board_id",
|
||||
"poster_id",
|
||||
"assignee_id",
|
||||
|
@ -172,7 +173,11 @@ func (b *Indexer) Search(ctx context.Context, options *internal.SearchOptions) (
|
|||
}
|
||||
|
||||
if options.ProjectID.Has() {
|
||||
query.And(inner_meilisearch.NewFilterEq("project_id", options.ProjectID.Value()))
|
||||
if v := options.ProjectID.Value(); v != 0 {
|
||||
query.And(inner_meilisearch.NewFilterEq("project_ids", v))
|
||||
} else {
|
||||
query.And(inner_meilisearch.NewFilterEq("no_label", true))
|
||||
}
|
||||
}
|
||||
if options.ProjectBoardID.Has() {
|
||||
query.And(inner_meilisearch.NewFilterEq("project_board_id", options.ProjectBoardID.Value()))
|
||||
|
|
|
@ -87,9 +87,9 @@ func getIssueIndexerData(ctx context.Context, issueID int64) (*internal.IndexerD
|
|||
return nil, false, err
|
||||
}
|
||||
|
||||
var projectID int64
|
||||
if issue.Project != nil {
|
||||
projectID = issue.Project.ID
|
||||
projectIDs := make([]int64, 0, len(issue.Projects))
|
||||
for _, project := range issue.Projects {
|
||||
projectIDs = append(projectIDs, project.ID)
|
||||
}
|
||||
|
||||
return &internal.IndexerData{
|
||||
|
@ -104,7 +104,8 @@ func getIssueIndexerData(ctx context.Context, issueID int64) (*internal.IndexerD
|
|||
LabelIDs: labels,
|
||||
NoLabel: len(labels) == 0,
|
||||
MilestoneID: issue.MilestoneID,
|
||||
ProjectID: projectID,
|
||||
ProjectIDs: projectIDs,
|
||||
NoProject: len(projectIDs) == 0,
|
||||
ProjectBoardID: issue.ProjectBoardID(ctx),
|
||||
PosterID: issue.PosterID,
|
||||
AssigneeID: issue.AssigneeID,
|
||||
|
|
|
@ -442,14 +442,9 @@ func UpdateIssueProject(ctx *context.Context) {
|
|||
}
|
||||
|
||||
projectID := ctx.FormInt64("id")
|
||||
action := ctx.FormString("action")
|
||||
for _, issue := range issues {
|
||||
if issue.Project != nil {
|
||||
if issue.Project.ID == projectID {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
if err := issues_model.ChangeProjectAssign(ctx, issue, ctx.Doer, projectID); err != nil {
|
||||
if err := issues_model.ChangeProjectAssign(ctx, issue, ctx.Doer, projectID, action); err != nil {
|
||||
ctx.ServerError("ChangeProjectAssign", err)
|
||||
return
|
||||
}
|
||||
|
@ -671,7 +666,7 @@ func MoveIssues(ctx *context.Context) {
|
|||
}
|
||||
}
|
||||
|
||||
if err = project_model.MoveIssuesOnProjectBoard(ctx, board, sortedIssueIDs); err != nil {
|
||||
if err = project_model.MoveIssuesOnProjectBoard(ctx, board, sortedIssueIDs, project.ID); err != nil {
|
||||
ctx.ServerError("MoveIssuesOnProjectBoard", err)
|
||||
return
|
||||
}
|
||||
|
|
|
@ -385,14 +385,9 @@ func UpdateIssueProject(ctx *context.Context) {
|
|||
}
|
||||
|
||||
projectID := ctx.FormInt64("id")
|
||||
action := ctx.FormString("action")
|
||||
for _, issue := range issues {
|
||||
if issue.Project != nil {
|
||||
if issue.Project.ID == projectID {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
if err := issues_model.ChangeProjectAssign(ctx, issue, ctx.Doer, projectID); err != nil {
|
||||
if err := issues_model.ChangeProjectAssign(ctx, issue, ctx.Doer, projectID, action); err != nil {
|
||||
ctx.ServerError("ChangeProjectAssign", err)
|
||||
return
|
||||
}
|
||||
|
@ -659,7 +654,7 @@ func MoveIssues(ctx *context.Context) {
|
|||
}
|
||||
}
|
||||
|
||||
if err = project_model.MoveIssuesOnProjectBoard(ctx, board, sortedIssueIDs); err != nil {
|
||||
if err = project_model.MoveIssuesOnProjectBoard(ctx, board, sortedIssueIDs, project.ID); err != nil {
|
||||
ctx.ServerError("MoveIssuesOnProjectBoard", err)
|
||||
return
|
||||
}
|
||||
|
|
|
@ -1334,7 +1334,7 @@ func CompareAndPullRequestPost(ctx *context.Context) {
|
|||
ctx.Error(http.StatusBadRequest, "user hasn't the permission to write to projects")
|
||||
return
|
||||
}
|
||||
if err := issues_model.ChangeProjectAssign(ctx, pullIssue, ctx.Doer, projectID); err != nil {
|
||||
if err := issues_model.ChangeProjectAssign(ctx, pullIssue, ctx.Doer, projectID, "attach"); err != nil {
|
||||
ctx.ServerError("ChangeProjectAssign", err)
|
||||
return
|
||||
}
|
||||
|
|
|
@ -358,7 +358,6 @@ func Issues(ctx *context.Context) {
|
|||
ctx.Status(http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
|
||||
ctx.Data["Title"] = ctx.Tr("issues")
|
||||
ctx.Data["PageIsIssues"] = true
|
||||
buildIssueOverview(ctx, unit.TypeIssues)
|
||||
|
|
|
@ -42,7 +42,7 @@ func NewIssue(ctx context.Context, repo *repo_model.Repository, issue *issues_mo
|
|||
}
|
||||
}
|
||||
if projectID > 0 {
|
||||
if err := issues_model.ChangeProjectAssign(ctx, issue, issue.Poster, projectID); err != nil {
|
||||
if err := issues_model.ChangeProjectAssign(ctx, issue, issue.Poster, projectID, "attach"); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
|
|
@ -154,7 +154,7 @@
|
|||
{{if .IsProjectsEnabled}}
|
||||
<div class="divider"></div>
|
||||
|
||||
<div class="ui {{if or (not .HasIssuesOrPullsWritePermission) .Repository.IsArchived}}disabled{{end}} floating jump select-project dropdown">
|
||||
<div class="ui {{if or (not .HasIssuesOrPullsWritePermission) .Repository.IsArchived}}disabled{{end}} floating jump select-projects dropdown">
|
||||
<a class="text muted flex-text-block">
|
||||
<strong>{{ctx.Locale.Tr "repo.issues.new.projects"}}</strong>
|
||||
{{if and .HasIssuesOrPullsWritePermission (not .Repository.IsArchived)}}
|
||||
|
@ -175,8 +175,19 @@
|
|||
{{ctx.Locale.Tr "repo.issues.new.open_projects"}}
|
||||
</div>
|
||||
{{range .OpenProjects}}
|
||||
<a class="item muted sidebar-item-link" data-id="{{.ID}}" data-href="{{.Link ctx}}">
|
||||
{{$ProjectID := .ID}}
|
||||
{{$checked := false}}
|
||||
{{range $.Issue.Projects}}
|
||||
{{if eq .ID $ProjectID}}
|
||||
{{$checked = true}}
|
||||
{{break}}
|
||||
{{end}}
|
||||
{{end}}
|
||||
<a class="item muted sidebar-item-link{{if $checked}} checked{{end}}" data-id="{{.ID}}" data-href="{{.Link ctx}}">
|
||||
<span class="octicon-check{{if not $checked}} tw-invisible{{end}}">{{svg "octicon-check"}}</span>
|
||||
<span class="text">
|
||||
{{svg .IconName 18 "tw-mr-2"}}{{.Title}}
|
||||
</span>
|
||||
</a>
|
||||
{{end}}
|
||||
{{end}}
|
||||
|
@ -186,20 +197,33 @@
|
|||
{{ctx.Locale.Tr "repo.issues.new.closed_projects"}}
|
||||
</div>
|
||||
{{range .ClosedProjects}}
|
||||
<a class="item muted sidebar-item-link" data-id="{{.ID}}" data-href="{{.Link ctx}}">
|
||||
{{$ProjectID := $.Projects.IssueID}}
|
||||
{{$checked := false}}
|
||||
{{range $.Issue.Projects}}
|
||||
{{if eq .IssueID $ProjectID}}
|
||||
{{$checked = true}}
|
||||
{{break}}
|
||||
{{end}}
|
||||
{{end}}
|
||||
<a class="item muted sidebar-item-link{{if $checked}} checked{{end}}" data-id="{{.ID}}" data-href="{{.Link ctx}}">
|
||||
<span class="octicon-check{{if not $checked}} tw-invisible{{end}}">{{svg "octicon-check"}}</span>
|
||||
<span class="text">
|
||||
{{svg .IconName 18 "tw-mr-2"}}{{.Title}}
|
||||
</span>
|
||||
</a>
|
||||
{{end}}
|
||||
{{end}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="ui select-project list">
|
||||
<span class="no-select item {{if .Issue.Project}}tw-hidden{{end}}">{{ctx.Locale.Tr "repo.issues.new.no_projects"}}</span>
|
||||
<div class="ui projects list">
|
||||
<span class="no-select item {{if .Issue.Projects}}tw-hidden{{end}}">{{ctx.Locale.Tr "repo.issues.new.no_projects"}}</span>
|
||||
<div class="selected">
|
||||
{{if .Issue.Project}}
|
||||
<a class="item muted sidebar-item-link" href="{{.Issue.Project.Link ctx}}">
|
||||
{{svg .Issue.Project.IconName 18 "tw-mr-2"}}{{.Issue.Project.Title}}
|
||||
</a>
|
||||
{{range .Issue.Projects}}
|
||||
<div class="item">
|
||||
<a class="muted sidebar-item-link" href="{{.Link ctx}}">
|
||||
{{svg .IconName 18 "tw-mr-2"}}{{.Title}}
|
||||
</a>
|
||||
</div>
|
||||
{{end}}
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -93,10 +93,10 @@
|
|||
<span class="gt-ellipsis">{{.Milestone.Name}}</span>
|
||||
</a>
|
||||
{{end}}
|
||||
{{if .Project}}
|
||||
<a class="project flex-text-inline tw-max-w-[300px]" href="{{.Project.Link ctx}}">
|
||||
{{svg .Project.IconName 14}}
|
||||
<span class="gt-ellipsis">{{.Project.Title}}</span>
|
||||
{{range .Projects}}
|
||||
<a class="project flex-text-inline tw-max-w-[300px]" href="{{.Link ctx}}">
|
||||
{{svg .IconName 14}}
|
||||
<span class="gt-ellipsis">{{.Title}}</span>
|
||||
</a>
|
||||
{{end}}
|
||||
{{if .Ref}}
|
||||
|
|
|
@ -240,6 +240,7 @@ export function initRepoCommentForm() {
|
|||
|
||||
// Init labels and assignees
|
||||
initListSubmits('select-label', 'labels');
|
||||
initListSubmits('select-projects', 'projects');
|
||||
initListSubmits('select-assignees', 'assignees');
|
||||
initListSubmits('select-assignees-modify', 'assignees');
|
||||
initListSubmits('select-reviewers-modify', 'assignees');
|
||||
|
|
Loading…
Reference in New Issue