| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667 |
- // Copyright 2022 The Gitea Authors. All rights reserved.
- // SPDX-License-Identifier: MIT
-
- package org
-
- import (
- "errors"
- "fmt"
- "net/http"
- "strings"
-
- "code.gitea.io/gitea/models/db"
- issues_model "code.gitea.io/gitea/models/issues"
- org_model "code.gitea.io/gitea/models/organization"
- project_model "code.gitea.io/gitea/models/project"
- attachment_model "code.gitea.io/gitea/models/repo"
- "code.gitea.io/gitea/models/unit"
- "code.gitea.io/gitea/modules/json"
- "code.gitea.io/gitea/modules/optional"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/templates"
- "code.gitea.io/gitea/modules/web"
- "code.gitea.io/gitea/routers/web/shared/issue"
- shared_user "code.gitea.io/gitea/routers/web/shared/user"
- "code.gitea.io/gitea/services/context"
- "code.gitea.io/gitea/services/forms"
- project_service "code.gitea.io/gitea/services/projects"
- )
-
- const (
- tplProjects templates.TplName = "org/projects/list"
- tplProjectsNew templates.TplName = "org/projects/new"
- tplProjectsView templates.TplName = "org/projects/view"
- )
-
- // MustEnableProjects check if projects are enabled in settings
- func MustEnableProjects(ctx *context.Context) {
- if unit.TypeProjects.UnitGlobalDisabled() {
- ctx.NotFound(nil)
- return
- }
- }
-
- // Projects renders the home page of projects
- func Projects(ctx *context.Context) {
- if _, err := shared_user.RenderUserOrgHeader(ctx); err != nil {
- ctx.ServerError("RenderUserOrgHeader", err)
- return
- }
- ctx.Data["Title"] = ctx.Tr("repo.projects")
-
- sortType := ctx.FormTrim("sort")
-
- isShowClosed := strings.ToLower(ctx.FormTrim("state")) == "closed"
- keyword := ctx.FormTrim("q")
- page := max(ctx.FormInt("page"), 1)
-
- var projectType project_model.Type
- if ctx.ContextUser.IsOrganization() {
- projectType = project_model.TypeOrganization
- } else {
- projectType = project_model.TypeIndividual
- }
- projects, total, err := db.FindAndCount[project_model.Project](ctx, project_model.SearchOptions{
- ListOptions: db.ListOptions{
- Page: page,
- PageSize: setting.UI.IssuePagingNum,
- },
- OwnerID: ctx.ContextUser.ID,
- IsClosed: optional.Some(isShowClosed),
- OrderBy: project_model.GetSearchOrderByBySortType(sortType),
- Type: projectType,
- Title: keyword,
- })
- if err != nil {
- ctx.ServerError("FindProjects", err)
- return
- }
-
- if err := project_service.LoadIssueNumbersForProjects(ctx, projects, ctx.Doer); err != nil {
- ctx.ServerError("LoadIssueNumbersForProjects", err)
- return
- }
-
- opTotal, err := db.Count[project_model.Project](ctx, project_model.SearchOptions{
- OwnerID: ctx.ContextUser.ID,
- IsClosed: optional.Some(!isShowClosed),
- Type: projectType,
- })
- if err != nil {
- ctx.ServerError("CountProjects", err)
- return
- }
-
- if isShowClosed {
- ctx.Data["OpenCount"] = opTotal
- ctx.Data["ClosedCount"] = total
- } else {
- ctx.Data["OpenCount"] = total
- ctx.Data["ClosedCount"] = opTotal
- }
-
- ctx.Data["Projects"] = projects
-
- if isShowClosed {
- ctx.Data["State"] = "closed"
- } else {
- ctx.Data["State"] = "open"
- }
- renderUtils := templates.NewRenderUtils(ctx)
- for _, project := range projects {
- project.RenderedContent = renderUtils.MarkdownToHtml(project.Description)
- }
-
- numPages := 0
- if total > 0 {
- numPages = (int(total) - 1/setting.UI.IssuePagingNum)
- }
-
- pager := context.NewPagination(int(total), setting.UI.IssuePagingNum, page, numPages)
- pager.AddParamFromRequest(ctx.Req)
- ctx.Data["Page"] = pager
-
- ctx.Data["CanWriteProjects"] = canWriteProjects(ctx)
- ctx.Data["IsShowClosed"] = isShowClosed
- ctx.Data["PageIsViewProjects"] = true
- ctx.Data["SortType"] = sortType
-
- ctx.HTML(http.StatusOK, tplProjects)
- }
-
- func canWriteProjects(ctx *context.Context) bool {
- if ctx.ContextUser.IsOrganization() {
- return ctx.Org.CanWriteUnit(ctx, unit.TypeProjects)
- }
- return ctx.Doer != nil && ctx.ContextUser.ID == ctx.Doer.ID
- }
-
- // RenderNewProject render creating a project page
- func RenderNewProject(ctx *context.Context) {
- ctx.Data["Title"] = ctx.Tr("repo.projects.new")
- ctx.Data["TemplateConfigs"] = project_model.GetTemplateConfigs()
- ctx.Data["CardTypes"] = project_model.GetCardConfig()
- ctx.Data["CanWriteProjects"] = canWriteProjects(ctx)
- ctx.Data["PageIsViewProjects"] = true
- ctx.Data["HomeLink"] = ctx.ContextUser.HomeLink()
- ctx.Data["CancelLink"] = ctx.ContextUser.HomeLink() + "/-/projects"
- if _, err := shared_user.RenderUserOrgHeader(ctx); err != nil {
- ctx.ServerError("RenderUserOrgHeader", err)
- return
- }
-
- ctx.HTML(http.StatusOK, tplProjectsNew)
- }
-
- // NewProjectPost creates a new project
- func NewProjectPost(ctx *context.Context) {
- form := web.GetForm(ctx).(*forms.CreateProjectForm)
- ctx.Data["Title"] = ctx.Tr("repo.projects.new")
- if _, err := shared_user.RenderUserOrgHeader(ctx); err != nil {
- ctx.ServerError("RenderUserOrgHeader", err)
- return
- }
-
- if ctx.HasError() {
- RenderNewProject(ctx)
- return
- }
-
- newProject := project_model.Project{
- OwnerID: ctx.ContextUser.ID,
- Title: form.Title,
- Description: form.Content,
- CreatorID: ctx.Doer.ID,
- TemplateType: form.TemplateType,
- CardType: form.CardType,
- }
-
- if ctx.ContextUser.IsOrganization() {
- newProject.Type = project_model.TypeOrganization
- } else {
- newProject.Type = project_model.TypeIndividual
- }
-
- if err := project_model.NewProject(ctx, &newProject); err != nil {
- ctx.ServerError("NewProject", err)
- return
- }
-
- ctx.Flash.Success(ctx.Tr("repo.projects.create_success", form.Title))
- ctx.Redirect(ctx.ContextUser.HomeLink() + "/-/projects")
- }
-
- // ChangeProjectStatus updates the status of a project between "open" and "close"
- func ChangeProjectStatus(ctx *context.Context) {
- var toClose bool
- switch ctx.PathParam("action") {
- case "open":
- toClose = false
- case "close":
- toClose = true
- default:
- ctx.JSONRedirect(ctx.ContextUser.HomeLink() + "/-/projects")
- return
- }
- id := ctx.PathParamInt64("id")
-
- if err := project_model.ChangeProjectStatusByRepoIDAndID(ctx, 0, id, toClose); err != nil {
- ctx.NotFoundOrServerError("ChangeProjectStatusByRepoIDAndID", project_model.IsErrProjectNotExist, err)
- return
- }
- ctx.JSONRedirect(project_model.ProjectLinkForOrg(ctx.ContextUser, id))
- }
-
- // DeleteProject delete a project
- func DeleteProject(ctx *context.Context) {
- p, err := project_model.GetProjectByID(ctx, ctx.PathParamInt64("id"))
- if err != nil {
- ctx.NotFoundOrServerError("GetProjectByID", project_model.IsErrProjectNotExist, err)
- return
- }
- if p.OwnerID != ctx.ContextUser.ID {
- ctx.NotFound(nil)
- return
- }
-
- if err := project_model.DeleteProjectByID(ctx, p.ID); err != nil {
- ctx.Flash.Error("DeleteProjectByID: " + err.Error())
- } else {
- ctx.Flash.Success(ctx.Tr("repo.projects.deletion_success"))
- }
-
- ctx.JSONRedirect(ctx.ContextUser.HomeLink() + "/-/projects")
- }
-
- // RenderEditProject allows a project to be edited
- func RenderEditProject(ctx *context.Context) {
- ctx.Data["Title"] = ctx.Tr("repo.projects.edit")
- ctx.Data["PageIsEditProjects"] = true
- ctx.Data["PageIsViewProjects"] = true
- ctx.Data["CanWriteProjects"] = canWriteProjects(ctx)
- ctx.Data["CardTypes"] = project_model.GetCardConfig()
-
- if _, err := shared_user.RenderUserOrgHeader(ctx); err != nil {
- ctx.ServerError("RenderUserOrgHeader", err)
- return
- }
-
- p, err := project_model.GetProjectByID(ctx, ctx.PathParamInt64("id"))
- if err != nil {
- ctx.NotFoundOrServerError("GetProjectByID", project_model.IsErrProjectNotExist, err)
- return
- }
- if p.OwnerID != ctx.ContextUser.ID {
- ctx.NotFound(nil)
- return
- }
-
- ctx.Data["projectID"] = p.ID
- ctx.Data["title"] = p.Title
- ctx.Data["content"] = p.Description
- ctx.Data["redirect"] = ctx.FormString("redirect")
- ctx.Data["HomeLink"] = ctx.ContextUser.HomeLink()
- ctx.Data["card_type"] = p.CardType
- ctx.Data["CancelLink"] = project_model.ProjectLinkForOrg(ctx.ContextUser, p.ID)
-
- ctx.HTML(http.StatusOK, tplProjectsNew)
- }
-
- // EditProjectPost response for editing a project
- func EditProjectPost(ctx *context.Context) {
- form := web.GetForm(ctx).(*forms.CreateProjectForm)
- projectID := ctx.PathParamInt64("id")
- ctx.Data["Title"] = ctx.Tr("repo.projects.edit")
- ctx.Data["PageIsEditProjects"] = true
- ctx.Data["PageIsViewProjects"] = true
- ctx.Data["CanWriteProjects"] = canWriteProjects(ctx)
- ctx.Data["CardTypes"] = project_model.GetCardConfig()
- ctx.Data["CancelLink"] = project_model.ProjectLinkForOrg(ctx.ContextUser, projectID)
-
- if _, err := shared_user.RenderUserOrgHeader(ctx); err != nil {
- ctx.ServerError("RenderUserOrgHeader", err)
- return
- }
-
- if ctx.HasError() {
- ctx.HTML(http.StatusOK, tplProjectsNew)
- return
- }
-
- p, err := project_model.GetProjectByID(ctx, projectID)
- if err != nil {
- ctx.NotFoundOrServerError("GetProjectByID", project_model.IsErrProjectNotExist, err)
- return
- }
- if p.OwnerID != ctx.ContextUser.ID {
- ctx.NotFound(nil)
- return
- }
-
- p.Title = form.Title
- p.Description = form.Content
- p.CardType = form.CardType
- if err = project_model.UpdateProject(ctx, p); err != nil {
- ctx.ServerError("UpdateProjects", err)
- return
- }
-
- ctx.Flash.Success(ctx.Tr("repo.projects.edit_success", p.Title))
- if ctx.FormString("redirect") == "project" {
- ctx.Redirect(p.Link(ctx))
- } else {
- ctx.Redirect(ctx.ContextUser.HomeLink() + "/-/projects")
- }
- }
-
- // ViewProject renders the project with board view for a project
- func ViewProject(ctx *context.Context) {
- project, err := project_model.GetProjectByID(ctx, ctx.PathParamInt64("id"))
- if err != nil {
- ctx.NotFoundOrServerError("GetProjectByID", project_model.IsErrProjectNotExist, err)
- return
- }
- if project.OwnerID != ctx.ContextUser.ID {
- ctx.NotFound(nil)
- return
- }
- if err := project.LoadOwner(ctx); err != nil {
- ctx.ServerError("LoadOwner", err)
- return
- }
-
- columns, err := project.GetColumns(ctx)
- if err != nil {
- ctx.ServerError("GetProjectColumns", err)
- return
- }
-
- preparedLabelFilter := issue.PrepareFilterIssueLabels(ctx, project.RepoID, project.Owner)
- if ctx.Written() {
- return
- }
- assigneeID := ctx.FormString("assignee")
-
- opts := issues_model.IssuesOptions{
- LabelIDs: preparedLabelFilter.SelectedLabelIDs,
- AssigneeID: assigneeID,
- Owner: project.Owner,
- Doer: ctx.Doer,
- }
-
- issuesMap, err := project_service.LoadIssuesFromProject(ctx, project, &opts)
- if err != nil {
- ctx.ServerError("LoadIssuesOfColumns", err)
- return
- }
- for _, column := range columns {
- column.NumIssues = int64(len(issuesMap[column.ID]))
- }
-
- if project.CardType != project_model.CardTypeTextOnly {
- issuesAttachmentMap := make(map[int64][]*attachment_model.Attachment)
- for _, issuesList := range issuesMap {
- for _, issue := range issuesList {
- if issueAttachment, err := attachment_model.GetAttachmentsByIssueIDImagesLatest(ctx, issue.ID); err == nil {
- issuesAttachmentMap[issue.ID] = issueAttachment
- }
- }
- }
- ctx.Data["issuesAttachmentMap"] = issuesAttachmentMap
- }
-
- linkedPrsMap := make(map[int64][]*issues_model.Issue)
- for _, issuesList := range issuesMap {
- for _, issue := range issuesList {
- var referencedIDs []int64
- for _, comment := range issue.Comments {
- if comment.RefIssueID != 0 && comment.RefIsPull {
- referencedIDs = append(referencedIDs, comment.RefIssueID)
- }
- }
-
- if len(referencedIDs) > 0 {
- if linkedPrs, err := issues_model.Issues(ctx, &issues_model.IssuesOptions{
- IssueIDs: referencedIDs,
- IsPull: optional.Some(true),
- }); err == nil {
- linkedPrsMap[issue.ID] = linkedPrs
- }
- }
- }
- }
-
- // TODO: Add option to filter also by repository specific labels
- labels, err := issues_model.GetLabelsByOrgID(ctx, project.OwnerID, "", db.ListOptions{})
- if err != nil {
- ctx.ServerError("GetLabelsByOrgID", err)
- return
- }
-
- // Get the exclusive scope for every label ID
- labelExclusiveScopes := make([]string, 0, len(preparedLabelFilter.SelectedLabelIDs))
- for _, labelID := range preparedLabelFilter.SelectedLabelIDs {
- foundExclusiveScope := false
- for _, label := range labels {
- if label.ID == labelID || label.ID == -labelID {
- labelExclusiveScopes = append(labelExclusiveScopes, label.ExclusiveScope())
- foundExclusiveScope = true
- break
- }
- }
- if !foundExclusiveScope {
- labelExclusiveScopes = append(labelExclusiveScopes, "")
- }
- }
-
- for _, l := range labels {
- l.LoadSelectedLabelsAfterClick(preparedLabelFilter.SelectedLabelIDs, labelExclusiveScopes)
- }
- ctx.Data["Labels"] = labels
- ctx.Data["NumLabels"] = len(labels)
-
- // Get assignees.
- assigneeUsers, err := org_model.GetOrgAssignees(ctx, project.OwnerID)
- if err != nil {
- ctx.ServerError("GetRepoAssignees", err)
- return
- }
- ctx.Data["Assignees"] = shared_user.MakeSelfOnTop(ctx.Doer, assigneeUsers)
- ctx.Data["AssigneeID"] = assigneeID
-
- project.RenderedContent = templates.NewRenderUtils(ctx).MarkdownToHtml(project.Description)
- ctx.Data["LinkedPRs"] = linkedPrsMap
- ctx.Data["PageIsViewProjects"] = true
- ctx.Data["CanWriteProjects"] = canWriteProjects(ctx)
- ctx.Data["Project"] = project
- ctx.Data["IssuesMap"] = issuesMap
- ctx.Data["Columns"] = columns
-
- if _, err := shared_user.RenderUserOrgHeader(ctx); err != nil {
- ctx.ServerError("RenderUserOrgHeader", err)
- return
- }
-
- ctx.HTML(http.StatusOK, tplProjectsView)
- }
-
- // DeleteProjectColumn allows for the deletion of a project column
- func DeleteProjectColumn(ctx *context.Context) {
- 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.PathParamInt64("id"))
- if err != nil {
- ctx.NotFoundOrServerError("GetProjectByID", project_model.IsErrProjectNotExist, err)
- return
- }
-
- pb, err := project_model.GetColumn(ctx, ctx.PathParamInt64("columnID"))
- if err != nil {
- ctx.ServerError("GetProjectColumn", err)
- return
- }
- if pb.ProjectID != ctx.PathParamInt64("id") {
- ctx.JSON(http.StatusUnprocessableEntity, map[string]string{
- "message": fmt.Sprintf("ProjectColumn[%d] is not in Project[%d] as expected", pb.ID, project.ID),
- })
- return
- }
-
- if project.OwnerID != ctx.ContextUser.ID {
- ctx.JSON(http.StatusUnprocessableEntity, map[string]string{
- "message": fmt.Sprintf("ProjectColumn[%d] is not in Owner[%d] as expected", pb.ID, ctx.ContextUser.ID),
- })
- return
- }
-
- if err := project_model.DeleteColumnByID(ctx, ctx.PathParamInt64("columnID")); err != nil {
- ctx.ServerError("DeleteProjectColumnByID", err)
- return
- }
-
- ctx.JSONOK()
- }
-
- // AddColumnToProjectPost allows a new column to be added to a project.
- func AddColumnToProjectPost(ctx *context.Context) {
- form := web.GetForm(ctx).(*forms.EditProjectColumnForm)
-
- project, err := project_model.GetProjectByID(ctx, ctx.PathParamInt64("id"))
- if err != nil {
- ctx.NotFoundOrServerError("GetProjectByID", project_model.IsErrProjectNotExist, err)
- return
- }
-
- if err := project_model.NewColumn(ctx, &project_model.Column{
- ProjectID: project.ID,
- Title: form.Title,
- Color: form.Color,
- CreatorID: ctx.Doer.ID,
- }); err != nil {
- ctx.ServerError("NewProjectColumn", err)
- return
- }
-
- ctx.JSONOK()
- }
-
- // CheckProjectColumnChangePermissions check permission
- func CheckProjectColumnChangePermissions(ctx *context.Context) (*project_model.Project, *project_model.Column) {
- if ctx.Doer == nil {
- ctx.JSON(http.StatusForbidden, map[string]string{
- "message": "Only signed in users are allowed to perform this action.",
- })
- return nil, nil
- }
-
- project, err := project_model.GetProjectByID(ctx, ctx.PathParamInt64("id"))
- if err != nil {
- ctx.NotFoundOrServerError("GetProjectByID", project_model.IsErrProjectNotExist, err)
- return nil, nil
- }
-
- column, err := project_model.GetColumn(ctx, ctx.PathParamInt64("columnID"))
- if err != nil {
- ctx.ServerError("GetProjectColumn", err)
- return nil, nil
- }
- if column.ProjectID != ctx.PathParamInt64("id") {
- ctx.JSON(http.StatusUnprocessableEntity, map[string]string{
- "message": fmt.Sprintf("ProjectColumn[%d] is not in Project[%d] as expected", column.ID, project.ID),
- })
- return nil, nil
- }
-
- if project.OwnerID != ctx.ContextUser.ID {
- ctx.JSON(http.StatusUnprocessableEntity, map[string]string{
- "message": fmt.Sprintf("ProjectColumn[%d] is not in Repository[%d] as expected", column.ID, project.ID),
- })
- return nil, nil
- }
- return project, column
- }
-
- // EditProjectColumn allows a project column's to be updated
- func EditProjectColumn(ctx *context.Context) {
- form := web.GetForm(ctx).(*forms.EditProjectColumnForm)
- _, column := CheckProjectColumnChangePermissions(ctx)
- if ctx.Written() {
- return
- }
-
- if form.Title != "" {
- column.Title = form.Title
- }
- column.Color = form.Color
- if form.Sorting != 0 {
- column.Sorting = form.Sorting
- }
-
- if err := project_model.UpdateColumn(ctx, column); err != nil {
- ctx.ServerError("UpdateProjectColumn", err)
- return
- }
-
- ctx.JSONOK()
- }
-
- // SetDefaultProjectColumn set default column for uncategorized issues/pulls
- func SetDefaultProjectColumn(ctx *context.Context) {
- project, column := CheckProjectColumnChangePermissions(ctx)
- if ctx.Written() {
- return
- }
-
- if err := project_model.SetDefaultColumn(ctx, project.ID, column.ID); err != nil {
- ctx.ServerError("SetDefaultColumn", err)
- return
- }
-
- ctx.JSONOK()
- }
-
- // MoveIssues moves or keeps issues in a column and sorts them inside that column
- func MoveIssues(ctx *context.Context) {
- 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.PathParamInt64("id"))
- if err != nil {
- ctx.NotFoundOrServerError("GetProjectByID", project_model.IsErrProjectNotExist, err)
- return
- }
- if project.OwnerID != ctx.ContextUser.ID {
- ctx.NotFound(nil)
- return
- }
-
- column, err := project_model.GetColumn(ctx, ctx.PathParamInt64("columnID"))
- if err != nil {
- ctx.NotFoundOrServerError("GetProjectColumn", project_model.IsErrProjectColumnNotExist, err)
- return
- }
-
- if column.ProjectID != project.ID {
- ctx.NotFound(nil)
- return
- }
-
- type movedIssuesForm struct {
- Issues []struct {
- IssueID int64 `json:"issueID"`
- Sorting int64 `json:"sorting"`
- } `json:"issues"`
- }
-
- form := &movedIssuesForm{}
- if err = json.NewDecoder(ctx.Req.Body).Decode(&form); err != nil {
- ctx.ServerError("DecodeMovedIssuesForm", err)
- return
- }
-
- issueIDs := make([]int64, 0, len(form.Issues))
- sortedIssueIDs := make(map[int64]int64)
- for _, issue := range form.Issues {
- issueIDs = append(issueIDs, issue.IssueID)
- sortedIssueIDs[issue.Sorting] = issue.IssueID
- }
- movedIssues, err := issues_model.GetIssuesByIDs(ctx, issueIDs)
- if err != nil {
- ctx.NotFoundOrServerError("GetIssueByID", issues_model.IsErrIssueNotExist, err)
- return
- }
-
- if len(movedIssues) != len(form.Issues) {
- ctx.ServerError("some issues do not exist", errors.New("some issues do not exist"))
- return
- }
-
- if _, err = movedIssues.LoadRepositories(ctx); err != nil {
- ctx.ServerError("LoadRepositories", err)
- return
- }
-
- for _, issue := range movedIssues {
- if issue.RepoID != project.RepoID && issue.Repo.OwnerID != project.OwnerID {
- ctx.ServerError("Some issue's repoID is not equal to project's repoID", errors.New("Some issue's repoID is not equal to project's repoID"))
- return
- }
- }
-
- if err = project_service.MoveIssuesOnProjectColumn(ctx, ctx.Doer, column, sortedIssueIDs); err != nil {
- ctx.ServerError("MoveIssuesOnProjectColumn", err)
- return
- }
-
- ctx.JSONOK()
- }
|