You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

issue_comment.go 13 kB

9 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507
  1. // Copyright 2015 The Gogs Authors. All rights reserved.
  2. // Use of this source code is governed by a MIT-style
  3. // license that can be found in the LICENSE file.
  4. package repo
  5. import (
  6. "errors"
  7. "net/http"
  8. "code.gitea.io/gitea/models"
  9. "code.gitea.io/gitea/modules/context"
  10. api "code.gitea.io/gitea/modules/structs"
  11. "code.gitea.io/gitea/routers/api/v1/utils"
  12. comment_service "code.gitea.io/gitea/services/comments"
  13. )
  14. // ListIssueComments list all the comments of an issue
  15. func ListIssueComments(ctx *context.APIContext) {
  16. // swagger:operation GET /repos/{owner}/{repo}/issues/{index}/comments issue issueGetComments
  17. // ---
  18. // summary: List all comments on an issue
  19. // produces:
  20. // - application/json
  21. // parameters:
  22. // - name: owner
  23. // in: path
  24. // description: owner of the repo
  25. // type: string
  26. // required: true
  27. // - name: repo
  28. // in: path
  29. // description: name of the repo
  30. // type: string
  31. // required: true
  32. // - name: index
  33. // in: path
  34. // description: index of the issue
  35. // type: integer
  36. // format: int64
  37. // required: true
  38. // - name: since
  39. // in: query
  40. // description: if provided, only comments updated since the specified time are returned.
  41. // type: string
  42. // format: date-time
  43. // - name: before
  44. // in: query
  45. // description: if provided, only comments updated before the provided time are returned.
  46. // type: string
  47. // format: date-time
  48. // responses:
  49. // "200":
  50. // "$ref": "#/responses/CommentList"
  51. before, since, err := utils.GetQueryBeforeSince(ctx)
  52. if err != nil {
  53. ctx.Error(http.StatusInternalServerError, "GetQueryBeforeSince", err)
  54. return
  55. }
  56. issue, err := models.GetIssueByIndex(ctx.Repo.Repository.ID, ctx.ParamsInt64(":index"))
  57. if err != nil {
  58. ctx.Error(http.StatusInternalServerError, "GetRawIssueByIndex", err)
  59. return
  60. }
  61. issue.Repo = ctx.Repo.Repository
  62. comments, err := models.FindComments(models.FindCommentsOptions{
  63. IssueID: issue.ID,
  64. Since: since,
  65. Before: before,
  66. Type: models.CommentTypeComment,
  67. })
  68. if err != nil {
  69. ctx.Error(http.StatusInternalServerError, "FindComments", err)
  70. return
  71. }
  72. if err := models.CommentList(comments).LoadPosters(); err != nil {
  73. ctx.Error(http.StatusInternalServerError, "LoadPosters", err)
  74. return
  75. }
  76. apiComments := make([]*api.Comment, len(comments))
  77. for i, comment := range comments {
  78. comment.Issue = issue
  79. apiComments[i] = comments[i].APIFormat()
  80. }
  81. ctx.JSON(http.StatusOK, &apiComments)
  82. }
  83. // ListRepoIssueComments returns all issue-comments for a repo
  84. func ListRepoIssueComments(ctx *context.APIContext) {
  85. // swagger:operation GET /repos/{owner}/{repo}/issues/comments issue issueGetRepoComments
  86. // ---
  87. // summary: List all comments in a repository
  88. // produces:
  89. // - application/json
  90. // parameters:
  91. // - name: owner
  92. // in: path
  93. // description: owner of the repo
  94. // type: string
  95. // required: true
  96. // - name: repo
  97. // in: path
  98. // description: name of the repo
  99. // type: string
  100. // required: true
  101. // - name: since
  102. // in: query
  103. // description: if provided, only comments updated since the provided time are returned.
  104. // type: string
  105. // format: date-time
  106. // - name: before
  107. // in: query
  108. // description: if provided, only comments updated before the provided time are returned.
  109. // type: string
  110. // format: date-time
  111. // responses:
  112. // "200":
  113. // "$ref": "#/responses/CommentList"
  114. before, since, err := utils.GetQueryBeforeSince(ctx)
  115. if err != nil {
  116. ctx.Error(http.StatusInternalServerError, "GetQueryBeforeSince", err)
  117. return
  118. }
  119. comments, err := models.FindComments(models.FindCommentsOptions{
  120. RepoID: ctx.Repo.Repository.ID,
  121. Since: since,
  122. Before: before,
  123. Type: models.CommentTypeComment,
  124. })
  125. if err != nil {
  126. ctx.Error(http.StatusInternalServerError, "FindComments", err)
  127. return
  128. }
  129. if err = models.CommentList(comments).LoadPosters(); err != nil {
  130. ctx.Error(http.StatusInternalServerError, "LoadPosters", err)
  131. return
  132. }
  133. apiComments := make([]*api.Comment, len(comments))
  134. if err := models.CommentList(comments).LoadIssues(); err != nil {
  135. ctx.Error(http.StatusInternalServerError, "LoadIssues", err)
  136. return
  137. }
  138. if err := models.CommentList(comments).LoadPosters(); err != nil {
  139. ctx.Error(http.StatusInternalServerError, "LoadPosters", err)
  140. return
  141. }
  142. if _, err := models.CommentList(comments).Issues().LoadRepositories(); err != nil {
  143. ctx.Error(http.StatusInternalServerError, "LoadRepositories", err)
  144. return
  145. }
  146. for i := range comments {
  147. apiComments[i] = comments[i].APIFormat()
  148. }
  149. ctx.JSON(http.StatusOK, &apiComments)
  150. }
  151. // CreateIssueComment create a comment for an issue
  152. func CreateIssueComment(ctx *context.APIContext, form api.CreateIssueCommentOption) {
  153. // swagger:operation POST /repos/{owner}/{repo}/issues/{index}/comments issue issueCreateComment
  154. // ---
  155. // summary: Add a comment to an issue
  156. // consumes:
  157. // - application/json
  158. // produces:
  159. // - application/json
  160. // parameters:
  161. // - name: owner
  162. // in: path
  163. // description: owner of the repo
  164. // type: string
  165. // required: true
  166. // - name: repo
  167. // in: path
  168. // description: name of the repo
  169. // type: string
  170. // required: true
  171. // - name: index
  172. // in: path
  173. // description: index of the issue
  174. // type: integer
  175. // format: int64
  176. // required: true
  177. // - name: body
  178. // in: body
  179. // schema:
  180. // "$ref": "#/definitions/CreateIssueCommentOption"
  181. // responses:
  182. // "201":
  183. // "$ref": "#/responses/Comment"
  184. // "403":
  185. // "$ref": "#/responses/forbidden"
  186. issue, err := models.GetIssueByIndex(ctx.Repo.Repository.ID, ctx.ParamsInt64(":index"))
  187. if err != nil {
  188. ctx.Error(http.StatusInternalServerError, "GetIssueByIndex", err)
  189. return
  190. }
  191. if issue.IsLocked && !ctx.Repo.CanWrite(models.UnitTypeIssues) && !ctx.User.IsAdmin {
  192. ctx.Error(http.StatusForbidden, "CreateIssueComment", errors.New(ctx.Tr("repo.issues.comment_on_locked")))
  193. return
  194. }
  195. comment, err := comment_service.CreateIssueComment(ctx.User, ctx.Repo.Repository, issue, form.Body, nil)
  196. if err != nil {
  197. ctx.Error(http.StatusInternalServerError, "CreateIssueComment", err)
  198. return
  199. }
  200. ctx.JSON(http.StatusCreated, comment.APIFormat())
  201. }
  202. // GetIssueComment Get a comment by ID
  203. func GetIssueComment(ctx *context.APIContext) {
  204. // swagger:operation GET /repos/{owner}/{repo}/issues/comments/{id} issue issueGetComment
  205. // ---
  206. // summary: Get a comment
  207. // consumes:
  208. // - application/json
  209. // produces:
  210. // - application/json
  211. // parameters:
  212. // - name: owner
  213. // in: path
  214. // description: owner of the repo
  215. // type: string
  216. // required: true
  217. // - name: repo
  218. // in: path
  219. // description: name of the repo
  220. // type: string
  221. // required: true
  222. // - name: id
  223. // in: path
  224. // description: id of the comment
  225. // type: integer
  226. // format: int64
  227. // required: true
  228. // responses:
  229. // "200":
  230. // "$ref": "#/responses/Comment"
  231. // "204":
  232. // "$ref": "#/responses/empty"
  233. // "403":
  234. // "$ref": "#/responses/forbidden"
  235. // "404":
  236. // "$ref": "#/responses/notFound"
  237. comment, err := models.GetCommentByID(ctx.ParamsInt64(":id"))
  238. if err != nil {
  239. if models.IsErrCommentNotExist(err) {
  240. ctx.NotFound(err)
  241. } else {
  242. ctx.Error(http.StatusInternalServerError, "GetCommentByID", err)
  243. }
  244. return
  245. }
  246. if err = comment.LoadIssue(); err != nil {
  247. ctx.InternalServerError(err)
  248. return
  249. }
  250. if comment.Issue.RepoID != ctx.Repo.Repository.ID {
  251. ctx.Status(http.StatusNotFound)
  252. return
  253. }
  254. if comment.Type != models.CommentTypeComment {
  255. ctx.Status(http.StatusNoContent)
  256. return
  257. }
  258. if err := comment.LoadPoster(); err != nil {
  259. ctx.Error(http.StatusInternalServerError, "comment.LoadPoster", err)
  260. return
  261. }
  262. ctx.JSON(http.StatusOK, comment.APIFormat())
  263. }
  264. // EditIssueComment modify a comment of an issue
  265. func EditIssueComment(ctx *context.APIContext, form api.EditIssueCommentOption) {
  266. // swagger:operation PATCH /repos/{owner}/{repo}/issues/comments/{id} issue issueEditComment
  267. // ---
  268. // summary: Edit a comment
  269. // consumes:
  270. // - application/json
  271. // produces:
  272. // - application/json
  273. // parameters:
  274. // - name: owner
  275. // in: path
  276. // description: owner of the repo
  277. // type: string
  278. // required: true
  279. // - name: repo
  280. // in: path
  281. // description: name of the repo
  282. // type: string
  283. // required: true
  284. // - name: id
  285. // in: path
  286. // description: id of the comment to edit
  287. // type: integer
  288. // format: int64
  289. // required: true
  290. // - name: body
  291. // in: body
  292. // schema:
  293. // "$ref": "#/definitions/EditIssueCommentOption"
  294. // responses:
  295. // "200":
  296. // "$ref": "#/responses/Comment"
  297. // "204":
  298. // "$ref": "#/responses/empty"
  299. // "403":
  300. // "$ref": "#/responses/forbidden"
  301. // "404":
  302. // "$ref": "#/responses/notFound"
  303. editIssueComment(ctx, form)
  304. }
  305. // EditIssueCommentDeprecated modify a comment of an issue
  306. func EditIssueCommentDeprecated(ctx *context.APIContext, form api.EditIssueCommentOption) {
  307. // swagger:operation PATCH /repos/{owner}/{repo}/issues/{index}/comments/{id} issue issueEditCommentDeprecated
  308. // ---
  309. // summary: Edit a comment
  310. // deprecated: true
  311. // consumes:
  312. // - application/json
  313. // produces:
  314. // - application/json
  315. // parameters:
  316. // - name: owner
  317. // in: path
  318. // description: owner of the repo
  319. // type: string
  320. // required: true
  321. // - name: repo
  322. // in: path
  323. // description: name of the repo
  324. // type: string
  325. // required: true
  326. // - name: index
  327. // in: path
  328. // description: this parameter is ignored
  329. // type: integer
  330. // required: true
  331. // - name: id
  332. // in: path
  333. // description: id of the comment to edit
  334. // type: integer
  335. // format: int64
  336. // required: true
  337. // - name: body
  338. // in: body
  339. // schema:
  340. // "$ref": "#/definitions/EditIssueCommentOption"
  341. // responses:
  342. // "200":
  343. // "$ref": "#/responses/Comment"
  344. // "204":
  345. // "$ref": "#/responses/empty"
  346. // "403":
  347. // "$ref": "#/responses/forbidden"
  348. // "404":
  349. // "$ref": "#/responses/notFound"
  350. editIssueComment(ctx, form)
  351. }
  352. func editIssueComment(ctx *context.APIContext, form api.EditIssueCommentOption) {
  353. comment, err := models.GetCommentByID(ctx.ParamsInt64(":id"))
  354. if err != nil {
  355. if models.IsErrCommentNotExist(err) {
  356. ctx.NotFound(err)
  357. } else {
  358. ctx.Error(http.StatusInternalServerError, "GetCommentByID", err)
  359. }
  360. return
  361. }
  362. if !ctx.IsSigned || (ctx.User.ID != comment.PosterID && !ctx.Repo.IsAdmin()) {
  363. ctx.Status(http.StatusForbidden)
  364. return
  365. } else if comment.Type != models.CommentTypeComment {
  366. ctx.Status(http.StatusNoContent)
  367. return
  368. }
  369. oldContent := comment.Content
  370. comment.Content = form.Body
  371. if err := comment_service.UpdateComment(comment, ctx.User, oldContent); err != nil {
  372. ctx.Error(http.StatusInternalServerError, "UpdateComment", err)
  373. return
  374. }
  375. ctx.JSON(http.StatusOK, comment.APIFormat())
  376. }
  377. // DeleteIssueComment delete a comment from an issue
  378. func DeleteIssueComment(ctx *context.APIContext) {
  379. // swagger:operation DELETE /repos/{owner}/{repo}/issues/comments/{id} issue issueDeleteComment
  380. // ---
  381. // summary: Delete a comment
  382. // parameters:
  383. // - name: owner
  384. // in: path
  385. // description: owner of the repo
  386. // type: string
  387. // required: true
  388. // - name: repo
  389. // in: path
  390. // description: name of the repo
  391. // type: string
  392. // required: true
  393. // - name: id
  394. // in: path
  395. // description: id of comment to delete
  396. // type: integer
  397. // format: int64
  398. // required: true
  399. // responses:
  400. // "204":
  401. // "$ref": "#/responses/empty"
  402. // "403":
  403. // "$ref": "#/responses/forbidden"
  404. // "404":
  405. // "$ref": "#/responses/notFound"
  406. deleteIssueComment(ctx)
  407. }
  408. // DeleteIssueCommentDeprecated delete a comment from an issue
  409. func DeleteIssueCommentDeprecated(ctx *context.APIContext) {
  410. // swagger:operation DELETE /repos/{owner}/{repo}/issues/{index}/comments/{id} issue issueDeleteCommentDeprecated
  411. // ---
  412. // summary: Delete a comment
  413. // deprecated: true
  414. // parameters:
  415. // - name: owner
  416. // in: path
  417. // description: owner of the repo
  418. // type: string
  419. // required: true
  420. // - name: repo
  421. // in: path
  422. // description: name of the repo
  423. // type: string
  424. // required: true
  425. // - name: index
  426. // in: path
  427. // description: this parameter is ignored
  428. // type: integer
  429. // required: true
  430. // - name: id
  431. // in: path
  432. // description: id of comment to delete
  433. // type: integer
  434. // format: int64
  435. // required: true
  436. // responses:
  437. // "204":
  438. // "$ref": "#/responses/empty"
  439. // "403":
  440. // "$ref": "#/responses/forbidden"
  441. // "404":
  442. // "$ref": "#/responses/notFound"
  443. deleteIssueComment(ctx)
  444. }
  445. func deleteIssueComment(ctx *context.APIContext) {
  446. comment, err := models.GetCommentByID(ctx.ParamsInt64(":id"))
  447. if err != nil {
  448. if models.IsErrCommentNotExist(err) {
  449. ctx.NotFound(err)
  450. } else {
  451. ctx.Error(http.StatusInternalServerError, "GetCommentByID", err)
  452. }
  453. return
  454. }
  455. if !ctx.IsSigned || (ctx.User.ID != comment.PosterID && !ctx.Repo.IsAdmin()) {
  456. ctx.Status(http.StatusForbidden)
  457. return
  458. } else if comment.Type != models.CommentTypeComment {
  459. ctx.Status(http.StatusNoContent)
  460. return
  461. }
  462. if err = comment_service.DeleteComment(comment, ctx.User); err != nil {
  463. ctx.Error(http.StatusInternalServerError, "DeleteCommentByID", err)
  464. return
  465. }
  466. ctx.Status(http.StatusNoContent)
  467. }