@@ -36,6 +36,7 @@ type ProjectBoard struct { | |||
ID int64 `xorm:"pk autoincr"` | |||
Title string | |||
Default bool `xorm:"NOT NULL DEFAULT false"` // issues not assigned to a specific board will be assigned to this board | |||
Sorting int8 `xorm:"DEFAULT 0"` | |||
ProjectID int64 `xorm:"INDEX NOT NULL"` | |||
CreatorID int64 `xorm:"NOT NULL"` | |||
@@ -157,15 +158,24 @@ func getProjectBoard(e Engine, boardID int64) (*ProjectBoard, error) { | |||
return board, nil | |||
} | |||
// UpdateProjectBoard updates the title of a project board | |||
// UpdateProjectBoard updates a project board | |||
func UpdateProjectBoard(board *ProjectBoard) error { | |||
return updateProjectBoard(x, board) | |||
} | |||
func updateProjectBoard(e Engine, board *ProjectBoard) error { | |||
_, err := e.ID(board.ID).Cols( | |||
"title", | |||
).Update(board) | |||
var fieldToUpdate []string | |||
if board.Sorting != 0 { | |||
fieldToUpdate = append(fieldToUpdate, "sorting") | |||
} | |||
if board.Title != "" { | |||
fieldToUpdate = append(fieldToUpdate, "title") | |||
} | |||
_, err := e.ID(board.ID).Cols(fieldToUpdate...).Update(board) | |||
return err | |||
} | |||
@@ -178,7 +188,7 @@ func GetProjectBoards(projectID int64) (ProjectBoardList, error) { | |||
func getProjectBoards(e Engine, projectID int64) ([]*ProjectBoard, error) { | |||
var boards = make([]*ProjectBoard, 0, 5) | |||
if err := e.Where("project_id=? AND `default`=?", projectID, false).Find(&boards); err != nil { | |||
if err := e.Where("project_id=? AND `default`=?", projectID, false).OrderBy("Sorting").Find(&boards); err != nil { | |||
return nil, err | |||
} | |||
@@ -277,3 +287,17 @@ func (bs ProjectBoardList) LoadIssues() (IssueList, error) { | |||
} | |||
return issues, nil | |||
} | |||
// UpdateProjectBoardSorting update project board sorting | |||
func UpdateProjectBoardSorting(bs ProjectBoardList) error { | |||
for i := range bs { | |||
_, err := x.ID(bs[i].ID).Cols( | |||
"sorting", | |||
).Update(bs[i]) | |||
if err != nil { | |||
return err | |||
} | |||
} | |||
return nil | |||
} |
@@ -487,10 +487,10 @@ type UserCreateProjectForm struct { | |||
UID int64 `binding:"Required"` | |||
} | |||
// EditProjectBoardTitleForm is a form for editing the title of a project's | |||
// board | |||
type EditProjectBoardTitleForm struct { | |||
Title string `binding:"Required;MaxSize(100)"` | |||
// EditProjectBoardForm is a form for editing a project board | |||
type EditProjectBoardForm struct { | |||
Title string `binding:"Required;MaxSize(100)"` | |||
Sorting int8 | |||
} | |||
// _____ .__.__ __ |
@@ -403,7 +403,7 @@ func DeleteProjectBoard(ctx *context.Context) { | |||
// AddBoardToProjectPost allows a new board to be added to a project. | |||
func AddBoardToProjectPost(ctx *context.Context) { | |||
form := web.GetForm(ctx).(*auth.EditProjectBoardTitleForm) | |||
form := web.GetForm(ctx).(*auth.EditProjectBoardForm) | |||
if !ctx.Repo.IsOwner() && !ctx.Repo.IsAdmin() && !ctx.Repo.CanAccess(models.AccessModeWrite, models.UnitTypeProjects) { | |||
ctx.JSON(403, map[string]string{ | |||
"message": "Only authorized users are allowed to perform this action.", | |||
@@ -481,9 +481,9 @@ func checkProjectBoardChangePermissions(ctx *context.Context) (*models.Project, | |||
return project, board | |||
} | |||
// EditProjectBoardTitle allows a project board's title to be updated | |||
func EditProjectBoardTitle(ctx *context.Context) { | |||
form := web.GetForm(ctx).(*auth.EditProjectBoardTitleForm) | |||
// EditProjectBoard allows a project board's to be updated | |||
func EditProjectBoard(ctx *context.Context) { | |||
form := web.GetForm(ctx).(*auth.EditProjectBoardForm) | |||
_, board := checkProjectBoardChangePermissions(ctx) | |||
if ctx.Written() { | |||
return | |||
@@ -493,6 +493,10 @@ func EditProjectBoardTitle(ctx *context.Context) { | |||
board.Title = form.Title | |||
} | |||
if form.Sorting != 0 { | |||
board.Sorting = form.Sorting | |||
} | |||
if err := models.UpdateProjectBoard(board); err != nil { | |||
ctx.ServerError("UpdateProjectBoard", err) | |||
return |
@@ -853,7 +853,7 @@ func RegisterRoutes(m *web.Route) { | |||
m.Get("/new", repo.NewProject) | |||
m.Post("/new", bindIgnErr(auth.CreateProjectForm{}), repo.NewProjectPost) | |||
m.Group("/{id}", func() { | |||
m.Post("", bindIgnErr(auth.EditProjectBoardTitleForm{}), repo.AddBoardToProjectPost) | |||
m.Post("", bindIgnErr(auth.EditProjectBoardForm{}), repo.AddBoardToProjectPost) | |||
m.Post("/delete", repo.DeleteProject) | |||
m.Get("/edit", repo.EditProject) | |||
@@ -861,7 +861,7 @@ func RegisterRoutes(m *web.Route) { | |||
m.Post("/{action:open|close}", repo.ChangeProjectStatus) | |||
m.Group("/{boardID}", func() { | |||
m.Put("", bindIgnErr(auth.EditProjectBoardTitleForm{}), repo.EditProjectBoardTitle) | |||
m.Put("", bindIgnErr(auth.EditProjectBoardForm{}), repo.EditProjectBoard) | |||
m.Delete("", repo.DeleteProjectBoard) | |||
m.Post("/default", repo.SetDefaultProjectBoard) | |||
@@ -72,7 +72,7 @@ | |||
<div class="board"> | |||
{{ range $board := .Boards }} | |||
<div class="ui segment board-column"> | |||
<div class="ui segment board-column" data-id="{{.ID}}" data-sorting="{{.Sorting}}" data-url="{{$.RepoLink}}/projects/{{$.Project.ID}}/{{.ID}}"> | |||
<div class="board-column-header"> | |||
<div class="ui large label board-label">{{.Title}}</div> | |||
{{if and $.CanWriteProjects (not $.Repository.IsArchived) $.PageIsProjects (ne .ID 0)}} |
@@ -8,6 +8,34 @@ export default async function initProject() { | |||
const {Sortable} = await import(/* webpackChunkName: "sortable" */'sortablejs'); | |||
const boardColumns = document.getElementsByClassName('board-column'); | |||
new Sortable( | |||
document.getElementsByClassName('board')[0], | |||
{ | |||
group: 'board-column', | |||
draggable: '.board-column', | |||
animation: 150, | |||
onSort: () => { | |||
const board = document.getElementsByClassName('board')[0]; | |||
const boardColumns = board.getElementsByClassName('board-column'); | |||
boardColumns.forEach((column, i) => { | |||
if (parseInt($(column).data('sorting')) !== i) { | |||
$.ajax({ | |||
url: $(column).data('url'), | |||
data: JSON.stringify({sorting: i}), | |||
headers: { | |||
'X-Csrf-Token': csrf, | |||
'X-Remote': true, | |||
}, | |||
contentType: 'application/json', | |||
method: 'PUT', | |||
}); | |||
} | |||
}); | |||
}, | |||
}, | |||
); | |||
for (const column of boardColumns) { | |||
new Sortable( | |||
column.getElementsByClassName('board')[0], | |||
@@ -74,6 +102,7 @@ export default async function initProject() { | |||
window.location.reload(); | |||
}); | |||
$('.delete-project-board').each(function () { | |||
$(this).click(function (e) { | |||
e.preventDefault(); |