Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix various problems around projects board view #30696

Merged
merged 74 commits into from May 8, 2024
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
Show all changes
74 commits
Select commit Hold shift + click to select a range
ad1095a
Fix moving column start multiple POST requests and default column wil…
lunny Apr 25, 2024
50a573f
Fix lint
lunny Apr 25, 2024
75d1031
Merge branch 'main' into lunny/fix_move_column
lunny Apr 25, 2024
753608a
Use star as defult column's icon but not a pin and add a star icon on…
lunny Apr 25, 2024
c041e36
Fix repository project sorting
lunny Apr 25, 2024
42bfa04
Fix lint
lunny Apr 25, 2024
715ec4c
Update web_src/js/features/repo-projects.js
lunny Apr 25, 2024
0610385
Update templates/projects/view.tmpl
lunny Apr 26, 2024
1c67fe6
Remove default column icon
lunny Apr 26, 2024
0613d7f
Add the issue to default column when adding to a project
lunny Apr 26, 2024
aeb0bd8
Merge branch 'main' into lunny/fix_move_column
lunny Apr 26, 2024
7bd212a
Merge branch 'lunny/fix_move_column' of github.com:lunny/gitea into l…
lunny Apr 26, 2024
53aea60
Fix lint
lunny Apr 26, 2024
8229bed
Merge branch 'main' into lunny/fix_move_column
lunny Apr 26, 2024
2d078a7
Merge branch 'main' into lunny/fix_move_column
silverwind Apr 26, 2024
d300bfd
Follow maintainers' suggestions
lunny Apr 27, 2024
da7eb69
Merge branch 'main' into lunny/fix_move_column
lunny Apr 27, 2024
1f4852e
Merge branch 'lunny/fix_move_column' of github.com:lunny/gitea into l…
lunny Apr 27, 2024
29d4008
Update models/project/issue.go
lunny Apr 27, 2024
c215806
Fix bug
lunny Apr 28, 2024
b987f00
Merge branch 'main' into lunny/fix_move_column
lunny Apr 28, 2024
3fb24e5
Merge branch 'lunny/fix_move_column' of github.com:lunny/gitea into l…
lunny Apr 28, 2024
6844f6f
Fix function name
lunny Apr 28, 2024
235c9d4
Fix bug
lunny Apr 28, 2024
cca2e95
Follow delvh's suggestion
lunny Apr 29, 2024
f280c9b
Merge branch 'main' into lunny/fix_move_column
lunny Apr 29, 2024
b6fccc0
Follow delvh's suggestion
lunny Apr 30, 2024
833ec56
Merge branch 'main' into lunny/fix_move_column
lunny Apr 30, 2024
81d455f
Use json return but not 500 directly
lunny Apr 30, 2024
03562c7
Add limitation for a project's column to 20
lunny Apr 30, 2024
24c2c13
Reuse method JSONError
lunny Apr 30, 2024
3710ea2
Merge branch 'main' into lunny/fix_move_column
lunny Apr 30, 2024
e3d5531
Fix possible sorting overflow
lunny Apr 30, 2024
b8a8f41
Merge branch 'main' into lunny/fix_move_column
lunny May 1, 2024
db9362d
rename PROJECT_BOARD_CHANGED -> PROJECT_COLUMN_CHANGED
lunny May 2, 2024
891881a
Merge branch 'main' into lunny/fix_move_column
lunny May 2, 2024
2bb832e
Revert "rename PROJECT_BOARD_CHANGED -> PROJECT_COLUMN_CHANGED"
lunny May 2, 2024
e9f58eb
Revert "Reuse method JSONError"
lunny May 2, 2024
85d9fe6
Revert "Use json return but not 500 directly"
lunny May 2, 2024
699c9e6
Follow yp05327's suggestion
lunny May 2, 2024
792ef38
Merge branch 'main' into lunny/fix_move_column
lunny May 2, 2024
f4dd172
Add tests
lunny May 3, 2024
eadf3bb
Merge branch 'main' into lunny/fix_move_column
lunny May 3, 2024
da28c1c
Revert unnecessary change
lunny May 3, 2024
ed56a0f
Add test for NewBoard
lunny May 3, 2024
b27dde9
recover the changes on test
lunny May 4, 2024
e8e934c
Merge branch 'main' into lunny/fix_move_column
lunny May 4, 2024
cf9ef10
Fix move issues sorting problem and possible tests failure
lunny May 4, 2024
f7f36dd
remove unnecessary wrap
lunny May 4, 2024
cd2350e
Fix bug
lunny May 4, 2024
0aa451b
Fix bug
lunny May 4, 2024
be61c91
Fix bug
lunny May 4, 2024
e0a7867
revert unnecessary change
lunny May 4, 2024
79994ea
Merge branch 'main' into lunny/fix_move_column
lunny May 4, 2024
4ce383f
Use another user
lunny May 4, 2024
e56f89f
Create a project dynamically to avoid override
lunny May 4, 2024
76465a4
Fix tests
lunny May 5, 2024
5514594
Add repo unit for repo4 dynamically in test
lunny May 5, 2024
969cc14
Merge branch 'main' into lunny/fix_move_column
lunny May 5, 2024
21f019b
Fix tests
lunny May 5, 2024
94ea1be
Add some permissions check
lunny May 5, 2024
ca73f6c
Fix project test to recover the changes of global variable
lunny May 5, 2024
f586577
fix sorting
wxiaoguang May 6, 2024
94cb84d
fix
wxiaoguang May 6, 2024
6ecdbb6
fix
wxiaoguang May 6, 2024
68c3fdc
fix
wxiaoguang May 6, 2024
ed67149
Merge branch 'main' into lunny/fix_move_column
wxiaoguang May 6, 2024
104c29b
fix
wxiaoguang May 6, 2024
703d35e
Merge branch 'main' into lunny/fix_move_column
lunny May 7, 2024
b38ebce
Merge branch 'main' into lunny/fix_move_column
lunny May 7, 2024
59b4739
move perm check, fix incorrect column type
wxiaoguang May 8, 2024
2eb705c
Merge branch 'main' into lunny/fix_move_column
GiteaBot May 8, 2024
116bdbc
Merge branch 'main' into lunny/fix_move_column
GiteaBot May 8, 2024
0e6f68f
Merge branch 'main' into lunny/fix_move_column
GiteaBot May 8, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions models/db/engine.go
Expand Up @@ -57,6 +57,7 @@ type Engine interface {
SumInt(bean any, columnName string) (res int64, err error)
Sync(...any) error
Select(string) *xorm.Session
SetExpr(string, any) *xorm.Session
NotIn(string, ...any) *xorm.Session
OrderBy(any, ...any) *xorm.Session
Exist(...any) (bool, error)
Expand Down
28 changes: 20 additions & 8 deletions models/project/board.go
Expand Up @@ -156,6 +156,15 @@ func NewBoard(ctx context.Context, board *Board) error {
return fmt.Errorf("bad color code: %s", board.Color)
}

var maxSorting int8
if _, err := db.GetEngine(ctx).Select("Max(sorting)").Table("project_board").
Where("project_id=?", board.ProjectID).Get(&maxSorting); err != nil {
return err
}
if maxSorting > 0 {
board.Sorting = maxSorting
}

_, err := db.GetEngine(ctx).Insert(board)
return err
}
Expand Down Expand Up @@ -242,17 +251,11 @@ func UpdateBoard(ctx context.Context, board *Board) error {
// GetBoards fetches all boards related to a project
func (p *Project) GetBoards(ctx context.Context) (BoardList, error) {
boards := make([]*Board, 0, 5)

if err := db.GetEngine(ctx).Where("project_id=? AND `default`=?", p.ID, false).OrderBy("sorting").Find(&boards); err != nil {
return nil, err
}

defaultB, err := p.getDefaultBoard(ctx)
if err != nil {
if err := db.GetEngine(ctx).Where("project_id=?", p.ID).OrderBy("sorting").Find(&boards); err != nil {
return nil, err
}

return append([]*Board{defaultB}, boards...), nil
return boards, nil
}

// getDefaultBoard return default board and ensure only one exists
Expand Down Expand Up @@ -316,3 +319,12 @@ func UpdateBoardSorting(ctx context.Context, bs BoardList) error {
return nil
})
}

