code.gitea.io/gitea@v1.22.3/routers/api/v1/repo/issue_comment.go (about)

     1  // Copyright 2015 The Gogs Authors. All rights reserved.
     2  // Copyright 2020 The Gitea Authors.
     3  // SPDX-License-Identifier: MIT
     4  
     5  package repo
     6  
     7  import (
     8  	stdCtx "context"
     9  	"errors"
    10  	"net/http"
    11  
    12  	issues_model "code.gitea.io/gitea/models/issues"
    13  	access_model "code.gitea.io/gitea/models/perm/access"
    14  	repo_model "code.gitea.io/gitea/models/repo"
    15  	"code.gitea.io/gitea/models/unit"
    16  	user_model "code.gitea.io/gitea/models/user"
    17  	"code.gitea.io/gitea/modules/optional"
    18  	api "code.gitea.io/gitea/modules/structs"
    19  	"code.gitea.io/gitea/modules/web"
    20  	"code.gitea.io/gitea/routers/api/v1/utils"
    21  	"code.gitea.io/gitea/services/context"
    22  	"code.gitea.io/gitea/services/convert"
    23  	issue_service "code.gitea.io/gitea/services/issue"
    24  )
    25  
    26  // ListIssueComments list all the comments of an issue
    27  func ListIssueComments(ctx *context.APIContext) {
    28  	// swagger:operation GET /repos/{owner}/{repo}/issues/{index}/comments issue issueGetComments
    29  	// ---
    30  	// summary: List all comments on an issue
    31  	// produces:
    32  	// - application/json
    33  	// parameters:
    34  	// - name: owner
    35  	//   in: path
    36  	//   description: owner of the repo
    37  	//   type: string
    38  	//   required: true
    39  	// - name: repo
    40  	//   in: path
    41  	//   description: name of the repo
    42  	//   type: string
    43  	//   required: true
    44  	// - name: index
    45  	//   in: path
    46  	//   description: index of the issue
    47  	//   type: integer
    48  	//   format: int64
    49  	//   required: true
    50  	// - name: since
    51  	//   in: query
    52  	//   description: if provided, only comments updated since the specified time are returned.
    53  	//   type: string
    54  	//   format: date-time
    55  	// - name: before
    56  	//   in: query
    57  	//   description: if provided, only comments updated before the provided time are returned.
    58  	//   type: string
    59  	//   format: date-time
    60  	// responses:
    61  	//   "200":
    62  	//     "$ref": "#/responses/CommentList"
    63  	//   "404":
    64  	//     "$ref": "#/responses/notFound"
    65  
    66  	before, since, err := context.GetQueryBeforeSince(ctx.Base)
    67  	if err != nil {
    68  		ctx.Error(http.StatusUnprocessableEntity, "GetQueryBeforeSince", err)
    69  		return
    70  	}
    71  	issue, err := issues_model.GetIssueByIndex(ctx, ctx.Repo.Repository.ID, ctx.ParamsInt64(":index"))
    72  	if err != nil {
    73  		ctx.Error(http.StatusInternalServerError, "GetRawIssueByIndex", err)
    74  		return
    75  	}
    76  	if !ctx.Repo.CanReadIssuesOrPulls(issue.IsPull) {
    77  		ctx.NotFound()
    78  		return
    79  	}
    80  
    81  	issue.Repo = ctx.Repo.Repository
    82  
    83  	opts := &issues_model.FindCommentsOptions{
    84  		IssueID: issue.ID,
    85  		Since:   since,
    86  		Before:  before,
    87  		Type:    issues_model.CommentTypeComment,
    88  	}
    89  
    90  	comments, err := issues_model.FindComments(ctx, opts)
    91  	if err != nil {
    92  		ctx.Error(http.StatusInternalServerError, "FindComments", err)
    93  		return
    94  	}
    95  
    96  	totalCount, err := issues_model.CountComments(ctx, opts)
    97  	if err != nil {
    98  		ctx.InternalServerError(err)
    99  		return
   100  	}
   101  
   102  	if err := comments.LoadPosters(ctx); err != nil {
   103  		ctx.Error(http.StatusInternalServerError, "LoadPosters", err)
   104  		return
   105  	}
   106  
   107  	if err := comments.LoadAttachments(ctx); err != nil {
   108  		ctx.Error(http.StatusInternalServerError, "LoadAttachments", err)
   109  		return
   110  	}
   111  
   112  	apiComments := make([]*api.Comment, len(comments))
   113  	for i, comment := range comments {
   114  		comment.Issue = issue
   115  		apiComments[i] = convert.ToAPIComment(ctx, ctx.Repo.Repository, comments[i])
   116  	}
   117  
   118  	ctx.SetTotalCountHeader(totalCount)
   119  	ctx.JSON(http.StatusOK, &apiComments)
   120  }
   121  
   122  // ListIssueCommentsAndTimeline list all the comments and events of an issue
   123  func ListIssueCommentsAndTimeline(ctx *context.APIContext) {
   124  	// swagger:operation GET /repos/{owner}/{repo}/issues/{index}/timeline issue issueGetCommentsAndTimeline
   125  	// ---
   126  	// summary: List all comments and events on an issue
   127  	// produces:
   128  	// - application/json
   129  	// parameters:
   130  	// - name: owner
   131  	//   in: path
   132  	//   description: owner of the repo
   133  	//   type: string
   134  	//   required: true
   135  	// - name: repo
   136  	//   in: path
   137  	//   description: name of the repo
   138  	//   type: string
   139  	//   required: true
   140  	// - name: index
   141  	//   in: path
   142  	//   description: index of the issue
   143  	//   type: integer
   144  	//   format: int64
   145  	//   required: true
   146  	// - name: since
   147  	//   in: query
   148  	//   description: if provided, only comments updated since the specified time are returned.
   149  	//   type: string
   150  	//   format: date-time
   151  	// - name: page
   152  	//   in: query
   153  	//   description: page number of results to return (1-based)
   154  	//   type: integer
   155  	// - name: limit
   156  	//   in: query
   157  	//   description: page size of results
   158  	//   type: integer
   159  	// - name: before
   160  	//   in: query
   161  	//   description: if provided, only comments updated before the provided time are returned.
   162  	//   type: string
   163  	//   format: date-time
   164  	// responses:
   165  	//   "200":
   166  	//     "$ref": "#/responses/TimelineList"
   167  	//   "404":
   168  	//     "$ref": "#/responses/notFound"
   169  
   170  	before, since, err := context.GetQueryBeforeSince(ctx.Base)
   171  	if err != nil {
   172  		ctx.Error(http.StatusUnprocessableEntity, "GetQueryBeforeSince", err)
   173  		return
   174  	}
   175  	issue, err := issues_model.GetIssueByIndex(ctx, ctx.Repo.Repository.ID, ctx.ParamsInt64(":index"))
   176  	if err != nil {
   177  		ctx.Error(http.StatusInternalServerError, "GetRawIssueByIndex", err)
   178  		return
   179  	}
   180  	issue.Repo = ctx.Repo.Repository
   181  
   182  	opts := &issues_model.FindCommentsOptions{
   183  		ListOptions: utils.GetListOptions(ctx),
   184  		IssueID:     issue.ID,
   185  		Since:       since,
   186  		Before:      before,
   187  		Type:        issues_model.CommentTypeUndefined,
   188  	}
   189  
   190  	comments, err := issues_model.FindComments(ctx, opts)
   191  	if err != nil {
   192  		ctx.Error(http.StatusInternalServerError, "FindComments", err)
   193  		return
   194  	}
   195  
   196  	if err := comments.LoadPosters(ctx); err != nil {
   197  		ctx.Error(http.StatusInternalServerError, "LoadPosters", err)
   198  		return
   199  	}
   200  
   201  	var apiComments []*api.TimelineComment
   202  	for _, comment := range comments {
   203  		if comment.Type != issues_model.CommentTypeCode && isXRefCommentAccessible(ctx, ctx.Doer, comment, issue.RepoID) {
   204  			comment.Issue = issue
   205  			apiComments = append(apiComments, convert.ToTimelineComment(ctx, issue.Repo, comment, ctx.Doer))
   206  		}
   207  	}
   208  
   209  	ctx.SetTotalCountHeader(int64(len(apiComments)))
   210  	ctx.JSON(http.StatusOK, &apiComments)
   211  }
   212  
   213  func isXRefCommentAccessible(ctx stdCtx.Context, user *user_model.User, c *issues_model.Comment, issueRepoID int64) bool {
   214  	// Remove comments that the user has no permissions to see
   215  	if issues_model.CommentTypeIsRef(c.Type) && c.RefRepoID != issueRepoID && c.RefRepoID != 0 {
   216  		var err error
   217  		// Set RefRepo for description in template
   218  		c.RefRepo, err = repo_model.GetRepositoryByID(ctx, c.RefRepoID)
   219  		if err != nil {
   220  			return false
   221  		}
   222  		perm, err := access_model.GetUserRepoPermission(ctx, c.RefRepo, user)
   223  		if err != nil {
   224  			return false
   225  		}
   226  		if !perm.CanReadIssuesOrPulls(c.RefIsPull) {
   227  			return false
   228  		}
   229  	}
   230  	return true
   231  }
   232  
   233  // ListRepoIssueComments returns all issue-comments for a repo
   234  func ListRepoIssueComments(ctx *context.APIContext) {
   235  	// swagger:operation GET /repos/{owner}/{repo}/issues/comments issue issueGetRepoComments
   236  	// ---
   237  	// summary: List all comments in a repository
   238  	// produces:
   239  	// - application/json
   240  	// parameters:
   241  	// - name: owner
   242  	//   in: path
   243  	//   description: owner of the repo
   244  	//   type: string
   245  	//   required: true
   246  	// - name: repo
   247  	//   in: path
   248  	//   description: name of the repo
   249  	//   type: string
   250  	//   required: true
   251  	// - name: since
   252  	//   in: query
   253  	//   description: if provided, only comments updated since the provided time are returned.
   254  	//   type: string
   255  	//   format: date-time
   256  	// - name: before
   257  	//   in: query
   258  	//   description: if provided, only comments updated before the provided time are returned.
   259  	//   type: string
   260  	//   format: date-time
   261  	// - name: page
   262  	//   in: query
   263  	//   description: page number of results to return (1-based)
   264  	//   type: integer
   265  	// - name: limit
   266  	//   in: query
   267  	//   description: page size of results
   268  	//   type: integer
   269  	// responses:
   270  	//   "200":
   271  	//     "$ref": "#/responses/CommentList"
   272  	//   "404":
   273  	//     "$ref": "#/responses/notFound"
   274  
   275  	before, since, err := context.GetQueryBeforeSince(ctx.Base)
   276  	if err != nil {
   277  		ctx.Error(http.StatusUnprocessableEntity, "GetQueryBeforeSince", err)
   278  		return
   279  	}
   280  
   281  	var isPull optional.Option[bool]
   282  	canReadIssue := ctx.Repo.CanRead(unit.TypeIssues)
   283  	canReadPull := ctx.Repo.CanRead(unit.TypePullRequests)
   284  	if canReadIssue && canReadPull {
   285  		isPull = optional.None[bool]()
   286  	} else if canReadIssue {
   287  		isPull = optional.Some(false)
   288  	} else if canReadPull {
   289  		isPull = optional.Some(true)
   290  	} else {
   291  		ctx.NotFound()
   292  		return
   293  	}
   294  
   295  	opts := &issues_model.FindCommentsOptions{
   296  		ListOptions: utils.GetListOptions(ctx),
   297  		RepoID:      ctx.Repo.Repository.ID,
   298  		Type:        issues_model.CommentTypeComment,
   299  		Since:       since,
   300  		Before:      before,
   301  		IsPull:      isPull,
   302  	}
   303  
   304  	comments, err := issues_model.FindComments(ctx, opts)
   305  	if err != nil {
   306  		ctx.Error(http.StatusInternalServerError, "FindComments", err)
   307  		return
   308  	}
   309  
   310  	totalCount, err := issues_model.CountComments(ctx, opts)
   311  	if err != nil {
   312  		ctx.InternalServerError(err)
   313  		return
   314  	}
   315  
   316  	if err = comments.LoadPosters(ctx); err != nil {
   317  		ctx.Error(http.StatusInternalServerError, "LoadPosters", err)
   318  		return
   319  	}
   320  
   321  	apiComments := make([]*api.Comment, len(comments))
   322  	if err := comments.LoadIssues(ctx); err != nil {
   323  		ctx.Error(http.StatusInternalServerError, "LoadIssues", err)
   324  		return
   325  	}
   326  	if err := comments.LoadAttachments(ctx); err != nil {
   327  		ctx.Error(http.StatusInternalServerError, "LoadAttachments", err)
   328  		return
   329  	}
   330  	if _, err := comments.Issues().LoadRepositories(ctx); err != nil {
   331  		ctx.Error(http.StatusInternalServerError, "LoadRepositories", err)
   332  		return
   333  	}
   334  	for i := range comments {
   335  		apiComments[i] = convert.ToAPIComment(ctx, ctx.Repo.Repository, comments[i])
   336  	}
   337  
   338  	ctx.SetTotalCountHeader(totalCount)
   339  	ctx.JSON(http.StatusOK, &apiComments)
   340  }
   341  
   342  // CreateIssueComment create a comment for an issue
   343  func CreateIssueComment(ctx *context.APIContext) {
   344  	// swagger:operation POST /repos/{owner}/{repo}/issues/{index}/comments issue issueCreateComment
   345  	// ---
   346  	// summary: Add a comment to an issue
   347  	// consumes:
   348  	// - application/json
   349  	// produces:
   350  	// - application/json
   351  	// parameters:
   352  	// - name: owner
   353  	//   in: path
   354  	//   description: owner of the repo
   355  	//   type: string
   356  	//   required: true
   357  	// - name: repo
   358  	//   in: path
   359  	//   description: name of the repo
   360  	//   type: string
   361  	//   required: true
   362  	// - name: index
   363  	//   in: path
   364  	//   description: index of the issue
   365  	//   type: integer
   366  	//   format: int64
   367  	//   required: true
   368  	// - name: body
   369  	//   in: body
   370  	//   schema:
   371  	//     "$ref": "#/definitions/CreateIssueCommentOption"
   372  	// responses:
   373  	//   "201":
   374  	//     "$ref": "#/responses/Comment"
   375  	//   "403":
   376  	//     "$ref": "#/responses/forbidden"
   377  	//   "404":
   378  	//     "$ref": "#/responses/notFound"
   379  	//   "423":
   380  	//     "$ref": "#/responses/repoArchivedError"
   381  
   382  	form := web.GetForm(ctx).(*api.CreateIssueCommentOption)
   383  	issue, err := issues_model.GetIssueByIndex(ctx, ctx.Repo.Repository.ID, ctx.ParamsInt64(":index"))
   384  	if err != nil {
   385  		ctx.Error(http.StatusInternalServerError, "GetIssueByIndex", err)
   386  		return
   387  	}
   388  
   389  	if !ctx.Repo.CanReadIssuesOrPulls(issue.IsPull) {
   390  		ctx.NotFound()
   391  		return
   392  	}
   393  
   394  	if issue.IsLocked && !ctx.Repo.CanWriteIssuesOrPulls(issue.IsPull) && !ctx.Doer.IsAdmin {
   395  		ctx.Error(http.StatusForbidden, "CreateIssueComment", errors.New(ctx.Locale.TrString("repo.issues.comment_on_locked")))
   396  		return
   397  	}
   398  
   399  	comment, err := issue_service.CreateIssueComment(ctx, ctx.Doer, ctx.Repo.Repository, issue, form.Body, nil)
   400  	if err != nil {
   401  		if errors.Is(err, user_model.ErrBlockedUser) {
   402  			ctx.Error(http.StatusForbidden, "CreateIssueComment", err)
   403  		} else {
   404  			ctx.Error(http.StatusInternalServerError, "CreateIssueComment", err)
   405  		}
   406  		return
   407  	}
   408  
   409  	ctx.JSON(http.StatusCreated, convert.ToAPIComment(ctx, ctx.Repo.Repository, comment))
   410  }
   411  
   412  // GetIssueComment Get a comment by ID
   413  func GetIssueComment(ctx *context.APIContext) {
   414  	// swagger:operation GET /repos/{owner}/{repo}/issues/comments/{id} issue issueGetComment
   415  	// ---
   416  	// summary: Get a comment
   417  	// consumes:
   418  	// - application/json
   419  	// produces:
   420  	// - application/json
   421  	// parameters:
   422  	// - name: owner
   423  	//   in: path
   424  	//   description: owner of the repo
   425  	//   type: string
   426  	//   required: true
   427  	// - name: repo
   428  	//   in: path
   429  	//   description: name of the repo
   430  	//   type: string
   431  	//   required: true
   432  	// - name: id
   433  	//   in: path
   434  	//   description: id of the comment
   435  	//   type: integer
   436  	//   format: int64
   437  	//   required: true
   438  	// responses:
   439  	//   "200":
   440  	//     "$ref": "#/responses/Comment"
   441  	//   "204":
   442  	//     "$ref": "#/responses/empty"
   443  	//   "403":
   444  	//     "$ref": "#/responses/forbidden"
   445  	//   "404":
   446  	//     "$ref": "#/responses/notFound"
   447  
   448  	comment, err := issues_model.GetCommentByID(ctx, ctx.ParamsInt64(":id"))
   449  	if err != nil {
   450  		if issues_model.IsErrCommentNotExist(err) {
   451  			ctx.NotFound(err)
   452  		} else {
   453  			ctx.Error(http.StatusInternalServerError, "GetCommentByID", err)
   454  		}
   455  		return
   456  	}
   457  
   458  	if err = comment.LoadIssue(ctx); err != nil {
   459  		ctx.InternalServerError(err)
   460  		return
   461  	}
   462  	if comment.Issue.RepoID != ctx.Repo.Repository.ID {
   463  		ctx.Status(http.StatusNotFound)
   464  		return
   465  	}
   466  
   467  	if !ctx.Repo.CanReadIssuesOrPulls(comment.Issue.IsPull) {
   468  		ctx.NotFound()
   469  		return
   470  	}
   471  
   472  	if comment.Type != issues_model.CommentTypeComment {
   473  		ctx.Status(http.StatusNoContent)
   474  		return
   475  	}
   476  
   477  	if err := comment.LoadPoster(ctx); err != nil {
   478  		ctx.Error(http.StatusInternalServerError, "comment.LoadPoster", err)
   479  		return
   480  	}
   481  
   482  	ctx.JSON(http.StatusOK, convert.ToAPIComment(ctx, ctx.Repo.Repository, comment))
   483  }
   484  
   485  // EditIssueComment modify a comment of an issue
   486  func EditIssueComment(ctx *context.APIContext) {
   487  	// swagger:operation PATCH /repos/{owner}/{repo}/issues/comments/{id} issue issueEditComment
   488  	// ---
   489  	// summary: Edit a comment
   490  	// consumes:
   491  	// - application/json
   492  	// produces:
   493  	// - application/json
   494  	// parameters:
   495  	// - name: owner
   496  	//   in: path
   497  	//   description: owner of the repo
   498  	//   type: string
   499  	//   required: true
   500  	// - name: repo
   501  	//   in: path
   502  	//   description: name of the repo
   503  	//   type: string
   504  	//   required: true
   505  	// - name: id
   506  	//   in: path
   507  	//   description: id of the comment to edit
   508  	//   type: integer
   509  	//   format: int64
   510  	//   required: true
   511  	// - name: body
   512  	//   in: body
   513  	//   schema:
   514  	//     "$ref": "#/definitions/EditIssueCommentOption"
   515  	// responses:
   516  	//   "200":
   517  	//     "$ref": "#/responses/Comment"
   518  	//   "204":
   519  	//     "$ref": "#/responses/empty"
   520  	//   "403":
   521  	//     "$ref": "#/responses/forbidden"
   522  	//   "404":
   523  	//     "$ref": "#/responses/notFound"
   524  	//   "423":
   525  	//     "$ref": "#/responses/repoArchivedError"
   526  
   527  	form := web.GetForm(ctx).(*api.EditIssueCommentOption)
   528  	editIssueComment(ctx, *form)
   529  }
   530  
   531  // EditIssueCommentDeprecated modify a comment of an issue
   532  func EditIssueCommentDeprecated(ctx *context.APIContext) {
   533  	// swagger:operation PATCH /repos/{owner}/{repo}/issues/{index}/comments/{id} issue issueEditCommentDeprecated
   534  	// ---
   535  	// summary: Edit a comment
   536  	// deprecated: true
   537  	// consumes:
   538  	// - application/json
   539  	// produces:
   540  	// - application/json
   541  	// parameters:
   542  	// - name: owner
   543  	//   in: path
   544  	//   description: owner of the repo
   545  	//   type: string
   546  	//   required: true
   547  	// - name: repo
   548  	//   in: path
   549  	//   description: name of the repo
   550  	//   type: string
   551  	//   required: true
   552  	// - name: index
   553  	//   in: path
   554  	//   description: this parameter is ignored
   555  	//   type: integer
   556  	//   required: true
   557  	// - name: id
   558  	//   in: path
   559  	//   description: id of the comment to edit
   560  	//   type: integer
   561  	//   format: int64
   562  	//   required: true
   563  	// - name: body
   564  	//   in: body
   565  	//   schema:
   566  	//     "$ref": "#/definitions/EditIssueCommentOption"
   567  	// responses:
   568  	//   "200":
   569  	//     "$ref": "#/responses/Comment"
   570  	//   "204":
   571  	//     "$ref": "#/responses/empty"
   572  	//   "403":
   573  	//     "$ref": "#/responses/forbidden"
   574  	//   "404":
   575  	//     "$ref": "#/responses/notFound"
   576  
   577  	form := web.GetForm(ctx).(*api.EditIssueCommentOption)
   578  	editIssueComment(ctx, *form)
   579  }
   580  
   581  func editIssueComment(ctx *context.APIContext, form api.EditIssueCommentOption) {
   582  	comment, err := issues_model.GetCommentByID(ctx, ctx.ParamsInt64(":id"))
   583  	if err != nil {
   584  		if issues_model.IsErrCommentNotExist(err) {
   585  			ctx.NotFound(err)
   586  		} else {
   587  			ctx.Error(http.StatusInternalServerError, "GetCommentByID", err)
   588  		}
   589  		return
   590  	}
   591  
   592  	if err := comment.LoadIssue(ctx); err != nil {
   593  		ctx.Error(http.StatusInternalServerError, "LoadIssue", err)
   594  		return
   595  	}
   596  
   597  	if comment.Issue.RepoID != ctx.Repo.Repository.ID {
   598  		ctx.Status(http.StatusNotFound)
   599  		return
   600  	}
   601  
   602  	if !ctx.IsSigned || (ctx.Doer.ID != comment.PosterID && !ctx.Repo.CanWriteIssuesOrPulls(comment.Issue.IsPull)) {
   603  		ctx.Status(http.StatusForbidden)
   604  		return
   605  	}
   606  
   607  	if !comment.Type.HasContentSupport() {
   608  		ctx.Status(http.StatusNoContent)
   609  		return
   610  	}
   611  
   612  	oldContent := comment.Content
   613  	comment.Content = form.Body
   614  	if err := issue_service.UpdateComment(ctx, comment, ctx.Doer, oldContent); err != nil {
   615  		if errors.Is(err, user_model.ErrBlockedUser) {
   616  			ctx.Error(http.StatusForbidden, "UpdateComment", err)
   617  		} else {
   618  			ctx.Error(http.StatusInternalServerError, "UpdateComment", err)
   619  		}
   620  		return
   621  	}
   622  
   623  	ctx.JSON(http.StatusOK, convert.ToAPIComment(ctx, ctx.Repo.Repository, comment))
   624  }
   625  
   626  // DeleteIssueComment delete a comment from an issue
   627  func DeleteIssueComment(ctx *context.APIContext) {
   628  	// swagger:operation DELETE /repos/{owner}/{repo}/issues/comments/{id} issue issueDeleteComment
   629  	// ---
   630  	// summary: Delete a comment
   631  	// parameters:
   632  	// - name: owner
   633  	//   in: path
   634  	//   description: owner of the repo
   635  	//   type: string
   636  	//   required: true
   637  	// - name: repo
   638  	//   in: path
   639  	//   description: name of the repo
   640  	//   type: string
   641  	//   required: true
   642  	// - name: id
   643  	//   in: path
   644  	//   description: id of comment to delete
   645  	//   type: integer
   646  	//   format: int64
   647  	//   required: true
   648  	// responses:
   649  	//   "204":
   650  	//     "$ref": "#/responses/empty"
   651  	//   "403":
   652  	//     "$ref": "#/responses/forbidden"
   653  	//   "404":
   654  	//     "$ref": "#/responses/notFound"
   655  
   656  	deleteIssueComment(ctx)
   657  }
   658  
   659  // DeleteIssueCommentDeprecated delete a comment from an issue
   660  func DeleteIssueCommentDeprecated(ctx *context.APIContext) {
   661  	// swagger:operation DELETE /repos/{owner}/{repo}/issues/{index}/comments/{id} issue issueDeleteCommentDeprecated
   662  	// ---
   663  	// summary: Delete a comment
   664  	// deprecated: true
   665  	// parameters:
   666  	// - name: owner
   667  	//   in: path
   668  	//   description: owner of the repo
   669  	//   type: string
   670  	//   required: true
   671  	// - name: repo
   672  	//   in: path
   673  	//   description: name of the repo
   674  	//   type: string
   675  	//   required: true
   676  	// - name: index
   677  	//   in: path
   678  	//   description: this parameter is ignored
   679  	//   type: integer
   680  	//   required: true
   681  	// - name: id
   682  	//   in: path
   683  	//   description: id of comment to delete
   684  	//   type: integer
   685  	//   format: int64
   686  	//   required: true
   687  	// responses:
   688  	//   "204":
   689  	//     "$ref": "#/responses/empty"
   690  	//   "403":
   691  	//     "$ref": "#/responses/forbidden"
   692  	//   "404":
   693  	//     "$ref": "#/responses/notFound"
   694  
   695  	deleteIssueComment(ctx)
   696  }
   697  
   698  func deleteIssueComment(ctx *context.APIContext) {
   699  	comment, err := issues_model.GetCommentByID(ctx, ctx.ParamsInt64(":id"))
   700  	if err != nil {
   701  		if issues_model.IsErrCommentNotExist(err) {
   702  			ctx.NotFound(err)
   703  		} else {
   704  			ctx.Error(http.StatusInternalServerError, "GetCommentByID", err)
   705  		}
   706  		return
   707  	}
   708  
   709  	if err := comment.LoadIssue(ctx); err != nil {
   710  		ctx.Error(http.StatusInternalServerError, "LoadIssue", err)
   711  		return
   712  	}
   713  
   714  	if comment.Issue.RepoID != ctx.Repo.Repository.ID {
   715  		ctx.Status(http.StatusNotFound)
   716  		return
   717  	}
   718  
   719  	if !ctx.IsSigned || (ctx.Doer.ID != comment.PosterID && !ctx.Repo.CanWriteIssuesOrPulls(comment.Issue.IsPull)) {
   720  		ctx.Status(http.StatusForbidden)
   721  		return
   722  	} else if comment.Type != issues_model.CommentTypeComment {
   723  		ctx.Status(http.StatusNoContent)
   724  		return
   725  	}
   726  
   727  	if err = issue_service.DeleteComment(ctx, ctx.Doer, comment); err != nil {
   728  		ctx.Error(http.StatusInternalServerError, "DeleteCommentByID", err)
   729  		return
   730  	}
   731  
   732  	ctx.Status(http.StatusNoContent)
   733  }