func DeleteIssueLabel(ctx *context.APIContext) { if !ctx.Repo.IsWriter() { ctx.Status(403) return } issue, err := models.GetIssueByIndex(ctx.Repo.Repository.ID, ctx.ParamsInt64(":index")) if err != nil { if models.IsErrIssueNotExist(err) { ctx.Status(404) } else { ctx.Error(500, "GetIssueByIndex", err) } return } label, err := models.GetLabelInRepoByID(ctx.Repo.Repository.ID, ctx.ParamsInt64(":id")) if err != nil { if models.IsErrLabelNotExist(err) { ctx.Error(422, "", err) } else { ctx.Error(500, "GetLabelInRepoByID", err) } return } if err := models.DeleteIssueLabel(issue, label); err != nil { ctx.Error(500, "DeleteIssueLabel", err) return } ctx.Status(204) }
func GetIssue(ctx *context.APIContext) { issue, err := models.GetIssueByIndex(ctx.Repo.Repository.ID, ctx.ParamsInt64(":index")) if err != nil { if models.IsErrIssueNotExist(err) { ctx.Status(404) } else { ctx.Error(500, "GetIssueByIndex", err) } return } ctx.JSON(200, issue.APIFormat()) }
func CreateIssueComment(ctx *context.APIContext, form api.CreateIssueCommentOption) { issue, err := models.GetIssueByIndex(ctx.Repo.Repository.ID, ctx.ParamsInt64(":index")) if err != nil { ctx.Error(500, "GetIssueByIndex", err) return } comment, err := models.CreateIssueComment(ctx.User, ctx.Repo.Repository, issue, form.Body, nil) if err != nil { ctx.Error(500, "CreateIssueComment", err) return } ctx.JSON(201, comment.APIFormat()) }
func ListIssueLabels(ctx *context.APIContext) { issue, err := models.GetIssueByIndex(ctx.Repo.Repository.ID, ctx.ParamsInt64(":index")) if err != nil { if models.IsErrIssueNotExist(err) { ctx.Status(404) } else { ctx.Error(500, "GetIssueByIndex", err) } return } apiLabels := make([]*api.Label, len(issue.Labels)) for i := range issue.Labels { apiLabels[i] = issue.Labels[i].APIFormat() } ctx.JSON(200, &apiLabels) }
func UpdateIssue(ctx *middleware.Context, form auth.CreateIssueForm) { idx := com.StrTo(ctx.Params(":index")).MustInt64() if idx <= 0 { ctx.Error(404) return } issue, err := models.GetIssueByIndex(ctx.Repo.Repository.Id, idx) if err != nil { if err == models.ErrIssueNotExist { ctx.Handle(404, "issue.UpdateIssue", err) } else { ctx.Handle(500, "issue.UpdateIssue(GetIssueByIndex)", err) } return } if ctx.User.Id != issue.PosterId && !ctx.Repo.IsOwner() { ctx.Error(403) return } issue.Name = form.IssueName //issue.MilestoneId = form.MilestoneId //issue.AssigneeId = form.AssigneeId //issue.LabelIds = form.Labels issue.Content = form.Content // try get content from text, ignore conflict with preview ajax if form.Content == "" { issue.Content = ctx.Query("text") } if err = models.UpdateIssue(issue); err != nil { ctx.Handle(500, "issue.UpdateIssue(UpdateIssue)", err) return } ctx.JSON(200, map[string]interface{}{ "ok": true, "title": issue.Name, "content": string(base.RenderMarkdown([]byte(issue.Content), ctx.Repo.RepoLink)), }) }
func AddIssueLabels(ctx *context.APIContext, form api.IssueLabelsOption) { if !ctx.Repo.IsWriter() { ctx.Status(403) return } issue, err := models.GetIssueByIndex(ctx.Repo.Repository.ID, ctx.ParamsInt64(":index")) if err != nil { if models.IsErrIssueNotExist(err) { ctx.Status(404) } else { ctx.Error(500, "GetIssueByIndex", err) } return } labels, err := models.GetLabelsInRepoByIDs(ctx.Repo.Repository.ID, form.Labels) if err != nil { ctx.Error(500, "GetLabelsInRepoByIDs", err) return } if err = issue.AddLabels(ctx.User, labels); err != nil { ctx.Error(500, "AddLabels", err) return } labels, err = models.GetLabelsByIssueID(issue.ID) if err != nil { ctx.Error(500, "GetLabelsByIssueID", err) return } apiLabels := make([]*api.Label, len(labels)) for i := range labels { apiLabels[i] = issue.Labels[i].APIFormat() } ctx.JSON(200, &apiLabels) }
func ClearIssueLabels(ctx *context.APIContext) { if !ctx.Repo.IsWriter() { ctx.Status(403) return } issue, err := models.GetIssueByIndex(ctx.Repo.Repository.ID, ctx.ParamsInt64(":index")) if err != nil { if models.IsErrIssueNotExist(err) { ctx.Status(404) } else { ctx.Error(500, "GetIssueByIndex", err) } return } if err := issue.ClearLabels(ctx.User); err != nil { ctx.Error(500, "ClearLabels", err) return } ctx.Status(204) }
func EditIssue(ctx *context.APIContext, form api.EditIssueOption) { issue, err := models.GetIssueByIndex(ctx.Repo.Repository.ID, ctx.ParamsInt64(":index")) if err != nil { if models.IsErrIssueNotExist(err) { ctx.Status(404) } else { ctx.Error(500, "GetIssueByIndex", err) } return } if !issue.IsPoster(ctx.User.ID) && !ctx.Repo.IsWriter() { ctx.Status(403) return } if len(form.Title) > 0 { issue.Title = form.Title } if form.Body != nil { issue.Content = *form.Body } if ctx.Repo.IsWriter() && form.Assignee != nil && (issue.Assignee == nil || issue.Assignee.LowerName != strings.ToLower(*form.Assignee)) { if len(*form.Assignee) == 0 { issue.AssigneeID = 0 } else { assignee, err := models.GetUserByName(*form.Assignee) if err != nil { if models.IsErrUserNotExist(err) { ctx.Error(422, "", fmt.Sprintf("assignee does not exist: [name: %s]", *form.Assignee)) } else { ctx.Error(500, "GetUserByName", err) } return } issue.AssigneeID = assignee.ID } if err = models.UpdateIssueUserByAssignee(issue); err != nil { ctx.Error(500, "UpdateIssueUserByAssignee", err) return } } if ctx.Repo.IsWriter() && form.Milestone != nil && issue.MilestoneID != *form.Milestone { oldMilestoneID := issue.MilestoneID issue.MilestoneID = *form.Milestone if err = models.ChangeMilestoneAssign(issue, oldMilestoneID); err != nil { ctx.Error(500, "ChangeMilestoneAssign", err) return } } if err = models.UpdateIssue(issue); err != nil { ctx.Error(500, "UpdateIssue", err) return } if form.State != nil { if err = issue.ChangeStatus(ctx.User, ctx.Repo.Repository, api.STATE_CLOSED == api.StateType(*form.State)); err != nil { ctx.Error(500, "ChangeStatus", err) return } } // Refetch from database to assign some automatic values issue, err = models.GetIssueByID(issue.ID) if err != nil { ctx.Error(500, "GetIssueByID", err) return } ctx.JSON(201, issue.APIFormat()) }
func Comment(ctx *middleware.Context) { send := func(status int, data interface{}, err error) { if err != nil { log.Error(4, "issue.Comment(?): %s", err) ctx.JSON(status, map[string]interface{}{ "ok": false, "status": status, "error": err.Error(), }) } else { ctx.JSON(status, map[string]interface{}{ "ok": true, "status": status, "data": data, }) } } index := com.StrTo(ctx.Query("issueIndex")).MustInt64() if index == 0 { ctx.Error(404) return } issue, err := models.GetIssueByIndex(ctx.Repo.Repository.Id, index) if err != nil { if err == models.ErrIssueNotExist { send(404, nil, err) } else { send(200, nil, err) } return } // Check if issue owner changes the status of issue. var newStatus string if ctx.Repo.IsOwner() || issue.PosterId == ctx.User.Id { newStatus = ctx.Query("change_status") } if len(newStatus) > 0 { if (strings.Contains(newStatus, "Reopen") && issue.IsClosed) || (strings.Contains(newStatus, "Close") && !issue.IsClosed) { issue.IsClosed = !issue.IsClosed if err = models.UpdateIssue(issue); err != nil { send(500, nil, err) return } else if err = models.UpdateIssueUserPairsByStatus(issue.Id, issue.IsClosed); err != nil { send(500, nil, err) return } if err = issue.GetLabels(); err != nil { send(500, nil, err) return } for _, label := range issue.Labels { if issue.IsClosed { label.NumClosedIssues++ } else { label.NumClosedIssues-- } if err = models.UpdateLabel(label); err != nil { send(500, nil, err) return } } // Change open/closed issue counter for the associated milestone if issue.MilestoneId > 0 { if err = models.ChangeMilestoneIssueStats(issue); err != nil { send(500, nil, err) } } cmtType := models.COMMENT_TYPE_CLOSE if !issue.IsClosed { cmtType = models.COMMENT_TYPE_REOPEN } if _, err = models.CreateComment(ctx.User.Id, ctx.Repo.Repository.Id, issue.Id, 0, 0, cmtType, "", nil); err != nil { send(200, nil, err) return } log.Trace("%s Issue(%d) status changed: %v", ctx.Req.RequestURI, issue.Id, !issue.IsClosed) } } var comment *models.Comment var ms []string content := ctx.Query("content") // Fix #321. Allow empty comments, as long as we have attachments. if len(content) > 0 || len(ctx.Req.MultipartForm.File["attachments"]) > 0 { switch ctx.Params(":action") { case "new": if comment, err = models.CreateComment(ctx.User.Id, ctx.Repo.Repository.Id, issue.Id, 0, 0, models.COMMENT_TYPE_COMMENT, content, nil); err != nil { send(500, nil, err) return } // Update mentions. ms = base.MentionPattern.FindAllString(issue.Content, -1) if len(ms) > 0 { for i := range ms { ms[i] = ms[i][1:] } if err := models.UpdateMentions(ms, issue.Id); err != nil { send(500, nil, err) return } } log.Trace("%s Comment created: %d", ctx.Req.RequestURI, issue.Id) default: ctx.Handle(404, "issue.Comment", err) return } } if comment != nil { uploadFiles(ctx, issue.Id, comment.Id) } // Notify watchers. act := &models.Action{ ActUserID: ctx.User.Id, ActUserName: ctx.User.LowerName, ActEmail: ctx.User.Email, OpType: models.COMMENT_ISSUE, Content: fmt.Sprintf("%d|%s", issue.Index, strings.Split(content, "\n")[0]), RepoID: ctx.Repo.Repository.Id, RepoUserName: ctx.Repo.Owner.LowerName, RepoName: ctx.Repo.Repository.LowerName, } if err = models.NotifyWatchers(act); err != nil { send(500, nil, err) return } // Mail watchers and mentions. if setting.Service.EnableNotifyMail { issue.Content = content tos, err := mailer.SendIssueNotifyMail(ctx.User, ctx.Repo.Owner, ctx.Repo.Repository, issue) if err != nil { send(500, nil, err) return } tos = append(tos, ctx.User.LowerName) newTos := make([]string, 0, len(ms)) for _, m := range ms { if com.IsSliceContainsStr(tos, m) { continue } newTos = append(newTos, m) } if err = mailer.SendIssueMentionMail(ctx.Render, ctx.User, ctx.Repo.Owner, ctx.Repo.Repository, issue, models.GetUserEmailsByNames(newTos)); err != nil { send(500, nil, err) return } } send(200, fmt.Sprintf("%s/issues/%d", ctx.Repo.RepoLink, index), nil) }
func UpdateIssueLabel(ctx *middleware.Context) { if !ctx.Repo.IsOwner() { ctx.Error(403) return } idx := com.StrTo(ctx.Params(":index")).MustInt64() if idx <= 0 { ctx.Error(404) return } issue, err := models.GetIssueByIndex(ctx.Repo.Repository.Id, idx) if err != nil { if err == models.ErrIssueNotExist { ctx.Handle(404, "issue.UpdateIssueLabel(GetIssueByIndex)", err) } else { ctx.Handle(500, "issue.UpdateIssueLabel(GetIssueByIndex)", err) } return } isAttach := ctx.Query("action") == "attach" labelStrId := ctx.Query("id") labelId := com.StrTo(labelStrId).MustInt64() label, err := models.GetLabelById(labelId) if err != nil { if err == models.ErrLabelNotExist { ctx.Handle(404, "issue.UpdateIssueLabel(GetLabelById)", err) } else { ctx.Handle(500, "issue.UpdateIssueLabel(GetLabelById)", err) } return } isHad := strings.Contains(issue.LabelIds, "$"+labelStrId+"|") isNeedUpdate := false if isAttach { if !isHad { issue.LabelIds += "$" + labelStrId + "|" isNeedUpdate = true } } else { if isHad { issue.LabelIds = strings.Replace(issue.LabelIds, "$"+labelStrId+"|", "", -1) isNeedUpdate = true } } if isNeedUpdate { if err = models.UpdateIssue(issue); err != nil { ctx.Handle(500, "issue.UpdateIssueLabel(UpdateIssue)", err) return } if isAttach { label.NumIssues++ if issue.IsClosed { label.NumClosedIssues++ } } else { label.NumIssues-- if issue.IsClosed { label.NumClosedIssues-- } } if err = models.UpdateLabel(label); err != nil { ctx.Handle(500, "issue.UpdateIssueLabel(UpdateLabel)", err) return } } ctx.JSON(200, map[string]interface{}{ "ok": true, }) }
func ViewIssue(ctx *middleware.Context) { ctx.Data["AttachmentsEnabled"] = setting.AttachmentEnabled idx := com.StrTo(ctx.Params(":index")).MustInt64() if idx == 0 { ctx.Handle(404, "issue.ViewIssue", nil) return } issue, err := models.GetIssueByIndex(ctx.Repo.Repository.Id, idx) if err != nil { if err == models.ErrIssueNotExist { ctx.Handle(404, "issue.ViewIssue(GetIssueByIndex)", err) } else { ctx.Handle(500, "issue.ViewIssue(GetIssueByIndex)", err) } return } // Get labels. if err = issue.GetLabels(); err != nil { ctx.Handle(500, "issue.ViewIssue(GetLabels)", err) return } labels, err := models.GetLabels(ctx.Repo.Repository.Id) if err != nil { ctx.Handle(500, "issue.ViewIssue(GetLabels.2)", err) return } checkLabels(issue.Labels, labels) ctx.Data["Labels"] = labels // Get assigned milestone. if issue.MilestoneId > 0 { ctx.Data["Milestone"], err = models.GetMilestoneById(issue.MilestoneId) if err != nil { if err == models.ErrMilestoneNotExist { log.Warn("issue.ViewIssue(GetMilestoneById): %v", err) } else { ctx.Handle(500, "issue.ViewIssue(GetMilestoneById)", err) return } } } // Get all milestones. ctx.Data["OpenMilestones"], err = models.GetMilestones(ctx.Repo.Repository.Id, false) if err != nil { ctx.Handle(500, "issue.ViewIssue(GetMilestones.1): %v", err) return } ctx.Data["ClosedMilestones"], err = models.GetMilestones(ctx.Repo.Repository.Id, true) if err != nil { ctx.Handle(500, "issue.ViewIssue(GetMilestones.2): %v", err) return } // Get all collaborators. ctx.Data["Collaborators"], err = ctx.Repo.Repository.GetCollaborators() if err != nil { ctx.Handle(500, "issue.CreateIssue(GetCollaborators)", err) return } if ctx.IsSigned { // Update issue-user. if err = models.UpdateIssueUserPairByRead(ctx.User.Id, issue.Id); err != nil { ctx.Handle(500, "issue.ViewIssue(UpdateIssueUserPairByRead): %v", err) return } } // Get poster and Assignee. if err = issue.GetPoster(); err != nil { ctx.Handle(500, "issue.ViewIssue(GetPoster): %v", err) return } else if err = issue.GetAssignee(); err != nil { ctx.Handle(500, "issue.ViewIssue(GetAssignee): %v", err) return } issue.RenderedContent = string(base.RenderMarkdown([]byte(issue.Content), ctx.Repo.RepoLink)) // Get comments. comments, err := models.GetIssueComments(issue.Id) if err != nil { ctx.Handle(500, "issue.ViewIssue(GetIssueComments): %v", err) return } // Get posters. for i := range comments { u, err := models.GetUserById(comments[i].PosterId) if err != nil { ctx.Handle(500, "issue.ViewIssue(GetUserById.2): %v", err) return } comments[i].Poster = u if comments[i].Type == models.COMMENT_TYPE_COMMENT { comments[i].Content = string(base.RenderMarkdown([]byte(comments[i].Content), ctx.Repo.RepoLink)) } } ctx.Data["AllowedTypes"] = setting.AttachmentAllowedTypes ctx.Data["Title"] = issue.Name ctx.Data["Issue"] = issue ctx.Data["Comments"] = comments ctx.Data["IsIssueOwner"] = ctx.Repo.IsOwner() || (ctx.IsSigned && issue.PosterId == ctx.User.Id) ctx.Data["IsRepoToolbarIssues"] = true ctx.Data["IsRepoToolbarIssuesList"] = false ctx.HTML(200, ISSUE_VIEW) }