func GetColumnsByIDs(ctx context.Context, columnsIDs []int64) (BoardList, error) {
lunny marked this conversation as resolved.
Show resolved Hide resolved
columns := make([]*Board, 0, 5)
if err := db.GetEngine(ctx).In("id", columnsIDs).OrderBy("sorting").Find(&columns); err != nil {
return nil, err
}

return columns, nil
}
27 changes: 27 additions & 0 deletions models/project/issue.go
Expand Up @@ -106,3 +106,30 @@ func (b *Board) removeIssues(ctx context.Context) error {
_, err := db.GetEngine(ctx).Exec("UPDATE `project_issue` SET project_board_id = 0 WHERE project_board_id = ? ", b.ID)
return err
}

// MoveColumnsOnProject moves or keeps issues in a column and sorts them inside that column
func MoveColumnsOnProject(ctx context.Context, project *Project, sortedColumnIDs map[int64]int64) error {
return db.WithTx(ctx, func(ctx context.Context) error {
sess := db.GetEngine(ctx)

columnIDs := make([]int64, 0, len(sortedColumnIDs))
for _, columnID := range sortedColumnIDs {
columnIDs = append(columnIDs, columnID)
}
lunny marked this conversation as resolved.
Show resolved Hide resolved
count, err := sess.Table(new(Board)).Where("project_id=?", project.ID).In("id", columnIDs).Count()
if err != nil {
return err
}
if int(count) != len(sortedColumnIDs) {
return fmt.Errorf("all issues have to be added to a project first")
lunny marked this conversation as resolved.
Show resolved Hide resolved
}

for sorting, columnID := range sortedColumnIDs {
_, err = sess.Exec("UPDATE `project_board` SET sorting=? WHERE id=?", sorting, columnID)
if err != nil {
return err
}
}
return nil
})
}
63 changes: 63 additions & 0 deletions routers/web/org/projects.go
Expand Up @@ -678,3 +678,66 @@ func MoveIssues(ctx *context.Context) {

ctx.JSONOK()
}

