gitea源码

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881
  1. // Copyright 2016 The Gogs Authors. All rights reserved.
  2. // Copyright 2019 The Gitea Authors. All rights reserved.
  3. // SPDX-License-Identifier: MIT
  4. package org
  5. import (
  6. "errors"
  7. "net/http"
  8. activities_model "code.gitea.io/gitea/models/activities"
  9. "code.gitea.io/gitea/models/organization"
  10. "code.gitea.io/gitea/models/perm"
  11. access_model "code.gitea.io/gitea/models/perm/access"
  12. repo_model "code.gitea.io/gitea/models/repo"
  13. unit_model "code.gitea.io/gitea/models/unit"
  14. user_model "code.gitea.io/gitea/models/user"
  15. "code.gitea.io/gitea/modules/log"
  16. api "code.gitea.io/gitea/modules/structs"
  17. "code.gitea.io/gitea/modules/web"
  18. "code.gitea.io/gitea/routers/api/v1/user"
  19. "code.gitea.io/gitea/routers/api/v1/utils"
  20. "code.gitea.io/gitea/services/context"
  21. "code.gitea.io/gitea/services/convert"
  22. feed_service "code.gitea.io/gitea/services/feed"
  23. org_service "code.gitea.io/gitea/services/org"
  24. repo_service "code.gitea.io/gitea/services/repository"
  25. )
  26. // ListTeams list all the teams of an organization
  27. func ListTeams(ctx *context.APIContext) {
  28. // swagger:operation GET /orgs/{org}/teams organization orgListTeams
  29. // ---
  30. // summary: List an organization's teams
  31. // produces:
  32. // - application/json
  33. // parameters:
  34. // - name: org
  35. // in: path
  36. // description: name of the organization
  37. // type: string
  38. // required: true
  39. // - name: page
  40. // in: query
  41. // description: page number of results to return (1-based)
  42. // type: integer
  43. // - name: limit
  44. // in: query
  45. // description: page size of results
  46. // type: integer
  47. // responses:
  48. // "200":
  49. // "$ref": "#/responses/TeamList"
  50. // "404":
  51. // "$ref": "#/responses/notFound"
  52. teams, count, err := organization.SearchTeam(ctx, &organization.SearchTeamOptions{
  53. ListOptions: utils.GetListOptions(ctx),
  54. OrgID: ctx.Org.Organization.ID,
  55. })
  56. if err != nil {
  57. ctx.APIErrorInternal(err)
  58. return
  59. }
  60. apiTeams, err := convert.ToTeams(ctx, teams, false)
  61. if err != nil {
  62. ctx.APIErrorInternal(err)
  63. return
  64. }
  65. ctx.SetTotalCountHeader(count)
  66. ctx.JSON(http.StatusOK, apiTeams)
  67. }
  68. // ListUserTeams list all the teams a user belongs to
  69. func ListUserTeams(ctx *context.APIContext) {
  70. // swagger:operation GET /user/teams user userListTeams
  71. // ---
  72. // summary: List all the teams a user belongs to
  73. // produces:
  74. // - application/json
  75. // parameters:
  76. // - name: page
  77. // in: query
  78. // description: page number of results to return (1-based)
  79. // type: integer
  80. // - name: limit
  81. // in: query
  82. // description: page size of results
  83. // type: integer
  84. // responses:
  85. // "200":
  86. // "$ref": "#/responses/TeamList"
  87. teams, count, err := organization.SearchTeam(ctx, &organization.SearchTeamOptions{
  88. ListOptions: utils.GetListOptions(ctx),
  89. UserID: ctx.Doer.ID,
  90. })
  91. if err != nil {
  92. ctx.APIErrorInternal(err)
  93. return
  94. }
  95. apiTeams, err := convert.ToTeams(ctx, teams, true)
  96. if err != nil {
  97. ctx.APIErrorInternal(err)
  98. return
  99. }
  100. ctx.SetTotalCountHeader(count)
  101. ctx.JSON(http.StatusOK, apiTeams)
  102. }
  103. // GetTeam api for get a team
  104. func GetTeam(ctx *context.APIContext) {
  105. // swagger:operation GET /teams/{id} organization orgGetTeam
  106. // ---
  107. // summary: Get a team
  108. // produces:
  109. // - application/json
  110. // parameters:
  111. // - name: id
  112. // in: path
  113. // description: id of the team to get
  114. // type: integer
  115. // format: int64
  116. // required: true
  117. // responses:
  118. // "200":
  119. // "$ref": "#/responses/Team"
  120. // "404":
  121. // "$ref": "#/responses/notFound"
  122. apiTeam, err := convert.ToTeam(ctx, ctx.Org.Team, true)
  123. if err != nil {
  124. ctx.APIErrorInternal(err)
  125. return
  126. }
  127. ctx.JSON(http.StatusOK, apiTeam)
  128. }
  129. func attachTeamUnits(team *organization.Team, defaultAccessMode perm.AccessMode, units []string) {
  130. unitTypes, _ := unit_model.FindUnitTypes(units...)
  131. team.Units = make([]*organization.TeamUnit, 0, len(units))
  132. for _, tp := range unitTypes {
  133. team.Units = append(team.Units, &organization.TeamUnit{
  134. OrgID: team.OrgID,
  135. Type: tp,
  136. AccessMode: defaultAccessMode,
  137. })
  138. }
  139. }
  140. func attachTeamUnitsMap(team *organization.Team, unitsMap map[string]string) {
  141. team.Units = make([]*organization.TeamUnit, 0, len(unitsMap))
  142. for unitKey, p := range unitsMap {
  143. team.Units = append(team.Units, &organization.TeamUnit{
  144. OrgID: team.OrgID,
  145. Type: unit_model.TypeFromKey(unitKey),
  146. AccessMode: perm.ParseAccessMode(p),
  147. })
  148. }
  149. }
  150. func attachAdminTeamUnits(team *organization.Team) {
  151. team.Units = make([]*organization.TeamUnit, 0, len(unit_model.AllRepoUnitTypes))
  152. for _, ut := range unit_model.AllRepoUnitTypes {
  153. up := perm.AccessModeAdmin
  154. if ut == unit_model.TypeExternalTracker || ut == unit_model.TypeExternalWiki {
  155. up = perm.AccessModeRead
  156. }
  157. team.Units = append(team.Units, &organization.TeamUnit{
  158. OrgID: team.OrgID,
  159. Type: ut,
  160. AccessMode: up,
  161. })
  162. }
  163. }
  164. // CreateTeam api for create a team
  165. func CreateTeam(ctx *context.APIContext) {
  166. // swagger:operation POST /orgs/{org}/teams organization orgCreateTeam
  167. // ---
  168. // summary: Create a team
  169. // consumes:
  170. // - application/json
  171. // produces:
  172. // - application/json
  173. // parameters:
  174. // - name: org
  175. // in: path
  176. // description: name of the organization
  177. // type: string
  178. // required: true
  179. // - name: body
  180. // in: body
  181. // schema:
  182. // "$ref": "#/definitions/CreateTeamOption"
  183. // responses:
  184. // "201":
  185. // "$ref": "#/responses/Team"
  186. // "404":
  187. // "$ref": "#/responses/notFound"
  188. // "422":
  189. // "$ref": "#/responses/validationError"
  190. form := web.GetForm(ctx).(*api.CreateTeamOption)
  191. teamPermission := perm.ParseAccessMode(form.Permission, perm.AccessModeNone, perm.AccessModeAdmin)
  192. team := &organization.Team{
  193. OrgID: ctx.Org.Organization.ID,
  194. Name: form.Name,
  195. Description: form.Description,
  196. IncludesAllRepositories: form.IncludesAllRepositories,
  197. CanCreateOrgRepo: form.CanCreateOrgRepo,
  198. AccessMode: teamPermission,
  199. }
  200. if team.AccessMode < perm.AccessModeAdmin {
  201. if len(form.UnitsMap) > 0 {
  202. attachTeamUnitsMap(team, form.UnitsMap)
  203. } else if len(form.Units) > 0 {
  204. unitPerm := perm.ParseAccessMode(form.Permission, perm.AccessModeRead, perm.AccessModeWrite)
  205. attachTeamUnits(team, unitPerm, form.Units)
  206. } else {
  207. ctx.APIErrorInternal(errors.New("units permission should not be empty"))
  208. return
  209. }
  210. } else {
  211. attachAdminTeamUnits(team)
  212. }
  213. if err := org_service.NewTeam(ctx, team); err != nil {
  214. if organization.IsErrTeamAlreadyExist(err) {
  215. ctx.APIError(http.StatusUnprocessableEntity, err)
  216. } else {
  217. ctx.APIErrorInternal(err)
  218. }
  219. return
  220. }
  221. apiTeam, err := convert.ToTeam(ctx, team, true)
  222. if err != nil {
  223. ctx.APIErrorInternal(err)
  224. return
  225. }
  226. ctx.JSON(http.StatusCreated, apiTeam)
  227. }
  228. // EditTeam api for edit a team
  229. func EditTeam(ctx *context.APIContext) {
  230. // swagger:operation PATCH /teams/{id} organization orgEditTeam
  231. // ---
  232. // summary: Edit a team
  233. // consumes:
  234. // - application/json
  235. // produces:
  236. // - application/json
  237. // parameters:
  238. // - name: id
  239. // in: path
  240. // description: id of the team to edit
  241. // type: integer
  242. // required: true
  243. // - name: body
  244. // in: body
  245. // schema:
  246. // "$ref": "#/definitions/EditTeamOption"
  247. // responses:
  248. // "200":
  249. // "$ref": "#/responses/Team"
  250. // "404":
  251. // "$ref": "#/responses/notFound"
  252. form := web.GetForm(ctx).(*api.EditTeamOption)
  253. team := ctx.Org.Team
  254. if err := team.LoadUnits(ctx); err != nil {
  255. ctx.APIErrorInternal(err)
  256. return
  257. }
  258. if form.CanCreateOrgRepo != nil {
  259. team.CanCreateOrgRepo = team.IsOwnerTeam() || *form.CanCreateOrgRepo
  260. }
  261. if len(form.Name) > 0 {
  262. team.Name = form.Name
  263. }
  264. if form.Description != nil {
  265. team.Description = *form.Description
  266. }
  267. isAuthChanged := false
  268. isIncludeAllChanged := false
  269. if !team.IsOwnerTeam() && len(form.Permission) != 0 {
  270. teamPermission := perm.ParseAccessMode(form.Permission, perm.AccessModeNone, perm.AccessModeAdmin)
  271. if team.AccessMode != teamPermission {
  272. isAuthChanged = true
  273. team.AccessMode = teamPermission
  274. }
  275. if form.IncludesAllRepositories != nil {
  276. isIncludeAllChanged = true
  277. team.IncludesAllRepositories = *form.IncludesAllRepositories
  278. }
  279. }
  280. if team.AccessMode < perm.AccessModeAdmin {
  281. if len(form.UnitsMap) > 0 {
  282. attachTeamUnitsMap(team, form.UnitsMap)
  283. } else if len(form.Units) > 0 {
  284. unitPerm := perm.ParseAccessMode(form.Permission, perm.AccessModeRead, perm.AccessModeWrite)
  285. attachTeamUnits(team, unitPerm, form.Units)
  286. }
  287. } else {
  288. attachAdminTeamUnits(team)
  289. }
  290. if err := org_service.UpdateTeam(ctx, team, isAuthChanged, isIncludeAllChanged); err != nil {
  291. ctx.APIErrorInternal(err)
  292. return
  293. }
  294. apiTeam, err := convert.ToTeam(ctx, team)
  295. if err != nil {
  296. ctx.APIErrorInternal(err)
  297. return
  298. }
  299. ctx.JSON(http.StatusOK, apiTeam)
  300. }
  301. // DeleteTeam api for delete a team
  302. func DeleteTeam(ctx *context.APIContext) {
  303. // swagger:operation DELETE /teams/{id} organization orgDeleteTeam
  304. // ---
  305. // summary: Delete a team
  306. // parameters:
  307. // - name: id
  308. // in: path
  309. // description: id of the team to delete
  310. // type: integer
  311. // format: int64
  312. // required: true
  313. // responses:
  314. // "204":
  315. // description: team deleted
  316. // "404":
  317. // "$ref": "#/responses/notFound"
  318. if err := org_service.DeleteTeam(ctx, ctx.Org.Team); err != nil {
  319. ctx.APIErrorInternal(err)
  320. return
  321. }
  322. ctx.Status(http.StatusNoContent)
  323. }
  324. // GetTeamMembers api for get a team's members
  325. func GetTeamMembers(ctx *context.APIContext) {
  326. // swagger:operation GET /teams/{id}/members organization orgListTeamMembers
  327. // ---
  328. // summary: List a team's members
  329. // produces:
  330. // - application/json
  331. // parameters:
  332. // - name: id
  333. // in: path
  334. // description: id of the team
  335. // type: integer
  336. // format: int64
  337. // required: true
  338. // - name: page
  339. // in: query
  340. // description: page number of results to return (1-based)
  341. // type: integer
  342. // - name: limit
  343. // in: query
  344. // description: page size of results
  345. // type: integer
  346. // responses:
  347. // "200":
  348. // "$ref": "#/responses/UserList"
  349. // "404":
  350. // "$ref": "#/responses/notFound"
  351. isMember, err := organization.IsOrganizationMember(ctx, ctx.Org.Team.OrgID, ctx.Doer.ID)
  352. if err != nil {
  353. ctx.APIErrorInternal(err)
  354. return
  355. } else if !isMember && !ctx.Doer.IsAdmin {
  356. ctx.APIErrorNotFound()
  357. return
  358. }
  359. teamMembers, err := organization.GetTeamMembers(ctx, &organization.SearchMembersOptions{
  360. ListOptions: utils.GetListOptions(ctx),
  361. TeamID: ctx.Org.Team.ID,
  362. })
  363. if err != nil {
  364. ctx.APIErrorInternal(err)
  365. return
  366. }
  367. members := make([]*api.User, len(teamMembers))
  368. for i, member := range teamMembers {
  369. members[i] = convert.ToUser(ctx, member, ctx.Doer)
  370. }
  371. ctx.SetTotalCountHeader(int64(ctx.Org.Team.NumMembers))
  372. ctx.JSON(http.StatusOK, members)
  373. }
  374. // GetTeamMember api for get a particular member of team
  375. func GetTeamMember(ctx *context.APIContext) {
  376. // swagger:operation GET /teams/{id}/members/{username} organization orgListTeamMember
  377. // ---
  378. // summary: List a particular member of team
  379. // produces:
  380. // - application/json
  381. // parameters:
  382. // - name: id
  383. // in: path
  384. // description: id of the team
  385. // type: integer
  386. // format: int64
  387. // required: true
  388. // - name: username
  389. // in: path
  390. // description: username of the user whose data is to be listed
  391. // type: string
  392. // required: true
  393. // responses:
  394. // "200":
  395. // "$ref": "#/responses/User"
  396. // "404":
  397. // "$ref": "#/responses/notFound"
  398. u := user.GetContextUserByPathParam(ctx)
  399. if ctx.Written() {
  400. return
  401. }
  402. teamID := ctx.PathParamInt64("teamid")
  403. isTeamMember, err := organization.IsUserInTeams(ctx, u.ID, []int64{teamID})
  404. if err != nil {
  405. ctx.APIErrorInternal(err)
  406. return
  407. } else if !isTeamMember {
  408. ctx.APIErrorNotFound()
  409. return
  410. }
  411. ctx.JSON(http.StatusOK, convert.ToUser(ctx, u, ctx.Doer))
  412. }
  413. // AddTeamMember api for add a member to a team
  414. func AddTeamMember(ctx *context.APIContext) {
  415. // swagger:operation PUT /teams/{id}/members/{username} organization orgAddTeamMember
  416. // ---
  417. // summary: Add a team member
  418. // produces:
  419. // - application/json
  420. // parameters:
  421. // - name: id
  422. // in: path
  423. // description: id of the team
  424. // type: integer
  425. // format: int64
  426. // required: true
  427. // - name: username
  428. // in: path
  429. // description: username of the user to add to a team
  430. // type: string
  431. // required: true
  432. // responses:
  433. // "204":
  434. // "$ref": "#/responses/empty"
  435. // "403":
  436. // "$ref": "#/responses/forbidden"
  437. // "404":
  438. // "$ref": "#/responses/notFound"
  439. u := user.GetContextUserByPathParam(ctx)
  440. if ctx.Written() {
  441. return
  442. }
  443. if err := org_service.AddTeamMember(ctx, ctx.Org.Team, u); err != nil {
  444. if errors.Is(err, user_model.ErrBlockedUser) {
  445. ctx.APIError(http.StatusForbidden, err)
  446. } else {
  447. ctx.APIErrorInternal(err)
  448. }
  449. return
  450. }
  451. ctx.Status(http.StatusNoContent)
  452. }
  453. // RemoveTeamMember api for remove one member from a team
  454. func RemoveTeamMember(ctx *context.APIContext) {
  455. // swagger:operation DELETE /teams/{id}/members/{username} organization orgRemoveTeamMember
  456. // ---
  457. // summary: Remove a team member
  458. // produces:
  459. // - application/json
  460. // parameters:
  461. // - name: id
  462. // in: path
  463. // description: id of the team
  464. // type: integer
  465. // format: int64
  466. // required: true
  467. // - name: username
  468. // in: path
  469. // description: username of the user to remove from a team
  470. // type: string
  471. // required: true
  472. // responses:
  473. // "204":
  474. // "$ref": "#/responses/empty"
  475. // "404":
  476. // "$ref": "#/responses/notFound"
  477. u := user.GetContextUserByPathParam(ctx)
  478. if ctx.Written() {
  479. return
  480. }
  481. if err := org_service.RemoveTeamMember(ctx, ctx.Org.Team, u); err != nil {
  482. ctx.APIErrorInternal(err)
  483. return
  484. }
  485. ctx.Status(http.StatusNoContent)
  486. }
  487. // GetTeamRepos api for get a team's repos
  488. func GetTeamRepos(ctx *context.APIContext) {
  489. // swagger:operation GET /teams/{id}/repos organization orgListTeamRepos
  490. // ---
  491. // summary: List a team's repos
  492. // produces:
  493. // - application/json
  494. // parameters:
  495. // - name: id
  496. // in: path
  497. // description: id of the team
  498. // type: integer
  499. // format: int64
  500. // required: true
  501. // - name: page
  502. // in: query
  503. // description: page number of results to return (1-based)
  504. // type: integer
  505. // - name: limit
  506. // in: query
  507. // description: page size of results
  508. // type: integer
  509. // responses:
  510. // "200":
  511. // "$ref": "#/responses/RepositoryList"
  512. // "404":
  513. // "$ref": "#/responses/notFound"
  514. team := ctx.Org.Team
  515. teamRepos, err := repo_model.GetTeamRepositories(ctx, &repo_model.SearchTeamRepoOptions{
  516. ListOptions: utils.GetListOptions(ctx),
  517. TeamID: team.ID,
  518. })
  519. if err != nil {
  520. ctx.APIErrorInternal(err)
  521. return
  522. }
  523. repos := make([]*api.Repository, len(teamRepos))
  524. for i, repo := range teamRepos {
  525. permission, err := access_model.GetUserRepoPermission(ctx, repo, ctx.Doer)
  526. if err != nil {
  527. ctx.APIErrorInternal(err)
  528. return
  529. }
  530. repos[i] = convert.ToRepo(ctx, repo, permission)
  531. }
  532. ctx.SetTotalCountHeader(int64(team.NumRepos))
  533. ctx.JSON(http.StatusOK, repos)
  534. }
  535. // GetTeamRepo api for get a particular repo of team
  536. func GetTeamRepo(ctx *context.APIContext) {
  537. // swagger:operation GET /teams/{id}/repos/{org}/{repo} organization orgListTeamRepo
  538. // ---
  539. // summary: List a particular repo of team
  540. // produces:
  541. // - application/json
  542. // parameters:
  543. // - name: id
  544. // in: path
  545. // description: id of the team
  546. // type: integer
  547. // format: int64
  548. // required: true
  549. // - name: org
  550. // in: path
  551. // description: organization that owns the repo to list
  552. // type: string
  553. // required: true
  554. // - name: repo
  555. // in: path
  556. // description: name of the repo to list
  557. // type: string
  558. // required: true
  559. // responses:
  560. // "200":
  561. // "$ref": "#/responses/Repository"
  562. // "404":
  563. // "$ref": "#/responses/notFound"
  564. repo := getRepositoryByParams(ctx)
  565. if ctx.Written() {
  566. return
  567. }
  568. if !organization.HasTeamRepo(ctx, ctx.Org.Team.OrgID, ctx.Org.Team.ID, repo.ID) {
  569. ctx.APIErrorNotFound()
  570. return
  571. }
  572. permission, err := access_model.GetUserRepoPermission(ctx, repo, ctx.Doer)
  573. if err != nil {
  574. ctx.APIErrorInternal(err)
  575. return
  576. }
  577. ctx.JSON(http.StatusOK, convert.ToRepo(ctx, repo, permission))
  578. }
  579. // getRepositoryByParams get repository by a team's organization ID and repo name
  580. func getRepositoryByParams(ctx *context.APIContext) *repo_model.Repository {
  581. repo, err := repo_model.GetRepositoryByName(ctx, ctx.Org.Team.OrgID, ctx.PathParam("reponame"))
  582. if err != nil {
  583. if repo_model.IsErrRepoNotExist(err) {
  584. ctx.APIErrorNotFound()
  585. } else {
  586. ctx.APIErrorInternal(err)
  587. }
  588. return nil
  589. }
  590. return repo
  591. }
  592. // AddTeamRepository api for adding a repository to a team
  593. func AddTeamRepository(ctx *context.APIContext) {
  594. // swagger:operation PUT /teams/{id}/repos/{org}/{repo} organization orgAddTeamRepository
  595. // ---
  596. // summary: Add a repository to a team
  597. // produces:
  598. // - application/json
  599. // parameters:
  600. // - name: id
  601. // in: path
  602. // description: id of the team
  603. // type: integer
  604. // format: int64
  605. // required: true
  606. // - name: org
  607. // in: path
  608. // description: organization that owns the repo to add
  609. // type: string
  610. // required: true
  611. // - name: repo
  612. // in: path
  613. // description: name of the repo to add
  614. // type: string
  615. // required: true
  616. // responses:
  617. // "204":
  618. // "$ref": "#/responses/empty"
  619. // "403":
  620. // "$ref": "#/responses/forbidden"
  621. // "404":
  622. // "$ref": "#/responses/notFound"
  623. repo := getRepositoryByParams(ctx)
  624. if ctx.Written() {
  625. return
  626. }
  627. if access, err := access_model.AccessLevel(ctx, ctx.Doer, repo); err != nil {
  628. ctx.APIErrorInternal(err)
  629. return
  630. } else if access < perm.AccessModeAdmin {
  631. ctx.APIError(http.StatusForbidden, "Must have admin-level access to the repository")
  632. return
  633. }
  634. if err := repo_service.TeamAddRepository(ctx, ctx.Org.Team, repo); err != nil {
  635. ctx.APIErrorInternal(err)
  636. return
  637. }
  638. ctx.Status(http.StatusNoContent)
  639. }
  640. // RemoveTeamRepository api for removing a repository from a team
  641. func RemoveTeamRepository(ctx *context.APIContext) {
  642. // swagger:operation DELETE /teams/{id}/repos/{org}/{repo} organization orgRemoveTeamRepository
  643. // ---
  644. // summary: Remove a repository from a team
  645. // description: This does not delete the repository, it only removes the
  646. // repository from the team.
  647. // produces:
  648. // - application/json
  649. // parameters:
  650. // - name: id
  651. // in: path
  652. // description: id of the team
  653. // type: integer
  654. // format: int64
  655. // required: true
  656. // - name: org
  657. // in: path
  658. // description: organization that owns the repo to remove
  659. // type: string
  660. // required: true
  661. // - name: repo
  662. // in: path
  663. // description: name of the repo to remove
  664. // type: string
  665. // required: true
  666. // responses:
  667. // "204":
  668. // "$ref": "#/responses/empty"
  669. // "403":
  670. // "$ref": "#/responses/forbidden"
  671. // "404":
  672. // "$ref": "#/responses/notFound"
  673. repo := getRepositoryByParams(ctx)
  674. if ctx.Written() {
  675. return
  676. }
  677. if access, err := access_model.AccessLevel(ctx, ctx.Doer, repo); err != nil {
  678. ctx.APIErrorInternal(err)
  679. return
  680. } else if access < perm.AccessModeAdmin {
  681. ctx.APIError(http.StatusForbidden, "Must have admin-level access to the repository")
  682. return
  683. }
  684. if err := repo_service.RemoveRepositoryFromTeam(ctx, ctx.Org.Team, repo.ID); err != nil {
  685. ctx.APIErrorInternal(err)
  686. return
  687. }
  688. ctx.Status(http.StatusNoContent)
  689. }
  690. // SearchTeam api for searching teams
  691. func SearchTeam(ctx *context.APIContext) {
  692. // swagger:operation GET /orgs/{org}/teams/search organization teamSearch
  693. // ---
  694. // summary: Search for teams within an organization
  695. // produces:
  696. // - application/json
  697. // parameters:
  698. // - name: org
  699. // in: path
  700. // description: name of the organization
  701. // type: string
  702. // required: true
  703. // - name: q
  704. // in: query
  705. // description: keywords to search
  706. // type: string
  707. // - name: include_desc
  708. // in: query
  709. // description: include search within team description (defaults to true)
  710. // type: boolean
  711. // - name: page
  712. // in: query
  713. // description: page number of results to return (1-based)
  714. // type: integer
  715. // - name: limit
  716. // in: query
  717. // description: page size of results
  718. // type: integer
  719. // responses:
  720. // "200":
  721. // description: "SearchResults of a successful search"
  722. // schema:
  723. // type: object
  724. // properties:
  725. // ok:
  726. // type: boolean
  727. // data:
  728. // type: array
  729. // items:
  730. // "$ref": "#/definitions/Team"
  731. // "404":
  732. // "$ref": "#/responses/notFound"
  733. listOptions := utils.GetListOptions(ctx)
  734. opts := &organization.SearchTeamOptions{
  735. Keyword: ctx.FormTrim("q"),
  736. OrgID: ctx.Org.Organization.ID,
  737. IncludeDesc: ctx.FormString("include_desc") == "" || ctx.FormBool("include_desc"),
  738. ListOptions: listOptions,
  739. }
  740. // Only admin is allowd to search for all teams
  741. if !ctx.Doer.IsAdmin {
  742. opts.UserID = ctx.Doer.ID
  743. }
  744. teams, maxResults, err := organization.SearchTeam(ctx, opts)
  745. if err != nil {
  746. log.Error("SearchTeam failed: %v", err)
  747. ctx.JSON(http.StatusInternalServerError, map[string]any{
  748. "ok": false,
  749. "error": "SearchTeam internal failure",
  750. })
  751. return
  752. }
  753. apiTeams, err := convert.ToTeams(ctx, teams, false)
  754. if err != nil {
  755. ctx.APIErrorInternal(err)
  756. return
  757. }
  758. ctx.SetLinkHeader(int(maxResults), listOptions.PageSize)
  759. ctx.SetTotalCountHeader(maxResults)
  760. ctx.JSON(http.StatusOK, map[string]any{
  761. "ok": true,
  762. "data": apiTeams,
  763. })
  764. }
  765. func ListTeamActivityFeeds(ctx *context.APIContext) {
  766. // swagger:operation GET /teams/{id}/activities/feeds organization orgListTeamActivityFeeds
  767. // ---
  768. // summary: List a team's activity feeds
  769. // produces:
  770. // - application/json
  771. // parameters:
  772. // - name: id
  773. // in: path
  774. // description: id of the team
  775. // type: integer
  776. // format: int64
  777. // required: true
  778. // - name: date
  779. // in: query
  780. // description: the date of the activities to be found
  781. // type: string
  782. // format: date
  783. // - name: page
  784. // in: query
  785. // description: page number of results to return (1-based)
  786. // type: integer
  787. // - name: limit
  788. // in: query
  789. // description: page size of results
  790. // type: integer
  791. // responses:
  792. // "200":
  793. // "$ref": "#/responses/ActivityFeedsList"
  794. // "404":
  795. // "$ref": "#/responses/notFound"
  796. listOptions := utils.GetListOptions(ctx)
  797. opts := activities_model.GetFeedsOptions{
  798. RequestedTeam: ctx.Org.Team,
  799. Actor: ctx.Doer,
  800. IncludePrivate: true,
  801. Date: ctx.FormString("date"),
  802. ListOptions: listOptions,
  803. }
  804. feeds, count, err := feed_service.GetFeeds(ctx, opts)
  805. if err != nil {
  806. ctx.APIErrorInternal(err)
  807. return
  808. }
  809. ctx.SetTotalCountHeader(count)
  810. ctx.JSON(http.StatusOK, convert.ToActivities(ctx, feeds, ctx.Doer))
  811. }