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