// MoveColumns moves or keeps columns in a project and sorts them inside that project
func MoveColumns(ctx *context.Context) {
if ctx.Doer == nil {
lunny marked this conversation as resolved.
Show resolved Hide resolved
ctx.JSON(http.StatusForbidden, map[string]string{
"message": "Only signed in users are allowed to perform this action.",
})
return
}

project, err := project_model.GetProjectByID(ctx, ctx.ParamsInt64(":id"))
if err != nil {
ctx.NotFoundOrServerError("GetProjectByID", project_model.IsErrProjectNotExist, err)
return
}
if project.OwnerID != ctx.ContextUser.ID {
ctx.NotFound("InvalidRepoID", nil)
return
}

type movedColumnsForm struct {
Columns []struct {
ColumnID int64 `json:"columnID"`
Sorting int64 `json:"sorting"`
} `json:"columns"`
}

form := &movedColumnsForm{}
if err = json.NewDecoder(ctx.Req.Body).Decode(&form); err != nil {
ctx.ServerError("DecodeMovedColumnsForm", err)
}
lunny marked this conversation as resolved.
Show resolved Hide resolved

columnIDs := make([]int64, 0, len(form.Columns))
sortedColumnIDs := make(map[int64]int64)
for _, column := range form.Columns {
columnIDs = append(columnIDs, column.ColumnID)
sortedColumnIDs[column.Sorting] = column.ColumnID
}
movedColumns, err := project_model.GetColumnsByIDs(ctx, columnIDs)
if err != nil {
ctx.NotFoundOrServerError("GetColumnsByIDs", issues_model.IsErrIssueNotExist, err)
return
}

if len(movedColumns) != len(form.Columns) {
ctx.ServerError("some columns do not exist", errors.New("some columns do not exist"))
return
}

for _, column := range movedColumns {
if column.ProjectID != project.ID {
ctx.ServerError("Some column's projectID is not equal to project's ID", errors.New("Some column's projectID is not equal to project's ID"))
return
}
}

if err = project_model.MoveColumnsOnProject(ctx, project, sortedColumnIDs); err != nil {
ctx.ServerError("MoveColumnsOnProject", err)
return
}

ctx.JSONOK()
}
67 changes: 67 additions & 0 deletions routers/web/repo/projects.go
Expand Up @@ -666,3 +666,70 @@ func MoveIssues(ctx *context.Context) {

ctx.JSONOK()
}

