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_reaction.go 12 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421
  1. // Copyright 2019 The Gitea 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. )
  12. // GetIssueCommentReactions list reactions of a issue comment
  13. func GetIssueCommentReactions(ctx *context.APIContext) {
  14. // swagger:operation GET /repos/{owner}/{repo}/issues/comments/{id}/reactions issue issueGetCommentReactions
  15. // ---
  16. // summary: Get a list reactions of a issue comment
  17. // consumes:
  18. // - application/json
  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: id
  33. // in: path
  34. // description: id of the comment to edit
  35. // type: integer
  36. // format: int64
  37. // required: true
  38. // responses:
  39. // "200":
  40. // "$ref": "#/responses/ReactionList"
  41. // "403":
  42. // "$ref": "#/responses/forbidden"
  43. comment, err := models.GetCommentByID(ctx.ParamsInt64(":id"))
  44. if err != nil {
  45. if models.IsErrCommentNotExist(err) {
  46. ctx.NotFound(err)
  47. } else {
  48. ctx.Error(http.StatusInternalServerError, "GetCommentByID", err)
  49. }
  50. return
  51. }
  52. if !ctx.Repo.CanRead(models.UnitTypeIssues) && !ctx.User.IsAdmin {
  53. ctx.Error(http.StatusForbidden, "GetIssueCommentReactions", errors.New("no permission to get reactions"))
  54. return
  55. }
  56. reactions, err := models.FindCommentReactions(comment)
  57. if err != nil {
  58. ctx.Error(http.StatusInternalServerError, "FindIssueReactions", err)
  59. return
  60. }
  61. _, err = reactions.LoadUsers()
  62. if err != nil {
  63. ctx.Error(http.StatusInternalServerError, "ReactionList.LoadUsers()", err)
  64. return
  65. }
  66. var result []api.Reaction
  67. for _, r := range reactions {
  68. result = append(result, api.Reaction{
  69. User: r.User.APIFormat(),
  70. Reaction: r.Type,
  71. Created: r.CreatedUnix.AsTime(),
  72. })
  73. }
  74. ctx.JSON(http.StatusOK, result)
  75. }
  76. // PostIssueCommentReaction add a reaction to a comment of a issue
  77. func PostIssueCommentReaction(ctx *context.APIContext, form api.EditReactionOption) {
  78. // swagger:operation POST /repos/{owner}/{repo}/issues/comments/{id}/reactions issue issuePostCommentReaction
  79. // ---
  80. // summary: Add a reaction to a comment of a issue comment
  81. // consumes:
  82. // - application/json
  83. // produces:
  84. // - application/json
  85. // parameters:
  86. // - name: owner
  87. // in: path
  88. // description: owner of the repo
  89. // type: string
  90. // required: true
  91. // - name: repo
  92. // in: path
  93. // description: name of the repo
  94. // type: string
  95. // required: true
  96. // - name: id
  97. // in: path
  98. // description: id of the comment to edit
  99. // type: integer
  100. // format: int64
  101. // required: true
  102. // - name: content
  103. // in: body
  104. // schema:
  105. // "$ref": "#/definitions/EditReactionOption"
  106. // responses:
  107. // "200":
  108. // "$ref": "#/responses/Reaction"
  109. // "201":
  110. // "$ref": "#/responses/Reaction"
  111. // "403":
  112. // "$ref": "#/responses/forbidden"
  113. changeIssueCommentReaction(ctx, form, true)
  114. }
  115. // DeleteIssueCommentReaction list reactions of a issue comment
  116. func DeleteIssueCommentReaction(ctx *context.APIContext, form api.EditReactionOption) {
  117. // swagger:operation DELETE /repos/{owner}/{repo}/issues/comments/{id}/reactions issue issueDeleteCommentReaction
  118. // ---
  119. // summary: Remove a reaction from a comment of a issue comment
  120. // consumes:
  121. // - application/json
  122. // produces:
  123. // - application/json
  124. // parameters:
  125. // - name: owner
  126. // in: path
  127. // description: owner of the repo
  128. // type: string
  129. // required: true
  130. // - name: repo
  131. // in: path
  132. // description: name of the repo
  133. // type: string
  134. // required: true
  135. // - name: id
  136. // in: path
  137. // description: id of the comment to edit
  138. // type: integer
  139. // format: int64
  140. // required: true
  141. // - name: content
  142. // in: body
  143. // schema:
  144. // "$ref": "#/definitions/EditReactionOption"
  145. // responses:
  146. // "200":
  147. // "$ref": "#/responses/empty"
  148. // "403":
  149. // "$ref": "#/responses/forbidden"
  150. changeIssueCommentReaction(ctx, form, false)
  151. }
  152. func changeIssueCommentReaction(ctx *context.APIContext, form api.EditReactionOption, isCreateType bool) {
  153. comment, err := models.GetCommentByID(ctx.ParamsInt64(":id"))
  154. if err != nil {
  155. if models.IsErrCommentNotExist(err) {
  156. ctx.NotFound(err)
  157. } else {
  158. ctx.Error(http.StatusInternalServerError, "GetCommentByID", err)
  159. }
  160. return
  161. }
  162. err = comment.LoadIssue()
  163. if err != nil {
  164. ctx.Error(http.StatusInternalServerError, "comment.LoadIssue() failed", err)
  165. }
  166. if comment.Issue.IsLocked && !ctx.Repo.CanWrite(models.UnitTypeIssues) && !ctx.User.IsAdmin {
  167. ctx.Error(http.StatusForbidden, "ChangeIssueCommentReaction", errors.New("no permission to change reaction"))
  168. return
  169. }
  170. if isCreateType {
  171. // PostIssueCommentReaction part
  172. reaction, err := models.CreateCommentReaction(ctx.User, comment.Issue, comment, form.Reaction)
  173. if err != nil {
  174. if models.IsErrForbiddenIssueReaction(err) {
  175. ctx.Error(http.StatusForbidden, err.Error(), err)
  176. } else if models.IsErrReactionAlreadyExist(err) {
  177. ctx.JSON(http.StatusOK, api.Reaction{
  178. User: ctx.User.APIFormat(),
  179. Reaction: reaction.Type,
  180. Created: reaction.CreatedUnix.AsTime(),
  181. })
  182. } else {
  183. ctx.Error(http.StatusInternalServerError, "CreateCommentReaction", err)
  184. }
  185. return
  186. }
  187. ctx.JSON(http.StatusCreated, api.Reaction{
  188. User: ctx.User.APIFormat(),
  189. Reaction: reaction.Type,
  190. Created: reaction.CreatedUnix.AsTime(),
  191. })
  192. } else {
  193. // DeleteIssueCommentReaction part
  194. err = models.DeleteCommentReaction(ctx.User, comment.Issue, comment, form.Reaction)
  195. if err != nil {
  196. ctx.Error(http.StatusInternalServerError, "DeleteCommentReaction", err)
  197. return
  198. }
  199. //ToDo respond 204
  200. ctx.Status(http.StatusOK)
  201. }
  202. }
  203. // GetIssueReactions list reactions of a issue comment
  204. func GetIssueReactions(ctx *context.APIContext) {
  205. // swagger:operation GET /repos/{owner}/{repo}/issues/{index}/reactions issue issueGetIssueReactions
  206. // ---
  207. // summary: Get a list reactions of a issue
  208. // consumes:
  209. // - application/json
  210. // produces:
  211. // - application/json
  212. // parameters:
  213. // - name: owner
  214. // in: path
  215. // description: owner of the repo
  216. // type: string
  217. // required: true
  218. // - name: repo
  219. // in: path
  220. // description: name of the repo
  221. // type: string
  222. // required: true
  223. // - name: index
  224. // in: path
  225. // description: index of the issue
  226. // type: integer
  227. // format: int64
  228. // required: true
  229. // responses:
  230. // "200":
  231. // "$ref": "#/responses/ReactionList"
  232. // "403":
  233. // "$ref": "#/responses/forbidden"
  234. issue, err := models.GetIssueWithAttrsByIndex(ctx.Repo.Repository.ID, ctx.ParamsInt64(":index"))
  235. if err != nil {
  236. if models.IsErrIssueNotExist(err) {
  237. ctx.NotFound()
  238. } else {
  239. ctx.Error(http.StatusInternalServerError, "GetIssueByIndex", err)
  240. }
  241. return
  242. }
  243. if !ctx.Repo.CanRead(models.UnitTypeIssues) && !ctx.User.IsAdmin {
  244. ctx.Error(http.StatusForbidden, "GetIssueReactions", errors.New("no permission to get reactions"))
  245. return
  246. }
  247. reactions, err := models.FindIssueReactions(issue)
  248. if err != nil {
  249. ctx.Error(http.StatusInternalServerError, "FindIssueReactions", err)
  250. return
  251. }
  252. _, err = reactions.LoadUsers()
  253. if err != nil {
  254. ctx.Error(http.StatusInternalServerError, "ReactionList.LoadUsers()", err)
  255. return
  256. }
  257. var result []api.Reaction
  258. for _, r := range reactions {
  259. result = append(result, api.Reaction{
  260. User: r.User.APIFormat(),
  261. Reaction: r.Type,
  262. Created: r.CreatedUnix.AsTime(),
  263. })
  264. }
  265. ctx.JSON(http.StatusOK, result)
  266. }
  267. // PostIssueReaction add a reaction to a comment of a issue
  268. func PostIssueReaction(ctx *context.APIContext, form api.EditReactionOption) {
  269. // swagger:operation POST /repos/{owner}/{repo}/issues/{index}/reactions issue issuePostIssueReaction
  270. // ---
  271. // summary: Add a reaction to a comment of a issue
  272. // consumes:
  273. // - application/json
  274. // produces:
  275. // - application/json
  276. // parameters:
  277. // - name: owner
  278. // in: path
  279. // description: owner of the repo
  280. // type: string
  281. // required: true
  282. // - name: repo
  283. // in: path
  284. // description: name of the repo
  285. // type: string
  286. // required: true
  287. // - name: index
  288. // in: path
  289. // description: index of the issue
  290. // type: integer
  291. // format: int64
  292. // required: true
  293. // - name: content
  294. // in: body
  295. // schema:
  296. // "$ref": "#/definitions/EditReactionOption"
  297. // responses:
  298. // "200":
  299. // "$ref": "#/responses/Reaction"
  300. // "201":
  301. // "$ref": "#/responses/Reaction"
  302. // "403":
  303. // "$ref": "#/responses/forbidden"
  304. changeIssueReaction(ctx, form, true)
  305. }
  306. // DeleteIssueReaction list reactions of a issue comment
  307. func DeleteIssueReaction(ctx *context.APIContext, form api.EditReactionOption) {
  308. // swagger:operation DELETE /repos/{owner}/{repo}/issues/{index}/reactions issue issueDeleteIssueReaction
  309. // ---
  310. // summary: Remove a reaction from a comment of a issue
  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: index of the issue
  329. // type: integer
  330. // format: int64
  331. // required: true
  332. // - name: content
  333. // in: body
  334. // schema:
  335. // "$ref": "#/definitions/EditReactionOption"
  336. // responses:
  337. // "200":
  338. // "$ref": "#/responses/empty"
  339. // "403":
  340. // "$ref": "#/responses/forbidden"
  341. changeIssueReaction(ctx, form, false)
  342. }
  343. func changeIssueReaction(ctx *context.APIContext, form api.EditReactionOption, isCreateType bool) {
  344. issue, err := models.GetIssueWithAttrsByIndex(ctx.Repo.Repository.ID, ctx.ParamsInt64(":index"))
  345. if err != nil {
  346. if models.IsErrIssueNotExist(err) {
  347. ctx.NotFound()
  348. } else {
  349. ctx.Error(http.StatusInternalServerError, "GetIssueByIndex", err)
  350. }
  351. return
  352. }
  353. if issue.IsLocked && !ctx.Repo.CanWrite(models.UnitTypeIssues) && !ctx.User.IsAdmin {
  354. ctx.Error(http.StatusForbidden, "ChangeIssueCommentReaction", errors.New("no permission to change reaction"))
  355. return
  356. }
  357. if isCreateType {
  358. // PostIssueReaction part
  359. reaction, err := models.CreateIssueReaction(ctx.User, issue, form.Reaction)
  360. if err != nil {
  361. if models.IsErrForbiddenIssueReaction(err) {
  362. ctx.Error(http.StatusForbidden, err.Error(), err)
  363. } else if models.IsErrReactionAlreadyExist(err) {
  364. ctx.JSON(http.StatusOK, api.Reaction{
  365. User: ctx.User.APIFormat(),
  366. Reaction: reaction.Type,
  367. Created: reaction.CreatedUnix.AsTime(),
  368. })
  369. } else {
  370. ctx.Error(http.StatusInternalServerError, "CreateCommentReaction", err)
  371. }
  372. return
  373. }
  374. ctx.JSON(http.StatusCreated, api.Reaction{
  375. User: ctx.User.APIFormat(),
  376. Reaction: reaction.Type,
  377. Created: reaction.CreatedUnix.AsTime(),
  378. })
  379. } else {
  380. // DeleteIssueReaction part
  381. err = models.DeleteIssueReaction(ctx.User, issue, form.Reaction)
  382. if err != nil {
  383. ctx.Error(http.StatusInternalServerError, "DeleteIssueReaction", err)
  384. return
  385. }
  386. //ToDo respond 204
  387. ctx.Status(http.StatusOK)
  388. }
  389. }