code.gitea.io/gitea@v1.21.7/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/context"
    18  	api "code.gitea.io/gitea/modules/structs"
    19  	"code.gitea.io/gitea/modules/util"
    20  	"code.gitea.io/gitea/modules/web"
    21  	"code.gitea.io/gitea/routers/api/v1/utils"
    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 util.OptionalBool
   282  	canReadIssue := ctx.Repo.CanRead(unit.TypeIssues)
   283  	canReadPull := ctx.Repo.CanRead(unit.TypePullRequests)
   284  	if canReadIssue && canReadPull {
   285  		isPull = util.OptionalBoolNone
   286  	} else if canReadIssue {
   287  		isPull = util.OptionalBoolFalse
   288  	} else if canReadPull {
   289  		isPull = util.OptionalBoolTrue
   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.LoadPosters(ctx); err != nil {
   327  		ctx.Error(http.StatusInternalServerError, "LoadPosters", err)
   328  		return
   329  	}
   330  	if err := comments.LoadAttachments(ctx); err != nil {
   331  		ctx.Error(http.StatusInternalServerError, "LoadAttachments", err)
   332  		return
   333  	}
   334  	if _, err := comments.Issues().LoadRepositories(ctx); err != nil {
   335  		ctx.Error(http.StatusInternalServerError, "LoadRepositories", err)
   336  		return
   337  	}
   338  	for i := range comments {
   339  		apiComments[i] = convert.ToAPIComment(ctx, ctx.Repo.Repository, comments[i])
   340  	}
   341  
   342  	ctx.SetTotalCountHeader(totalCount)
   343  	ctx.JSON(http.StatusOK, &apiComments)
   344  }
   345  
   346  // CreateIssueComment create a comment for an issue
   347  func CreateIssueComment(ctx *context.APIContext) {
   348  	// swagger:operation POST /repos/{owner}/{repo}/issues/{index}/comments issue issueCreateComment
   349  	// ---
   350  	// summary: Add a comment to an issue
   351  	// consumes:
   352  	// - application/json
   353  	// produces:
   354  	// - application/json
   355  	// parameters:
   356  	// - name: owner
   357  	//   in: path
   358  	//   description: owner of the repo
   359  	//   type: string
   360  	//   required: true
   361  	// - name: repo
   362  	//   in: path
   363  	//   description: name of the repo
   364  	//   type: string
   365  	//   required: true
   366  	// - name: index
   367  	//   in: path
   368  	//   description: index of the issue
   369  	//   type: integer
   370  	//   format: int64
   371  	//   required: true
   372  	// - name: body
   373  	//   in: body
   374  	//   schema:
   375  	//     "$ref": "#/definitions/CreateIssueCommentOption"
   376  	// responses:
   377  	//   "201":
   378  	//     "$ref": "#/responses/Comment"
   379  	//   "403":
   380  	//     "$ref": "#/responses/forbidden"
   381  	//   "404":
   382  	//     "$ref": "#/responses/notFound"
   383  	form := web.GetForm(ctx).(*api.CreateIssueCommentOption)
   384  	issue, err := issues_model.GetIssueByIndex(ctx, ctx.Repo.Repository.ID, ctx.ParamsInt64(":index"))
   385  	if err != nil {
   386  		ctx.Error(http.StatusInternalServerError, "GetIssueByIndex", err)
   387  		return
   388  	}
   389  
   390  	if !ctx.Repo.CanReadIssuesOrPulls(issue.IsPull) {
   391  		ctx.NotFound()
   392  		return
   393  	}
   394  
   395  	if issue.IsLocked && !ctx.Repo.CanWriteIssuesOrPulls(issue.IsPull) && !ctx.Doer.IsAdmin {
   396  		ctx.Error(http.StatusForbidden, "CreateIssueComment", errors.New(ctx.Tr("repo.issues.comment_on_locked")))
   397  		return
   398  	}
   399  
   400  	comment, err := issue_service.CreateIssueComment(ctx, ctx.Doer, ctx.Repo.Repository, issue, form.Body, nil)
   401  	if err != nil {
   402  		ctx.Error(http.StatusInternalServerError, "CreateIssueComment", err)
   403  		return
   404  	}
   405  
   406  	ctx.JSON(http.StatusCreated, convert.ToAPIComment(ctx, ctx.Repo.Repository, comment))
   407  }
   408  
   409  // GetIssueComment Get a comment by ID
   410  func GetIssueComment(ctx *context.APIContext) {
   411  	// swagger:operation GET /repos/{owner}/{repo}/issues/comments/{id} issue issueGetComment
   412  	// ---
   413  	// summary: Get a comment
   414  	// consumes:
   415  	// - application/json
   416  	// produces:
   417  	// - application/json
   418  	// parameters:
   419  	// - name: owner
   420  	//   in: path
   421  	//   description: owner of the repo
   422  	//   type: string
   423  	//   required: true
   424  	// - name: repo
   425  	//   in: path
   426  	//   description: name of the repo
   427  	//   type: string
   428  	//   required: true
   429  	// - name: id
   430  	//   in: path
   431  	//   description: id of the comment
   432  	//   type: integer
   433  	//   format: int64
   434  	//   required: true
   435  	// responses:
   436  	//   "200":
   437  	//     "$ref": "#/responses/Comment"
   438  	//   "204":
   439  	//     "$ref": "#/responses/empty"
   440  	//   "403":
   441  	//     "$ref": "#/responses/forbidden"
   442  	//   "404":
   443  	//     "$ref": "#/responses/notFound"
   444  
   445  	comment, err := issues_model.GetCommentByID(ctx, ctx.ParamsInt64(":id"))
   446  	if err != nil {
   447  		if issues_model.IsErrCommentNotExist(err) {
   448  			ctx.NotFound(err)
   449  		} else {
   450  			ctx.Error(http.StatusInternalServerError, "GetCommentByID", err)
   451  		}
   452  		return
   453  	}
   454  
   455  	if err = comment.LoadIssue(ctx); err != nil {
   456  		ctx.InternalServerError(err)
   457  		return
   458  	}
   459  	if comment.Issue.RepoID != ctx.Repo.Repository.ID {
   460  		ctx.Status(http.StatusNotFound)
   461  		return
   462  	}
   463  
   464  	if !ctx.Repo.CanReadIssuesOrPulls(comment.Issue.IsPull) {
   465  		ctx.NotFound()
   466  		return
   467  	}
   468  
   469  	if comment.Type != issues_model.CommentTypeComment {
   470  		ctx.Status(http.StatusNoContent)
   471  		return
   472  	}
   473  
   474  	if err := comment.LoadPoster(ctx); err != nil {
   475  		ctx.Error(http.StatusInternalServerError, "comment.LoadPoster", err)
   476  		return
   477  	}
   478  
   479  	ctx.JSON(http.StatusOK, convert.ToAPIComment(ctx, ctx.Repo.Repository, comment))
   480  }
   481  
   482  // EditIssueComment modify a comment of an issue
   483  func EditIssueComment(ctx *context.APIContext) {
   484  	// swagger:operation PATCH /repos/{owner}/{repo}/issues/comments/{id} issue issueEditComment
   485  	// ---
   486  	// summary: Edit a comment
   487  	// consumes:
   488  	// - application/json
   489  	// produces:
   490  	// - application/json
   491  	// parameters:
   492  	// - name: owner
   493  	//   in: path
   494  	//   description: owner of the repo
   495  	//   type: string
   496  	//   required: true
   497  	// - name: repo
   498  	//   in: path
   499  	//   description: name of the repo
   500  	//   type: string
   501  	//   required: true
   502  	// - name: id
   503  	//   in: path
   504  	//   description: id of the comment to edit
   505  	//   type: integer
   506  	//   format: int64
   507  	//   required: true
   508  	// - name: body
   509  	//   in: body
   510  	//   schema:
   511  	//     "$ref": "#/definitions/EditIssueCommentOption"
   512  	// responses:
   513  	//   "200":
   514  	//     "$ref": "#/responses/Comment"
   515  	//   "204":
   516  	//     "$ref": "#/responses/empty"
   517  	//   "403":
   518  	//     "$ref": "#/responses/forbidden"
   519  	//   "404":
   520  	//     "$ref": "#/responses/notFound"
   521  
   522  	form := web.GetForm(ctx).(*api.EditIssueCommentOption)
   523  	editIssueComment(ctx, *form)
   524  }
   525  
   526  // EditIssueCommentDeprecated modify a comment of an issue
   527  func EditIssueCommentDeprecated(ctx *context.APIContext) {
   528  	// swagger:operation PATCH /repos/{owner}/{repo}/issues/{index}/comments/{id} issue issueEditCommentDeprecated
   529  	// ---
   530  	// summary: Edit a comment
   531  	// deprecated: true
   532  	// consumes:
   533  	// - application/json
   534  	// produces:
   535  	// - application/json
   536  	// parameters:
   537  	// - name: owner
   538  	//   in: path
   539  	//   description: owner of the repo
   540  	//   type: string
   541  	//   required: true
   542  	// - name: repo
   543  	//   in: path
   544  	//   description: name of the repo
   545  	//   type: string
   546  	//   required: true
   547  	// - name: index
   548  	//   in: path
   549  	//   description: this parameter is ignored
   550  	//   type: integer
   551  	//   required: true
   552  	// - name: id
   553  	//   in: path
   554  	//   description: id of the comment to edit
   555  	//   type: integer
   556  	//   format: int64
   557  	//   required: true
   558  	// - name: body
   559  	//   in: body
   560  	//   schema:
   561  	//     "$ref": "#/definitions/EditIssueCommentOption"
   562  	// responses:
   563  	//   "200":
   564  	//     "$ref": "#/responses/Comment"
   565  	//   "204":
   566  	//     "$ref": "#/responses/empty"
   567  	//   "403":
   568  	//     "$ref": "#/responses/forbidden"
   569  	//   "404":
   570  	//     "$ref": "#/responses/notFound"
   571  
   572  	form := web.GetForm(ctx).(*api.EditIssueCommentOption)
   573  	editIssueComment(ctx, *form)
   574  }
   575  
   576  func editIssueComment(ctx *context.APIContext, form api.EditIssueCommentOption) {
   577  	comment, err := issues_model.GetCommentByID(ctx, ctx.ParamsInt64(":id"))
   578  	if err != nil {
   579  		if issues_model.IsErrCommentNotExist(err) {
   580  			ctx.NotFound(err)
   581  		} else {
   582  			ctx.Error(http.StatusInternalServerError, "GetCommentByID", err)
   583  		}
   584  		return
   585  	}
   586  
   587  	if err := comment.LoadIssue(ctx); err != nil {
   588  		ctx.Error(http.StatusInternalServerError, "LoadIssue", err)
   589  		return
   590  	}
   591  
   592  	if comment.Issue.RepoID != ctx.Repo.Repository.ID {
   593  		ctx.Status(http.StatusNotFound)
   594  		return
   595  	}
   596  
   597  	if !ctx.IsSigned || (ctx.Doer.ID != comment.PosterID && !ctx.Repo.CanWriteIssuesOrPulls(comment.Issue.IsPull)) {
   598  		ctx.Status(http.StatusForbidden)
   599  		return
   600  	}
   601  
   602  	if !comment.Type.HasContentSupport() {
   603  		ctx.Status(http.StatusNoContent)
   604  		return
   605  	}
   606  
   607  	oldContent := comment.Content
   608  	comment.Content = form.Body
   609  	if err := issue_service.UpdateComment(ctx, comment, ctx.Doer, oldContent); err != nil {
   610  		ctx.Error(http.StatusInternalServerError, "UpdateComment", err)
   611  		return
   612  	}
   613  
   614  	ctx.JSON(http.StatusOK, convert.ToAPIComment(ctx, ctx.Repo.Repository, comment))
   615  }
   616  
   617  // DeleteIssueComment delete a comment from an issue
   618  func DeleteIssueComment(ctx *context.APIContext) {
   619  	// swagger:operation DELETE /repos/{owner}/{repo}/issues/comments/{id} issue issueDeleteComment
   620  	// ---
   621  	// summary: Delete a comment
   622  	// parameters:
   623  	// - name: owner
   624  	//   in: path
   625  	//   description: owner of the repo
   626  	//   type: string
   627  	//   required: true
   628  	// - name: repo
   629  	//   in: path
   630  	//   description: name of the repo
   631  	//   type: string
   632  	//   required: true
   633  	// - name: id
   634  	//   in: path
   635  	//   description: id of comment to delete
   636  	//   type: integer
   637  	//   format: int64
   638  	//   required: true
   639  	// responses:
   640  	//   "204":
   641  	//     "$ref": "#/responses/empty"
   642  	//   "403":
   643  	//     "$ref": "#/responses/forbidden"
   644  	//   "404":
   645  	//     "$ref": "#/responses/notFound"
   646  
   647  	deleteIssueComment(ctx)
   648  }
   649  
   650  // DeleteIssueCommentDeprecated delete a comment from an issue
   651  func DeleteIssueCommentDeprecated(ctx *context.APIContext) {
   652  	// swagger:operation DELETE /repos/{owner}/{repo}/issues/{index}/comments/{id} issue issueDeleteCommentDeprecated
   653  	// ---
   654  	// summary: Delete a comment
   655  	// deprecated: true
   656  	// parameters:
   657  	// - name: owner
   658  	//   in: path
   659  	//   description: owner of the repo
   660  	//   type: string
   661  	//   required: true
   662  	// - name: repo
   663  	//   in: path
   664  	//   description: name of the repo
   665  	//   type: string
   666  	//   required: true
   667  	// - name: index
   668  	//   in: path
   669  	//   description: this parameter is ignored
   670  	//   type: integer
   671  	//   required: true
   672  	// - name: id
   673  	//   in: path
   674  	//   description: id of comment to delete
   675  	//   type: integer
   676  	//   format: int64
   677  	//   required: true
   678  	// responses:
   679  	//   "204":
   680  	//     "$ref": "#/responses/empty"
   681  	//   "403":
   682  	//     "$ref": "#/responses/forbidden"
   683  	//   "404":
   684  	//     "$ref": "#/responses/notFound"
   685  
   686  	deleteIssueComment(ctx)
   687  }
   688  
   689  func deleteIssueComment(ctx *context.APIContext) {
   690  	comment, err := issues_model.GetCommentByID(ctx, ctx.ParamsInt64(":id"))
   691  	if err != nil {
   692  		if issues_model.IsErrCommentNotExist(err) {
   693  			ctx.NotFound(err)
   694  		} else {
   695  			ctx.Error(http.StatusInternalServerError, "GetCommentByID", err)
   696  		}
   697  		return
   698  	}
   699  
   700  	if err := comment.LoadIssue(ctx); err != nil {
   701  		ctx.Error(http.StatusInternalServerError, "LoadIssue", err)
   702  		return
   703  	}
   704  
   705  	if comment.Issue.RepoID != ctx.Repo.Repository.ID {
   706  		ctx.Status(http.StatusNotFound)
   707  		return
   708  	}
   709  
   710  	if !ctx.IsSigned || (ctx.Doer.ID != comment.PosterID && !ctx.Repo.CanWriteIssuesOrPulls(comment.Issue.IsPull)) {
   711  		ctx.Status(http.StatusForbidden)
   712  		return
   713  	} else if comment.Type != issues_model.CommentTypeComment {
   714  		ctx.Status(http.StatusNoContent)
   715  		return
   716  	}
   717  
   718  	if err = issue_service.DeleteComment(ctx, ctx.Doer, comment); err != nil {
   719  		ctx.Error(http.StatusInternalServerError, "DeleteCommentByID", err)
   720  		return
   721  	}
   722  
   723  	ctx.Status(http.StatusNoContent)
   724  }