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

Allow cross-repository dependencies on issues (#7901) * in progress changes for #7405, added ability to add cross-repo dependencies * removed unused repolink var * fixed query that was breaking ci tests; fixed check in issue dependency add so that the id of the issue and dependency is checked rather than the indexes * reverted removal of string in local files becasue these are done via crowdin, not updated manually * removed 'Select("issue.*")' from getBlockedByDependencies and getBlockingDependencies based on comments in PR review * changed getBlockedByDependencies and getBlockingDependencies to use a more xorm-like query, also updated the sidebar as a result * simplified the getBlockingDependencies and getBlockedByDependencies methods; changed the sidebar to show the dependencies in a different format where you can see the name of the repository * made some changes to the issue view in the dependencies (issue name on top, repo full name on separate line). Change view of issue in the dependency search results (also showing the full repo name on separate line) * replace call to FindUserAccessibleRepoIDs with SearchRepositoryByName. The former was hardcoded to use isPrivate = false on the repo search, but this code needed it to be true. The SearchRepositoryByName method is used more in the code including on the user's dashboard * some more tweaks to the layout of the issues when showing dependencies and in the search box when you add new dependencies * added Name to the RepositoryMeta struct * updated swagger doc * fixed total count for link header on SearchIssues * fixed indentation * fixed aligment of remove icon on dependencies in issue sidebar * removed unnecessary nil check (unnecessary because issue.loadRepo is called prior to this block) * reverting .css change, somehow missed or forgot that less is used * updated less file and generated css; updated sidebar template with styles to line up delete and issue index * added ordering to the blocked by/depends on queries * fixed sorting in issue dependency search and the depends on/blocks views to show issues from the current repo first, then by created date descending; added a "all cross repository dependencies" setting to allow this feature to be turned off, if turned off, the issue dependency search will work the way it did before (restricted to the current repository) * re-applied my swagger changes after merge * fixed split string condition in issue search * changed ALLOW_CROSS_REPOSITORY_DEPENDENCIES description to sound more global than just the issue dependency search; returning 400 in the cross repo issue search api method if not enabled; fixed bug where the issue count did not respect the state parameter * when adding a dependency to an issue, added a check to make sure the issue and dependency are in the same repo if cross repo dependencies is not enabled * updated sortIssuesSession call in PullRequests, another commit moved this method from pull.go to pull_list.go so I had to re-apply my change here * fixed incorrect setting of user id parameter in search repos call
4 роки тому
Allow cross-repository dependencies on issues (#7901) * in progress changes for #7405, added ability to add cross-repo dependencies * removed unused repolink var * fixed query that was breaking ci tests; fixed check in issue dependency add so that the id of the issue and dependency is checked rather than the indexes * reverted removal of string in local files becasue these are done via crowdin, not updated manually * removed 'Select("issue.*")' from getBlockedByDependencies and getBlockingDependencies based on comments in PR review * changed getBlockedByDependencies and getBlockingDependencies to use a more xorm-like query, also updated the sidebar as a result * simplified the getBlockingDependencies and getBlockedByDependencies methods; changed the sidebar to show the dependencies in a different format where you can see the name of the repository * made some changes to the issue view in the dependencies (issue name on top, repo full name on separate line). Change view of issue in the dependency search results (also showing the full repo name on separate line) * replace call to FindUserAccessibleRepoIDs with SearchRepositoryByName. The former was hardcoded to use isPrivate = false on the repo search, but this code needed it to be true. The SearchRepositoryByName method is used more in the code including on the user's dashboard * some more tweaks to the layout of the issues when showing dependencies and in the search box when you add new dependencies * added Name to the RepositoryMeta struct * updated swagger doc * fixed total count for link header on SearchIssues * fixed indentation * fixed aligment of remove icon on dependencies in issue sidebar * removed unnecessary nil check (unnecessary because issue.loadRepo is called prior to this block) * reverting .css change, somehow missed or forgot that less is used * updated less file and generated css; updated sidebar template with styles to line up delete and issue index * added ordering to the blocked by/depends on queries * fixed sorting in issue dependency search and the depends on/blocks views to show issues from the current repo first, then by created date descending; added a "all cross repository dependencies" setting to allow this feature to be turned off, if turned off, the issue dependency search will work the way it did before (restricted to the current repository) * re-applied my swagger changes after merge * fixed split string condition in issue search * changed ALLOW_CROSS_REPOSITORY_DEPENDENCIES description to sound more global than just the issue dependency search; returning 400 in the cross repo issue search api method if not enabled; fixed bug where the issue count did not respect the state parameter * when adding a dependency to an issue, added a check to make sure the issue and dependency are in the same repo if cross repo dependencies is not enabled * updated sortIssuesSession call in PullRequests, another commit moved this method from pull.go to pull_list.go so I had to re-apply my change here * fixed incorrect setting of user id parameter in search repos call
4 роки тому
Allow cross-repository dependencies on issues (#7901) * in progress changes for #7405, added ability to add cross-repo dependencies * removed unused repolink var * fixed query that was breaking ci tests; fixed check in issue dependency add so that the id of the issue and dependency is checked rather than the indexes * reverted removal of string in local files becasue these are done via crowdin, not updated manually * removed 'Select("issue.*")' from getBlockedByDependencies and getBlockingDependencies based on comments in PR review * changed getBlockedByDependencies and getBlockingDependencies to use a more xorm-like query, also updated the sidebar as a result * simplified the getBlockingDependencies and getBlockedByDependencies methods; changed the sidebar to show the dependencies in a different format where you can see the name of the repository * made some changes to the issue view in the dependencies (issue name on top, repo full name on separate line). Change view of issue in the dependency search results (also showing the full repo name on separate line) * replace call to FindUserAccessibleRepoIDs with SearchRepositoryByName. The former was hardcoded to use isPrivate = false on the repo search, but this code needed it to be true. The SearchRepositoryByName method is used more in the code including on the user's dashboard * some more tweaks to the layout of the issues when showing dependencies and in the search box when you add new dependencies * added Name to the RepositoryMeta struct * updated swagger doc * fixed total count for link header on SearchIssues * fixed indentation * fixed aligment of remove icon on dependencies in issue sidebar * removed unnecessary nil check (unnecessary because issue.loadRepo is called prior to this block) * reverting .css change, somehow missed or forgot that less is used * updated less file and generated css; updated sidebar template with styles to line up delete and issue index * added ordering to the blocked by/depends on queries * fixed sorting in issue dependency search and the depends on/blocks views to show issues from the current repo first, then by created date descending; added a "all cross repository dependencies" setting to allow this feature to be turned off, if turned off, the issue dependency search will work the way it did before (restricted to the current repository) * re-applied my swagger changes after merge * fixed split string condition in issue search * changed ALLOW_CROSS_REPOSITORY_DEPENDENCIES description to sound more global than just the issue dependency search; returning 400 in the cross repo issue search api method if not enabled; fixed bug where the issue count did not respect the state parameter * when adding a dependency to an issue, added a check to make sure the issue and dependency are in the same repo if cross repo dependencies is not enabled * updated sortIssuesSession call in PullRequests, another commit moved this method from pull.go to pull_list.go so I had to re-apply my change here * fixed incorrect setting of user id parameter in search repos call
4 роки тому
7 роки тому
6 роки тому
6 роки тому
6 роки тому
6 роки тому
Issue due date (#3794) * Started adding deadline to ui * Implemented basic issue due date managing * Improved UI for due date managing * Added at least write access to the repo in order to modify issue due dates * Ui improvements * Added issue comments creation when adding/modifying/removing a due date * Show due date in issue list * Added api support for issue due dates * Fixed lint suggestions * Added deadline to sdk * Updated css * Added support for adding/modifiying deadlines for pull requests via api * Fixed comments not created when updating or removing a deadline * update sdk (will do properly once go-gitea/go-sdk#103 is merged) * enhanced updateIssueDeadline * Removed unnessecary Issue.DeadlineString * UI improvements * Small improvments to comment creation + ui & validation improvements * Check if an issue is overdue is now a seperate function * Updated go-sdk with govendor as it was merged * Simplified isOverdue method * removed unessecary deadline to 0 set * Update swagger definitions * Added missing return * Added an explanary comment * Improved updateIssueDeadline method so it'll only update `deadline_unix` * Small changes and improvements * no need to explicitly load the issue when updating a deadline, just use whats already there * small optimisations * Added check if a deadline was modified before updating it * Moved comment creating logic into its own function * Code cleanup for creating deadline comment * locale improvement * When modifying a deadline, the old deadline is saved with the comment * small improvments to xorm session handling when updating an issue deadline + style nitpicks * style nitpicks * Moved checking for if the user has write acces to middleware
6 роки тому
Issue due date (#3794) * Started adding deadline to ui * Implemented basic issue due date managing * Improved UI for due date managing * Added at least write access to the repo in order to modify issue due dates * Ui improvements * Added issue comments creation when adding/modifying/removing a due date * Show due date in issue list * Added api support for issue due dates * Fixed lint suggestions * Added deadline to sdk * Updated css * Added support for adding/modifiying deadlines for pull requests via api * Fixed comments not created when updating or removing a deadline * update sdk (will do properly once go-gitea/go-sdk#103 is merged) * enhanced updateIssueDeadline * Removed unnessecary Issue.DeadlineString * UI improvements * Small improvments to comment creation + ui & validation improvements * Check if an issue is overdue is now a seperate function * Updated go-sdk with govendor as it was merged * Simplified isOverdue method * removed unessecary deadline to 0 set * Update swagger definitions * Added missing return * Added an explanary comment * Improved updateIssueDeadline method so it'll only update `deadline_unix` * Small changes and improvements * no need to explicitly load the issue when updating a deadline, just use whats already there * small optimisations * Added check if a deadline was modified before updating it * Moved comment creating logic into its own function * Code cleanup for creating deadline comment * locale improvement * When modifying a deadline, the old deadline is saved with the comment * small improvments to xorm session handling when updating an issue deadline + style nitpicks * style nitpicks * Moved checking for if the user has write acces to middleware
6 роки тому
Issue due date (#3794) * Started adding deadline to ui * Implemented basic issue due date managing * Improved UI for due date managing * Added at least write access to the repo in order to modify issue due dates * Ui improvements * Added issue comments creation when adding/modifying/removing a due date * Show due date in issue list * Added api support for issue due dates * Fixed lint suggestions * Added deadline to sdk * Updated css * Added support for adding/modifiying deadlines for pull requests via api * Fixed comments not created when updating or removing a deadline * update sdk (will do properly once go-gitea/go-sdk#103 is merged) * enhanced updateIssueDeadline * Removed unnessecary Issue.DeadlineString * UI improvements * Small improvments to comment creation + ui & validation improvements * Check if an issue is overdue is now a seperate function * Updated go-sdk with govendor as it was merged * Simplified isOverdue method * removed unessecary deadline to 0 set * Update swagger definitions * Added missing return * Added an explanary comment * Improved updateIssueDeadline method so it'll only update `deadline_unix` * Small changes and improvements * no need to explicitly load the issue when updating a deadline, just use whats already there * small optimisations * Added check if a deadline was modified before updating it * Moved comment creating logic into its own function * Code cleanup for creating deadline comment * locale improvement * When modifying a deadline, the old deadline is saved with the comment * small improvments to xorm session handling when updating an issue deadline + style nitpicks * style nitpicks * Moved checking for if the user has write acces to middleware
6 роки тому
Issue due date (#3794) * Started adding deadline to ui * Implemented basic issue due date managing * Improved UI for due date managing * Added at least write access to the repo in order to modify issue due dates * Ui improvements * Added issue comments creation when adding/modifying/removing a due date * Show due date in issue list * Added api support for issue due dates * Fixed lint suggestions * Added deadline to sdk * Updated css * Added support for adding/modifiying deadlines for pull requests via api * Fixed comments not created when updating or removing a deadline * update sdk (will do properly once go-gitea/go-sdk#103 is merged) * enhanced updateIssueDeadline * Removed unnessecary Issue.DeadlineString * UI improvements * Small improvments to comment creation + ui & validation improvements * Check if an issue is overdue is now a seperate function * Updated go-sdk with govendor as it was merged * Simplified isOverdue method * removed unessecary deadline to 0 set * Update swagger definitions * Added missing return * Added an explanary comment * Improved updateIssueDeadline method so it'll only update `deadline_unix` * Small changes and improvements * no need to explicitly load the issue when updating a deadline, just use whats already there * small optimisations * Added check if a deadline was modified before updating it * Moved comment creating logic into its own function * Code cleanup for creating deadline comment * locale improvement * When modifying a deadline, the old deadline is saved with the comment * small improvments to xorm session handling when updating an issue deadline + style nitpicks * style nitpicks * Moved checking for if the user has write acces to middleware
6 роки тому
6 роки тому
6 роки тому
6 роки тому
6 роки тому
Issue due date (#3794) * Started adding deadline to ui * Implemented basic issue due date managing * Improved UI for due date managing * Added at least write access to the repo in order to modify issue due dates * Ui improvements * Added issue comments creation when adding/modifying/removing a due date * Show due date in issue list * Added api support for issue due dates * Fixed lint suggestions * Added deadline to sdk * Updated css * Added support for adding/modifiying deadlines for pull requests via api * Fixed comments not created when updating or removing a deadline * update sdk (will do properly once go-gitea/go-sdk#103 is merged) * enhanced updateIssueDeadline * Removed unnessecary Issue.DeadlineString * UI improvements * Small improvments to comment creation + ui & validation improvements * Check if an issue is overdue is now a seperate function * Updated go-sdk with govendor as it was merged * Simplified isOverdue method * removed unessecary deadline to 0 set * Update swagger definitions * Added missing return * Added an explanary comment * Improved updateIssueDeadline method so it'll only update `deadline_unix` * Small changes and improvements * no need to explicitly load the issue when updating a deadline, just use whats already there * small optimisations * Added check if a deadline was modified before updating it * Moved comment creating logic into its own function * Code cleanup for creating deadline comment * locale improvement * When modifying a deadline, the old deadline is saved with the comment * small improvments to xorm session handling when updating an issue deadline + style nitpicks * style nitpicks * Moved checking for if the user has write acces to middleware
6 роки тому
6 роки тому
5 роки тому
6 роки тому
6 роки тому
6 роки тому
6 роки тому
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740
  1. // Copyright 2016 The Gogs Authors. All rights reserved.
  2. // Copyright 2018 The Gitea Authors. All rights reserved.
  3. // Use of this source code is governed by a MIT-style
  4. // license that can be found in the LICENSE file.
  5. package repo
  6. import (
  7. "fmt"
  8. "net/http"
  9. "strings"
  10. "time"
  11. "code.gitea.io/gitea/models"
  12. "code.gitea.io/gitea/modules/context"
  13. issue_indexer "code.gitea.io/gitea/modules/indexer/issues"
  14. "code.gitea.io/gitea/modules/log"
  15. "code.gitea.io/gitea/modules/setting"
  16. api "code.gitea.io/gitea/modules/structs"
  17. "code.gitea.io/gitea/modules/timeutil"
  18. "code.gitea.io/gitea/modules/util"
  19. issue_service "code.gitea.io/gitea/services/issue"
  20. )
  21. // SearchIssues searches for issues across the repositories that the user has access to
  22. func SearchIssues(ctx *context.APIContext) {
  23. // swagger:operation GET /repos/issues/search issue issueSearchIssues
  24. // ---
  25. // summary: Search for issues across the repositories that the user has access to
  26. // produces:
  27. // - application/json
  28. // parameters:
  29. // - name: state
  30. // in: query
  31. // description: whether issue is open or closed
  32. // type: string
  33. // - name: labels
  34. // in: query
  35. // description: comma separated list of labels. Fetch only issues that have any of this labels. Non existent labels are discarded
  36. // type: string
  37. // - name: page
  38. // in: query
  39. // description: page number of requested issues
  40. // type: integer
  41. // - name: q
  42. // in: query
  43. // description: search string
  44. // type: string
  45. // - name: priority_repo_id
  46. // in: query
  47. // description: repository to prioritize in the results
  48. // type: integer
  49. // format: int64
  50. // responses:
  51. // "200":
  52. // "$ref": "#/responses/IssueList"
  53. var isClosed util.OptionalBool
  54. switch ctx.Query("state") {
  55. case "closed":
  56. isClosed = util.OptionalBoolTrue
  57. case "all":
  58. isClosed = util.OptionalBoolNone
  59. default:
  60. isClosed = util.OptionalBoolFalse
  61. }
  62. // find repos user can access (for issue search)
  63. repoIDs := make([]int64, 0)
  64. issueCount := 0
  65. for page := 1; ; page++ {
  66. repos, count, err := models.SearchRepositoryByName(&models.SearchRepoOptions{
  67. Page: page,
  68. PageSize: 15,
  69. Private: true,
  70. Keyword: "",
  71. OwnerID: ctx.User.ID,
  72. TopicOnly: false,
  73. Collaborate: util.OptionalBoolNone,
  74. UserIsAdmin: ctx.IsUserSiteAdmin(),
  75. UserID: ctx.User.ID,
  76. OrderBy: models.SearchOrderByRecentUpdated,
  77. })
  78. if err != nil {
  79. ctx.Error(500, "SearchRepositoryByName", err)
  80. return
  81. }
  82. if len(repos) == 0 {
  83. break
  84. }
  85. log.Trace("Processing next %d repos of %d", len(repos), count)
  86. for _, repo := range repos {
  87. switch isClosed {
  88. case util.OptionalBoolTrue:
  89. issueCount += repo.NumClosedIssues
  90. case util.OptionalBoolFalse:
  91. issueCount += repo.NumOpenIssues
  92. case util.OptionalBoolNone:
  93. issueCount += repo.NumIssues
  94. }
  95. repoIDs = append(repoIDs, repo.ID)
  96. }
  97. }
  98. var issues []*models.Issue
  99. keyword := strings.Trim(ctx.Query("q"), " ")
  100. if strings.IndexByte(keyword, 0) >= 0 {
  101. keyword = ""
  102. }
  103. var issueIDs []int64
  104. var labelIDs []int64
  105. var err error
  106. if len(keyword) > 0 && len(repoIDs) > 0 {
  107. issueIDs, err = issue_indexer.SearchIssuesByKeyword(repoIDs, keyword)
  108. }
  109. labels := ctx.Query("labels")
  110. if splitted := strings.Split(labels, ","); labels != "" && len(splitted) > 0 {
  111. labelIDs, err = models.GetLabelIDsInReposByNames(repoIDs, splitted)
  112. if err != nil {
  113. ctx.Error(500, "GetLabelIDsInRepoByNames", err)
  114. return
  115. }
  116. }
  117. // Only fetch the issues if we either don't have a keyword or the search returned issues
  118. // This would otherwise return all issues if no issues were found by the search.
  119. if len(keyword) == 0 || len(issueIDs) > 0 || len(labelIDs) > 0 {
  120. issues, err = models.Issues(&models.IssuesOptions{
  121. RepoIDs: repoIDs,
  122. Page: ctx.QueryInt("page"),
  123. PageSize: setting.UI.IssuePagingNum,
  124. IsClosed: isClosed,
  125. IssueIDs: issueIDs,
  126. LabelIDs: labelIDs,
  127. SortType: "priorityrepo",
  128. PriorityRepoID: ctx.QueryInt64("priority_repo_id"),
  129. })
  130. }
  131. if err != nil {
  132. ctx.Error(500, "Issues", err)
  133. return
  134. }
  135. apiIssues := make([]*api.Issue, len(issues))
  136. for i := range issues {
  137. apiIssues[i] = issues[i].APIFormat()
  138. }
  139. ctx.SetLinkHeader(issueCount, setting.UI.IssuePagingNum)
  140. ctx.JSON(200, &apiIssues)
  141. }
  142. // ListIssues list the issues of a repository
  143. func ListIssues(ctx *context.APIContext) {
  144. // swagger:operation GET /repos/{owner}/{repo}/issues issue issueListIssues
  145. // ---
  146. // summary: List a repository's issues
  147. // produces:
  148. // - application/json
  149. // parameters:
  150. // - name: owner
  151. // in: path
  152. // description: owner of the repo
  153. // type: string
  154. // required: true
  155. // - name: repo
  156. // in: path
  157. // description: name of the repo
  158. // type: string
  159. // required: true
  160. // - name: state
  161. // in: query
  162. // description: whether issue is open or closed
  163. // type: string
  164. // - name: labels
  165. // in: query
  166. // description: comma separated list of labels. Fetch only issues that have any of this labels. Non existent labels are discarded
  167. // type: string
  168. // - name: page
  169. // in: query
  170. // description: page number of requested issues
  171. // type: integer
  172. // - name: q
  173. // in: query
  174. // description: search string
  175. // type: string
  176. // responses:
  177. // "200":
  178. // "$ref": "#/responses/IssueList"
  179. var isClosed util.OptionalBool
  180. switch ctx.Query("state") {
  181. case "closed":
  182. isClosed = util.OptionalBoolTrue
  183. case "all":
  184. isClosed = util.OptionalBoolNone
  185. default:
  186. isClosed = util.OptionalBoolFalse
  187. }
  188. var issues []*models.Issue
  189. keyword := strings.Trim(ctx.Query("q"), " ")
  190. if strings.IndexByte(keyword, 0) >= 0 {
  191. keyword = ""
  192. }
  193. var issueIDs []int64
  194. var labelIDs []int64
  195. var err error
  196. if len(keyword) > 0 {
  197. issueIDs, err = issue_indexer.SearchIssuesByKeyword([]int64{ctx.Repo.Repository.ID}, keyword)
  198. }
  199. if splitted := strings.Split(ctx.Query("labels"), ","); len(splitted) > 0 {
  200. labelIDs, err = models.GetLabelIDsInRepoByNames(ctx.Repo.Repository.ID, splitted)
  201. if err != nil {
  202. ctx.Error(500, "GetLabelIDsInRepoByNames", err)
  203. return
  204. }
  205. }
  206. // Only fetch the issues if we either don't have a keyword or the search returned issues
  207. // This would otherwise return all issues if no issues were found by the search.
  208. if len(keyword) == 0 || len(issueIDs) > 0 || len(labelIDs) > 0 {
  209. issues, err = models.Issues(&models.IssuesOptions{
  210. RepoIDs: []int64{ctx.Repo.Repository.ID},
  211. Page: ctx.QueryInt("page"),
  212. PageSize: setting.UI.IssuePagingNum,
  213. IsClosed: isClosed,
  214. IssueIDs: issueIDs,
  215. LabelIDs: labelIDs,
  216. })
  217. }
  218. if err != nil {
  219. ctx.Error(500, "Issues", err)
  220. return
  221. }
  222. apiIssues := make([]*api.Issue, len(issues))
  223. for i := range issues {
  224. apiIssues[i] = issues[i].APIFormat()
  225. }
  226. ctx.SetLinkHeader(ctx.Repo.Repository.NumIssues, setting.UI.IssuePagingNum)
  227. ctx.JSON(200, &apiIssues)
  228. }
  229. // GetIssue get an issue of a repository
  230. func GetIssue(ctx *context.APIContext) {
  231. // swagger:operation GET /repos/{owner}/{repo}/issues/{index} issue issueGetIssue
  232. // ---
  233. // summary: Get an issue
  234. // produces:
  235. // - application/json
  236. // parameters:
  237. // - name: owner
  238. // in: path
  239. // description: owner of the repo
  240. // type: string
  241. // required: true
  242. // - name: repo
  243. // in: path
  244. // description: name of the repo
  245. // type: string
  246. // required: true
  247. // - name: index
  248. // in: path
  249. // description: index of the issue to get
  250. // type: integer
  251. // format: int64
  252. // required: true
  253. // responses:
  254. // "200":
  255. // "$ref": "#/responses/Issue"
  256. issue, err := models.GetIssueWithAttrsByIndex(ctx.Repo.Repository.ID, ctx.ParamsInt64(":index"))
  257. if err != nil {
  258. if models.IsErrIssueNotExist(err) {
  259. ctx.NotFound()
  260. } else {
  261. ctx.Error(500, "GetIssueByIndex", err)
  262. }
  263. return
  264. }
  265. ctx.JSON(200, issue.APIFormat())
  266. }
  267. // CreateIssue create an issue of a repository
  268. func CreateIssue(ctx *context.APIContext, form api.CreateIssueOption) {
  269. // swagger:operation POST /repos/{owner}/{repo}/issues issue issueCreateIssue
  270. // ---
  271. // summary: Create an issue. If using deadline only the date will be taken into account, and time of day ignored.
  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: body
  288. // in: body
  289. // schema:
  290. // "$ref": "#/definitions/CreateIssueOption"
  291. // responses:
  292. // "201":
  293. // "$ref": "#/responses/Issue"
  294. var deadlineUnix timeutil.TimeStamp
  295. if form.Deadline != nil && ctx.Repo.CanWrite(models.UnitTypeIssues) {
  296. deadlineUnix = timeutil.TimeStamp(form.Deadline.Unix())
  297. }
  298. issue := &models.Issue{
  299. RepoID: ctx.Repo.Repository.ID,
  300. Repo: ctx.Repo.Repository,
  301. Title: form.Title,
  302. PosterID: ctx.User.ID,
  303. Poster: ctx.User,
  304. Content: form.Body,
  305. DeadlineUnix: deadlineUnix,
  306. }
  307. var assigneeIDs = make([]int64, 0)
  308. var err error
  309. if ctx.Repo.CanWrite(models.UnitTypeIssues) {
  310. issue.MilestoneID = form.Milestone
  311. assigneeIDs, err = models.MakeIDsFromAPIAssigneesToAdd(form.Assignee, form.Assignees)
  312. if err != nil {
  313. if models.IsErrUserNotExist(err) {
  314. ctx.Error(422, "", fmt.Sprintf("Assignee does not exist: [name: %s]", err))
  315. } else {
  316. ctx.Error(500, "AddAssigneeByName", err)
  317. }
  318. return
  319. }
  320. if assigneeIDs != nil {
  321. for _, aID := range assigneeIDs {
  322. assignee, err := models.GetUserByID(aID)
  323. if err != nil {
  324. ctx.Error(500, "GetUserByID", err)
  325. return
  326. }
  327. // Check if the passed assignees is assignable
  328. valid, err := models.CanBeAssigned(assignee, ctx.Repo.Repository, false)
  329. if err != nil {
  330. ctx.Error(500, "canBeAssigned", err)
  331. return
  332. }
  333. if !valid {
  334. ctx.Error(422, "canBeAssigned", models.ErrUserDoesNotHaveAccessToRepo{UserID: aID, RepoName: ctx.Repo.Repository.Name})
  335. return
  336. }
  337. }
  338. }
  339. } else {
  340. // setting labels is not allowed if user is not a writer
  341. form.Labels = make([]int64, 0)
  342. }
  343. if err := issue_service.NewIssue(ctx.Repo.Repository, issue, form.Labels, nil, assigneeIDs); err != nil {
  344. if models.IsErrUserDoesNotHaveAccessToRepo(err) {
  345. ctx.Error(400, "UserDoesNotHaveAccessToRepo", err)
  346. return
  347. }
  348. ctx.Error(500, "NewIssue", err)
  349. return
  350. }
  351. if form.Closed {
  352. if err := issue_service.ChangeStatus(issue, ctx.User, true); err != nil {
  353. if models.IsErrDependenciesLeft(err) {
  354. ctx.Error(http.StatusPreconditionFailed, "DependenciesLeft", "cannot close this issue because it still has open dependencies")
  355. return
  356. }
  357. ctx.Error(500, "ChangeStatus", err)
  358. return
  359. }
  360. }
  361. // Refetch from database to assign some automatic values
  362. issue, err = models.GetIssueByID(issue.ID)
  363. if err != nil {
  364. ctx.Error(500, "GetIssueByID", err)
  365. return
  366. }
  367. ctx.JSON(201, issue.APIFormat())
  368. }
  369. // EditIssue modify an issue of a repository
  370. func EditIssue(ctx *context.APIContext, form api.EditIssueOption) {
  371. // swagger:operation PATCH /repos/{owner}/{repo}/issues/{index} issue issueEditIssue
  372. // ---
  373. // summary: Edit an issue. If using deadline only the date will be taken into account, and time of day ignored.
  374. // consumes:
  375. // - application/json
  376. // produces:
  377. // - application/json
  378. // parameters:
  379. // - name: owner
  380. // in: path
  381. // description: owner of the repo
  382. // type: string
  383. // required: true
  384. // - name: repo
  385. // in: path
  386. // description: name of the repo
  387. // type: string
  388. // required: true
  389. // - name: index
  390. // in: path
  391. // description: index of the issue to edit
  392. // type: integer
  393. // format: int64
  394. // required: true
  395. // - name: body
  396. // in: body
  397. // schema:
  398. // "$ref": "#/definitions/EditIssueOption"
  399. // responses:
  400. // "201":
  401. // "$ref": "#/responses/Issue"
  402. issue, err := models.GetIssueByIndex(ctx.Repo.Repository.ID, ctx.ParamsInt64(":index"))
  403. if err != nil {
  404. if models.IsErrIssueNotExist(err) {
  405. ctx.NotFound()
  406. } else {
  407. ctx.Error(500, "GetIssueByIndex", err)
  408. }
  409. return
  410. }
  411. issue.Repo = ctx.Repo.Repository
  412. err = issue.LoadAttributes()
  413. if err != nil {
  414. ctx.Error(500, "LoadAttributes", err)
  415. return
  416. }
  417. if !issue.IsPoster(ctx.User.ID) && !ctx.Repo.CanWrite(models.UnitTypeIssues) {
  418. ctx.Status(403)
  419. return
  420. }
  421. if len(form.Title) > 0 {
  422. issue.Title = form.Title
  423. }
  424. if form.Body != nil {
  425. issue.Content = *form.Body
  426. }
  427. // Update or remove the deadline, only if set and allowed
  428. if (form.Deadline != nil || form.RemoveDeadline != nil) && ctx.Repo.CanWrite(models.UnitTypeIssues) {
  429. var deadlineUnix timeutil.TimeStamp
  430. if (form.RemoveDeadline == nil || !*form.RemoveDeadline) && !form.Deadline.IsZero() {
  431. deadline := time.Date(form.Deadline.Year(), form.Deadline.Month(), form.Deadline.Day(),
  432. 23, 59, 59, 0, form.Deadline.Location())
  433. deadlineUnix = timeutil.TimeStamp(deadline.Unix())
  434. }
  435. if err := models.UpdateIssueDeadline(issue, deadlineUnix, ctx.User); err != nil {
  436. ctx.Error(500, "UpdateIssueDeadline", err)
  437. return
  438. }
  439. issue.DeadlineUnix = deadlineUnix
  440. }
  441. // Add/delete assignees
  442. // Deleting is done the GitHub way (quote from their api documentation):
  443. // https://developer.github.com/v3/issues/#edit-an-issue
  444. // "assignees" (array): Logins for Users to assign to this issue.
  445. // Pass one or more user logins to replace the set of assignees on this Issue.
  446. // Send an empty array ([]) to clear all assignees from the Issue.
  447. if ctx.Repo.CanWrite(models.UnitTypeIssues) && (form.Assignees != nil || form.Assignee != nil) {
  448. oneAssignee := ""
  449. if form.Assignee != nil {
  450. oneAssignee = *form.Assignee
  451. }
  452. err = issue_service.UpdateAssignees(issue, oneAssignee, form.Assignees, ctx.User)
  453. if err != nil {
  454. ctx.Error(500, "UpdateAssignees", err)
  455. return
  456. }
  457. }
  458. if ctx.Repo.CanWrite(models.UnitTypeIssues) && form.Milestone != nil &&
  459. issue.MilestoneID != *form.Milestone {
  460. oldMilestoneID := issue.MilestoneID
  461. issue.MilestoneID = *form.Milestone
  462. if err = issue_service.ChangeMilestoneAssign(issue, ctx.User, oldMilestoneID); err != nil {
  463. ctx.Error(500, "ChangeMilestoneAssign", err)
  464. return
  465. }
  466. }
  467. if err = models.UpdateIssue(issue); err != nil {
  468. ctx.Error(500, "UpdateIssue", err)
  469. return
  470. }
  471. if form.State != nil {
  472. if err = issue_service.ChangeStatus(issue, ctx.User, api.StateClosed == api.StateType(*form.State)); err != nil {
  473. if models.IsErrDependenciesLeft(err) {
  474. ctx.Error(http.StatusPreconditionFailed, "DependenciesLeft", "cannot close this issue because it still has open dependencies")
  475. return
  476. }
  477. ctx.Error(500, "ChangeStatus", err)
  478. return
  479. }
  480. }
  481. // Refetch from database to assign some automatic values
  482. issue, err = models.GetIssueByID(issue.ID)
  483. if err != nil {
  484. ctx.Error(500, "GetIssueByID", err)
  485. return
  486. }
  487. ctx.JSON(201, issue.APIFormat())
  488. }
  489. // UpdateIssueDeadline updates an issue deadline
  490. func UpdateIssueDeadline(ctx *context.APIContext, form api.EditDeadlineOption) {
  491. // swagger:operation POST /repos/{owner}/{repo}/issues/{index}/deadline issue issueEditIssueDeadline
  492. // ---
  493. // summary: Set an issue deadline. If set to null, the deadline is deleted. If using deadline only the date will be taken into account, and time of day ignored.
  494. // consumes:
  495. // - application/json
  496. // produces:
  497. // - application/json
  498. // parameters:
  499. // - name: owner
  500. // in: path
  501. // description: owner of the repo
  502. // type: string
  503. // required: true
  504. // - name: repo
  505. // in: path
  506. // description: name of the repo
  507. // type: string
  508. // required: true
  509. // - name: index
  510. // in: path
  511. // description: index of the issue to create or update a deadline on
  512. // type: integer
  513. // format: int64
  514. // required: true
  515. // - name: body
  516. // in: body
  517. // schema:
  518. // "$ref": "#/definitions/EditDeadlineOption"
  519. // responses:
  520. // "201":
  521. // "$ref": "#/responses/IssueDeadline"
  522. // "403":
  523. // description: Not repo writer
  524. // "404":
  525. // description: Issue not found
  526. issue, err := models.GetIssueByIndex(ctx.Repo.Repository.ID, ctx.ParamsInt64(":index"))
  527. if err != nil {
  528. if models.IsErrIssueNotExist(err) {
  529. ctx.NotFound()
  530. } else {
  531. ctx.Error(500, "GetIssueByIndex", err)
  532. }
  533. return
  534. }
  535. if !ctx.Repo.CanWrite(models.UnitTypeIssues) {
  536. ctx.Status(403)
  537. return
  538. }
  539. var deadlineUnix timeutil.TimeStamp
  540. var deadline time.Time
  541. if form.Deadline != nil && !form.Deadline.IsZero() {
  542. deadline = time.Date(form.Deadline.Year(), form.Deadline.Month(), form.Deadline.Day(),
  543. 23, 59, 59, 0, form.Deadline.Location())
  544. deadlineUnix = timeutil.TimeStamp(deadline.Unix())
  545. }
  546. if err := models.UpdateIssueDeadline(issue, deadlineUnix, ctx.User); err != nil {
  547. ctx.Error(500, "UpdateIssueDeadline", err)
  548. return
  549. }
  550. ctx.JSON(201, api.IssueDeadline{Deadline: &deadline})
  551. }
  552. // StartIssueStopwatch creates a stopwatch for the given issue.
  553. func StartIssueStopwatch(ctx *context.APIContext) {
  554. // swagger:operation POST /repos/{owner}/{repo}/issues/{index}/stopwatch/start issue issueStartStopWatch
  555. // ---
  556. // summary: Start stopwatch on an issue.
  557. // consumes:
  558. // - application/json
  559. // produces:
  560. // - application/json
  561. // parameters:
  562. // - name: owner
  563. // in: path
  564. // description: owner of the repo
  565. // type: string
  566. // required: true
  567. // - name: repo
  568. // in: path
  569. // description: name of the repo
  570. // type: string
  571. // required: true
  572. // - name: index
  573. // in: path
  574. // description: index of the issue to create the stopwatch on
  575. // type: integer
  576. // format: int64
  577. // required: true
  578. // responses:
  579. // "201":
  580. // "$ref": "#/responses/empty"
  581. // "403":
  582. // description: Not repo writer, user does not have rights to toggle stopwatch
  583. // "404":
  584. // description: Issue not found
  585. // "409":
  586. // description: Cannot start a stopwatch again if it already exists
  587. issue, err := models.GetIssueByIndex(ctx.Repo.Repository.ID, ctx.ParamsInt64(":index"))
  588. if err != nil {
  589. if models.IsErrIssueNotExist(err) {
  590. ctx.NotFound()
  591. } else {
  592. ctx.Error(500, "GetIssueByIndex", err)
  593. }
  594. return
  595. }
  596. if !ctx.Repo.CanWrite(models.UnitTypeIssues) {
  597. ctx.Status(403)
  598. return
  599. }
  600. if !ctx.Repo.CanUseTimetracker(issue, ctx.User) {
  601. ctx.Status(403)
  602. return
  603. }
  604. if models.StopwatchExists(ctx.User.ID, issue.ID) {
  605. ctx.Error(409, "StopwatchExists", "a stopwatch has already been started for this issue")
  606. return
  607. }
  608. if err := models.CreateOrStopIssueStopwatch(ctx.User, issue); err != nil {
  609. ctx.Error(500, "CreateOrStopIssueStopwatch", err)
  610. return
  611. }
  612. ctx.Status(201)
  613. }
  614. // StopIssueStopwatch stops a stopwatch for the given issue.
  615. func StopIssueStopwatch(ctx *context.APIContext) {
  616. // swagger:operation POST /repos/{owner}/{repo}/issues/{index}/stopwatch/stop issue issueStopWatch
  617. // ---
  618. // summary: Stop an issue's existing stopwatch.
  619. // consumes:
  620. // - application/json
  621. // produces:
  622. // - application/json
  623. // parameters:
  624. // - name: owner
  625. // in: path
  626. // description: owner of the repo
  627. // type: string
  628. // required: true
  629. // - name: repo
  630. // in: path
  631. // description: name of the repo
  632. // type: string
  633. // required: true
  634. // - name: index
  635. // in: path
  636. // description: index of the issue to stop the stopwatch on
  637. // type: integer
  638. // format: int64
  639. // required: true
  640. // responses:
  641. // "201":
  642. // "$ref": "#/responses/empty"
  643. // "403":
  644. // description: Not repo writer, user does not have rights to toggle stopwatch
  645. // "404":
  646. // description: Issue not found
  647. // "409":
  648. // description: Cannot stop a non existent stopwatch
  649. issue, err := models.GetIssueByIndex(ctx.Repo.Repository.ID, ctx.ParamsInt64(":index"))
  650. if err != nil {
  651. if models.IsErrIssueNotExist(err) {
  652. ctx.NotFound()
  653. } else {
  654. ctx.Error(500, "GetIssueByIndex", err)
  655. }
  656. return
  657. }
  658. if !ctx.Repo.CanWrite(models.UnitTypeIssues) {
  659. ctx.Status(403)
  660. return
  661. }
  662. if !ctx.Repo.CanUseTimetracker(issue, ctx.User) {
  663. ctx.Status(403)
  664. return
  665. }
  666. if !models.StopwatchExists(ctx.User.ID, issue.ID) {
  667. ctx.Error(409, "StopwatchExists", "cannot stop a non existent stopwatch")
  668. return
  669. }
  670. if err := models.CreateOrStopIssueStopwatch(ctx.User, issue); err != nil {
  671. ctx.Error(500, "CreateOrStopIssueStopwatch", err)
  672. return
  673. }
  674. ctx.Status(201)
  675. }