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