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"`
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
}
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
}
}
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
+}
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
}
// _____ .__.__ __
// 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.",
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
board.Title = form.Title
}
+ if form.Sorting != 0 {
+ board.Sorting = form.Sorting
+ }
+
if err := models.UpdateProjectBoard(board); err != nil {
ctx.ServerError("UpdateProjectBoard", err)
return
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)
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)
<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)}}
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],
window.location.reload();
});
+
$('.delete-project-board').each(function () {
$(this).click(function (e) {
e.preventDefault();