Ви не можете вибрати більше 25 тем Теми мають розпочинатися з літери або цифри, можуть містити дефіси (-) і не повинні перевищувати 35 символів.

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