// MoveColumns moves or keeps columns in a project and sorts them inside that project
func MoveColumns(ctx *context.Context) {
lunny marked this conversation as resolved.
Show resolved Hide resolved
if ctx.Doer == nil {
ctx.JSON(http.StatusForbidden, map[string]string{
"message": "Only signed in users are allowed to perform this action.",
})
return
}

project, err := project_model.GetProjectByID(ctx, ctx.ParamsInt64(":id"))
if err != nil {
if project_model.IsErrProjectNotExist(err) {
ctx.NotFound("ProjectNotExist", nil)
} else {
ctx.ServerError("GetProjectByID", err)
}
return
}
if project.RepoID != ctx.Repo.Repository.ID {
ctx.NotFound("InvalidRepoID", nil)
return
}

type movedColumnsForm struct {
Columns []struct {
ColumnID int64 `json:"columnID"`
Sorting int64 `json:"sorting"`
} `json:"columns"`
}

form := &movedColumnsForm{}
if err = json.NewDecoder(ctx.Req.Body).Decode(&form); err != nil {
ctx.ServerError("DecodeMovedColumnsForm", err)
}

columnIDs := make([]int64, 0, len(form.Columns))
sortedColumnIDs := make(map[int64]int64)
for _, column := range form.Columns {
columnIDs = append(columnIDs, column.ColumnID)
sortedColumnIDs[column.Sorting] = column.ColumnID
}
movedColumns, err := project_model.GetColumnsByIDs(ctx, columnIDs)
if err != nil {
ctx.NotFoundOrServerError("GetColumnsByIDs", issues_model.IsErrIssueNotExist, err)
return
}

if len(movedColumns) != len(form.Columns) {
ctx.ServerError("some columns do not exist", errors.New("some columns do not exist"))
return
}

for _, column := range movedColumns {
if column.ProjectID != project.ID {
ctx.ServerError("Some column's projectID is not equal to project's ID", errors.New("Some column's projectID is not equal to project's ID"))
return
}
}

if err = project_model.MoveColumnsOnProject(ctx, project, sortedColumnIDs); err != nil {
ctx.ServerError("MoveColumnsOnProject", err)
return
}

ctx.JSONOK()
}
2 changes: 2 additions & 0 deletions routers/web/web.go
Expand Up @@ -999,6 +999,7 @@ func registerRoutes(m *web.Route) {
m.Post("/new", web.Bind(forms.CreateProjectForm{}), org.NewProjectPost)
m.Group("/{id}", func() {
m.Post("", web.Bind(forms.EditProjectBoardForm{}), org.AddBoardToProjectPost)
m.Post("/move", org.MoveColumns)
m.Post("/delete", org.DeleteProject)

m.Get("/edit", org.RenderEditProject)
Expand Down Expand Up @@ -1354,6 +1355,7 @@ func registerRoutes(m *web.Route) {
m.Post("/new", web.Bind(forms.CreateProjectForm{}), repo.NewProjectPost)
m.Group("/{id}", func() {
m.Post("", web.Bind(forms.EditProjectBoardForm{}), repo.AddBoardToProjectPost)
m.Post("/move", repo.MoveColumns)
m.Post("/delete", repo.DeleteProject)

m.Get("/edit", repo.RenderEditProject)
Expand Down
6 changes: 3 additions & 3 deletions templates/projects/view.tmpl
Expand Up @@ -64,15 +64,15 @@
</div>

<div id="project-board">
<div class="board {{if .CanWriteProjects}}sortable{{end}}">
<div class="board {{if .CanWriteProjects}}sortable{{end}}"{{if .CanWriteProjects}} data-url="{{$.Link}}/move"{{end}}>
lunny marked this conversation as resolved.
Show resolved Hide resolved
{{range .Columns}}
<div class="ui segment project-column"{{if .Color}} style="background: {{.Color}} !important; color: {{ContrastColor .Color}} !important"{{end}} data-id="{{.ID}}" data-sorting="{{.Sorting}}" data-url="{{$.Link}}/{{.ID}}">
<div class="project-column-header{{if $canWriteProject}} tw-cursor-grab{{end}}">
<div class="ui large label project-column-title tw-py-1">
<div class="ui small circular grey label project-column-issue-count">
{{.NumIssues ctx}}
</div>
<span class="project-column-title-label">{{.Title}}</span>
<span class="project-column-title-label">{{.Title}}{{if .Default}}{{svg "octicon-star"}}{{end}}</span>
lunny marked this conversation as resolved.
Show resolved Hide resolved
</div>
{{if $canWriteProject}}
<div class="ui dropdown jump item">
Expand All @@ -90,7 +90,7 @@
data-modal-default-project-column-header="{{ctx.Locale.Tr "repo.projects.column.set_default"}}"
data-modal-default-project-column-content="{{ctx.Locale.Tr "repo.projects.column.set_default_desc"}}"
data-url="{{$.Link}}/{{.ID}}/default">
{{svg "octicon-pin"}}
{{svg "octicon-star"}}
{{ctx.Locale.Tr "repo.projects.column.set_default"}}
</a>
<a class="item show-modal button show-delete-project-column-modal"
Expand Down
26 changes: 14 additions & 12 deletions web_src/js/features/repo-projects.js
Expand Up @@ -2,7 +2,6 @@ import $ from 'jquery';
import {contrastColor} from '../utils/color.js';
import {createSortable} from '../modules/sortable.js';
import {POST, DELETE, PUT} from '../modules/fetch.js';
import tinycolor from 'tinycolor2';

function updateIssueCount(cards) {
const parent = cards.parentElement;
Expand Down Expand Up @@ -63,17 +62,20 @@ async function initRepoProjectSortable() {
delay: 500,
onSort: async () => {
boardColumns = mainBoard.getElementsByClassName('project-column');
for (let i = 0; i < boardColumns.length; i++) {
const column = boardColumns[i];
if (parseInt(column.getAttribute('data-sorting')) !== i) {
try {
const bgColor = column.style.backgroundColor; // will be rgb() string
const color = bgColor ? tinycolor(bgColor).toHexString() : '';
await PUT(column.getAttribute('data-url'), {data: {sorting: i, color}});
} catch (error) {
console.error(error);
}
}

const columnSorting = {
columns: Array.from(boardColumns, (column, i) => ({
columnID: parseInt(column.getAttribute('data-id')),
sorting: i,
})),
};

try {
await POST(mainBoard.getAttribute('data-url'), {
data: columnSorting,
});
lunny marked this conversation as resolved.
Show resolved Hide resolved
} catch (error) {
console.error(error);
silverwind marked this conversation as resolved.
Show resolved Hide resolved
}
},
});
Expand Down