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.

repo.go 29 kB

Template Repositories (#8768) * Start work on templates Signed-off-by: jolheiser <john.olheiser@gmail.com> * Continue work Signed-off-by: jolheiser <john.olheiser@gmail.com> * Fix IsTemplate vs IsGenerated Signed-off-by: jolheiser <john.olheiser@gmail.com> * Fix tabs vs spaces * Tabs vs Spaces * Add templates to API & start adding tests Signed-off-by: jolheiser <john.olheiser@gmail.com> * Fix integration tests Signed-off-by: jolheiser <john.olheiser@gmail.com> * Remove unused User Signed-off-by: jolheiser <john.olheiser@gmail.com> * Move template tests to existing repos Signed-off-by: jolheiser <john.olheiser@gmail.com> * Minor re-check updates and cleanup Signed-off-by: jolheiser <john.olheiser@gmail.com> * make fmt Signed-off-by: jolheiser <john.olheiser@gmail.com> * Test cleanup Signed-off-by: jolheiser <john.olheiser@gmail.com> * Fix optionalbool Signed-off-by: jolheiser <john.olheiser@gmail.com> * make fmt Signed-off-by: jolheiser <john.olheiser@gmail.com> * Test fixes and icon change Signed-off-by: jolheiser <john.olheiser@gmail.com> * Add new user and repo for tests Signed-off-by: jolheiser <john.olheiser@gmail.com> * Fix tests (finally) Signed-off-by: jolheiser <john.olheiser@gmail.com> * Update meta repo with env variables Signed-off-by: jolheiser <john.olheiser@gmail.com> * Move generation to create page Combine with repo create template Modify API search to prioritize owner for repo Signed-off-by: jolheiser <john.olheiser@gmail.com> * Fix tests and coverage Signed-off-by: jolheiser <john.olheiser@gmail.com> * Fix swagger and JS lint Signed-off-by: jolheiser <john.olheiser@gmail.com> * Fix API searching for own private repos Signed-off-by: jolheiser <john.olheiser@gmail.com> * Change wording Signed-off-by: jolheiser <john.olheiser@gmail.com> * Fix repo search test. User had a private repo that didn't show up Signed-off-by: jolheiser <john.olheiser@gmail.com> * Another search test fix Signed-off-by: jolheiser <john.olheiser@gmail.com> * Clarify git content Co-Authored-By: guillep2k <18600385+guillep2k@users.noreply.github.com> * Feedback updates Signed-off-by: jolheiser <john.olheiser@gmail.com> * Add topics WIP Signed-off-by: jolheiser <john.olheiser@gmail.com> * Finish adding topics Signed-off-by: jolheiser <john.olheiser@gmail.com> * Update locale Signed-off-by: jolheiser <john.olheiser@gmail.com>
6 years ago
Template Repositories (#8768) * Start work on templates Signed-off-by: jolheiser <john.olheiser@gmail.com> * Continue work Signed-off-by: jolheiser <john.olheiser@gmail.com> * Fix IsTemplate vs IsGenerated Signed-off-by: jolheiser <john.olheiser@gmail.com> * Fix tabs vs spaces * Tabs vs Spaces * Add templates to API & start adding tests Signed-off-by: jolheiser <john.olheiser@gmail.com> * Fix integration tests Signed-off-by: jolheiser <john.olheiser@gmail.com> * Remove unused User Signed-off-by: jolheiser <john.olheiser@gmail.com> * Move template tests to existing repos Signed-off-by: jolheiser <john.olheiser@gmail.com> * Minor re-check updates and cleanup Signed-off-by: jolheiser <john.olheiser@gmail.com> * make fmt Signed-off-by: jolheiser <john.olheiser@gmail.com> * Test cleanup Signed-off-by: jolheiser <john.olheiser@gmail.com> * Fix optionalbool Signed-off-by: jolheiser <john.olheiser@gmail.com> * make fmt Signed-off-by: jolheiser <john.olheiser@gmail.com> * Test fixes and icon change Signed-off-by: jolheiser <john.olheiser@gmail.com> * Add new user and repo for tests Signed-off-by: jolheiser <john.olheiser@gmail.com> * Fix tests (finally) Signed-off-by: jolheiser <john.olheiser@gmail.com> * Update meta repo with env variables Signed-off-by: jolheiser <john.olheiser@gmail.com> * Move generation to create page Combine with repo create template Modify API search to prioritize owner for repo Signed-off-by: jolheiser <john.olheiser@gmail.com> * Fix tests and coverage Signed-off-by: jolheiser <john.olheiser@gmail.com> * Fix swagger and JS lint Signed-off-by: jolheiser <john.olheiser@gmail.com> * Fix API searching for own private repos Signed-off-by: jolheiser <john.olheiser@gmail.com> * Change wording Signed-off-by: jolheiser <john.olheiser@gmail.com> * Fix repo search test. User had a private repo that didn't show up Signed-off-by: jolheiser <john.olheiser@gmail.com> * Another search test fix Signed-off-by: jolheiser <john.olheiser@gmail.com> * Clarify git content Co-Authored-By: guillep2k <18600385+guillep2k@users.noreply.github.com> * Feedback updates Signed-off-by: jolheiser <john.olheiser@gmail.com> * Add topics WIP Signed-off-by: jolheiser <john.olheiser@gmail.com> * Finish adding topics Signed-off-by: jolheiser <john.olheiser@gmail.com> * Update locale Signed-off-by: jolheiser <john.olheiser@gmail.com>
6 years ago
Template Repositories (#8768) * Start work on templates Signed-off-by: jolheiser <john.olheiser@gmail.com> * Continue work Signed-off-by: jolheiser <john.olheiser@gmail.com> * Fix IsTemplate vs IsGenerated Signed-off-by: jolheiser <john.olheiser@gmail.com> * Fix tabs vs spaces * Tabs vs Spaces * Add templates to API & start adding tests Signed-off-by: jolheiser <john.olheiser@gmail.com> * Fix integration tests Signed-off-by: jolheiser <john.olheiser@gmail.com> * Remove unused User Signed-off-by: jolheiser <john.olheiser@gmail.com> * Move template tests to existing repos Signed-off-by: jolheiser <john.olheiser@gmail.com> * Minor re-check updates and cleanup Signed-off-by: jolheiser <john.olheiser@gmail.com> * make fmt Signed-off-by: jolheiser <john.olheiser@gmail.com> * Test cleanup Signed-off-by: jolheiser <john.olheiser@gmail.com> * Fix optionalbool Signed-off-by: jolheiser <john.olheiser@gmail.com> * make fmt Signed-off-by: jolheiser <john.olheiser@gmail.com> * Test fixes and icon change Signed-off-by: jolheiser <john.olheiser@gmail.com> * Add new user and repo for tests Signed-off-by: jolheiser <john.olheiser@gmail.com> * Fix tests (finally) Signed-off-by: jolheiser <john.olheiser@gmail.com> * Update meta repo with env variables Signed-off-by: jolheiser <john.olheiser@gmail.com> * Move generation to create page Combine with repo create template Modify API search to prioritize owner for repo Signed-off-by: jolheiser <john.olheiser@gmail.com> * Fix tests and coverage Signed-off-by: jolheiser <john.olheiser@gmail.com> * Fix swagger and JS lint Signed-off-by: jolheiser <john.olheiser@gmail.com> * Fix API searching for own private repos Signed-off-by: jolheiser <john.olheiser@gmail.com> * Change wording Signed-off-by: jolheiser <john.olheiser@gmail.com> * Fix repo search test. User had a private repo that didn't show up Signed-off-by: jolheiser <john.olheiser@gmail.com> * Another search test fix Signed-off-by: jolheiser <john.olheiser@gmail.com> * Clarify git content Co-Authored-By: guillep2k <18600385+guillep2k@users.noreply.github.com> * Feedback updates Signed-off-by: jolheiser <john.olheiser@gmail.com> * Add topics WIP Signed-off-by: jolheiser <john.olheiser@gmail.com> * Finish adding topics Signed-off-by: jolheiser <john.olheiser@gmail.com> * Update locale Signed-off-by: jolheiser <john.olheiser@gmail.com>
6 years ago
Template Repositories (#8768) * Start work on templates Signed-off-by: jolheiser <john.olheiser@gmail.com> * Continue work Signed-off-by: jolheiser <john.olheiser@gmail.com> * Fix IsTemplate vs IsGenerated Signed-off-by: jolheiser <john.olheiser@gmail.com> * Fix tabs vs spaces * Tabs vs Spaces * Add templates to API & start adding tests Signed-off-by: jolheiser <john.olheiser@gmail.com> * Fix integration tests Signed-off-by: jolheiser <john.olheiser@gmail.com> * Remove unused User Signed-off-by: jolheiser <john.olheiser@gmail.com> * Move template tests to existing repos Signed-off-by: jolheiser <john.olheiser@gmail.com> * Minor re-check updates and cleanup Signed-off-by: jolheiser <john.olheiser@gmail.com> * make fmt Signed-off-by: jolheiser <john.olheiser@gmail.com> * Test cleanup Signed-off-by: jolheiser <john.olheiser@gmail.com> * Fix optionalbool Signed-off-by: jolheiser <john.olheiser@gmail.com> * make fmt Signed-off-by: jolheiser <john.olheiser@gmail.com> * Test fixes and icon change Signed-off-by: jolheiser <john.olheiser@gmail.com> * Add new user and repo for tests Signed-off-by: jolheiser <john.olheiser@gmail.com> * Fix tests (finally) Signed-off-by: jolheiser <john.olheiser@gmail.com> * Update meta repo with env variables Signed-off-by: jolheiser <john.olheiser@gmail.com> * Move generation to create page Combine with repo create template Modify API search to prioritize owner for repo Signed-off-by: jolheiser <john.olheiser@gmail.com> * Fix tests and coverage Signed-off-by: jolheiser <john.olheiser@gmail.com> * Fix swagger and JS lint Signed-off-by: jolheiser <john.olheiser@gmail.com> * Fix API searching for own private repos Signed-off-by: jolheiser <john.olheiser@gmail.com> * Change wording Signed-off-by: jolheiser <john.olheiser@gmail.com> * Fix repo search test. User had a private repo that didn't show up Signed-off-by: jolheiser <john.olheiser@gmail.com> * Another search test fix Signed-off-by: jolheiser <john.olheiser@gmail.com> * Clarify git content Co-Authored-By: guillep2k <18600385+guillep2k@users.noreply.github.com> * Feedback updates Signed-off-by: jolheiser <john.olheiser@gmail.com> * Add topics WIP Signed-off-by: jolheiser <john.olheiser@gmail.com> * Finish adding topics Signed-off-by: jolheiser <john.olheiser@gmail.com> * Update locale Signed-off-by: jolheiser <john.olheiser@gmail.com>
6 years ago
Template Repositories (#8768) * Start work on templates Signed-off-by: jolheiser <john.olheiser@gmail.com> * Continue work Signed-off-by: jolheiser <john.olheiser@gmail.com> * Fix IsTemplate vs IsGenerated Signed-off-by: jolheiser <john.olheiser@gmail.com> * Fix tabs vs spaces * Tabs vs Spaces * Add templates to API & start adding tests Signed-off-by: jolheiser <john.olheiser@gmail.com> * Fix integration tests Signed-off-by: jolheiser <john.olheiser@gmail.com> * Remove unused User Signed-off-by: jolheiser <john.olheiser@gmail.com> * Move template tests to existing repos Signed-off-by: jolheiser <john.olheiser@gmail.com> * Minor re-check updates and cleanup Signed-off-by: jolheiser <john.olheiser@gmail.com> * make fmt Signed-off-by: jolheiser <john.olheiser@gmail.com> * Test cleanup Signed-off-by: jolheiser <john.olheiser@gmail.com> * Fix optionalbool Signed-off-by: jolheiser <john.olheiser@gmail.com> * make fmt Signed-off-by: jolheiser <john.olheiser@gmail.com> * Test fixes and icon change Signed-off-by: jolheiser <john.olheiser@gmail.com> * Add new user and repo for tests Signed-off-by: jolheiser <john.olheiser@gmail.com> * Fix tests (finally) Signed-off-by: jolheiser <john.olheiser@gmail.com> * Update meta repo with env variables Signed-off-by: jolheiser <john.olheiser@gmail.com> * Move generation to create page Combine with repo create template Modify API search to prioritize owner for repo Signed-off-by: jolheiser <john.olheiser@gmail.com> * Fix tests and coverage Signed-off-by: jolheiser <john.olheiser@gmail.com> * Fix swagger and JS lint Signed-off-by: jolheiser <john.olheiser@gmail.com> * Fix API searching for own private repos Signed-off-by: jolheiser <john.olheiser@gmail.com> * Change wording Signed-off-by: jolheiser <john.olheiser@gmail.com> * Fix repo search test. User had a private repo that didn't show up Signed-off-by: jolheiser <john.olheiser@gmail.com> * Another search test fix Signed-off-by: jolheiser <john.olheiser@gmail.com> * Clarify git content Co-Authored-By: guillep2k <18600385+guillep2k@users.noreply.github.com> * Feedback updates Signed-off-by: jolheiser <john.olheiser@gmail.com> * Add topics WIP Signed-off-by: jolheiser <john.olheiser@gmail.com> * Finish adding topics Signed-off-by: jolheiser <john.olheiser@gmail.com> * Update locale Signed-off-by: jolheiser <john.olheiser@gmail.com>
6 years ago
11 years ago
10 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
10 years ago
Template Repositories (#8768) * Start work on templates Signed-off-by: jolheiser <john.olheiser@gmail.com> * Continue work Signed-off-by: jolheiser <john.olheiser@gmail.com> * Fix IsTemplate vs IsGenerated Signed-off-by: jolheiser <john.olheiser@gmail.com> * Fix tabs vs spaces * Tabs vs Spaces * Add templates to API & start adding tests Signed-off-by: jolheiser <john.olheiser@gmail.com> * Fix integration tests Signed-off-by: jolheiser <john.olheiser@gmail.com> * Remove unused User Signed-off-by: jolheiser <john.olheiser@gmail.com> * Move template tests to existing repos Signed-off-by: jolheiser <john.olheiser@gmail.com> * Minor re-check updates and cleanup Signed-off-by: jolheiser <john.olheiser@gmail.com> * make fmt Signed-off-by: jolheiser <john.olheiser@gmail.com> * Test cleanup Signed-off-by: jolheiser <john.olheiser@gmail.com> * Fix optionalbool Signed-off-by: jolheiser <john.olheiser@gmail.com> * make fmt Signed-off-by: jolheiser <john.olheiser@gmail.com> * Test fixes and icon change Signed-off-by: jolheiser <john.olheiser@gmail.com> * Add new user and repo for tests Signed-off-by: jolheiser <john.olheiser@gmail.com> * Fix tests (finally) Signed-off-by: jolheiser <john.olheiser@gmail.com> * Update meta repo with env variables Signed-off-by: jolheiser <john.olheiser@gmail.com> * Move generation to create page Combine with repo create template Modify API search to prioritize owner for repo Signed-off-by: jolheiser <john.olheiser@gmail.com> * Fix tests and coverage Signed-off-by: jolheiser <john.olheiser@gmail.com> * Fix swagger and JS lint Signed-off-by: jolheiser <john.olheiser@gmail.com> * Fix API searching for own private repos Signed-off-by: jolheiser <john.olheiser@gmail.com> * Change wording Signed-off-by: jolheiser <john.olheiser@gmail.com> * Fix repo search test. User had a private repo that didn't show up Signed-off-by: jolheiser <john.olheiser@gmail.com> * Another search test fix Signed-off-by: jolheiser <john.olheiser@gmail.com> * Clarify git content Co-Authored-By: guillep2k <18600385+guillep2k@users.noreply.github.com> * Feedback updates Signed-off-by: jolheiser <john.olheiser@gmail.com> * Add topics WIP Signed-off-by: jolheiser <john.olheiser@gmail.com> * Finish adding topics Signed-off-by: jolheiser <john.olheiser@gmail.com> * Update locale Signed-off-by: jolheiser <john.olheiser@gmail.com>
6 years ago
10 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979
  1. // Copyright 2014 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. "bytes"
  8. "errors"
  9. "fmt"
  10. "net/http"
  11. "net/url"
  12. "strings"
  13. "code.gitea.io/gitea/models"
  14. "code.gitea.io/gitea/modules/auth"
  15. "code.gitea.io/gitea/modules/context"
  16. "code.gitea.io/gitea/modules/convert"
  17. "code.gitea.io/gitea/modules/git"
  18. "code.gitea.io/gitea/modules/log"
  19. "code.gitea.io/gitea/modules/migrations"
  20. "code.gitea.io/gitea/modules/notification"
  21. "code.gitea.io/gitea/modules/setting"
  22. "code.gitea.io/gitea/modules/structs"
  23. api "code.gitea.io/gitea/modules/structs"
  24. "code.gitea.io/gitea/modules/util"
  25. "code.gitea.io/gitea/modules/validation"
  26. mirror_service "code.gitea.io/gitea/services/mirror"
  27. repo_service "code.gitea.io/gitea/services/repository"
  28. )
  29. var searchOrderByMap = map[string]map[string]models.SearchOrderBy{
  30. "asc": {
  31. "alpha": models.SearchOrderByAlphabetically,
  32. "created": models.SearchOrderByOldest,
  33. "updated": models.SearchOrderByLeastUpdated,
  34. "size": models.SearchOrderBySize,
  35. "id": models.SearchOrderByID,
  36. },
  37. "desc": {
  38. "alpha": models.SearchOrderByAlphabeticallyReverse,
  39. "created": models.SearchOrderByNewest,
  40. "updated": models.SearchOrderByRecentUpdated,
  41. "size": models.SearchOrderBySizeReverse,
  42. "id": models.SearchOrderByIDReverse,
  43. },
  44. }
  45. // Search repositories via options
  46. func Search(ctx *context.APIContext) {
  47. // swagger:operation GET /repos/search repository repoSearch
  48. // ---
  49. // summary: Search for repositories
  50. // produces:
  51. // - application/json
  52. // parameters:
  53. // - name: q
  54. // in: query
  55. // description: keyword
  56. // type: string
  57. // - name: topic
  58. // in: query
  59. // description: Limit search to repositories with keyword as topic
  60. // type: boolean
  61. // - name: includeDesc
  62. // in: query
  63. // description: include search of keyword within repository description
  64. // type: boolean
  65. // - name: uid
  66. // in: query
  67. // description: search only for repos that the user with the given id owns or contributes to
  68. // type: integer
  69. // format: int64
  70. // - name: priority_owner_id
  71. // in: query
  72. // description: repo owner to prioritize in the results
  73. // type: integer
  74. // format: int64
  75. // - name: starredBy
  76. // in: query
  77. // description: search only for repos that the user with the given id has starred
  78. // type: integer
  79. // format: int64
  80. // - name: private
  81. // in: query
  82. // description: include private repositories this user has access to (defaults to true)
  83. // type: boolean
  84. // - name: template
  85. // in: query
  86. // description: include template repositories this user has access to (defaults to true)
  87. // type: boolean
  88. // - name: page
  89. // in: query
  90. // description: page number of results to return (1-based)
  91. // type: integer
  92. // - name: limit
  93. // in: query
  94. // description: page size of results, maximum page size is 50
  95. // type: integer
  96. // - name: mode
  97. // in: query
  98. // description: type of repository to search for. Supported values are
  99. // "fork", "source", "mirror" and "collaborative"
  100. // type: string
  101. // - name: exclusive
  102. // in: query
  103. // description: if `uid` is given, search only for repos that the user owns
  104. // type: boolean
  105. // - name: sort
  106. // in: query
  107. // description: sort repos by attribute. Supported values are
  108. // "alpha", "created", "updated", "size", and "id".
  109. // Default is "alpha"
  110. // type: string
  111. // - name: order
  112. // in: query
  113. // description: sort order, either "asc" (ascending) or "desc" (descending).
  114. // Default is "asc", ignored if "sort" is not specified.
  115. // type: string
  116. // responses:
  117. // "200":
  118. // "$ref": "#/responses/SearchResults"
  119. // "422":
  120. // "$ref": "#/responses/validationError"
  121. opts := &models.SearchRepoOptions{
  122. Keyword: strings.Trim(ctx.Query("q"), " "),
  123. OwnerID: ctx.QueryInt64("uid"),
  124. PriorityOwnerID: ctx.QueryInt64("priority_owner_id"),
  125. Page: ctx.QueryInt("page"),
  126. PageSize: convert.ToCorrectPageSize(ctx.QueryInt("limit")),
  127. TopicOnly: ctx.QueryBool("topic"),
  128. Collaborate: util.OptionalBoolNone,
  129. Private: ctx.IsSigned && (ctx.Query("private") == "" || ctx.QueryBool("private")),
  130. Template: util.OptionalBoolNone,
  131. UserIsAdmin: ctx.IsUserSiteAdmin(),
  132. UserID: ctx.Data["SignedUserID"].(int64),
  133. StarredByID: ctx.QueryInt64("starredBy"),
  134. IncludeDescription: ctx.QueryBool("includeDesc"),
  135. }
  136. if ctx.Query("template") != "" {
  137. opts.Template = util.OptionalBoolOf(ctx.QueryBool("template"))
  138. }
  139. if ctx.QueryBool("exclusive") {
  140. opts.Collaborate = util.OptionalBoolFalse
  141. }
  142. var mode = ctx.Query("mode")
  143. switch mode {
  144. case "source":
  145. opts.Fork = util.OptionalBoolFalse
  146. opts.Mirror = util.OptionalBoolFalse
  147. case "fork":
  148. opts.Fork = util.OptionalBoolTrue
  149. case "mirror":
  150. opts.Mirror = util.OptionalBoolTrue
  151. case "collaborative":
  152. opts.Mirror = util.OptionalBoolFalse
  153. opts.Collaborate = util.OptionalBoolTrue
  154. case "":
  155. default:
  156. ctx.Error(http.StatusUnprocessableEntity, "", fmt.Errorf("Invalid search mode: \"%s\"", mode))
  157. return
  158. }
  159. var sortMode = ctx.Query("sort")
  160. if len(sortMode) > 0 {
  161. var sortOrder = ctx.Query("order")
  162. if len(sortOrder) == 0 {
  163. sortOrder = "asc"
  164. }
  165. if searchModeMap, ok := searchOrderByMap[sortOrder]; ok {
  166. if orderBy, ok := searchModeMap[sortMode]; ok {
  167. opts.OrderBy = orderBy
  168. } else {
  169. ctx.Error(http.StatusUnprocessableEntity, "", fmt.Errorf("Invalid sort mode: \"%s\"", sortMode))
  170. return
  171. }
  172. } else {
  173. ctx.Error(http.StatusUnprocessableEntity, "", fmt.Errorf("Invalid sort order: \"%s\"", sortOrder))
  174. return
  175. }
  176. }
  177. var err error
  178. repos, count, err := models.SearchRepository(opts)
  179. if err != nil {
  180. ctx.JSON(500, api.SearchError{
  181. OK: false,
  182. Error: err.Error(),
  183. })
  184. return
  185. }
  186. results := make([]*api.Repository, len(repos))
  187. for i, repo := range repos {
  188. if err = repo.GetOwner(); err != nil {
  189. ctx.JSON(500, api.SearchError{
  190. OK: false,
  191. Error: err.Error(),
  192. })
  193. return
  194. }
  195. accessMode, err := models.AccessLevel(ctx.User, repo)
  196. if err != nil {
  197. ctx.JSON(500, api.SearchError{
  198. OK: false,
  199. Error: err.Error(),
  200. })
  201. }
  202. results[i] = repo.APIFormat(accessMode)
  203. }
  204. ctx.SetLinkHeader(int(count), setting.API.MaxResponseItems)
  205. ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", count))
  206. ctx.JSON(200, api.SearchResults{
  207. OK: true,
  208. Data: results,
  209. })
  210. }
  211. // CreateUserRepo create a repository for a user
  212. func CreateUserRepo(ctx *context.APIContext, owner *models.User, opt api.CreateRepoOption) {
  213. if opt.AutoInit && opt.Readme == "" {
  214. opt.Readme = "Default"
  215. }
  216. repo, err := repo_service.CreateRepository(ctx.User, owner, models.CreateRepoOptions{
  217. Name: opt.Name,
  218. Description: opt.Description,
  219. IssueLabels: opt.IssueLabels,
  220. Gitignores: opt.Gitignores,
  221. License: opt.License,
  222. Readme: opt.Readme,
  223. IsPrivate: opt.Private,
  224. AutoInit: opt.AutoInit,
  225. })
  226. if err != nil {
  227. if models.IsErrRepoAlreadyExist(err) {
  228. ctx.Error(409, "", "The repository with the same name already exists.")
  229. } else if models.IsErrNameReserved(err) ||
  230. models.IsErrNamePatternNotAllowed(err) {
  231. ctx.Error(422, "", err)
  232. } else {
  233. ctx.Error(500, "CreateRepository", err)
  234. }
  235. return
  236. }
  237. ctx.JSON(201, repo.APIFormat(models.AccessModeOwner))
  238. }
  239. // Create one repository of mine
  240. func Create(ctx *context.APIContext, opt api.CreateRepoOption) {
  241. // swagger:operation POST /user/repos repository user createCurrentUserRepo
  242. // ---
  243. // summary: Create a repository
  244. // consumes:
  245. // - application/json
  246. // produces:
  247. // - application/json
  248. // parameters:
  249. // - name: body
  250. // in: body
  251. // schema:
  252. // "$ref": "#/definitions/CreateRepoOption"
  253. // responses:
  254. // "201":
  255. // "$ref": "#/responses/Repository"
  256. // "409":
  257. // description: The repository with the same name already exists.
  258. // "422":
  259. // "$ref": "#/responses/validationError"
  260. if ctx.User.IsOrganization() {
  261. // Shouldn't reach this condition, but just in case.
  262. ctx.Error(422, "", "not allowed creating repository for organization")
  263. return
  264. }
  265. CreateUserRepo(ctx, ctx.User, opt)
  266. }
  267. // CreateOrgRepo create one repository of the organization
  268. func CreateOrgRepo(ctx *context.APIContext, opt api.CreateRepoOption) {
  269. // swagger:operation POST /org/{org}/repos organization createOrgRepo
  270. // ---
  271. // summary: Create a repository in an organization
  272. // consumes:
  273. // - application/json
  274. // produces:
  275. // - application/json
  276. // parameters:
  277. // - name: org
  278. // in: path
  279. // description: name of organization
  280. // type: string
  281. // required: true
  282. // - name: body
  283. // in: body
  284. // schema:
  285. // "$ref": "#/definitions/CreateRepoOption"
  286. // responses:
  287. // "201":
  288. // "$ref": "#/responses/Repository"
  289. // "422":
  290. // "$ref": "#/responses/validationError"
  291. // "403":
  292. // "$ref": "#/responses/forbidden"
  293. org, err := models.GetOrgByName(ctx.Params(":org"))
  294. if err != nil {
  295. if models.IsErrOrgNotExist(err) {
  296. ctx.Error(422, "", err)
  297. } else {
  298. ctx.Error(500, "GetOrgByName", err)
  299. }
  300. return
  301. }
  302. if !models.HasOrgVisible(org, ctx.User) {
  303. ctx.NotFound("HasOrgVisible", nil)
  304. return
  305. }
  306. if !ctx.User.IsAdmin {
  307. canCreate, err := org.CanCreateOrgRepo(ctx.User.ID)
  308. if err != nil {
  309. ctx.ServerError("CanCreateOrgRepo", err)
  310. return
  311. } else if !canCreate {
  312. ctx.Error(403, "", "Given user is not allowed to create repository in organization.")
  313. return
  314. }
  315. }
  316. CreateUserRepo(ctx, org, opt)
  317. }
  318. // Migrate migrate remote git repository to gitea
  319. func Migrate(ctx *context.APIContext, form auth.MigrateRepoForm) {
  320. // swagger:operation POST /repos/migrate repository repoMigrate
  321. // ---
  322. // summary: Migrate a remote git repository
  323. // consumes:
  324. // - application/json
  325. // produces:
  326. // - application/json
  327. // parameters:
  328. // - name: body
  329. // in: body
  330. // schema:
  331. // "$ref": "#/definitions/MigrateRepoForm"
  332. // responses:
  333. // "201":
  334. // "$ref": "#/responses/Repository"
  335. ctxUser := ctx.User
  336. // Not equal means context user is an organization,
  337. // or is another user/organization if current user is admin.
  338. if form.UID != ctxUser.ID {
  339. org, err := models.GetUserByID(form.UID)
  340. if err != nil {
  341. if models.IsErrUserNotExist(err) {
  342. ctx.Error(422, "", err)
  343. } else {
  344. ctx.Error(500, "GetUserByID", err)
  345. }
  346. return
  347. }
  348. ctxUser = org
  349. }
  350. if ctx.HasError() {
  351. ctx.Error(422, "", ctx.GetErrMsg())
  352. return
  353. }
  354. if !ctx.User.IsAdmin {
  355. if !ctxUser.IsOrganization() && ctx.User.ID != ctxUser.ID {
  356. ctx.Error(403, "", "Given user is not an organization.")
  357. return
  358. }
  359. if ctxUser.IsOrganization() {
  360. // Check ownership of organization.
  361. isOwner, err := ctxUser.IsOwnedBy(ctx.User.ID)
  362. if err != nil {
  363. ctx.Error(500, "IsOwnedBy", err)
  364. return
  365. } else if !isOwner {
  366. ctx.Error(403, "", "Given user is not owner of organization.")
  367. return
  368. }
  369. }
  370. }
  371. remoteAddr, err := form.ParseRemoteAddr(ctx.User)
  372. if err != nil {
  373. if models.IsErrInvalidCloneAddr(err) {
  374. addrErr := err.(models.ErrInvalidCloneAddr)
  375. switch {
  376. case addrErr.IsURLError:
  377. ctx.Error(422, "", err)
  378. case addrErr.IsPermissionDenied:
  379. ctx.Error(422, "", "You are not allowed to import local repositories.")
  380. case addrErr.IsInvalidPath:
  381. ctx.Error(422, "", "Invalid local path, it does not exist or not a directory.")
  382. default:
  383. ctx.Error(500, "ParseRemoteAddr", "Unknown error type (ErrInvalidCloneAddr): "+err.Error())
  384. }
  385. } else {
  386. ctx.Error(500, "ParseRemoteAddr", err)
  387. }
  388. return
  389. }
  390. var gitServiceType = structs.PlainGitService
  391. u, err := url.Parse(remoteAddr)
  392. if err == nil && strings.EqualFold(u.Host, "github.com") {
  393. gitServiceType = structs.GithubService
  394. }
  395. var opts = migrations.MigrateOptions{
  396. CloneAddr: remoteAddr,
  397. RepoName: form.RepoName,
  398. Description: form.Description,
  399. Private: form.Private || setting.Repository.ForcePrivate,
  400. Mirror: form.Mirror,
  401. AuthUsername: form.AuthUsername,
  402. AuthPassword: form.AuthPassword,
  403. Wiki: form.Wiki,
  404. Issues: form.Issues,
  405. Milestones: form.Milestones,
  406. Labels: form.Labels,
  407. Comments: true,
  408. PullRequests: form.PullRequests,
  409. Releases: form.Releases,
  410. GitServiceType: gitServiceType,
  411. }
  412. if opts.Mirror {
  413. opts.Issues = false
  414. opts.Milestones = false
  415. opts.Labels = false
  416. opts.Comments = false
  417. opts.PullRequests = false
  418. opts.Releases = false
  419. }
  420. repo, err := models.CreateRepository(ctx.User, ctxUser, models.CreateRepoOptions{
  421. Name: opts.RepoName,
  422. Description: opts.Description,
  423. OriginalURL: opts.CloneAddr,
  424. IsPrivate: opts.Private,
  425. IsMirror: opts.Mirror,
  426. Status: models.RepositoryBeingMigrated,
  427. })
  428. if err != nil {
  429. handleMigrateError(ctx, ctxUser, remoteAddr, err)
  430. return
  431. }
  432. opts.MigrateToRepoID = repo.ID
  433. defer func() {
  434. if e := recover(); e != nil {
  435. var buf bytes.Buffer
  436. fmt.Fprintf(&buf, "Handler crashed with error: %v", log.Stack(2))
  437. err = errors.New(buf.String())
  438. }
  439. if err == nil {
  440. repo.Status = models.RepositoryReady
  441. if err := models.UpdateRepositoryCols(repo, "status"); err == nil {
  442. notification.NotifyMigrateRepository(ctx.User, ctxUser, repo)
  443. return
  444. }
  445. }
  446. if repo != nil {
  447. if errDelete := models.DeleteRepository(ctx.User, ctxUser.ID, repo.ID); errDelete != nil {
  448. log.Error("DeleteRepository: %v", errDelete)
  449. }
  450. }
  451. }()
  452. if _, err = migrations.MigrateRepository(ctx.User, ctxUser.Name, opts); err != nil {
  453. handleMigrateError(ctx, ctxUser, remoteAddr, err)
  454. return
  455. }
  456. log.Trace("Repository migrated: %s/%s", ctxUser.Name, form.RepoName)
  457. ctx.JSON(201, repo.APIFormat(models.AccessModeAdmin))
  458. }
  459. func handleMigrateError(ctx *context.APIContext, repoOwner *models.User, remoteAddr string, err error) {
  460. switch {
  461. case models.IsErrRepoAlreadyExist(err):
  462. ctx.Error(409, "", "The repository with the same name already exists.")
  463. case migrations.IsRateLimitError(err):
  464. ctx.Error(422, "", "Remote visit addressed rate limitation.")
  465. case migrations.IsTwoFactorAuthError(err):
  466. ctx.Error(422, "", "Remote visit required two factors authentication.")
  467. case models.IsErrReachLimitOfRepo(err):
  468. ctx.Error(422, "", fmt.Sprintf("You have already reached your limit of %d repositories.", repoOwner.MaxCreationLimit()))
  469. case models.IsErrNameReserved(err):
  470. ctx.Error(422, "", fmt.Sprintf("The username '%s' is reserved.", err.(models.ErrNameReserved).Name))
  471. case models.IsErrNamePatternNotAllowed(err):
  472. ctx.Error(422, "", fmt.Sprintf("The pattern '%s' is not allowed in a username.", err.(models.ErrNamePatternNotAllowed).Pattern))
  473. default:
  474. err = util.URLSanitizedError(err, remoteAddr)
  475. if strings.Contains(err.Error(), "Authentication failed") ||
  476. strings.Contains(err.Error(), "Bad credentials") ||
  477. strings.Contains(err.Error(), "could not read Username") {
  478. ctx.Error(422, "", fmt.Sprintf("Authentication failed: %v.", err))
  479. } else if strings.Contains(err.Error(), "fatal:") {
  480. ctx.Error(422, "", fmt.Sprintf("Migration failed: %v.", err))
  481. } else {
  482. ctx.Error(500, "MigrateRepository", err)
  483. }
  484. }
  485. }
  486. // Get one repository
  487. func Get(ctx *context.APIContext) {
  488. // swagger:operation GET /repos/{owner}/{repo} repository repoGet
  489. // ---
  490. // summary: Get a repository
  491. // produces:
  492. // - application/json
  493. // parameters:
  494. // - name: owner
  495. // in: path
  496. // description: owner of the repo
  497. // type: string
  498. // required: true
  499. // - name: repo
  500. // in: path
  501. // description: name of the repo
  502. // type: string
  503. // required: true
  504. // responses:
  505. // "200":
  506. // "$ref": "#/responses/Repository"
  507. ctx.JSON(200, ctx.Repo.Repository.APIFormat(ctx.Repo.AccessMode))
  508. }
  509. // GetByID returns a single Repository
  510. func GetByID(ctx *context.APIContext) {
  511. // swagger:operation GET /repositories/{id} repository repoGetByID
  512. // ---
  513. // summary: Get a repository by id
  514. // produces:
  515. // - application/json
  516. // parameters:
  517. // - name: id
  518. // in: path
  519. // description: id of the repo to get
  520. // type: integer
  521. // format: int64
  522. // required: true
  523. // responses:
  524. // "200":
  525. // "$ref": "#/responses/Repository"
  526. repo, err := models.GetRepositoryByID(ctx.ParamsInt64(":id"))
  527. if err != nil {
  528. if models.IsErrRepoNotExist(err) {
  529. ctx.NotFound()
  530. } else {
  531. ctx.Error(500, "GetRepositoryByID", err)
  532. }
  533. return
  534. }
  535. perm, err := models.GetUserRepoPermission(repo, ctx.User)
  536. if err != nil {
  537. ctx.Error(500, "AccessLevel", err)
  538. return
  539. } else if !perm.HasAccess() {
  540. ctx.NotFound()
  541. return
  542. }
  543. ctx.JSON(200, repo.APIFormat(perm.AccessMode))
  544. }
  545. // Edit edit repository properties
  546. func Edit(ctx *context.APIContext, opts api.EditRepoOption) {
  547. // swagger:operation PATCH /repos/{owner}/{repo} repository repoEdit
  548. // ---
  549. // summary: Edit a repository's properties. Only fields that are set will be changed.
  550. // produces:
  551. // - application/json
  552. // parameters:
  553. // - name: owner
  554. // in: path
  555. // description: owner of the repo to edit
  556. // type: string
  557. // required: true
  558. // - name: repo
  559. // in: path
  560. // description: name of the repo to edit
  561. // type: string
  562. // required: true
  563. // required: true
  564. // - name: body
  565. // in: body
  566. // description: "Properties of a repo that you can edit"
  567. // schema:
  568. // "$ref": "#/definitions/EditRepoOption"
  569. // responses:
  570. // "200":
  571. // "$ref": "#/responses/Repository"
  572. // "403":
  573. // "$ref": "#/responses/forbidden"
  574. // "422":
  575. // "$ref": "#/responses/validationError"
  576. if err := updateBasicProperties(ctx, opts); err != nil {
  577. return
  578. }
  579. if err := updateRepoUnits(ctx, opts); err != nil {
  580. return
  581. }
  582. if opts.Archived != nil {
  583. if err := updateRepoArchivedState(ctx, opts); err != nil {
  584. return
  585. }
  586. }
  587. ctx.JSON(http.StatusOK, ctx.Repo.Repository.APIFormat(ctx.Repo.AccessMode))
  588. }
  589. // updateBasicProperties updates the basic properties of a repo: Name, Description, Website and Visibility
  590. func updateBasicProperties(ctx *context.APIContext, opts api.EditRepoOption) error {
  591. owner := ctx.Repo.Owner
  592. repo := ctx.Repo.Repository
  593. newRepoName := repo.Name
  594. if opts.Name != nil {
  595. newRepoName = *opts.Name
  596. }
  597. // Check if repository name has been changed and not just a case change
  598. if repo.LowerName != strings.ToLower(newRepoName) {
  599. if err := repo_service.ChangeRepositoryName(ctx.User, repo, newRepoName); err != nil {
  600. switch {
  601. case models.IsErrRepoAlreadyExist(err):
  602. ctx.Error(http.StatusUnprocessableEntity, fmt.Sprintf("repo name is already taken [name: %s]", newRepoName), err)
  603. case models.IsErrNameReserved(err):
  604. ctx.Error(http.StatusUnprocessableEntity, fmt.Sprintf("repo name is reserved [name: %s]", newRepoName), err)
  605. case models.IsErrNamePatternNotAllowed(err):
  606. ctx.Error(http.StatusUnprocessableEntity, fmt.Sprintf("repo name's pattern is not allowed [name: %s, pattern: %s]", newRepoName, err.(models.ErrNamePatternNotAllowed).Pattern), err)
  607. default:
  608. ctx.Error(http.StatusUnprocessableEntity, "ChangeRepositoryName", err)
  609. }
  610. return err
  611. }
  612. log.Trace("Repository name changed: %s/%s -> %s", ctx.Repo.Owner.Name, repo.Name, newRepoName)
  613. }
  614. // Update the name in the repo object for the response
  615. repo.Name = newRepoName
  616. repo.LowerName = strings.ToLower(newRepoName)
  617. if opts.Description != nil {
  618. repo.Description = *opts.Description
  619. }
  620. if opts.Website != nil {
  621. repo.Website = *opts.Website
  622. }
  623. visibilityChanged := false
  624. if opts.Private != nil {
  625. // Visibility of forked repository is forced sync with base repository.
  626. if repo.IsFork {
  627. *opts.Private = repo.BaseRepo.IsPrivate
  628. }
  629. visibilityChanged = repo.IsPrivate != *opts.Private
  630. // when ForcePrivate enabled, you could change public repo to private, but only admin users can change private to public
  631. if visibilityChanged && setting.Repository.ForcePrivate && !*opts.Private && !ctx.User.IsAdmin {
  632. err := fmt.Errorf("cannot change private repository to public")
  633. ctx.Error(http.StatusUnprocessableEntity, "Force Private enabled", err)
  634. return err
  635. }
  636. repo.IsPrivate = *opts.Private
  637. }
  638. if opts.Template != nil {
  639. repo.IsTemplate = *opts.Template
  640. }
  641. // Default branch only updated if changed and exist
  642. if opts.DefaultBranch != nil && repo.DefaultBranch != *opts.DefaultBranch && ctx.Repo.GitRepo.IsBranchExist(*opts.DefaultBranch) {
  643. if err := ctx.Repo.GitRepo.SetDefaultBranch(*opts.DefaultBranch); err != nil {
  644. if !git.IsErrUnsupportedVersion(err) {
  645. ctx.Error(http.StatusInternalServerError, "SetDefaultBranch", err)
  646. return err
  647. }
  648. }
  649. repo.DefaultBranch = *opts.DefaultBranch
  650. }
  651. if err := models.UpdateRepository(repo, visibilityChanged); err != nil {
  652. ctx.Error(http.StatusInternalServerError, "UpdateRepository", err)
  653. return err
  654. }
  655. log.Trace("Repository basic settings updated: %s/%s", owner.Name, repo.Name)
  656. return nil
  657. }
  658. // updateRepoUnits updates repo units: Issue settings, Wiki settings, PR settings
  659. func updateRepoUnits(ctx *context.APIContext, opts api.EditRepoOption) error {
  660. owner := ctx.Repo.Owner
  661. repo := ctx.Repo.Repository
  662. var units []models.RepoUnit
  663. for _, tp := range models.MustRepoUnits {
  664. units = append(units, models.RepoUnit{
  665. RepoID: repo.ID,
  666. Type: tp,
  667. Config: new(models.UnitConfig),
  668. })
  669. }
  670. if opts.HasIssues == nil {
  671. // If HasIssues setting not touched, rewrite existing repo unit
  672. if unit, err := repo.GetUnit(models.UnitTypeIssues); err == nil {
  673. units = append(units, *unit)
  674. } else if unit, err := repo.GetUnit(models.UnitTypeExternalTracker); err == nil {
  675. units = append(units, *unit)
  676. }
  677. } else if *opts.HasIssues {
  678. if opts.ExternalTracker != nil {
  679. // Check that values are valid
  680. if !validation.IsValidExternalURL(opts.ExternalTracker.ExternalTrackerURL) {
  681. err := fmt.Errorf("External tracker URL not valid")
  682. ctx.Error(http.StatusUnprocessableEntity, "Invalid external tracker URL", err)
  683. return err
  684. }
  685. if len(opts.ExternalTracker.ExternalTrackerFormat) != 0 && !validation.IsValidExternalTrackerURLFormat(opts.ExternalTracker.ExternalTrackerFormat) {
  686. err := fmt.Errorf("External tracker URL format not valid")
  687. ctx.Error(http.StatusUnprocessableEntity, "Invalid external tracker URL format", err)
  688. return err
  689. }
  690. units = append(units, models.RepoUnit{
  691. RepoID: repo.ID,
  692. Type: models.UnitTypeExternalTracker,
  693. Config: &models.ExternalTrackerConfig{
  694. ExternalTrackerURL: opts.ExternalTracker.ExternalTrackerURL,
  695. ExternalTrackerFormat: opts.ExternalTracker.ExternalTrackerFormat,
  696. ExternalTrackerStyle: opts.ExternalTracker.ExternalTrackerStyle,
  697. },
  698. })
  699. } else {
  700. // Default to built-in tracker
  701. var config *models.IssuesConfig
  702. if opts.InternalTracker != nil {
  703. config = &models.IssuesConfig{
  704. EnableTimetracker: opts.InternalTracker.EnableTimeTracker,
  705. AllowOnlyContributorsToTrackTime: opts.InternalTracker.AllowOnlyContributorsToTrackTime,
  706. EnableDependencies: opts.InternalTracker.EnableIssueDependencies,
  707. }
  708. } else if unit, err := repo.GetUnit(models.UnitTypeIssues); err != nil {
  709. // Unit type doesn't exist so we make a new config file with default values
  710. config = &models.IssuesConfig{
  711. EnableTimetracker: true,
  712. AllowOnlyContributorsToTrackTime: true,
  713. EnableDependencies: true,
  714. }
  715. } else {
  716. config = unit.IssuesConfig()
  717. }
  718. units = append(units, models.RepoUnit{
  719. RepoID: repo.ID,
  720. Type: models.UnitTypeIssues,
  721. Config: config,
  722. })
  723. }
  724. }
  725. if opts.HasWiki == nil {
  726. // If HasWiki setting not touched, rewrite existing repo unit
  727. if unit, err := repo.GetUnit(models.UnitTypeWiki); err == nil {
  728. units = append(units, *unit)
  729. } else if unit, err := repo.GetUnit(models.UnitTypeExternalWiki); err == nil {
  730. units = append(units, *unit)
  731. }
  732. } else if *opts.HasWiki {
  733. if opts.ExternalWiki != nil {
  734. // Check that values are valid
  735. if !validation.IsValidExternalURL(opts.ExternalWiki.ExternalWikiURL) {
  736. err := fmt.Errorf("External wiki URL not valid")
  737. ctx.Error(http.StatusUnprocessableEntity, "", "Invalid external wiki URL")
  738. return err
  739. }
  740. units = append(units, models.RepoUnit{
  741. RepoID: repo.ID,
  742. Type: models.UnitTypeExternalWiki,
  743. Config: &models.ExternalWikiConfig{
  744. ExternalWikiURL: opts.ExternalWiki.ExternalWikiURL,
  745. },
  746. })
  747. } else {
  748. config := &models.UnitConfig{}
  749. units = append(units, models.RepoUnit{
  750. RepoID: repo.ID,
  751. Type: models.UnitTypeWiki,
  752. Config: config,
  753. })
  754. }
  755. }
  756. if opts.HasPullRequests == nil {
  757. // If HasPullRequest setting not touched, rewrite existing repo unit
  758. if unit, err := repo.GetUnit(models.UnitTypePullRequests); err == nil {
  759. units = append(units, *unit)
  760. }
  761. } else if *opts.HasPullRequests {
  762. // We do allow setting individual PR settings through the API, so
  763. // we get the config settings and then set them
  764. // if those settings were provided in the opts.
  765. unit, err := repo.GetUnit(models.UnitTypePullRequests)
  766. var config *models.PullRequestsConfig
  767. if err != nil {
  768. // Unit type doesn't exist so we make a new config file with default values
  769. config = &models.PullRequestsConfig{
  770. IgnoreWhitespaceConflicts: false,
  771. AllowMerge: true,
  772. AllowRebase: true,
  773. AllowRebaseMerge: true,
  774. AllowSquash: true,
  775. }
  776. } else {
  777. config = unit.PullRequestsConfig()
  778. }
  779. if opts.IgnoreWhitespaceConflicts != nil {
  780. config.IgnoreWhitespaceConflicts = *opts.IgnoreWhitespaceConflicts
  781. }
  782. if opts.AllowMerge != nil {
  783. config.AllowMerge = *opts.AllowMerge
  784. }
  785. if opts.AllowRebase != nil {
  786. config.AllowRebase = *opts.AllowRebase
  787. }
  788. if opts.AllowRebaseMerge != nil {
  789. config.AllowRebaseMerge = *opts.AllowRebaseMerge
  790. }
  791. if opts.AllowSquash != nil {
  792. config.AllowSquash = *opts.AllowSquash
  793. }
  794. units = append(units, models.RepoUnit{
  795. RepoID: repo.ID,
  796. Type: models.UnitTypePullRequests,
  797. Config: config,
  798. })
  799. }
  800. if err := models.UpdateRepositoryUnits(repo, units); err != nil {
  801. ctx.Error(http.StatusInternalServerError, "UpdateRepositoryUnits", err)
  802. return err
  803. }
  804. log.Trace("Repository advanced settings updated: %s/%s", owner.Name, repo.Name)
  805. return nil
  806. }
  807. // updateRepoArchivedState updates repo's archive state
  808. func updateRepoArchivedState(ctx *context.APIContext, opts api.EditRepoOption) error {
  809. repo := ctx.Repo.Repository
  810. // archive / un-archive
  811. if opts.Archived != nil {
  812. if repo.IsMirror {
  813. err := fmt.Errorf("repo is a mirror, cannot archive/un-archive")
  814. ctx.Error(http.StatusUnprocessableEntity, err.Error(), err)
  815. return err
  816. }
  817. if *opts.Archived {
  818. if err := repo.SetArchiveRepoState(*opts.Archived); err != nil {
  819. log.Error("Tried to archive a repo: %s", err)
  820. ctx.Error(http.StatusInternalServerError, "ArchiveRepoState", err)
  821. return err
  822. }
  823. log.Trace("Repository was archived: %s/%s", ctx.Repo.Owner.Name, repo.Name)
  824. } else {
  825. if err := repo.SetArchiveRepoState(*opts.Archived); err != nil {
  826. log.Error("Tried to un-archive a repo: %s", err)
  827. ctx.Error(http.StatusInternalServerError, "ArchiveRepoState", err)
  828. return err
  829. }
  830. log.Trace("Repository was un-archived: %s/%s", ctx.Repo.Owner.Name, repo.Name)
  831. }
  832. }
  833. return nil
  834. }
  835. // Delete one repository
  836. func Delete(ctx *context.APIContext) {
  837. // swagger:operation DELETE /repos/{owner}/{repo} repository repoDelete
  838. // ---
  839. // summary: Delete a repository
  840. // produces:
  841. // - application/json
  842. // parameters:
  843. // - name: owner
  844. // in: path
  845. // description: owner of the repo to delete
  846. // type: string
  847. // required: true
  848. // - name: repo
  849. // in: path
  850. // description: name of the repo to delete
  851. // type: string
  852. // required: true
  853. // responses:
  854. // "204":
  855. // "$ref": "#/responses/empty"
  856. // "403":
  857. // "$ref": "#/responses/forbidden"
  858. owner := ctx.Repo.Owner
  859. repo := ctx.Repo.Repository
  860. canDelete, err := repo.CanUserDelete(ctx.User)
  861. if err != nil {
  862. ctx.Error(500, "CanUserDelete", err)
  863. return
  864. } else if !canDelete {
  865. ctx.Error(403, "", "Given user is not owner of organization.")
  866. return
  867. }
  868. if err := repo_service.DeleteRepository(ctx.User, repo); err != nil {
  869. ctx.Error(500, "DeleteRepository", err)
  870. return
  871. }
  872. log.Trace("Repository deleted: %s/%s", owner.Name, repo.Name)
  873. ctx.Status(204)
  874. }
  875. // MirrorSync adds a mirrored repository to the sync queue
  876. func MirrorSync(ctx *context.APIContext) {
  877. // swagger:operation POST /repos/{owner}/{repo}/mirror-sync repository repoMirrorSync
  878. // ---
  879. // summary: Sync a mirrored repository
  880. // produces:
  881. // - application/json
  882. // parameters:
  883. // - name: owner
  884. // in: path
  885. // description: owner of the repo to sync
  886. // type: string
  887. // required: true
  888. // - name: repo
  889. // in: path
  890. // description: name of the repo to sync
  891. // type: string
  892. // required: true
  893. // responses:
  894. // "200":
  895. // "$ref": "#/responses/empty"
  896. repo := ctx.Repo.Repository
  897. if !ctx.Repo.CanWrite(models.UnitTypeCode) {
  898. ctx.Error(403, "MirrorSync", "Must have write access")
  899. }
  900. mirror_service.StartToMirror(repo.ID)
  901. ctx.Status(200)
  902. }